diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx deleted file mode 100644 index c383e35314..0000000000 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerListItem.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Flex, Spacer } from '@invoke-ai/ui-library'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { rgbaColorToString } from 'features/canvas/util/colorToString'; -import LayerAutoNegativeCombobox from 'features/regionalPrompts/components/LayerAutoNegativeCombobox'; -import { LayerColorPicker } from 'features/regionalPrompts/components/LayerColorPicker'; -import { LayerMenu } from 'features/regionalPrompts/components/LayerMenu'; -import { LayerVisibilityToggle } from 'features/regionalPrompts/components/LayerVisibilityToggle'; -import { RegionalPromptsNegativePrompt } from 'features/regionalPrompts/components/RegionalPromptsNegativePrompt'; -import { RegionalPromptsPositivePrompt } from 'features/regionalPrompts/components/RegionalPromptsPositivePrompt'; -import { isRegionalPromptLayer, rpLayerSelected } from 'features/regionalPrompts/store/regionalPromptsSlice'; -import { memo, useCallback } from 'react'; -import { assert } from 'tsafe'; - -type Props = { - id: string; -}; - -export const LayerListItem = memo(({ id }: Props) => { - const dispatch = useAppDispatch(); - const selectedLayer = useAppSelector((s) => s.regionalPrompts.present.selectedLayer); - const color = useAppSelector((s) => { - const layer = s.regionalPrompts.present.layers.find((l) => l.id === id); - assert(isRegionalPromptLayer(layer), `Layer ${id} not found or not an RP layer`); - return rgbaColorToString({ ...layer.color, a: selectedLayer === id ? 1 : 0.35 }); - }); - const onClickCapture = useCallback(() => { - // Must be capture so that the layer is selected before deleting/resetting/etc - dispatch(rpLayerSelected(id)); - }, [dispatch, id]); - return ( - - - - - - - - - - - - - - ); -}); - -LayerListItem.displayName = 'LayerListItem'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerAutoNegativeCombobox.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerAutoNegativeCombobox.tsx similarity index 93% rename from invokeai/frontend/web/src/features/regionalPrompts/components/LayerAutoNegativeCombobox.tsx rename to invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerAutoNegativeCombobox.tsx index cf98e639e5..161e94598f 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerAutoNegativeCombobox.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerAutoNegativeCombobox.tsx @@ -35,7 +35,7 @@ const useAutoNegative = (layerId: string) => { return autoNegative; }; -const AutoNegativeCombobox = ({ layerId }: Props) => { +export const RPLayerAutoNegativeCombobox = memo(({ layerId }: Props) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const autoNegative = useAutoNegative(layerId); @@ -58,6 +58,6 @@ const AutoNegativeCombobox = ({ layerId }: Props) => { ); -}; +}); -export default memo(AutoNegativeCombobox); +RPLayerAutoNegativeCombobox.displayName = 'RPLayerAutoNegativeCombobox'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerColorPicker.tsx similarity index 80% rename from invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx rename to invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerColorPicker.tsx index 1ac6706d04..c326c407aa 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerColorPicker.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerColorPicker.tsx @@ -13,26 +13,26 @@ import { PiEyedropperBold } from 'react-icons/pi'; import { assert } from 'tsafe'; type Props = { - id: string; + layerId: string; }; -export const LayerColorPicker = memo(({ id }: Props) => { +export const RPLayerColorPicker = memo(({ layerId }: Props) => { const selectColor = useMemo( () => createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => { - const layer = regionalPrompts.present.layers.find((l) => l.id === id); - assert(isRegionalPromptLayer(layer), `Layer ${id} not found or not an RP layer`); + const layer = regionalPrompts.present.layers.find((l) => l.id === layerId); + assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`); return layer.color; }), - [id] + [layerId] ); const color = useAppSelector(selectColor); const dispatch = useAppDispatch(); const onColorChange = useCallback( (color: RgbColor) => { - dispatch(rpLayerColorChanged({ layerId: id, color })); + dispatch(rpLayerColorChanged({ layerId, color })); }, - [dispatch, id] + [dispatch, layerId] ); return ( @@ -48,4 +48,4 @@ export const LayerColorPicker = memo(({ id }: Props) => { ); }); -LayerColorPicker.displayName = 'LayerColorPicker'; +RPLayerColorPicker.displayName = 'RPLayerColorPicker'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerListItem.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerListItem.tsx new file mode 100644 index 0000000000..b4a23e61f6 --- /dev/null +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerListItem.tsx @@ -0,0 +1,47 @@ +import { Flex, Spacer } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { rgbaColorToString } from 'features/canvas/util/colorToString'; +import { RPLayerAutoNegativeCombobox } from 'features/regionalPrompts/components/RPLayerAutoNegativeCombobox'; +import { RPLayerColorPicker } from 'features/regionalPrompts/components/RPLayerColorPicker'; +import { RPLayerMenu } from 'features/regionalPrompts/components/RPLayerMenu'; +import { RPLayerNegativePrompt } from 'features/regionalPrompts/components/RPLayerNegativePrompt'; +import { RPLayerPositivePrompt } from 'features/regionalPrompts/components/RPLayerPositivePrompt'; +import { RPLayerVisibilityToggle } from 'features/regionalPrompts/components/RPLayerVisibilityToggle'; +import { isRegionalPromptLayer, rpLayerSelected } from 'features/regionalPrompts/store/regionalPromptsSlice'; +import { memo, useCallback } from 'react'; +import { assert } from 'tsafe'; + +type Props = { + layerId: string; +}; + +export const RPLayerListItem = memo(({ layerId }: Props) => { + const dispatch = useAppDispatch(); + const selectedLayer = useAppSelector((s) => s.regionalPrompts.present.selectedLayer); + const color = useAppSelector((s) => { + const layer = s.regionalPrompts.present.layers.find((l) => l.id === layerId); + assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`); + return rgbaColorToString({ ...layer.color, a: selectedLayer === layerId ? 1 : 0.35 }); + }); + const onClickCapture = useCallback(() => { + // Must be capture so that the layer is selected before deleting/resetting/etc + dispatch(rpLayerSelected(layerId)); + }, [dispatch, layerId]); + return ( + + + + + + + + + + + + + + ); +}); + +RPLayerListItem.displayName = 'RPLayerListItem'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerMenu.tsx similarity index 77% rename from invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx rename to invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerMenu.tsx index c69fff907c..431e02616f 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerMenu.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerMenu.tsx @@ -2,6 +2,7 @@ import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { + isRegionalPromptLayer, layerDeleted, layerMovedBackward, layerMovedForward, @@ -21,16 +22,19 @@ import { PiDotsThreeVerticalBold, PiTrashSimpleBold, } from 'react-icons/pi'; +import { assert } from 'tsafe'; -type Props = { id: string }; +type Props = { layerId: string }; -export const LayerMenu = memo(({ id }: Props) => { +export const RPLayerMenu = memo(({ layerId }: Props) => { const dispatch = useAppDispatch(); const { t } = useTranslation(); const selectValidActions = useMemo( () => createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => { - const layerIndex = regionalPrompts.present.layers.findIndex((l) => l.id === id); + const layer = regionalPrompts.present.layers.find((l) => l.id === layerId); + assert(isRegionalPromptLayer(layer), `Layer ${layerId} not found or not an RP layer`); + const layerIndex = regionalPrompts.present.layers.findIndex((l) => l.id === layerId); const layerCount = regionalPrompts.present.layers.length; return { canMoveForward: layerIndex < layerCount - 1, @@ -39,27 +43,27 @@ export const LayerMenu = memo(({ id }: Props) => { canMoveToBack: layerIndex > 0, }; }), - [id] + [layerId] ); const validActions = useAppSelector(selectValidActions); const moveForward = useCallback(() => { - dispatch(layerMovedForward(id)); - }, [dispatch, id]); + dispatch(layerMovedForward(layerId)); + }, [dispatch, layerId]); const moveToFront = useCallback(() => { - dispatch(layerMovedToFront(id)); - }, [dispatch, id]); + dispatch(layerMovedToFront(layerId)); + }, [dispatch, layerId]); const moveBackward = useCallback(() => { - dispatch(layerMovedBackward(id)); - }, [dispatch, id]); + dispatch(layerMovedBackward(layerId)); + }, [dispatch, layerId]); const moveToBack = useCallback(() => { - dispatch(layerMovedToBack(id)); - }, [dispatch, id]); + dispatch(layerMovedToBack(layerId)); + }, [dispatch, layerId]); const resetLayer = useCallback(() => { - dispatch(rpLayerReset(id)); - }, [dispatch, id]); + dispatch(rpLayerReset(layerId)); + }, [dispatch, layerId]); const deleteLayer = useCallback(() => { - dispatch(layerDeleted(id)); - }, [dispatch, id]); + dispatch(layerDeleted(layerId)); + }, [dispatch, layerId]); return ( } /> @@ -88,4 +92,4 @@ export const LayerMenu = memo(({ id }: Props) => { ); }); -LayerMenu.displayName = 'LayerMenu'; +RPLayerMenu.displayName = 'RPLayerMenu'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsNegativePrompt.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerNegativePrompt.tsx similarity index 94% rename from invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsNegativePrompt.tsx rename to invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerNegativePrompt.tsx index 05e94304a2..3a9e49812a 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsNegativePrompt.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerNegativePrompt.tsx @@ -15,7 +15,7 @@ type Props = { layerId: string; }; -export const RegionalPromptsNegativePrompt = memo((props: Props) => { +export const RPLayerNegativePrompt = memo((props: Props) => { const prompt = useLayerNegativePrompt(props.layerId); const dispatch = useAppDispatch(); const textareaRef = useRef(null); @@ -64,4 +64,4 @@ export const RegionalPromptsNegativePrompt = memo((props: Props) => { ); }); -RegionalPromptsNegativePrompt.displayName = 'RegionalPromptsPrompt'; +RPLayerNegativePrompt.displayName = 'RPLayerNegativePrompt'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsPositivePrompt.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerPositivePrompt.tsx similarity index 94% rename from invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsPositivePrompt.tsx rename to invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerPositivePrompt.tsx index 84cc6c1f57..56276278f4 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsPositivePrompt.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerPositivePrompt.tsx @@ -15,7 +15,7 @@ type Props = { layerId: string; }; -export const RegionalPromptsPositivePrompt = memo((props: Props) => { +export const RPLayerPositivePrompt = memo((props: Props) => { const prompt = useLayerPositivePrompt(props.layerId); const dispatch = useAppDispatch(); const textareaRef = useRef(null); @@ -64,4 +64,4 @@ export const RegionalPromptsPositivePrompt = memo((props: Props) => { ); }); -RegionalPromptsPositivePrompt.displayName = 'RegionalPromptsPrompt'; +RPLayerPositivePrompt.displayName = 'RPLayerPositivePrompt'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerVisibilityToggle.tsx similarity index 72% rename from invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx rename to invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerVisibilityToggle.tsx index a4c96ddea8..efaec24888 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/LayerVisibilityToggle.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RPLayerVisibilityToggle.tsx @@ -6,15 +6,15 @@ import { memo, useCallback } from 'react'; import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi'; type Props = { - id: string; + layerId: string; }; -export const LayerVisibilityToggle = memo(({ id }: Props) => { +export const RPLayerVisibilityToggle = memo(({ layerId }: Props) => { const dispatch = useAppDispatch(); - const isVisible = useLayerIsVisible(id); + const isVisible = useLayerIsVisible(layerId); const onClick = useCallback(() => { - dispatch(rpLayerIsVisibleToggled(id)); - }, [dispatch, id]); + dispatch(rpLayerIsVisibleToggled(layerId)); + }, [dispatch, layerId]); return ( { ); }); -LayerVisibilityToggle.displayName = 'LayerVisibilityToggle'; +RPLayerVisibilityToggle.displayName = 'RPLayerVisibilityToggle'; diff --git a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx index 02e10f6b8c..90dec61667 100644 --- a/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx +++ b/invokeai/frontend/web/src/features/regionalPrompts/components/RegionalPromptsEditor.tsx @@ -5,17 +5,20 @@ import { useAppSelector } from 'app/store/storeHooks'; import { AddLayerButton } from 'features/regionalPrompts/components/AddLayerButton'; import { BrushSize } from 'features/regionalPrompts/components/BrushSize'; import { DeleteAllLayersButton } from 'features/regionalPrompts/components/DeleteAllLayersButton'; -import { LayerListItem } from 'features/regionalPrompts/components/LayerListItem'; import { PromptLayerOpacity } from 'features/regionalPrompts/components/PromptLayerOpacity'; +import { RPLayerListItem } from 'features/regionalPrompts/components/RPLayerListItem'; import { StageComponent } from 'features/regionalPrompts/components/StageComponent'; import { ToolChooser } from 'features/regionalPrompts/components/ToolChooser'; import { UndoRedoButtonGroup } from 'features/regionalPrompts/components/UndoRedoButtonGroup'; -import { selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice'; +import { isRegionalPromptLayer, selectRegionalPromptsSlice } from 'features/regionalPrompts/store/regionalPromptsSlice'; import { getRegionalPromptLayerBlobs } from 'features/regionalPrompts/util/getLayerBlobs'; import { memo } from 'react'; -const selectLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => - regionalPrompts.present.layers.map((l) => l.id).reverse() +const selectRPLayerIdsReversed = createMemoizedSelector(selectRegionalPromptsSlice, (regionalPrompts) => + regionalPrompts.present.layers + .filter(isRegionalPromptLayer) + .map((l) => l.id) + .reverse() ); const debugBlobs = () => { @@ -23,7 +26,7 @@ const debugBlobs = () => { }; export const RegionalPromptsEditor = memo(() => { - const layerIdsReversed = useAppSelector(selectLayerIdsReversed); + const rpLayerIdsReversed = useAppSelector(selectRPLayerIdsReversed); return ( @@ -38,8 +41,8 @@ export const RegionalPromptsEditor = memo(() => { - {layerIdsReversed.map((id) => ( - + {rpLayerIdsReversed.map((id) => ( + ))}