From 52f9749bf5ad11b251f78c51d5817994e72391d4 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Fri, 29 Dec 2023 20:43:20 +1100
Subject: [PATCH] feat(ui): partial rebuild of model manager internal logic
---
invokeai/frontend/web/package.json | 3 +-
invokeai/frontend/web/pnpm-lock.yaml | 14 +-
.../components/InvControl/InvControl.tsx | 51 +++---
.../src/common/components/InvControl/theme.ts | 28 ++-
.../src/common/components/InvControl/types.ts | 1 +
.../AddModelsPanel/AdvancedAddCheckpoint.tsx | 166 +++++++++---------
.../AddModelsPanel/AdvancedAddDiffusers.tsx | 155 ++++++++--------
.../subpanels/AddModelsPanel/ScanModels.tsx | 9 +-
.../subpanels/ImportModelsPanel.tsx | 2 +-
.../ModelManagerPanel/CheckpointModelEdit.tsx | 90 +++++-----
.../ModelManagerPanel/DiffusersModelEdit.tsx | 83 +++++----
.../ModelManagerPanel/LoRAModelEdit.tsx | 75 ++++----
.../subpanels/shared/BaseModelSelect.tsx | 29 ++-
.../shared/CheckpointConfigsSelect.tsx | 27 ++-
.../subpanels/shared/ModelVariantSelect.tsx | 34 +++-
invokeai/frontend/web/src/theme/theme.ts | 7 +-
16 files changed, 457 insertions(+), 317 deletions(-)
diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json
index 7b9446b2f3..3fda030089 100644
--- a/invokeai/frontend/web/package.json
+++ b/invokeai/frontend/web/package.json
@@ -66,7 +66,7 @@
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@fontsource-variable/inter": "^5.0.16",
- "@mantine/form": "^6.0.21",
+ "@mantine/form": "6.0.21",
"@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "^2.0.1",
"@roarr/browser-log-writer": "^1.3.0",
@@ -89,6 +89,7 @@
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-error-boundary": "^4.0.12",
+ "react-hook-form": "^7.49.2",
"react-hotkeys-hook": "4.4.1",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml
index 8997336b36..573f921ade 100644
--- a/invokeai/frontend/web/pnpm-lock.yaml
+++ b/invokeai/frontend/web/pnpm-lock.yaml
@@ -48,7 +48,7 @@ dependencies:
specifier: ^5.0.16
version: 5.0.16
'@mantine/form':
- specifier: ^6.0.21
+ specifier: 6.0.21
version: 6.0.21(react@18.2.0)
'@nanostores/react':
specifier: ^0.7.1
@@ -116,6 +116,9 @@ dependencies:
react-error-boundary:
specifier: ^4.0.12
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:
specifier: 4.4.1
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)
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):
resolution: {integrity: sha512-sClBMBioFEgFGYLTWWRKvhxcCx1DRznd+wkFHwQZspnRBkHTgruKIHptlK/U/2DPX8BhHoRGzpMVWUXMmdZlmw==}
peerDependencies:
diff --git a/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx b/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx
index 4223c147ef..499b1b25ae 100644
--- a/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx
+++ b/invokeai/frontend/web/src/common/components/InvControl/InvControl.tsx
@@ -1,6 +1,7 @@
import {
Flex,
FormControl as ChakraFormControl,
+ FormErrorMessage as ChakraFormErrorMessage,
FormHelperText as ChakraFormHelperText,
forwardRef,
} from '@chakra-ui/react';
@@ -22,36 +23,38 @@ export const InvControl = memo(
isDisabled,
labelProps,
label,
+ error,
...formControlProps
} = props;
const ctx = useContext(InvControlGroupContext);
- if (helperText) {
- return (
-
-
- {label && (
-
- {label}
-
- )}
- {children}
-
+ return (
+
+
+ {label && (
+
+ {label}
+
+ )}
+ {children}
+
+ {helperText && (
{helperText}
-
- );
- }
+ )}
+ {error && {error}}
+
+ );
return (
{
+const formBaseStyle = defineFormPartsStyle((props) => {
return {
container: {
display: 'flex',
@@ -19,7 +24,7 @@ const formBaseStyle = definePartsStyle((props) => {
};
});
-const withHelperText = definePartsStyle(() => ({
+const withHelperText = defineFormPartsStyle(() => ({
container: {
flexDirection: 'column',
gap: 0,
@@ -41,7 +46,7 @@ const withHelperText = definePartsStyle(() => ({
},
}));
-export const formTheme = defineMultiStyleConfig({
+export const formTheme = defineFormMultiStyleConfig({
baseStyle: formBaseStyle,
variants: {
withHelperText,
@@ -73,3 +78,14 @@ const formLabelBaseStyle = defineStyle(() => {
export const formLabelTheme = defineStyleConfig({
baseStyle: formLabelBaseStyle,
});
+
+const { defineMultiStyleConfig: defineFormErrorMultiStyleConfig } =
+ createMultiStyleConfigHelpers(formErrorParts.keys);
+
+export const formErrorTheme = defineFormErrorMultiStyleConfig({
+ baseStyle: {
+ text: {
+ color: 'error.300',
+ },
+ },
+});
diff --git a/invokeai/frontend/web/src/common/components/InvControl/types.ts b/invokeai/frontend/web/src/common/components/InvControl/types.ts
index ffe35f24cd..56cd8e6f1a 100644
--- a/invokeai/frontend/web/src/common/components/InvControl/types.ts
+++ b/invokeai/frontend/web/src/common/components/InvControl/types.ts
@@ -7,6 +7,7 @@ import type { Feature } from 'common/components/IAIInformationalPopover/constant
export type InvControlProps = ChakraFormControlProps & {
label?: string;
helperText?: string;
+ error?: string;
feature?: Feature;
renderInfoPopoverInPortal?: boolean;
labelProps?: Omit<
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx
index c199c7fb1c..26798c7574 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx
@@ -1,5 +1,4 @@
import { Flex } from '@chakra-ui/react';
-import { useForm } from '@mantine/form';
import { useAppDispatch } from 'app/store/storeHooks';
import { InvButton } from 'common/components/InvButton/InvButton';
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 type { CSSProperties, FocusEventHandler } 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 { useAddMainModelsMutation } from 'services/api/endpoints/models';
import type { CheckpointModelConfig } from 'services/api/types';
@@ -28,8 +29,16 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
const dispatch = useAppDispatch();
const { model_path } = props;
- const advancedAddCheckpointForm = useForm({
- initialValues: {
+ const {
+ register,
+ handleSubmit,
+ control,
+ getValues,
+ setValue,
+ formState: { errors },
+ reset,
+ } = useForm({
+ defaultValues: {
model_name: model_path ? getModelName(model_path) : '',
base_model: 'sd-1',
model_type: 'main',
@@ -41,64 +50,64 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
variant: 'normal',
config: 'configs\\stable-diffusion\\v1-inference.yaml',
},
+ mode: 'onChange',
});
const [addMainModel] = useAddMainModelsMutation();
const [useCustomConfig, setUseCustomConfig] = useState(false);
- const advancedAddCheckpointFormHandler = (values: CheckpointModelConfig) => {
- addMainModel({
- body: values,
- })
- .unwrap()
- .then((_) => {
- dispatch(
- addToast(
- makeToast({
- title: t('modelManager.modelAdded', {
- modelName: values.model_name,
- }),
- status: 'success',
- })
- )
- );
- advancedAddCheckpointForm.reset();
-
- // Close Advanced Panel in Scan Models tab
- if (model_path) {
- dispatch(setAdvancedAddScanModel(null));
- }
+ const onSubmit = useCallback>(
+ (values) => {
+ addMainModel({
+ body: values,
})
- .catch((error) => {
- if (error) {
+ .unwrap()
+ .then((_) => {
dispatch(
addToast(
makeToast({
- title: t('toast.modelAddFailed'),
- status: 'error',
+ title: t('modelManager.modelAdded', {
+ modelName: values.model_name,
+ }),
+ status: 'success',
})
)
);
- }
- });
- };
+ reset();
- const handleBlurModelLocation: FocusEventHandler =
- useCallback(
- (e) => {
- if (advancedAddCheckpointForm.values['model_name'] === '') {
- const modelName = getModelName(e.currentTarget.value);
- if (modelName) {
- advancedAddCheckpointForm.setFieldValue(
- 'model_name',
- modelName as string
+ // Close Advanced Panel in Scan Models tab
+ if (model_path) {
+ dispatch(setAdvancedAddScanModel(null));
+ }
+ })
+ .catch((error) => {
+ if (error) {
+ dispatch(
+ addToast(
+ makeToast({
+ title: t('toast.modelAddFailed'),
+ status: 'error',
+ })
+ )
);
}
+ });
+ },
+ [addMainModel, dispatch, model_path, reset, t]
+ );
+
+ const onBlur: FocusEventHandler = useCallback(
+ (e) => {
+ if (getValues().model_name === '') {
+ const modelName = getModelName(e.currentTarget.value);
+ if (modelName) {
+ setValue('model_name', modelName as string);
}
- },
- [advancedAddCheckpointForm]
- );
+ }
+ },
+ [getValues, setValue]
+ );
const handleChangeUseCustomConfig = useCallback(
() => setUseCustomConfig((prev) => !prev),
@@ -106,56 +115,53 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
);
return (
-