diff --git a/frontend/public/locales/modelmanager/en.json b/frontend/public/locales/modelmanager/en.json index 48b2821ec7..aa824590af 100644 --- a/frontend/public/locales/modelmanager/en.json +++ b/frontend/public/locales/modelmanager/en.json @@ -11,7 +11,7 @@ "manual": "Manual", "name": "Name", "nameValidationMsg": "Enter a name for your model", - "description": "description", + "description": "Description", "descriptionValidationMsg": "Add a description for your model", "config": "Config", "configValidationMsg": "Path to the config file of your model.", @@ -24,6 +24,7 @@ "height": "Height", "heightValidationMsg": "Default height of your model.", "addModel": "Add Model", + "updateModel": "Update Model", "availableModels": "Available Models", "search": "Search", "load": "Load", diff --git a/frontend/src/app/invokeai.d.ts b/frontend/src/app/invokeai.d.ts index af292058c2..d5ee6adbbc 100644 --- a/frontend/src/app/invokeai.d.ts +++ b/frontend/src/app/invokeai.d.ts @@ -180,14 +180,14 @@ export declare type FoundModel = { }; export declare type InvokeModelConfigProps = { - name: string; - description: string; - config: string; - weights: string; - vae: string; - width: number; - height: number; - default: boolean; + name: string | undefined; + description: string | undefined; + config: string | undefined; + weights: string | undefined; + vae: string | undefined; + width: number | undefined; + height: number | undefined; + default: boolean | undefined; }; /** diff --git a/frontend/src/features/system/components/ModelManager/ModelEdit.tsx b/frontend/src/features/system/components/ModelManager/ModelEdit.tsx index 4738b0a2a7..11cbf8e2fe 100644 --- a/frontend/src/features/system/components/ModelManager/ModelEdit.tsx +++ b/frontend/src/features/system/components/ModelManager/ModelEdit.tsx @@ -1,11 +1,30 @@ -import { createSelector, Dictionary } from '@reduxjs/toolkit'; +import { createSelector } from '@reduxjs/toolkit'; import type { RootState } from 'app/store'; -import { useAppSelector } from 'app/storeHooks'; +import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { systemSelector } from 'features/system/store/systemSelectors'; import React, { useEffect, useState } from 'react'; import _ from 'lodash'; -import { Model } from 'app/invokeai'; -import { Flex, Spacer, Text } from '@chakra-ui/react'; +import type { InvokeModelConfigProps } from 'app/invokeai'; +import { + Button, + Flex, + FormControl, + FormErrorMessage, + FormHelperText, + FormLabel, + Input, + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, + Text, + VStack, +} from '@chakra-ui/react'; +import { Field, Formik } from 'formik'; +import type { FieldInputProps, FormikProps } from 'formik'; +import { useTranslation } from 'react-i18next'; +import { addNewModel } from 'app/socketio/actions'; const selector = createSelector( [systemSelector], @@ -23,38 +42,271 @@ const selector = createSelector( } ); +const MIN_MODEL_SIZE = 64; +const MAX_MODEL_SIZE = 2048; + export default function ModelEdit() { const { openModel, model_list } = useAppSelector(selector); - const [openedModel, setOpenedModel] = useState(); + const isProcessing = useAppSelector( + (state: RootState) => state.system.isProcessing + ); + + const dispatch = useAppDispatch(); + + const { t } = useTranslation(); + + const [editModelFormValues, setEditModelFormValues] = + useState({ + name: '', + description: '', + config: 'configs/stable-diffusion/v1-inference.yaml', + weights: '', + vae: '', + width: 512, + height: 512, + default: false, + }); useEffect(() => { if (openModel) { const retrievedModel = _.pickBy(model_list, (val, key) => { return _.isEqual(key, openModel); }); - setOpenedModel(retrievedModel[openModel]); + setEditModelFormValues({ + name: openModel, + description: retrievedModel[openModel]?.description, + config: retrievedModel[openModel]?.config, + weights: retrievedModel[openModel]?.weights, + vae: retrievedModel[openModel]?.vae, + width: retrievedModel[openModel]?.width, + height: retrievedModel[openModel]?.height, + default: retrievedModel[openModel]?.default, + }); } }, [model_list, openModel]); - console.log(openedModel); + const editModelFormSubmitHandler = (values: InvokeModelConfigProps) => { + dispatch(addNewModel(values)); + }; - return ( - - + return openModel ? ( + + {openModel} - {openedModel?.status} - - {openedModel?.config} - {openedModel?.description} - {openedModel?.height} - {openedModel?.width} - {openedModel?.default} - {openedModel?.vae} - {openedModel?.weights} + + + {({ handleSubmit, errors, touched }) => ( +
+ + {/* Description */} + + + {t('modelmanager:description')} + + + + {!!errors.description && touched.description ? ( + {errors.description} + ) : ( + + {t('modelmanager:descriptionValidationMsg')} + + )} + + + + {/* Config */} + + + {t('modelmanager:config')} + + + + {!!errors.config && touched.config ? ( + {errors.config} + ) : ( + + {t('modelmanager:configValidationMsg')} + + )} + + + + {/* Weights */} + + + {t('modelmanager:modelLocation')} + + + + {!!errors.weights && touched.weights ? ( + {errors.weights} + ) : ( + + {t('modelmanager:modelLocationValidationMsg')} + + )} + + + + {/* VAE */} + + + {t('modelmanager:vaeLocation')} + + + + {!!errors.vae && touched.vae ? ( + {errors.vae} + ) : ( + + {t('modelmanager:vaeLocationValidationMsg')} + + )} + + + + + {/* Width */} + + + {t('modelmanager:width')} + + + + {({ + field, + form, + }: { + field: FieldInputProps; + form: FormikProps; + }) => ( + + form.setFieldValue(field.name, Number(value)) + } + > + + + + + + + )} + + + {!!errors.width && touched.width ? ( + {errors.width} + ) : ( + + {t('modelmanager:widthValidationMsg')} + + )} + + + + {/* Height */} + + + {t('modelmanager:height')} + + + + {({ + field, + form, + }: { + field: FieldInputProps; + form: FormikProps; + }) => ( + + form.setFieldValue(field.name, Number(value)) + } + > + + + + + + + )} + + + {!!errors.height && touched.height ? ( + {errors.height} + ) : ( + + {t('modelmanager:heightValidationMsg')} + + )} + + + + + + +
+ )} +
+ ) : ( + + + Pick A Model To Edit + + ); }