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
e7d7b37896
commit
cdd2f18bbd
invokeai/frontend/web
public/locales
src
features/modelManagerV2/subpanels
AddModelPanel
AdvancedImport.tsxAdvancedImportCheckpoint.tsxAdvancedImportDiffusers.tsxBaseModelSelect.tsxCheckpointConfigsSelect.tsxImportQueueModel.tsxModelVariantSelect.tsx
ImportModels.tsxScanModels
SimpleImport.tsxservices/api/endpoints
@ -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",
|
||||
|
56
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImport.tsx
Normal file
56
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImport.tsx
Normal file
@ -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>
|
||||
);
|
||||
};
|
160
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportCheckpoint.tsx
Normal file
160
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportCheckpoint.tsx
Normal file
@ -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%',
|
||||
};
|
132
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportDiffusers.tsx
Normal file
132
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/AdvancedImportDiffusers.tsx
Normal file
@ -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%',
|
||||
};
|
38
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/BaseModelSelect.tsx
Normal file
38
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/BaseModelSelect.tsx
Normal file
@ -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);
|
32
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/CheckpointConfigsSelect.tsx
Normal file
32
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/CheckpointConfigsSelect.tsx
Normal file
@ -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
|
||||
|
36
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelVariantSelect.tsx
Normal file
36
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ModelVariantSelect.tsx
Normal file
@ -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);
|
3
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModels.tsx
3
invokeai/frontend/web/src/features/modelManagerV2/subpanels/AddModelPanel/ScanModels/ScanModels.tsx
@ -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…
x
Reference in New Issue
Block a user