From 7a7a2e147c35692d3b42c9c8fedb51842bf2edaa Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 23 Jun 2025 16:02:08 +0000 Subject: [PATCH] Add toggle for non-raster layers with hotkey and UI button --- invokeai/frontend/web/public/locales/en.json | 6 ++++ .../EntityListSelectedEntityActionBar.tsx | 2 ++ .../components/CanvasLayersPanelContent.tsx | 2 ++ .../CanvasNonRasterLayersIsHiddenToggle.tsx | 36 +++++++++++++++++++ .../useCanvasToggleNonRasterLayersHotkey.ts | 19 ++++++++++ .../hooks/useNonRasterLayersIsHidden.ts | 21 +++++++++++ .../controlLayers/store/canvasSlice.ts | 17 +++++++++ .../components/HotkeysModal/useHotkeyData.ts | 1 + 8 files changed, 104 insertions(+) create mode 100644 invokeai/frontend/web/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx create mode 100644 invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts create mode 100644 invokeai/frontend/web/src/features/controlLayers/hooks/useNonRasterLayersIsHidden.ts diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index ce14674926..d330adc575 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -579,6 +579,10 @@ "cancelTransform": { "title": "Cancel Transform", "desc": "Cancel the pending transform." + }, + "toggleNonRasterLayers": { + "title": "Toggle Non-Raster Layers", + "desc": "Show or hide all non-raster layer categories (Control Layers, Inpaint Masks, Regional Guidance)." } }, "workflows": { @@ -1994,6 +1998,8 @@ "disableTransparencyEffect": "Disable Transparency Effect", "hidingType": "Hiding {{type}}", "showingType": "Showing {{type}}", + "showNonRasterLayers": "Show Non-Raster Layers", + "hideNonRasterLayers": "Hide Non-Raster Layers", "dynamicGrid": "Dynamic Grid", "logDebugInfo": "Log Debug Info", "locked": "Locked", diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar.tsx index 2ec01041e2..5af3b1c40b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar.tsx @@ -1,4 +1,5 @@ import { Flex, Spacer } from '@invoke-ai/ui-library'; +import { CanvasNonRasterLayersIsHiddenToggle } from 'features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle'; import { EntityListGlobalActionBarAddLayerMenu } from 'features/controlLayers/components/CanvasEntityList/EntityListGlobalActionBarAddLayerMenu'; import { EntityListSelectedEntityActionBarDuplicateButton } from 'features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarDuplicateButton'; import { EntityListSelectedEntityActionBarFill } from 'features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBarFill'; @@ -22,6 +23,7 @@ export const EntityListSelectedEntityActionBar = memo(() => { + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasLayersPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasLayersPanelContent.tsx index 49ae65205b..3425f149b9 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasLayersPanelContent.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasLayersPanelContent.tsx @@ -4,6 +4,7 @@ import { CanvasAddEntityButtons } from 'features/controlLayers/components/Canvas import { CanvasEntityList } from 'features/controlLayers/components/CanvasEntityList/CanvasEntityList'; import { EntityListSelectedEntityActionBar } from 'features/controlLayers/components/CanvasEntityList/EntityListSelectedEntityActionBar'; import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; +import { useCanvasToggleNonRasterLayersHotkey } from 'features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey'; import { selectHasEntities } from 'features/controlLayers/store/selectors'; import { memo } from 'react'; @@ -11,6 +12,7 @@ import { ParamDenoisingStrength } from './ParamDenoisingStrength'; export const CanvasLayersPanel = memo(() => { const hasEntities = useAppSelector(selectHasEntities); + useCanvasToggleNonRasterLayersHotkey(); return ( diff --git a/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx new file mode 100644 index 0000000000..cb53fb2e9b --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/components/common/CanvasNonRasterLayersIsHiddenToggle.tsx @@ -0,0 +1,36 @@ +import { IconButton } from '@invoke-ai/ui-library'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { useNonRasterLayersIsHidden } from 'features/controlLayers/hooks/useNonRasterLayersIsHidden'; +import { allNonRasterLayersIsHiddenToggled } from 'features/controlLayers/store/canvasSlice'; +import type { MouseEventHandler } from 'react'; +import { memo, useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; +import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi'; + +export const CanvasNonRasterLayersIsHiddenToggle = memo(() => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const isHidden = useNonRasterLayersIsHidden(); + + const onClick = useCallback( + (e) => { + e.stopPropagation(); + dispatch(allNonRasterLayersIsHiddenToggled()); + }, + [dispatch] + ); + + return ( + : } + onClick={onClick} + alignSelf="stretch" + /> + ); +}); + +CanvasNonRasterLayersIsHiddenToggle.displayName = 'CanvasNonRasterLayersIsHiddenToggle'; \ No newline at end of file diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts new file mode 100644 index 0000000000..e43241457e --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useCanvasToggleNonRasterLayersHotkey.ts @@ -0,0 +1,19 @@ +import { useAppDispatch } from 'app/store/storeHooks'; +import { allNonRasterLayersIsHiddenToggled } from 'features/controlLayers/store/canvasSlice'; +import { useRegisteredHotkeys } from 'features/system/components/HotkeysModal/useHotkeyData'; +import { useCallback } from 'react'; + +export const useCanvasToggleNonRasterLayersHotkey = () => { + const dispatch = useAppDispatch(); + + const handleToggleNonRasterLayers = useCallback(() => { + dispatch(allNonRasterLayersIsHiddenToggled()); + }, [dispatch]); + + useRegisteredHotkeys({ + id: 'toggleNonRasterLayers', + category: 'canvas', + callback: handleToggleNonRasterLayers, + dependencies: [handleToggleNonRasterLayers], + }); +}; \ No newline at end of file diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useNonRasterLayersIsHidden.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useNonRasterLayersIsHidden.ts new file mode 100644 index 0000000000..027720dd46 --- /dev/null +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/useNonRasterLayersIsHidden.ts @@ -0,0 +1,21 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { useAppSelector } from 'app/store/storeHooks'; +import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; +import { useMemo } from 'react'; + +export const useNonRasterLayersIsHidden = (): boolean => { + const selectNonRasterLayersIsHidden = useMemo( + () => + createSelector(selectCanvasSlice, (canvas) => { + // Check if all non-raster layer categories are hidden + return ( + canvas.controlLayers.isHidden && + canvas.inpaintMasks.isHidden && + canvas.regionalGuidance.isHidden + ); + }), + [] + ); + const isHidden = useAppSelector(selectNonRasterLayersIsHidden); + return isHidden; +}; \ No newline at end of file diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts index 4b823a70da..b7f7a6cf2a 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts @@ -1539,6 +1539,22 @@ export const canvasSlice = createSlice({ break; } }, + allNonRasterLayersIsHiddenToggled: (state) => { + // Toggle visibility for all non-raster layer categories + // Check if any non-raster layers are currently visible + const hasVisibleNonRasterLayers = + !state.controlLayers.isHidden || + !state.inpaintMasks.isHidden || + !state.regionalGuidance.isHidden; + + // If any are visible, hide all; if all are hidden, show all + const shouldHide = hasVisibleNonRasterLayers; + + state.controlLayers.isHidden = shouldHide; + state.inpaintMasks.isHidden = shouldHide; + state.regionalGuidance.isHidden = shouldHide; + // Note: reference_image doesn't have isHidden property, so it's not included + }, allEntitiesDeleted: (state) => { // Deleting all entities is equivalent to resetting the state for each entity type const initialState = getInitialCanvasState(); @@ -1648,6 +1664,7 @@ export const { entitiesReordered, allEntitiesDeleted, allEntitiesOfTypeIsHiddenToggled, + allNonRasterLayersIsHiddenToggled, // bbox bboxChangedFromCanvas, bboxScaledWidthChanged, diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts b/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts index 781397e794..0241f45cec 100644 --- a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts +++ b/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts @@ -122,6 +122,7 @@ export const useHotkeyData = (): HotkeysData => { addHotkey('canvas', 'cancelTransform', ['esc']); addHotkey('canvas', 'applySegmentAnything', ['enter']); addHotkey('canvas', 'cancelSegmentAnything', ['esc']); + addHotkey('canvas', 'toggleNonRasterLayers', ['shift+h']); // Workflows addHotkey('workflows', 'addNode', ['shift+a', 'space']);