From 857d74bbfe282440df918a9e8a69c3b13bf9633b Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Mon, 5 Aug 2024 19:11:48 -0400 Subject: [PATCH] wip apply and calculate prompt with interpolation --- .../middleware/listenerMiddleware/index.ts | 2 + .../listeners/activeStylePresetChanged.ts | 31 +++++++++++++++ invokeai/frontend/web/src/app/store/store.ts | 6 ++- .../parameters/components/Prompts/Prompts.tsx | 4 ++ .../components/StylePresetForm.tsx | 12 ++---- .../components/StylePresetListItem.tsx | 11 +++++- .../components/StylePresetMenu.tsx | 2 +- .../components/StylePresetMenuTrigger.tsx | 28 +++++++++----- .../components/StylePresetModal.tsx | 2 +- .../{slice.ts => stylePresetModalSlice.ts} | 4 +- .../stylePresets/store/stylePresetSlice.ts | 38 +++++++++++++++++++ .../src/features/stylePresets/store/types.ts | 8 +++- 12 files changed, 122 insertions(+), 26 deletions(-) create mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/activeStylePresetChanged.ts rename invokeai/frontend/web/src/features/stylePresets/store/{slice.ts => stylePresetModalSlice.ts} (89%) create mode 100644 invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index a1ce52b407..15420bf768 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -53,6 +53,7 @@ import type { AppDispatch, RootState } from 'app/store/store'; import { addArchivedOrDeletedBoardListener } from './listeners/addArchivedOrDeletedBoardListener'; import { addEnqueueRequestedUpscale } from './listeners/enqueueRequestedUpscale'; +import { addActiveStylePresetChanged } from './listeners/activeStylePresetChanged'; export const listenerMiddleware = createListenerMiddleware(); @@ -146,6 +147,7 @@ addAdHocPostProcessingRequestedListener(startAppListening); // Prompts addDynamicPromptsListener(startAppListening); +addActiveStylePresetChanged(startAppListening) addSetDefaultSettingsListener(startAppListening); addControlAdapterPreprocessor(startAppListening); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/activeStylePresetChanged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/activeStylePresetChanged.ts new file mode 100644 index 0000000000..aa146bcc29 --- /dev/null +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/activeStylePresetChanged.ts @@ -0,0 +1,31 @@ +import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { negativePromptChanged, positivePromptChanged, } from 'features/controlLayers/store/controlLayersSlice'; +import { activeStylePresetChanged, calculatedNegPromptChanged, calculatedPosPromptChanged } from '../../../../../features/stylePresets/store/stylePresetSlice'; +import { isAnyOf } from '@reduxjs/toolkit'; + +export const addActiveStylePresetChanged = (startAppListening: AppStartListening) => { + startAppListening({ + matcher: isAnyOf(activeStylePresetChanged, positivePromptChanged, negativePromptChanged), + effect: async (action, { dispatch, getState }) => { + const state = getState(); + + const activeStylePreset = state.stylePreset.activeStylePreset; + const positivePrompt = state.controlLayers.present.positivePrompt + const negativePrompt = state.controlLayers.present.negativePrompt + + if (!activeStylePreset) { + return; + } + + const { positive_prompt: presetPositivePrompt, negative_prompt: presetNegativePrompt } = activeStylePreset.preset_data; + + const calculatedPosPrompt = presetPositivePrompt.includes('{prompt}') ? presetPositivePrompt.replace('{prompt}', positivePrompt) : `${positivePrompt} ${presetPositivePrompt}` + + const calculatedNegPrompt = presetNegativePrompt.includes('{prompt}') ? presetNegativePrompt.replace('{prompt}', negativePrompt) : `${negativePrompt} ${presetNegativePrompt}` + + dispatch(calculatedPosPromptChanged(calculatedPosPrompt)) + + dispatch(calculatedNegPromptChanged(calculatedNegPrompt)) + }, + }); +}; diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index f061d0e59f..70326ea5fd 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -28,7 +28,7 @@ import { generationPersistConfig, generationSlice } from 'features/parameters/st import { upscalePersistConfig, upscaleSlice } from 'features/parameters/store/upscaleSlice'; import { queueSlice } from 'features/queue/store/queueSlice'; import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice'; -import { stylePresetModalSlice } from 'features/stylePresets/store/slice'; +import { stylePresetModalSlice } from 'features/stylePresets/store/stylePresetModalSlice'; import { configSlice } from 'features/system/store/configSlice'; import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice'; import { uiPersistConfig, uiSlice } from 'features/ui/store/uiSlice'; @@ -47,6 +47,7 @@ import { actionSanitizer } from './middleware/devtools/actionSanitizer'; import { actionsDenylist } from './middleware/devtools/actionsDenylist'; import { stateSanitizer } from './middleware/devtools/stateSanitizer'; import { listenerMiddleware } from './middleware/listenerMiddleware'; +import { stylePresetSlice } from '../../features/stylePresets/store/stylePresetSlice'; const allReducers = { [canvasSlice.name]: canvasSlice.reducer, @@ -70,7 +71,8 @@ const allReducers = { [workflowSettingsSlice.name]: workflowSettingsSlice.reducer, [api.reducerPath]: api.reducer, [upscaleSlice.name]: upscaleSlice.reducer, - [stylePresetModalSlice.name]: stylePresetModalSlice.reducer + [stylePresetModalSlice.name]: stylePresetModalSlice.reducer, + [stylePresetSlice.name]: stylePresetSlice.reducer }; const rootReducer = combineReducers(allReducers); diff --git a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx b/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx index 39746c9ba6..aefe15efe7 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx @@ -19,12 +19,16 @@ const concatPromptsSelector = createSelector( export const Prompts = memo(() => { const shouldConcatPrompts = useAppSelector(concatPromptsSelector); + const calculatedPosPrompt = useAppSelector((s) => s.stylePreset.calculatedPosPrompt); + const calculatedNegPrompt = useAppSelector((s) => s.stylePreset.calculatedNegPrompt); return ( + {calculatedPosPrompt} {!shouldConcatPrompts && } + {calculatedNegPrompt} {!shouldConcatPrompts && } ); diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx index 201c1e27cc..c8faf9fc2e 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx @@ -1,15 +1,11 @@ import { Button, Flex, FormControl, FormLabel, Input, Textarea } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; -import { isModalOpenChanged,updatingStylePresetChanged } from 'features/stylePresets/store/slice'; +import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { toast } from 'features/toast/toast'; -import type { ChangeEventHandler} from 'react'; +import type { ChangeEventHandler } from 'react'; import { useCallback, useEffect, useState } from 'react'; -import type { - StylePresetRecordDTO} from 'services/api/endpoints/stylePresets'; -import { - useCreateStylePresetMutation, - useUpdateStylePresetMutation, -} from 'services/api/endpoints/stylePresets'; +import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets'; +import { useCreateStylePresetMutation, useUpdateStylePresetMutation } from 'services/api/endpoints/stylePresets'; export const StylePresetForm = ({ updatingPreset }: { updatingPreset: StylePresetRecordDTO | null }) => { const [createStylePreset] = useCreateStylePresetMutation(); diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx index 86a5b78b04..321c5a09bb 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetListItem.tsx @@ -1,9 +1,10 @@ import { Button, Flex, Text } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; -import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/slice'; +import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { useCallback } from 'react'; -import type { StylePresetRecordDTO} from 'services/api/endpoints/stylePresets'; +import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets'; import { useDeleteStylePresetMutation } from 'services/api/endpoints/stylePresets'; +import { activeStylePresetChanged, isMenuOpenChanged } from '../store/stylePresetSlice'; export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordDTO }) => { const dispatch = useAppDispatch(); @@ -14,6 +15,11 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordDTO } dispatch(isModalOpenChanged(true)); }, [dispatch, preset]); + const handleClickApply = useCallback(() => { + dispatch(activeStylePresetChanged(preset)); + dispatch(isMenuOpenChanged(false)); + }, [dispatch, preset]); + const handleDeletePreset = useCallback(async () => { try { await deleteStylePreset(preset.id); @@ -39,6 +45,7 @@ export const StylePresetListItem = ({ preset }: { preset: StylePresetRecordDTO } + diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx index e4fd240820..374aa37be3 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenu.tsx @@ -1,6 +1,6 @@ import { Button, Flex, Text } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; -import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/slice'; +import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { useCallback } from 'react'; import { useListStylePresetsQuery } from 'services/api/endpoints/stylePresets'; diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenuTrigger.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenuTrigger.tsx index 0b5fe7b9be..889695d5ee 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenuTrigger.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetMenuTrigger.tsx @@ -1,18 +1,28 @@ -import { - Button, - Popover, - PopoverBody, - PopoverContent, - PopoverTrigger, -} from '@invoke-ai/ui-library'; +import { Button, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library'; import { StylePresetMenu } from './StylePresetMenu'; +import { useAppDispatch, useAppSelector } from '../../../app/store/storeHooks'; +import { useCallback } from 'react'; +import { isMenuOpenChanged } from '../store/stylePresetSlice'; export const StylePresetMenuTrigger = () => { + const isMenuOpen = useAppSelector((s) => s.stylePreset.isMenuOpen); + const dispatch = useAppDispatch(); + + const handleClose = useCallback(() => { + dispatch(isMenuOpenChanged(false)); + }, [dispatch]); + + const handleToggle = useCallback(() => { + dispatch(isMenuOpenChanged(!isMenuOpen)); + }, [dispatch, isMenuOpen]); + return ( - + - + diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx index a783d93db2..02be01e31f 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetModal.tsx @@ -8,7 +8,7 @@ import { ModalOverlay, } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/slice'; +import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { useCallback, useMemo } from 'react'; import { StylePresetForm } from './StylePresetForm'; diff --git a/invokeai/frontend/web/src/features/stylePresets/store/slice.ts b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModalSlice.ts similarity index 89% rename from invokeai/frontend/web/src/features/stylePresets/store/slice.ts rename to invokeai/frontend/web/src/features/stylePresets/store/stylePresetModalSlice.ts index 4ba8b881c3..b0dc894037 100644 --- a/invokeai/frontend/web/src/features/stylePresets/store/slice.ts +++ b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetModalSlice.ts @@ -3,10 +3,10 @@ import { createSlice } from '@reduxjs/toolkit'; import type { RootState } from 'app/store/store'; import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets'; -import type { StylePresetState } from './types'; +import type { StylePresetModalState } from './types'; -export const initialState: StylePresetState = { +export const initialState: StylePresetModalState = { isModalOpen: false, updatingStylePreset: null, }; diff --git a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts new file mode 100644 index 0000000000..39cfd30487 --- /dev/null +++ b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts @@ -0,0 +1,38 @@ +import type { PayloadAction } from '@reduxjs/toolkit'; +import { createSlice } from '@reduxjs/toolkit'; +import type { RootState } from 'app/store/store'; +import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets'; + +import type { StylePresetState } from './types'; + + +export const initialState: StylePresetState = { + isMenuOpen: false, + activeStylePreset: null, + calculatedPosPrompt: undefined, + calculatedNegPrompt: undefined +}; + + +export const stylePresetSlice = createSlice({ + name: 'stylePreset', + initialState: initialState, + reducers: { + isMenuOpenChanged: (state, action: PayloadAction) => { + state.isMenuOpen = action.payload; + }, + activeStylePresetChanged: (state, action: PayloadAction) => { + state.activeStylePreset = action.payload; + }, + calculatedPosPromptChanged: (state, action: PayloadAction) => { + state.calculatedPosPrompt = action.payload; + }, + calculatedNegPromptChanged: (state, action: PayloadAction) => { + state.calculatedNegPrompt = action.payload; + }, + }, +}); + +export const { isMenuOpenChanged, activeStylePresetChanged, calculatedPosPromptChanged, calculatedNegPromptChanged } = stylePresetSlice.actions; + +export const selectStylePresetSlice = (state: RootState) => state.stylePreset; diff --git a/invokeai/frontend/web/src/features/stylePresets/store/types.ts b/invokeai/frontend/web/src/features/stylePresets/store/types.ts index 0ea2f8e71c..45af35a4b5 100644 --- a/invokeai/frontend/web/src/features/stylePresets/store/types.ts +++ b/invokeai/frontend/web/src/features/stylePresets/store/types.ts @@ -1,8 +1,14 @@ import type { StylePresetRecordDTO } from "services/api/endpoints/stylePresets"; -export type StylePresetState = { +export type StylePresetModalState = { isModalOpen: boolean; updatingStylePreset: StylePresetRecordDTO | null; }; +export type StylePresetState = { + isMenuOpen: boolean; + activeStylePreset: StylePresetRecordDTO | null; + calculatedPosPrompt?: string + calculatedNegPrompt?: string +}