mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
UI in MM to create trigger phrases
This commit is contained in:
parent
bcf58cac59
commit
ba1dd4b02b
@ -1,9 +1,11 @@
|
|||||||
import { Flex } from '@invoke-ai/ui-library';
|
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
|
||||||
import { useGetModelConfigQuery } from 'services/api/endpoints/models';
|
import { useGetModelConfigQuery } from 'services/api/endpoints/models';
|
||||||
|
|
||||||
|
import { TriggerPhrases } from './TriggerPhrases';
|
||||||
|
|
||||||
export const ModelMetadata = () => {
|
export const ModelMetadata = () => {
|
||||||
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
|
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
|
||||||
const { data } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
|
const { data } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
|
||||||
@ -11,6 +13,9 @@ export const ModelMetadata = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex flexDir="column" height="full" gap="3">
|
<Flex flexDir="column" height="full" gap="3">
|
||||||
|
<Box layerStyle="second" borderRadius="base" p={3}>
|
||||||
|
<TriggerPhrases />
|
||||||
|
</Box>
|
||||||
<DataViewer label="metadata" data={data?.source_api_response || {}} />
|
<DataViewer label="metadata" data={data?.source_api_response || {}} />
|
||||||
</Flex>
|
</Flex>
|
||||||
</>
|
</>
|
||||||
|
@ -0,0 +1,106 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
FormControl,
|
||||||
|
FormErrorMessage,
|
||||||
|
Input,
|
||||||
|
Tag,
|
||||||
|
TagCloseButton,
|
||||||
|
TagLabel,
|
||||||
|
} from '@invoke-ai/ui-library';
|
||||||
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { ModelListHeader } from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelListHeader';
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useGetModelConfigQuery, useUpdateModelMutation } from 'services/api/endpoints/models';
|
||||||
|
|
||||||
|
export const TriggerPhrases = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
|
||||||
|
const { data: modelConfig } = useGetModelConfigQuery(selectedModelKey ?? skipToken);
|
||||||
|
const [phrase, setPhrase] = useState('');
|
||||||
|
|
||||||
|
const [updateModel, { isLoading }] = useUpdateModelMutation();
|
||||||
|
|
||||||
|
const handlePhraseChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setPhrase(e.target.value);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const triggerPhrases = useMemo(() => {
|
||||||
|
return modelConfig?.trigger_phrases || [];
|
||||||
|
}, [modelConfig?.trigger_phrases]);
|
||||||
|
|
||||||
|
const errors = useMemo(() => {
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (phrase.length && triggerPhrases.includes(phrase)) {
|
||||||
|
errors.push('Phrase is already in list');
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}, [phrase, triggerPhrases]);
|
||||||
|
|
||||||
|
const addTriggerPhrase = useCallback(async () => {
|
||||||
|
if (!selectedModelKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!phrase.length || triggerPhrases.includes(phrase)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateModel({
|
||||||
|
key: selectedModelKey,
|
||||||
|
body: { trigger_phrases: [...triggerPhrases, phrase] },
|
||||||
|
}).unwrap();
|
||||||
|
setPhrase('');
|
||||||
|
}, [updateModel, selectedModelKey, phrase, triggerPhrases]);
|
||||||
|
|
||||||
|
const removeTriggerPhrase = useCallback(
|
||||||
|
async (phraseToRemove: string) => {
|
||||||
|
if (!selectedModelKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredPhrases = triggerPhrases.filter((p) => p !== phraseToRemove);
|
||||||
|
|
||||||
|
await updateModel({ key: selectedModelKey, body: { trigger_phrases: filteredPhrases } }).unwrap();
|
||||||
|
},
|
||||||
|
[updateModel, selectedModelKey, triggerPhrases]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" w="full" gap="5">
|
||||||
|
<ModelListHeader title={t('modelManager.triggerPhrases')} />
|
||||||
|
<form>
|
||||||
|
<FormControl w="full" isInvalid={Boolean(errors.length)}>
|
||||||
|
<Flex flexDir="column" w="full">
|
||||||
|
<Flex gap="3" alignItems="center" w="full">
|
||||||
|
<Input value={phrase} onChange={handlePhraseChange} placeholder={t('modelManager.typePhraseHere')} />
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
onClick={addTriggerPhrase}
|
||||||
|
isDisabled={Boolean(errors.length)}
|
||||||
|
isLoading={isLoading}
|
||||||
|
>
|
||||||
|
{t('common.add')}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
{!!errors.length && errors.map((error) => <FormErrorMessage key={error}>{error}</FormErrorMessage>)}
|
||||||
|
</Flex>
|
||||||
|
</FormControl>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<Flex gap="4" flexWrap="wrap" mt="3" mb="3">
|
||||||
|
{triggerPhrases.map((phrase, index) => (
|
||||||
|
<Tag size="md" key={index}>
|
||||||
|
<TagLabel>{phrase}</TagLabel>
|
||||||
|
<TagCloseButton onClick={removeTriggerPhrase.bind(null, phrase)} isDisabled={isLoading} />
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user