diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 7ae5fb648d..ff512cb48e 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -1684,27 +1684,34 @@ "addPositivePrompt": "Add $t(common.positivePrompt)", "addNegativePrompt": "Add $t(common.negativePrompt)", "addIPAdapter": "Add $t(common.ipAdapter)", + "addRasterLayer": "Add $t(controlLayers.rasterLayer)", + "addControlLayer": "Add $t(controlLayers.controlLayer)", + "addInpaintMask": "Add $t(controlLayers.inpaintMask)", + "addRegionalGuidance": "Add $t(controlLayers.regionalGuidance)", "regionalGuidanceLayer": "$t(controlLayers.regionalGuidance) $t(unifiedCanvas.layer)", "raster": "Raster", - "rasterLayer_one": "Raster Layer", - "controlLayer_one": "Control Layer", - "inpaintMask_one": "Inpaint Mask", - "regionalGuidance_one": "Regional Guidance", - "ipAdapter_one": "IP Adapter", - "rasterLayer_other": "Raster Layers", - "controlLayer_other": "Control Layers", - "inpaintMask_other": "Inpaint Masks", - "regionalGuidance_other": "Regional Guidance", - "ipAdapter_other": "IP Adapters", + "rasterLayer": "Raster Layer", + "controlLayer": "Control Layer", + "inpaintMask": "Inpaint Mask", + "regionalGuidance": "Regional Guidance", + "ipAdapter": "IP Adapter", + "rasterLayer_withCount_one": "$t(controlLayers.rasterLayer)", + "controlLayer_withCount_one": "$t(controlLayers.controlLayer)", + "inpaintMask_withCount_one": "$t(controlLayers.inpaintMask)", + "regionalGuidance_withCount_one": "$t(controlLayers.regionalGuidance)", + "ipAdapter_withCount_one": "$t(controlLayers.ipAdapter)", + "rasterLayer_withCount_other": "Raster Layers", + "controlLayer_withCount_other": "Control Layers", + "inpaintMask_withCount_other": "Inpaint Masks", + "regionalGuidance_withCount_other": "Regional Guidance", + "ipAdapter_withCount_other": "IP Adapters", "opacity": "Opacity", "regionalGuidance_withCount_hidden": "Regional Guidance ({{count}} hidden)", - "controlAdapters_withCount_hidden": "Control Adapters ({{count}} hidden)", "controlLayers_withCount_hidden": "Control Layers ({{count}} hidden)", "rasterLayers_withCount_hidden": "Raster Layers ({{count}} hidden)", "ipAdapters_withCount_hidden": "IP Adapters ({{count}} hidden)", "inpaintMasks_withCount_hidden": "Inpaint Masks ({{count}} hidden)", "regionalGuidance_withCount_visible": "Regional Guidance ({{count}})", - "controlAdapters_withCount_visible": "Control Adapters ({{count}})", "controlLayers_withCount_visible": "Control Layers ({{count}})", "rasterLayers_withCount_visible": "Raster Layers ({{count}})", "ipAdapters_withCount_visible": "IP Adapters ({{count}})", diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx index 7b50721185..0eea95edc6 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasAddEntityButtons.tsx @@ -34,19 +34,19 @@ export const CanvasAddEntityButtons = memo(() => { diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarAddLayerMenuItems.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarAddLayerMenuItems.tsx index 810422ad8a..492b28cbdd 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarAddLayerMenuItems.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListActionBarAddLayerMenuItems.tsx @@ -33,19 +33,19 @@ export const CanvasEntityListMenuItems = memo(() => { return ( <> } onClick={addInpaintMask}> - {t('controlLayers.inpaintMask', { count: 1 })} + {t('controlLayers.inpaintMask')} } onClick={addRegionalGuidance}> - {t('controlLayers.regionalGuidance', { count: 1 })} + {t('controlLayers.regionalGuidance')} } onClick={addRasterLayer}> - {t('controlLayers.rasterLayer', { count: 1 })} + {t('controlLayers.rasterLayer')} } onClick={addControlLayer}> - {t('controlLayers.controlLayer', { count: 1 })} + {t('controlLayers.controlLayer')} } onClick={addIPAdapter}> - {t('controlLayers.ipAdapter', { count: 1 })} + {t('controlLayers.ipAdapter')} ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx new file mode 100644 index 0000000000..f3dd8a2c1a --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityAddOfTypeButton.tsx @@ -0,0 +1,70 @@ +import { IconButton } from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { + controlLayerAdded, + inpaintMaskAdded, + ipaAdded, + rasterLayerAdded, + rgAdded, +} from 'features/controlLayers/store/canvasSlice'; +import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; +import { memo, useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiPlusBold } from 'react-icons/pi'; + +type Props = { + type: CanvasEntityIdentifier['type']; +}; + +export const CanvasEntityAddOfTypeButton = memo(({ type }: Props) => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const onClick = useCallback(() => { + switch (type) { + case 'inpaint_mask': + dispatch(inpaintMaskAdded({ isSelected: true })); + break; + case 'regional_guidance': + dispatch(rgAdded({ isSelected: true })); + break; + case 'raster_layer': + dispatch(rasterLayerAdded({ isSelected: true })); + break; + case 'control_layer': + dispatch(controlLayerAdded({ isSelected: true })); + break; + case 'ip_adapter': + dispatch(ipaAdded({ isSelected: true })); + break; + } + }, [dispatch, type]); + + const label = useMemo(() => { + switch (type) { + case 'inpaint_mask': + return t('controlLayers.addInpaintMask'); + case 'regional_guidance': + return t('controlLayers.addRegionalGuidance'); + case 'raster_layer': + return t('controlLayers.addRasterLayer'); + case 'control_layer': + return t('controlLayers.addControlLayer'); + case 'ip_adapter': + return t('controlLayers.addIPAdapter'); + } + }, [type, t]); + + return ( + } + onClick={onClick} + alignSelf="stretch" + /> + ); +}); + +CanvasEntityAddOfTypeButton.displayName = 'CanvasEntityAddOfTypeButton'; diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityGroupList.tsx b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityGroupList.tsx index c665040905..61b42bd4c4 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityGroupList.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasEntityGroupList.tsx @@ -1,6 +1,7 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { Button, Collapse, Flex, Icon, Spacer, Text } from '@invoke-ai/ui-library'; import { useBoolean } from 'common/hooks/useBoolean'; +import { CanvasEntityAddOfTypeButton } from 'features/controlLayers/components/common/CanvasEntityAddOfTypeButton'; import { CanvasEntityTypeIsHiddenToggle } from 'features/controlLayers/components/common/CanvasEntityTypeIsHiddenToggle'; import { useEntityTypeTitle } from 'features/controlLayers/hooks/useEntityTypeTitle'; import type { CanvasEntityIdentifier } from 'features/controlLayers/store/types'; @@ -53,6 +54,7 @@ export const CanvasEntityGroupList = memo(({ isSelected, type, children }: Props + {type !== 'ip_adapter' && } diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTitle.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTitle.ts index d054083e16..6fd2f50799 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTitle.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTitle.ts @@ -29,15 +29,15 @@ export const useEntityTitle = (entityIdentifier: CanvasEntityIdentifier) => { const parts: string[] = []; if (entityIdentifier.type === 'inpaint_mask') { - parts.push(t('controlLayers.inpaintMask', { count: 1 })); + parts.push(t('controlLayers.inpaintMask')); } else if (entityIdentifier.type === 'control_layer') { - parts.push(t('controlLayers.controlLayer', { count: 1 })); + parts.push(t('controlLayers.controlLayer')); } else if (entityIdentifier.type === 'raster_layer') { - parts.push(t('controlLayers.rasterLayer', { count: 1 })); + parts.push(t('controlLayers.rasterLayer')); } else if (entityIdentifier.type === 'ip_adapter') { - parts.push(t('common.ipAdapter', { count: 1 })); + parts.push(t('common.ipAdapter')); } else if (entityIdentifier.type === 'regional_guidance') { - parts.push(t('controlLayers.regionalGuidance', { count: 1 })); + parts.push(t('controlLayers.regionalGuidance')); } else { assert(false, 'Unexpected entity type'); } diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeString.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeString.ts index 5c1682cfe5..042f3ba21f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeString.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useEntityTypeString.ts @@ -8,15 +8,15 @@ export const useEntityTypeString = (type: CanvasEntityIdentifier['type']): strin const typeString = useMemo(() => { switch (type) { case 'control_layer': - return t('controlLayers.controlLayer', { count: 0 }); + return t('controlLayers.controlLayer'); case 'raster_layer': - return t('controlLayers.rasterLayer', { count: 0 }); + return t('controlLayers.rasterLayer'); case 'inpaint_mask': - return t('controlLayers.inpaintMask', { count: 0 }); + return t('controlLayers.inpaintMask'); case 'regional_guidance': - return t('controlLayers.regionalGuidance', { count: 0 }); + return t('controlLayers.regionalGuidance'); case 'ip_adapter': - return t('controlLayers.ipAdapter', { count: 0 }); + return t('controlLayers.ipAdapter'); default: return ''; }