From d1db6198b5c8536d488832571fb39da48c8971be Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:14:33 +1000 Subject: [PATCH] perf(ui): memoize & otherwise optimize regional prompts ui --- .../components/AddLayerButton.tsx | 8 ++- .../components/BrushPreview.tsx | 45 +++++------- .../regionalPrompts/components/BrushSize.tsx | 8 ++- .../components/LayerBoundingBox.tsx | 13 ++-- .../components/LayerColorPicker.tsx | 31 ++++++--- .../components/LayerComponent.tsx | 68 +++++++++---------- .../components/LayerListItem.tsx | 8 ++- .../regionalPrompts/components/LayerMenu.tsx | 13 ++-- .../components/LayerVisibilityToggle.tsx | 8 ++- .../components/LineComponent.tsx | 7 +- .../components/PromptLayerOpacity.tsx | 8 ++- .../components/RectComponent.tsx | 17 ++++- .../components/RegionalPromptsEditor.tsx | 11 +-- .../components/RegionalPromptsStage.tsx | 4 +- .../store/regionalPromptsSlice.ts | 6 +- 15 files changed, 144 insertions(+), 111 deletions(-) diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/AddLayerButton.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/AddLayerButton.tsx index f39db803c7..01107489b7 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/AddLayerButton.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/AddLayerButton.tsx @@ -1,10 +1,10 @@ import { Button } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { layerAdded } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export const AddLayerButton = () => { +export const AddLayerButton = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const onClick = useCallback(() => { @@ -12,4 +12,6 @@ export const AddLayerButton = () => { }, [dispatch]); return ; -}; +}); + +AddLayerButton.displayName = 'AddLayerButton'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/BrushPreview.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/BrushPreview.tsx index 2792d4ae81..1c625625cf 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/BrushPreview.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/BrushPreview.tsx @@ -1,49 +1,38 @@ import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; -import { rgbColorToString } from 'features/canvas/util/colorToString'; +import { rgbaColorToString } from 'features/canvas/util/colorToString'; import { $cursorPosition } from 'features/regionalPrompts/store/regionalPromptsSlice'; +import { memo } from 'react'; import { Circle, Group } from 'react-konva'; -const useBrushData = () => { +export const BrushPreviewOutline = memo(() => { const brushSize = useAppSelector((s) => s.regionalPrompts.brushSize); const tool = useAppSelector((s) => s.regionalPrompts.tool); + const a = useAppSelector((s) => s.regionalPrompts.promptLayerOpacity); const color = useAppSelector((s) => { const _color = s.regionalPrompts.layers.find((l) => l.id === s.regionalPrompts.selectedLayer)?.color; if (!_color) { return null; } - return rgbColorToString(_color); + return rgbaColorToString({ ..._color, a }); }); + const pos = useStore($cursorPosition); - return { brushSize, tool, color, pos }; -}; - -export const BrushPreviewFill = () => { - const { brushSize, tool, color, pos } = useBrushData(); if (!brushSize || !color || !pos || tool === 'move') { return null; } return ( - - ); -}; - -export const BrushPreviewOutline = () => { - const { brushSize, tool, color, pos } = useBrushData(); - if (!brushSize || !color || !pos || tool === 'move') { - return null; - } - - return ( - + + { /> ); -}; +}); + +BrushPreviewOutline.displayName = 'BrushPreviewOutline'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/BrushSize.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/BrushSize.tsx index 67871381cb..a1a4bb1cfc 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/BrushSize.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/BrushSize.tsx @@ -1,10 +1,10 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { brushSizeChanged } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export const BrushSize = () => { +export const BrushSize = memo(() => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const brushSize = useAppSelector((s) => s.regionalPrompts.brushSize); @@ -21,4 +21,6 @@ export const BrushSize = () => { ); -}; +}); + +BrushSize.displayName = 'BrushSize'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerBoundingBox.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerBoundingBox.tsx index e9d4514876..31e09d1e05 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerBoundingBox.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerBoundingBox.tsx @@ -1,14 +1,14 @@ -import { createSelector } from '@reduxjs/toolkit'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { layerSelected, selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { Rect as KonvaRect } from 'react-konva'; type Props = { layerId: string; }; -export const LayerBoundingBox = ({ layerId }: Props) => { +export const LayerBoundingBox = memo(({ layerId }: Props) => { const dispatch = useAppDispatch(); const tool = useAppSelector((s) => s.regionalPrompts.tool); const selectedLayer = useAppSelector((s) => s.regionalPrompts.selectedLayer); @@ -19,7 +19,7 @@ export const LayerBoundingBox = ({ layerId }: Props) => { const selectBbox = useMemo( () => - createSelector( + createMemoizedSelector( selectRegionalPromptsSlice, (regionalPrompts) => regionalPrompts.layers.find((layer) => layer.id === layerId)?.bbox ?? null ), @@ -33,6 +33,7 @@ export const LayerBoundingBox = ({ layerId }: Props) => { return ( { listening={tool === 'move'} /> ); -}; +}); + +LayerBoundingBox.displayName = 'LayerBoundingBox'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx index aabea569fd..8208912239 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx @@ -1,18 +1,31 @@ import { IconButton, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import RgbColorPicker from 'common/components/RgbColorPicker'; -import { useLayer } from 'features/regionalPrompts/hooks/layerStateHooks'; -import { promptRegionLayerColorChanged } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback } from 'react'; +import { + promptRegionLayerColorChanged, + selectRegionalPromptsSlice, +} from 'features/regionalPrompts/store/regionalPromptsSlice'; +import { memo, useCallback, useMemo } from 'react'; import type { RgbColor } from 'react-colorful'; import { PiEyedropperBold } from 'react-icons/pi'; +import { assert } from 'tsafe'; type Props = { id: string; }; -export const LayerColorPicker = ({ id }: Props) => { - const layer = useLayer(id); +export const LayerColorPicker = memo(({ id }: Props) => { + const selectColor = useMemo( + () => + createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => { + const layer = regionalPrompts.layers.find((l) => l.id === id); + assert(layer); + return layer.color; + }), + [id] + ); + const color = useAppSelector(selectColor); const dispatch = useAppDispatch(); const onColorChange = useCallback( (color: RgbColor) => { @@ -27,9 +40,11 @@ export const LayerColorPicker = ({ id }: Props) => { - + ); -}; +}); + +LayerColorPicker.displayName = 'LayerColorPicker'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx index 9e4596fa30..c0a104b9de 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerComponent.tsx @@ -1,6 +1,5 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import getScaledCursorPosition from 'features/canvas/util/getScaledCursorPosition'; -import { BrushPreviewFill } from 'features/regionalPrompts/components/BrushPreview'; import { LayerBoundingBox } from 'features/regionalPrompts/components/LayerBoundingBox'; import { LineComponent } from 'features/regionalPrompts/components/LineComponent'; import { RectComponent } from 'features/regionalPrompts/components/RectComponent'; @@ -17,8 +16,7 @@ import type { Group as KonvaGroupType } from 'konva/lib/Group'; import type { Layer as KonvaLayerType } from 'konva/lib/Layer'; import type { KonvaEventObject, Node as KonvaNodeType, NodeConfig as KonvaNodeConfigType } from 'konva/lib/Node'; import type { IRect, Vector2d } from 'konva/lib/types'; -import type React from 'react'; -import { useCallback, useEffect, useRef } from 'react'; +import { memo, useCallback, useEffect, useRef } from 'react'; import { Group as KonvaGroup, Layer as KonvaLayer } from 'react-konva'; type Props = { @@ -28,15 +26,13 @@ type Props = { export const selectPromptLayerObjectGroup = (item: KonvaNodeType) => item.name() !== REGIONAL_PROMPT_LAYER_OBJECT_GROUP_NAME; -export const LayerComponent: React.FC = ({ id }) => { +export const LayerComponent = memo(({ id }: Props) => { const dispatch = useAppDispatch(); const layer = useLayer(id); - const selectedLayer = useAppSelector((s) => s.regionalPrompts.selectedLayer); const promptLayerOpacity = useAppSelector((s) => s.regionalPrompts.promptLayerOpacity); const tool = useAppSelector((s) => s.regionalPrompts.tool); const layerRef = useRef(null); const groupRef = useRef(null); - const onChangeBbox = useCallback( (bbox: IRect | null) => { dispatch(layerBboxChanged({ layerId: layer.id, bbox })); @@ -94,39 +90,39 @@ export const LayerComponent: React.FC = ({ id }) => { } // Caching the group allows its opacity to apply to all shapes at once. We should cache only when the layer's // objects or attributes with a visual effect (e.g. color) change. + // TODO: Figure out a more efficient way to handle opacity - maybe a separate rect w/ globalCompositeOperation... groupRef.current.cache(); }, [layer.objects, layer.color, layer.isVisible]); return ( - <> - + - - {layer.objects.map((obj) => { - if (obj.kind === 'line') { - return ; - } - if (obj.kind === 'fillRect') { - return ; - } - })} - - - - {layer.id === selectedLayer && } - + {layer.objects.map((obj) => { + if (obj.kind === 'line') { + return ; + } + if (obj.kind === 'fillRect') { + return ; + } + })} + + + ); -}; +}); + +LayerComponent.displayName = 'LayerComponent'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx index 36b34d642c..39a7efd758 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx @@ -6,14 +6,14 @@ import { LayerMenu } from 'features/regionalPrompts/components/LayerMenu'; import { LayerVisibilityToggle } from 'features/regionalPrompts/components/LayerVisibilityToggle'; import { RegionalPromptsPrompt } from 'features/regionalPrompts/components/RegionalPromptsPrompt'; import { layerSelected } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; type Props = { id: string; }; -export const LayerListItem = ({ id }: Props) => { +export const LayerListItem = memo(({ id }: Props) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const selectedLayer = useAppSelector((s) => s.regionalPrompts.selectedLayer); @@ -46,4 +46,6 @@ export const LayerListItem = ({ id }: Props) => { ); -}; +}); + +LayerListItem.displayName = 'LayerListItem'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx index 2317eda26a..bfe85de446 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx @@ -1,5 +1,5 @@ import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library'; -import { createSelector } from '@reduxjs/toolkit'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { layerDeleted, @@ -10,8 +10,7 @@ import { layerReset, selectRegionalPromptsSlice, } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import type React from 'react'; -import { useCallback, useMemo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiArrowCounterClockwiseBold, @@ -25,12 +24,12 @@ import { type Props = { id: string }; -export const LayerMenu: React.FC = ({ id }) => { +export const LayerMenu = memo(({ id }: Props) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const selectValidActions = useMemo( () => - createSelector(selectRegionalPromptsSlice, (regionalPrompts) => { + createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => { const layerIndex = regionalPrompts.layers.findIndex((l) => l.id === id); const layerCount = regionalPrompts.layers.length; return { @@ -87,4 +86,6 @@ export const LayerMenu: React.FC = ({ id }) => { ); -}; +}); + +LayerMenu.displayName = 'LayerMenu'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx index bb636e8338..2a004f262a 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx @@ -2,14 +2,14 @@ import { IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch } from 'app/store/storeHooks'; import { useLayerIsVisible } from 'features/regionalPrompts/hooks/layerStateHooks'; import { layerIsVisibleToggled } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi'; type Props = { id: string; }; -export const LayerVisibilityToggle = ({ id }: Props) => { +export const LayerVisibilityToggle = memo(({ id }: Props) => { const dispatch = useAppDispatch(); const isVisible = useLayerIsVisible(id); const onClick = useCallback(() => { @@ -25,4 +25,6 @@ export const LayerVisibilityToggle = ({ id }: Props) => { onClick={onClick} /> ); -}; +}); + +LayerVisibilityToggle.displayName = 'LayerVisibilityToggle'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx index a0250ab6da..5b6796b895 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/LineComponent.tsx @@ -1,5 +1,6 @@ import { rgbColorToString } from 'features/canvas/util/colorToString'; import type { LineObject } from 'features/regionalPrompts/store/regionalPromptsSlice'; +import { memo } from 'react'; import type { RgbColor } from 'react-colorful'; import { Line } from 'react-konva'; @@ -9,7 +10,7 @@ type Props = { color: RgbColor; }; -export const LineComponent = ({ layerId, line, color }: Props) => { +export const LineComponent = memo(({ layerId, line, color }: Props) => { return ( { listening={false} /> ); -}; +}); + +LineComponent.displayName = 'LineComponent'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/PromptLayerOpacity.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/PromptLayerOpacity.tsx index 164505b086..1ae4ecc524 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/PromptLayerOpacity.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/PromptLayerOpacity.tsx @@ -1,10 +1,10 @@ import { CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { promptLayerOpacityChanged } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { useCallback } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -export const PromptLayerOpacity = () => { +export const PromptLayerOpacity = memo(() => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const promptLayerOpacity = useAppSelector((s) => s.regionalPrompts.promptLayerOpacity); @@ -20,4 +20,6 @@ export const PromptLayerOpacity = () => { ); -}; +}); + +PromptLayerOpacity.displayName = 'PromptLayerOpacity'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx index a8248e1301..882c5823a5 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RectComponent.tsx @@ -1,5 +1,6 @@ import { rgbColorToString } from 'features/canvas/util/colorToString'; import type { FillRectObject } from 'features/regionalPrompts/store/regionalPromptsSlice'; +import { memo } from 'react'; import type { RgbColor } from 'react-colorful'; import { Rect } from 'react-konva'; @@ -8,8 +9,18 @@ type Props = { color: RgbColor; }; -export const RectComponent = ({ rect, color }: Props) => { +export const RectComponent = memo(({ rect, color }: Props) => { return ( - + ); -}; +}); + +RectComponent.displayName = 'RectComponent'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx index efc77796c3..22a6d4f223 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx @@ -1,6 +1,6 @@ /* eslint-disable i18next/no-literal-string */ import { Button, Flex } from '@invoke-ai/ui-library'; -import { createSelector } from '@reduxjs/toolkit'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton'; import { BrushSize } from 'features/regionalPrompts/components/BrushSize'; @@ -11,8 +11,9 @@ import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser'; import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice'; import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs'; import { ImageSizeLinear } from 'features/settingsAccordions/components/ImageSettingsAccordion/ImageSizeLinear'; +import { memo } from 'react'; -const selectLayerIdsReversed = createSelector(selectRegionalPromptsSlice, (regionalPrompts) => +const selectLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => regionalPrompts.layers.map((l) => l.id).reverse() ); @@ -20,7 +21,7 @@ const debugBlobs = () => { getRegionalPromptLayerBlobs(true); }; -export const RegionalPromptsEditor = () => { +export const RegionalPromptsEditor = memo(() => { const layerIdsReversed = useAppSelector(selectLayerIdsReversed); return ( @@ -40,4 +41,6 @@ export const RegionalPromptsEditor = () => { ); -}; +}); + +RegionalPromptsEditor.displayName = 'RegionalPromptsEditor'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx index 1936a13e17..9a55b0d4e1 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsStage.tsx @@ -1,5 +1,5 @@ import { chakra } from '@invoke-ai/ui-library'; -import { createSelector } from '@reduxjs/toolkit'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { BrushPreviewOutline } from 'features/regionalPrompts/components/BrushPreview'; import { LayerComponent } from 'features/regionalPrompts/components/LayerComponent'; @@ -15,7 +15,7 @@ import type Konva from 'konva'; import { memo, useCallback, useMemo, useRef } from 'react'; import { Layer, Stage } from 'react-konva'; -const selectLayerIds = createSelector(selectRegionalPromptsSlice, (regionalPrompts) => +const selectLayerIds = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => regionalPrompts.layers.map((l) => l.id) ); diff --git a/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts b/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts index 8d96bcdf30..44ceb46b4b 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts +++ b/invokeai/frontend/web/src/features/regionalPrompts/store/regionalPromptsSlice.ts @@ -2,7 +2,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; import { moveBackward, moveForward, moveToBack, moveToFront } from 'common/util/arrayUtils'; -import type Konva from 'konva'; +import type { Stage } from 'konva/lib/Stage'; import type { IRect, Vector2d } from 'konva/lib/types'; import { atom } from 'nanostores'; import type { RgbColor } from 'react-colorful'; @@ -270,8 +270,8 @@ export const regionalPromptsPersistConfig: PersistConfig = export const $isMouseDown = atom(false); export const $isMouseOver = atom(false); export const $cursorPosition = atom(null); -export const $stage = atom(null); -export const getStage = (): Konva.Stage => { +export const $stage = atom(null); +export const getStage = (): Stage => { const stage = $stage.get(); assert(stage); return stage;