diff --git a/invokeai/frontend/web/src/app/hooks/useStudioInitAction.ts b/invokeai/frontend/web/src/app/hooks/useStudioInitAction.ts index f82949bb1e..34d2f8ea88 100644 --- a/invokeai/frontend/web/src/app/hooks/useStudioInitAction.ts +++ b/invokeai/frontend/web/src/app/hooks/useStudioInitAction.ts @@ -2,9 +2,8 @@ import { useStore } from '@nanostores/react'; import { useAppStore } from 'app/store/storeHooks'; import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; import { withResultAsync } from 'common/util/result'; -import { canvasReset } from 'features/controlLayers/store/actions'; -import { settingsSendToCanvasChanged } from 'features/controlLayers/store/canvasSettingsSlice'; import { rasterLayerAdded } from 'features/controlLayers/store/canvasSlice'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import type { CanvasRasterLayerState } from 'features/controlLayers/store/types'; import { imageDTOToImageObject } from 'features/controlLayers/store/util'; import { $imageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; @@ -91,9 +90,8 @@ export const useStudioInitAction = (action?: StudioInitAction) => { const overrides: Partial = { objects: [imageObject], }; - store.dispatch(canvasReset()); + store.dispatch(canvasSessionStarted({ sessionType: 'advanced' })); store.dispatch(rasterLayerAdded({ overrides, isSelected: true })); - store.dispatch(settingsSendToCanvasChanged(true)); store.dispatch(setActiveTab('canvas')); store.dispatch(sentImageToCanvas()); $imageViewer.set(false); @@ -164,15 +162,15 @@ export const useStudioInitAction = (action?: StudioInitAction) => { switch (destination) { case 'generation': // Go to the canvas tab, open the image viewer, and enable send-to-gallery mode + store.dispatch(canvasSessionStarted({ sessionType: 'simple' })); store.dispatch(setActiveTab('canvas')); store.dispatch(activeTabCanvasRightPanelChanged('gallery')); - store.dispatch(settingsSendToCanvasChanged(false)); $imageViewer.set(true); break; case 'canvas': // Go to the canvas tab, close the image viewer, and disable send-to-gallery mode + store.dispatch(canvasSessionStarted({ sessionType: 'advanced' })); store.dispatch(setActiveTab('canvas')); - store.dispatch(settingsSendToCanvasChanged(true)); $imageViewer.set(false); break; case 'workflows': diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts index 11dd39d867..a83da52d21 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/addCommitStagingAreaImageListener.ts @@ -1,7 +1,7 @@ import { isAnyOf } from '@reduxjs/toolkit'; import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; -import { canvasReset, newSessionRequested } from 'features/controlLayers/store/actions'; +import { canvasReset } from 'features/controlLayers/store/actions'; import { stagingAreaReset } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { toast } from 'features/toast/toast'; import { t } from 'i18next'; @@ -9,7 +9,7 @@ import { queueApi } from 'services/api/endpoints/queue'; const log = logger('canvas'); -const matchCanvasOrStagingAreaReset = isAnyOf(stagingAreaReset, canvasReset, newSessionRequested); +const matchCanvasOrStagingAreaReset = isAnyOf(stagingAreaReset, canvasReset); export const addStagingListeners = (startAppListening: AppStartListening) => { startAppListening({ diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx index a4c554073c..e17df9b425 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx @@ -19,9 +19,10 @@ import { StagingAreaToolbar } from 'features/controlLayers/components/StagingAre import { CanvasToolbar } from 'features/controlLayers/components/Toolbar/CanvasToolbar'; import { Transform } from 'features/controlLayers/components/Transform/Transform'; import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; -import { canvasReset, newAdvancedCanvasSessionRequested } from 'features/controlLayers/store/actions'; +import { canvasReset } from 'features/controlLayers/store/actions'; import { selectDynamicGrid, selectShowHUD } from 'features/controlLayers/store/canvasSettingsSlice'; import { + canvasSessionStarted, selectCanvasSessionType, selectIsStaging, selectSelectedImage, @@ -88,7 +89,7 @@ CanvasMainPanelContent.displayName = 'CanvasMainPanelContent'; const NoActiveSession = memo(() => { const dispatch = useAppDispatch(); const newSesh = useCallback(() => { - dispatch(newAdvancedCanvasSessionRequested()); + dispatch(canvasSessionStarted({ sessionType: 'advanced' })); }, [dispatch]); return ( diff --git a/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx b/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx index 0e341380ad..fd65ae8095 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/NewSessionConfirmationAlertDialog.tsx @@ -2,7 +2,7 @@ import { Checkbox, ConfirmationAlertDialog, Flex, FormControl, FormLabel, Text } import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAssertSingleton } from 'common/hooks/useAssertSingleton'; import { buildUseBoolean } from 'common/hooks/useBoolean'; -import { newAdvancedCanvasSessionRequested, newSimpleCanvasSessionRequested } from 'features/controlLayers/store/actions'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { selectSystemShouldConfirmOnNewSession, shouldConfirmOnNewSessionToggled, @@ -20,7 +20,7 @@ export const useNewGallerySession = () => { const newSessionDialog = useNewGallerySessionDialog(); const newGallerySessionImmediate = useCallback(() => { - dispatch(newSimpleCanvasSessionRequested()); + dispatch(canvasSessionStarted({ sessionType: 'simple' })); dispatch(activeTabCanvasRightPanelChanged('gallery')); }, [dispatch]); @@ -41,7 +41,7 @@ export const useNewCanvasSession = () => { const newSessionDialog = useNewCanvasSessionDialog(); const newCanvasSessionImmediate = useCallback(() => { - dispatch(newAdvancedCanvasSessionRequested()); + dispatch(canvasSessionStarted({ sessionType: 'advanced' })); dispatch(activeTabCanvasRightPanelChanged('layers')); }, [dispatch]); diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasProgressImageModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasProgressImageModule.ts index 3dd469528c..c36933a6cf 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasProgressImageModule.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasProgressImageModule.ts @@ -85,6 +85,8 @@ export class CanvasProgressImageModule extends CanvasModuleBase { if (data.destination !== 'canvas') { return; } + + // The staging area module handles _completed_ events. Only care about failed or canceled here. if (data.status === 'failed' || data.status === 'canceled') { this.$lastProgressEvent.set(null); this.$hasActiveGeneration.set(false); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/actions.ts b/invokeai/frontend/web/src/features/controlLayers/store/actions.ts index a63d19946d..9e1d9734cd 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/actions.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/actions.ts @@ -1,7 +1,4 @@ -import { createAction, isAnyOf } from '@reduxjs/toolkit'; +import { createAction } from '@reduxjs/toolkit'; // Needed to split this from canvasSlice.ts to avoid circular dependencies export const canvasReset = createAction('canvas/canvasReset'); -export const newSimpleCanvasSessionRequested = createAction('canvas/newSimpleCanvasSessionRequested'); -export const newAdvancedCanvasSessionRequested = createAction('canvas/newAdvancedCanvasSessionRequested'); -export const newSessionRequested = isAnyOf(newSimpleCanvasSessionRequested, newAdvancedCanvasSessionRequested); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.ts index 6e8b3f0599..ef9f86d189 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSettingsSlice.ts @@ -1,7 +1,6 @@ import type { PayloadAction, Selector } from '@reduxjs/toolkit'; import { createSelector, createSlice } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; -import { newAdvancedCanvasSessionRequested, newSimpleCanvasSessionRequested } from 'features/controlLayers/store/actions'; import type { RgbaColor } from 'features/controlLayers/store/types'; type CanvasSettingsState = { @@ -34,11 +33,6 @@ type CanvasSettingsState = { * The color to use when drawing lines or filling shapes. */ color: RgbaColor; - /** - * Whether to send generated images to canvas staging area. When disabled, generated images will be sent directly to - * the gallery. - */ - sendToCanvas: boolean; /** * Whether to composite inpainted/outpainted regions back onto the source image when saving canvas generations. * @@ -89,7 +83,6 @@ const initialState: CanvasSettingsState = { eraserWidth: 50, invertScrollForToolWidth: false, color: { r: 31, g: 160, b: 224, a: 1 }, // invokeBlue.500 - sendToCanvas: false, outputOnlyMaskedRegions: true, autoProcess: true, snapToGrid: true, @@ -126,9 +119,6 @@ export const canvasSettingsSlice = createSlice({ settingsInvertScrollForToolWidthChanged: (state, action: PayloadAction) => { state.invertScrollForToolWidth = action.payload; }, - settingsSendToCanvasChanged: (state, action: PayloadAction) => { - state.sendToCanvas = action.payload; - }, settingsOutputOnlyMaskedRegionsToggled: (state) => { state.outputOnlyMaskedRegions = !state.outputOnlyMaskedRegions; }, @@ -157,14 +147,6 @@ export const canvasSettingsSlice = createSlice({ state.pressureSensitivity = !state.pressureSensitivity; }, }, - extraReducers(builder) { - builder.addCase(newSimpleCanvasSessionRequested, (state) => { - state.sendToCanvas = false; - }); - builder.addCase(newAdvancedCanvasSessionRequested, (state) => { - state.sendToCanvas = true; - }); - }, }); export const { @@ -175,7 +157,6 @@ export const { settingsEraserWidthChanged, settingsColorChanged, settingsInvertScrollForToolWidthChanged, - settingsSendToCanvasChanged, settingsOutputOnlyMaskedRegionsToggled, settingsAutoProcessToggled, settingsSnapToGridToggled, @@ -212,7 +193,6 @@ export const selectBboxOverlay = createCanvasSettingsSelector((settings) => sett export const selectShowHUD = createCanvasSettingsSelector((settings) => settings.showHUD); export const selectAutoProcess = createCanvasSettingsSelector((settings) => settings.autoProcess); export const selectSnapToGrid = createCanvasSettingsSelector((settings) => settings.snapToGrid); -export const selectSendToCanvas = createCanvasSettingsSelector((canvasSettings) => canvasSettings.sendToCanvas); export const selectShowProgressOnCanvas = createCanvasSettingsSelector( (canvasSettings) => canvasSettings.showProgressOnCanvas ); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts index d73d0cd317..1b135b0ce4 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts @@ -5,11 +5,8 @@ import { moveOneToEnd, moveOneToStart, moveToEnd, moveToStart } from 'common/uti import { deepClone } from 'common/util/deepClone'; import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { - canvasReset, - newAdvancedCanvasSessionRequested, - newSimpleCanvasSessionRequested, -} from 'features/controlLayers/store/actions'; +import { canvasReset } from 'features/controlLayers/store/actions'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { modelChanged } from 'features/controlLayers/store/paramsSlice'; import { selectAllEntities, @@ -1806,13 +1803,7 @@ export const canvasSlice = createSlice({ syncScaledSize(state); } }); - builder.addCase(newSimpleCanvasSessionRequested, (state) => { - return resetState(state); - }); - builder.addCase(newAdvancedCanvasSessionRequested, (state) => { - const newState = resetState(state); - return newState; - }); + builder.addCase(canvasSessionStarted, (state) => resetState(state)); }, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts index 2dc441a801..4e840dcc35 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts @@ -1,11 +1,7 @@ import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; import { deepClone } from 'common/util/deepClone'; -import { - canvasReset, - newAdvancedCanvasSessionRequested, - newSimpleCanvasSessionRequested, -} from 'features/controlLayers/store/actions'; +import { canvasReset } from 'features/controlLayers/store/actions'; import type { StagingAreaImage } from 'features/controlLayers/store/types'; import { selectCanvasQueueCounts } from 'services/api/endpoints/queue'; @@ -15,15 +11,17 @@ type CanvasStagingAreaState = { selectedImageIndex: number; }; -const initialState: CanvasStagingAreaState = { +const INITIAL_STATE: CanvasStagingAreaState = { sessionType: null, images: [], selectedImageIndex: 0, }; +const getInitialState = (): CanvasStagingAreaState => deepClone(INITIAL_STATE); + export const canvasSessionSlice = createSlice({ name: 'canvasSession', - initialState, + initialState: getInitialState(), reducers: { stagingAreaImageStaged: (state, action: PayloadAction<{ stagingAreaImage: StagingAreaImage }>) => { const { stagingAreaImage } = action.payload; @@ -49,25 +47,15 @@ export const canvasSessionSlice = createSlice({ state.images = []; state.selectedImageIndex = 0; }, - canvasSessionStarted: (state, action: PayloadAction<{ sessionType: CanvasStagingAreaState['sessionType'] }>) => { + canvasSessionStarted: (_, action: PayloadAction<{ sessionType: CanvasStagingAreaState['sessionType'] }>) => { const { sessionType } = action.payload; + const state = getInitialState(); state.sessionType = sessionType; - state.images = []; - state.selectedImageIndex = 0; + return state; }, }, extraReducers(builder) { - builder.addCase(canvasReset, () => deepClone(initialState)); - builder.addCase(newSimpleCanvasSessionRequested, () => { - const state = deepClone(initialState); - state.sessionType === 'simple'; - return state; - }); - builder.addCase(newAdvancedCanvasSessionRequested, () => { - const state = deepClone(initialState); - state.sessionType === 'advanced'; - return state; - }); + builder.addCase(canvasReset, () => getInitialState()); }, }); @@ -88,7 +76,7 @@ const migrate = (state: any): any => { export const canvasStagingAreaPersistConfig: PersistConfig = { name: canvasSessionSlice.name, - initialState, + initialState: getInitialState(), migrate, persistDenylist: [], }; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/lorasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/lorasSlice.ts index df94543507..e4d91cc2f2 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/lorasSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/lorasSlice.ts @@ -1,13 +1,12 @@ import { createSelector, createSlice, type PayloadAction } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; import { deepClone } from 'common/util/deepClone'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import type { LoRA } from 'features/controlLayers/store/types'; import { zModelIdentifierField } from 'features/nodes/types/common'; import type { LoRAModelConfig } from 'services/api/types'; import { v4 as uuidv4 } from 'uuid'; -import { newSessionRequested } from './actions'; - type LoRAsState = { loras: LoRA[]; }; @@ -65,7 +64,7 @@ export const lorasSlice = createSlice({ }, }, extraReducers(builder) { - builder.addMatcher(newSessionRequested, () => { + builder.addCase(canvasSessionStarted, () => { // When a new session is requested, clear all LoRAs return deepClone(initialState); }); diff --git a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts index dabddf3c13..9fcf4a89f8 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/paramsSlice.ts @@ -1,6 +1,7 @@ import type { PayloadAction, Selector } from '@reduxjs/toolkit'; import { createSelector, createSlice } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import type { ParamsState, RgbaColor } from 'features/controlLayers/store/types'; import { getInitialParamsState } from 'features/controlLayers/store/types'; import { CLIP_SKIP_MAP } from 'features/parameters/types/constants'; @@ -24,7 +25,6 @@ import { clamp } from 'lodash-es'; import { modelConfigsAdapterSelectors, selectModelConfigsQuery } from 'services/api/endpoints/models'; import { isNonRefinerMainModelConfig } from 'services/api/types'; -import { newSessionRequested } from './actions'; export const paramsSlice = createSlice({ name: 'params', @@ -189,7 +189,7 @@ export const paramsSlice = createSlice({ paramsReset: (state) => resetState(state), }, extraReducers(builder) { - builder.addMatcher(newSessionRequested, (state) => resetState(state)); + builder.addCase(canvasSessionStarted, (state) => resetState(state)); }, }); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx index 5e26c69311..c856099900 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx @@ -11,7 +11,7 @@ import type { AnimationProps } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion'; import { memo, useCallback, useRef, useState } from 'react'; import type { ImageDTO } from 'services/api/types'; -import { $hasProgressImage, $isProgressFromCanvas } from 'services/events/stores'; +import { $hasProgressImage } from 'services/events/stores'; import { NoContentForViewer } from './NoContentForViewer'; import ProgressImage from './ProgressImage'; @@ -87,10 +87,9 @@ export default memo(CurrentImagePreview); const ImageContent = memo(({ imageDTO }: { imageDTO?: ImageDTO }) => { const hasProgressImage = useStore($hasProgressImage); - const isProgressFromCanvas = useStore($isProgressFromCanvas); const shouldShowProgressInViewer = useAppSelector(selectShouldShowProgressInViewer); - if (hasProgressImage && !isProgressFromCanvas && shouldShowProgressInViewer) { + if (hasProgressImage && shouldShowProgressInViewer) { return ; } diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImage.tsx index d876ada415..f18c104fb2 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ProgressImage.tsx @@ -5,7 +5,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; import { selectSystemSlice } from 'features/system/store/systemSlice'; import { memo, useMemo } from 'react'; -import { $isProgressFromCanvas, $progressImage } from 'services/events/stores'; +import { $progressImage } from 'services/events/stores'; const selectShouldAntialiasProgressImage = createSelector( selectSystemSlice, @@ -14,7 +14,6 @@ const selectShouldAntialiasProgressImage = createSelector( const CurrentImagePreview = () => { const progressImage = useStore($progressImage); - const isProgressFromCanvas = useStore($isProgressFromCanvas); const shouldAntialiasProgressImage = useAppSelector(selectShouldAntialiasProgressImage); const sx = useMemo( @@ -24,7 +23,7 @@ const CurrentImagePreview = () => { [shouldAntialiasProgressImage] ); - if (!progressImage || isProgressFromCanvas) { + if (!progressImage) { return null; } diff --git a/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts b/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts index a16a729077..c9499ad613 100644 --- a/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts +++ b/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts @@ -2,7 +2,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSelector, createSlice } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; import { deepClone } from 'common/util/deepClone'; -import { newSessionRequested } from 'features/controlLayers/store/actions'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import type { ParameterHRFMethod, ParameterStrength } from 'features/parameters/types/parameterSchemas'; interface HRFState { @@ -34,7 +34,7 @@ export const hrfSlice = createSlice({ }, }, extraReducers(builder) { - builder.addMatcher(newSessionRequested, () => { + builder.addCase(canvasSessionStarted, () => { return deepClone(initialHRFState); }); }, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts index 0c2ad4a1ce..c839bfbf18 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addFLUXFill.ts @@ -119,9 +119,8 @@ export const addFLUXFill = async ({ }); g.addEdge(maskCombine, 'image', expandMask, 'mask'); - // Do the paste back if we are sending to gallery (in which case we want to see the full image), or if we are sending - // to canvas but not outputting only masked regions - if (!canvasSettings.sendToCanvas || !canvasSettings.outputOnlyMaskedRegions) { + // Do the paste back if we are not outputting only masked regions + if (!canvasSettings.outputOnlyMaskedRegions) { const imageLayerBlend = g.addNode({ type: 'invokeai_img_blend', id: getPrefixedId('image_layer_blend'), @@ -178,9 +177,8 @@ export const addFLUXFill = async ({ }); g.addEdge(maskCombine, 'image', expandMask, 'mask'); - // Do the paste back if we are sending to gallery (in which case we want to see the full image), or if we are sending - // to canvas but not outputting only masked regions - if (!canvasSettings.sendToCanvas || !canvasSettings.outputOnlyMaskedRegions) { + // Do the paste back if we are not outputting only masked regions + if (!canvasSettings.outputOnlyMaskedRegions) { const imageLayerBlend = g.addNode({ type: 'invokeai_img_blend', id: getPrefixedId('image_layer_blend'), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts index f2fce326d1..82191385a0 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addInpaint.ts @@ -185,9 +185,8 @@ export const addInpaint = async ({ g.addEdge(expandMask, 'image', resizeMaskToOriginalSize, 'image'); // After denoising, resize the image and mask back to original size - // Do the paste back if we are sending to gallery (in which case we want to see the full image), or if we are sending - // to canvas but not outputting only masked regions - if (!canvasSettings.sendToCanvas || !canvasSettings.outputOnlyMaskedRegions) { + // Do the paste back if we are not outputting only masked regions + if (!canvasSettings.outputOnlyMaskedRegions) { const imageLayerBlend = g.addNode({ type: 'invokeai_img_blend', id: getPrefixedId('image_layer_blend'), @@ -259,9 +258,8 @@ export const addInpaint = async ({ }); g.addEdge(createGradientMask, 'expanded_mask_area', expandMask, 'mask'); - // Do the paste back if we are sending to gallery (in which case we want to see the full image), or if we are sending - // to canvas but not outputting only masked regions - if (!canvasSettings.sendToCanvas || !canvasSettings.outputOnlyMaskedRegions) { + // Do the paste back if we are not outputting only masked regions + if (!canvasSettings.outputOnlyMaskedRegions) { const imageLayerBlend = g.addNode({ type: 'invokeai_img_blend', id: getPrefixedId('image_layer_blend'), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts index 2c04288c9f..d02e6c6621 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addOutpaint.ts @@ -207,9 +207,9 @@ export const addOutpaint = async ({ g.addEdge(l2i, 'image', resizeOutputImageToOriginalSize, 'image'); g.addEdge(createGradientMask, 'expanded_mask_area', expandMask, 'mask'); g.addEdge(expandMask, 'image', resizeOutputMaskToOriginalSize, 'image'); - // Do the paste back if we are sending to gallery (in which case we want to see the full image), or if we are sending - // to canvas but not outputting only masked regions - if (!canvasSettings.sendToCanvas || !canvasSettings.outputOnlyMaskedRegions) { + + // Do the paste back if we are not outputting only masked regions + if (!canvasSettings.outputOnlyMaskedRegions) { const imageLayerBlend = g.addNode({ type: 'invokeai_img_blend', id: getPrefixedId('image_layer_blend'), @@ -295,9 +295,8 @@ export const addOutpaint = async ({ }); g.addEdge(createGradientMask, 'expanded_mask_area', expandMask, 'mask'); - // Do the paste back if we are sending to gallery (in which case we want to see the full image), or if we are sending - // to canvas but not outputting only masked regions - if (!canvasSettings.sendToCanvas || !canvasSettings.outputOnlyMaskedRegions) { + // Do the paste back if we are not outputting only masked regions + if (!canvasSettings.outputOnlyMaskedRegions) { const imageLayerBlend = g.addNode({ type: 'invokeai_img_blend', id: getPrefixedId('image_layer_blend'), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts index dbad701bc9..7c6b0fa76d 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildChatGPT4oGraph.ts @@ -2,7 +2,6 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { isChatGPT4oAspectRatioID, isChatGPT4oReferenceImageConfig } from 'features/controlLayers/store/types'; @@ -10,11 +9,7 @@ import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/va import { type ImageField, zModelIdentifierField } from 'features/nodes/types/common'; import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { - CANVAS_OUTPUT_PREFIX, - getBoardField, - selectPresetModifiedPrompts, -} from 'features/nodes/util/graph/graphBuilderUtils'; +import { CANVAS_OUTPUT_PREFIX, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; import { t } from 'i18next'; import type { Equals } from 'tsafe'; @@ -37,7 +32,6 @@ export const buildChatGPT4oGraph = async ( const model = selectMainModelConfig(state); const canvas = selectCanvasSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const { bbox } = canvas; const { positivePrompt } = selectPresetModifiedPrompts(state); @@ -65,9 +59,6 @@ export const buildChatGPT4oGraph = async ( } } - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - if (generationMode === 'txt2img') { const g = new Graph(getPrefixedId('chatgpt_4o_txt2img_graph')); const gptImage = g.addNode({ diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildCogView4Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildCogView4Graph.ts index fb63235f9c..7b94605d9f 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildCogView4Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildCogView4Graph.ts @@ -2,7 +2,6 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; @@ -16,7 +15,6 @@ import { getGenerationMode } from 'features/nodes/util/graph/generation/getGener import { Graph } from 'features/nodes/util/graph/generation/Graph'; import { CANVAS_OUTPUT_PREFIX, - getBoardField, getSizes, selectPresetModifiedPrompts, } from 'features/nodes/util/graph/graphBuilderUtils'; @@ -36,7 +34,6 @@ export const buildCogView4Graph = async ( log.debug({ generationMode }, 'Building CogView4 graph'); const params = selectParamsSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const canvas = selectCanvasSlice(state); const { bbox } = canvas; @@ -176,13 +173,7 @@ export const buildCogView4Graph = async ( canvasOutput = addWatermarker(g, canvasOutput); } - // This image will be staged, should not be saved to the gallery or added to a board. - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - - if (!canvasSettings.sendToCanvas) { - g.upsertMetadata(selectCanvasMetadata(state)); - } + g.upsertMetadata(selectCanvasMetadata(state)); g.updateNode(canvasOutput, { id: getPrefixedId(CANVAS_OUTPUT_PREFIX), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts index 136769b492..e024f852ed 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildFLUXGraph.ts @@ -2,7 +2,6 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { addFLUXFill } from 'features/nodes/util/graph/generation/addFLUXFill'; @@ -19,7 +18,6 @@ import { getGenerationMode } from 'features/nodes/util/graph/generation/getGener import { Graph } from 'features/nodes/util/graph/generation/Graph'; import { CANVAS_OUTPUT_PREFIX, - getBoardField, getSizes, selectPresetModifiedPrompts, } from 'features/nodes/util/graph/graphBuilderUtils'; @@ -43,7 +41,6 @@ export const buildFLUXGraph = async (state: RootState, manager?: CanvasManager | log.debug({ generationMode }, 'Building FLUX graph'); const params = selectParamsSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const canvas = selectCanvasSlice(state); const { bbox } = canvas; @@ -335,13 +332,7 @@ export const buildFLUXGraph = async (state: RootState, manager?: CanvasManager | canvasOutput = addWatermarker(g, canvasOutput); } - // This image will be staged, should not be saved to the gallery or added to a board. - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - - if (!canvasSettings.sendToCanvas) { - g.upsertMetadata(selectCanvasMetadata(state)); - } + g.upsertMetadata(selectCanvasMetadata(state)); g.updateNode(canvasOutput, { id: getPrefixedId(CANVAS_OUTPUT_PREFIX), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts index 6f5497a64a..ce1ecac591 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen3Graph.ts @@ -2,18 +2,13 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; +import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { isImagenAspectRatioID } from 'features/controlLayers/store/types'; import { zModelIdentifierField } from 'features/nodes/types/common'; import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { - CANVAS_OUTPUT_PREFIX, - getBoardField, - selectPresetModifiedPrompts, -} from 'features/nodes/util/graph/graphBuilderUtils'; +import { CANVAS_OUTPUT_PREFIX, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; import { t } from 'i18next'; import type { Equals } from 'tsafe'; @@ -34,7 +29,6 @@ export const buildImagen3Graph = async ( log.debug({ generationMode }, 'Building Imagen3 graph'); const canvas = selectCanvasSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const { bbox } = canvas; const { positivePrompt, negativePrompt } = selectPresetModifiedPrompts(state); @@ -45,9 +39,6 @@ export const buildImagen3Graph = async ( assert(isImagenAspectRatioID(bbox.aspectRatio.id), 'Imagen3 does not support this aspect ratio'); assert(positivePrompt.length > 0, 'Imagen3 requires positive prompt to have at least one character'); - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - if (generationMode === 'txt2img') { const g = new Graph(getPrefixedId('imagen3_txt2img_graph')); const imagen3 = g.addNode({ @@ -70,6 +61,7 @@ export const buildImagen3Graph = async ( width: bbox.rect.width, height: bbox.rect.height, model: Graph.getModelMetadataField(model), + ...selectCanvasMetadata(state), }); return { g, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts index b5e866312d..ee499da7f8 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildImagen4Graph.ts @@ -2,18 +2,13 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice'; -import { selectCanvasSlice } from 'features/controlLayers/store/selectors'; +import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { isImagenAspectRatioID } from 'features/controlLayers/store/types'; import { zModelIdentifierField } from 'features/nodes/types/common'; import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode'; import { Graph } from 'features/nodes/util/graph/generation/Graph'; -import { - CANVAS_OUTPUT_PREFIX, - getBoardField, - selectPresetModifiedPrompts, -} from 'features/nodes/util/graph/graphBuilderUtils'; +import { CANVAS_OUTPUT_PREFIX, selectPresetModifiedPrompts } from 'features/nodes/util/graph/graphBuilderUtils'; import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types'; import { t } from 'i18next'; import type { Equals } from 'tsafe'; @@ -34,7 +29,6 @@ export const buildImagen4Graph = async ( log.debug({ generationMode }, 'Building Imagen4 graph'); const canvas = selectCanvasSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const { bbox } = canvas; const { positivePrompt, negativePrompt } = selectPresetModifiedPrompts(state); @@ -45,9 +39,6 @@ export const buildImagen4Graph = async ( assert(isImagenAspectRatioID(bbox.aspectRatio.id), 'Imagen4 does not support this aspect ratio'); assert(positivePrompt.length > 0, 'Imagen4 requires positive prompt to have at least one character'); - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - if (generationMode === 'txt2img') { const g = new Graph(getPrefixedId('imagen4_txt2img_graph')); const imagen4 = g.addNode({ @@ -70,7 +61,9 @@ export const buildImagen4Graph = async ( width: bbox.rect.width, height: bbox.rect.height, model: Graph.getModelMetadataField(model), + ...selectCanvasMetadata(state), }); + return { g, seedFieldIdentifier: { nodeId: imagen4.id, fieldName: 'seed' }, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts index 7f869f69f8..fa1a85778c 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD1Graph.ts @@ -2,7 +2,6 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { addControlNets, addT2IAdapters } from 'features/nodes/util/graph/generation/addControlAdapters'; @@ -20,7 +19,6 @@ import { getGenerationMode } from 'features/nodes/util/graph/generation/getGener import { Graph } from 'features/nodes/util/graph/generation/Graph'; import { CANVAS_OUTPUT_PREFIX, - getBoardField, getSizes, selectPresetModifiedPrompts, } from 'features/nodes/util/graph/graphBuilderUtils'; @@ -38,7 +36,6 @@ export const buildSD1Graph = async (state: RootState, manager?: CanvasManager | log.debug({ generationMode }, 'Building SD1/SD2 graph'); const params = selectParamsSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const canvas = selectCanvasSlice(state); const { bbox } = canvas; @@ -307,13 +304,7 @@ export const buildSD1Graph = async (state: RootState, manager?: CanvasManager | canvasOutput = addWatermarker(g, canvasOutput); } - // This image will be staged, should not be saved to the gallery or added to a board. - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - - if (!canvasSettings.sendToCanvas) { - g.upsertMetadata(selectCanvasMetadata(state)); - } + g.upsertMetadata(selectCanvasMetadata(state)); g.updateNode(canvasOutput, { id: getPrefixedId(CANVAS_OUTPUT_PREFIX), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD3Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD3Graph.ts index ab1acf7806..f6ba0fae60 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD3Graph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSD3Graph.ts @@ -2,8 +2,7 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; -import { selectMainModelConfig,selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; +import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage'; import { addInpaint } from 'features/nodes/util/graph/generation/addInpaint'; @@ -15,7 +14,6 @@ import { getGenerationMode } from 'features/nodes/util/graph/generation/getGener import { Graph } from 'features/nodes/util/graph/generation/Graph'; import { CANVAS_OUTPUT_PREFIX, - getBoardField, getSizes, selectPresetModifiedPrompts, } from 'features/nodes/util/graph/graphBuilderUtils'; @@ -35,7 +33,6 @@ export const buildSD3Graph = async (state: RootState, manager?: CanvasManager | assert(model.base === 'sd-3'); const params = selectParamsSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const canvas = selectCanvasSlice(state); const { bbox } = canvas; @@ -197,13 +194,7 @@ export const buildSD3Graph = async (state: RootState, manager?: CanvasManager | canvasOutput = addWatermarker(g, canvasOutput); } - // This image will be staged, should not be saved to the gallery or added to a board. - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - - if (!canvasSettings.sendToCanvas) { - g.upsertMetadata(selectCanvasMetadata(state)); - } + g.upsertMetadata(selectCanvasMetadata(state)); g.updateNode(canvasOutput, { id: getPrefixedId(CANVAS_OUTPUT_PREFIX), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts index de19fe9151..9aaf3960c7 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildSDXLGraph.ts @@ -2,7 +2,6 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager'; import { getPrefixedId } from 'features/controlLayers/konva/util'; -import { selectCanvasSettingsSlice } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice'; import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors'; import { addControlNets, addT2IAdapters } from 'features/nodes/util/graph/generation/addControlAdapters'; @@ -20,7 +19,6 @@ import { getGenerationMode } from 'features/nodes/util/graph/generation/getGener import { Graph } from 'features/nodes/util/graph/generation/Graph'; import { CANVAS_OUTPUT_PREFIX, - getBoardField, getSizes, selectPresetModifiedPrompts, } from 'features/nodes/util/graph/graphBuilderUtils'; @@ -42,7 +40,6 @@ export const buildSDXLGraph = async (state: RootState, manager?: CanvasManager | assert(model.base === 'sdxl'); const params = selectParamsSlice(state); - const canvasSettings = selectCanvasSettingsSlice(state); const canvas = selectCanvasSlice(state); const { bbox } = canvas; @@ -313,13 +310,7 @@ export const buildSDXLGraph = async (state: RootState, manager?: CanvasManager | canvasOutput = addWatermarker(g, canvasOutput); } - // This image will be staged, should not be saved to the gallery or added to a board. - const is_intermediate = canvasSettings.sendToCanvas; - const board = canvasSettings.sendToCanvas ? undefined : getBoardField(state); - - if (!canvasSettings.sendToCanvas) { - g.upsertMetadata(selectCanvasMetadata(state)); - } + g.upsertMetadata(selectCanvasMetadata(state)); g.updateNode(canvasOutput, { id: getPrefixedId(CANVAS_OUTPUT_PREFIX), diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx b/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx index bec8f2212a..68992a84f4 100644 --- a/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx +++ b/invokeai/frontend/web/src/features/queue/components/InvokeButtonTooltip/InvokeButtonTooltip.tsx @@ -2,7 +2,6 @@ import type { TooltipProps } from '@invoke-ai/ui-library'; import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-ai/ui-library'; import { useStore } from '@nanostores/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { selectSendToCanvas } from 'features/controlLayers/store/canvasSettingsSlice'; import { selectIterations } from 'features/controlLayers/store/paramsSlice'; import { selectDynamicPromptsIsLoading } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; import { selectAutoAddBoardId } from 'features/gallery/store/gallerySelectors'; @@ -237,32 +236,14 @@ StyledDivider.displayName = 'StyledDivider'; const AddingToText = memo(() => { const { t } = useTranslation(); - const sendToCanvas = useAppSelector(selectSendToCanvas); const autoAddBoardId = useAppSelector(selectAutoAddBoardId); const autoAddBoardName = useBoardName(autoAddBoardId); - const addingTo = useMemo(() => { - if (sendToCanvas) { - return t('controlLayers.stagingOnCanvas'); - } - return t('parameters.invoke.addingImagesTo'); - }, [sendToCanvas, t]); - - const destination = useMemo(() => { - if (sendToCanvas) { - return t('queue.canvas'); - } - if (autoAddBoardName) { - return autoAddBoardName; - } - return t('boards.uncategorized'); - }, [autoAddBoardName, sendToCanvas, t]); - return ( - {addingTo}{' '} + {t('parameters.invoke.addingImagesTo')}{' '} - {destination} + {autoAddBoardName || t('boards.uncategorized')} ); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx index fc0172a117..d3f05cb30b 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueControls.tsx @@ -1,28 +1,17 @@ import { Flex, Spacer } from '@invoke-ai/ui-library'; -import { useAppSelector } from 'app/store/storeHooks'; -import { CanvasManagerProviderGate } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; import { ClearQueueIconButton } from 'features/queue/components/ClearQueueIconButton'; import { QueueActionsMenuButton } from 'features/queue/components/QueueActionsMenuButton'; -import { SendToToggle } from 'features/queue/components/SendToToggle'; import ProgressBar from 'features/system/components/ProgressBar'; -import { selectActiveTab } from 'features/ui/store/uiSelectors'; import { memo } from 'react'; import { InvokeButton } from './InvokeQueueBackButton'; const QueueControls = () => { - const tab = useAppSelector(selectActiveTab); - return ( - {tab === 'canvas' && ( - - - - )} diff --git a/invokeai/frontend/web/src/features/queue/components/SendToToggle.tsx b/invokeai/frontend/web/src/features/queue/components/SendToToggle.tsx deleted file mode 100644 index f4de748c91..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/SendToToggle.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import type { SystemStyleObject } from '@invoke-ai/ui-library'; -import { - Box, - Button, - chakra, - Flex, - Icon, - Popover, - PopoverArrow, - PopoverBody, - PopoverContent, - PopoverTrigger, - Portal, - Text, - useCheckbox, - useToken, -} from '@invoke-ai/ui-library'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { selectSendToCanvas, settingsSendToCanvasChanged } from 'features/controlLayers/store/canvasSettingsSlice'; -import { selectIsStaging } from 'features/controlLayers/store/canvasStagingAreaSlice'; -import { $imageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; -import { activeTabCanvasRightPanelChanged, setActiveTab } from 'features/ui/store/uiSlice'; -import type { ChangeEvent, PropsWithChildren } from 'react'; -import { memo, useCallback, useMemo } from 'react'; -import { Trans, useTranslation } from 'react-i18next'; -import { PiImageBold, PiPaintBrushBold } from 'react-icons/pi'; - -const getSx = (padding: string | number): SystemStyleObject => ({ - bg: 'base.700', - w: '72px', - cursor: 'pointer', - '&[data-checked]': { - '.thumb': { - left: `calc(100% - ${padding})`, - transform: 'translateX(-100%)', - bg: 'invokeGreen.300', - }, - '.unchecked-icon': { - color: 'base.50', - opacity: 0.4, - }, - '.checked-icon': { - color: 'base.900', - opacity: 1, - }, - }, - '&[data-disabled]': { - bg: 'base.700', - '.thumb': { - bg: 'base.500', - }, - '.unchecked-icon': { - color: 'base.800', - }, - '.checked-icon': { - color: 'base.800', - }, - }, - '.thumb': { - transition: 'left 0.1s ease-in-out, transform 0.1s ease-in-out', - left: padding, - transform: 'translateX(0)', - bg: 'invokeBlue.400', - shadow: 'md', - }, - '.unchecked-icon': { - color: 'base.900', - opacity: 1, - }, - '.checked-icon': { - color: 'base.50', - opacity: 0.4, - }, -}); - -export const SendToToggle = memo(() => { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const sendToCanvas = useAppSelector(selectSendToCanvas); - const isStaging = useAppSelector(selectIsStaging); - - const gap = useToken('space', 1); - const sx = useMemo(() => getSx(gap), [gap]); - - const onChange = useCallback( - (e: ChangeEvent) => { - dispatch(settingsSendToCanvasChanged(e.target.checked)); - }, - [dispatch] - ); - - const { getCheckboxProps, getInputProps, htmlProps } = useCheckbox({ - onChange, - isChecked: sendToCanvas, - isDisabled: isStaging, - }); - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - ); -}); - -SendToToggle.displayName = 'SendToToggle'; - -const TooltipContent = memo(() => { - const { t } = useTranslation(); - const sendToCanvas = useAppSelector(selectSendToCanvas); - const isStaging = useAppSelector(selectIsStaging); - - if (isStaging) { - return ( - - {t('controlLayers.sendingToCanvas')} - - }} /> - - - ); - } - - return ( - - - {sendToCanvas ? t('controlLayers.sendToCanvas') : t('controlLayers.sendToGallery')} - - - {sendToCanvas ? t('controlLayers.sendToCanvasDesc') : t('controlLayers.sendToGalleryDesc')} - - - ); -}); - -TooltipContent.displayName = 'TooltipContent'; - -const ActivateCanvasButton = memo((props: PropsWithChildren) => { - const dispatch = useAppDispatch(); - const onClick = useCallback(() => { - dispatch(setActiveTab('canvas')); - dispatch(activeTabCanvasRightPanelChanged('layers')); - $imageViewer.set(false); - }, [dispatch]); - return ( - - ); -}); - -ActivateCanvasButton.displayName = 'ActivateCanvasButton'; diff --git a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts index a0e6eb4002..efee56c9fe 100644 --- a/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts +++ b/invokeai/frontend/web/src/features/stylePresets/store/stylePresetSlice.ts @@ -2,7 +2,7 @@ import type { PayloadAction, Selector } from '@reduxjs/toolkit'; import { createSelector, createSlice } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; import { deepClone } from 'common/util/deepClone'; -import { newSessionRequested } from 'features/controlLayers/store/actions'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { atom } from 'nanostores'; import { stylePresetsApi } from 'services/api/endpoints/stylePresets'; @@ -29,6 +29,9 @@ export const stylePresetSlice = createSlice({ }, }, extraReducers(builder) { + builder.addCase(canvasSessionStarted, () => { + return deepClone(initialState); + }); builder.addMatcher(stylePresetsApi.endpoints.deleteStylePreset.matchFulfilled, (state, action) => { if (state.activeStylePresetId === null) { return; @@ -47,9 +50,6 @@ export const stylePresetSlice = createSlice({ state.activeStylePresetId = null; } }); - builder.addMatcher(newSessionRequested, () => { - return deepClone(initialState); - }); }, }); diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index eabcf5ae22..3c4bf462bc 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -1,7 +1,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSelector, createSlice } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; -import { newSessionRequested } from 'features/controlLayers/store/actions'; +import { canvasSessionStarted } from 'features/controlLayers/store/canvasStagingAreaSlice'; import type { Dimensions } from 'features/controlLayers/store/types'; import { workflowLoaded } from 'features/nodes/store/nodesSlice'; import { atom } from 'nanostores'; @@ -56,7 +56,7 @@ export const uiSlice = createSlice({ builder.addCase(workflowLoaded, (state) => { state.activeTab = 'workflows'; }); - builder.addMatcher(newSessionRequested, (state) => { + builder.addCase(canvasSessionStarted, (state) => { state.activeTab = 'canvas'; }); }, diff --git a/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx b/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx index 2748dd1e6d..92ad0aa011 100644 --- a/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx +++ b/invokeai/frontend/web/src/services/events/onInvocationComplete.tsx @@ -1,10 +1,12 @@ import { logger } from 'app/logging/logger'; import type { AppDispatch, RootState } from 'app/store/store'; import { deepClone } from 'common/util/deepClone'; +import { stagingAreaImageStaged } from 'features/controlLayers/store/canvasStagingAreaSlice'; import { boardIdSelected, galleryViewChanged, imageSelected, offsetChanged } from 'features/gallery/store/gallerySlice'; import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useNodeExecutionState'; import { isImageField, isImageFieldCollection } from 'features/nodes/types/common'; import { zNodeStatus } from 'features/nodes/types/invocation'; +import { isCanvasOutputEvent } from 'features/nodes/util/graph/graphBuilderUtils'; import type { ApiTagDescription } from 'services/api'; import { boardsApi } from 'services/api/endpoints/boards'; import { getImageDTOSafe, imagesApi } from 'services/api/endpoints/images'; @@ -163,26 +165,18 @@ export const buildOnInvocationComplete = (getState: () => RootState, dispatch: A }; const handleOriginCanvas = async (data: S['InvocationCompleteEvent']) => { - const imageDTOs = await getResultImageDTOs(data); + if (!isCanvasOutputEvent(data)) { + return; + } // We expect only a single image in the canvas output - const imageDTO = imageDTOs[0]; + const imageDTO = (await getResultImageDTOs(data))[0]; + if (!imageDTO) { return; } - if (data.destination === 'canvas') { - // TODO(psyche): Can/should we let canvas handle this itself? - // if (isCanvasOutputEvent(data)) { - // if (data.result.type === 'image_output') { - // dispatch(stagingAreaImageStaged({ stagingAreaImage: { imageDTO, offsetX: 0, offsetY: 0 } })); - // } - // addImagesToGallery(data, [imageDTO]); - // } - } else if (!imageDTO.is_intermediate) { - // Desintaion is gallery - addImagesToGallery(data, [imageDTO]); - } + dispatch(stagingAreaImageStaged({ stagingAreaImage: { imageDTO, offsetX: 0, offsetY: 0 } })); }; const handleOriginOther = async (data: S['InvocationCompleteEvent']) => { diff --git a/invokeai/frontend/web/src/services/events/stores.ts b/invokeai/frontend/web/src/services/events/stores.ts index f46f3af079..d25a887c7d 100644 --- a/invokeai/frontend/web/src/services/events/stores.ts +++ b/invokeai/frontend/web/src/services/events/stores.ts @@ -8,21 +8,28 @@ export const $socket = atom(null); export const $socketOptions = map>({}); export const $isConnected = atom(false); export const $lastProgressEvent = atom(null); -export const $progressImage = computed($lastProgressEvent, (val) => val?.image ?? null); -export const $canvasProgressImage = computed($lastProgressEvent, (event) => { +$lastProgressEvent.subscribe((event) => { if (!event) { - return null; + return; } - if (event.origin !== 'canvas') { - return null; + switch (event.destination) { + case 'workflows': + $lastWorkflowsProgressEvent.set(event); + break; + case 'upscaling': + $lastUpscalingProgressEvent.set(event); + break; + case 'canvas': + $lastCanvasProgressEvent.set(event); + break; } - if (!event.image) { - return null; - } - return event.image; }); +export const $lastCanvasProgressEvent = atom(null); +export const $lastWorkflowsProgressEvent = atom(null); +export const $lastUpscalingProgressEvent = atom(null); + +export const $progressImage = computed($lastProgressEvent, (val) => val?.image ?? null); export const $hasProgressImage = computed($lastProgressEvent, (val) => Boolean(val?.image)); -export const $isProgressFromCanvas = computed($lastProgressEvent, (val) => val?.destination === 'canvas'); export const $invocationProgressMessage = computed($lastProgressEvent, (val) => { if (!val) { return null;