mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): partial rebuild of model manager internal logic
This commit is contained in:
parent
2a661450c3
commit
52f9749bf5
@ -66,7 +66,7 @@
|
|||||||
"@emotion/react": "^11.11.3",
|
"@emotion/react": "^11.11.3",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@fontsource-variable/inter": "^5.0.16",
|
"@fontsource-variable/inter": "^5.0.16",
|
||||||
"@mantine/form": "^6.0.21",
|
"@mantine/form": "6.0.21",
|
||||||
"@nanostores/react": "^0.7.1",
|
"@nanostores/react": "^0.7.1",
|
||||||
"@reduxjs/toolkit": "^2.0.1",
|
"@reduxjs/toolkit": "^2.0.1",
|
||||||
"@roarr/browser-log-writer": "^1.3.0",
|
"@roarr/browser-log-writer": "^1.3.0",
|
||||||
@ -89,6 +89,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-error-boundary": "^4.0.12",
|
"react-error-boundary": "^4.0.12",
|
||||||
|
"react-hook-form": "^7.49.2",
|
||||||
"react-hotkeys-hook": "4.4.1",
|
"react-hotkeys-hook": "4.4.1",
|
||||||
"react-i18next": "^13.5.0",
|
"react-i18next": "^13.5.0",
|
||||||
"react-icons": "^4.12.0",
|
"react-icons": "^4.12.0",
|
||||||
|
@ -48,7 +48,7 @@ dependencies:
|
|||||||
specifier: ^5.0.16
|
specifier: ^5.0.16
|
||||||
version: 5.0.16
|
version: 5.0.16
|
||||||
'@mantine/form':
|
'@mantine/form':
|
||||||
specifier: ^6.0.21
|
specifier: 6.0.21
|
||||||
version: 6.0.21(react@18.2.0)
|
version: 6.0.21(react@18.2.0)
|
||||||
'@nanostores/react':
|
'@nanostores/react':
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
@ -116,6 +116,9 @@ dependencies:
|
|||||||
react-error-boundary:
|
react-error-boundary:
|
||||||
specifier: ^4.0.12
|
specifier: ^4.0.12
|
||||||
version: 4.0.12(react@18.2.0)
|
version: 4.0.12(react@18.2.0)
|
||||||
|
react-hook-form:
|
||||||
|
specifier: ^7.49.2
|
||||||
|
version: 7.49.2(react@18.2.0)
|
||||||
react-hotkeys-hook:
|
react-hotkeys-hook:
|
||||||
specifier: 4.4.1
|
specifier: 4.4.1
|
||||||
version: 4.4.1(react-dom@18.2.0)(react@18.2.0)
|
version: 4.4.1(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -11049,6 +11052,15 @@ packages:
|
|||||||
use-sidecar: 1.1.2(@types/react@18.2.46)(react@18.2.0)
|
use-sidecar: 1.1.2(@types/react@18.2.46)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/react-hook-form@7.49.2(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-TZcnSc17+LPPVpMRIDNVITY6w20deMdNi6iehTFLV1x8SqThXGwu93HjlUVU09pzFgZH7qZOvLMM7UYf2ShAHA==}
|
||||||
|
engines: {node: '>=18', pnpm: '8'}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.0 || ^17 || ^18
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/react-hotkeys-hook@4.4.1(react-dom@18.2.0)(react@18.2.0):
|
/react-hotkeys-hook@4.4.1(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==}
|
resolution: {integrity: sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Flex,
|
Flex,
|
||||||
FormControl as ChakraFormControl,
|
FormControl as ChakraFormControl,
|
||||||
|
FormErrorMessage as ChakraFormErrorMessage,
|
||||||
FormHelperText as ChakraFormHelperText,
|
FormHelperText as ChakraFormHelperText,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
@ -22,12 +23,12 @@ export const InvControl = memo(
|
|||||||
isDisabled,
|
isDisabled,
|
||||||
labelProps,
|
labelProps,
|
||||||
label,
|
label,
|
||||||
|
error,
|
||||||
...formControlProps
|
...formControlProps
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const ctx = useContext(InvControlGroupContext);
|
const ctx = useContext(InvControlGroupContext);
|
||||||
|
|
||||||
if (helperText) {
|
|
||||||
return (
|
return (
|
||||||
<ChakraFormControl
|
<ChakraFormControl
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -48,10 +49,12 @@ export const InvControl = memo(
|
|||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
{helperText && (
|
||||||
<ChakraFormHelperText>{helperText}</ChakraFormHelperText>
|
<ChakraFormHelperText>{helperText}</ChakraFormHelperText>
|
||||||
|
)}
|
||||||
|
{error && <ChakraFormErrorMessage>{error}</ChakraFormErrorMessage>}
|
||||||
</ChakraFormControl>
|
</ChakraFormControl>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChakraFormControl
|
<ChakraFormControl
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import { formAnatomy as parts } from '@chakra-ui/anatomy';
|
import {
|
||||||
|
formAnatomy as formParts,
|
||||||
|
formErrorAnatomy as formErrorParts,
|
||||||
|
} from '@chakra-ui/anatomy';
|
||||||
import {
|
import {
|
||||||
createMultiStyleConfigHelpers,
|
createMultiStyleConfigHelpers,
|
||||||
defineStyle,
|
defineStyle,
|
||||||
defineStyleConfig,
|
defineStyleConfig,
|
||||||
} from '@chakra-ui/styled-system';
|
} from '@chakra-ui/styled-system';
|
||||||
|
|
||||||
const { definePartsStyle, defineMultiStyleConfig } =
|
const {
|
||||||
createMultiStyleConfigHelpers(parts.keys);
|
definePartsStyle: defineFormPartsStyle,
|
||||||
|
defineMultiStyleConfig: defineFormMultiStyleConfig,
|
||||||
|
} = createMultiStyleConfigHelpers(formParts.keys);
|
||||||
|
|
||||||
const formBaseStyle = definePartsStyle((props) => {
|
const formBaseStyle = defineFormPartsStyle((props) => {
|
||||||
return {
|
return {
|
||||||
container: {
|
container: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -19,7 +24,7 @@ const formBaseStyle = definePartsStyle((props) => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const withHelperText = definePartsStyle(() => ({
|
const withHelperText = defineFormPartsStyle(() => ({
|
||||||
container: {
|
container: {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: 0,
|
gap: 0,
|
||||||
@ -41,7 +46,7 @@ const withHelperText = definePartsStyle(() => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const formTheme = defineMultiStyleConfig({
|
export const formTheme = defineFormMultiStyleConfig({
|
||||||
baseStyle: formBaseStyle,
|
baseStyle: formBaseStyle,
|
||||||
variants: {
|
variants: {
|
||||||
withHelperText,
|
withHelperText,
|
||||||
@ -73,3 +78,14 @@ const formLabelBaseStyle = defineStyle(() => {
|
|||||||
export const formLabelTheme = defineStyleConfig({
|
export const formLabelTheme = defineStyleConfig({
|
||||||
baseStyle: formLabelBaseStyle,
|
baseStyle: formLabelBaseStyle,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { defineMultiStyleConfig: defineFormErrorMultiStyleConfig } =
|
||||||
|
createMultiStyleConfigHelpers(formErrorParts.keys);
|
||||||
|
|
||||||
|
export const formErrorTheme = defineFormErrorMultiStyleConfig({
|
||||||
|
baseStyle: {
|
||||||
|
text: {
|
||||||
|
color: 'error.300',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -7,6 +7,7 @@ import type { Feature } from 'common/components/IAIInformationalPopover/constant
|
|||||||
export type InvControlProps = ChakraFormControlProps & {
|
export type InvControlProps = ChakraFormControlProps & {
|
||||||
label?: string;
|
label?: string;
|
||||||
helperText?: string;
|
helperText?: string;
|
||||||
|
error?: string;
|
||||||
feature?: Feature;
|
feature?: Feature;
|
||||||
renderInfoPopoverInPortal?: boolean;
|
renderInfoPopoverInPortal?: boolean;
|
||||||
labelProps?: Omit<
|
labelProps?: Omit<
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { useForm } from '@mantine/form';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
||||||
@ -13,6 +12,8 @@ import { addToast } from 'features/system/store/systemSlice';
|
|||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import type { CSSProperties, FocusEventHandler } from 'react';
|
import type { CSSProperties, FocusEventHandler } from 'react';
|
||||||
import { memo, useCallback, useState } from 'react';
|
import { memo, useCallback, useState } from 'react';
|
||||||
|
import type { SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAddMainModelsMutation } from 'services/api/endpoints/models';
|
import { useAddMainModelsMutation } from 'services/api/endpoints/models';
|
||||||
import type { CheckpointModelConfig } from 'services/api/types';
|
import type { CheckpointModelConfig } from 'services/api/types';
|
||||||
@ -28,8 +29,16 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { model_path } = props;
|
const { model_path } = props;
|
||||||
|
|
||||||
const advancedAddCheckpointForm = useForm<CheckpointModelConfig>({
|
const {
|
||||||
initialValues: {
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
getValues,
|
||||||
|
setValue,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm<CheckpointModelConfig>({
|
||||||
|
defaultValues: {
|
||||||
model_name: model_path ? getModelName(model_path) : '',
|
model_name: model_path ? getModelName(model_path) : '',
|
||||||
base_model: 'sd-1',
|
base_model: 'sd-1',
|
||||||
model_type: 'main',
|
model_type: 'main',
|
||||||
@ -41,13 +50,15 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
|
|||||||
variant: 'normal',
|
variant: 'normal',
|
||||||
config: 'configs\\stable-diffusion\\v1-inference.yaml',
|
config: 'configs\\stable-diffusion\\v1-inference.yaml',
|
||||||
},
|
},
|
||||||
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|
||||||
const [addMainModel] = useAddMainModelsMutation();
|
const [addMainModel] = useAddMainModelsMutation();
|
||||||
|
|
||||||
const [useCustomConfig, setUseCustomConfig] = useState<boolean>(false);
|
const [useCustomConfig, setUseCustomConfig] = useState<boolean>(false);
|
||||||
|
|
||||||
const advancedAddCheckpointFormHandler = (values: CheckpointModelConfig) => {
|
const onSubmit = useCallback<SubmitHandler<CheckpointModelConfig>>(
|
||||||
|
(values) => {
|
||||||
addMainModel({
|
addMainModel({
|
||||||
body: values,
|
body: values,
|
||||||
})
|
})
|
||||||
@ -63,7 +74,7 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
advancedAddCheckpointForm.reset();
|
reset();
|
||||||
|
|
||||||
// Close Advanced Panel in Scan Models tab
|
// Close Advanced Panel in Scan Models tab
|
||||||
if (model_path) {
|
if (model_path) {
|
||||||
@ -82,22 +93,20 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
[addMainModel, dispatch, model_path, reset, t]
|
||||||
|
);
|
||||||
|
|
||||||
const handleBlurModelLocation: FocusEventHandler<HTMLInputElement> =
|
const onBlur: FocusEventHandler<HTMLInputElement> = useCallback(
|
||||||
useCallback(
|
|
||||||
(e) => {
|
(e) => {
|
||||||
if (advancedAddCheckpointForm.values['model_name'] === '') {
|
if (getValues().model_name === '') {
|
||||||
const modelName = getModelName(e.currentTarget.value);
|
const modelName = getModelName(e.currentTarget.value);
|
||||||
if (modelName) {
|
if (modelName) {
|
||||||
advancedAddCheckpointForm.setFieldValue(
|
setValue('model_name', modelName as string);
|
||||||
'model_name',
|
|
||||||
modelName as string
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[advancedAddCheckpointForm]
|
[getValues, setValue]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleChangeUseCustomConfig = useCallback(
|
const handleChangeUseCustomConfig = useCallback(
|
||||||
@ -106,56 +115,53 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form onSubmit={handleSubmit(onSubmit)} style={formStyles}>
|
||||||
onSubmit={advancedAddCheckpointForm.onSubmit((v) =>
|
|
||||||
advancedAddCheckpointFormHandler(v)
|
|
||||||
)}
|
|
||||||
style={formStyles}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" gap={2}>
|
<Flex flexDirection="column" gap={2}>
|
||||||
<InvControl label={t('modelManager.model')} isRequired>
|
<InvControl
|
||||||
|
label={t('modelManager.model')}
|
||||||
|
isInvalid={Boolean(errors.model_name)}
|
||||||
|
error={errors.model_name?.message}
|
||||||
|
>
|
||||||
<InvInput
|
<InvInput
|
||||||
{...advancedAddCheckpointForm.getInputProps('model_name')}
|
{...register('model_name', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 3 || 'Must be at least 3 characters',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.baseModel')}>
|
<BaseModelSelect<CheckpointModelConfig>
|
||||||
<BaseModelSelect
|
control={control}
|
||||||
{...advancedAddCheckpointForm.getInputProps('base_model')}
|
name="base_model"
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
<InvControl
|
||||||
<InvControl label={t('modelManager.modelLocation')} isRequired>
|
label={t('modelManager.modelLocation')}
|
||||||
|
isInvalid={Boolean(errors.path)}
|
||||||
|
error={errors.path?.message}
|
||||||
|
>
|
||||||
<InvInput
|
<InvInput
|
||||||
{...advancedAddCheckpointForm.getInputProps('path')}
|
{...register('path', {
|
||||||
onBlur={handleBlurModelLocation}
|
validate: (value) =>
|
||||||
|
value.trim().length > 0 || 'Must provide a path',
|
||||||
|
onBlur,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.description')}>
|
<InvControl label={t('modelManager.description')}>
|
||||||
<InvInput
|
<InvInput {...register('description')} />
|
||||||
{...advancedAddCheckpointForm.getInputProps('description')}
|
|
||||||
/>
|
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.vaeLocation')}>
|
<InvControl label={t('modelManager.vaeLocation')}>
|
||||||
<InvInput {...advancedAddCheckpointForm.getInputProps('vae')} />
|
<InvInput {...register('vae')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.variant')}>
|
<ModelVariantSelect<CheckpointModelConfig>
|
||||||
<ModelVariantSelect
|
control={control}
|
||||||
{...advancedAddCheckpointForm.getInputProps('variant')}
|
name="variant"
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
|
||||||
<Flex flexDirection="column" width="100%" gap={2}>
|
<Flex flexDirection="column" width="100%" gap={2}>
|
||||||
{!useCustomConfig ? (
|
{!useCustomConfig ? (
|
||||||
<CheckpointConfigsSelect
|
<CheckpointConfigsSelect control={control} name="config" />
|
||||||
required
|
|
||||||
{...advancedAddCheckpointForm.getInputProps('config')}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<InvControl
|
<InvControl isRequired label={t('modelManager.config')}>
|
||||||
label={t('modelManager.customConfigFileLocation')}
|
<InvInput {...register('config')} />
|
||||||
isRequired
|
|
||||||
>
|
|
||||||
<InvInput
|
|
||||||
{...advancedAddCheckpointForm.getInputProps('config')}
|
|
||||||
/>
|
|
||||||
</InvControl>
|
</InvControl>
|
||||||
)}
|
)}
|
||||||
<InvControl label={t('modelManager.useCustomConfig')}>
|
<InvControl label={t('modelManager.useCustomConfig')}>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { useForm } from '@mantine/form';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
@ -11,6 +10,8 @@ import { addToast } from 'features/system/store/systemSlice';
|
|||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import type { CSSProperties, FocusEventHandler } from 'react';
|
import type { CSSProperties, FocusEventHandler } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
import type { SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAddMainModelsMutation } from 'services/api/endpoints/models';
|
import { useAddMainModelsMutation } from 'services/api/endpoints/models';
|
||||||
import type { DiffusersModelConfig } from 'services/api/types';
|
import type { DiffusersModelConfig } from 'services/api/types';
|
||||||
@ -28,8 +29,16 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
|
|||||||
|
|
||||||
const [addMainModel] = useAddMainModelsMutation();
|
const [addMainModel] = useAddMainModelsMutation();
|
||||||
|
|
||||||
const advancedAddDiffusersForm = useForm<DiffusersModelConfig>({
|
const {
|
||||||
initialValues: {
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
getValues,
|
||||||
|
setValue,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm<DiffusersModelConfig>({
|
||||||
|
defaultValues: {
|
||||||
model_name: model_path ? getModelName(model_path, false) : '',
|
model_name: model_path ? getModelName(model_path, false) : '',
|
||||||
base_model: 'sd-1',
|
base_model: 'sd-1',
|
||||||
model_type: 'main',
|
model_type: 'main',
|
||||||
@ -40,9 +49,11 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
|
|||||||
vae: '',
|
vae: '',
|
||||||
variant: 'normal',
|
variant: 'normal',
|
||||||
},
|
},
|
||||||
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|
||||||
const advancedAddDiffusersFormHandler = (values: DiffusersModelConfig) => {
|
const onSubmit = useCallback<SubmitHandler<DiffusersModelConfig>>(
|
||||||
|
(values) => {
|
||||||
addMainModel({
|
addMainModel({
|
||||||
body: values,
|
body: values,
|
||||||
})
|
})
|
||||||
@ -58,7 +69,7 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
advancedAddDiffusersForm.reset();
|
reset();
|
||||||
// Close Advanced Panel in Scan Models tab
|
// Close Advanced Panel in Scan Models tab
|
||||||
if (model_path) {
|
if (model_path) {
|
||||||
dispatch(setAdvancedAddScanModel(null));
|
dispatch(setAdvancedAddScanModel(null));
|
||||||
@ -76,60 +87,66 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
[addMainModel, dispatch, model_path, reset, t]
|
||||||
|
);
|
||||||
|
|
||||||
const handleBlurModelLocation: FocusEventHandler<HTMLInputElement> =
|
const onBlur: FocusEventHandler<HTMLInputElement> = useCallback(
|
||||||
useCallback(
|
|
||||||
(e) => {
|
(e) => {
|
||||||
if (advancedAddDiffusersForm.values['model_name'] === '') {
|
if (getValues().model_name === '') {
|
||||||
const modelName = getModelName(e.currentTarget.value, false);
|
const modelName = getModelName(e.currentTarget.value, false);
|
||||||
if (modelName) {
|
if (modelName) {
|
||||||
advancedAddDiffusersForm.setFieldValue(
|
setValue('model_name', modelName as string);
|
||||||
'model_name',
|
|
||||||
modelName as string
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[advancedAddDiffusersForm]
|
[getValues, setValue]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form onSubmit={handleSubmit(onSubmit)} style={formStyles}>
|
||||||
onSubmit={advancedAddDiffusersForm.onSubmit((v) =>
|
|
||||||
advancedAddDiffusersFormHandler(v)
|
|
||||||
)}
|
|
||||||
style={formStyles}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" gap={2}>
|
<Flex flexDirection="column" gap={2}>
|
||||||
<InvControl isRequired label={t('modelManager.model')}>
|
<InvControl
|
||||||
<InvInput {...advancedAddDiffusersForm.getInputProps('model_name')} />
|
label={t('modelManager.name')}
|
||||||
</InvControl>
|
isInvalid={Boolean(errors.model_name)}
|
||||||
<InvControl label={t('modelManager.baseModel')}>
|
error={errors.model_name?.message}
|
||||||
<BaseModelSelect
|
>
|
||||||
{...advancedAddDiffusersForm.getInputProps('base_model')}
|
<InvInput
|
||||||
|
{...register('model_name', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 3 || 'Must be at least 3 characters',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl isRequired label={t('modelManager.modelLocation')}>
|
<InvControl label={t('modelManager.baseModel')}>
|
||||||
|
<BaseModelSelect<DiffusersModelConfig>
|
||||||
|
control={control}
|
||||||
|
name="base_model"
|
||||||
|
/>
|
||||||
|
</InvControl>
|
||||||
|
<InvControl
|
||||||
|
label={t('modelManager.modelLocation')}
|
||||||
|
isInvalid={Boolean(errors.path)}
|
||||||
|
error={errors.path?.message}
|
||||||
|
>
|
||||||
<InvInput
|
<InvInput
|
||||||
placeholder={t('modelManager.modelLocationValidationMsg')}
|
{...register('path', {
|
||||||
{...advancedAddDiffusersForm.getInputProps('path')}
|
validate: (value) =>
|
||||||
onBlur={handleBlurModelLocation}
|
value.trim().length > 0 || 'Must provide a path',
|
||||||
|
onBlur,
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.description')}>
|
<InvControl label={t('modelManager.description')}>
|
||||||
<InvInput
|
<InvInput {...register('description')} />
|
||||||
{...advancedAddDiffusersForm.getInputProps('description')}
|
|
||||||
/>
|
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.vaeLocation')}>
|
<InvControl label={t('modelManager.vaeLocation')}>
|
||||||
<InvInput {...advancedAddDiffusersForm.getInputProps('vae')} />
|
<InvInput {...register('vae')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.variant')}>
|
<ModelVariantSelect<DiffusersModelConfig>
|
||||||
<ModelVariantSelect
|
control={control}
|
||||||
{...advancedAddDiffusersForm.getInputProps('variant')}
|
name="variant"
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
|
||||||
|
|
||||||
<InvButton mt={2} type="submit">
|
<InvButton mt={2} type="submit">
|
||||||
{t('modelManager.addModel')}
|
{t('modelManager.addModel')}
|
||||||
|
@ -7,15 +7,10 @@ import SearchFolderForm from './SearchFolderForm';
|
|||||||
|
|
||||||
const ScanModels = () => {
|
const ScanModels = () => {
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection="column" w="100%" gap={4}>
|
<Flex flexDirection="column" w="100%" h="full" gap={4}>
|
||||||
<SearchFolderForm />
|
<SearchFolderForm />
|
||||||
<Flex gap={4}>
|
<Flex gap={4}>
|
||||||
<Flex
|
<Flex overflow="scroll" gap={4} w="100%" h="full">
|
||||||
maxHeight="calc(100vh - 300px)"
|
|
||||||
overflow="scroll"
|
|
||||||
gap={4}
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
<FoundModelsList />
|
<FoundModelsList />
|
||||||
</Flex>
|
</Flex>
|
||||||
<ScanAdvancedAddModels />
|
<ScanAdvancedAddModels />
|
||||||
|
@ -17,7 +17,7 @@ const ImportModelsPanel = () => {
|
|||||||
const handleClickScanTab = useCallback(() => setAddModelTab('scan'), []);
|
const handleClickScanTab = useCallback(() => setAddModelTab('scan'), []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex flexDirection="column" gap={4}>
|
<Flex flexDirection="column" gap={4} h="full">
|
||||||
<InvButtonGroup>
|
<InvButtonGroup>
|
||||||
<InvButton
|
<InvButton
|
||||||
onClick={handleClickAddTab}
|
onClick={handleClickAddTab}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Badge, Divider, Flex } from '@chakra-ui/react';
|
import { Badge, Divider, Flex } from '@chakra-ui/react';
|
||||||
import { useForm } from '@mantine/form';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
import { InvCheckbox } from 'common/components/InvCheckbox/wrapper';
|
||||||
@ -13,6 +12,8 @@ import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import { memo, useCallback, useEffect, useState } from 'react';
|
import { memo, useCallback, useEffect, useState } from 'react';
|
||||||
|
import type { SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { CheckpointModelConfigEntity } from 'services/api/endpoints/models';
|
import type { CheckpointModelConfigEntity } from 'services/api/endpoints/models';
|
||||||
import {
|
import {
|
||||||
@ -44,8 +45,14 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const checkpointEditForm = useForm<CheckpointModelConfig>({
|
const {
|
||||||
initialValues: {
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm<CheckpointModelConfig>({
|
||||||
|
defaultValues: {
|
||||||
model_name: model.model_name ? model.model_name : '',
|
model_name: model.model_name ? model.model_name : '',
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_type: 'main',
|
model_type: 'main',
|
||||||
@ -56,10 +63,7 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
config: model.config ? model.config : '',
|
config: model.config ? model.config : '',
|
||||||
variant: model.variant,
|
variant: model.variant,
|
||||||
},
|
},
|
||||||
validate: {
|
mode: 'onChange',
|
||||||
path: (value) =>
|
|
||||||
value.trim().length === 0 ? 'Must provide a path' : null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleChangeUseCustomConfig = useCallback(
|
const handleChangeUseCustomConfig = useCallback(
|
||||||
@ -67,8 +71,8 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const editModelFormSubmitHandler = useCallback(
|
const onSubmit = useCallback<SubmitHandler<CheckpointModelConfig>>(
|
||||||
(values: CheckpointModelConfig) => {
|
(values) => {
|
||||||
const responseBody = {
|
const responseBody = {
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_name: model.model_name,
|
model_name: model.model_name,
|
||||||
@ -77,7 +81,7 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
updateMainModel(responseBody)
|
updateMainModel(responseBody)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
checkpointEditForm.setValues(payload as CheckpointModelConfig);
|
reset(payload as CheckpointModelConfig, { keepDefaultValues: true });
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
@ -88,7 +92,7 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((_) => {
|
.catch((_) => {
|
||||||
checkpointEditForm.reset();
|
reset();
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
@ -99,14 +103,7 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[dispatch, model.base_model, model.model_name, reset, t, updateMainModel]
|
||||||
checkpointEditForm,
|
|
||||||
dispatch,
|
|
||||||
model.base_model,
|
|
||||||
model.model_name,
|
|
||||||
t,
|
|
||||||
updateMainModel,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -135,42 +132,53 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
|
|||||||
maxHeight={window.innerHeight - 270}
|
maxHeight={window.innerHeight - 270}
|
||||||
overflowY="scroll"
|
overflowY="scroll"
|
||||||
>
|
>
|
||||||
<form
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
onSubmit={checkpointEditForm.onSubmit((values) =>
|
|
||||||
editModelFormSubmitHandler(values)
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||||
<InvControl label={t('modelManager.name')}>
|
<InvControl
|
||||||
<InvInput {...checkpointEditForm.getInputProps('model_name')} />
|
label={t('modelManager.name')}
|
||||||
|
isInvalid={Boolean(errors.model_name)}
|
||||||
|
error={errors.model_name?.message}
|
||||||
|
>
|
||||||
|
<InvInput
|
||||||
|
{...register('model_name', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 3 || 'Must be at least 3 characters',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.description')}>
|
<InvControl label={t('modelManager.description')}>
|
||||||
<InvInput {...checkpointEditForm.getInputProps('description')} />
|
<InvInput {...register('description')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<BaseModelSelect
|
<BaseModelSelect<CheckpointModelConfig>
|
||||||
required
|
control={control}
|
||||||
{...checkpointEditForm.getInputProps('base_model')}
|
name="base_model"
|
||||||
/>
|
/>
|
||||||
<ModelVariantSelect
|
<ModelVariantSelect<CheckpointModelConfig>
|
||||||
required
|
control={control}
|
||||||
{...checkpointEditForm.getInputProps('variant')}
|
name="variant"
|
||||||
|
/>
|
||||||
|
<InvControl
|
||||||
|
label={t('modelManager.modelLocation')}
|
||||||
|
isInvalid={Boolean(errors.path)}
|
||||||
|
error={errors.path?.message}
|
||||||
|
>
|
||||||
|
<InvInput
|
||||||
|
{...register('path', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 0 || 'Must provide a path',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
<InvControl isRequired label={t('modelManager.modelLocation')}>
|
|
||||||
<InvInput {...checkpointEditForm.getInputProps('path')} />
|
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.vaeLocation')}>
|
<InvControl label={t('modelManager.vaeLocation')}>
|
||||||
<InvInput {...checkpointEditForm.getInputProps('vae')} />
|
<InvInput {...register('vae')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
|
|
||||||
<Flex flexDirection="column" gap={2}>
|
<Flex flexDirection="column" gap={2}>
|
||||||
{!useCustomConfig ? (
|
{!useCustomConfig ? (
|
||||||
<CheckpointConfigsSelect
|
<CheckpointConfigsSelect control={control} name="config" />
|
||||||
required
|
|
||||||
{...checkpointEditForm.getInputProps('config')}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<InvControl isRequired label={t('modelManager.config')}>
|
<InvControl isRequired label={t('modelManager.config')}>
|
||||||
<InvInput {...checkpointEditForm.getInputProps('config')} />
|
<InvInput {...register('config')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
)}
|
)}
|
||||||
<InvControl label="Use Custom Config">
|
<InvControl label="Use Custom Config">
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Divider, Flex } from '@chakra-ui/react';
|
import { Divider, Flex } from '@chakra-ui/react';
|
||||||
import { useForm } from '@mantine/form';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
@ -11,6 +10,8 @@ import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
import type { SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { DiffusersModelConfigEntity } from 'services/api/endpoints/models';
|
import type { DiffusersModelConfigEntity } from 'services/api/endpoints/models';
|
||||||
import { useUpdateMainModelsMutation } from 'services/api/endpoints/models';
|
import { useUpdateMainModelsMutation } from 'services/api/endpoints/models';
|
||||||
@ -28,8 +29,14 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const diffusersEditForm = useForm<DiffusersModelConfig>({
|
const {
|
||||||
initialValues: {
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm<DiffusersModelConfig>({
|
||||||
|
defaultValues: {
|
||||||
model_name: model.model_name ? model.model_name : '',
|
model_name: model.model_name ? model.model_name : '',
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_type: 'main',
|
model_type: 'main',
|
||||||
@ -39,14 +46,11 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
|
|||||||
vae: model.vae ? model.vae : '',
|
vae: model.vae ? model.vae : '',
|
||||||
variant: model.variant,
|
variant: model.variant,
|
||||||
},
|
},
|
||||||
validate: {
|
mode: 'onChange',
|
||||||
path: (value) =>
|
|
||||||
value.trim().length === 0 ? 'Must provide a path' : null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const editModelFormSubmitHandler = useCallback(
|
const onSubmit = useCallback<SubmitHandler<DiffusersModelConfig>>(
|
||||||
(values: DiffusersModelConfig) => {
|
(values) => {
|
||||||
const responseBody = {
|
const responseBody = {
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_name: model.model_name,
|
model_name: model.model_name,
|
||||||
@ -56,7 +60,7 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
|
|||||||
updateMainModel(responseBody)
|
updateMainModel(responseBody)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
diffusersEditForm.setValues(payload as DiffusersModelConfig);
|
reset(payload as DiffusersModelConfig, { keepDefaultValues: true });
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
@ -67,7 +71,7 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((_) => {
|
.catch((_) => {
|
||||||
diffusersEditForm.reset();
|
reset();
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
@ -78,14 +82,7 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[dispatch, model.base_model, model.model_name, reset, t, updateMainModel]
|
||||||
diffusersEditForm,
|
|
||||||
dispatch,
|
|
||||||
model.base_model,
|
|
||||||
model.model_name,
|
|
||||||
t,
|
|
||||||
updateMainModel,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -100,31 +97,45 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<form
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
onSubmit={diffusersEditForm.onSubmit((values) =>
|
|
||||||
editModelFormSubmitHandler(values)
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||||
<InvControl label={t('modelManager.name')}>
|
<InvControl
|
||||||
<InvInput {...diffusersEditForm.getInputProps('model_name')} />
|
label={t('modelManager.name')}
|
||||||
|
isInvalid={Boolean(errors.model_name)}
|
||||||
|
error={errors.model_name?.message}
|
||||||
|
>
|
||||||
|
<InvInput
|
||||||
|
{...register('model_name', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 3 || 'Must be at least 3 characters',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.description')}>
|
<InvControl label={t('modelManager.description')}>
|
||||||
<InvInput {...diffusersEditForm.getInputProps('description')} />
|
<InvInput {...register('description')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<BaseModelSelect
|
<BaseModelSelect<DiffusersModelConfig>
|
||||||
required
|
control={control}
|
||||||
{...diffusersEditForm.getInputProps('base_model')}
|
name="base_model"
|
||||||
/>
|
/>
|
||||||
<ModelVariantSelect
|
<ModelVariantSelect<DiffusersModelConfig>
|
||||||
required
|
control={control}
|
||||||
{...diffusersEditForm.getInputProps('variant')}
|
name="variant"
|
||||||
|
/>
|
||||||
|
<InvControl
|
||||||
|
label={t('modelManager.modelLocation')}
|
||||||
|
isInvalid={Boolean(errors.path)}
|
||||||
|
error={errors.path?.message}
|
||||||
|
>
|
||||||
|
<InvInput
|
||||||
|
{...register('path', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 0 || 'Must provide a path',
|
||||||
|
})}
|
||||||
/>
|
/>
|
||||||
<InvControl isRequired label={t('modelManager.modelLocation')}>
|
|
||||||
<InvInput {...diffusersEditForm.getInputProps('path')} />
|
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.vaeLocation')}>
|
<InvControl label={t('modelManager.vaeLocation')}>
|
||||||
<InvInput {...diffusersEditForm.getInputProps('vae')} />
|
<InvInput {...register('vae')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvButton type="submit" isLoading={isLoading}>
|
<InvButton type="submit" isLoading={isLoading}>
|
||||||
{t('modelManager.updateModel')}
|
{t('modelManager.updateModel')}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Divider, Flex } from '@chakra-ui/react';
|
import { Divider, Flex } from '@chakra-ui/react';
|
||||||
import { useForm } from '@mantine/form';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
@ -13,6 +12,8 @@ import {
|
|||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { makeToast } from 'features/system/util/makeToast';
|
import { makeToast } from 'features/system/util/makeToast';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
|
import type { SubmitHandler } from 'react-hook-form';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import type { LoRAModelConfigEntity } from 'services/api/endpoints/models';
|
import type { LoRAModelConfigEntity } from 'services/api/endpoints/models';
|
||||||
import { useUpdateLoRAModelsMutation } from 'services/api/endpoints/models';
|
import { useUpdateLoRAModelsMutation } from 'services/api/endpoints/models';
|
||||||
@ -30,8 +31,14 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const loraEditForm = useForm<LoRAModelConfig>({
|
const {
|
||||||
initialValues: {
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
control,
|
||||||
|
formState: { errors },
|
||||||
|
reset,
|
||||||
|
} = useForm<LoRAModelConfig>({
|
||||||
|
defaultValues: {
|
||||||
model_name: model.model_name ? model.model_name : '',
|
model_name: model.model_name ? model.model_name : '',
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_type: 'lora',
|
model_type: 'lora',
|
||||||
@ -39,14 +46,11 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
|
|||||||
description: model.description ? model.description : '',
|
description: model.description ? model.description : '',
|
||||||
model_format: model.model_format,
|
model_format: model.model_format,
|
||||||
},
|
},
|
||||||
validate: {
|
mode: 'onChange',
|
||||||
path: (value) =>
|
|
||||||
value.trim().length === 0 ? 'Must provide a path' : null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const editModelFormSubmitHandler = useCallback(
|
const onSubmit = useCallback<SubmitHandler<LoRAModelConfig>>(
|
||||||
(values: LoRAModelConfig) => {
|
(values) => {
|
||||||
const responseBody = {
|
const responseBody = {
|
||||||
base_model: model.base_model,
|
base_model: model.base_model,
|
||||||
model_name: model.model_name,
|
model_name: model.model_name,
|
||||||
@ -56,7 +60,7 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
|
|||||||
updateLoRAModel(responseBody)
|
updateLoRAModel(responseBody)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.then((payload) => {
|
.then((payload) => {
|
||||||
loraEditForm.setValues(payload as LoRAModelConfig);
|
reset(payload as LoRAModelConfig, { keepDefaultValues: true });
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
@ -67,7 +71,7 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.catch((_) => {
|
.catch((_) => {
|
||||||
loraEditForm.reset();
|
reset();
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast(
|
addToast(
|
||||||
makeToast({
|
makeToast({
|
||||||
@ -78,14 +82,7 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[
|
[dispatch, model.base_model, model.model_name, reset, t, updateLoRAModel]
|
||||||
dispatch,
|
|
||||||
loraEditForm,
|
|
||||||
model.base_model,
|
|
||||||
model.model_name,
|
|
||||||
t,
|
|
||||||
updateLoRAModel,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -101,21 +98,39 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<form
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
onSubmit={loraEditForm.onSubmit((values) =>
|
|
||||||
editModelFormSubmitHandler(values)
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
<Flex flexDirection="column" overflowY="scroll" gap={4}>
|
||||||
<InvControl label={t('modelManager.name')}>
|
<InvControl
|
||||||
<InvInput {...loraEditForm.getInputProps('model_name')} />
|
label={t('modelManager.name')}
|
||||||
|
isInvalid={Boolean(errors.model_name)}
|
||||||
|
error={errors.model_name?.message}
|
||||||
|
>
|
||||||
|
<InvInput
|
||||||
|
{...register('model_name', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 3 || 'Must be at least 3 characters',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvControl label={t('modelManager.description')}>
|
<InvControl label={t('modelManager.description')}>
|
||||||
<InvInput {...loraEditForm.getInputProps('description')} />
|
<InvInput {...register('description')} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<BaseModelSelect {...loraEditForm.getInputProps('base_model')} />
|
<BaseModelSelect<LoRAModelConfig>
|
||||||
<InvControl label={t('modelManager.modelLocation')}>
|
control={control}
|
||||||
<InvInput {...loraEditForm.getInputProps('path')} />
|
name="base_model"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InvControl
|
||||||
|
label={t('modelManager.modelLocation')}
|
||||||
|
isInvalid={Boolean(errors.path)}
|
||||||
|
error={errors.path?.message}
|
||||||
|
>
|
||||||
|
<InvInput
|
||||||
|
{...register('path', {
|
||||||
|
validate: (value) =>
|
||||||
|
value.trim().length > 0 || 'Must provide a path',
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
<InvButton type="submit" isLoading={isLoading}>
|
<InvButton type="submit" isLoading={isLoading}>
|
||||||
{t('modelManager.updateModel')}
|
{t('modelManager.updateModel')}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
||||||
import type {
|
import type {
|
||||||
|
InvSelectOnChange,
|
||||||
InvSelectOption,
|
InvSelectOption,
|
||||||
InvSelectProps,
|
|
||||||
} from 'common/components/InvSelect/types';
|
} from 'common/components/InvSelect/types';
|
||||||
|
import { typedMemo } from 'common/util/typedMemo';
|
||||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||||
import { memo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import type { UseControllerProps } from 'react-hook-form';
|
||||||
|
import { useController } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { AnyModelConfig } from 'services/api/types';
|
||||||
|
|
||||||
const options: InvSelectOption[] = [
|
const options: InvSelectOption[] = [
|
||||||
{ value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] },
|
{ value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] },
|
||||||
@ -15,15 +19,26 @@ const options: InvSelectOption[] = [
|
|||||||
{ value: 'sdxl-refiner', label: MODEL_TYPE_MAP['sdxl-refiner'] },
|
{ value: 'sdxl-refiner', label: MODEL_TYPE_MAP['sdxl-refiner'] },
|
||||||
];
|
];
|
||||||
|
|
||||||
type BaseModelSelectProps = Omit<InvSelectProps, 'options'>;
|
const BaseModelSelect = <T extends AnyModelConfig>(
|
||||||
|
props: UseControllerProps<T>
|
||||||
const BaseModelSelect = (props: BaseModelSelectProps) => {
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { field } = useController(props);
|
||||||
|
const value = useMemo(
|
||||||
|
() => options.find((o) => o.value === field.value),
|
||||||
|
[field.value]
|
||||||
|
);
|
||||||
|
const onChange = useCallback<InvSelectOnChange>(
|
||||||
|
(v) => {
|
||||||
|
field.onChange(v?.value);
|
||||||
|
},
|
||||||
|
[field]
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<InvControl label={t('modelManager.baseModel')}>
|
<InvControl label={t('modelManager.baseModel')}>
|
||||||
<InvSelect options={options} {...props} />
|
<InvSelect value={value} options={options} onChange={onChange} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(BaseModelSelect);
|
export default typedMemo(BaseModelSelect);
|
||||||
|
@ -2,29 +2,44 @@ import type { ChakraProps } from '@chakra-ui/react';
|
|||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
||||||
import type {
|
import type {
|
||||||
|
InvSelectOnChange,
|
||||||
InvSelectOption,
|
InvSelectOption,
|
||||||
InvSelectProps,
|
|
||||||
} from 'common/components/InvSelect/types';
|
} from 'common/components/InvSelect/types';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useController, type UseControllerProps } from 'react-hook-form';
|
||||||
import { useGetCheckpointConfigsQuery } from 'services/api/endpoints/models';
|
import { useGetCheckpointConfigsQuery } from 'services/api/endpoints/models';
|
||||||
|
import type { CheckpointModelConfig } from 'services/api/types';
|
||||||
type CheckpointConfigSelectProps = Omit<InvSelectProps, 'options'>;
|
|
||||||
|
|
||||||
const sx: ChakraProps['sx'] = { w: 'full' };
|
const sx: ChakraProps['sx'] = { w: 'full' };
|
||||||
|
|
||||||
const CheckpointConfigsSelect = (props: CheckpointConfigSelectProps) => {
|
const CheckpointConfigsSelect = (
|
||||||
|
props: UseControllerProps<CheckpointModelConfig>
|
||||||
|
) => {
|
||||||
const { data } = useGetCheckpointConfigsQuery();
|
const { data } = useGetCheckpointConfigsQuery();
|
||||||
const options = useMemo<InvSelectOption[]>(
|
const options = useMemo<InvSelectOption[]>(
|
||||||
() => (data ? data.map((i) => ({ label: i, value: i })) : []),
|
() => (data ? data.map((i) => ({ label: i, value: i })) : []),
|
||||||
[data]
|
[data]
|
||||||
);
|
);
|
||||||
|
const { field } = useController(props);
|
||||||
|
const value = useMemo(
|
||||||
|
() => options.find((o) => o.value === field.value),
|
||||||
|
[field.value, options]
|
||||||
|
);
|
||||||
|
const onChange = useCallback<InvSelectOnChange>(
|
||||||
|
(v) => {
|
||||||
|
field.onChange(v?.value);
|
||||||
|
},
|
||||||
|
[field]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvControl label="Config File">
|
<InvControl label="Config File">
|
||||||
<InvSelect
|
<InvSelect
|
||||||
placeholder="Select A Config File"
|
placeholder="Select A Config File"
|
||||||
|
value={value}
|
||||||
options={options}
|
options={options}
|
||||||
|
onChange={onChange}
|
||||||
sx={sx}
|
sx={sx}
|
||||||
{...props}
|
|
||||||
/>
|
/>
|
||||||
</InvControl>
|
</InvControl>
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||||
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
import { InvSelect } from 'common/components/InvSelect/InvSelect';
|
||||||
import type {
|
import type {
|
||||||
|
InvSelectOnChange,
|
||||||
InvSelectOption,
|
InvSelectOption,
|
||||||
InvSelectProps,
|
|
||||||
} from 'common/components/InvSelect/types';
|
} from 'common/components/InvSelect/types';
|
||||||
import { memo } from 'react';
|
import { typedMemo } from 'common/util/typedMemo';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import type { UseControllerProps } from 'react-hook-form';
|
||||||
|
import { useController } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type {
|
||||||
|
CheckpointModelConfig,
|
||||||
|
DiffusersModelConfig,
|
||||||
|
} from 'services/api/types';
|
||||||
|
|
||||||
const options: InvSelectOption[] = [
|
const options: InvSelectOption[] = [
|
||||||
{ value: 'normal', label: 'Normal' },
|
{ value: 'normal', label: 'Normal' },
|
||||||
@ -13,15 +20,28 @@ const options: InvSelectOption[] = [
|
|||||||
{ value: 'depth', label: 'Depth' },
|
{ value: 'depth', label: 'Depth' },
|
||||||
];
|
];
|
||||||
|
|
||||||
type VariantSelectProps = Omit<InvSelectProps, 'options'>;
|
const ModelVariantSelect = <
|
||||||
|
T extends CheckpointModelConfig | DiffusersModelConfig,
|
||||||
const ModelVariantSelect = (props: VariantSelectProps) => {
|
>(
|
||||||
|
props: UseControllerProps<T>
|
||||||
|
) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { field } = useController(props);
|
||||||
|
const value = useMemo(
|
||||||
|
() => options.find((o) => o.value === field.value),
|
||||||
|
[field.value]
|
||||||
|
);
|
||||||
|
const onChange = useCallback<InvSelectOnChange>(
|
||||||
|
(v) => {
|
||||||
|
field.onChange(v?.value);
|
||||||
|
},
|
||||||
|
[field]
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<InvControl label={t('modelManager.variant')}>
|
<InvControl label={t('modelManager.variant')}>
|
||||||
<InvSelect options={options} {...props} />
|
<InvSelect value={value} options={options} onChange={onChange} />
|
||||||
</InvControl>
|
</InvControl>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(ModelVariantSelect);
|
export default typedMemo(ModelVariantSelect);
|
||||||
|
@ -4,7 +4,11 @@ import { badgeTheme } from 'common/components/InvBadge/theme';
|
|||||||
import { buttonTheme } from 'common/components/InvButton/theme';
|
import { buttonTheme } from 'common/components/InvButton/theme';
|
||||||
import { cardTheme } from 'common/components/InvCard/theme';
|
import { cardTheme } from 'common/components/InvCard/theme';
|
||||||
import { checkboxTheme } from 'common/components/InvCheckbox/theme';
|
import { checkboxTheme } from 'common/components/InvCheckbox/theme';
|
||||||
import { formLabelTheme, formTheme } from 'common/components/InvControl/theme';
|
import {
|
||||||
|
formErrorTheme,
|
||||||
|
formLabelTheme,
|
||||||
|
formTheme,
|
||||||
|
} from 'common/components/InvControl/theme';
|
||||||
import { editableTheme } from 'common/components/InvEditable/theme';
|
import { editableTheme } from 'common/components/InvEditable/theme';
|
||||||
import { headingTheme } from 'common/components/InvHeading/theme';
|
import { headingTheme } from 'common/components/InvHeading/theme';
|
||||||
import { inputTheme } from 'common/components/InvInput/theme';
|
import { inputTheme } from 'common/components/InvInput/theme';
|
||||||
@ -112,6 +116,7 @@ export const theme: ThemeOverride = {
|
|||||||
Text: textTheme,
|
Text: textTheme,
|
||||||
Textarea: textareaTheme,
|
Textarea: textareaTheme,
|
||||||
Tooltip: tooltipTheme,
|
Tooltip: tooltipTheme,
|
||||||
|
FormError: formErrorTheme,
|
||||||
},
|
},
|
||||||
space: space,
|
space: space,
|
||||||
sizes: space,
|
sizes: space,
|
||||||
|
Loading…
Reference in New Issue
Block a user