mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Add Negative Prompts Box
This commit is contained in:
parent
4a026bd46e
commit
7604b36577
@ -43,6 +43,7 @@
|
||||
"invoke": "Invoke",
|
||||
"cancel": "Cancel",
|
||||
"promptPlaceholder": "Type prompt here. [negative tokens], (upweight)++, (downweight)--, swap and blend are available (see docs)",
|
||||
"negativePrompts": "Negative Prompts",
|
||||
"sendTo": "Send to",
|
||||
"sendToImg2Img": "Send to Image to Image",
|
||||
"sendToUnifiedCanvas": "Send To Unified Canvas",
|
||||
|
@ -11,7 +11,6 @@ const useClickOutsideWatcher = () => {
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
watchers.forEach(({ ref, enable, callback }) => {
|
||||
if (enable && ref.current && !ref.current.contains(e.target as Node)) {
|
||||
console.log('callback');
|
||||
callback();
|
||||
}
|
||||
});
|
||||
|
20
invokeai/frontend/src/common/util/getPromptAndNegative.ts
Normal file
20
invokeai/frontend/src/common/util/getPromptAndNegative.ts
Normal file
@ -0,0 +1,20 @@
|
||||
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;
|
||||
|
||||
const negativePromptRegExp = new RegExp(/(?<=\[)[^\][]*(?=])/, 'gi');
|
||||
const negativePromptMatches = [...prompt.matchAll(negativePromptRegExp)];
|
||||
|
||||
if (negativePromptMatches && negativePromptMatches.length > 0) {
|
||||
negativePrompt = negativePromptMatches.join(', ');
|
||||
prompt = prompt
|
||||
.replaceAll(negativePromptRegExp, '')
|
||||
.replaceAll('[]', '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
return [prompt, negativePrompt];
|
||||
}
|
@ -106,6 +106,7 @@ export const frontendToBackendParameters = (
|
||||
iterations,
|
||||
perlin,
|
||||
prompt,
|
||||
negativePrompt,
|
||||
sampler,
|
||||
seamBlur,
|
||||
seamless,
|
||||
@ -155,6 +156,10 @@ export const frontendToBackendParameters = (
|
||||
let esrganParameters: false | BackendEsrGanParameters = false;
|
||||
let facetoolParameters: false | BackendFacetoolParameters = false;
|
||||
|
||||
if (negativePrompt !== '') {
|
||||
generationParameters.prompt = `${prompt} [${negativePrompt}]`;
|
||||
}
|
||||
|
||||
generationParameters.seed = shouldRandomizeSeed
|
||||
? randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX)
|
||||
: seed;
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
setAllParameters,
|
||||
setInitialImage,
|
||||
setIsLightBoxOpen,
|
||||
setNegativePrompt,
|
||||
setPrompt,
|
||||
setSeed,
|
||||
setShouldShowImageDetails,
|
||||
@ -44,6 +45,7 @@ import { GalleryState } from 'features/gallery/store/gallerySlice';
|
||||
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
|
||||
import IAIPopover from 'common/components/IAIPopover';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
|
||||
const systemSelector = createSelector(
|
||||
[
|
||||
@ -241,9 +243,18 @@ const CurrentImageButtons = () => {
|
||||
[currentImage]
|
||||
);
|
||||
|
||||
const handleClickUsePrompt = () =>
|
||||
currentImage?.metadata?.image?.prompt &&
|
||||
dispatch(setPrompt(currentImage.metadata.image.prompt));
|
||||
const handleClickUsePrompt = () => {
|
||||
if (currentImage?.metadata?.image?.prompt) {
|
||||
const [prompt, negativePrompt] = getPromptAndNegative(
|
||||
currentImage?.metadata?.image?.prompt
|
||||
);
|
||||
|
||||
prompt && dispatch(setPrompt(prompt));
|
||||
negativePrompt
|
||||
? dispatch(setNegativePrompt(negativePrompt))
|
||||
: dispatch(setNegativePrompt(''));
|
||||
}
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
'p',
|
||||
|
@ -10,9 +10,10 @@ import { DragEvent, memo, useState } from 'react';
|
||||
import {
|
||||
setActiveTab,
|
||||
setAllImageToImageParameters,
|
||||
setAllTextToImageParameters,
|
||||
setAllParameters,
|
||||
setInitialImage,
|
||||
setIsLightBoxOpen,
|
||||
setNegativePrompt,
|
||||
setPrompt,
|
||||
setSeed,
|
||||
} from 'features/options/store/optionsSlice';
|
||||
@ -24,6 +25,7 @@ import {
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { hoverableImageSelector } from 'features/gallery/store/gallerySliceSelectors';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
|
||||
interface HoverableImageProps {
|
||||
image: InvokeAI.Image;
|
||||
@ -62,7 +64,17 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const handleMouseOut = () => setIsHovered(false);
|
||||
|
||||
const handleUsePrompt = () => {
|
||||
image.metadata && dispatch(setPrompt(image.metadata.image.prompt));
|
||||
if (image.metadata) {
|
||||
const [prompt, negativePrompt] = getPromptAndNegative(
|
||||
image.metadata?.image?.prompt
|
||||
);
|
||||
|
||||
prompt && dispatch(setPrompt(prompt));
|
||||
negativePrompt
|
||||
? dispatch(setNegativePrompt(negativePrompt))
|
||||
: dispatch(setNegativePrompt(''));
|
||||
}
|
||||
|
||||
toast({
|
||||
title: t('toast:promptSet'),
|
||||
status: 'success',
|
||||
@ -115,7 +127,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
};
|
||||
|
||||
const handleUseAllParameters = () => {
|
||||
metadata && dispatch(setAllTextToImageParameters(metadata));
|
||||
metadata && dispatch(setAllParameters(metadata));
|
||||
toast({
|
||||
title: t('toast:parametersSet'),
|
||||
status: 'success',
|
||||
|
@ -38,7 +38,6 @@ export const uploadImage =
|
||||
});
|
||||
|
||||
const image = (await response.json()) as InvokeAI.ImageUploadResponse;
|
||||
console.log(image);
|
||||
const newImage: InvokeAI.Image = {
|
||||
uuid: uuidv4(),
|
||||
category: 'user',
|
||||
|
@ -0,0 +1,38 @@
|
||||
import { FormControl, Textarea } from '@chakra-ui/react';
|
||||
import type { RootState } from 'app/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import { setNegativePrompt } from 'features/options/store/optionsSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function NegativePromptInput() {
|
||||
const negativePrompt = useAppSelector(
|
||||
(state: RootState) => state.options.negativePrompt
|
||||
);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<FormControl>
|
||||
<Textarea
|
||||
id="negativePrompt"
|
||||
name="negativePrompt"
|
||||
value={negativePrompt}
|
||||
onChange={(e) => dispatch(setNegativePrompt(e.target.value))}
|
||||
background="var(--prompt-bg-color)"
|
||||
placeholder={t('options:negativePrompts')}
|
||||
_placeholder={{ fontSize: '0.8rem' }}
|
||||
borderColor="var(--border-color)"
|
||||
_hover={{
|
||||
borderColor: 'var(--border-color-light)',
|
||||
}}
|
||||
_focusVisible={{
|
||||
borderColor: 'var(--border-color-invalid)',
|
||||
boxShadow: '0 0 10px var(--box-shadow-color-invalid)',
|
||||
}}
|
||||
fontSize="0.9rem"
|
||||
color="var(--text-color-secondary)"
|
||||
/>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
@ -5,6 +5,7 @@ import promptToString from 'common/util/promptToString';
|
||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||
import { FACETOOL_TYPES } from 'app/constants';
|
||||
import { InvokeTabName, tabMap } from 'features/tabs/tabMap';
|
||||
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
|
||||
export type UpscalingLevel = 2 | 4;
|
||||
|
||||
@ -28,6 +29,7 @@ export interface OptionsState {
|
||||
optionsPanelScrollPosition: number;
|
||||
perlin: number;
|
||||
prompt: string;
|
||||
negativePrompt: string;
|
||||
sampler: string;
|
||||
seamBlur: number;
|
||||
seamless: boolean;
|
||||
@ -77,6 +79,7 @@ const initialOptionsState: OptionsState = {
|
||||
optionsPanelScrollPosition: 0,
|
||||
perlin: 0,
|
||||
prompt: '',
|
||||
negativePrompt: '',
|
||||
sampler: 'k_lms',
|
||||
seamBlur: 16,
|
||||
seamless: false,
|
||||
@ -123,6 +126,17 @@ export const optionsSlice = createSlice({
|
||||
state.prompt = promptToString(newPrompt);
|
||||
}
|
||||
},
|
||||
setNegativePrompt: (
|
||||
state,
|
||||
action: PayloadAction<string | InvokeAI.Prompt>
|
||||
) => {
|
||||
const newPrompt = action.payload;
|
||||
if (typeof newPrompt === 'string') {
|
||||
state.negativePrompt = newPrompt;
|
||||
} else {
|
||||
state.negativePrompt = promptToString(newPrompt);
|
||||
}
|
||||
},
|
||||
setIterations: (state, action: PayloadAction<number>) => {
|
||||
state.iterations = action.payload;
|
||||
},
|
||||
@ -307,7 +321,14 @@ export const optionsSlice = createSlice({
|
||||
state.shouldRandomizeSeed = false;
|
||||
}
|
||||
|
||||
if (prompt) state.prompt = promptToString(prompt);
|
||||
if (prompt) {
|
||||
const [promptOnly, negativePrompt] = getPromptAndNegative(prompt);
|
||||
if (promptOnly) state.prompt = promptOnly;
|
||||
negativePrompt
|
||||
? (state.negativePrompt = negativePrompt)
|
||||
: (state.negativePrompt = '');
|
||||
}
|
||||
|
||||
if (sampler) state.sampler = sampler;
|
||||
if (steps) state.steps = steps;
|
||||
if (cfg_scale) state.cfgScale = cfg_scale;
|
||||
@ -448,6 +469,7 @@ export const {
|
||||
setParameter,
|
||||
setPerlin,
|
||||
setPrompt,
|
||||
setNegativePrompt,
|
||||
setSampler,
|
||||
setSeamBlur,
|
||||
setSeamless,
|
||||
|
@ -19,6 +19,8 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
||||
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
|
||||
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
|
||||
|
||||
export default function ImageToImagePanel() {
|
||||
const { t } = useTranslation();
|
||||
@ -67,7 +69,10 @@ export default function ImageToImagePanel() {
|
||||
|
||||
return (
|
||||
<InvokeOptionsPanel>
|
||||
<PromptInput />
|
||||
<Flex flexDir="column" rowGap="0.5rem">
|
||||
<PromptInput />
|
||||
<NegativePromptInput />
|
||||
</Flex>
|
||||
<ProcessButtons />
|
||||
<MainOptions />
|
||||
<ImageToImageStrength
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { Feature } from 'app/features';
|
||||
import FaceRestoreOptions from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreOptions';
|
||||
import FaceRestoreToggle from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreToggle';
|
||||
@ -10,6 +11,7 @@ import VariationsOptions from 'features/options/components/AdvancedOptions/Varia
|
||||
import MainOptions from 'features/options/components/MainOptions/MainOptions';
|
||||
import OptionsAccordion from 'features/options/components/OptionsAccordion';
|
||||
import ProcessButtons from 'features/options/components/ProcessButtons/ProcessButtons';
|
||||
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
|
||||
import PromptInput from 'features/options/components/PromptInput/PromptInput';
|
||||
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -50,7 +52,10 @@ export default function TextToImagePanel() {
|
||||
|
||||
return (
|
||||
<InvokeOptionsPanel>
|
||||
<PromptInput />
|
||||
<Flex flexDir="column" rowGap="0.5rem">
|
||||
<PromptInput />
|
||||
<NegativePromptInput />
|
||||
</Flex>
|
||||
<ProcessButtons />
|
||||
<MainOptions />
|
||||
<OptionsAccordion accordionInfo={textToImageAccordions} />
|
||||
|
@ -13,6 +13,8 @@ import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
|
||||
import BoundingBoxSettings from 'features/options/components/AdvancedOptions/Canvas/BoundingBoxSettings/BoundingBoxSettings';
|
||||
import InfillAndScalingOptions from 'features/options/components/AdvancedOptions/Canvas/InfillAndScalingOptions';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
|
||||
|
||||
export default function UnifiedCanvasPanel() {
|
||||
const { t } = useTranslation();
|
||||
@ -48,7 +50,10 @@ export default function UnifiedCanvasPanel() {
|
||||
|
||||
return (
|
||||
<InvokeOptionsPanel>
|
||||
<PromptInput />
|
||||
<Flex flexDir="column" rowGap="0.5rem">
|
||||
<PromptInput />
|
||||
<NegativePromptInput />
|
||||
</Flex>
|
||||
<ProcessButtons />
|
||||
<MainOptions />
|
||||
<ImageToImageStrength
|
||||
|
Loading…
Reference in New Issue
Block a user