From 2604fd9fde993e065bd450228e69de4cb375d602 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Tue, 6 Aug 2024 15:31:13 -0400 Subject: [PATCH] a whole bunch of stuff --- invokeai/app/api/routers/style_presets.py | 28 +- .../style_preset_records_base.py | 13 +- .../style_preset_records_common.py | 7 - .../style_preset_records_sqlite.py | 38 +- .../middleware/listenerMiddleware/index.ts | 2 - .../listeners/activeStylePresetChanged.ts | 31 -- invokeai/frontend/web/src/app/store/store.ts | 2 +- .../graph/buildMultidiffusionUpscaleGraph.ts | 7 +- .../graph/canvas/addSDXLRefinerToGraph.ts | 4 +- .../canvas/buildCanvasImageToImageGraph.ts | 5 +- .../graph/canvas/buildCanvasInpaintGraph.ts | 5 +- .../graph/canvas/buildCanvasOutpaintGraph.ts | 5 +- .../buildCanvasSDXLImageToImageGraph.ts | 5 +- .../canvas/buildCanvasSDXLInpaintGraph.ts | 5 +- .../canvas/buildCanvasSDXLOutpaintGraph.ts | 5 +- .../canvas/buildCanvasSDXLTextToImageGraph.ts | 5 +- .../canvas/buildCanvasTextToImageGraph.ts | 5 +- .../generation/buildGenerationTabGraph.ts | 13 +- .../generation/buildGenerationTabSDXLGraph.ts | 13 +- .../nodes/util/graph/graphBuilderUtils.ts | 22 +- .../parameters/components/Prompts/Prompts.tsx | 10 +- .../components/ActiveStylePreset.tsx | 72 ++++ .../components/StylePresetForm.tsx | 116 +++---- .../components/StylePresetList.tsx | 31 ++ .../components/StylePresetListItem.tsx | 59 +++- .../components/StylePresetMenu.tsx | 74 +++- .../components/StylePresetMenuTrigger.tsx | 39 +-- .../components/StylePresetPromptField.tsx | 61 ++++ .../components/StylePresetSearch.tsx | 60 ++++ .../hooks/usePresetModifiedPrompts.ts | 25 ++ .../hooks/useStylePresetFields.ts | 17 + .../stylePresets/store/stylePresetSlice.ts | 12 +- .../src/features/stylePresets/store/types.ts | 3 +- .../ParametersPanelTextToImage.tsx | 28 +- .../services/api/endpoints/stylePresets.ts | 5 +- .../frontend/web/src/services/api/schema.ts | 326 ++++++++---------- 36 files changed, 686 insertions(+), 472 deletions(-) delete mode 100644 invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/activeStylePresetChanged.ts create mode 100644 invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx create mode 100644 invokeai/frontend/web/src/features/stylePresets/components/StylePresetList.tsx create mode 100644 invokeai/frontend/web/src/features/stylePresets/components/StylePresetPromptField.tsx create mode 100644 invokeai/frontend/web/src/features/stylePresets/components/StylePresetSearch.tsx create mode 100644 invokeai/frontend/web/src/features/stylePresets/hooks/usePresetModifiedPrompts.ts create mode 100644 invokeai/frontend/web/src/features/stylePresets/hooks/useStylePresetFields.ts diff --git a/invokeai/app/api/routers/style_presets.py b/invokeai/app/api/routers/style_presets.py index ae38db76aa..07f07fddb9 100644 --- a/invokeai/app/api/routers/style_presets.py +++ b/invokeai/app/api/routers/style_presets.py @@ -1,19 +1,13 @@ -from typing import Optional - -from fastapi import APIRouter, Body, HTTPException, Path, Query +from fastapi import APIRouter, Body, HTTPException, Path from invokeai.app.api.dependencies import ApiDependencies -from invokeai.app.services.shared.pagination import PaginatedResults -from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection from invokeai.app.services.style_preset_records.style_preset_records_common import ( StylePresetChanges, StylePresetNotFoundError, StylePresetRecordDTO, StylePresetWithoutId, - StylePresetRecordOrderBy, ) - style_presets_router = APIRouter(prefix="/v1/style_presets", tags=["style_presets"]) @@ -78,23 +72,9 @@ async def create_style_preset( "/", operation_id="list_style_presets", responses={ - 200: {"model": PaginatedResults[StylePresetRecordDTO]}, + 200: {"model": list[StylePresetRecordDTO]}, }, ) -async def list_style_presets( - page: int = Query(default=0, description="The page to get"), - per_page: int = Query(default=10, description="The number of style presets per page"), - order_by: StylePresetRecordOrderBy = Query( - default=StylePresetRecordOrderBy.Name, description="The attribute to order by" - ), - direction: SQLiteDirection = Query(default=SQLiteDirection.Ascending, description="The direction to order by"), - query: Optional[str] = Query(default=None, description="The text to query by (matches name and description)"), -) -> PaginatedResults[StylePresetRecordDTO]: +async def list_style_presets() -> list[StylePresetRecordDTO]: """Gets a page of style presets""" - return ApiDependencies.invoker.services.style_preset_records.get_many( - page=page, - per_page=per_page, - order_by=order_by, - direction=direction, - query=query, - ) + return ApiDependencies.invoker.services.style_preset_records.get_many() diff --git a/invokeai/app/services/style_preset_records/style_preset_records_base.py b/invokeai/app/services/style_preset_records/style_preset_records_base.py index 58121c3445..2a408419f3 100644 --- a/invokeai/app/services/style_preset_records/style_preset_records_base.py +++ b/invokeai/app/services/style_preset_records/style_preset_records_base.py @@ -1,13 +1,9 @@ from abc import ABC, abstractmethod -from typing import Optional -from invokeai.app.services.shared.pagination import PaginatedResults -from invokeai.app.services.shared.sqlite.sqlite_common import SQLiteDirection from invokeai.app.services.style_preset_records.style_preset_records_common import ( StylePresetChanges, StylePresetRecordDTO, StylePresetWithoutId, - StylePresetRecordOrderBy, ) @@ -35,13 +31,6 @@ class StylePresetRecordsStorageBase(ABC): pass @abstractmethod - def get_many( - self, - page: int, - per_page: int, - order_by: StylePresetRecordOrderBy, - direction: SQLiteDirection, - query: Optional[str], - ) -> PaginatedResults[StylePresetRecordDTO]: + def get_many(self) -> list[StylePresetRecordDTO]: """Gets many workflows.""" pass diff --git a/invokeai/app/services/style_preset_records/style_preset_records_common.py b/invokeai/app/services/style_preset_records/style_preset_records_common.py index 7ce7ccc730..f683eea97b 100644 --- a/invokeai/app/services/style_preset_records/style_preset_records_common.py +++ b/invokeai/app/services/style_preset_records/style_preset_records_common.py @@ -10,13 +10,6 @@ class StylePresetNotFoundError(Exception): """Raised when a style preset is not found""" -class StylePresetRecordOrderBy(str, Enum, metaclass=MetaEnum): - """The order by options for workflow records""" - - CreatedAt = "created_at" - Name = "name" - - class PresetData(BaseModel, extra="forbid"): positive_prompt: str = Field(description="Positive prompt") negative_prompt: str = Field(description="Negative prompt") diff --git a/invokeai/app/services/style_preset_records/style_preset_records_sqlite.py b/invokeai/app/services/style_preset_records/style_preset_records_sqlite.py index e0110afa9a..b0d8ecc714 100644 --- a/invokeai/app/services/style_preset_records/style_preset_records_sqlite.py +++ b/invokeai/app/services/style_preset_records/style_preset_records_sqlite.py @@ -11,7 +11,6 @@ from invokeai.app.services.style_preset_records.style_preset_records_common impo StylePresetNotFoundError, StylePresetRecordDTO, StylePresetWithoutId, - StylePresetRecordOrderBy, ) from invokeai.app.util.misc import uuid_string @@ -128,50 +127,21 @@ class SqliteStylePresetRecordsStorage(StylePresetRecordsStorageBase): def get_many( self, - page: int, - per_page: int, - order_by: StylePresetRecordOrderBy, - direction: SQLiteDirection, - query: Optional[str] = None, - ) -> PaginatedResults[StylePresetRecordDTO]: + ) -> list[StylePresetRecordDTO]: try: self._lock.acquire() - # sanitize! - assert order_by in StylePresetRecordOrderBy - assert direction in SQLiteDirection - count_query = "SELECT COUNT(*) FROM style_presets" main_query = """ SELECT * FROM style_presets + ORDER BY name ASC """ - main_params: list[int | str] = [] - count_params: list[int | str] = [] - stripped_query = query.strip() if query else None - if stripped_query: - wildcard_query = "%" + stripped_query + "%" - main_query += " AND name LIKE ? " - count_query += " AND name LIKE ?;" - main_params.extend([wildcard_query, wildcard_query]) - count_params.extend([wildcard_query, wildcard_query]) - main_query += f" ORDER BY {order_by.value} {direction.value} LIMIT ? OFFSET ?;" - main_params.extend([per_page, page * per_page]) - self._cursor.execute(main_query, main_params) + self._cursor.execute(main_query) rows = self._cursor.fetchall() style_presets = [StylePresetRecordDTO.from_dict(dict(row)) for row in rows] - self._cursor.execute(count_query, count_params) - total = self._cursor.fetchone()[0] - pages = total // per_page + (total % per_page > 0) - - return PaginatedResults( - items=style_presets, - page=page, - per_page=per_page, - pages=pages, - total=total, - ) + return style_presets except Exception: self._conn.rollback() raise 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 15420bf768..a1ce52b407 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -53,7 +53,6 @@ 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(); @@ -147,7 +146,6 @@ 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 deleted file mode 100644 index aa146bcc29..0000000000 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/activeStylePresetChanged.ts +++ /dev/null @@ -1,31 +0,0 @@ -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 70326ea5fd..5cd97110a6 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -29,6 +29,7 @@ import { upscalePersistConfig, upscaleSlice } from 'features/parameters/store/up import { queueSlice } from 'features/queue/store/queueSlice'; import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice'; import { stylePresetModalSlice } from 'features/stylePresets/store/stylePresetModalSlice'; +import { stylePresetSlice } from 'features/stylePresets/store/stylePresetSlice'; import { configSlice } from 'features/system/store/configSlice'; import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice'; import { uiPersistConfig, uiSlice } from 'features/ui/store/uiSlice'; @@ -47,7 +48,6 @@ 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, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts index 551cc45113..646f97cf44 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildMultidiffusionUpscaleGraph.ts @@ -22,11 +22,10 @@ import { } from './constants'; import { addLoRAs } from './generation/addLoRAs'; import { addSDXLLoRas } from './generation/addSDXLLoRAs'; -import { getBoardField, getSDXLStylePrompts } from './graphBuilderUtils'; +import { getBoardField, getPresetModifiedPrompts } from './graphBuilderUtils'; export const buildMultidiffusionUpscaleGraph = async (state: RootState): Promise => { const { model, cfgScale: cfg_scale, scheduler, steps, vaePrecision, seed, vae } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; const { upscaleModel, upscaleInitialImage, structure, creativity, tileControlnetModel, scale } = state.upscale; assert(model, 'No model found in state'); @@ -99,7 +98,7 @@ export const buildMultidiffusionUpscaleGraph = async (state: RootState): Promise let modelNode; if (model.base === 'sdxl') { - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); posCondNode = g.addNode({ type: 'sdxl_compel_prompt', @@ -140,6 +139,8 @@ export const buildMultidiffusionUpscaleGraph = async (state: RootState): Promise vae: vae ?? undefined, }); } else { + const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state); + posCondNode = g.addNode({ type: 'compel', id: POSITIVE_CONDITIONING, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts index 6fc406ca74..7809744afa 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts @@ -16,7 +16,7 @@ import { SDXL_REFINER_POSITIVE_CONDITIONING, SDXL_REFINER_SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import { getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import type { NonNullableGraph } from 'services/api/types'; import { isRefinerMainModelModelConfig } from 'services/api/types'; @@ -59,7 +59,7 @@ export const addSDXLRefinerToGraph = async ( const modelLoaderId = modelLoaderNodeId ? modelLoaderNodeId : SDXL_MODEL_LOADER; // Construct Style Prompt - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + const { positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); // Unplug SDXL Latents Generation To Latents To Image graph.edges = graph.edges.filter((e) => !(e.source.node_id === baseNodeId && ['latents'].includes(e.source.field))); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts index 5c89dcbf29..15db40bff4 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts @@ -16,7 +16,7 @@ import { POSITIVE_CONDITIONING, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate } 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 { isNonRefinerMainModelConfig } from 'services/api/types'; @@ -51,7 +51,6 @@ export const buildCanvasImageToImageGraph = async ( seamlessXAxis, seamlessYAxis, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; // The bounding box determines width and height, not the width and height params const { width, height } = state.canvas.boundingBoxDimensions; @@ -71,6 +70,8 @@ export const buildCanvasImageToImageGraph = async ( const use_cpu = shouldUseCpuNoise; + const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state); + /** * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the * full graph here as a template. Then use the parameters from app state and set friendlier node diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts index 20304b8830..5cdefb395e 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts @@ -19,7 +19,7 @@ import { POSITIVE_CONDITIONING, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate } 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 { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; @@ -58,7 +58,6 @@ export const buildCanvasInpaintGraph = async ( canvasCoherenceEdgeSize, maskBlur, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; if (!model) { log.error('No model found in state'); @@ -79,6 +78,8 @@ export const buildCanvasInpaintGraph = async ( const use_cpu = shouldUseCpuNoise; + const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state); + const graph: NonNullableGraph = { id: CANVAS_INPAINT_GRAPH, nodes: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts index 2c85b20222..00fc129edc 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts @@ -23,7 +23,7 @@ import { POSITIVE_CONDITIONING, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate } 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 { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; @@ -70,7 +70,6 @@ export const buildCanvasOutpaintGraph = async ( canvasCoherenceEdgeSize, maskBlur, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; if (!model) { log.error('No model found in state'); @@ -91,6 +90,8 @@ export const buildCanvasOutpaintGraph = async ( const use_cpu = shouldUseCpuNoise; + const { positivePrompt, negativePrompt } = getPresetModifiedPrompts(state); + const graph: NonNullableGraph = { id: CANVAS_OUTPAINT_GRAPH, nodes: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts index b4549ff582..49525d37bf 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts @@ -16,7 +16,7 @@ import { SDXL_REFINER_SEAMLESS, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } 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 { isNonRefinerMainModelConfig } from 'services/api/types'; @@ -51,7 +51,6 @@ export const buildCanvasSDXLImageToImageGraph = async ( seamlessYAxis, img2imgStrength: strength, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; const { refinerModel, refinerStart } = state.sdxl; @@ -75,7 +74,7 @@ export const buildCanvasSDXLImageToImageGraph = async ( const use_cpu = shouldUseCpuNoise; // Construct Style Prompt - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); /** * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts index dfbe2436d2..e8c3af2e4d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts @@ -19,7 +19,7 @@ import { SDXL_REFINER_SEAMLESS, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } 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 { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; @@ -58,7 +58,6 @@ export const buildCanvasSDXLInpaintGraph = async ( canvasCoherenceEdgeSize, maskBlur, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; const { refinerModel, refinerStart } = state.sdxl; @@ -83,7 +82,7 @@ export const buildCanvasSDXLInpaintGraph = async ( const use_cpu = shouldUseCpuNoise; // Construct Style Prompt - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); const graph: NonNullableGraph = { id: SDXL_CANVAS_INPAINT_GRAPH, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts index d58796575c..98cb9ece96 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts @@ -23,7 +23,7 @@ import { SDXL_REFINER_SEAMLESS, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } 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 { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; @@ -70,7 +70,6 @@ export const buildCanvasSDXLOutpaintGraph = async ( canvasCoherenceEdgeSize, maskBlur, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; const { refinerModel, refinerStart } = state.sdxl; @@ -94,7 +93,7 @@ export const buildCanvasSDXLOutpaintGraph = async ( const use_cpu = shouldUseCpuNoise; // Construct Style Prompt - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); const graph: NonNullableGraph = { id: SDXL_CANVAS_OUTPAINT_GRAPH, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts index b9e8e011b3..14bc649c99 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts @@ -14,7 +14,7 @@ import { SDXL_REFINER_SEAMLESS, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } 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 { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; @@ -44,7 +44,6 @@ export const buildCanvasSDXLTextToImageGraph = async (state: RootState): Promise seamlessXAxis, seamlessYAxis, } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; // The bounding box determines width and height, not the width and height params const { width, height } = state.canvas.boundingBoxDimensions; @@ -67,7 +66,7 @@ export const buildCanvasSDXLTextToImageGraph = async (state: RootState): Promise let modelLoaderNodeId = SDXL_MODEL_LOADER; // Construct Style Prompt - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + const { positivePrompt, negativePrompt, positiveStylePrompt, negativeStylePrompt } = getPresetModifiedPrompts(state); /** * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts index fe33ab5cf3..57cb695a71 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts @@ -14,7 +14,7 @@ import { POSITIVE_CONDITIONING, SEAMLESS, } from 'features/nodes/util/graph/constants'; -import { getBoardField, getIsIntermediate } 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 { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; @@ -44,7 +44,6 @@ export const buildCanvasTextToImageGraph = async (state: RootState): Promise | Invocation<'img_nsfw'> | Invocation<'img_watermark'> = l2i; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts index 416e81a632..a9a2d86a70 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts @@ -19,7 +19,7 @@ import { addSDXLRefiner } from 'features/nodes/util/graph/generation/addSDXLRefi import { addSeamless } from 'features/nodes/util/graph/generation/addSeamless'; import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { getBoardField, getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import { getBoardField, getPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import type { Invocation, NonNullableGraph } from 'services/api/types'; import { isNonRefinerMainModelConfig } from 'services/api/types'; import { assert } from 'tsafe'; @@ -36,14 +36,13 @@ export const buildGenerationTabSDXLGraph = async (state: RootState): Promise | Invocation<'img_nsfw'> | Invocation<'img_watermark'> = l2i; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts index 55795a092c..e977c87f16 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/graphBuilderUtils.ts @@ -1,5 +1,6 @@ import type { RootState } from 'app/store/store'; import type { BoardField } from 'features/nodes/types/common'; +import { buildPresetModifiedPrompt } from 'features/stylePresets/hooks/usePresetModifiedPrompts'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; /** @@ -14,13 +15,30 @@ export const getBoardField = (state: RootState): BoardField | undefined => { }; /** - * Gets the SDXL style prompts, based on the concat setting. + * Gets the prompts, modified for the active style preset. */ -export const getSDXLStylePrompts = (state: RootState): { positiveStylePrompt: string; negativeStylePrompt: string } => { +export const getPresetModifiedPrompts = (state: RootState): { positivePrompt: string; negativePrompt: string, positiveStylePrompt?: string; negativeStylePrompt?: string } => { const { positivePrompt, negativePrompt, positivePrompt2, negativePrompt2, shouldConcatPrompts } = state.controlLayers.present; + const { activeStylePreset } = state.stylePreset + + if (activeStylePreset) { + const presetModifiedPositivePrompt = buildPresetModifiedPrompt(activeStylePreset.preset_data.positive_prompt, positivePrompt) + + const presetModifiedNegativePrompt = buildPresetModifiedPrompt(activeStylePreset.preset_data.negative_prompt, negativePrompt) + + return { + positivePrompt: presetModifiedPositivePrompt, + negativePrompt: presetModifiedNegativePrompt, + positiveStylePrompt: shouldConcatPrompts ? presetModifiedPositivePrompt : positivePrompt2, + negativeStylePrompt: shouldConcatPrompts ? presetModifiedNegativePrompt : negativePrompt2, + + }; + } return { + positivePrompt, + negativePrompt, positiveStylePrompt: shouldConcatPrompts ? positivePrompt : positivePrompt2, negativeStylePrompt: shouldConcatPrompts ? negativePrompt : negativePrompt2, }; 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 aefe15efe7..5666989c8d 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Prompts/Prompts.tsx @@ -7,7 +7,7 @@ import { ParamPositivePrompt } from 'features/parameters/components/Core/ParamPo import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { ParamSDXLNegativeStylePrompt } from 'features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt'; import { ParamSDXLPositiveStylePrompt } from 'features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt'; -import { StylePresetMenuTrigger } from 'features/stylePresets/components/StylePresetMenuTrigger'; +import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts'; import { memo } from 'react'; const concatPromptsSelector = createSelector( @@ -19,16 +19,14 @@ 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); + const { presetModifiedPositivePrompt, presetModifiedNegativePrompt } = usePresetModifiedPrompts(); return ( - - {calculatedPosPrompt} + {presetModifiedPositivePrompt} {!shouldConcatPrompts && } - {calculatedNegPrompt} + {presetModifiedNegativePrompt} {!shouldConcatPrompts && } ); diff --git a/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx b/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx new file mode 100644 index 0000000000..ebad9de24a --- /dev/null +++ b/invokeai/frontend/web/src/features/stylePresets/components/ActiveStylePreset.tsx @@ -0,0 +1,72 @@ +import { Flex, IconButton, Text } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { negativePromptChanged, positivePromptChanged } from 'features/controlLayers/store/controlLayersSlice'; +import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage'; +import { usePresetModifiedPrompts } from 'features/stylePresets/hooks/usePresetModifiedPrompts'; +import { activeStylePresetChanged } from 'features/stylePresets/store/stylePresetSlice'; +import type { MouseEventHandler} from 'react'; +import { useCallback } from 'react'; +import { CgPushDown } from 'react-icons/cg'; +import { PiXBold } from 'react-icons/pi'; + +export const ActiveStylePreset = () => { + const { activeStylePreset } = useAppSelector((s) => s.stylePreset); + const dispatch = useAppDispatch(); + + const { presetModifiedPositivePrompt, presetModifiedNegativePrompt } = usePresetModifiedPrompts(); + + const handleClearActiveStylePreset = useCallback>( + (e) => { + e.stopPropagation(); + dispatch(activeStylePresetChanged(null)); + }, + [dispatch] + ); + + const handleFlattenPrompts = useCallback>( + (e) => { + e.stopPropagation(); + dispatch(positivePromptChanged(presetModifiedPositivePrompt)); + dispatch(negativePromptChanged(presetModifiedNegativePrompt)); + dispatch(activeStylePresetChanged(null)); + }, + [dispatch, presetModifiedPositivePrompt, presetModifiedNegativePrompt] + ); + + if (!activeStylePreset) { + return <>Choose Preset; + } + return ( + <> + + + + + + Prompt Style + + + {activeStylePreset.name} + + + + + } + /> + } + /> + + + + ); +}; diff --git a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx index c8faf9fc2e..7d84d4e43f 100644 --- a/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx +++ b/invokeai/frontend/web/src/features/stylePresets/components/StylePresetForm.tsx @@ -1,84 +1,80 @@ -import { Button, Flex, FormControl, FormLabel, Input, Textarea } from '@invoke-ai/ui-library'; +import { Button, Flex, FormControl, FormLabel, Icon, Input, Text } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; +import { useStylePresetFields } from 'features/stylePresets/hooks/useStylePresetFields'; import { isModalOpenChanged, updatingStylePresetChanged } from 'features/stylePresets/store/stylePresetModalSlice'; import { toast } from 'features/toast/toast'; -import type { ChangeEventHandler } from 'react'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback } from 'react'; +import type { SubmitHandler} from 'react-hook-form'; +import { useForm } from 'react-hook-form'; +import { PiBracketsCurlyBold } from 'react-icons/pi'; import type { StylePresetRecordDTO } from 'services/api/endpoints/stylePresets'; import { useCreateStylePresetMutation, useUpdateStylePresetMutation } from 'services/api/endpoints/stylePresets'; +import { StylePresetPromptField } from './StylePresetPromptField'; + +export type StylePresetFormData = { + name: string; + positivePrompt: string; + negativePrompt: string; +}; + export const StylePresetForm = ({ updatingPreset }: { updatingPreset: StylePresetRecordDTO | null }) => { const [createStylePreset] = useCreateStylePresetMutation(); const [updateStylePreset] = useUpdateStylePresetMutation(); const dispatch = useAppDispatch(); - const [name, setName] = useState(updatingPreset ? updatingPreset.name : ''); - const [posPrompt, setPosPrompt] = useState(updatingPreset ? updatingPreset.preset_data.positive_prompt : ''); - const [negPrompt, setNegPrompt] = useState(updatingPreset ? updatingPreset.preset_data.negative_prompt : ''); + const stylePresetFieldDefaults = useStylePresetFields(updatingPreset); - const handleChangeName = useCallback>((e) => { - setName(e.target.value); - }, []); + const { handleSubmit, control, formState, reset, register } = useForm({ + defaultValues: stylePresetFieldDefaults, + }); - const handleChangePosPrompt = useCallback>((e) => { - setPosPrompt(e.target.value); - }, []); - - const handleChangeNegPrompt = useCallback>((e) => { - setNegPrompt(e.target.value); - }, []); - - useEffect(() => { - if (updatingPreset) { - setName(updatingPreset.name); - setPosPrompt(updatingPreset.preset_data.positive_prompt); - setNegPrompt(updatingPreset.preset_data.negative_prompt); - } else { - setName(''); - setPosPrompt(''); - setNegPrompt(''); - } - }, [updatingPreset]); - - const handleClickSave = useCallback(async () => { - try { - if (updatingPreset) { - await updateStylePreset({ - id: updatingPreset.id, - changes: { name, preset_data: { positive_prompt: posPrompt, negative_prompt: negPrompt } }, - }).unwrap(); - } else { - await createStylePreset({ - name: name, - preset_data: { positive_prompt: posPrompt, negative_prompt: negPrompt }, - }).unwrap(); + const handleClickSave = useCallback>( + async (data) => { + try { + if (updatingPreset) { + await updateStylePreset({ + id: updatingPreset.id, + changes: { + name: data.name, + preset_data: { positive_prompt: data.positivePrompt, negative_prompt: data.negativePrompt }, + }, + }).unwrap(); + } else { + await createStylePreset({ + name: data.name, + preset_data: { positive_prompt: data.positivePrompt, negative_prompt: data.negativePrompt }, + }).unwrap(); + } + } catch (error) { + toast({ + status: 'error', + title: 'Failed to save style preset', + }); } - } catch (error) { - toast({ - status: 'error', - title: 'Failed to save style preset', - }); - } - dispatch(updatingStylePresetChanged(null)); - dispatch(isModalOpenChanged(false)); - }, [dispatch, updatingPreset, name, posPrompt, negPrompt, updateStylePreset, createStylePreset]); + dispatch(updatingStylePresetChanged(null)); + dispatch(isModalOpenChanged(false)); + }, + [dispatch, updatingPreset, updateStylePreset, createStylePreset] + ); return ( Name - + - - Positive Prompt -