mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
added advanced import forms, not fully working yet
This commit is contained in:
parent
0567f98e4a
commit
135ef9066f
@ -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",
|
||||
|
@ -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<typeof zManualAddMode>;
|
||||
export const isManualAddMode = (v: unknown): v is ManualAddMode => zManualAddMode.safeParse(v).success;
|
||||
|
||||
export const AdvancedImport = () => {
|
||||
const [advancedAddMode, setAdvancedAddMode] = useState<ManualAddMode>('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 (
|
||||
<ScrollableContent>
|
||||
<Flex flexDirection="column" gap={4} width="100%" pb={10}>
|
||||
<Flex alignItems="flex-end" gap="4">
|
||||
<Flex direction="column" gap="3" width="full">
|
||||
<FormLabel>{t('modelManager.modelType')}</FormLabel>
|
||||
<Combobox value={value} options={options} onChange={handleChange} />
|
||||
</Flex>
|
||||
<Text px="2" fontSize="xs" textAlign="center">
|
||||
{t('modelManager.advancedImportInfo')}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
<Flex p={4} borderRadius={4} bg="base.850" height="100%">
|
||||
{advancedAddMode === 'diffusers' && <AdvancedImportDiffusers />}
|
||||
{advancedAddMode === 'checkpoint' && <AdvancedImportCheckpoint />}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ScrollableContent>
|
||||
);
|
||||
};
|
@ -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<boolean>(false);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm<CheckpointModelConfig>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
base: 'sd-1',
|
||||
type: 'main',
|
||||
path: '',
|
||||
description: '',
|
||||
format: 'checkpoint',
|
||||
vae: '',
|
||||
variant: 'normal',
|
||||
config: '',
|
||||
},
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const onSubmit = useCallback<SubmitHandler<DiffusersModelConfig>>(
|
||||
(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 (
|
||||
<form onSubmit={handleSubmit(onSubmit)} style={formStyles}>
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
<FormControl isInvalid={Boolean(errors.name)}>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.name')}</FormLabel>
|
||||
<Input
|
||||
{...register('name', {
|
||||
validate: (value) => value.trim().length > 3 || 'Must be at least 3 characters',
|
||||
})}
|
||||
/>
|
||||
{errors.name?.message && <FormErrorMessage>{errors.name?.message}</FormErrorMessage>}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<Flex>
|
||||
<FormControl>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.description')}</FormLabel>
|
||||
<Textarea size="sm" {...register('description')} />
|
||||
{errors.name?.message && <FormErrorMessage>{errors.name?.message}</FormErrorMessage>}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
<Flex gap="2">
|
||||
<BaseModelSelect<CheckpointModelConfig> control={control} name="base" />
|
||||
<ModelVariantSelect<CheckpointModelConfig> control={control} name="variant" />
|
||||
</Flex>
|
||||
<FormControl isInvalid={Boolean(errors.path)}>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.modelLocation')}</FormLabel>
|
||||
<Input
|
||||
{...register('path', {
|
||||
validate: (value) => value.trim().length > 0 || 'Must provide a path',
|
||||
})}
|
||||
/>
|
||||
{errors.path?.message && <FormErrorMessage>{errors.path?.message}</FormErrorMessage>}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.vaeLocation')}</FormLabel>
|
||||
<Input {...register('vae')} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<Flex flexDirection="column" width="100%" gap={2}>
|
||||
{!useCustomConfig ? (
|
||||
<CheckpointConfigsSelect control={control} name="config" />
|
||||
) : (
|
||||
<FormControl isRequired>
|
||||
<FormLabel>{t('modelManager.config')}</FormLabel>
|
||||
<Input {...register('config')} />
|
||||
</FormControl>
|
||||
)}
|
||||
<FormControl>
|
||||
<FormLabel>{t('modelManager.useCustomConfig')}</FormLabel>
|
||||
<Checkbox isChecked={useCustomConfig} onChange={handleChangeUseCustomConfig} />
|
||||
</FormControl>
|
||||
<Button mt={2} type="submit">
|
||||
{t('modelManager.addModel')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const formStyles: CSSProperties = {
|
||||
width: '100%',
|
||||
};
|
@ -0,0 +1,132 @@
|
||||
import { Button, 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 } 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 { DiffusersModelConfig } from 'services/api/types';
|
||||
|
||||
import BaseModelSelect from './BaseModelSelect';
|
||||
import ModelVariantSelect from './ModelVariantSelect';
|
||||
|
||||
export const AdvancedImportDiffusers = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const [addMainModel] = useAddMainModelsMutation();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm<DiffusersModelConfig>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
base: 'sd-1',
|
||||
type: 'main',
|
||||
path: '',
|
||||
description: '',
|
||||
format: 'diffusers',
|
||||
vae: '',
|
||||
variant: 'normal',
|
||||
},
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const onSubmit = useCallback<SubmitHandler<DiffusersModelConfig>>(
|
||||
(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]
|
||||
);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)} style={formStyles}>
|
||||
<Flex flexDirection="column" gap={2}>
|
||||
<FormControl isInvalid={Boolean(errors.name)}>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.name')}</FormLabel>
|
||||
<Input
|
||||
{...register('name', {
|
||||
validate: (value) => value.trim().length > 3 || 'Must be at least 3 characters',
|
||||
})}
|
||||
/>
|
||||
{errors.name?.message && <FormErrorMessage>{errors.name?.message}</FormErrorMessage>}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<Flex>
|
||||
<FormControl>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.description')}</FormLabel>
|
||||
<Textarea size="sm" {...register('description')} />
|
||||
{errors.name?.message && <FormErrorMessage>{errors.name?.message}</FormErrorMessage>}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
</Flex>
|
||||
<Flex gap="2">
|
||||
<BaseModelSelect<DiffusersModelConfig> control={control} name="base" />
|
||||
<ModelVariantSelect<DiffusersModelConfig> control={control} name="variant" />
|
||||
</Flex>
|
||||
<FormControl isInvalid={Boolean(errors.path)}>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.modelLocation')}</FormLabel>
|
||||
<Input
|
||||
{...register('path', {
|
||||
validate: (value) => value.trim().length > 0 || 'Must provide a path',
|
||||
})}
|
||||
/>
|
||||
{errors.path?.message && <FormErrorMessage>{errors.path?.message}</FormErrorMessage>}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.vaeLocation')}</FormLabel>
|
||||
<Input {...register('vae')} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
|
||||
<Button mt={2} type="submit">
|
||||
{t('modelManager.addModel')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const formStyles: CSSProperties = {
|
||||
width: '100%',
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
|
||||
import { Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { typedMemo } from 'common/util/typedMemo';
|
||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { UseControllerProps } from 'react-hook-form';
|
||||
import { useController } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { AnyModelConfig } from 'services/api/types';
|
||||
|
||||
const options: ComboboxOption[] = [
|
||||
{ value: 'sd-1', label: MODEL_TYPE_MAP['sd-1'] },
|
||||
{ value: 'sd-2', label: MODEL_TYPE_MAP['sd-2'] },
|
||||
{ value: 'sdxl', label: MODEL_TYPE_MAP['sdxl'] },
|
||||
{ value: 'sdxl-refiner', label: MODEL_TYPE_MAP['sdxl-refiner'] },
|
||||
];
|
||||
|
||||
const BaseModelSelect = <T extends AnyModelConfig>(props: UseControllerProps<T>) => {
|
||||
const { t } = useTranslation();
|
||||
const { field } = useController(props);
|
||||
const value = useMemo(() => options.find((o) => o.value === field.value), [field.value]);
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
field.onChange(v?.value);
|
||||
},
|
||||
[field]
|
||||
);
|
||||
return (
|
||||
<FormControl>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.baseModel')}</FormLabel>
|
||||
<Combobox value={value} options={options} onChange={onChange} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default typedMemo(BaseModelSelect);
|
@ -0,0 +1,32 @@
|
||||
import type { ChakraProps, ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
|
||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useController, type UseControllerProps } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetCheckpointConfigsQuery } from 'services/api/endpoints/models';
|
||||
import type { CheckpointModelConfig } from 'services/api/types';
|
||||
|
||||
const sx: ChakraProps['sx'] = { w: 'full' };
|
||||
|
||||
const CheckpointConfigsSelect = (props: UseControllerProps<CheckpointModelConfig>) => {
|
||||
const { data } = useGetCheckpointConfigsQuery();
|
||||
const { t } = useTranslation();
|
||||
const options = useMemo<ComboboxOption[]>(() => (data ? data.map((i) => ({ label: i, value: i })) : []), [data]);
|
||||
const { field } = useController(props);
|
||||
const value = useMemo(() => options.find((o) => o.value === field.value), [field.value, options]);
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
field.onChange(v?.value);
|
||||
},
|
||||
[field]
|
||||
);
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<FormLabel>{t('modelManager.configFile')}</FormLabel>
|
||||
<Combobox placeholder="Select A Config File" value={value} options={options} onChange={onChange} sx={sx} />
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(CheckpointConfigsSelect);
|
@ -57,6 +57,10 @@ export const ImportQueueModel = (props: ModelListItemProps) => {
|
||||
return `${bytes.toFixed(2)} ${units[i]}`;
|
||||
};
|
||||
|
||||
const modelName = useMemo(() => {
|
||||
return model.source.repo_id || model.source.url || model.source.path.substring(model.source.path.lastIndexOf('/') + 1);
|
||||
}, [model.source]);
|
||||
|
||||
const progressValue = useMemo(() => {
|
||||
return (model.bytes / model.total_bytes) * 100;
|
||||
}, [model.bytes, model.total_bytes]);
|
||||
@ -71,7 +75,7 @@ export const ImportQueueModel = (props: ModelListItemProps) => {
|
||||
return (
|
||||
<Flex gap="2" w="full" alignItems="center" textAlign="center">
|
||||
<Text w="20%" whiteSpace="nowrap" overflow="hidden" textOverflow="ellipsis">
|
||||
{model.source.repo_id}
|
||||
{modelName}
|
||||
</Text>
|
||||
<Progress
|
||||
value={progressValue}
|
||||
@ -83,8 +87,7 @@ export const ImportQueueModel = (props: ModelListItemProps) => {
|
||||
<Text minW="20%" fontSize="xs" w="20%">
|
||||
{progressString}
|
||||
</Text>
|
||||
<Text w="15%">{model.status[0].toUpperCase() +
|
||||
model.status.slice(1)}</Text>
|
||||
<Text w="15%">{model.status[0].toUpperCase() + model.status.slice(1)}</Text>
|
||||
<Box w="10%">
|
||||
{(model.status === 'downloading' || model.status === 'waiting') && (
|
||||
<IconButton
|
||||
|
@ -0,0 +1,36 @@
|
||||
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library';
|
||||
import { Combobox, Flex, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
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 type { CheckpointModelConfig, DiffusersModelConfig } from 'services/api/types';
|
||||
|
||||
const options: ComboboxOption[] = [
|
||||
{ value: 'normal', label: 'Normal' },
|
||||
{ value: 'inpaint', label: 'Inpaint' },
|
||||
{ value: 'depth', label: 'Depth' },
|
||||
];
|
||||
|
||||
const ModelVariantSelect = <T extends CheckpointModelConfig | DiffusersModelConfig>(props: UseControllerProps<T>) => {
|
||||
const { t } = useTranslation();
|
||||
const { field } = useController(props);
|
||||
const value = useMemo(() => options.find((o) => o.value === field.value), [field.value]);
|
||||
const onChange = useCallback<ComboboxOnChange>(
|
||||
(v) => {
|
||||
field.onChange(v?.value);
|
||||
},
|
||||
[field]
|
||||
);
|
||||
return (
|
||||
<FormControl>
|
||||
<Flex direction="column" width="full">
|
||||
<FormLabel>{t('modelManager.variant')}</FormLabel>
|
||||
<Combobox value={value} options={options} onChange={onChange} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
export default typedMemo(ModelVariantSelect);
|
@ -1,5 +1,4 @@
|
||||
import { ScanModelsForm } from './ScanModelsForm';
|
||||
|
||||
export const ScanModels = () => {
|
||||
return <ScanModelsForm />;
|
||||
return null;
|
||||
};
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Flex, FormControl, FormLabel, Input, Button, FormErrorMessage, Divider } from '@invoke-ai/ui-library';
|
||||
import { ChangeEventHandler, useCallback, useState } from 'react';
|
||||
import { useLazyScanModelsQuery } from '../../../../../services/api/endpoints/models';
|
||||
import { Button,Flex, FormControl, FormErrorMessage, FormLabel, Input } from '@invoke-ai/ui-library';
|
||||
import type { ChangeEventHandler} from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLazyScanModelsQuery } from 'services/api/endpoints/models';
|
||||
|
||||
import { ScanModelsResults } from './ScanModelsResults';
|
||||
import ScrollableContent from '../../../../../common/components/OverlayScrollbars/ScrollableContent';
|
||||
|
||||
export const ScanModelsForm = () => {
|
||||
const [scanPath, setScanPath] = useState('');
|
||||
|
@ -1,18 +1,18 @@
|
||||
import {
|
||||
Text,
|
||||
Divider,
|
||||
Flex,
|
||||
Heading,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
Divider,
|
||||
Box,
|
||||
Text,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { t } from 'i18next';
|
||||
import { ChangeEventHandler, useCallback, useMemo, useState } from 'react';
|
||||
import type { ChangeEventHandler} from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { PiXBold } from 'react-icons/pi';
|
||||
import ScrollableContent from '../../../../../common/components/OverlayScrollbars/ScrollableContent';
|
||||
|
||||
export const ScanModelsResults = ({ results }: { results: string[] }) => {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Flex, FormControl, FormLabel, Input, Button } from '@invoke-ai/ui-library';
|
||||
import { Button,Flex, FormControl, FormLabel, Input } 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 { t } from 'i18next';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { useAppDispatch } from '../../../../app/store/storeHooks';
|
||||
import { useImportMainModelsMutation } from '../../../../services/api/endpoints/models';
|
||||
import { addToast } from '../../../system/store/systemSlice';
|
||||
import { makeToast } from '../../../system/util/makeToast';
|
||||
import { useCallback } from 'react';
|
||||
import type { SubmitHandler} from 'react-hook-form';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useImportMainModelsMutation } from 'services/api/endpoints/models';
|
||||
|
||||
type SimpleImportModelConfig = {
|
||||
location: string;
|
||||
@ -15,50 +17,59 @@ export const SimpleImport = () => {
|
||||
|
||||
const [importMainModel, { isLoading }] = useImportMainModelsMutation();
|
||||
|
||||
const addModelForm = useForm({
|
||||
initialValues: {
|
||||
const { register, handleSubmit, formState, reset } = useForm<SimpleImportModelConfig>({
|
||||
defaultValues: {
|
||||
location: '',
|
||||
},
|
||||
mode: 'onChange',
|
||||
});
|
||||
|
||||
const handleAddModelSubmit = (values: SimpleImportModelConfig) => {
|
||||
importMainModel({ source: values.location, config: undefined })
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: t('toast.modelAddedSimple'),
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
addModelForm.reset();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error) {
|
||||
const onSubmit = useCallback<SubmitHandler<SimpleImportModelConfig>>(
|
||||
(values) => {
|
||||
if (!values?.location) {
|
||||
return;
|
||||
}
|
||||
|
||||
importMainModel({ source: values.location, config: undefined })
|
||||
.unwrap()
|
||||
.then((_) => {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `${error.data.detail} `,
|
||||
status: 'error',
|
||||
title: t('toast.modelAddedSimple'),
|
||||
status: 'success',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
reset();
|
||||
})
|
||||
.catch((error) => {
|
||||
reset();
|
||||
if (error) {
|
||||
dispatch(
|
||||
addToast(
|
||||
makeToast({
|
||||
title: `${error.data.detail} `,
|
||||
status: 'error',
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
[dispatch, reset, importMainModel]
|
||||
);
|
||||
|
||||
return (
|
||||
<form onSubmit={addModelForm.onSubmit((v) => handleAddModelSubmit(v))}>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Flex gap={2} alignItems="flex-end" justifyContent="space-between">
|
||||
<FormControl>
|
||||
<Flex direction="column" w="full">
|
||||
<FormLabel>{t('modelManager.modelLocation')}</FormLabel>
|
||||
<Input {...addModelForm.getInputProps('location')} />
|
||||
<Input {...register('location')} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<Button isDisabled={!addModelForm.values.location} isLoading={isLoading} type="submit">
|
||||
<Button onClick={handleSubmit(onSubmit)} isDisabled={!formState.isDirty} isLoading={isLoading} type="submit">
|
||||
{t('modelManager.addModel')}
|
||||
</Button>
|
||||
</Flex>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Box, Divider, Heading, Tab, Flex, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
import { Box, Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library';
|
||||
|
||||
import { AdvancedImport } from './AddModelPanel/AdvancedImport';
|
||||
import { ImportQueue } from './AddModelPanel/ImportQueue';
|
||||
import { SimpleImport } from './AddModelPanel/SimpleImport';
|
||||
import { ScanModels } from './AddModelPanel/ScanModels/ScanModels';
|
||||
import { SimpleImport } from './AddModelPanel/SimpleImport';
|
||||
|
||||
export const ImportModels = () => {
|
||||
return (
|
||||
@ -21,7 +22,9 @@ export const ImportModels = () => {
|
||||
<TabPanel>
|
||||
<SimpleImport />
|
||||
</TabPanel>
|
||||
<TabPanel>Advanced Import Placeholder</TabPanel>
|
||||
<TabPanel height="100%">
|
||||
<AdvancedImport />
|
||||
</TabPanel>
|
||||
<TabPanel height="100%">
|
||||
<ScanModels />
|
||||
</TabPanel>
|
||||
|
@ -206,7 +206,7 @@ export const modelsApi = api.injectEndpoints({
|
||||
body: body,
|
||||
};
|
||||
},
|
||||
invalidatesTags: ['Model'],
|
||||
invalidatesTags: ['Model', 'ModelImports'],
|
||||
}),
|
||||
deleteModels: build.mutation<DeleteMainModelResponse, DeleteMainModelArg>({
|
||||
query: ({ key }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user