diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 4d40f7105c..dfb0103dff 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -696,6 +696,7 @@ "addNewModel": "Add New Model", "addSelected": "Add Selected", "advanced": "Advanced", + "advancedImportInfo": "The advanced tab allows for manual configuration of core model settings. Only use this tab if you are confident that you know the correct model type and configuration for the selected model.", "allModels": "All Models", "alpha": "Alpha", "availableModels": "Available Models", diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImport.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImport.tsx new file mode 100644 index 0000000000..9ec39a0649 --- /dev/null +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImport.tsx @@ -0,0 +1,56 @@ +import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; +import { Combobox, Flex, FormLabel,Text } from '@invoke-ai/ui-library'; +import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; +import { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { z } from 'zod'; + +import { AdvancedImportCheckpoint } from './AdvancedImportCheckpoint'; +import { AdvancedImportDiffusers } from './AdvancedImportDiffusers'; + +export const zManualAddMode = z.enum(['diffusers', 'checkpoint']); +export type ManualAddMode = z.infer; +export const isManualAddMode = (v: unknown): v is ManualAddMode => zManualAddMode.safeParse(v).success; + +export const AdvancedImport = () => { + const [advancedAddMode, setAdvancedAddMode] = useState('diffusers'); + + const { t } = useTranslation(); + const handleChange: ComboboxOnChange = useCallback((v) => { + if (!isManualAddMode(v?.value)) { + return; + } + setAdvancedAddMode(v.value); + }, []); + + const options: ComboboxOption[] = useMemo( + () => [ + { label: t('modelManager.diffusersModels'), value: 'diffusers' }, + { label: t('modelManager.checkpointOrSafetensors'), value: 'checkpoint' }, + ], + [t] + ); + + const value = useMemo(() => options.find((o) => o.value === advancedAddMode), [options, advancedAddMode]); + + return ( + + + + + {t('modelManager.modelType')} + + + + {t('modelManager.advancedImportInfo')} + + + + + {advancedAddMode === 'diffusers' && } + {advancedAddMode === 'checkpoint' && } + + + + ); +}; diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportCheckpoint.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportCheckpoint.tsx new file mode 100644 index 0000000000..bf22ef12d1 --- /dev/null +++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportCheckpoint.tsx @@ -0,0 +1,160 @@ +import { + Button, + Checkbox, + Flex, + FormControl, + FormErrorMessage, + FormLabel, + Input, + Textarea, +} from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { addToast } from 'features/system/store/systemSlice'; +import { makeToast } from 'features/system/util/makeToast'; +import type { CSSProperties } from 'react'; +import { 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, DiffusersModelConfig } from 'services/api/types'; + +import BaseModelSelect from './BaseModelSelect'; +import CheckpointConfigsSelect from './CheckpointConfigsSelect'; +import ModelVariantSelect from './ModelVariantSelect'; + +export const AdvancedImportCheckpoint = () => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + + const [addMainModel] = useAddMainModelsMutation(); + + const [useCustomConfig, setUseCustomConfig] = useState(false); + + const { + register, + handleSubmit, + control, + formState: { errors }, + reset, + } = useForm({ + defaultValues: { + name: '', + base: 'sd-1', + type: 'main', + path: '', + description: '', + format: 'checkpoint', + vae: '', + variant: 'normal', + config: '', + }, + mode: 'onChange', + }); + + const onSubmit = useCallback>( + (values) => { + addMainModel({ + body: values, + }) + .unwrap() + .then((_) => { + dispatch( + addToast( + makeToast({ + title: t('modelManager.modelAdded', { + modelName: values.name, + }), + status: 'success', + }) + ) + ); + reset(); + }) + .catch((error) => { + if (error) { + dispatch( + addToast( + makeToast({ + title: t('toast.modelAddFailed'), + status: 'error', + }) + ) + ); + } + }); + }, + [addMainModel, dispatch, reset, t] + ); + + const handleChangeUseCustomConfig = useCallback(() => setUseCustomConfig((prev) => !prev), []); + + return ( +
+ + + + {t('modelManager.name')} + value.trim().length > 3 || 'Must be at least 3 characters', + })} + /> + {errors.name?.message && {errors.name?.message}} + + + + + + {t('modelManager.description')} +