translations and lint fix

This commit is contained in:
Mary Hipp 2024-08-08 13:56:37 -04:00
parent 442fc02429
commit 4cc41e0188
35 changed files with 269 additions and 275 deletions

View File

@ -1691,6 +1691,27 @@
"missingUpscaleModel": "Missing upscale model", "missingUpscaleModel": "Missing upscale model",
"missingTileControlNetModel": "No valid tile ControlNet models installed" "missingTileControlNetModel": "No valid tile ControlNet models installed"
}, },
"stylePresets": {
"active": "Active",
"choosePromptTemplate": "Choose Prompt Template",
"createPromptTemplate": "Create Prompt Template",
"defaultTemplates": "Default Templates",
"deleteImage": "Delete Image",
"deleteTemplate": "Delete Template",
"deleteTemplate2": "Are you sure you want to delete this template? This cannot be undone.",
"editTemplate": "Edit Template",
"myTemplates": "My Templates",
"name": "Name",
"negativePrompt": "Negative Prompt",
"noMatchingTemplates": "No matching templates",
"placeholderDirections": "Use the { } button to specify where your manual prompt should be included in the template. If you do not provide one, the template will be appended to your prompt.",
"positivePrompt": "Positive Prompt",
"templateDeleted": "Prompt template deleted",
"unableToDeleteTemplate": "Unable to delete prompt template",
"updatePromptTemplate": "Update Prompt Template",
"uploadImage": "Upload Image",
"useForTemplate": "Use For Prompt Template"
},
"upsell": { "upsell": {
"inviteTeammates": "Invite Teammates", "inviteTeammates": "Invite Teammates",
"professional": "Professional", "professional": "Professional",

View File

@ -72,7 +72,7 @@ const allReducers = {
[api.reducerPath]: api.reducer, [api.reducerPath]: api.reducer,
[upscaleSlice.name]: upscaleSlice.reducer, [upscaleSlice.name]: upscaleSlice.reducer,
[stylePresetModalSlice.name]: stylePresetModalSlice.reducer, [stylePresetModalSlice.name]: stylePresetModalSlice.reducer,
[stylePresetSlice.name]: stylePresetSlice.reducer [stylePresetSlice.name]: stylePresetSlice.reducer,
}; };
const rootReducer = combineReducers(allReducers); const rootReducer = combineReducers(allReducers);

View File

@ -6,7 +6,6 @@ import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
import { useDownloadImage } from 'common/hooks/useDownloadImage'; import { useDownloadImage } from 'common/hooks/useDownloadImage';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice'; import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice'; import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
import { isModalOpenChanged as isStylePresetModalOpenChanged } from 'features/stylePresets/store/stylePresetModalSlice';
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice'; import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice'; import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
import { useImageActions } from 'features/gallery/hooks/useImageActions'; import { useImageActions } from 'features/gallery/hooks/useImageActions';
@ -41,8 +40,6 @@ import {
} from 'react-icons/pi'; } from 'react-icons/pi';
import { useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images'; import { useStarImagesMutation, useUnstarImagesMutation } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types'; import type { ImageDTO } from 'services/api/types';
import { isMenuOpenChanged } from '../../../stylePresets/store/stylePresetSlice';
import { createPresetFromImageChanged } from '../../../stylePresets/store/stylePresetModalSlice';
type SingleSelectionMenuItemsProps = { type SingleSelectionMenuItemsProps = {
imageDTO: ImageDTO; imageDTO: ImageDTO;
@ -200,7 +197,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
onClickCapture={createAsPreset} onClickCapture={createAsPreset}
isDisabled={isLoadingMetadata || !hasPrompts} isDisabled={isLoadingMetadata || !hasPrompts}
> >
Create Preset {t('stylePresets.useForTemplate')}
</MenuItem> </MenuItem>
<MenuDivider /> <MenuDivider />
<MenuItem icon={<PiShareFatBold />} onClickCapture={handleSendToImageToImage} id="send-to-img2img"> <MenuItem icon={<PiShareFatBold />} onClickCapture={handleSendToImageToImage} id="send-to-img2img">

View File

@ -1,12 +1,12 @@
import { skipToken } from '@reduxjs/toolkit/query';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useImageUrlToBlob } from 'common/hooks/useImageUrlToBlob';
import { handlers, parseAndRecallAllMetadata, parseAndRecallPrompts } from 'features/metadata/util/handlers'; import { handlers, parseAndRecallAllMetadata, parseAndRecallPrompts } from 'features/metadata/util/handlers';
import { isModalOpenChanged, prefilledFormDataChanged } from 'features/stylePresets/store/stylePresetModalSlice';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata'; import { useDebouncedMetadata } from 'services/api/hooks/useDebouncedMetadata';
import { useImageUrlToBlob } from '../../../common/hooks/useImageUrlToBlob';
import { prefilledFormDataChanged, isModalOpenChanged } from '../../stylePresets/store/stylePresetModalSlice';
import { useGetImageDTOQuery } from '../../../services/api/endpoints/images';
import { skipToken } from '@reduxjs/toolkit/query';
export const useImageActions = (image_name?: string) => { export const useImageActions = (image_name?: string) => {
const activeTabName = useAppSelector(activeTabNameSelector); const activeTabName = useAppSelector(activeTabNameSelector);
@ -15,8 +15,8 @@ export const useImageActions = (image_name?: string) => {
const [hasSeed, setHasSeed] = useState(false); const [hasSeed, setHasSeed] = useState(false);
const [hasPrompts, setHasPrompts] = useState(false); const [hasPrompts, setHasPrompts] = useState(false);
const imageUrlToBlob = useImageUrlToBlob(); const imageUrlToBlob = useImageUrlToBlob();
const dispatch = useAppDispatch() const dispatch = useAppDispatch();
const { data: imageDTO } = useGetImageDTOQuery(image_name ?? skipToken) const { data: imageDTO } = useGetImageDTOQuery(image_name ?? skipToken);
useEffect(() => { useEffect(() => {
const parseMetadata = async () => { const parseMetadata = async () => {
@ -70,15 +70,31 @@ export const useImageActions = (image_name?: string) => {
const createAsPreset = useCallback(async () => { const createAsPreset = useCallback(async () => {
if (image_name && metadata && imageDTO) { if (image_name && metadata && imageDTO) {
const positivePrompt = await handlers.positivePrompt.parse(metadata) const positivePrompt = await handlers.positivePrompt.parse(metadata);
const negativePrompt = await handlers.negativePrompt.parse(metadata) const negativePrompt = await handlers.negativePrompt.parse(metadata);
const imageBlob = await imageUrlToBlob(imageDTO.image_url, 100) const imageBlob = await imageUrlToBlob(imageDTO.image_url, 100);
dispatch(prefilledFormDataChanged({ name: "", positivePrompt, negativePrompt, image: imageBlob ? new File([imageBlob], "stylePreset.png", { type: 'image/png', }) : null })) dispatch(
dispatch(isModalOpenChanged(true)) prefilledFormDataChanged({
name: '',
positivePrompt,
negativePrompt,
image: imageBlob ? new File([imageBlob], 'stylePreset.png', { type: 'image/png' }) : null,
})
);
dispatch(isModalOpenChanged(true));
} }
}, [image_name, metadata, dispatch, imageDTO, imageUrlToBlob]);
}, [image_name, metadata, dispatch, imageDTO]) return {
recallAll,
return { recallAll, remix, recallSeed, recallPrompts, hasMetadata, hasSeed, hasPrompts, isLoadingMetadata, createAsPreset }; remix,
recallSeed,
recallPrompts,
hasMetadata,
hasSeed,
hasPrompts,
isLoadingMetadata,
createAsPreset,
};
}; };

View File

@ -98,7 +98,8 @@ export const buildMultidiffusionUpscaleGraph = async (state: RootState): Promise
let modelNode; let modelNode;
if (model.base === 'sdxl') { if (model.base === 'sdxl') {
const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } =
getPresetModifiedPrompts(state);
posCondNode = g.addNode({ posCondNode = g.addNode({
type: 'sdxl_compel_prompt', type: 'sdxl_compel_prompt',

View File

@ -16,7 +16,11 @@ import {
POSITIVE_CONDITIONING, POSITIVE_CONDITIONING,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
import { isNonRefinerMainModelConfig } from 'services/api/types'; import { isNonRefinerMainModelConfig } from 'services/api/types';

View File

@ -19,7 +19,11 @@ import {
POSITIVE_CONDITIONING, POSITIVE_CONDITIONING,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';

View File

@ -23,7 +23,11 @@ import {
POSITIVE_CONDITIONING, POSITIVE_CONDITIONING,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';

View File

@ -16,7 +16,11 @@ import {
SDXL_REFINER_SEAMLESS, SDXL_REFINER_SEAMLESS,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
import { isNonRefinerMainModelConfig } from 'services/api/types'; import { isNonRefinerMainModelConfig } from 'services/api/types';

View File

@ -19,7 +19,11 @@ import {
SDXL_REFINER_SEAMLESS, SDXL_REFINER_SEAMLESS,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';

View File

@ -23,7 +23,11 @@ import {
SDXL_REFINER_SEAMLESS, SDXL_REFINER_SEAMLESS,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';

View File

@ -14,7 +14,11 @@ import {
SDXL_REFINER_SEAMLESS, SDXL_REFINER_SEAMLESS,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';

View File

@ -14,7 +14,11 @@ import {
POSITIVE_CONDITIONING, POSITIVE_CONDITIONING,
SEAMLESS, SEAMLESS,
} from 'features/nodes/util/graph/constants'; } from 'features/nodes/util/graph/constants';
import { getBoardField, getIsIntermediate, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import {
getBoardField,
getIsIntermediate,
getPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types';
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';

View File

@ -17,22 +17,29 @@ export const getBoardField = (state: RootState): BoardField | undefined => {
/** /**
* Gets the prompts, modified for the active style preset. * Gets the prompts, modified for the active style preset.
*/ */
export const getPresetModifiedPrompts = (state: RootState): { positivePrompt: string; negativePrompt: string, positiveStylePrompt?: string; negativeStylePrompt?: string } => { export const getPresetModifiedPrompts = (
state: RootState
): { positivePrompt: string; negativePrompt: string; positiveStylePrompt?: string; negativeStylePrompt?: string } => {
const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } = const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } =
state.controlLayers.present; state.controlLayers.present;
const { activeStylePreset } = state.stylePreset const { activeStylePreset } = state.stylePreset;
if (activeStylePreset) { if (activeStylePreset) {
const presetModifiedPositivePrompt = buildPresetModifiedPrompt(activeStylePreset.preset_data.positive_prompt, positivePrompt) const presetModifiedPositivePrompt = buildPresetModifiedPrompt(
activeStylePreset.preset_data.positive_prompt,
positivePrompt
);
const presetModifiedNegativePrompt = buildPresetModifiedPrompt(activeStylePreset.preset_data.negative_prompt, negativePrompt) const presetModifiedNegativePrompt = buildPresetModifiedPrompt(
activeStylePreset.preset_data.negative_prompt,
negativePrompt
);
return { return {
positivePrompt: presetModifiedPositivePrompt, positivePrompt: presetModifiedPositivePrompt,
negativePrompt: presetModifiedNegativePrompt, negativePrompt: presetModifiedNegativePrompt,
positiveStylePrompt: shouldConcatPrompts ? presetModifiedPositivePrompt : positivePrompt2, positiveStylePrompt: shouldConcatPrompts ? presetModifiedPositivePrompt : positivePrompt2,
negativeStylePrompt: shouldConcatPrompts ? presetModifiedNegativePrompt : negativePrompt2, negativeStylePrompt: shouldConcatPrompts ? presetModifiedNegativePrompt : negativePrompt2,
}; };
} }

View File

@ -2,12 +2,12 @@ import { Box, Textarea } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePromptChanged } from 'features/controlLayers/store/controlLayersSlice'; import { negativePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton'; import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover'; import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt'; import { usePrompt } from 'features/prompt/usePrompt';
import { memo, useCallback, useRef } from 'react'; import { memo, useCallback, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ViewModePrompt } from '../Prompts/ViewModePrompt';
const DEFAULT_HEIGHT = 20; const DEFAULT_HEIGHT = 20;
@ -42,7 +42,7 @@ 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"> <Box pos="relative" w="full">
<Textarea <Textarea
id="negativePrompt" id="negativePrompt"
name="negativePrompt" name="negativePrompt"

View File

@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice'; import { positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton'; import { ShowDynamicPromptsPreviewButton } from 'features/dynamicPrompts/components/ShowDynamicPromptsPreviewButton';
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper'; import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
import { ViewModePrompt } from 'features/parameters/components/Prompts/ViewModePrompt';
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton'; import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
import { PromptPopover } from 'features/prompt/PromptPopover'; import { PromptPopover } from 'features/prompt/PromptPopover';
import { usePrompt } from 'features/prompt/usePrompt'; import { usePrompt } from 'features/prompt/usePrompt';
@ -11,7 +12,6 @@ import { memo, useCallback, useRef } from 'react';
import type { HotkeyCallback } from 'react-hotkeys-hook'; import type { HotkeyCallback } from 'react-hotkeys-hook';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ViewModePrompt } from '../Prompts/ViewModePrompt';
const DEFAULT_HEIGHT = 28; const DEFAULT_HEIGHT = 28;

View File

@ -1,9 +1,9 @@
import { Flex, Icon, Text, Tooltip, Box } from '@invoke-ai/ui-library'; import { Box, Flex, Icon, Text, Tooltip } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from '../../../../app/store/storeHooks'; import { useAppDispatch } from 'app/store/storeHooks';
import { viewModeChanged } from 'features/stylePresets/store/stylePresetSlice';
import { getViewModeChunks } from 'features/stylePresets/util/getViewModeChunks';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { PiEyeBold, PiQuestionBold } from 'react-icons/pi'; import { PiEyeBold } from 'react-icons/pi';
import { viewModeChanged } from '../../../stylePresets/store/stylePresetSlice';
import { getViewModeChunks } from '../../../stylePresets/util/getViewModeChunks';
export const ViewModePrompt = ({ export const ViewModePrompt = ({
presetPrompt, presetPrompt,
@ -51,11 +51,7 @@ export const ViewModePrompt = ({
<Box position="absolute" top={0} right={0} backgroundColor="rgba(0,0,0,0.75)" padding="2px 5px"> <Box position="absolute" top={0} right={0} backgroundColor="rgba(0,0,0,0.75)" padding="2px 5px">
<Flex alignItems="center" gap="1"> <Flex alignItems="center" gap="1">
<Tooltip <Tooltip label="This is how your prompt will look with your currently selected preset. To edit your prompt, click anywhere in the text box.">
label={
'This is how your prompt will look with your currently selected preset. To edit your prompt, click anywhere in the text box.'
}
>
<Flex> <Flex>
<Icon as={PiEyeBold} color="base.500" boxSize="12px" /> <Icon as={PiEyeBold} color="base.500" boxSize="12px" />
</Flex> </Flex>

View File

@ -1,98 +0,0 @@
import { useDisclosure } from '@invoke-ai/ui-library';
import { isNil } from 'lodash-es';
import type { ChangeEventHandler, FormEvent, FormEventHandler, KeyboardEventHandler, RefObject, SyntheticEvent } from 'react';
import { useCallback } from 'react';
import { flushSync } from 'react-dom';
type UseInsertTriggerArg = {
prompt: string;
paragraphRef: RefObject<HTMLParagraphElement>;
onChange: (v: string) => void;
};
export const usePromptContentEditable = ({ prompt, paragraphRef, onChange: _onChange }: UseInsertTriggerArg) => {
const { isOpen, onClose, onOpen } = useDisclosure();
const onChange = useCallback(
(e: any) => {
e.preventDefault();
_onChange(e.data)
},
[_onChange]
);
const insertTrigger = useCallback(
(v: string) => {
const element = paragraphRef.current;
if (!element) {
return;
}
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) {
// Insert at the end if no selection found
const newPrompt = prompt + v;
flushSync(() => {
_onChange(newPrompt);
});
return;
}
const range = selection.getRangeAt(0);
const cursorPosition = range.startOffset;
console.log({ cursorPosition })
const updatedPrompt = prompt.slice(0, cursorPosition) + v + prompt.slice(cursorPosition);
console.log({ updatedPrompt })
flushSync(() => {
_onChange(updatedPrompt);
});
},
[paragraphRef, _onChange, prompt]
);
const onFocus = useCallback(() => {
paragraphRef.current?.focus();
}, [paragraphRef]);
const handleClosePopover = useCallback(() => {
onClose();
onFocus();
}, [onFocus, onClose]);
const onSelect = useCallback(
(v: string) => {
insertTrigger(v)
handleClosePopover();
},
[handleClosePopover, insertTrigger]
);
const onKeyDown: KeyboardEventHandler<HTMLParagraphElement> = useCallback(
(e) => {
if (e.key === '<') {
onOpen();
e.preventDefault();
}
},
[onOpen]
);
return {
onChange,
isOpen,
onClose,
onOpen,
onSelect,
onKeyDown,
onFocus,
};
};

View File

@ -1,17 +1,20 @@
import { Flex, IconButton, Text, Box, ButtonGroup } from '@invoke-ai/ui-library'; import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice'; import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts'; import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
import { activeStylePresetChanged, viewModeChanged } from 'features/stylePresets/store/stylePresetSlice'; import { activeStylePresetChanged, viewModeChanged } from 'features/stylePresets/store/stylePresetSlice';
import type { MouseEventHandler } from 'react'; import type { MouseEventHandler } from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiEyeBold, PiStackSimpleBold, PiXBold } from 'react-icons/pi'; import { PiEyeBold, PiStackSimpleBold, PiXBold } from 'react-icons/pi';
import StylePresetImage from './StylePresetImage'; import StylePresetImage from './StylePresetImage';
export const ActiveStylePreset = () => { export const ActiveStylePreset = () => {
const { activeStylePreset, viewMode } = useAppSelector((s) => s.stylePreset); const { activeStylePreset, viewMode } = useAppSelector((s) => s.stylePreset);
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation();
const { presetModifiedPositivePrompt, presetModifiedNegativePrompt } = usePresetModifiedPrompts(); const { presetModifiedPositivePrompt, presetModifiedNegativePrompt } = usePresetModifiedPrompts();
@ -47,7 +50,7 @@ export const ActiveStylePreset = () => {
return ( return (
<Flex h="25px" alignItems="center"> <Flex h="25px" alignItems="center">
<Text fontSize="sm" fontWeight="semibold" color="base.300"> <Text fontSize="sm" fontWeight="semibold" color="base.300">
Choose Preset {t('stylePresets.choosePromptTemplate')}
</Text> </Text>
</Flex> </Flex>
); );

View File

@ -1,15 +1,15 @@
import { Button, Flex, FormControl, FormLabel, Icon, Input, Text } from '@invoke-ai/ui-library'; import { Button, Flex, FormControl, FormLabel, Input, Text } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { isModalOpenChanged, updatingStylePresetIdChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { isModalOpenChanged, 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';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { PiBracketsCurlyBold } from 'react-icons/pi'; import { useTranslation } from 'react-i18next';
import { useCreateStylePresetMutation, useUpdateStylePresetMutation } from 'services/api/endpoints/stylePresets'; import { useCreateStylePresetMutation, useUpdateStylePresetMutation } from 'services/api/endpoints/stylePresets';
import { StylePresetPromptField } from './StylePresetPromptField';
import { StylePresetImageField } from './StylePresetImageField'; import { StylePresetImageField } from './StylePresetImageField';
import { StylePresetPromptField } from './StylePresetPromptField';
export type StylePresetFormData = { export type StylePresetFormData = {
name: string; name: string;
@ -22,10 +22,11 @@ export const StylePresetForm = ({ updatingStylePresetId }: { updatingStylePreset
const [createStylePreset] = useCreateStylePresetMutation(); const [createStylePreset] = useCreateStylePresetMutation();
const [updateStylePreset] = useUpdateStylePresetMutation(); const [updateStylePreset] = useUpdateStylePresetMutation();
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation();
const defaultValues = useAppSelector((s) => s.stylePresetModal.prefilledFormData); const defaultValues = useAppSelector((s) => s.stylePresetModal.prefilledFormData);
const { handleSubmit, control, formState, reset, register } = useForm<StylePresetFormData>({ const { handleSubmit, control, register } = useForm<StylePresetFormData>({
defaultValues: defaultValues || { defaultValues: defaultValues || {
name: '', name: '',
positivePrompt: '', positivePrompt: '',
@ -70,19 +71,18 @@ export const StylePresetForm = ({ updatingStylePresetId }: { updatingStylePreset
<Flex alignItems="center" gap="4"> <Flex alignItems="center" gap="4">
<StylePresetImageField control={control} name="image" /> <StylePresetImageField control={control} name="image" />
<FormControl orientation="vertical"> <FormControl orientation="vertical">
<FormLabel>Name</FormLabel> <FormLabel>{t('stylePresets.name')}</FormLabel>
<Input size="md" {...register('name')} /> <Input size="md" {...register('name')} />
</FormControl> </FormControl>
</Flex> </Flex>
<StylePresetPromptField label="Positive Prompt" control={control} name="positivePrompt" /> <StylePresetPromptField label="Positive Prompt" control={control} name="positivePrompt" />
<StylePresetPromptField label="Negative Prompt" control={control} name="negativePrompt" /> <StylePresetPromptField label="Negative Prompt" control={control} name="negativePrompt" />
<Text variant="subtext"> <Text variant="subtext">{t('stylePresets.placeholderDirections')}</Text>
Use the <Icon as={PiBracketsCurlyBold} /> button to specify where your manual prompt should be included in the
template. If you do not provide one, the template will be appended to your prompt.
</Text>
<Button onClick={handleSubmit(handleClickSave)}>Save</Button> <Flex justifyContent="flex-end">
<Button onClick={handleSubmit(handleClickSave)}>{t('common.save')}</Button>
</Flex>
</Flex> </Flex>
); );
}; };

View File

@ -1,13 +1,16 @@
import { Tooltip, Flex, Button, Icon, Box, Image, IconButton } from '@invoke-ai/ui-library'; import { Box, Button, Flex, Icon, IconButton, Image, Tooltip } from '@invoke-ai/ui-library';
import { t } from 'i18next'; import { useCallback } from 'react';
import { useCallback, useState } from 'react';
import { useDropzone } from 'react-dropzone'; import { useDropzone } from 'react-dropzone';
import type { UseControllerProps } from 'react-hook-form';
import { useController } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { PiArrowCounterClockwiseBold, PiUploadSimpleBold } from 'react-icons/pi'; import { PiArrowCounterClockwiseBold, PiUploadSimpleBold } from 'react-icons/pi';
import { useController, UseControllerProps } from 'react-hook-form';
import { StylePresetFormData } from './StylePresetForm'; import type { StylePresetFormData } from './StylePresetForm';
export const StylePresetImageField = (props: UseControllerProps<StylePresetFormData>) => { export const StylePresetImageField = (props: UseControllerProps<StylePresetFormData>) => {
const { field } = useController(props); const { field } = useController(props);
const { t } = useTranslation();
const onDropAccepted = useCallback( const onDropAccepted = useCallback(
(files: File[]) => { (files: File[]) => {
const file = files[0]; const file = files[0];
@ -15,12 +18,12 @@ export const StylePresetImageField = (props: UseControllerProps<StylePresetFormD
field.onChange(file); field.onChange(file);
} }
}, },
[field, t] [field]
); );
const handleResetImage = useCallback(() => { const handleResetImage = useCallback(() => {
field.onChange(null); field.onChange(null);
}, []); }, [field]);
const { getInputProps, getRootProps } = useDropzone({ const { getInputProps, getRootProps } = useDropzone({
accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] }, accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg', '.png'] },
@ -46,8 +49,8 @@ export const StylePresetImageField = (props: UseControllerProps<StylePresetFormD
insetInlineEnd={0} insetInlineEnd={0}
insetBlockStart={0} insetBlockStart={0}
onClick={handleResetImage} onClick={handleResetImage}
aria-label={t('modelManager.deleteModelImage')} aria-label={t('stylePresets.deleteImage')}
tooltip={t('modelManager.deleteModelImage')} tooltip={t('stylePresets.deleteImage')}
icon={<PiArrowCounterClockwiseBold />} icon={<PiArrowCounterClockwiseBold />}
size="md" size="md"
variant="ghost" variant="ghost"
@ -58,7 +61,7 @@ export const StylePresetImageField = (props: UseControllerProps<StylePresetFormD
return ( return (
<> <>
<Tooltip label={t('modelManager.uploadImage')}> <Tooltip label={t('stylePresets.uploadImage')}>
<Flex <Flex
as={Button} as={Button}
w={65} w={65}

View File

@ -1,18 +1,21 @@
import { Badge, ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library'; import { Badge, ConfirmationAlertDialog, Flex, IconButton, Text, useDisclosure } from '@invoke-ai/ui-library';
import type { MouseEvent } from 'react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useImageUrlToBlob } from 'common/hooks/useImageUrlToBlob';
import { import {
isModalOpenChanged, isModalOpenChanged,
prefilledFormDataChanged, prefilledFormDataChanged,
updatingStylePresetIdChanged, updatingStylePresetIdChanged,
} from 'features/stylePresets/store/stylePresetModalSlice'; } from 'features/stylePresets/store/stylePresetModalSlice';
import { activeStylePresetChanged, isMenuOpenChanged } from 'features/stylePresets/store/stylePresetSlice'; import { activeStylePresetChanged, isMenuOpenChanged } from 'features/stylePresets/store/stylePresetSlice';
import { toast } from 'features/toast/toast';
import type { MouseEvent } from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPencilBold, PiTrashBold } from 'react-icons/pi'; import { 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';
import StylePresetImage from './StylePresetImage'; import StylePresetImage from './StylePresetImage';
import { useImageUrlToBlob } from 'common/hooks/useImageUrlToBlob';
export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithImage }) => { export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithImage }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
@ -20,6 +23,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset); const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
const { isOpen, onOpen, onClose } = useDisclosure(); const { isOpen, onOpen, onClose } = useDisclosure();
const imageUrlToBlob = useImageUrlToBlob(); const imageUrlToBlob = useImageUrlToBlob();
const { t } = useTranslation();
const handleClickEdit = useCallback( const handleClickEdit = useCallback(
async (e: MouseEvent<HTMLButtonElement>) => { async (e: MouseEvent<HTMLButtonElement>) => {
@ -43,7 +47,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
dispatch(updatingStylePresetIdChanged(preset.id)); dispatch(updatingStylePresetIdChanged(preset.id));
dispatch(isModalOpenChanged(true)); dispatch(isModalOpenChanged(true));
}, },
[dispatch, preset] [dispatch, preset, imageUrlToBlob]
); );
const handleClickApply = useCallback(async () => { const handleClickApply = useCallback(async () => {
@ -56,14 +60,23 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
e.stopPropagation(); e.stopPropagation();
onOpen(); onOpen();
}, },
[dispatch, preset] [onOpen]
); );
const handleDeletePreset = useCallback(async () => { const handleDeletePreset = useCallback(async () => {
try { try {
await deleteStylePreset(preset.id); await deleteStylePreset(preset.id);
} catch (error) {} toast({
}, [preset]); status: 'success',
title: t('stylePresets.templateDeleted'),
});
} catch (error) {
toast({
status: 'error',
title: t('stylePresets.unableToDeleteTemplate'),
});
}
}, [preset, t, deleteStylePreset]);
return ( return (
<> <>
@ -92,7 +105,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
bg="transparent" bg="transparent"
flexShrink={0} flexShrink={0}
> >
Active {t('stylePresets.active')}
</Badge> </Badge>
)} )}
</Flex> </Flex>
@ -101,14 +114,14 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
<IconButton <IconButton
size="sm" size="sm"
variant="ghost" variant="ghost"
aria-label="Edit" aria-label={t('stylePresets.editTemplate')}
onClick={handleClickEdit} onClick={handleClickEdit}
icon={<PiPencilBold />} icon={<PiPencilBold />}
/> />
<IconButton <IconButton
size="sm" size="sm"
variant="ghost" variant="ghost"
aria-label="Delete" aria-label={t('stylePresets.deleteTemplate')}
onClick={handleClickDelete} onClick={handleClickDelete}
colorScheme="error" colorScheme="error"
icon={<PiTrashBold />} icon={<PiTrashBold />}
@ -116,16 +129,16 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
</Flex> </Flex>
</Flex> </Flex>
<Flex flexDir="column"> <Flex flexDir="column" gap="1">
<Text fontSize="xs"> <Text fontSize="xs">
<Text as="span" fontWeight="semibold"> <Text as="span" fontWeight="semibold">
Positive prompt: {t('stylePresets.positivePrompt')}:
</Text>{' '} </Text>{' '}
{preset.preset_data.positive_prompt} {preset.preset_data.positive_prompt}
</Text> </Text>
<Text fontSize="xs"> <Text fontSize="xs">
<Text as="span" fontWeight="semibold"> <Text as="span" fontWeight="semibold">
Negative prompt: {t('stylePresets.negativePrompt')}:
</Text>{' '} </Text>{' '}
{preset.preset_data.negative_prompt} {preset.preset_data.negative_prompt}
</Text> </Text>
@ -135,12 +148,11 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordWithI
<ConfirmationAlertDialog <ConfirmationAlertDialog
isOpen={isOpen} isOpen={isOpen}
onClose={onClose} onClose={onClose}
title={'Delete preset'} title={t('stylePresets.deleteTemplate')}
acceptCallback={handleDeletePreset} acceptCallback={handleDeletePreset}
acceptButtonText={'Delete'} acceptButtonText="Delete"
> >
<p>{'Delete Preset?'}</p> <p>{t('stylePresets.deleteTemplate2')}</p>
<br />
</ConfirmationAlertDialog> </ConfirmationAlertDialog>
</> </>
); );

View File

@ -7,6 +7,7 @@ import {
updatingStylePresetIdChanged, updatingStylePresetIdChanged,
} from 'features/stylePresets/store/stylePresetModalSlice'; } from 'features/stylePresets/store/stylePresetModalSlice';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiPlusBold } from 'react-icons/pi'; import { PiPlusBold } from 'react-icons/pi';
import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets'; import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets'; import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets';
@ -42,6 +43,7 @@ export const StylePresetMenu = () => {
}); });
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleClickAddNew = useCallback(() => { const handleClickAddNew = useCallback(() => {
dispatch(prefilledFormDataChanged(null)); dispatch(prefilledFormDataChanged(null));
@ -67,13 +69,13 @@ export const StylePresetMenu = () => {
{data.presets.length === 0 && data.defaultPresets.length === 0 && ( {data.presets.length === 0 && data.defaultPresets.length === 0 && (
<Text m="20px" textAlign="center"> <Text m="20px" textAlign="center">
No matching presets {t('stylePrests.noMatchingTemplates')}
</Text> </Text>
)} )}
<StylePresetList title="My Presets" data={data.presets} /> <StylePresetList title={t('stylePresets.myTemplates')} data={data.presets} />
<StylePresetList title="Default Presets" data={data.defaultPresets} /> <StylePresetList title={t('stylePresets.defaultTemplates')} data={data.defaultPresets} />
</Flex> </Flex>
); );
}; };

View File

@ -10,17 +10,19 @@ import {
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { isModalOpenChanged, updatingStylePresetIdChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { isModalOpenChanged, updatingStylePresetIdChanged } from 'features/stylePresets/store/stylePresetModalSlice';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { StylePresetForm } from './StylePresetForm'; import { StylePresetForm } from './StylePresetForm';
export const StylePresetModal = () => { export const StylePresetModal = () => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { t } = useTranslation();
const isModalOpen = useAppSelector((s) => s.stylePresetModal.isModalOpen); const isModalOpen = useAppSelector((s) => s.stylePresetModal.isModalOpen);
const updatingStylePresetId = useAppSelector((s) => s.stylePresetModal.updatingStylePresetId); const updatingStylePresetId = useAppSelector((s) => s.stylePresetModal.updatingStylePresetId);
const modalTitle = useMemo(() => { const modalTitle = useMemo(() => {
return updatingStylePresetId ? `Update Style Preset` : `Create Style Preset`; return updatingStylePresetId ? t('stylePresets.updatePromptTemplate') : t('stylePresets.createPromptTemplate');
}, [updatingStylePresetId]); }, [updatingStylePresetId, t]);
const handleCloseModal = useCallback(() => { const handleCloseModal = useCallback(() => {
dispatch(updatingStylePresetIdChanged(null)); dispatch(updatingStylePresetIdChanged(null));

View File

@ -1,4 +1,5 @@
import { Flex, FormControl, FormLabel, IconButton, Textarea } from '@invoke-ai/ui-library'; import { Flex, FormControl, FormLabel, IconButton, Textarea } from '@invoke-ai/ui-library';
import { PRESET_PLACEHOLDER } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
import type { ChangeEventHandler } from 'react'; import type { ChangeEventHandler } from 'react';
import { useCallback, useMemo, useRef } from 'react'; import { useCallback, useMemo, useRef } from 'react';
import type { UseControllerProps } from 'react-hook-form'; import type { UseControllerProps } from 'react-hook-form';
@ -6,7 +7,6 @@ import { useController } from 'react-hook-form';
import { PiBracketsCurlyBold } from 'react-icons/pi'; import { PiBracketsCurlyBold } from 'react-icons/pi';
import type { StylePresetFormData } from './StylePresetForm'; import type { StylePresetFormData } from './StylePresetForm';
import { PRESET_PLACEHOLDER } from '../hooks/usePresetModifiedPrompts';
interface Props extends UseControllerProps<StylePresetFormData> { interface Props extends UseControllerProps<StylePresetFormData> {
label: string; label: string;

View File

@ -1,25 +1,27 @@
import { useAppSelector } from "app/store/storeHooks" import { useAppSelector } from 'app/store/storeHooks';
export const PRESET_PLACEHOLDER = `{prompt}` export const PRESET_PLACEHOLDER = `{prompt}`;
export const buildPresetModifiedPrompt = (presetPrompt: string, currentPrompt: string) => { export const buildPresetModifiedPrompt = (presetPrompt: string, currentPrompt: string) => {
return presetPrompt.includes(PRESET_PLACEHOLDER) ? presetPrompt.replace(new RegExp(PRESET_PLACEHOLDER), currentPrompt) : `${currentPrompt} ${presetPrompt}` return presetPrompt.includes(PRESET_PLACEHOLDER)
} ? presetPrompt.replace(new RegExp(PRESET_PLACEHOLDER), currentPrompt)
: `${currentPrompt} ${presetPrompt}`;
};
export const usePresetModifiedPrompts = () => { export const usePresetModifiedPrompts = () => {
const activeStylePreset = useAppSelector(s => s.stylePreset.activeStylePreset) const activeStylePreset = useAppSelector((s) => s.stylePreset.activeStylePreset);
const { positivePrompt, negativePrompt } = useAppSelector(s => s.controlLayers.present) const { positivePrompt, negativePrompt } = useAppSelector((s) => s.controlLayers.present);
if (!activeStylePreset) { if (!activeStylePreset) {
return { presetModifiedPositivePrompt: positivePrompt, presetModifiedNegativePrompt: negativePrompt } return { presetModifiedPositivePrompt: positivePrompt, presetModifiedNegativePrompt: negativePrompt };
} }
const { positive_prompt: presetPositivePrompt, negative_prompt: presetNegativePrompt } = activeStylePreset.preset_data; const { positive_prompt: presetPositivePrompt, negative_prompt: presetNegativePrompt } =
activeStylePreset.preset_data;
const presetModifiedPositivePrompt = buildPresetModifiedPrompt(presetPositivePrompt, positivePrompt) const presetModifiedPositivePrompt = buildPresetModifiedPrompt(presetPositivePrompt, positivePrompt);
const presetModifiedNegativePrompt = buildPresetModifiedPrompt(presetNegativePrompt, negativePrompt) const presetModifiedNegativePrompt = buildPresetModifiedPrompt(presetNegativePrompt, negativePrompt);
return { presetModifiedPositivePrompt, presetModifiedNegativePrompt } return { presetModifiedPositivePrompt, presetModifiedNegativePrompt };
} };

View File

@ -1,18 +1,16 @@
import type { PayloadAction } from '@reduxjs/toolkit'; import type { PayloadAction } from '@reduxjs/toolkit';
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
import type { RootState } from 'app/store/store'; import type { RootState } from 'app/store/store';
import type { StylePresetFormData } from 'features/stylePresets/components/StylePresetForm';
import type { StylePresetModalState } from './types'; import type { StylePresetModalState } from './types';
import { StylePresetFormData } from '../components/StylePresetForm';
export const initialState: StylePresetModalState = { export const initialState: StylePresetModalState = {
isModalOpen: false, isModalOpen: false,
updatingStylePresetId: null, updatingStylePresetId: null,
prefilledFormData: null prefilledFormData: null,
}; };
export const stylePresetModalSlice = createSlice({ export const stylePresetModalSlice = createSlice({
name: 'stylePresetModal', name: 'stylePresetModal',
initialState: initialState, initialState: initialState,
@ -29,6 +27,7 @@ export const stylePresetModalSlice = createSlice({
}, },
}); });
export const { isModalOpenChanged, updatingStylePresetIdChanged, prefilledFormDataChanged } = stylePresetModalSlice.actions; export const { isModalOpenChanged, updatingStylePresetIdChanged, prefilledFormDataChanged } =
stylePresetModalSlice.actions;
export const selectStylePresetModalSlice = (state: RootState) => state.stylePresetModal; export const selectStylePresetModalSlice = (state: RootState) => state.stylePresetModal;

View File

@ -5,15 +5,13 @@ import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePre
import type { StylePresetState } from './types'; import type { StylePresetState } from './types';
export const initialState: StylePresetState = { export const initialState: StylePresetState = {
isMenuOpen: false, isMenuOpen: false,
activeStylePreset: null, activeStylePreset: null,
searchTerm: "", searchTerm: '',
viewMode: false viewMode: false,
}; };
export const stylePresetSlice = createSlice({ export const stylePresetSlice = createSlice({
name: 'stylePreset', name: 'stylePreset',
initialState: initialState, initialState: initialState,
@ -33,6 +31,7 @@ export const stylePresetSlice = createSlice({
}, },
}); });
export const { isMenuOpenChanged, activeStylePresetChanged, searchTermChanged, viewModeChanged } = stylePresetSlice.actions; export const { isMenuOpenChanged, activeStylePresetChanged, searchTermChanged, viewModeChanged } =
stylePresetSlice.actions;
export const selectStylePresetSlice = (state: RootState) => state.stylePreset; export const selectStylePresetSlice = (state: RootState) => state.stylePreset;

View File

@ -1,17 +1,15 @@
import type { StylePresetRecordWithImage } from "services/api/endpoints/stylePresets"; import type { StylePresetFormData } from 'features/stylePresets/components/StylePresetForm';
import { StylePresetFormData } from "../components/StylePresetForm"; import type { StylePresetRecordWithImage } from 'services/api/endpoints/stylePresets';
export type StylePresetModalState = { export type StylePresetModalState = {
isModalOpen: boolean; isModalOpen: boolean;
updatingStylePresetId: string | null; updatingStylePresetId: string | null;
prefilledFormData: StylePresetFormData | null prefilledFormData: StylePresetFormData | null;
}; };
export type StylePresetState = { export type StylePresetState = {
isMenuOpen: boolean; isMenuOpen: boolean;
activeStylePreset: StylePresetRecordWithImage | null; activeStylePreset: StylePresetRecordWithImage | null;
searchTerm: string; searchTerm: string;
viewMode: boolean; viewMode: boolean;
} };

View File

@ -1,4 +1,4 @@
import { PRESET_PLACEHOLDER } from '../hooks/usePresetModifiedPrompts'; import { PRESET_PLACEHOLDER } from 'features/stylePresets/hooks/usePresetModifiedPrompts';
export const getViewModeChunks = (currentPrompt: string, presetPrompt?: string) => { export const getViewModeChunks = (currentPrompt: string, presetPrompt?: string) => {
if (!presetPrompt || !presetPrompt.length) { if (!presetPrompt || !presetPrompt.length) {

View File

@ -9,11 +9,11 @@ import { ControlSettingsAccordion } from 'features/settingsAccordions/components
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion'; import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
import { ImageSettingsAccordion } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion'; import { ImageSettingsAccordion } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion';
import { RefinerSettingsAccordion } from 'features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion'; import { RefinerSettingsAccordion } from 'features/settingsAccordions/components/RefinerSettingsAccordion/RefinerSettingsAccordion';
import { StylePresetMenu } from 'features/stylePresets/components/StylePresetMenu';
import { StylePresetMenuTrigger } from 'features/stylePresets/components/StylePresetMenuTrigger';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { memo } from 'react'; import { memo } from 'react';
import { StylePresetMenu } from '../../../stylePresets/components/StylePresetMenu';
import { StylePresetMenuTrigger } from '../../../stylePresets/components/StylePresetMenuTrigger';
const overlayScrollbarsStyles: CSSProperties = { const overlayScrollbarsStyles: CSSProperties = {
height: '100%', height: '100%',

View File

@ -1,16 +1,16 @@
import { Box, Flex } from '@invoke-ai/ui-library'; import { Box, Flex } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { Prompts } from 'features/parameters/components/Prompts/Prompts'; import { Prompts } from 'features/parameters/components/Prompts/Prompts';
import QueueControls from 'features/queue/components/QueueControls'; import QueueControls from 'features/queue/components/QueueControls';
import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion'; import { AdvancedSettingsAccordion } from 'features/settingsAccordions/components/AdvancedSettingsAccordion/AdvancedSettingsAccordion';
import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion'; import { GenerationSettingsAccordion } from 'features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion';
import { UpscaleSettingsAccordion } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion'; import { UpscaleSettingsAccordion } from 'features/settingsAccordions/components/UpscaleSettingsAccordion/UpscaleSettingsAccordion';
import { StylePresetMenu } from 'features/stylePresets/components/StylePresetMenu';
import { StylePresetMenuTrigger } from 'features/stylePresets/components/StylePresetMenuTrigger';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { memo } from 'react'; import { memo } from 'react';
import { useAppSelector } from '../../../../app/store/storeHooks';
import { StylePresetMenu } from '../../../stylePresets/components/StylePresetMenu';
import { StylePresetMenuTrigger } from '../../../stylePresets/components/StylePresetMenuTrigger';
const overlayScrollbarsStyles: CSSProperties = { const overlayScrollbarsStyles: CSSProperties = {
height: '100%', height: '100%',

View File

@ -2,7 +2,8 @@ import type { paths } from 'services/api/schema';
import { api, buildV1Url, LIST_TAG } from '..'; import { api, buildV1Url, LIST_TAG } from '..';
export type StylePresetRecordWithImage = paths['/api/v1/style_presets/i/{style_preset_id}']['get']['responses']['200']['content']['application/json'] export type StylePresetRecordWithImage =
paths['/api/v1/style_presets/i/{style_preset_id}']['get']['responses']['200']['content']['application/json'];
/** /**
* Builds an endpoint URL for the style_presets router * Builds an endpoint URL for the style_presets router
@ -60,7 +61,9 @@ export const stylePresetsApi = api.injectEndpoints({
}), }),
updateStylePreset: build.mutation< updateStylePreset: build.mutation<
paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['responses']['200']['content']['application/json'], paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['responses']['200']['content']['application/json'],
paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['requestBody']['content']['multipart/form-data'] & { id: string } paths['/api/v1/style_presets/i/{style_preset_id}']['patch']['requestBody']['content']['multipart/form-data'] & {
id: string;
}
>({ >({
query: ({ id, name, positive_prompt, negative_prompt, image }) => { query: ({ id, name, positive_prompt, negative_prompt, image }) => {
const formData = new FormData(); const formData = new FormData();
@ -72,12 +75,11 @@ export const stylePresetsApi = api.injectEndpoints({
formData.append('positive_prompt', positive_prompt); formData.append('positive_prompt', positive_prompt);
formData.append('negative_prompt', negative_prompt); formData.append('negative_prompt', negative_prompt);
return { return {
url: buildStylePresetsUrl(`i/${id}`), url: buildStylePresetsUrl(`i/${id}`),
method: 'PATCH', method: 'PATCH',
body: formData body: formData,
} };
}, },
invalidatesTags: (response, error, { id }) => [ invalidatesTags: (response, error, { id }) => [
{ type: 'StylePreset', id: LIST_TAG }, { type: 'StylePreset', id: LIST_TAG },