refactor(ui): canvas flow (wip)

This commit is contained in:
psychedelicious
2025-05-22 17:41:19 +10:00
parent c4d1e78f59
commit cf2d67ef3d
14 changed files with 315 additions and 192 deletions

View File

@@ -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',
})
);

View File

@@ -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 <CanvasNoSession />;
return <NoActiveSession />;
}
if (isSessionStarted && isCanvasEmpty) {
return <SimpleActiveSession />;
}
return <CanvasActiveSession />;
@@ -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 (
<Flex w="full" h="full" alignItems="center" justifyContent="center">
FRESH CANVAS is fresh when: - No control layers - No inpaint masks - No regions - No Raster Layers
<Flex flexDir="column" w="full" h="full" alignItems="center" justifyContent="center">
<Text fontSize="lg" fontWeight="bold">
No Active Session
</Text>
<Button display="flex" flexDir="column" gap={2} p={8} minH={0} minW={0} onClick={newSesh}>
<Text>New Canvas Session</Text>
<Text>- New Canvas Session</Text>
<Text>- 1 Inpaint mask layer added</Text>
</Button>
<Flex flexDir="column" gap={2} p={8} border="dashed yellow 2px">
<Text>Generate with Starting Image</Text>
<Text>- New Canvas Session</Text>
<Text>- Dropped image as raster layer</Text>
<Text>- Bbox resized</Text>
</Flex>
<Flex flexDir="column" gap={2} p={8} border="dashed yellow 2px">
<Text>Generate with Control Image</Text>
<Text>- New Canvas Session</Text>
<Text>- Dropped image as control layer</Text>
<Text>- Bbox resized</Text>
</Flex>
<Flex flexDir="column" gap={2} p={8} border="dashed yellow 2px">
<Text>Edit Image</Text>
<Text>- New Canvas Session</Text>
<Text>- Dropped image as raster layer</Text>
<Text>- Bbox resized</Text>
<Text>- 1 Inpaint mask layer added</Text>
</Flex>
</Flex>
);
});
CanvasNoSession.displayName = 'CanvasNoSession';
NoActiveSession.displayName = 'NoActiveSession';
const SimpleActiveSession = memo(() => {
const isStaging = useAppSelector(selectIsStaging);
const selectedImage = useAppSelector(selectSelectedImage);
const stagedImages = useAppSelector(selectStagedImages);
return (
<Flex flexDir="column" w="full" h="full" alignItems="center" justifyContent="center">
<Text fontSize="lg" fontWeight="bold">
Simple Session (staging view) {isStaging && 'STAGING'}
</Text>
{selectedImage && <Image src={selectedImage.imageDTO.image_url} />}
<Flex gap={2} maxW="full" overflow='scroll'>
{stagedImages.map(({ imageDTO }) => (
<Image key={imageDTO.image_name} maxW={108} src={imageDTO.thumbnail_url} />
))}
</Flex>
</Flex>
);
});
SimpleActiveSession.displayName = 'SimpleActiveSession';
const CanvasActiveSession = memo(() => {
const dynamicGrid = useAppSelector(selectDynamicGrid);

View File

@@ -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;
});
},
});

View File

@@ -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

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildChatGPT4oGraph = async (
state: RootState,
manager?: CanvasManager | null
): Promise<GraphBuilderReturn> => {
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,

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildCogView4Graph = async (
state: RootState,
manager?: CanvasManager | null
): Promise<GraphBuilderReturn> => {
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,

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildFLUXGraph = async (state: RootState, manager?: CanvasManager | null): Promise<GraphBuilderReturn> => {
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<ImageOutputNodes> = 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<Equals<typeof generationMode, never>>(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 {

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildImagen3Graph = async (
state: RootState,
manager?: CanvasManager | null
): Promise<GraphBuilderReturn> => {
const generationMode = await getGenerationMode(manager);
if (generationMode !== 'txt2img') {
throw new UnsupportedGenerationModeError(t('toast.imagenIncompatibleGenerationMode', { model: 'Imagen3' }));

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildImagen4Graph = async (
state: RootState,
manager?: CanvasManager | null
): Promise<GraphBuilderReturn> => {
const generationMode = await getGenerationMode(manager);
if (generationMode !== 'txt2img') {
throw new UnsupportedGenerationModeError(t('toast.imagenIncompatibleGenerationMode', { model: 'Imagen4' }));

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildSD1Graph = async (state: RootState, manager?: CanvasManager | null): Promise<GraphBuilderReturn> => {
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<Equals<typeof generationMode, never>>(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 {

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildSD3Graph = async (state: RootState, manager?: CanvasManager | null): Promise<GraphBuilderReturn> => {
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,

View File

@@ -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<GraphBuilderReturn> => {
const generationMode = await manager.compositor.getGenerationMode();
export const buildSDXLGraph = async (state: RootState, manager?: CanvasManager | null): Promise<GraphBuilderReturn> => {
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<Equals<typeof generationMode, never>>(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 {

View File

@@ -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<GenerationMode> => {
if (!manager) {
return 'txt2img';
}
return await manager.compositor.getGenerationMode();
};

View File

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