diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts
index afd9a2fff2..8ed57f33ad 100644
--- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts
+++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts
@@ -35,7 +35,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
const { prepend } = action.payload;
const manager = $canvasManager.get();
- assert(manager, 'No canvas manager');
+ // assert(manager, 'No canvas manager');
const model = state.params.model;
assert(model, 'No model found in state');
@@ -90,7 +90,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
const { g, seedFieldIdentifier, positivePromptFieldIdentifier } = buildGraphResult.value;
- const destination = state.canvasSettings.sendToCanvas ? 'canvas' : 'gallery';
+ // const destination = state.canvasSettings.sendToCanvas ? 'canvas' : 'gallery';
const prepareBatchResult = withResult(() =>
prepareLinearUIBatch({
@@ -100,7 +100,7 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
seedFieldIdentifier,
positivePromptFieldIdentifier,
origin: 'canvas',
- destination,
+ destination: 'canvas',
})
);
diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
index 18c9f54489..2861a1db54 100644
--- a/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
+++ b/invokeai/frontend/web/src/features/controlLayers/components/CanvasMainPanelContent.tsx
@@ -1,13 +1,6 @@
-import {
- ContextMenu,
- Flex,
- IconButton,
- Menu,
- MenuButton,
- MenuList,
- type SystemStyleObject,
-} from '@invoke-ai/ui-library';
-import { useAppSelector } from 'app/store/storeHooks';
+import type { SystemStyleObject } from '@invoke-ai/ui-library';
+import { Button, ContextMenu, Flex, IconButton, Image, Menu, MenuButton, MenuList, Text } from '@invoke-ai/ui-library';
+import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { FocusRegionWrapper } from 'common/components/FocusRegionWrapper';
import { CanvasAlertsPreserveMask } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsPreserveMask';
import { CanvasAlertsSelectedEntityStatus } from 'features/controlLayers/components/CanvasAlerts/CanvasAlertsSelectedEntityStatus';
@@ -25,7 +18,13 @@ 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 { newCanvasSessionRequested } from 'features/controlLayers/store/actions';
import { selectDynamicGrid, selectShowHUD } from 'features/controlLayers/store/canvasSettingsSlice';
+import {
+ selectIsStaging,
+ selectSelectedImage,
+ selectStagedImages,
+} from 'features/controlLayers/store/canvasStagingAreaSlice';
import { selectIsCanvasEmpty, selectIsSessionStarted } from 'features/controlLayers/store/selectors';
import { memo, useCallback } from 'react';
import { PiDotsThreeOutlineVerticalFill } from 'react-icons/pi';
@@ -54,7 +53,11 @@ export const CanvasMainPanelContent = memo(() => {
const isCanvasEmpty = useAppSelector(selectIsCanvasEmpty);
if (!isSessionStarted && isCanvasEmpty) {
- return ;
+ return ;
+ }
+
+ if (isSessionStarted && isCanvasEmpty) {
+ return ;
}
return ;
@@ -62,14 +65,64 @@ export const CanvasMainPanelContent = memo(() => {
CanvasMainPanelContent.displayName = 'CanvasMainPanelContent';
-const CanvasNoSession = memo(() => {
+const NoActiveSession = memo(() => {
+ const dispatch = useAppDispatch();
+ const newSesh = useCallback(() => {
+ dispatch(newCanvasSessionRequested());
+ }, [dispatch]);
return (
-
- FRESH CANVAS is fresh when: - No control layers - No inpaint masks - No regions - No Raster Layers
+
+
+ No Active Session
+
+
+
+ Generate with Starting Image
+ - New Canvas Session
+ - Dropped image as raster layer
+ - Bbox resized
+
+
+ Generate with Control Image
+ - New Canvas Session
+ - Dropped image as control layer
+ - Bbox resized
+
+
+ Edit Image
+ - New Canvas Session
+ - Dropped image as raster layer
+ - Bbox resized
+ - 1 Inpaint mask layer added
+
);
});
-CanvasNoSession.displayName = 'CanvasNoSession';
+NoActiveSession.displayName = 'NoActiveSession';
+
+const SimpleActiveSession = memo(() => {
+ const isStaging = useAppSelector(selectIsStaging);
+ const selectedImage = useAppSelector(selectSelectedImage);
+ const stagedImages = useAppSelector(selectStagedImages);
+ return (
+
+
+ Simple Session (staging view) {isStaging && 'STAGING'}
+
+ {selectedImage && }
+
+ {stagedImages.map(({ imageDTO }) => (
+
+ ))}
+
+
+ );
+});
+SimpleActiveSession.displayName = 'SimpleActiveSession';
const CanvasActiveSession = memo(() => {
const dynamicGrid = useAppSelector(selectDynamicGrid);
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
index 1f49abfa87..d02aaed004 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasSlice.ts
@@ -5,7 +5,11 @@ 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, newSessionRequested } from 'features/controlLayers/store/actions';
+import {
+ canvasReset,
+ newCanvasSessionRequested,
+ newGallerySessionRequested,
+} from 'features/controlLayers/store/actions';
import { modelChanged } from 'features/controlLayers/store/paramsSlice';
import {
selectAllEntities,
@@ -1802,9 +1806,14 @@ export const canvasSlice = createSlice({
syncScaledSize(state);
}
});
- builder.addMatcher(newSessionRequested, (state) => {
+ builder.addCase(newGallerySessionRequested, (state) => {
return resetState(state);
});
+ builder.addCase(newCanvasSessionRequested, (state) => {
+ const newState = resetState(state);
+ newState.isSessionStarted = true;
+ return newState;
+ });
},
});
diff --git a/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts
index bf37b6e0d1..660ac1a680 100644
--- a/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts
+++ b/invokeai/frontend/web/src/features/controlLayers/store/canvasStagingAreaSlice.ts
@@ -97,6 +97,10 @@ export const selectSelectedImage = createSelector(
[selectCanvasStagingAreaSlice, selectStagedImageIndex],
(stagingArea, index) => stagingArea.stagedImages[index] ?? null
);
+export const selectStagedImages = createSelector(
+ selectCanvasStagingAreaSlice,
+ (stagingArea) => stagingArea.stagedImages
+);
export const selectImageCount = createSelector(
selectCanvasStagingAreaSlice,
(stagingArea) => stagingArea.stagedImages.length
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 95f1a20cf3..c53da0e1ec 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
@@ -3,10 +3,12 @@ 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';
import { getGlobalReferenceImageWarnings } from 'features/controlLayers/store/validators';
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,
@@ -15,14 +17,16 @@ import {
} from 'features/nodes/util/graph/graphBuilderUtils';
import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
import { t } from 'i18next';
-import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
const log = logger('system');
-export const buildChatGPT4oGraph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildChatGPT4oGraph = async (
+ state: RootState,
+ manager?: CanvasManager | null
+): Promise => {
+ const generationMode = await getGenerationMode(manager);
if (generationMode !== 'txt2img' && generationMode !== 'img2img') {
throw new UnsupportedGenerationModeError(t('toast.chatGPT4oIncompatibleGenerationMode'));
@@ -91,6 +95,7 @@ export const buildChatGPT4oGraph = async (state: RootState, manager: CanvasManag
}
if (generationMode === 'img2img') {
+ assert(manager, 'Need manager to do img2img');
const adapters = manager.compositor.getVisibleAdaptersOfType('raster_layer');
const { image_name } = await manager.compositor.getCompositeImageDTO(adapters, bbox.rect, {
is_intermediate: true,
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 4f19c6bd2e..b0b29774e9 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
@@ -12,6 +12,7 @@ import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChec
import { addOutpaint } from 'features/nodes/util/graph/generation/addOutpaint';
import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage';
import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker';
+import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
CANVAS_OUTPUT_PREFIX,
@@ -27,8 +28,11 @@ import { assert } from 'tsafe';
const log = logger('system');
-export const buildCogView4Graph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildCogView4Graph = async (
+ state: RootState,
+ manager?: CanvasManager | null
+): Promise => {
+ const generationMode = await getGenerationMode(manager);
log.debug({ generationMode }, 'Building CogView4 graph');
const params = selectParamsSlice(state);
@@ -109,6 +113,7 @@ export const buildCogView4Graph = async (state: RootState, manager: CanvasManage
canvasOutput = addTextToImage({ g, l2i, originalSize, scaledSize });
g.upsertMetadata({ generation_mode: 'cogview4_txt2img' });
} else if (generationMode === 'img2img') {
+ assert(manager, 'Need manager to do img2img');
canvasOutput = await addImageToImage({
g,
manager,
@@ -124,6 +129,7 @@ export const buildCogView4Graph = async (state: RootState, manager: CanvasManage
});
g.upsertMetadata({ generation_mode: 'cogview4_img2img' });
} else if (generationMode === 'inpaint') {
+ assert(manager, 'Need manager to do inpaint');
canvasOutput = await addInpaint({
state,
g,
@@ -141,6 +147,7 @@ export const buildCogView4Graph = async (state: RootState, manager: CanvasManage
});
g.upsertMetadata({ generation_mode: 'cogview4_inpaint' });
} else if (generationMode === 'outpaint') {
+ assert(manager, 'Need manager to do outpaint');
canvasOutput = await addOutpaint({
state,
g,
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 bff3803fb3..36abfb6a4c 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
@@ -3,7 +3,7 @@ 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 { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectCanvasMetadata, selectCanvasSlice } from 'features/controlLayers/store/selectors';
import { addFLUXFill } from 'features/nodes/util/graph/generation/addFLUXFill';
import { addFLUXLoRAs } from 'features/nodes/util/graph/generation/addFLUXLoRAs';
@@ -15,6 +15,7 @@ import { addOutpaint } from 'features/nodes/util/graph/generation/addOutpaint';
import { addRegions } from 'features/nodes/util/graph/generation/addRegions';
import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage';
import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker';
+import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
CANVAS_OUTPUT_PREFIX,
@@ -28,7 +29,6 @@ import {
UnsupportedGenerationModeError,
} from 'features/nodes/util/graph/types';
import { t } from 'i18next';
-import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
import type { Invocation } from 'services/api/types';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
@@ -38,8 +38,8 @@ import { addIPAdapters } from './addIPAdapters';
const log = logger('system');
-export const buildFLUXGraph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildFLUXGraph = async (state: RootState, manager?: CanvasManager | null): Promise => {
+ const generationMode = await getGenerationMode(manager);
log.debug({ generationMode }, 'Building FLUX graph');
const params = selectParamsSlice(state);
@@ -171,6 +171,7 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
let canvasOutput: Invocation = l2i;
if (isFLUXFill) {
+ assert(manager, 'Need manager to do FLUX Fill');
canvasOutput = await addFLUXFill({
state,
g,
@@ -184,6 +185,7 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
canvasOutput = addTextToImage({ g, l2i, originalSize, scaledSize });
g.upsertMetadata({ generation_mode: 'flux_txt2img' });
} else if (generationMode === 'img2img') {
+ assert(manager, 'Need manager to do img2img');
canvasOutput = await addImageToImage({
g,
manager,
@@ -199,6 +201,7 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
});
g.upsertMetadata({ generation_mode: 'flux_img2img' });
} else if (generationMode === 'inpaint') {
+ assert(manager, 'Need manager to do inpaint');
canvasOutput = await addInpaint({
state,
g,
@@ -216,6 +219,7 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
});
g.upsertMetadata({ generation_mode: 'flux_inpaint' });
} else if (generationMode === 'outpaint') {
+ assert(manager, 'Need manager to do outpaint');
canvasOutput = await addOutpaint({
state,
g,
@@ -236,32 +240,34 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
assert>(false);
}
- const controlNetCollector = g.addNode({
- type: 'collect',
- id: getPrefixedId('control_net_collector'),
- });
- const controlNetResult = await addControlNets({
- manager,
- entities: canvas.controlLayers.entities,
- g,
- rect: canvas.bbox.rect,
- collector: controlNetCollector,
- model,
- });
- if (controlNetResult.addedControlNets > 0) {
- g.addEdge(controlNetCollector, 'collection', denoise, 'control');
- } else {
- g.deleteNode(controlNetCollector.id);
- }
+ if (manager) {
+ const controlNetCollector = g.addNode({
+ type: 'collect',
+ id: getPrefixedId('control_net_collector'),
+ });
+ const controlNetResult = await addControlNets({
+ manager,
+ entities: canvas.controlLayers.entities,
+ g,
+ rect: canvas.bbox.rect,
+ collector: controlNetCollector,
+ model,
+ });
+ if (controlNetResult.addedControlNets > 0) {
+ g.addEdge(controlNetCollector, 'collection', denoise, 'control');
+ } else {
+ g.deleteNode(controlNetCollector.id);
+ }
- await addControlLoRA({
- manager,
- entities: canvas.controlLayers.entities,
- g,
- rect: canvas.bbox.rect,
- denoise,
- model,
- });
+ await addControlLoRA({
+ manager,
+ entities: canvas.controlLayers.entities,
+ g,
+ rect: canvas.bbox.rect,
+ denoise,
+ model,
+ });
+ }
const ipAdapterCollect = g.addNode({
type: 'collect',
@@ -274,6 +280,8 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
model,
});
+ let totalIPAdaptersAdded = ipAdapterResult.addedIPAdapters;
+
const fluxReduxCollect = g.addNode({
type: 'collect',
id: getPrefixedId('ip_adapter_collector'),
@@ -284,31 +292,33 @@ export const buildFLUXGraph = async (state: RootState, manager: CanvasManager):
collector: fluxReduxCollect,
model,
});
+ let totalReduxesAdded = fluxReduxResult.addedFLUXReduxes;
- const regionsResult = await addRegions({
- manager,
- regions: canvas.regionalGuidance.entities,
- g,
- bbox: canvas.bbox.rect,
- model,
- posCond,
- negCond: null,
- posCondCollect,
- negCondCollect: null,
- ipAdapterCollect,
- fluxReduxCollect,
- });
+ if (manager) {
+ const regionsResult = await addRegions({
+ manager,
+ regions: canvas.regionalGuidance.entities,
+ g,
+ bbox: canvas.bbox.rect,
+ model,
+ posCond,
+ negCond: null,
+ posCondCollect,
+ negCondCollect: null,
+ ipAdapterCollect,
+ fluxReduxCollect,
+ });
+
+ totalIPAdaptersAdded += regionsResult.reduce((acc, r) => acc + r.addedIPAdapters, 0);
+ totalReduxesAdded += regionsResult.reduce((acc, r) => acc + r.addedFLUXReduxes, 0);
+ }
- const totalIPAdaptersAdded =
- ipAdapterResult.addedIPAdapters + regionsResult.reduce((acc, r) => acc + r.addedIPAdapters, 0);
if (totalIPAdaptersAdded > 0) {
g.addEdge(ipAdapterCollect, 'collection', denoise, 'ip_adapter');
} else {
g.deleteNode(ipAdapterCollect.id);
}
- const totalReduxesAdded =
- fluxReduxResult.addedFLUXReduxes + regionsResult.reduce((acc, r) => acc + r.addedFLUXReduxes, 0);
if (totalReduxesAdded > 0) {
g.addEdge(fluxReduxCollect, 'collection', denoise, 'redux_conditioning');
} else {
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 7be80eb788..6624aa73af 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
@@ -3,9 +3,11 @@ 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 { 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,
@@ -14,14 +16,16 @@ import {
} from 'features/nodes/util/graph/graphBuilderUtils';
import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
import { t } from 'i18next';
-import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
const log = logger('system');
-export const buildImagen3Graph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildImagen3Graph = async (
+ state: RootState,
+ manager?: CanvasManager | null
+): Promise => {
+ const generationMode = await getGenerationMode(manager);
if (generationMode !== 'txt2img') {
throw new UnsupportedGenerationModeError(t('toast.imagenIncompatibleGenerationMode', { model: 'Imagen3' }));
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 71ea35ff01..698552be54 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
@@ -3,9 +3,11 @@ 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 { 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,
@@ -14,14 +16,16 @@ import {
} from 'features/nodes/util/graph/graphBuilderUtils';
import { type GraphBuilderReturn, UnsupportedGenerationModeError } from 'features/nodes/util/graph/types';
import { t } from 'i18next';
-import { selectMainModelConfig } from 'services/api/endpoints/models';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
const log = logger('system');
-export const buildImagen4Graph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildImagen4Graph = async (
+ state: RootState,
+ manager?: CanvasManager | null
+): Promise => {
+ const generationMode = await getGenerationMode(manager);
if (generationMode !== 'txt2img') {
throw new UnsupportedGenerationModeError(t('toast.imagenIncompatibleGenerationMode', { model: 'Imagen4' }));
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 73875d57b8..0abdadfaba 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
@@ -3,7 +3,7 @@ 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 { 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';
import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage';
@@ -16,6 +16,7 @@ import { addOutpaint } from 'features/nodes/util/graph/generation/addOutpaint';
import { addSeamless } from 'features/nodes/util/graph/generation/addSeamless';
import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage';
import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker';
+import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
CANVAS_OUTPUT_PREFIX,
@@ -24,7 +25,6 @@ import {
selectPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types';
-import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
import type { Invocation } from 'services/api/types';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
@@ -33,8 +33,8 @@ import { addRegions } from './addRegions';
const log = logger('system');
-export const buildSD1Graph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildSD1Graph = async (state: RootState, manager?: CanvasManager | null): Promise => {
+ const generationMode = await getGenerationMode(manager);
log.debug({ generationMode }, 'Building SD1/SD2 graph');
const params = selectParamsSlice(state);
@@ -170,6 +170,7 @@ export const buildSD1Graph = async (state: RootState, manager: CanvasManager): P
canvasOutput = addTextToImage({ g, l2i, originalSize, scaledSize });
g.upsertMetadata({ generation_mode: 'txt2img' });
} else if (generationMode === 'img2img') {
+ assert(manager, 'Need manager to do img2img');
canvasOutput = await addImageToImage({
g,
manager,
@@ -185,6 +186,7 @@ export const buildSD1Graph = async (state: RootState, manager: CanvasManager): P
});
g.upsertMetadata({ generation_mode: 'img2img' });
} else if (generationMode === 'inpaint') {
+ assert(manager, 'Need manager to do inpaint');
canvasOutput = await addInpaint({
state,
g,
@@ -202,6 +204,7 @@ export const buildSD1Graph = async (state: RootState, manager: CanvasManager): P
});
g.upsertMetadata({ generation_mode: 'inpaint' });
} else if (generationMode === 'outpaint') {
+ assert(manager, 'Need manager to do outpaint');
canvasOutput = await addOutpaint({
state,
g,
@@ -222,40 +225,42 @@ export const buildSD1Graph = async (state: RootState, manager: CanvasManager): P
assert>(false);
}
- const controlNetCollector = g.addNode({
- type: 'collect',
- id: getPrefixedId('control_net_collector'),
- });
- const controlNetResult = await addControlNets({
- manager,
- entities: canvas.controlLayers.entities,
- g,
- rect: canvas.bbox.rect,
- collector: controlNetCollector,
- model,
- });
- if (controlNetResult.addedControlNets > 0) {
- g.addEdge(controlNetCollector, 'collection', denoise, 'control');
- } else {
- g.deleteNode(controlNetCollector.id);
- }
+ if (manager) {
+ const controlNetCollector = g.addNode({
+ type: 'collect',
+ id: getPrefixedId('control_net_collector'),
+ });
+ const controlNetResult = await addControlNets({
+ manager,
+ entities: canvas.controlLayers.entities,
+ g,
+ rect: canvas.bbox.rect,
+ collector: controlNetCollector,
+ model,
+ });
+ if (controlNetResult.addedControlNets > 0) {
+ g.addEdge(controlNetCollector, 'collection', denoise, 'control');
+ } else {
+ g.deleteNode(controlNetCollector.id);
+ }
- const t2iAdapterCollector = g.addNode({
- type: 'collect',
- id: getPrefixedId('t2i_adapter_collector'),
- });
- const t2iAdapterResult = await addT2IAdapters({
- manager,
- entities: canvas.controlLayers.entities,
- g,
- rect: canvas.bbox.rect,
- collector: t2iAdapterCollector,
- model,
- });
- if (t2iAdapterResult.addedT2IAdapters > 0) {
- g.addEdge(t2iAdapterCollector, 'collection', denoise, 't2i_adapter');
- } else {
- g.deleteNode(t2iAdapterCollector.id);
+ const t2iAdapterCollector = g.addNode({
+ type: 'collect',
+ id: getPrefixedId('t2i_adapter_collector'),
+ });
+ const t2iAdapterResult = await addT2IAdapters({
+ manager,
+ entities: canvas.controlLayers.entities,
+ g,
+ rect: canvas.bbox.rect,
+ collector: t2iAdapterCollector,
+ model,
+ });
+ if (t2iAdapterResult.addedT2IAdapters > 0) {
+ g.addEdge(t2iAdapterCollector, 'collection', denoise, 't2i_adapter');
+ } else {
+ g.deleteNode(t2iAdapterCollector.id);
+ }
}
const ipAdapterCollect = g.addNode({
@@ -268,23 +273,26 @@ export const buildSD1Graph = async (state: RootState, manager: CanvasManager): P
collector: ipAdapterCollect,
model,
});
+ let totalIPAdaptersAdded = ipAdapterResult.addedIPAdapters;
- const regionsResult = await addRegions({
- manager,
- regions: canvas.regionalGuidance.entities,
- g,
- bbox: canvas.bbox.rect,
- model,
- posCond,
- negCond,
- posCondCollect,
- negCondCollect,
- ipAdapterCollect,
- fluxReduxCollect: null,
- });
+ if (manager) {
+ const regionsResult = await addRegions({
+ manager,
+ regions: canvas.regionalGuidance.entities,
+ g,
+ bbox: canvas.bbox.rect,
+ model,
+ posCond,
+ negCond,
+ posCondCollect,
+ negCondCollect,
+ ipAdapterCollect,
+ fluxReduxCollect: null,
+ });
+
+ totalIPAdaptersAdded += regionsResult.reduce((acc, r) => acc + r.addedIPAdapters, 0);
+ }
- const totalIPAdaptersAdded =
- ipAdapterResult.addedIPAdapters + regionsResult.reduce((acc, r) => acc + r.addedIPAdapters, 0);
if (totalIPAdaptersAdded > 0) {
g.addEdge(ipAdapterCollect, 'collection', denoise, 'ip_adapter');
} else {
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 c0c864daaa..7843c6796f 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
@@ -3,7 +3,7 @@ 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 { 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';
@@ -11,6 +11,7 @@ import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChec
import { addOutpaint } from 'features/nodes/util/graph/generation/addOutpaint';
import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage';
import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker';
+import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
CANVAS_OUTPUT_PREFIX,
@@ -19,15 +20,14 @@ import {
selectPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types';
-import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
import type { Invocation } from 'services/api/types';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
const log = logger('system');
-export const buildSD3Graph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildSD3Graph = async (state: RootState, manager?: CanvasManager | null): Promise => {
+ const generationMode = await getGenerationMode(manager);
log.debug({ generationMode }, 'Building SD3 graph');
const model = selectMainModelConfig(state);
@@ -134,6 +134,7 @@ export const buildSD3Graph = async (state: RootState, manager: CanvasManager): P
canvasOutput = addTextToImage({ g, l2i, originalSize, scaledSize });
g.upsertMetadata({ generation_mode: 'sd3_txt2img' });
} else if (generationMode === 'img2img') {
+ assert(manager, 'Need manager to do img2img');
canvasOutput = await addImageToImage({
g,
manager,
@@ -149,6 +150,7 @@ export const buildSD3Graph = async (state: RootState, manager: CanvasManager): P
});
g.upsertMetadata({ generation_mode: 'sd3_img2img' });
} else if (generationMode === 'inpaint') {
+ assert(manager, 'Need manager to do inpaint');
canvasOutput = await addInpaint({
state,
g,
@@ -166,6 +168,7 @@ export const buildSD3Graph = async (state: RootState, manager: CanvasManager): P
});
g.upsertMetadata({ generation_mode: 'sd3_inpaint' });
} else if (generationMode === 'outpaint') {
+ assert(manager, 'Need manager to do outpaint');
canvasOutput = await addOutpaint({
state,
g,
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 f6c167770e..8da0df7d3d 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
@@ -3,7 +3,7 @@ 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 { 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';
import { addImageToImage } from 'features/nodes/util/graph/generation/addImageToImage';
@@ -16,6 +16,7 @@ import { addSDXLRefiner } from 'features/nodes/util/graph/generation/addSDXLRefi
import { addSeamless } from 'features/nodes/util/graph/generation/addSeamless';
import { addTextToImage } from 'features/nodes/util/graph/generation/addTextToImage';
import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker';
+import { getGenerationMode } from 'features/nodes/util/graph/generation/getGenerationMode';
import { Graph } from 'features/nodes/util/graph/generation/Graph';
import {
CANVAS_OUTPUT_PREFIX,
@@ -24,7 +25,6 @@ import {
selectPresetModifiedPrompts,
} from 'features/nodes/util/graph/graphBuilderUtils';
import type { GraphBuilderReturn, ImageOutputNodes } from 'features/nodes/util/graph/types';
-import { selectMainModelConfig } from 'features/controlLayers/store/paramsSlice';
import type { Invocation } from 'services/api/types';
import type { Equals } from 'tsafe';
import { assert } from 'tsafe';
@@ -33,8 +33,8 @@ import { addRegions } from './addRegions';
const log = logger('system');
-export const buildSDXLGraph = async (state: RootState, manager: CanvasManager): Promise => {
- const generationMode = await manager.compositor.getGenerationMode();
+export const buildSDXLGraph = async (state: RootState, manager?: CanvasManager | null): Promise => {
+ const generationMode = await getGenerationMode(manager);
log.debug({ generationMode }, 'Building SDXL graph');
const model = selectMainModelConfig(state);
@@ -177,6 +177,7 @@ export const buildSDXLGraph = async (state: RootState, manager: CanvasManager):
canvasOutput = addTextToImage({ g, l2i, originalSize, scaledSize });
g.upsertMetadata({ generation_mode: 'sdxl_txt2img' });
} else if (generationMode === 'img2img') {
+ assert(manager, 'Need manager to do img2img');
canvasOutput = await addImageToImage({
g,
manager,
@@ -192,6 +193,7 @@ export const buildSDXLGraph = async (state: RootState, manager: CanvasManager):
});
g.upsertMetadata({ generation_mode: 'sdxl_img2img' });
} else if (generationMode === 'inpaint') {
+ assert(manager, 'Need manager to do inpaint');
canvasOutput = await addInpaint({
state,
g,
@@ -209,6 +211,7 @@ export const buildSDXLGraph = async (state: RootState, manager: CanvasManager):
});
g.upsertMetadata({ generation_mode: 'sdxl_inpaint' });
} else if (generationMode === 'outpaint') {
+ assert(manager, 'Need manager to do outpaint');
canvasOutput = await addOutpaint({
state,
g,
@@ -229,40 +232,42 @@ export const buildSDXLGraph = async (state: RootState, manager: CanvasManager):
assert>(false);
}
- const controlNetCollector = g.addNode({
- type: 'collect',
- id: getPrefixedId('control_net_collector'),
- });
- const controlNetResult = await addControlNets({
- manager,
- entities: canvas.controlLayers.entities,
- g,
- rect: canvas.bbox.rect,
- collector: controlNetCollector,
- model,
- });
- if (controlNetResult.addedControlNets > 0) {
- g.addEdge(controlNetCollector, 'collection', denoise, 'control');
- } else {
- g.deleteNode(controlNetCollector.id);
- }
+ if (manager) {
+ const controlNetCollector = g.addNode({
+ type: 'collect',
+ id: getPrefixedId('control_net_collector'),
+ });
+ const controlNetResult = await addControlNets({
+ manager,
+ entities: canvas.controlLayers.entities,
+ g,
+ rect: canvas.bbox.rect,
+ collector: controlNetCollector,
+ model,
+ });
+ if (controlNetResult.addedControlNets > 0) {
+ g.addEdge(controlNetCollector, 'collection', denoise, 'control');
+ } else {
+ g.deleteNode(controlNetCollector.id);
+ }
- const t2iAdapterCollector = g.addNode({
- type: 'collect',
- id: getPrefixedId('t2i_adapter_collector'),
- });
- const t2iAdapterResult = await addT2IAdapters({
- manager,
- entities: canvas.controlLayers.entities,
- g,
- rect: canvas.bbox.rect,
- collector: t2iAdapterCollector,
- model,
- });
- if (t2iAdapterResult.addedT2IAdapters > 0) {
- g.addEdge(t2iAdapterCollector, 'collection', denoise, 't2i_adapter');
- } else {
- g.deleteNode(t2iAdapterCollector.id);
+ const t2iAdapterCollector = g.addNode({
+ type: 'collect',
+ id: getPrefixedId('t2i_adapter_collector'),
+ });
+ const t2iAdapterResult = await addT2IAdapters({
+ manager,
+ entities: canvas.controlLayers.entities,
+ g,
+ rect: canvas.bbox.rect,
+ collector: t2iAdapterCollector,
+ model,
+ });
+ if (t2iAdapterResult.addedT2IAdapters > 0) {
+ g.addEdge(t2iAdapterCollector, 'collection', denoise, 't2i_adapter');
+ } else {
+ g.deleteNode(t2iAdapterCollector.id);
+ }
}
const ipAdapterCollect = g.addNode({
@@ -275,23 +280,25 @@ export const buildSDXLGraph = async (state: RootState, manager: CanvasManager):
collector: ipAdapterCollect,
model,
});
+ let totalIPAdaptersAdded = ipAdapterResult.addedIPAdapters;
- const regionsResult = await addRegions({
- manager,
- regions: canvas.regionalGuidance.entities,
- g,
- bbox: canvas.bbox.rect,
- model,
- posCond,
- negCond,
- posCondCollect,
- negCondCollect,
- ipAdapterCollect,
- fluxReduxCollect: null,
- });
+ if (manager) {
+ const regionsResult = await addRegions({
+ manager,
+ regions: canvas.regionalGuidance.entities,
+ g,
+ bbox: canvas.bbox.rect,
+ model,
+ posCond,
+ negCond,
+ posCondCollect,
+ negCondCollect,
+ ipAdapterCollect,
+ fluxReduxCollect: null,
+ });
+ totalIPAdaptersAdded += regionsResult.reduce((acc, r) => acc + r.addedIPAdapters, 0);
+ }
- const totalIPAdaptersAdded =
- ipAdapterResult.addedIPAdapters + regionsResult.reduce((acc, r) => acc + r.addedIPAdapters, 0);
if (totalIPAdaptersAdded > 0) {
g.addEdge(ipAdapterCollect, 'collection', denoise, 'ip_adapter');
} else {
diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/getGenerationMode.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/getGenerationMode.ts
new file mode 100644
index 0000000000..b458088d5a
--- /dev/null
+++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/getGenerationMode.ts
@@ -0,0 +1,9 @@
+import type { CanvasManager } from 'features/controlLayers/konva/CanvasManager';
+import type { GenerationMode } from 'features/controlLayers/store/types';
+
+export const getGenerationMode = async (manager?: CanvasManager | null): Promise => {
+ if (!manager) {
+ return 'txt2img';
+ }
+ return await manager.compositor.getGenerationMode();
+};
diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/web/src/features/queue/store/readiness.ts
index ebe96a21be..f17542a4df 100644
--- a/invokeai/frontend/web/src/features/queue/store/readiness.ts
+++ b/invokeai/frontend/web/src/features/queue/store/readiness.ts
@@ -2,13 +2,13 @@ import { useStore } from '@nanostores/react';
import { createSelector } from '@reduxjs/toolkit';
import { EMPTY_ARRAY } from 'app/store/constants';
import { useAppStore } from 'app/store/nanostores/store';
-import { $true } from 'app/store/nanostores/util';
+import { $false } from 'app/store/nanostores/util';
import type { AppDispatch, AppStore } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import type { AppConfig } from 'app/types/invokeai';
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
import { useCanvasManagerSafe } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
-import { selectMainModelConfig,selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
+import { selectMainModelConfig, selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
import { selectCanvasSlice } from 'features/controlLayers/store/selectors';
import type { CanvasState, ParamsState } from 'features/controlLayers/store/types';
import {
@@ -145,11 +145,11 @@ export const useReadinessWatcher = () => {
const config = useAppSelector(selectConfigSlice);
const templates = useStore($templates);
const isConnected = useStore($isConnected);
- const canvasIsFiltering = useStore(canvasManager?.stateApi.$isFiltering ?? $true);
- const canvasIsTransforming = useStore(canvasManager?.stateApi.$isTransforming ?? $true);
- const canvasIsRasterizing = useStore(canvasManager?.stateApi.$isRasterizing ?? $true);
- const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $true);
- const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $true);
+ const canvasIsFiltering = useStore(canvasManager?.stateApi.$isFiltering ?? $false);
+ const canvasIsTransforming = useStore(canvasManager?.stateApi.$isTransforming ?? $false);
+ const canvasIsRasterizing = useStore(canvasManager?.stateApi.$isRasterizing ?? $false);
+ const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $false);
+ const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $false);
const isInPublishFlow = useStore($isInPublishFlow);
const { isChatGPT4oHighModelDisabled } = useIsModelDisabled();