got model images displaying, still need to clean up types and unused code

This commit is contained in:
Jennifer Player 2024-03-06 00:58:27 -05:00 committed by Kent Keirsey
parent 86aef9f31d
commit 8a68355926
9 changed files with 12630 additions and 41 deletions

View File

@ -374,11 +374,6 @@ AnyModelConfig = Annotated[
AnyModelConfigValidator = TypeAdapter(AnyModelConfig)
class ModelImage(str, Enum):
path: str
class ModelConfigFactory(object):
"""Class for parsing config dicts into StableDiffusion Config obects."""

6
invokeai/frontend/package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "frontend",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

12557
invokeai/frontend/web/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -829,6 +829,7 @@
"repo_id": "Repo ID",
"repoIDValidationMsg": "Online repository of your model",
"repoVariant": "Repo Variant",
"resetImage": "Reset This Image",
"safetensorModels": "SafeTensors",
"sameFolder": "Same folder",
"scan": "Scan",
@ -853,6 +854,7 @@
"triggerPhrases": "Trigger Phrases",
"typePhraseHere": "Type phrase here",
"upcastAttention": "Upcast Attention",
"uploadImage":"Upload Image",
"updateModel": "Update Model",
"useCustomConfig": "Use Custom Config",
"useDefaultSettings": "Use Default Settings",

View File

@ -0,0 +1,29 @@
import { Box, Image } from '@invoke-ai/ui-library';
import { typedMemo } from 'common/util/typedMemo';
import { useState } from 'react';
import { buildModelsUrl } from 'services/api/endpoints/models';
type Props = {
model_key: string;
};
const ModelImage = ({ model_key }: Props) => {
const [image, setImage] = useState<string | undefined>(buildModelsUrl(`i/${model_key}/image`));
if (!image) return <Box height="50px" width="50px"></Box>;
return (
<Image
onError={() => setImage(undefined)}
src={image}
objectFit="cover"
objectPosition="50% 50%"
height="50px"
width="50px"
borderRadius="base"
/>
);
};
export default typedMemo(ModelImage);

View File

@ -21,6 +21,7 @@ import { IoWarning } from 'react-icons/io5';
import { PiTrashSimpleBold } from 'react-icons/pi';
import { useDeleteModelsMutation } from 'services/api/endpoints/models';
import type { AnyModelConfig } from 'services/api/types';
import ModelImage from './ModelImage';
type ModelListItemProps = {
model: AnyModelConfig;
@ -73,6 +74,7 @@ const ModelListItem = (props: ModelListItemProps) => {
return (
<Flex gap={2} alignItems="center" w="full">
<ModelImage model_key={model.key} />
<Flex
as={Button}
isChecked={isSelected}

View File

@ -1,21 +1,24 @@
import { Box, IconButton, Image } from '@invoke-ai/ui-library';
import { typedMemo } from 'common/util/typedMemo';
import { useCallback, useEffect, useMemo } from 'react';
import type { UseControllerProps } from 'react-hook-form';
import { useCallback, useState } from 'react';
import type { Control } from 'react-hook-form';
import { useController, useWatch } from 'react-hook-form';
import type { AnyModelConfig } from 'services/api/types';
import { Button } from '@invoke-ai/ui-library';
import { useDropzone } from 'react-dropzone';
import { PiArrowCounterClockwiseBold, PiUploadSimpleBold } from 'react-icons/pi';
import { useGetModelImageQuery } from 'services/api/endpoints/models';
import { UpdateModelArg, buildModelsUrl } from 'services/api/endpoints/models';
import { useTranslation } from 'react-i18next';
const ModelImageUpload = (props: UseControllerProps<AnyModelConfig>) => {
const { field } = useController(props);
type Props = {
control: Control<UpdateModelArg['body']>;
};
const key = useWatch({ control: props.control, name: 'key' });
const { data } = useGetModelImageQuery(key);
const ModelImageUpload = ({ control }: Props) => {
const key = useWatch({ control, name: 'key' });
const [image, setImage] = useState<string | undefined>(buildModelsUrl(`i/${key}/image`));
const { field } = useController({ control, name: 'image' });
const { t } = useTranslation();
const onDropAccepted = useCallback(
(files: File[]) => {
@ -26,12 +29,14 @@ const ModelImageUpload = (props: UseControllerProps<AnyModelConfig>) => {
}
field.onChange(file);
setImage(URL.createObjectURL(file));
},
[field]
);
const handleResetControlImage = useCallback(() => {
const handleResetImage = useCallback(() => {
field.onChange(undefined);
setImage(undefined);
}, [field]);
const { getInputProps, getRootProps } = useDropzone({
@ -41,29 +46,28 @@ const ModelImageUpload = (props: UseControllerProps<AnyModelConfig>) => {
multiple: false,
});
const image = useMemo(() => {
console.log(field.value, 'asdf' );
if (field.value) {
return URL.createObjectURL(field.value);
}
return data;
}, [field.value, data]);
if (image) {
return (
<Box>
<Box
position="relative"
_hover={{ filter: 'brightness(50%)' }}
transition="filter ease 0.2s"
>
<Image
onError={() => setImage(undefined)}
src={image}
objectFit="contain"
maxW="full"
maxH="200px"
maxH="100px"
borderRadius="base"
/>
<IconButton
onClick={handleResetControlImage}
aria-label="reset this image"
tooltip="reset this image"
position="absolute"
top="1"
right="1"
onClick={handleResetImage}
aria-label={t('modelManager.resetImage')}
tooltip={t('modelManager.resetImage')}
icon={<PiArrowCounterClockwiseBold size={16} />}
size="sm"
variant="link"
@ -75,7 +79,7 @@ const ModelImageUpload = (props: UseControllerProps<AnyModelConfig>) => {
return (
<>
<Button leftIcon={<PiUploadSimpleBold />} {...getRootProps()} pointerEvents="auto">
Upload Image
{t('modelManager.uploadImage')}
</Button>
<input {...getInputProps()} />
</>

View File

@ -168,7 +168,7 @@ export const ModelEdit = () => {
<Flex flexDir="column" gap={3} mt="4">
<Flex gap="4" alignItems="center">
<ModelImageUpload control={control} name="image" />
<ModelImageUpload control={control} />
<FormControl flexDir="column" alignItems="flex-start" gap={1}>
<FormLabel>{t('modelManager.description')}</FormLabel>
<Textarea fontSize="md" resize="none" {...register('description')} />

View File

@ -25,14 +25,12 @@ export type UpdateModelArg = {
export type UpdateModelImageArg = {
key: paths['/api/v2/models/i/{key}/image']['patch']['parameters']['path']['key'];
image: paths['/api/v2/models/i/{key}/image']['patch']['formData']['content']['multipart/form-data'];
image: paths['/api/v2/models/i/{key}/image']['patch']['requestBody']['content']['multipart/form-data'];
};
type UpdateModelResponse = paths['/api/v2/models/i/{key}']['patch']['responses']['200']['content']['application/json'];
type UpdateModelImageResponse =
paths['/api/v2/models/i/{key}/image']['patch']['responses']['200']['content']['application/json'];
type GetModelImageResponse =
paths['/api/v2/models/i/{key}/image']['get']['responses']['200']['content']['application/json'];
type GetModelConfigResponse = paths['/api/v2/models/i/{key}']['get']['responses']['200']['content']['application/json'];
@ -139,7 +137,7 @@ const buildTransformResponse =
* buildModelsUrl('some-path')
* // '/api/v1/models/some-path'
*/
const buildModelsUrl = (path: string = '') => buildV2Url(`models/${path}`);
export const buildModelsUrl = (path: string = '') => buildV2Url(`models/${path}`);
export const modelsApi = api.injectEndpoints({
endpoints: (build) => ({
@ -153,13 +151,10 @@ export const modelsApi = api.injectEndpoints({
},
invalidatesTags: ['Model'],
}),
getModelImage: build.query<GetModelImageResponse, string>({
query: (key) => buildModelsUrl(`i/${key}/image`),
}),
updateModelImage: build.mutation<UpdateModelImageResponse, UpdateModelImageArg>({
query: ({ key, image }) => {
const formData = new FormData();
formData.append('image', image);
formData.append('image', image.image);
return {
url: buildModelsUrl(`i/${key}/image`),
method: 'PATCH',
@ -354,8 +349,7 @@ export const {
useGetTextualInversionModelsQuery,
useGetVaeModelsQuery,
useDeleteModelsMutation,
useUpdateModelsMutation,
useGetModelImageQuery,
useUpdateModelMutation,
useUpdateModelImageMutation,
useInstallModelMutation,
useConvertModelMutation,