diff --git a/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx index 5eb05fe95b..b9d3b0fc62 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx @@ -41,13 +41,14 @@ export const AddLayerButton = memo(() => { icon={} variant="link" data-testid="control-layers-add-layer-menu-button" + alignSelf="stretch" /> - {t('controlLayers.inpaintMask')} - {t('controlLayers.regionalGuidance')} - {t('controlLayers.rasterLayer')} - {t('controlLayers.controlLayer')} - {t('controlLayers.globalIPAdapterLayer')} + {t('controlLayers.inpaintMask', { count: 1 })} + {t('controlLayers.regionalGuidance', { count: 1 })} + {t('controlLayers.rasterLayer', { count: 1 })} + {t('controlLayers.controlLayer', { count: 1 })} + {t('controlLayers.ipAdapter', { count: 1 })} ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx new file mode 100644 index 0000000000..b98c0e78d6 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx @@ -0,0 +1,59 @@ +import { Button, ButtonGroup, Flex } from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { useDefaultControlAdapter, useDefaultIPAdapter } from 'features/controlLayers/hooks/useLayerControlAdapter'; +import { + controlLayerAdded, + inpaintMaskAdded, + ipaAdded, + rasterLayerAdded, + rgAdded, +} from 'features/controlLayers/store/canvasV2Slice'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiPlusBold } from 'react-icons/pi'; + +export const CanvasAddEntityButtons = memo(() => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const defaultControlAdapter = useDefaultControlAdapter(); + const defaultIPAdapter = useDefaultIPAdapter(); + const addInpaintMask = useCallback(() => { + dispatch(inpaintMaskAdded()); + }, [dispatch]); + const addRegionalGuidance = useCallback(() => { + dispatch(rgAdded()); + }, [dispatch]); + const addRasterLayer = useCallback(() => { + dispatch(rasterLayerAdded({ isSelected: true })); + }, [dispatch]); + const addControlLayer = useCallback(() => { + dispatch(controlLayerAdded({ isSelected: true, overrides: { controlAdapter: defaultControlAdapter } })); + }, [defaultControlAdapter, dispatch]); + const addIPAdapter = useCallback(() => { + dispatch(ipaAdded({ ipAdapter: defaultIPAdapter })); + }, [defaultIPAdapter, dispatch]); + + return ( + + + + + + + + + + ); +}); + +CanvasAddEntityButtons.displayName = 'CanvasAddEntityButtons'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx index 3522895db4..2b28bdc61e 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList.tsx @@ -6,23 +6,20 @@ import { InpaintMaskList } from 'features/controlLayers/components/InpaintMask/I import { IPAdapterList } from 'features/controlLayers/components/IPAdapter/IPAdapterList'; import { RasterLayerEntityList } from 'features/controlLayers/components/RasterLayer/RasterLayerEntityList'; import { RegionalGuidanceEntityList } from 'features/controlLayers/components/RegionalGuidance/RegionalGuidanceEntityList'; -import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { memo } from 'react'; export const CanvasEntityList = memo(() => { return ( - - - - - - - - - - - - + + + + + + + + + + ); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasPanelContent.tsx new file mode 100644 index 0000000000..3ecf8f2b6d --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasPanelContent.tsx @@ -0,0 +1,18 @@ +import { useAppSelector } from 'app/store/storeHooks'; +import { CanvasAddEntityButtons } from 'features/controlLayers/components/CanvasAddEntityButtons'; +import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList'; +import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; +import { selectEntityCount } from 'features/controlLayers/store/selectors'; +import { memo } from 'react'; + +export const CanvasPanelContent = memo(() => { + const hasEntities = useAppSelector((s) => selectEntityCount(s) > 0); + return ( + + {!hasEntities && } + {hasEntities && } + + ); +}); + +CanvasPanelContent.displayName = 'CanvasPanelContent'; diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityCount.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeCount.ts similarity index 92% rename from invokeai/frontend/web/src/features/controlLayers/hooks/useEntityCount.ts rename to invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeCount.ts index 9c73cf32b5..f2209af4a9 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityCount.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeCount.ts @@ -4,7 +4,7 @@ import { selectCanvasV2Slice } from 'features/controlLayers/store/canvasV2Slice' import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; import { useMemo } from 'react'; -export const useEntityCount = (type: CanvasEntityIdentifier['type']): number => { +export const useEntityTypeCount = (type: CanvasEntityIdentifier['type']): number => { const selectEntityCount = useMemo( () => createSelector(selectCanvasV2Slice, (canvasV2) => { diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeTitle.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeTitle.ts index 80a0210f26..c693db533c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeTitle.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeTitle.ts @@ -1,4 +1,4 @@ -import { useEntityCount } from 'features/controlLayers/hooks/useEntityCount'; +import { useEntityTypeCount } from 'features/controlLayers/hooks/useEntityTypeCount'; import { useEntityTypeIsHidden } from 'features/controlLayers/hooks/useEntityTypeIsHidden'; import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; import { useMemo } from 'react'; @@ -8,7 +8,7 @@ export const useEntityTypeTitle = (type: CanvasEntityIdentifier['type']): string const { t } = useTranslation(); const isHidden = useEntityTypeIsHidden(type); - const count = useEntityCount(type); + const count = useEntityTypeCount(type); const title = useMemo(() => { const context = isHidden ? 'hidden' : 'visible'; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts b/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts index c81856b5df..d78417d55e 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/selectors.ts @@ -5,9 +5,10 @@ import { getOptimalDimension } from 'features/parameters/util/optimalDimension'; export const selectEntityCount = createSelector(selectCanvasV2Slice, (canvasV2) => { return ( canvasV2.regions.entities.length + - // canvasV2.controlAdapters.entities.length + canvasV2.ipAdapters.entities.length + - canvasV2.rasterLayers.entities.length + canvasV2.rasterLayers.entities.length + + canvasV2.controlLayers.entities.length + + canvasV2.inpaintMasks.entities.length ); }); diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelTextToImage.tsx b/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelTextToImage.tsx index 43c878d8f2..3faa520471 100644 --- a/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelTextToImage.tsx +++ b/invokeai/frontend/web/src/features/ui/components/ParametersPanels/ParametersPanelTextToImage.tsx @@ -4,7 +4,7 @@ import { useStore } from '@nanostores/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; import { AddLayerButton } from 'features/controlLayers/components/AddLayerButton'; -import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList'; +import { CanvasPanelContent } from 'features/controlLayers/components/CanvasPanelContent'; import { $isPreviewVisible } from 'features/controlLayers/store/canvasV2Slice'; import { selectEntityCount } from 'features/controlLayers/store/selectors'; import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice'; @@ -90,7 +90,7 @@ const ParametersPanelTextToImage = () => { gap={2} onChange={onChangeTabs} > - + {t('common.settingsLabel')} @@ -115,7 +115,7 @@ const ParametersPanelTextToImage = () => { - +