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']);