Add option to save all staging images to gallery in canvas mode

Co-authored-by: kent <kent@invoke.ai>
This commit is contained in:
Cursor Agent
2025-07-07 01:27:09 +00:00
committed by psychedelicious
parent e6c67cc00f
commit ef135f9923
8 changed files with 75 additions and 5 deletions

View File

@@ -1962,6 +1962,7 @@
"recalculateRects": "Recalculate Rects",
"clipToBbox": "Clip Strokes to Bbox",
"outputOnlyMaskedRegions": "Output Only Generated Regions",
"saveAllStagingImagesToGallery": "Save All Staging Images to Gallery",
"addLayer": "Add Layer",
"duplicate": "Duplicate",
"moveToFront": "Move to Front",
@@ -2330,6 +2331,9 @@
"label": "Preserve Masked Region",
"alert": "Preserving Masked Region"
},
"saveAllStagingImagesToGallery": {
"alert": "Saving All Staging Images to Gallery"
},
"isolatedStagingPreview": "Isolated Staging Preview",
"isolatedPreview": "Isolated Preview",
"isolatedLayerPreview": "Isolated Layer Preview",

View File

@@ -0,0 +1,23 @@
import { Alert, AlertIcon, AlertTitle } from '@invoke-ai/ui-library';
import { useAppSelector } from 'app/store/storeHooks';
import { selectSaveAllStagingImagesToGallery } from 'features/controlLayers/store/canvasSettingsSlice';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
export const CanvasAlertsSaveAllStagingImagesToGallery = memo(() => {
const { t } = useTranslation();
const saveAllStagingImagesToGallery = useAppSelector(selectSaveAllStagingImagesToGallery);
if (!saveAllStagingImagesToGallery) {
return null;
}
return (
<Alert status="info" borderRadius="base" fontSize="sm" shadow="md" w="fit-content">
<AlertIcon />
<AlertTitle>{t('controlLayers.settings.saveAllStagingImagesToGallery.alert')}</AlertTitle>
</Alert>
);
});
CanvasAlertsSaveAllStagingImagesToGallery.displayName = 'CanvasAlertsSaveAllStagingImagesToGallery';

View File

@@ -26,6 +26,7 @@ import { CanvasSettingsPreserveMaskCheckbox } from 'features/controlLayers/compo
import { CanvasSettingsPressureSensitivityCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsPressureSensitivity';
import { CanvasSettingsRecalculateRectsButton } from 'features/controlLayers/components/Settings/CanvasSettingsRecalculateRectsButton';
import { CanvasSettingsRuleOfThirdsSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsRuleOfThirdsGuideSwitch';
import { CanvasSettingsSaveAllStagingImagesToGalleryCheckbox } from 'features/controlLayers/components/Settings/CanvasSettingsSaveAllStagingImagesToGalleryCheckbox';
import { CanvasSettingsShowHUDSwitch } from 'features/controlLayers/components/Settings/CanvasSettingsShowHUDSwitch';
import { CanvasSettingsShowProgressOnCanvas } from 'features/controlLayers/components/Settings/CanvasSettingsShowProgressOnCanvasSwitch';
import { memo } from 'react';
@@ -61,6 +62,7 @@ export const CanvasSettingsPopover = memo(() => {
<CanvasSettingsPreserveMaskCheckbox />
<CanvasSettingsClipToBboxCheckbox />
<CanvasSettingsOutputOnlyMaskedRegionsCheckbox />
<CanvasSettingsSaveAllStagingImagesToGalleryCheckbox />
</Flex>
<Divider />

View File

@@ -0,0 +1,25 @@
import { Checkbox, FormControl, FormLabel } from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
selectSaveAllStagingImagesToGallery,
settingsSaveAllStagingImagesToGalleryToggled,
} from 'features/controlLayers/store/canvasSettingsSlice';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
export const CanvasSettingsSaveAllStagingImagesToGalleryCheckbox = memo(() => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const saveAllStagingImagesToGallery = useAppSelector(selectSaveAllStagingImagesToGallery);
const onChange = useCallback(() => {
dispatch(settingsSaveAllStagingImagesToGalleryToggled());
}, [dispatch]);
return (
<FormControl w="full">
<FormLabel flexGrow={1}>{t('controlLayers.saveAllStagingImagesToGallery')}</FormLabel>
<Checkbox isChecked={saveAllStagingImagesToGallery} onChange={onChange} />
</FormControl>
);
});
CanvasSettingsSaveAllStagingImagesToGalleryCheckbox.displayName = 'CanvasSettingsSaveAllStagingImagesToGalleryCheckbox';

View File

@@ -77,6 +77,10 @@ type CanvasSettingsState = {
* Whether to show the rule of thirds composition guide overlay on the canvas.
*/
ruleOfThirds: boolean;
/**
* Whether to save all staging images to the gallery instead of keeping them as intermediate images.
*/
saveAllStagingImagesToGallery: boolean;
};
const initialState: CanvasSettingsState = {
@@ -97,6 +101,7 @@ const initialState: CanvasSettingsState = {
isolatedLayerPreview: true,
pressureSensitivity: true,
ruleOfThirds: false,
saveAllStagingImagesToGallery: false,
};
export const canvasSettingsSlice = createSlice({
@@ -154,6 +159,9 @@ export const canvasSettingsSlice = createSlice({
settingsRuleOfThirdsToggled: (state) => {
state.ruleOfThirds = !state.ruleOfThirds;
},
settingsSaveAllStagingImagesToGalleryToggled: (state) => {
state.saveAllStagingImagesToGallery = !state.saveAllStagingImagesToGallery;
},
},
});
@@ -175,6 +183,7 @@ export const {
settingsIsolatedLayerPreviewToggled,
settingsPressureSensitivityToggled,
settingsRuleOfThirdsToggled,
settingsSaveAllStagingImagesToGalleryToggled,
} = canvasSettingsSlice.actions;
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
@@ -209,3 +218,6 @@ export const selectIsolatedStagingPreview = createCanvasSettingsSelector((settin
export const selectIsolatedLayerPreview = createCanvasSettingsSelector((settings) => settings.isolatedLayerPreview);
export const selectPressureSensitivity = createCanvasSettingsSelector((settings) => settings.pressureSensitivity);
export const selectRuleOfThirds = createCanvasSettingsSelector((settings) => settings.ruleOfThirds);
export const selectSaveAllStagingImagesToGallery = createCanvasSettingsSelector(
(settings) => settings.saveAllStagingImagesToGallery
);

View File

@@ -55,9 +55,7 @@ export const ModelInstallQueue = memo(() => {
<Box layerStyle="first" p={3} borderRadius="base" w="full" h="full">
<ScrollableContent>
<Flex flexDir="column-reverse" gap="2" w="full">
{data?.map((model) => (
<ModelInstallQueueItem key={model.id} installJob={model} />
))}
{data?.map((model) => <ModelInstallQueueItem key={model.id} installJob={model} />)}
</Flex>
</ScrollableContent>
</Box>

View File

@@ -1,6 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from 'app/store/store';
import { getPrefixedId } from 'features/controlLayers/konva/util';
import { selectSaveAllStagingImagesToGallery } from 'features/controlLayers/store/canvasSettingsSlice';
import {
selectImg2imgStrength,
selectMainModelConfig,
@@ -44,8 +45,11 @@ export const selectCanvasOutputFields = (state: RootState) => {
// Advanced session means working on canvas - images are not saved to gallery or added to a board.
// Simple session means working in YOLO mode - images are saved to gallery & board.
const tab = selectActiveTab(state);
const is_intermediate = tab === 'canvas';
const board = tab === 'canvas' ? undefined : getBoardField(state);
const saveAllStagingImagesToGallery = selectSaveAllStagingImagesToGallery(state);
// If we're on canvas and the save all staging images setting is enabled, save to gallery
const is_intermediate = tab === 'canvas' && !saveAllStagingImagesToGallery;
const board = tab === 'canvas' && !saveAllStagingImagesToGallery ? undefined : getBoardField(state);
return {
is_intermediate,

View File

@@ -2,6 +2,7 @@ import { ContextMenu, Divider, Flex, IconButton, Menu, MenuButton, MenuList } fr
import { useAppSelector } from 'app/store/storeHooks';
import { CanvasAlertsInvocationProgress } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsInvocationProgress';
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
import { CanvasAlertsSaveAllStagingImagesToGallery } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSaveAllStagingImagesToGallery';
import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus';
import { CanvasBusySpinner } from 'features/controlLayers/components/CanvasBusySpinner';
import { CanvasContextMenuGlobalMenuItems } from 'features/controlLayers/components/CanvasContextMenu/CanvasContextMenuGlobalMenuItems';
@@ -88,6 +89,7 @@ export const CanvasWorkspacePanel = memo(() => {
{showHUD && <CanvasHUD />}
<CanvasAlertsSelectedEntityStatus />
<CanvasAlertsPreserveMask />
<CanvasAlertsSaveAllStagingImagesToGallery />
<CanvasAlertsInvocationProgress />
</Flex>
<Flex position="absolute" top={1} insetInlineEnd={1}>