ui: add ability to copy template

This commit is contained in:
Mary Hipp 2024-08-12 12:32:31 -04:00
parent 4837e578b2
commit 096f001634
6 changed files with 64 additions and 28 deletions

View File

@ -1693,6 +1693,7 @@
"active": "Active", "active": "Active",
"choosePromptTemplate": "Choose Prompt Template", "choosePromptTemplate": "Choose Prompt Template",
"clearTemplateSelection": "Clear Template Selection", "clearTemplateSelection": "Clear Template Selection",
"copyTemplate": "Copy Template",
"createPromptTemplate": "Create Prompt Template", "createPromptTemplate": "Create Prompt Template",
"defaultTemplates": "Default Templates", "defaultTemplates": "Default Templates",
"deleteImage": "Delete Image", "deleteImage": "Delete Image",

View File

@ -43,9 +43,6 @@ export const ParamNegativePrompt = memo(() => {
return ( return (
<PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}> <PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}>
<Box pos="relative" w="full"> <Box pos="relative" w="full">
{viewMode && (
<ViewModePrompt prompt={prompt} presetPrompt={activeStylePreset?.preset_data.negative_prompt || ''} />
)}
<Textarea <Textarea
id="negativePrompt" id="negativePrompt"
name="negativePrompt" name="negativePrompt"
@ -61,6 +58,9 @@ export const ParamNegativePrompt = memo(() => {
<PromptOverlayButtonWrapper> <PromptOverlayButtonWrapper>
<AddPromptTriggerButton isOpen={isOpen} onOpen={onOpen} /> <AddPromptTriggerButton isOpen={isOpen} onOpen={onOpen} />
</PromptOverlayButtonWrapper> </PromptOverlayButtonWrapper>
{viewMode && (
<ViewModePrompt prompt={prompt} presetPrompt={activeStylePreset?.preset_data.negative_prompt || ''} />
)}
</Box> </Box>
</PromptPopover> </PromptPopover>
); );

View File

@ -58,9 +58,6 @@ export const ParamPositivePrompt = memo(() => {
return ( return (
<PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}> <PromptPopover isOpen={isOpen} onClose={onClose} onSelect={onSelect} width={textareaRef.current?.clientWidth}>
<Box pos="relative"> <Box pos="relative">
{viewMode && (
<ViewModePrompt prompt={prompt} presetPrompt={activeStylePreset?.preset_data.positive_prompt || ''} />
)}
<Textarea <Textarea
id="prompt" id="prompt"
name="prompt" name="prompt"
@ -78,6 +75,9 @@ export const ParamPositivePrompt = memo(() => {
{baseModel === 'sdxl' && <SDXLConcatButton />} {baseModel === 'sdxl' && <SDXLConcatButton />}
<ShowDynamicPromptsPreviewButton /> <ShowDynamicPromptsPreviewButton />
</PromptOverlayButtonWrapper> </PromptOverlayButtonWrapper>
{viewMode && (
<ViewModePrompt prompt={prompt} presetPrompt={activeStylePreset?.preset_data.positive_prompt || ''} />
)}
</Box> </Box>
</PromptPopover> </PromptPopover>
); );

View File

@ -19,7 +19,7 @@ export const ViewModePrompt = ({ presetPrompt, prompt }: { presetPrompt: string;
}, [dispatch]); }, [dispatch]);
return ( return (
<Box position="absolute" top={0} bottom={0} left={0} right={0} zIndex={1} layerStyle="second" borderRadius="base"> <Box position="absolute" top={0} bottom={0} left={0} right={0} layerStyle="second" borderRadius="base">
<Flex flexDir="column" onClick={handleExitViewMode} justifyContent="space-between" h="full" padding="8px 10px"> <Flex flexDir="column" onClick={handleExitViewMode} justifyContent="space-between" h="full" padding="8px 10px">
<Flex overflow="scroll"> <Flex overflow="scroll">
<Text fontSize="sm" lineHeight="1rem" w="full"> <Text fontSize="sm" lineHeight="1rem" w="full">

View File

@ -1,6 +1,10 @@
import { Button, Flex, FormControl, FormLabel, Input, Text } from '@invoke-ai/ui-library'; import { Button, Flex, FormControl, FormLabel, Input, Text } from '@invoke-ai/ui-library';
import { useAppDispatch } from 'app/store/storeHooks'; import { useAppDispatch } from 'app/store/storeHooks';
import { isModalOpenChanged, updatingStylePresetIdChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import {
isModalOpenChanged,
prefilledFormDataChanged,
updatingStylePresetIdChanged,
} from 'features/stylePresets/store/stylePresetModalSlice';
import { toast } from 'features/toast/toast'; import { toast } from 'features/toast/toast';
import { useCallback } from 'react'; import { useCallback } from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
@ -65,6 +69,7 @@ export const StylePresetForm = ({
}); });
} }
dispatch(prefilledFormDataChanged(null));
dispatch(updatingStylePresetIdChanged(null)); dispatch(updatingStylePresetIdChanged(null));
dispatch(isModalOpenChanged(false)); dispatch(isModalOpenChanged(false));
}, },

View File

@ -11,7 +11,7 @@ import { toast } from 'features/toast/toast';
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { PiPencilBold, PiTrashBold } from 'react-icons/pi'; import { PiCopyBold, PiPencilBold, PiTrashBold } from 'react-icons/pi';
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets'; import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
import { useDeleteStylePresetMutation } from 'services/api/endpoints/stylePresets'; import { useDeleteStylePresetMutation } from 'services/api/endpoints/stylePresets';
@ -58,6 +58,27 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
[onOpen] [onOpen]
); );
const handleClickCopy = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
const { name, preset_data } = preset;
const { positive_prompt, negative_prompt } = preset_data;
dispatch(
prefilledFormDataChanged({
name: `${name} (${t('common.copy')})`,
positivePrompt: positive_prompt || '',
negativePrompt: negative_prompt || '',
imageUrl: preset.image,
})
);
dispatch(updatingStylePresetIdChanged(null));
dispatch(isModalOpenChanged(true));
},
[dispatch, preset, t]
);
const handleDeletePreset = useCallback(async () => { const handleDeletePreset = useCallback(async () => {
try { try {
await deleteStylePreset(preset.id); await deleteStylePreset(preset.id);
@ -106,25 +127,34 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
)} )}
</Flex> </Flex>
{preset.type !== 'default' && ( <Flex alignItems="center" gap={1}>
<Flex alignItems="center" gap={1}> <IconButton
<IconButton size="sm"
size="sm" variant="ghost"
variant="ghost" aria-label={t('stylePresets.copyTemplate')}
aria-label={t('stylePresets.editTemplate')} onClick={handleClickCopy}
onClick={handleClickEdit} icon={<PiCopyBold />}
icon={<PiPencilBold />} />
/> {preset.type !== 'default' && (
<IconButton <>
size="sm" <IconButton
variant="ghost" size="sm"
aria-label={t('stylePresets.deleteTemplate')} variant="ghost"
onClick={handleClickDelete} aria-label={t('stylePresets.editTemplate')}
colorScheme="error" onClick={handleClickEdit}
icon={<PiTrashBold />} icon={<PiPencilBold />}
/> />
</Flex> <IconButton
)} size="sm"
variant="ghost"
aria-label={t('stylePresets.deleteTemplate')}
onClick={handleClickDelete}
colorScheme="error"
icon={<PiTrashBold />}
/>
</>
)}
</Flex>
</Flex> </Flex>
<Flex flexDir="column" gap={1}> <Flex flexDir="column" gap={1}>