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();