mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): use prompt bug when prompt has colon
This bug is related to the format in which we stored prompts for some time: an array of weighted subprompts. This caused some strife when recalling a prompt if the prompt had colons in it, due to our recently introduced handling of negative prompts. Currently there is no need to store a prompt as anything other than a string, so we revert to doing that. Compatibility with structured prompts is maintained via helper hook.
This commit is contained in:
parent
ab018ccdfe
commit
7ffaa17551
@ -1383,13 +1383,6 @@ class InvokeAIWebServer:
|
||||
# semantic drift
|
||||
rfc_dict["sampler"] = parameters["sampler_name"]
|
||||
|
||||
# display weighted subprompts (liable to change)
|
||||
subprompts = split_weighted_subprompts(
|
||||
parameters["prompt"], skip_normalize=True
|
||||
)
|
||||
subprompts = [{"prompt": x[0], "weight": x[1]} for x in subprompts]
|
||||
rfc_dict["prompt"] = subprompts
|
||||
|
||||
# 'variations' should always exist and be an array, empty or consisting of {'seed': seed, 'weight': weight} pairs
|
||||
variations = []
|
||||
|
||||
|
3
invokeai/frontend/src/app/invokeai.d.ts
vendored
3
invokeai/frontend/src/app/invokeai.d.ts
vendored
@ -29,7 +29,8 @@ export declare type PromptItem = {
|
||||
weight: number;
|
||||
};
|
||||
|
||||
export declare type Prompt = Array<PromptItem>;
|
||||
// TECHDEBT: We need to retain compatibility with plain prompt strings and the structure Prompt type
|
||||
export declare type Prompt = Array<PromptItem> | string;
|
||||
|
||||
export declare type SeedWeightPair = {
|
||||
seed: number;
|
||||
|
@ -1,9 +1,11 @@
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import promptToString from './promptToString';
|
||||
|
||||
export function getPromptAndNegative(input_prompt: InvokeAI.Prompt) {
|
||||
let prompt: string = promptToString(input_prompt);
|
||||
let negativePrompt: string | null = null;
|
||||
export function getPromptAndNegative(inputPrompt: InvokeAI.Prompt) {
|
||||
let prompt: string =
|
||||
typeof inputPrompt === 'string' ? inputPrompt : promptToString(inputPrompt);
|
||||
|
||||
let negativePrompt = '';
|
||||
|
||||
// Matches all negative prompts, 1st capturing group is the prompt itself
|
||||
const negativePromptRegExp = new RegExp(/\[([^\][]*)]/, 'gi');
|
||||
|
@ -1,6 +1,10 @@
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
|
||||
const promptToString = (prompt: InvokeAI.Prompt): string => {
|
||||
if (typeof prompt === 'string') {
|
||||
return prompt;
|
||||
}
|
||||
|
||||
if (prompt.length === 1) {
|
||||
return prompt[0].prompt;
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
import {
|
||||
setDoesCanvasNeedScaling,
|
||||
setInitialCanvasImage,
|
||||
@ -20,8 +19,6 @@ import UpscaleSettings from 'features/parameters/components/AdvancedParameters/U
|
||||
import {
|
||||
setAllParameters,
|
||||
setInitialImage,
|
||||
setNegativePrompt,
|
||||
setPrompt,
|
||||
setSeed,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors';
|
||||
@ -53,6 +50,8 @@ import {
|
||||
} from 'react-icons/fa';
|
||||
import { gallerySelector } from '../store/gallerySelectors';
|
||||
import DeleteImageModal from './DeleteImageModal';
|
||||
import { useCallback } from 'react';
|
||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||
|
||||
const currentImageButtonsSelector = createSelector(
|
||||
[
|
||||
@ -125,6 +124,7 @@ const CurrentImageButtons = () => {
|
||||
|
||||
const toast = useToast();
|
||||
const { t } = useTranslation();
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
|
||||
const handleClickUseAsInitialImage = () => {
|
||||
if (!currentImage) return;
|
||||
@ -253,18 +253,11 @@ const CurrentImageButtons = () => {
|
||||
[currentImage]
|
||||
);
|
||||
|
||||
const handleClickUsePrompt = () => {
|
||||
const handleClickUsePrompt = useCallback(() => {
|
||||
if (currentImage?.metadata?.image?.prompt) {
|
||||
const [prompt, negativePrompt] = getPromptAndNegative(
|
||||
currentImage?.metadata?.image?.prompt
|
||||
);
|
||||
|
||||
prompt && dispatch(setPrompt(prompt));
|
||||
negativePrompt
|
||||
? dispatch(setNegativePrompt(negativePrompt))
|
||||
: dispatch(setNegativePrompt(''));
|
||||
setBothPrompts(currentImage?.metadata?.image?.prompt);
|
||||
}
|
||||
};
|
||||
}, [currentImage?.metadata?.image?.prompt, setBothPrompts]);
|
||||
|
||||
useHotkeys(
|
||||
'p',
|
||||
|
@ -8,8 +8,6 @@ import {
|
||||
setAllImageToImageParameters,
|
||||
setAllParameters,
|
||||
setInitialImage,
|
||||
setNegativePrompt,
|
||||
setPrompt,
|
||||
setSeed,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { DragEvent, memo, useState } from 'react';
|
||||
@ -18,7 +16,6 @@ import DeleteImageModal from './DeleteImageModal';
|
||||
|
||||
import * as ContextMenu from '@radix-ui/react-context-menu';
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
import {
|
||||
resizeAndScaleCanvas,
|
||||
setInitialCanvasImage,
|
||||
@ -26,6 +23,7 @@ import {
|
||||
import { hoverableImageSelector } from 'features/gallery/store/gallerySelectors';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||
|
||||
interface HoverableImageProps {
|
||||
image: InvokeAI.Image;
|
||||
@ -55,23 +53,16 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
const { t } = useTranslation();
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
|
||||
const handleMouseOver = () => setIsHovered(true);
|
||||
|
||||
const handleMouseOut = () => setIsHovered(false);
|
||||
|
||||
const handleUsePrompt = () => {
|
||||
if (image.metadata) {
|
||||
const [prompt, negativePrompt] = getPromptAndNegative(
|
||||
image.metadata?.image?.prompt
|
||||
);
|
||||
|
||||
prompt && dispatch(setPrompt(prompt));
|
||||
negativePrompt
|
||||
? dispatch(setNegativePrompt(negativePrompt))
|
||||
: dispatch(setNegativePrompt(''));
|
||||
if (image.metadata?.image?.prompt) {
|
||||
setBothPrompts(image.metadata?.image?.prompt);
|
||||
}
|
||||
|
||||
toast({
|
||||
|
@ -12,6 +12,7 @@ import * as InvokeAI from 'app/invokeai';
|
||||
import { useAppDispatch } from 'app/storeHooks';
|
||||
import promptToString from 'common/util/promptToString';
|
||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||
import {
|
||||
setCfgScale,
|
||||
setHeight,
|
||||
@ -19,7 +20,6 @@ import {
|
||||
setInitialImage,
|
||||
setMaskPath,
|
||||
setPerlin,
|
||||
setPrompt,
|
||||
setSampler,
|
||||
setSeamless,
|
||||
setSeed,
|
||||
@ -129,6 +129,8 @@ const ImageMetadataViewer = memo(
|
||||
({ image, styleClass }: ImageMetadataViewerProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
|
||||
useHotkeys('esc', () => {
|
||||
dispatch(setShouldShowImageDetails(false));
|
||||
});
|
||||
@ -152,7 +154,6 @@ const ImageMetadataViewer = memo(
|
||||
seed,
|
||||
steps,
|
||||
strength,
|
||||
denoise_str,
|
||||
threshold,
|
||||
type,
|
||||
variations,
|
||||
@ -189,8 +190,10 @@ const ImageMetadataViewer = memo(
|
||||
<MetadataItem
|
||||
label="Prompt"
|
||||
labelPosition="top"
|
||||
value={promptToString(prompt)}
|
||||
onClick={() => dispatch(setPrompt(prompt))}
|
||||
value={
|
||||
typeof prompt === 'string' ? prompt : promptToString(prompt)
|
||||
}
|
||||
onClick={() => setBothPrompts(prompt)}
|
||||
/>
|
||||
)}
|
||||
{seed !== undefined && (
|
||||
|
26
invokeai/frontend/src/features/parameters/hooks/usePrompt.ts
Normal file
26
invokeai/frontend/src/features/parameters/hooks/usePrompt.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
|
||||
import * as InvokeAI from 'app/invokeai';
|
||||
import promptToString from 'common/util/promptToString';
|
||||
import { useAppDispatch } from 'app/storeHooks';
|
||||
import { setNegativePrompt, setPrompt } from '../store/generationSlice';
|
||||
|
||||
// TECHDEBT: We have two metadata prompt formats and need to handle recalling either of them.
|
||||
// This hook provides a function to do that.
|
||||
const useSetBothPrompts = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (inputPrompt: InvokeAI.Prompt) => {
|
||||
const promptString =
|
||||
typeof inputPrompt === 'string'
|
||||
? inputPrompt
|
||||
: promptToString(inputPrompt);
|
||||
|
||||
const [prompt, negativePrompt] = getPromptAndNegative(promptString);
|
||||
|
||||
dispatch(setPrompt(prompt));
|
||||
dispatch(setNegativePrompt(negativePrompt));
|
||||
};
|
||||
};
|
||||
|
||||
export default useSetBothPrompts;
|
Loading…
Reference in New Issue
Block a user