From 1d1e4d02dc547975316a87f28bd6a2911aa61b52 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 5 Apr 2024 17:03:29 +1100 Subject: [PATCH] feat(ui): rough out regional prompts store --- invokeai/frontend/web/src/app/store/store.ts | 6 + .../store/regionalPromptsSlice.ts | 126 ++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index b538a3eaeb..4626f4e36b 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -21,6 +21,10 @@ import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workf import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice'; import { postprocessingPersistConfig, postprocessingSlice } from 'features/parameters/store/postprocessingSlice'; import { queueSlice } from 'features/queue/store/queueSlice'; +import { + regionalPromptsPersistConfig, + regionalPromptsSlice, +} from 'features/regionalPrompts/store/regionalPromptsSlice'; import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice'; import { configSlice } from 'features/system/store/configSlice'; import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice'; @@ -59,6 +63,7 @@ const allReducers = { [queueSlice.name]: queueSlice.reducer, [workflowSlice.name]: workflowSlice.reducer, [hrfSlice.name]: hrfSlice.reducer, + [regionalPromptsSlice.name]: regionalPromptsSlice.reducer, [api.reducerPath]: api.reducer, }; @@ -103,6 +108,7 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = { [loraPersistConfig.name]: loraPersistConfig, [modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig, [hrfPersistConfig.name]: hrfPersistConfig, + [regionalPromptsPersistConfig.name]: regionalPromptsPersistConfig, }; const unserialize: UnserializeFunction = (data, key) => { diff --git a/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts b/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts new file mode 100644 index 0000000000..6a248ea527 --- /dev/null +++ b/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts @@ -0,0 +1,126 @@ +import type { EntityState } from '@reduxjs/toolkit'; +import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'; +import { getSelectorsOptions } from 'app/store/createMemoizedSelector'; +import type { PersistConfig, RootState } from 'app/store/store'; +import type { RgbaColor } from 'react-colorful'; +import { v4 as uuidv4 } from 'uuid'; + +type LayerObjectBase = { + id: string; + isSelected: boolean; +}; + +export type ImageObject = LayerObjectBase & { + kind: 'image'; + imageName: string; + x: number; + y: number; + width: number; + height: number; +}; + +export type LineObject = LayerObjectBase & { + kind: 'line'; + strokeWidth: number; + points: number[]; + color: RgbaColor; +}; + +export type FillRectObject = LayerObjectBase & { + kind: 'fillRect'; + x: number; + y: number; + width: number; + height: number; + color: RgbaColor; +}; + +export type LayerObject = ImageObject | LineObject | FillRectObject; + +export type PromptRegionLayer = { + id: string; + objects: EntityState<LayerObject, string>; + prompt: string; +}; + +export const layersAdapter = createEntityAdapter<PromptRegionLayer, string>({ + selectId: (layer) => layer.id, +}); +export const layersSelectors = layersAdapter.getSelectors(undefined, getSelectorsOptions); + +export const layerObjectsAdapter = createEntityAdapter<LayerObject, string>({ + selectId: (obj) => obj.id, +}); +export const layerObjectsSelectors = layerObjectsAdapter.getSelectors(undefined, getSelectorsOptions); + +const getMockState = () => { + // Mock data + const layer1ID = uuidv4(); + const obj1ID = uuidv4(); + const obj2ID = uuidv4(); + + const objectEntities: Record<string, LayerObject> = { + [obj1ID]: { + id: obj1ID, + kind: 'line', + isSelected: false, + color: { r: 255, g: 0, b: 0, a: 1 }, + strokeWidth: 5, + points: [20, 20, 100, 100], + }, + [obj2ID]: { + id: obj2ID, + kind: 'fillRect', + isSelected: false, + color: { r: 0, g: 255, b: 0, a: 1 }, + x: 150, + y: 150, + width: 100, + height: 100, + }, + }; + const objectsInitialState = layerObjectsAdapter.getInitialState(undefined, objectEntities); + const entities: Record<string, PromptRegionLayer> = { + [layer1ID]: { + id: layer1ID, + prompt: 'strawberries', + objects: objectsInitialState, + }, + }; + + return entities; +}; + +export const initialRegionalPromptsState = layersAdapter.getInitialState( + { _version: 1, selectedID: null }, + getMockState() +); + +export type RegionalPromptsState = typeof initialRegionalPromptsState; + +export const regionalPromptsSlice = createSlice({ + name: 'regionalPrompts', + initialState: initialRegionalPromptsState, + reducers: { + layerAdded: layersAdapter.addOne, + layerRemoved: layersAdapter.removeOne, + layerUpdated: layersAdapter.updateOne, + layersReset: layersAdapter.removeAll, + }, +}); + +export const { layerAdded, layerRemoved, layerUpdated, layersReset } = regionalPromptsSlice.actions; + +export const selectRegionalPromptsSlice = (state: RootState) => state.regionalPrompts; + +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +const migrateRegionalPromptsState = (state: any): any => { + return state; +}; + +export const regionalPromptsPersistConfig: PersistConfig<RegionalPromptsState> = { + name: regionalPromptsSlice.name, + initialState: initialRegionalPromptsState, + migrate: migrateRegionalPromptsState, + persistDenylist: [], +};