diff --git a/frontend/src/app/invokeai.d.ts b/frontend/src/app/invokeai.d.ts index c07db54c07..4fa10962e4 100644 --- a/frontend/src/app/invokeai.d.ts +++ b/frontend/src/app/invokeai.d.ts @@ -165,6 +165,11 @@ export declare type Model = { status: ModelStatus; description: string; weights: string; + config?: string; + vae?: string; + width?: number; + height?: number; + default?: boolean; }; export declare type ModelList = Record; @@ -259,3 +264,24 @@ export declare type UploadOutpaintingMergeImagePayload = { dataURL: string; name: string; }; + +export declare type InvokeModelConfigProps = { + name: string; + description?: string; + config: string; + weights: string; + vae?: string; + width: number; + height: number; + default?: boolean; +}; + +export declare type OnFoundModelResponse = { + search_folder: string; + found_models: FoundModel[]; +}; + +export declare type FoundModel = { + name: string; + location: string; +}; diff --git a/frontend/src/app/store.ts b/frontend/src/app/store.ts index 73423f3ff2..8f452f3dda 100644 --- a/frontend/src/app/store.ts +++ b/frontend/src/app/store.ts @@ -44,6 +44,7 @@ const systemBlacklist = [ 'socketId', 'totalIterations', 'totalSteps', + 'openModel', ].map((blacklistItem) => `system.${blacklistItem}`); const galleryBlacklist = [ diff --git a/frontend/src/features/system/components/ModelManager/ModelEdit.tsx b/frontend/src/features/system/components/ModelManager/ModelEdit.tsx index 7046cb16d6..4a5a7f9edf 100644 --- a/frontend/src/features/system/components/ModelManager/ModelEdit.tsx +++ b/frontend/src/features/system/components/ModelManager/ModelEdit.tsx @@ -1,5 +1,330 @@ -import React from 'react'; +import { createSelector } from '@reduxjs/toolkit'; +import { Field, FieldInputProps, Formik, FormikProps } from 'formik'; +import { RootState, useAppDispatch, useAppSelector } from 'app/store'; +import { addNewModel, } from 'app/socketio/actions'; +import { InvokeModelConfigProps } from 'app/invokeai'; +import { SystemState } from 'features/system/store/systemSlice'; +import { + Button, + Flex, + FormControl, + FormErrorMessage, + FormHelperText, + FormLabel, + HStack, + Input, + NumberDecrementStepper, + NumberIncrementStepper, + NumberInput, + NumberInputField, + NumberInputStepper, + Text, + VStack, +} from '@chakra-ui/react'; + + + export default function ModelEdit() { - return
ModelEdit
; + + const MIN_MODEL_SIZE = 64; + const MAX_MODEL_SIZE = 2048; + const modelListSelector = createSelector( + (state: RootState) => state.system, + (system: SystemState) => { + const models = _.map(system.model_list, (model, key) => { + return { name: key, ...model }; + }); + + const selectedModel = models.find((model) => model.name === system.openModel); + + return { + openedModel: { + name:selectedModel?.name, + description:selectedModel?.description, + config:selectedModel?.config, + weights:selectedModel?.weights, + vae:selectedModel?.vae, + width:selectedModel?.width, + height:selectedModel?.height, + default:selectedModel?.default, + } + }; + } + ); + + + const { openedModel } = useAppSelector(modelListSelector); + + + const viewModelFormValues: InvokeModelConfigProps = { + name: openedModel.name || '', + description: openedModel.description || '', + config: openedModel.config || '', + weights: openedModel.weights || '', + vae: openedModel.vae || '', + width: openedModel.width || 0, + height: openedModel.height || 0, + default: openedModel.default || false, + }; + + const dispatch = useAppDispatch(); + + const addModelFormSubmitHandler = (values: InvokeModelConfigProps) => { + dispatch(addNewModel(values)); + onClose(); + }; + + function hasWhiteSpace(s: string) { + return /\\s/g.test(s); + } + + function baseValidation(value: string) { + let error; + if (hasWhiteSpace(value)) error = 'Cannot use spaces'; + return error; + } + + if (!openedModel) { + return ( + 'No model selected' + + )} else { + + return ( + + {({ handleSubmit, errors, touched }) => ( +
+ + + Manual + + {/* Name */} + + Name + + + {!!errors.name && touched.name ? ( + {errors.name} + ) : ( + + Enter a name for your model + + )} + + + + {/* Description */} + + Description + + + {!!errors.description && touched.description ? ( + + {errors.description} + + ) : ( + + Add a description for your model + + )} + + + + {/* Config */} + + Config + + + {!!errors.config && touched.config ? ( + {errors.config} + ) : ( + + Path to the config file of your model. + + )} + + + + {/* Weights */} + + Model Location + + + {!!errors.weights && touched.weights ? ( + + {errors.weights} + + ) : ( + + Path to where your model is located. + + )} + + + + {/* VAE */} + + VAE Location + + + {!!errors.vae && touched.vae ? ( + {errors.vae} + ) : ( + + Path to where your VAE is located. + + )} + + + + + {/* Width */} + + Width + + + {({ + field, + form, + }: { + field: FieldInputProps; + form: FormikProps; + }) => ( + + form.setFieldValue( + field.name, + Number(value) + ) + } + > + + + + + + + )} + + + {!!errors.width && touched.width ? ( + + {errors.width} + + ) : ( + + Default width of your model. + + )} + + + + {/* Height */} + + Height + + + {({ + field, + form, + }: { + field: FieldInputProps; + form: FormikProps; + }) => ( + + form.setFieldValue( + field.name, + Number(value) + ) + } + > + + + + + + + )} + + + {!!errors.height && touched.height ? ( + + {errors.height} + + ) : ( + + Default height of your model. + + )} + + + + + + +
+ )} +
+)} } diff --git a/frontend/src/features/system/components/ModelManager/ModelListItem.tsx b/frontend/src/features/system/components/ModelManager/ModelListItem.tsx index 6151beaf59..b8eea91952 100644 --- a/frontend/src/features/system/components/ModelManager/ModelListItem.tsx +++ b/frontend/src/features/system/components/ModelManager/ModelListItem.tsx @@ -1,4 +1,4 @@ -import { DeleteIcon } from '@chakra-ui/icons'; +import { EditIcon, DeleteIcon } from '@chakra-ui/icons'; import { Button, Flex, @@ -12,6 +12,7 @@ import { deleteModel, requestModelChange } from 'app/socketio/actions'; import { RootState } from 'app/store'; import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import IAIAlertDialog from 'common/components/IAIAlertDialog'; +import { setOpenModel } from 'features/system/store/systemSlice'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -29,12 +30,17 @@ export default function ModelListItem(props: ModelListItemProps) { const { t } = useTranslation(); const dispatch = useAppDispatch(); + const { name, status, description } = props; const handleChangeModel = () => { dispatch(requestModelChange(name)); }; + const openModel = () => { + dispatch(setOpenModel(name)); + }; + const handleModelDelete = () => { dispatch(deleteModel(name)); }; @@ -68,6 +74,14 @@ export default function ModelListItem(props: ModelListItemProps) { > {t('modelmanager:load')} + } + size={'sm'} + onClick={openModel} + aria-label="Modify Config" + isDisabled={status === 'active' || isProcessing || !isConnected} + className=" modal-close-btn" + /> { state.foundModels = action.payload; }, + setOpenModel: (state, action: PayloadAction) => { + state.openModel = action.payload; + }, }, }); @@ -268,6 +273,7 @@ export const { setProcessingIndeterminateTask, setSearchFolder, setFoundModels, + setOpenModel } = systemSlice.actions; export default systemSlice.reducer;