mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
tidy(ui): excise img2img tab
This commit is contained in:
committed by
Kent Keirsey
parent
7d58908e32
commit
a6ac184211
@ -32,7 +32,6 @@ import { addImagesStarredListener } from 'app/store/middleware/listenerMiddlewar
|
|||||||
import { addImagesUnstarredListener } from 'app/store/middleware/listenerMiddleware/listeners/imagesUnstarred';
|
import { addImagesUnstarredListener } from 'app/store/middleware/listenerMiddleware/listeners/imagesUnstarred';
|
||||||
import { addImageToDeleteSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected';
|
import { addImageToDeleteSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected';
|
||||||
import { addImageUploadedFulfilledListener } from 'app/store/middleware/listenerMiddleware/listeners/imageUploaded';
|
import { addImageUploadedFulfilledListener } from 'app/store/middleware/listenerMiddleware/listeners/imageUploaded';
|
||||||
import { addInitialImageSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/initialImageSelected';
|
|
||||||
import { addModelSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelSelected';
|
import { addModelSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelSelected';
|
||||||
import { addModelsLoadedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelsLoaded';
|
import { addModelsLoadedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelsLoaded';
|
||||||
import { addDynamicPromptsListener } from 'app/store/middleware/listenerMiddleware/listeners/promptChanged';
|
import { addDynamicPromptsListener } from 'app/store/middleware/listenerMiddleware/listeners/promptChanged';
|
||||||
@ -73,9 +72,6 @@ const startAppListening = listenerMiddleware.startListening as AppStartListening
|
|||||||
// Image uploaded
|
// Image uploaded
|
||||||
addImageUploadedFulfilledListener(startAppListening);
|
addImageUploadedFulfilledListener(startAppListening);
|
||||||
|
|
||||||
// Image selected
|
|
||||||
addInitialImageSelectedListener(startAppListening);
|
|
||||||
|
|
||||||
// Image deleted
|
// Image deleted
|
||||||
addRequestedSingleImageDeletionListener(startAppListening);
|
addRequestedSingleImageDeletionListener(startAppListening);
|
||||||
addDeleteBoardAndImagesFulfilledListener(startAppListening);
|
addDeleteBoardAndImagesFulfilledListener(startAppListening);
|
||||||
|
@ -3,7 +3,6 @@ import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
|||||||
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppStartListening) => {
|
export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppStartListening) => {
|
||||||
@ -14,7 +13,6 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
|
|||||||
|
|
||||||
// Remove all deleted images from the UI
|
// Remove all deleted images from the UI
|
||||||
|
|
||||||
let wasInitialImageReset = false;
|
|
||||||
let wasCanvasReset = false;
|
let wasCanvasReset = false;
|
||||||
let wasNodeEditorReset = false;
|
let wasNodeEditorReset = false;
|
||||||
let wereControlAdaptersReset = false;
|
let wereControlAdaptersReset = false;
|
||||||
@ -23,11 +21,6 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
|
|||||||
deleted_images.forEach((image_name) => {
|
deleted_images.forEach((image_name) => {
|
||||||
const imageUsage = getImageUsage(generation, canvas, nodes, controlAdapters, image_name);
|
const imageUsage = getImageUsage(generation, canvas, nodes, controlAdapters, image_name);
|
||||||
|
|
||||||
if (imageUsage.isInitialImage && !wasInitialImageReset) {
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
wasInitialImageReset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageUsage.isCanvasImage && !wasCanvasReset) {
|
if (imageUsage.isCanvasImage && !wasCanvasReset) {
|
||||||
dispatch(resetCanvas());
|
dispatch(resetCanvas());
|
||||||
wasCanvasReset = true;
|
wasCanvasReset = true;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { enqueueRequested } from 'app/store/actions';
|
import { enqueueRequested } from 'app/store/actions';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
||||||
import { buildLinearImageToImageGraph } from 'features/nodes/util/graph/buildLinearImageToImageGraph';
|
|
||||||
import { buildLinearSDXLImageToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLImageToImageGraph';
|
|
||||||
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLTextToImageGraph';
|
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLTextToImageGraph';
|
||||||
import { buildLinearTextToImageGraph } from 'features/nodes/util/graph/buildLinearTextToImageGraph';
|
import { buildLinearTextToImageGraph } from 'features/nodes/util/graph/buildLinearTextToImageGraph';
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
@ -10,7 +8,7 @@ import { queueApi } from 'services/api/endpoints/queue';
|
|||||||
export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) => {
|
export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
||||||
enqueueRequested.match(action) && (action.payload.tabName === 'txt2img' || action.payload.tabName === 'img2img'),
|
enqueueRequested.match(action) && action.payload.tabName === 'txt2img',
|
||||||
effect: async (action, { getState, dispatch }) => {
|
effect: async (action, { getState, dispatch }) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const model = state.generation.model;
|
const model = state.generation.model;
|
||||||
@ -19,17 +17,9 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
|
|||||||
let graph;
|
let graph;
|
||||||
|
|
||||||
if (model && model.base === 'sdxl') {
|
if (model && model.base === 'sdxl') {
|
||||||
if (action.payload.tabName === 'txt2img') {
|
graph = await buildLinearSDXLTextToImageGraph(state);
|
||||||
graph = await buildLinearSDXLTextToImageGraph(state);
|
|
||||||
} else {
|
|
||||||
graph = await buildLinearSDXLImageToImageGraph(state);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (action.payload.tabName === 'txt2img') {
|
graph = await buildLinearTextToImageGraph(state);
|
||||||
graph = await buildLinearTextToImageGraph(state);
|
|
||||||
} else {
|
|
||||||
graph = await buildLinearImageToImageGraph(state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchConfig = prepareLinearUIBatch(state, graph, prepend);
|
const batchConfig = prepareLinearUIBatch(state, graph, prepend);
|
||||||
|
@ -14,7 +14,6 @@ import { imageSelected } from 'features/gallery/store/gallerySlice';
|
|||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { isImageFieldInputInstance } from 'features/nodes/types/field';
|
import { isImageFieldInputInstance } from 'features/nodes/types/field';
|
||||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
|
||||||
import { clamp, forEach } from 'lodash-es';
|
import { clamp, forEach } from 'lodash-es';
|
||||||
import { api } from 'services/api';
|
import { api } from 'services/api';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
@ -73,11 +72,6 @@ export const addRequestedSingleImageDeletionListener = (startAppListening: AppSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageDTOs.forEach((imageDTO) => {
|
imageDTOs.forEach((imageDTO) => {
|
||||||
// reset init image if we deleted it
|
|
||||||
if (getState().generation.initialImage?.imageName === imageDTO.image_name) {
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset control adapters that use the deleted images
|
// reset control adapters that use the deleted images
|
||||||
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
|
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
|
||||||
if (
|
if (
|
||||||
@ -168,11 +162,6 @@ export const addRequestedSingleImageDeletionListener = (startAppListening: AppSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageDTOs.forEach((imageDTO) => {
|
imageDTOs.forEach((imageDTO) => {
|
||||||
// reset init image if we deleted it
|
|
||||||
if (getState().generation.initialImage?.imageName === imageDTO.image_name) {
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset control adapters that use the deleted images
|
// reset control adapters that use the deleted images
|
||||||
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
|
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
|
||||||
if (
|
if (
|
||||||
|
@ -16,7 +16,7 @@ import {
|
|||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { initialImageChanged, selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
export const dndDropped = createAction<{
|
export const dndDropped = createAction<{
|
||||||
@ -53,18 +53,6 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Image dropped on initial image
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
overData.actionType === 'SET_INITIAL_IMAGE' &&
|
|
||||||
activeData.payloadType === 'IMAGE_DTO' &&
|
|
||||||
activeData.payload.imageDTO
|
|
||||||
) {
|
|
||||||
dispatch(initialImageChanged(activeData.payload.imageDTO));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image dropped on ControlNet
|
* Image dropped on ControlNet
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,6 @@ export const addImageToDeleteSelectedListener = (startAppListening: AppStartList
|
|||||||
|
|
||||||
const isImageInUse =
|
const isImageInUse =
|
||||||
imagesUsage.some((i) => i.isCanvasImage) ||
|
imagesUsage.some((i) => i.isCanvasImage) ||
|
||||||
imagesUsage.some((i) => i.isInitialImage) ||
|
|
||||||
imagesUsage.some((i) => i.isControlImage) ||
|
imagesUsage.some((i) => i.isControlImage) ||
|
||||||
imagesUsage.some((i) => i.isNodesImage);
|
imagesUsage.some((i) => i.isNodesImage);
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
rgLayerIPAdapterImageChanged,
|
rgLayerIPAdapterImageChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { initialImageChanged, selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { omit } from 'lodash-es';
|
import { omit } from 'lodash-es';
|
||||||
@ -158,17 +158,6 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_INITIAL_IMAGE') {
|
|
||||||
dispatch(initialImageChanged(imageDTO));
|
|
||||||
dispatch(
|
|
||||||
addToast({
|
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
|
||||||
description: t('toast.setInitialImage'),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_NODES_IMAGE') {
|
if (postUploadAction?.type === 'SET_NODES_IMAGE') {
|
||||||
const { nodeId, fieldName } = postUploadAction;
|
const { nodeId, fieldName } = postUploadAction;
|
||||||
dispatch(fieldImageValueChanged({ nodeId, fieldName, value: imageDTO }));
|
dispatch(fieldImageValueChanged({ nodeId, fieldName, value: imageDTO }));
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
|
||||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
|
||||||
import { makeToast } from 'features/system/util/makeToast';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
|
|
||||||
export const addInitialImageSelectedListener = (startAppListening: AppStartListening) => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: initialImageSelected,
|
|
||||||
effect: (action, { dispatch }) => {
|
|
||||||
if (!action.payload) {
|
|
||||||
dispatch(addToast(makeToast({ title: t('toast.imageNotLoadedDesc'), status: 'error' })));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(initialImageChanged(action.payload));
|
|
||||||
dispatch(addToast(makeToast(t('toast.sentToImageToImage'))));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -21,10 +21,6 @@ const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (ac
|
|||||||
postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' };
|
postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'img2img') {
|
|
||||||
postUploadAction = { type: 'SET_INITIAL_IMAGE' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return postUploadAction;
|
return postUploadAction;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -75,21 +75,13 @@ export const useGlobalHotkeys = () => {
|
|||||||
useHotkeys(
|
useHotkeys(
|
||||||
'2',
|
'2',
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('img2img'));
|
dispatch(setActiveTab('unifiedCanvas'));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'3',
|
'3',
|
||||||
() => {
|
|
||||||
dispatch(setActiveTab('unifiedCanvas'));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
'4',
|
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('nodes'));
|
dispatch(setActiveTab('nodes'));
|
||||||
},
|
},
|
||||||
@ -97,7 +89,7 @@ export const useGlobalHotkeys = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'5',
|
'4',
|
||||||
() => {
|
() => {
|
||||||
if (isModelManagerEnabled) {
|
if (isModelManagerEnabled) {
|
||||||
dispatch(setActiveTab('modelManager'));
|
dispatch(setActiveTab('modelManager'));
|
||||||
@ -107,7 +99,7 @@ export const useGlobalHotkeys = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
isModelManagerEnabled ? '6' : '5',
|
isModelManagerEnabled ? '5' : '4',
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('queue'));
|
dispatch(setActiveTab('queue'));
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,7 @@ const selector = createMemoizedSelector(
|
|||||||
activeTabNameSelector,
|
activeTabNameSelector,
|
||||||
],
|
],
|
||||||
(controlAdapters, generation, system, nodes, dynamicPrompts, controlLayers, activeTabName) => {
|
(controlAdapters, generation, system, nodes, dynamicPrompts, controlLayers, activeTabName) => {
|
||||||
const { initialImage, model } = generation;
|
const { model } = generation;
|
||||||
const { positivePrompt } = controlLayers.present;
|
const { positivePrompt } = controlLayers.present;
|
||||||
|
|
||||||
const { isConnected } = system;
|
const { isConnected } = system;
|
||||||
@ -40,10 +40,6 @@ const selector = createMemoizedSelector(
|
|||||||
reasons.push(i18n.t('parameters.invoke.systemDisconnected'));
|
reasons.push(i18n.t('parameters.invoke.systemDisconnected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'img2img' && !initialImage) {
|
|
||||||
reasons.push(i18n.t('parameters.invoke.noInitialImageSelected'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTabName === 'nodes') {
|
if (activeTabName === 'nodes') {
|
||||||
if (nodes.shouldValidateGraph) {
|
if (nodes.shouldValidateGraph) {
|
||||||
if (!nodes.nodes.length) {
|
if (!nodes.nodes.length) {
|
||||||
|
@ -627,7 +627,7 @@ export const controlLayersSlice = createSlice({
|
|||||||
reducer: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => {
|
reducer: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => {
|
||||||
const { layerId, imageDTO } = action.payload;
|
const { layerId, imageDTO } = action.payload;
|
||||||
// Highlander! There can be only one!
|
// Highlander! There can be only one!
|
||||||
assert(!state.layers.find(isInitialImageLayer));
|
state.layers = state.layers.filter((l) => isInitialImageLayer(l));
|
||||||
const layer: InitialImageLayer = {
|
const layer: InitialImageLayer = {
|
||||||
id: layerId,
|
id: layerId,
|
||||||
type: 'initial_image_layer',
|
type: 'initial_image_layer',
|
||||||
|
@ -38,7 +38,6 @@ const selectImageUsages = createMemoizedSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const imageUsageSummary: ImageUsage = {
|
const imageUsageSummary: ImageUsage = {
|
||||||
isInitialImage: some(allImageUsage, (i) => i.isInitialImage),
|
|
||||||
isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage),
|
isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage),
|
||||||
isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
|
isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
|
||||||
isControlImage: some(allImageUsage, (i) => i.isControlImage),
|
isControlImage: some(allImageUsage, (i) => i.isControlImage),
|
||||||
|
@ -29,7 +29,6 @@ const ImageUsageMessage = (props: Props) => {
|
|||||||
<>
|
<>
|
||||||
<Text>{topMessage}</Text>
|
<Text>{topMessage}</Text>
|
||||||
<UnorderedList paddingInlineStart={6}>
|
<UnorderedList paddingInlineStart={6}>
|
||||||
{imageUsage.isInitialImage && <ListItem>{t('common.img2img')}</ListItem>}
|
|
||||||
{imageUsage.isCanvasImage && <ListItem>{t('common.unifiedCanvas')}</ListItem>}
|
{imageUsage.isCanvasImage && <ListItem>{t('common.unifiedCanvas')}</ListItem>}
|
||||||
{imageUsage.isControlImage && <ListItem>{t('common.controlNet')}</ListItem>}
|
{imageUsage.isControlImage && <ListItem>{t('common.controlNet')}</ListItem>}
|
||||||
{imageUsage.isNodesImage && <ListItem>{t('common.nodeEditor')}</ListItem>}
|
{imageUsage.isNodesImage && <ListItem>{t('common.nodeEditor')}</ListItem>}
|
||||||
|
@ -25,8 +25,6 @@ export const getImageUsage = (
|
|||||||
controlAdapters: ControlAdaptersState,
|
controlAdapters: ControlAdaptersState,
|
||||||
image_name: string
|
image_name: string
|
||||||
) => {
|
) => {
|
||||||
const isInitialImage = generation.initialImage?.imageName === image_name;
|
|
||||||
|
|
||||||
const isCanvasImage = canvas.layerState.objects.some((obj) => obj.kind === 'image' && obj.imageName === image_name);
|
const isCanvasImage = canvas.layerState.objects.some((obj) => obj.kind === 'image' && obj.imageName === image_name);
|
||||||
|
|
||||||
const isNodesImage = nodes.nodes.filter(isInvocationNode).some((node) => {
|
const isNodesImage = nodes.nodes.filter(isInvocationNode).some((node) => {
|
||||||
@ -41,7 +39,6 @@ export const getImageUsage = (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const imageUsage: ImageUsage = {
|
const imageUsage: ImageUsage = {
|
||||||
isInitialImage,
|
|
||||||
isCanvasImage,
|
isCanvasImage,
|
||||||
isNodesImage,
|
isNodesImage,
|
||||||
isControlImage,
|
isControlImage,
|
||||||
|
@ -6,7 +6,6 @@ export type DeleteImageState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ImageUsage = {
|
export type ImageUsage = {
|
||||||
isInitialImage: boolean;
|
|
||||||
isCanvasImage: boolean;
|
isCanvasImage: boolean;
|
||||||
isNodesImage: boolean;
|
isNodesImage: boolean;
|
||||||
isControlImage: boolean;
|
isControlImage: boolean;
|
||||||
|
@ -22,10 +22,6 @@ type CurrentImageDropData = BaseDropData & {
|
|||||||
actionType: 'SET_CURRENT_IMAGE';
|
actionType: 'SET_CURRENT_IMAGE';
|
||||||
};
|
};
|
||||||
|
|
||||||
type InitialImageDropData = BaseDropData & {
|
|
||||||
actionType: 'SET_INITIAL_IMAGE';
|
|
||||||
};
|
|
||||||
|
|
||||||
type ControlAdapterDropData = BaseDropData & {
|
type ControlAdapterDropData = BaseDropData & {
|
||||||
actionType: 'SET_CONTROL_ADAPTER_IMAGE';
|
actionType: 'SET_CONTROL_ADAPTER_IMAGE';
|
||||||
context: {
|
context: {
|
||||||
@ -85,7 +81,6 @@ export type RemoveFromBoardDropData = BaseDropData & {
|
|||||||
|
|
||||||
export type TypesafeDroppableData =
|
export type TypesafeDroppableData =
|
||||||
| CurrentImageDropData
|
| CurrentImageDropData
|
||||||
| InitialImageDropData
|
|
||||||
| ControlAdapterDropData
|
| ControlAdapterDropData
|
||||||
| CanvasInitialImageDropData
|
| CanvasInitialImageDropData
|
||||||
| NodesImageDropData
|
| NodesImageDropData
|
||||||
|
@ -15,8 +15,6 @@ export const isValidDrop = (overData: TypesafeDroppableData | undefined, active:
|
|||||||
switch (actionType) {
|
switch (actionType) {
|
||||||
case 'SET_CURRENT_IMAGE':
|
case 'SET_CURRENT_IMAGE':
|
||||||
return payloadType === 'IMAGE_DTO';
|
return payloadType === 'IMAGE_DTO';
|
||||||
case 'SET_INITIAL_IMAGE':
|
|
||||||
return payloadType === 'IMAGE_DTO';
|
|
||||||
case 'SET_CONTROL_ADAPTER_IMAGE':
|
case 'SET_CONTROL_ADAPTER_IMAGE':
|
||||||
return payloadType === 'IMAGE_DTO';
|
return payloadType === 'IMAGE_DTO';
|
||||||
case 'SET_CA_LAYER_IMAGE':
|
case 'SET_CA_LAYER_IMAGE':
|
||||||
|
@ -50,7 +50,6 @@ const DeleteBoardModal = (props: Props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const imageUsageSummary: ImageUsage = {
|
const imageUsageSummary: ImageUsage = {
|
||||||
isInitialImage: some(allImageUsage, (i) => i.isInitialImage),
|
|
||||||
isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage),
|
isCanvasImage: some(allImageUsage, (i) => i.isCanvasImage),
|
||||||
isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
|
isNodesImage: some(allImageUsage, (i) => i.isNodesImage),
|
||||||
isControlImage: some(allImageUsage, (i) => i.isControlImage),
|
isControlImage: some(allImageUsage, (i) => i.isControlImage),
|
||||||
|
@ -4,6 +4,7 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
|||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
|
import { upscaleRequested } from 'app/store/middleware/listenerMiddleware/listeners/upscaleRequested';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
import { DeleteImageButton } from 'features/deleteImageModal/components/DeleteImageButton';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
import SingleSelectionMenuItems from 'features/gallery/components/ImageContextMenu/SingleSelectionMenuItems';
|
||||||
@ -13,7 +14,6 @@ import { selectLastSelectedImage } from 'features/gallery/store/gallerySelectors
|
|||||||
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
import { selectGallerySlice } from 'features/gallery/store/gallerySlice';
|
||||||
import { parseAndRecallImageDimensions } from 'features/metadata/util/handlers';
|
import { parseAndRecallImageDimensions } from 'features/metadata/util/handlers';
|
||||||
import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
|
import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUpscaleSettings';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
|
||||||
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
|
import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
import { selectSystemSlice } from 'features/system/store/systemSlice';
|
||||||
@ -86,8 +86,11 @@ const CurrentImageButtons = () => {
|
|||||||
useHotkeys('d', handleUseSize, [handleUseSize]);
|
useHotkeys('d', handleUseSize, [handleUseSize]);
|
||||||
|
|
||||||
const handleSendToImageToImage = useCallback(() => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
|
if (!imageDTO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
dispatch(sentImageToImg2Img());
|
dispatch(sentImageToImg2Img());
|
||||||
dispatch(initialImageSelected(imageDTO));
|
dispatch(iiLayerAdded(imageDTO));
|
||||||
}, [dispatch, imageDTO]);
|
}, [dispatch, imageDTO]);
|
||||||
|
|
||||||
useHotkeys('shift+i', handleSendToImageToImage, [imageDTO]);
|
useHotkeys('shift+i', handleSendToImageToImage, [imageDTO]);
|
||||||
|
@ -7,10 +7,10 @@ import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
|
|||||||
import { useDownloadImage } from 'common/hooks/useDownloadImage';
|
import { useDownloadImage } from 'common/hooks/useDownloadImage';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
||||||
|
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
import { useImageActions } from 'features/gallery/hooks/useImageActions';
|
import { useImageActions } from 'features/gallery/hooks/useImageActions';
|
||||||
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
import { sentImageToCanvas, sentImageToImg2Img } from 'features/gallery/store/actions';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
@ -72,7 +72,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
|
|
||||||
const handleSendToImageToImage = useCallback(() => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
dispatch(sentImageToImg2Img());
|
dispatch(sentImageToImg2Img());
|
||||||
dispatch(initialImageSelected(imageDTO));
|
dispatch(iiLayerAdded(imageDTO));
|
||||||
}, [dispatch, imageDTO]);
|
}, [dispatch, imageDTO]);
|
||||||
|
|
||||||
const handleSendToCanvas = useCallback(() => {
|
const handleSendToCanvas = useCallback(() => {
|
||||||
|
@ -12,7 +12,6 @@ import { PiArrowLeftBold } from 'react-icons/pi';
|
|||||||
|
|
||||||
const TAB_NAME_TO_TKEY: Record<InvokeTabName, string> = {
|
const TAB_NAME_TO_TKEY: Record<InvokeTabName, string> = {
|
||||||
txt2img: 'common.txt2img',
|
txt2img: 'common.txt2img',
|
||||||
img2img: 'common.img2img',
|
|
||||||
unifiedCanvas: 'common.unifiedCanvas',
|
unifiedCanvas: 'common.unifiedCanvas',
|
||||||
nodes: 'common.nodes',
|
nodes: 'common.nodes',
|
||||||
modelManager: 'modelManager.modelManager',
|
modelManager: 'modelManager.modelManager',
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
caLayerControlNetsDeleted,
|
caLayerControlNetsDeleted,
|
||||||
caLayerT2IAdaptersDeleted,
|
caLayerT2IAdaptersDeleted,
|
||||||
heightChanged,
|
heightChanged,
|
||||||
|
iiLayerAdded,
|
||||||
ipaLayerAdded,
|
ipaLayerAdded,
|
||||||
ipaLayersDeleted,
|
ipaLayersDeleted,
|
||||||
negativePrompt2Changed,
|
negativePrompt2Changed,
|
||||||
@ -32,7 +33,6 @@ import type {
|
|||||||
} from 'features/metadata/types';
|
} from 'features/metadata/types';
|
||||||
import { modelSelected } from 'features/parameters/store/actions';
|
import { modelSelected } from 'features/parameters/store/actions';
|
||||||
import {
|
import {
|
||||||
initialImageChanged,
|
|
||||||
setCfgRescaleMultiplier,
|
setCfgRescaleMultiplier,
|
||||||
setCfgScale,
|
setCfgScale,
|
||||||
setImg2imgStrength,
|
setImg2imgStrength,
|
||||||
@ -107,7 +107,7 @@ const recallScheduler: MetadataRecallFunc<ParameterScheduler> = (scheduler) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const recallInitialImage: MetadataRecallFunc<ImageDTO> = async (imageDTO) => {
|
const recallInitialImage: MetadataRecallFunc<ImageDTO> = async (imageDTO) => {
|
||||||
getStore().dispatch(initialImageChanged(imageDTO));
|
getStore().dispatch(iiLayerAdded(imageDTO));
|
||||||
};
|
};
|
||||||
|
|
||||||
const setSizeOptions = { updateAspectRatio: true, clamp: true };
|
const setSizeOptions = { updateAspectRatio: true, clamp: true };
|
||||||
|
@ -9,8 +9,6 @@ import {
|
|||||||
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||||
SDXL_CONTROL_LAYERS_GRAPH,
|
SDXL_CONTROL_LAYERS_GRAPH,
|
||||||
SDXL_DENOISE_LATENTS,
|
SDXL_DENOISE_LATENTS,
|
||||||
SDXL_IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
SDXL_TEXT_TO_IMAGE_GRAPH,
|
|
||||||
SEAMLESS,
|
SEAMLESS,
|
||||||
VAE_LOADER,
|
VAE_LOADER,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
@ -56,8 +54,6 @@ export const addSeamlessToLinearGraph = (
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
||||||
graph.id === SDXL_TEXT_TO_IMAGE_GRAPH ||
|
|
||||||
graph.id === SDXL_IMAGE_TO_IMAGE_GRAPH ||
|
|
||||||
graph.id === SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH ||
|
graph.id === SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH ||
|
||||||
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
||||||
graph.id === SDXL_CANVAS_INPAINT_GRAPH ||
|
graph.id === SDXL_CANVAS_INPAINT_GRAPH ||
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
CANVAS_OUTPUT,
|
CANVAS_OUTPUT,
|
||||||
CANVAS_TEXT_TO_IMAGE_GRAPH,
|
CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||||
CONTROL_LAYERS_GRAPH,
|
CONTROL_LAYERS_GRAPH,
|
||||||
IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
IMAGE_TO_LATENTS,
|
IMAGE_TO_LATENTS,
|
||||||
INPAINT_CREATE_MASK,
|
INPAINT_CREATE_MASK,
|
||||||
INPAINT_IMAGE,
|
INPAINT_IMAGE,
|
||||||
@ -19,9 +18,7 @@ import {
|
|||||||
SDXL_CANVAS_OUTPAINT_GRAPH,
|
SDXL_CANVAS_OUTPAINT_GRAPH,
|
||||||
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH,
|
||||||
SDXL_CONTROL_LAYERS_GRAPH,
|
SDXL_CONTROL_LAYERS_GRAPH,
|
||||||
SDXL_IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
SDXL_REFINER_SEAMLESS,
|
SDXL_REFINER_SEAMLESS,
|
||||||
SDXL_TEXT_TO_IMAGE_GRAPH,
|
|
||||||
SEAMLESS,
|
SEAMLESS,
|
||||||
VAE_LOADER,
|
VAE_LOADER,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
@ -52,13 +49,7 @@ export const addVAEToGraph = async (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (graph.id === CONTROL_LAYERS_GRAPH || graph.id === SDXL_CONTROL_LAYERS_GRAPH) {
|
||||||
graph.id === CONTROL_LAYERS_GRAPH ||
|
|
||||||
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
|
||||||
graph.id === IMAGE_TO_IMAGE_GRAPH ||
|
|
||||||
graph.id === SDXL_TEXT_TO_IMAGE_GRAPH ||
|
|
||||||
graph.id === SDXL_IMAGE_TO_IMAGE_GRAPH
|
|
||||||
) {
|
|
||||||
graph.edges.push({
|
graph.edges.push({
|
||||||
source: {
|
source: {
|
||||||
node_id: isSeamlessEnabled
|
node_id: isSeamlessEnabled
|
||||||
@ -104,8 +95,6 @@ export const addVAEToGraph = async (
|
|||||||
if (
|
if (
|
||||||
(graph.id === CONTROL_LAYERS_GRAPH ||
|
(graph.id === CONTROL_LAYERS_GRAPH ||
|
||||||
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
graph.id === SDXL_CONTROL_LAYERS_GRAPH ||
|
||||||
graph.id === IMAGE_TO_IMAGE_GRAPH ||
|
|
||||||
graph.id === SDXL_IMAGE_TO_IMAGE_GRAPH ||
|
|
||||||
graph.id === CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
graph.id === CANVAS_IMAGE_TO_IMAGE_GRAPH ||
|
||||||
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH) &&
|
graph.id === SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH) &&
|
||||||
Boolean(graph.nodes[IMAGE_TO_LATENTS])
|
Boolean(graph.nodes[IMAGE_TO_LATENTS])
|
||||||
|
@ -1,369 +0,0 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import type { RootState } from 'app/store/store';
|
|
||||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
|
||||||
import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils';
|
|
||||||
import {
|
|
||||||
type ImageResizeInvocation,
|
|
||||||
type ImageToLatentsInvocation,
|
|
||||||
isNonRefinerMainModelConfig,
|
|
||||||
type NonNullableGraph,
|
|
||||||
} from 'services/api/types';
|
|
||||||
|
|
||||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
|
||||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
|
||||||
import { addLoRAsToGraph } from './addLoRAsToGraph';
|
|
||||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
|
||||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
|
||||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
|
||||||
import { addVAEToGraph } from './addVAEToGraph';
|
|
||||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
|
||||||
import {
|
|
||||||
CLIP_SKIP,
|
|
||||||
DENOISE_LATENTS,
|
|
||||||
IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
IMAGE_TO_LATENTS,
|
|
||||||
LATENTS_TO_IMAGE,
|
|
||||||
MAIN_MODEL_LOADER,
|
|
||||||
NEGATIVE_CONDITIONING,
|
|
||||||
NOISE,
|
|
||||||
POSITIVE_CONDITIONING,
|
|
||||||
RESIZE,
|
|
||||||
SEAMLESS,
|
|
||||||
} from './constants';
|
|
||||||
import { addCoreMetadataNode, getModelMetadataField } from './metadata';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the Image to Image tab graph.
|
|
||||||
*/
|
|
||||||
export const buildLinearImageToImageGraph = async (state: RootState): Promise<NonNullableGraph> => {
|
|
||||||
const log = logger('nodes');
|
|
||||||
const {
|
|
||||||
model,
|
|
||||||
cfgScale: cfg_scale,
|
|
||||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
|
||||||
scheduler,
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
initialImage,
|
|
||||||
img2imgStrength: strength,
|
|
||||||
shouldFitToWidthHeight,
|
|
||||||
clipSkip,
|
|
||||||
shouldUseCpuNoise,
|
|
||||||
vaePrecision,
|
|
||||||
seamlessXAxis,
|
|
||||||
seamlessYAxis,
|
|
||||||
} = state.generation;
|
|
||||||
const { positivePrompt, negativePrompt } = state.controlLayers.present;
|
|
||||||
const { width, height } = state.controlLayers.present.size;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
|
||||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
|
||||||
* ids.
|
|
||||||
*
|
|
||||||
* The only thing we need extra logic for is handling randomized seed, control net, and for img2img,
|
|
||||||
* the `fit` param. These are added to the graph at the end.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!initialImage) {
|
|
||||||
log.error('No initial image found in state');
|
|
||||||
throw new Error('No initial image found in state');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!model) {
|
|
||||||
log.error('No model found in state');
|
|
||||||
throw new Error('No model found in state');
|
|
||||||
}
|
|
||||||
|
|
||||||
const fp32 = vaePrecision === 'fp32';
|
|
||||||
const is_intermediate = true;
|
|
||||||
|
|
||||||
let modelLoaderNodeId = MAIN_MODEL_LOADER;
|
|
||||||
|
|
||||||
const use_cpu = shouldUseCpuNoise;
|
|
||||||
|
|
||||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
|
||||||
const graph: NonNullableGraph = {
|
|
||||||
id: IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
nodes: {
|
|
||||||
[modelLoaderNodeId]: {
|
|
||||||
type: 'main_model_loader',
|
|
||||||
id: modelLoaderNodeId,
|
|
||||||
model,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[CLIP_SKIP]: {
|
|
||||||
type: 'clip_skip',
|
|
||||||
id: CLIP_SKIP,
|
|
||||||
skipped_layers: clipSkip,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[POSITIVE_CONDITIONING]: {
|
|
||||||
type: 'compel',
|
|
||||||
id: POSITIVE_CONDITIONING,
|
|
||||||
prompt: positivePrompt,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[NEGATIVE_CONDITIONING]: {
|
|
||||||
type: 'compel',
|
|
||||||
id: NEGATIVE_CONDITIONING,
|
|
||||||
prompt: negativePrompt,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[NOISE]: {
|
|
||||||
type: 'noise',
|
|
||||||
id: NOISE,
|
|
||||||
use_cpu,
|
|
||||||
seed,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[LATENTS_TO_IMAGE]: {
|
|
||||||
type: 'l2i',
|
|
||||||
id: LATENTS_TO_IMAGE,
|
|
||||||
fp32,
|
|
||||||
is_intermediate: getIsIntermediate(state),
|
|
||||||
board: getBoardField(state),
|
|
||||||
},
|
|
||||||
[DENOISE_LATENTS]: {
|
|
||||||
type: 'denoise_latents',
|
|
||||||
id: DENOISE_LATENTS,
|
|
||||||
cfg_scale,
|
|
||||||
cfg_rescale_multiplier,
|
|
||||||
scheduler,
|
|
||||||
steps,
|
|
||||||
denoising_start: 1 - strength,
|
|
||||||
denoising_end: 1,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[IMAGE_TO_LATENTS]: {
|
|
||||||
type: 'i2l',
|
|
||||||
id: IMAGE_TO_LATENTS,
|
|
||||||
// must be set manually later, bc `fit` parameter may require a resize node inserted
|
|
||||||
// image: {
|
|
||||||
// image_name: initialImage.image_name,
|
|
||||||
// },
|
|
||||||
fp32,
|
|
||||||
is_intermediate,
|
|
||||||
use_cache: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
// Connect Model Loader to UNet and CLIP Skip
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'unet',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: DENOISE_LATENTS,
|
|
||||||
field: 'unet',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: CLIP_SKIP,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Connect CLIP Skip to Conditioning
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: CLIP_SKIP,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: POSITIVE_CONDITIONING,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: CLIP_SKIP,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: NEGATIVE_CONDITIONING,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Connect everything to Denoise Latents
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: POSITIVE_CONDITIONING,
|
|
||||||
field: 'conditioning',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: DENOISE_LATENTS,
|
|
||||||
field: 'positive_conditioning',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: NEGATIVE_CONDITIONING,
|
|
||||||
field: 'conditioning',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: DENOISE_LATENTS,
|
|
||||||
field: 'negative_conditioning',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: DENOISE_LATENTS,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: IMAGE_TO_LATENTS,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: DENOISE_LATENTS,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Decode denoised latents to image
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: DENOISE_LATENTS,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: LATENTS_TO_IMAGE,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle `fit`
|
|
||||||
if (shouldFitToWidthHeight && (initialImage.width !== width || initialImage.height !== height)) {
|
|
||||||
// The init image needs to be resized to the specified width and height before being passed to `IMAGE_TO_LATENTS`
|
|
||||||
|
|
||||||
// Create a resize node, explicitly setting its image
|
|
||||||
const resizeNode: ImageResizeInvocation = {
|
|
||||||
id: RESIZE,
|
|
||||||
type: 'img_resize',
|
|
||||||
image: {
|
|
||||||
image_name: initialImage.imageName,
|
|
||||||
},
|
|
||||||
is_intermediate: true,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
graph.nodes[RESIZE] = resizeNode;
|
|
||||||
|
|
||||||
// The `RESIZE` node then passes its image to `IMAGE_TO_LATENTS`
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: RESIZE, field: 'image' },
|
|
||||||
destination: {
|
|
||||||
node_id: IMAGE_TO_LATENTS,
|
|
||||||
field: 'image',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// The `RESIZE` node also passes its width and height to `NOISE`
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: RESIZE, field: 'width' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'width',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: RESIZE, field: 'height' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'height',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly
|
|
||||||
(graph.nodes[IMAGE_TO_LATENTS] as ImageToLatentsInvocation).image = {
|
|
||||||
image_name: initialImage.imageName,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pass the image's dimensions to the `NOISE` node
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: IMAGE_TO_LATENTS, field: 'width' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'width',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: IMAGE_TO_LATENTS, field: 'height' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'height',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
|
|
||||||
|
|
||||||
addCoreMetadataNode(
|
|
||||||
graph,
|
|
||||||
{
|
|
||||||
generation_mode: 'img2img',
|
|
||||||
cfg_scale,
|
|
||||||
cfg_rescale_multiplier,
|
|
||||||
height,
|
|
||||||
width,
|
|
||||||
positive_prompt: positivePrompt,
|
|
||||||
negative_prompt: negativePrompt,
|
|
||||||
model: getModelMetadataField(modelConfig),
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
rand_device: use_cpu ? 'cpu' : 'cuda',
|
|
||||||
scheduler,
|
|
||||||
clip_skip: clipSkip,
|
|
||||||
strength,
|
|
||||||
init_image: initialImage.imageName,
|
|
||||||
},
|
|
||||||
LATENTS_TO_IMAGE
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add Seamless To Graph
|
|
||||||
if (seamlessXAxis || seamlessYAxis) {
|
|
||||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
|
||||||
modelLoaderNodeId = SEAMLESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// optionally add custom VAE
|
|
||||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
|
||||||
|
|
||||||
// add LoRA support
|
|
||||||
await addLoRAsToGraph(state, graph, DENOISE_LATENTS, modelLoaderNodeId);
|
|
||||||
|
|
||||||
// add controlnet, mutating `graph`
|
|
||||||
await addControlNetToLinearGraph(state, graph, DENOISE_LATENTS);
|
|
||||||
|
|
||||||
// Add IP Adapter
|
|
||||||
await addIPAdapterToLinearGraph(state, graph, DENOISE_LATENTS);
|
|
||||||
await addT2IAdaptersToLinearGraph(state, graph, DENOISE_LATENTS);
|
|
||||||
|
|
||||||
// NSFW & watermark - must be last thing added to graph
|
|
||||||
if (state.system.shouldUseNSFWChecker) {
|
|
||||||
// must add before watermarker!
|
|
||||||
addNSFWCheckerToGraph(state, graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.system.shouldUseWatermarker) {
|
|
||||||
// must add after nsfw checker!
|
|
||||||
addWatermarkerToGraph(state, graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
return graph;
|
|
||||||
};
|
|
@ -1,390 +0,0 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
|
||||||
import type { RootState } from 'app/store/store';
|
|
||||||
import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers';
|
|
||||||
import {
|
|
||||||
type ImageResizeInvocation,
|
|
||||||
type ImageToLatentsInvocation,
|
|
||||||
isNonRefinerMainModelConfig,
|
|
||||||
type NonNullableGraph,
|
|
||||||
} from 'services/api/types';
|
|
||||||
|
|
||||||
import { addControlNetToLinearGraph } from './addControlNetToLinearGraph';
|
|
||||||
import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph';
|
|
||||||
import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph';
|
|
||||||
import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph';
|
|
||||||
import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph';
|
|
||||||
import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph';
|
|
||||||
import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph';
|
|
||||||
import { addVAEToGraph } from './addVAEToGraph';
|
|
||||||
import { addWatermarkerToGraph } from './addWatermarkerToGraph';
|
|
||||||
import {
|
|
||||||
IMAGE_TO_LATENTS,
|
|
||||||
LATENTS_TO_IMAGE,
|
|
||||||
NEGATIVE_CONDITIONING,
|
|
||||||
NOISE,
|
|
||||||
POSITIVE_CONDITIONING,
|
|
||||||
RESIZE,
|
|
||||||
SDXL_DENOISE_LATENTS,
|
|
||||||
SDXL_IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
SDXL_MODEL_LOADER,
|
|
||||||
SDXL_REFINER_SEAMLESS,
|
|
||||||
SEAMLESS,
|
|
||||||
} from './constants';
|
|
||||||
import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from './graphBuilderUtils';
|
|
||||||
import { addCoreMetadataNode, getModelMetadataField } from './metadata';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the Image to Image tab graph.
|
|
||||||
*/
|
|
||||||
export const buildLinearSDXLImageToImageGraph = async (state: RootState): Promise<NonNullableGraph> => {
|
|
||||||
const log = logger('nodes');
|
|
||||||
const {
|
|
||||||
model,
|
|
||||||
cfgScale: cfg_scale,
|
|
||||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
|
||||||
scheduler,
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
initialImage,
|
|
||||||
shouldFitToWidthHeight,
|
|
||||||
shouldUseCpuNoise,
|
|
||||||
vaePrecision,
|
|
||||||
seamlessXAxis,
|
|
||||||
seamlessYAxis,
|
|
||||||
img2imgStrength: strength,
|
|
||||||
} = state.generation;
|
|
||||||
const { positivePrompt, negativePrompt } = state.controlLayers.present;
|
|
||||||
const { width, height } = state.controlLayers.present.size;
|
|
||||||
|
|
||||||
const { refinerModel, refinerStart } = state.sdxl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
|
||||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
|
||||||
* ids.
|
|
||||||
*
|
|
||||||
* The only thing we need extra logic for is handling randomized seed, control net, and for img2img,
|
|
||||||
* the `fit` param. These are added to the graph at the end.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!initialImage) {
|
|
||||||
log.error('No initial image found in state');
|
|
||||||
throw new Error('No initial image found in state');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!model) {
|
|
||||||
log.error('No model found in state');
|
|
||||||
throw new Error('No model found in state');
|
|
||||||
}
|
|
||||||
|
|
||||||
const fp32 = vaePrecision === 'fp32';
|
|
||||||
const is_intermediate = true;
|
|
||||||
|
|
||||||
// Model Loader ID
|
|
||||||
let modelLoaderNodeId = SDXL_MODEL_LOADER;
|
|
||||||
|
|
||||||
const use_cpu = shouldUseCpuNoise;
|
|
||||||
|
|
||||||
// Construct Style Prompt
|
|
||||||
const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state);
|
|
||||||
|
|
||||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
|
||||||
const graph: NonNullableGraph = {
|
|
||||||
id: SDXL_IMAGE_TO_IMAGE_GRAPH,
|
|
||||||
nodes: {
|
|
||||||
[modelLoaderNodeId]: {
|
|
||||||
type: 'sdxl_model_loader',
|
|
||||||
id: modelLoaderNodeId,
|
|
||||||
model,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[POSITIVE_CONDITIONING]: {
|
|
||||||
type: 'sdxl_compel_prompt',
|
|
||||||
id: POSITIVE_CONDITIONING,
|
|
||||||
prompt: positivePrompt,
|
|
||||||
style: positiveStylePrompt,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[NEGATIVE_CONDITIONING]: {
|
|
||||||
type: 'sdxl_compel_prompt',
|
|
||||||
id: NEGATIVE_CONDITIONING,
|
|
||||||
prompt: negativePrompt,
|
|
||||||
style: negativeStylePrompt,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[NOISE]: {
|
|
||||||
type: 'noise',
|
|
||||||
id: NOISE,
|
|
||||||
use_cpu,
|
|
||||||
seed,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[LATENTS_TO_IMAGE]: {
|
|
||||||
type: 'l2i',
|
|
||||||
id: LATENTS_TO_IMAGE,
|
|
||||||
fp32,
|
|
||||||
is_intermediate: getIsIntermediate(state),
|
|
||||||
board: getBoardField(state),
|
|
||||||
},
|
|
||||||
[SDXL_DENOISE_LATENTS]: {
|
|
||||||
type: 'denoise_latents',
|
|
||||||
id: SDXL_DENOISE_LATENTS,
|
|
||||||
cfg_scale,
|
|
||||||
cfg_rescale_multiplier,
|
|
||||||
scheduler,
|
|
||||||
steps,
|
|
||||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
|
||||||
denoising_end: refinerModel ? refinerStart : 1,
|
|
||||||
is_intermediate,
|
|
||||||
},
|
|
||||||
[IMAGE_TO_LATENTS]: {
|
|
||||||
type: 'i2l',
|
|
||||||
id: IMAGE_TO_LATENTS,
|
|
||||||
// must be set manually later, bc `fit` parameter may require a resize node inserted
|
|
||||||
// image: {
|
|
||||||
// image_name: initialImage.image_name,
|
|
||||||
// },
|
|
||||||
fp32,
|
|
||||||
is_intermediate,
|
|
||||||
use_cache: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
edges: [
|
|
||||||
// Connect Model Loader to UNet, CLIP & VAE
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'unet',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: SDXL_DENOISE_LATENTS,
|
|
||||||
field: 'unet',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: POSITIVE_CONDITIONING,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'clip2',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: POSITIVE_CONDITIONING,
|
|
||||||
field: 'clip2',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: NEGATIVE_CONDITIONING,
|
|
||||||
field: 'clip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: modelLoaderNodeId,
|
|
||||||
field: 'clip2',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: NEGATIVE_CONDITIONING,
|
|
||||||
field: 'clip2',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Connect everything to Denoise Latents
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: POSITIVE_CONDITIONING,
|
|
||||||
field: 'conditioning',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: SDXL_DENOISE_LATENTS,
|
|
||||||
field: 'positive_conditioning',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: NEGATIVE_CONDITIONING,
|
|
||||||
field: 'conditioning',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: SDXL_DENOISE_LATENTS,
|
|
||||||
field: 'negative_conditioning',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: SDXL_DENOISE_LATENTS,
|
|
||||||
field: 'noise',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: IMAGE_TO_LATENTS,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: SDXL_DENOISE_LATENTS,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Decode Denoised Latents To Image
|
|
||||||
{
|
|
||||||
source: {
|
|
||||||
node_id: SDXL_DENOISE_LATENTS,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
destination: {
|
|
||||||
node_id: LATENTS_TO_IMAGE,
|
|
||||||
field: 'latents',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle `fit`
|
|
||||||
if (shouldFitToWidthHeight && (initialImage.width !== width || initialImage.height !== height)) {
|
|
||||||
// The init image needs to be resized to the specified width and height before being passed to `IMAGE_TO_LATENTS`
|
|
||||||
|
|
||||||
// Create a resize node, explicitly setting its image
|
|
||||||
const resizeNode: ImageResizeInvocation = {
|
|
||||||
id: RESIZE,
|
|
||||||
type: 'img_resize',
|
|
||||||
image: {
|
|
||||||
image_name: initialImage.imageName,
|
|
||||||
},
|
|
||||||
is_intermediate: true,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
|
|
||||||
graph.nodes[RESIZE] = resizeNode;
|
|
||||||
|
|
||||||
// The `RESIZE` node then passes its image to `IMAGE_TO_LATENTS`
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: RESIZE, field: 'image' },
|
|
||||||
destination: {
|
|
||||||
node_id: IMAGE_TO_LATENTS,
|
|
||||||
field: 'image',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// The `RESIZE` node also passes its width and height to `NOISE`
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: RESIZE, field: 'width' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'width',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: RESIZE, field: 'height' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'height',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly
|
|
||||||
(graph.nodes[IMAGE_TO_LATENTS] as ImageToLatentsInvocation).image = {
|
|
||||||
image_name: initialImage.imageName,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Pass the image's dimensions to the `NOISE` node
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: IMAGE_TO_LATENTS, field: 'width' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'width',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
graph.edges.push({
|
|
||||||
source: { node_id: IMAGE_TO_LATENTS, field: 'height' },
|
|
||||||
destination: {
|
|
||||||
node_id: NOISE,
|
|
||||||
field: 'height',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig);
|
|
||||||
|
|
||||||
addCoreMetadataNode(
|
|
||||||
graph,
|
|
||||||
{
|
|
||||||
generation_mode: 'sdxl_img2img',
|
|
||||||
cfg_scale,
|
|
||||||
cfg_rescale_multiplier,
|
|
||||||
height,
|
|
||||||
width,
|
|
||||||
positive_prompt: positivePrompt,
|
|
||||||
negative_prompt: negativePrompt,
|
|
||||||
model: getModelMetadataField(modelConfig),
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
rand_device: use_cpu ? 'cpu' : 'cuda',
|
|
||||||
scheduler,
|
|
||||||
strength,
|
|
||||||
init_image: initialImage.imageName,
|
|
||||||
positive_style_prompt: positiveStylePrompt,
|
|
||||||
negative_style_prompt: negativeStylePrompt,
|
|
||||||
},
|
|
||||||
LATENTS_TO_IMAGE
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add Seamless To Graph
|
|
||||||
if (seamlessXAxis || seamlessYAxis) {
|
|
||||||
addSeamlessToLinearGraph(state, graph, modelLoaderNodeId);
|
|
||||||
modelLoaderNodeId = SEAMLESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Refiner if enabled
|
|
||||||
if (refinerModel) {
|
|
||||||
await addSDXLRefinerToGraph(state, graph, SDXL_DENOISE_LATENTS);
|
|
||||||
if (seamlessXAxis || seamlessYAxis) {
|
|
||||||
modelLoaderNodeId = SDXL_REFINER_SEAMLESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// optionally add custom VAE
|
|
||||||
await addVAEToGraph(state, graph, modelLoaderNodeId);
|
|
||||||
|
|
||||||
// Add LoRA Support
|
|
||||||
await addSDXLLoRAsToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId);
|
|
||||||
|
|
||||||
// add controlnet, mutating `graph`
|
|
||||||
await addControlNetToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
|
||||||
|
|
||||||
// Add IP Adapter
|
|
||||||
await addIPAdapterToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
|
||||||
|
|
||||||
await addT2IAdaptersToLinearGraph(state, graph, SDXL_DENOISE_LATENTS);
|
|
||||||
|
|
||||||
// NSFW & watermark - must be last thing added to graph
|
|
||||||
if (state.system.shouldUseNSFWChecker) {
|
|
||||||
// must add before watermarker!
|
|
||||||
addNSFWCheckerToGraph(state, graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.system.shouldUseWatermarker) {
|
|
||||||
// must add after nsfw checker!
|
|
||||||
addWatermarkerToGraph(state, graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
return graph;
|
|
||||||
};
|
|
@ -57,14 +57,10 @@ export const NEGATIVE_CONDITIONING_COLLECT = 'negative_conditioning_collect';
|
|||||||
// friendly graph ids
|
// friendly graph ids
|
||||||
export const CONTROL_LAYERS_GRAPH = 'control_layers_graph';
|
export const CONTROL_LAYERS_GRAPH = 'control_layers_graph';
|
||||||
export const SDXL_CONTROL_LAYERS_GRAPH = 'sdxl_control_layers_graph';
|
export const SDXL_CONTROL_LAYERS_GRAPH = 'sdxl_control_layers_graph';
|
||||||
export const TEXT_TO_IMAGE_GRAPH = 'text_to_image_graph';
|
|
||||||
export const IMAGE_TO_IMAGE_GRAPH = 'image_to_image_graph';
|
|
||||||
export const CANVAS_TEXT_TO_IMAGE_GRAPH = 'canvas_text_to_image_graph';
|
export const CANVAS_TEXT_TO_IMAGE_GRAPH = 'canvas_text_to_image_graph';
|
||||||
export const CANVAS_IMAGE_TO_IMAGE_GRAPH = 'canvas_image_to_image_graph';
|
export const CANVAS_IMAGE_TO_IMAGE_GRAPH = 'canvas_image_to_image_graph';
|
||||||
export const CANVAS_INPAINT_GRAPH = 'canvas_inpaint_graph';
|
export const CANVAS_INPAINT_GRAPH = 'canvas_inpaint_graph';
|
||||||
export const CANVAS_OUTPAINT_GRAPH = 'canvas_outpaint_graph';
|
export const CANVAS_OUTPAINT_GRAPH = 'canvas_outpaint_graph';
|
||||||
export const SDXL_TEXT_TO_IMAGE_GRAPH = 'sdxl_text_to_image_graph';
|
|
||||||
export const SDXL_IMAGE_TO_IMAGE_GRAPH = 'sxdl_image_to_image_graph';
|
|
||||||
export const SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH = 'sdxl_canvas_text_to_image_graph';
|
export const SDXL_CANVAS_TEXT_TO_IMAGE_GRAPH = 'sdxl_canvas_text_to_image_graph';
|
||||||
export const SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH = 'sdxl_canvas_image_to_image_graph';
|
export const SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH = 'sdxl_canvas_image_to_image_graph';
|
||||||
export const SDXL_CANVAS_INPAINT_GRAPH = 'sdxl_canvas_inpaint_graph';
|
export const SDXL_CANVAS_INPAINT_GRAPH = 'sdxl_canvas_inpaint_graph';
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
|
||||||
import type { RootState } from 'app/store/store';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
|
||||||
import { setShouldFitToWidthHeight } from 'features/parameters/store/generationSlice';
|
|
||||||
import type { ChangeEvent } from 'react';
|
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
const ImageToImageFit = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const shouldFitToWidthHeight = useAppSelector((state: RootState) => state.generation.shouldFitToWidthHeight);
|
|
||||||
|
|
||||||
const handleChangeFit = useCallback(
|
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
dispatch(setShouldFitToWidthHeight(e.target.checked));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FormControl w="full">
|
|
||||||
<InformationalPopover feature="imageFit">
|
|
||||||
<FormLabel flexGrow={1}>{t('parameters.imageFit')}</FormLabel>
|
|
||||||
</InformationalPopover>
|
|
||||||
<Switch isChecked={shouldFitToWidthHeight} onChange={handleChangeFit} />
|
|
||||||
</FormControl>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ImageToImageFit);
|
|
@ -1,61 +0,0 @@
|
|||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
|
||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
|
||||||
import { clearInitialImage, selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
|
||||||
import { memo, useEffect, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
|
||||||
|
|
||||||
const selectInitialImage = createMemoizedSelector(selectGenerationSlice, (generation) => generation.initialImage);
|
|
||||||
|
|
||||||
const InitialImage = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const initialImage = useAppSelector(selectInitialImage);
|
|
||||||
const isConnected = useAppSelector((s) => s.system.isConnected);
|
|
||||||
|
|
||||||
const { currentData: imageDTO, isError } = useGetImageDTOQuery(initialImage?.imageName ?? skipToken);
|
|
||||||
|
|
||||||
const draggableData = useMemo<TypesafeDraggableData | undefined>(() => {
|
|
||||||
if (imageDTO) {
|
|
||||||
return {
|
|
||||||
id: 'initial-image',
|
|
||||||
payloadType: 'IMAGE_DTO',
|
|
||||||
payload: { imageDTO },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, [imageDTO]);
|
|
||||||
|
|
||||||
const droppableData = useMemo<TypesafeDroppableData | undefined>(
|
|
||||||
() => ({
|
|
||||||
id: 'initial-image',
|
|
||||||
actionType: 'SET_INITIAL_IMAGE',
|
|
||||||
}),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isError && isConnected) {
|
|
||||||
// The image doesn't exist, reset init image
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
}
|
|
||||||
}, [dispatch, isConnected, isError]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IAIDndImage
|
|
||||||
imageDTO={imageDTO}
|
|
||||||
droppableData={droppableData}
|
|
||||||
draggableData={draggableData}
|
|
||||||
isUploadDisabled={true}
|
|
||||||
fitContainer
|
|
||||||
dropLabel={t('toast.setInitialImage')}
|
|
||||||
noContentFallback={<IAINoContentFallback label={t('parameters.invoke.noInitialImageSelected')} />}
|
|
||||||
dataTestId="initial-image"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(InitialImage);
|
|
@ -1,87 +0,0 @@
|
|||||||
import { Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
|
||||||
import { parseAndRecallImageDimensions } from 'features/metadata/util/handlers';
|
|
||||||
import { clearInitialImage, selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { PiArrowCounterClockwiseBold, PiRulerBold, PiUploadSimpleBold } from 'react-icons/pi';
|
|
||||||
import type { PostUploadAction } from 'services/api/types';
|
|
||||||
|
|
||||||
import InitialImage from './InitialImage';
|
|
||||||
|
|
||||||
const selectInitialImage = createMemoizedSelector(selectGenerationSlice, (generation) => generation.initialImage);
|
|
||||||
|
|
||||||
const postUploadAction: PostUploadAction = {
|
|
||||||
type: 'SET_INITIAL_IMAGE',
|
|
||||||
};
|
|
||||||
|
|
||||||
const InitialImageDisplay = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const initialImage = useAppSelector(selectInitialImage);
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({
|
|
||||||
postUploadAction,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleReset = useCallback(() => {
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const handleUseSizeInitialImage = useCallback(() => {
|
|
||||||
if (initialImage) {
|
|
||||||
parseAndRecallImageDimensions(initialImage);
|
|
||||||
}
|
|
||||||
}, [initialImage]);
|
|
||||||
|
|
||||||
useHotkeys('shift+d', handleUseSizeInitialImage, [initialImage]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
layerStyle="first"
|
|
||||||
position="relative"
|
|
||||||
flexDirection="column"
|
|
||||||
height="full"
|
|
||||||
width="full"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
borderRadius="base"
|
|
||||||
p={2}
|
|
||||||
gap={4}
|
|
||||||
>
|
|
||||||
<Flex w="full" flexWrap="wrap" justifyContent="center" alignItems="center" gap={2}>
|
|
||||||
<Text ps={2} fontWeight="semibold" userSelect="none" color="base.200">
|
|
||||||
{t('metadata.initImage')}
|
|
||||||
</Text>
|
|
||||||
<Spacer />
|
|
||||||
<IconButton
|
|
||||||
tooltip={t('toast.uploadInitialImage')}
|
|
||||||
aria-label={t('toast.uploadInitialImage')}
|
|
||||||
icon={<PiUploadSimpleBold />}
|
|
||||||
{...getUploadButtonProps()}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
tooltip={`${t('parameters.useSize')} (Shift+D)`}
|
|
||||||
aria-label={`${t('parameters.useSize')} (Shift+D)`}
|
|
||||||
icon={<PiRulerBold />}
|
|
||||||
onClick={handleUseSizeInitialImage}
|
|
||||||
isDisabled={!initialImage}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
tooltip={t('toast.resetInitialImage')}
|
|
||||||
aria-label={t('toast.resetInitialImage')}
|
|
||||||
icon={<PiArrowCounterClockwiseBold />}
|
|
||||||
onClick={handleReset}
|
|
||||||
isDisabled={!initialImage}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<InitialImage />
|
|
||||||
<input {...getUploadInputProps()} />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(InitialImageDisplay);
|
|
@ -2,8 +2,8 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
|||||||
import { useAppToaster } from 'app/components/Toaster';
|
import { useAppToaster } from 'app/components/Toaster';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { iiLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
|
import { parseAndRecallAllMetadata } from 'features/metadata/util/handlers';
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
@ -37,7 +37,7 @@ export const usePreselectedImage = (selectedImage?: {
|
|||||||
|
|
||||||
const handleSendToImg2Img = useCallback(() => {
|
const handleSendToImg2Img = useCallback(() => {
|
||||||
if (selectedImageDto) {
|
if (selectedImageDto) {
|
||||||
dispatch(initialImageSelected(selectedImageDto));
|
dispatch(iiLayerAdded(selectedImageDto));
|
||||||
}
|
}
|
||||||
}, [dispatch, selectedImageDto]);
|
}, [dispatch, selectedImageDto]);
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { createAction } from '@reduxjs/toolkit';
|
import { createAction } from '@reduxjs/toolkit';
|
||||||
import type { ParameterModel } from 'features/parameters/types/parameterSchemas';
|
import type { ParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
|
|
||||||
export const initialImageSelected = createAction<ImageDTO | undefined>('generation/initialImageSelected');
|
|
||||||
|
|
||||||
export const modelSelected = createAction<ParameterModel>('generation/modelSelected');
|
export const modelSelected = createAction<ParameterModel>('generation/modelSelected');
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import { getOptimalDimension } from 'features/parameters/util/optimalDimension';
|
|||||||
import { configChanged } from 'features/system/store/configSlice';
|
import { configChanged } from 'features/system/store/configSlice';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
import type { RgbaColor } from 'react-colorful';
|
import type { RgbaColor } from 'react-colorful';
|
||||||
import type { ImageDTO } from 'services/api/types';
|
|
||||||
|
|
||||||
import type { GenerationState } from './types';
|
import type { GenerationState } from './types';
|
||||||
|
|
||||||
@ -34,7 +33,6 @@ const initialGenerationState: GenerationState = {
|
|||||||
canvasCoherenceMinDenoise: 0,
|
canvasCoherenceMinDenoise: 0,
|
||||||
canvasCoherenceEdgeSize: 16,
|
canvasCoherenceEdgeSize: 16,
|
||||||
seed: 0,
|
seed: 0,
|
||||||
shouldFitToWidthHeight: true,
|
|
||||||
shouldRandomizeSeed: true,
|
shouldRandomizeSeed: true,
|
||||||
steps: 50,
|
steps: 50,
|
||||||
model: null,
|
model: null,
|
||||||
@ -86,15 +84,9 @@ export const generationSlice = createSlice({
|
|||||||
setSeamlessYAxis: (state, action: PayloadAction<boolean>) => {
|
setSeamlessYAxis: (state, action: PayloadAction<boolean>) => {
|
||||||
state.seamlessYAxis = action.payload;
|
state.seamlessYAxis = action.payload;
|
||||||
},
|
},
|
||||||
setShouldFitToWidthHeight: (state, action: PayloadAction<boolean>) => {
|
|
||||||
state.shouldFitToWidthHeight = action.payload;
|
|
||||||
},
|
|
||||||
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
|
setShouldRandomizeSeed: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldRandomizeSeed = action.payload;
|
state.shouldRandomizeSeed = action.payload;
|
||||||
},
|
},
|
||||||
clearInitialImage: (state) => {
|
|
||||||
state.initialImage = undefined;
|
|
||||||
},
|
|
||||||
setMaskBlur: (state, action: PayloadAction<number>) => {
|
setMaskBlur: (state, action: PayloadAction<number>) => {
|
||||||
state.maskBlur = action.payload;
|
state.maskBlur = action.payload;
|
||||||
},
|
},
|
||||||
@ -107,10 +99,6 @@ export const generationSlice = createSlice({
|
|||||||
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
|
setCanvasCoherenceMinDenoise: (state, action: PayloadAction<number>) => {
|
||||||
state.canvasCoherenceMinDenoise = action.payload;
|
state.canvasCoherenceMinDenoise = action.payload;
|
||||||
},
|
},
|
||||||
initialImageChanged: (state, action: PayloadAction<ImageDTO>) => {
|
|
||||||
const { image_name, width, height } = action.payload;
|
|
||||||
state.initialImage = { imageName: image_name, width, height };
|
|
||||||
},
|
|
||||||
modelChanged: {
|
modelChanged: {
|
||||||
reducer: (
|
reducer: (
|
||||||
state,
|
state,
|
||||||
@ -195,7 +183,6 @@ export const generationSlice = createSlice({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
clearInitialImage,
|
|
||||||
setCfgScale,
|
setCfgScale,
|
||||||
setCfgRescaleMultiplier,
|
setCfgRescaleMultiplier,
|
||||||
setImg2imgStrength,
|
setImg2imgStrength,
|
||||||
@ -207,10 +194,8 @@ export const {
|
|||||||
setCanvasCoherenceEdgeSize,
|
setCanvasCoherenceEdgeSize,
|
||||||
setCanvasCoherenceMinDenoise,
|
setCanvasCoherenceMinDenoise,
|
||||||
setSeed,
|
setSeed,
|
||||||
setShouldFitToWidthHeight,
|
|
||||||
setShouldRandomizeSeed,
|
setShouldRandomizeSeed,
|
||||||
setSteps,
|
setSteps,
|
||||||
initialImageChanged,
|
|
||||||
modelChanged,
|
modelChanged,
|
||||||
vaeSelected,
|
vaeSelected,
|
||||||
setSeamlessXAxis,
|
setSeamlessXAxis,
|
||||||
|
@ -20,7 +20,6 @@ export interface GenerationState {
|
|||||||
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
|
cfgRescaleMultiplier: ParameterCFGRescaleMultiplier;
|
||||||
img2imgStrength: ParameterStrength;
|
img2imgStrength: ParameterStrength;
|
||||||
infillMethod: string;
|
infillMethod: string;
|
||||||
initialImage?: { imageName: string; width: number; height: number };
|
|
||||||
iterations: number;
|
iterations: number;
|
||||||
scheduler: ParameterScheduler;
|
scheduler: ParameterScheduler;
|
||||||
maskBlur: number;
|
maskBlur: number;
|
||||||
@ -29,7 +28,6 @@ export interface GenerationState {
|
|||||||
canvasCoherenceMinDenoise: ParameterStrength;
|
canvasCoherenceMinDenoise: ParameterStrength;
|
||||||
canvasCoherenceEdgeSize: number;
|
canvasCoherenceEdgeSize: number;
|
||||||
seed: ParameterSeed;
|
seed: ParameterSeed;
|
||||||
shouldFitToWidthHeight: boolean;
|
|
||||||
shouldRandomizeSeed: boolean;
|
shouldRandomizeSeed: boolean;
|
||||||
steps: ParameterSteps;
|
steps: ParameterSteps;
|
||||||
model: ParameterModel | null;
|
model: ParameterModel | null;
|
||||||
|
@ -9,7 +9,6 @@ import { selectHrfSlice } from 'features/hrf/store/hrfSlice';
|
|||||||
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing';
|
||||||
import ParamScaledHeight from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight';
|
import ParamScaledHeight from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight';
|
||||||
import ParamScaledWidth from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth';
|
import ParamScaledWidth from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth';
|
||||||
import ImageToImageFit from 'features/parameters/components/ImageToImage/ImageToImageFit';
|
|
||||||
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength';
|
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength';
|
||||||
import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput';
|
import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput';
|
||||||
import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
|
import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize';
|
||||||
@ -94,8 +93,7 @@ export const ImageSettingsAccordion = memo(() => {
|
|||||||
<ParamSeedShuffle />
|
<ParamSeedShuffle />
|
||||||
<ParamSeedRandomize />
|
<ParamSeedRandomize />
|
||||||
</Flex>
|
</Flex>
|
||||||
{(activeTabName === 'img2img' || activeTabName === 'unifiedCanvas') && <ImageToImageStrength />}
|
{activeTabName === 'unifiedCanvas' && <ImageToImageStrength />}
|
||||||
{activeTabName === 'img2img' && <ImageToImageFit />}
|
|
||||||
{activeTabName === 'txt2img' && !isSDXL && <HrfSettings />}
|
{activeTabName === 'txt2img' && !isSDXL && <HrfSettings />}
|
||||||
{activeTabName === 'unifiedCanvas' && (
|
{activeTabName === 'unifiedCanvas' && (
|
||||||
<>
|
<>
|
||||||
|
@ -12,7 +12,6 @@ import { selectConfigSlice } from 'features/system/store/configSlice';
|
|||||||
import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
|
import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton';
|
||||||
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
|
import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons';
|
||||||
import ParametersPanelTextToImage from 'features/ui/components/ParametersPanelTextToImage';
|
import ParametersPanelTextToImage from 'features/ui/components/ParametersPanelTextToImage';
|
||||||
import ImageToImageTab from 'features/ui/components/tabs/ImageToImageTab';
|
|
||||||
import ModelManagerTab from 'features/ui/components/tabs/ModelManagerTab';
|
import ModelManagerTab from 'features/ui/components/tabs/ModelManagerTab';
|
||||||
import NodesTab from 'features/ui/components/tabs/NodesTab';
|
import NodesTab from 'features/ui/components/tabs/NodesTab';
|
||||||
import QueueTab from 'features/ui/components/tabs/QueueTab';
|
import QueueTab from 'features/ui/components/tabs/QueueTab';
|
||||||
@ -30,7 +29,7 @@ import { memo, useCallback, useMemo, useRef } from 'react';
|
|||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiFlowArrowBold } from 'react-icons/pi';
|
import { PiFlowArrowBold } from 'react-icons/pi';
|
||||||
import { RiBox2Line, RiBrushLine, RiImage2Line, RiInputMethodLine, RiPlayList2Fill } from 'react-icons/ri';
|
import { RiBox2Line, RiBrushLine, RiInputMethodLine, RiPlayList2Fill } from 'react-icons/ri';
|
||||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
import { Panel, PanelGroup } from 'react-resizable-panels';
|
||||||
|
|
||||||
@ -51,12 +50,6 @@ const TAB_DATA: Record<InvokeTabName, TabData> = {
|
|||||||
icon: <RiInputMethodLine />,
|
icon: <RiInputMethodLine />,
|
||||||
content: <TextToImageTab />,
|
content: <TextToImageTab />,
|
||||||
},
|
},
|
||||||
img2img: {
|
|
||||||
id: 'img2img',
|
|
||||||
translationKey: 'common.img2img',
|
|
||||||
icon: <RiImage2Line />,
|
|
||||||
content: <ImageToImageTab />,
|
|
||||||
},
|
|
||||||
unifiedCanvas: {
|
unifiedCanvas: {
|
||||||
id: 'unifiedCanvas',
|
id: 'unifiedCanvas',
|
||||||
translationKey: 'common.unifiedCanvas',
|
translationKey: 'common.unifiedCanvas',
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
import { Box, Flex } from '@invoke-ai/ui-library';
|
|
||||||
import CurrentImageDisplay from 'features/gallery/components/CurrentImage/CurrentImageDisplay';
|
|
||||||
import InitialImageDisplay from 'features/parameters/components/ImageToImage/InitialImageDisplay';
|
|
||||||
import ResizeHandle from 'features/ui/components/tabs/ResizeHandle';
|
|
||||||
import { usePanelStorage } from 'features/ui/hooks/usePanelStorage';
|
|
||||||
import type { CSSProperties } from 'react';
|
|
||||||
import { memo, useCallback, useRef } from 'react';
|
|
||||||
import type { ImperativePanelGroupHandle } from 'react-resizable-panels';
|
|
||||||
import { Panel, PanelGroup } from 'react-resizable-panels';
|
|
||||||
|
|
||||||
const panelGroupStyles: CSSProperties = {
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
};
|
|
||||||
const panelStyles: CSSProperties = {
|
|
||||||
position: 'relative',
|
|
||||||
};
|
|
||||||
|
|
||||||
const ImageToImageTab = () => {
|
|
||||||
const panelGroupRef = useRef<ImperativePanelGroupHandle>(null);
|
|
||||||
|
|
||||||
const handleDoubleClickHandle = useCallback(() => {
|
|
||||||
if (!panelGroupRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
panelGroupRef.current.setLayout([50, 50]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const panelStorage = usePanelStorage();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box position="relative" w="full" h="full">
|
|
||||||
<PanelGroup
|
|
||||||
ref={panelGroupRef}
|
|
||||||
autoSaveId="imageTab.content"
|
|
||||||
direction="horizontal"
|
|
||||||
style={panelGroupStyles}
|
|
||||||
storage={panelStorage}
|
|
||||||
>
|
|
||||||
<Panel id="imageTab.content.initImage" order={0} defaultSize={50} minSize={25} style={panelStyles}>
|
|
||||||
<InitialImageDisplay />
|
|
||||||
</Panel>
|
|
||||||
<ResizeHandle orientation="vertical" onDoubleClick={handleDoubleClickHandle} />
|
|
||||||
<Panel id="imageTab.content.selectedImage" order={1} defaultSize={50} minSize={25}>
|
|
||||||
<Box layerStyle="first" position="relative" w="full" h="full" p={2} borderRadius="base">
|
|
||||||
<Flex w="full" h="full">
|
|
||||||
<CurrentImageDisplay />
|
|
||||||
</Flex>
|
|
||||||
</Box>
|
|
||||||
</Panel>
|
|
||||||
</PanelGroup>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ImageToImageTab);
|
|
@ -1,3 +1,3 @@
|
|||||||
export const TAB_NUMBER_MAP = ['txt2img', 'img2img', 'unifiedCanvas', 'nodes', 'modelManager', 'queue'] as const;
|
export const TAB_NUMBER_MAP = ['txt2img', 'unifiedCanvas', 'nodes', 'modelManager', 'queue'] as const;
|
||||||
|
|
||||||
export type InvokeTabName = (typeof TAB_NUMBER_MAP)[number];
|
export type InvokeTabName = (typeof TAB_NUMBER_MAP)[number];
|
||||||
|
@ -2,7 +2,6 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
import { workflowLoadRequested } from 'features/nodes/store/actions';
|
import { workflowLoadRequested } from 'features/nodes/store/actions';
|
||||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
|
||||||
|
|
||||||
import type { InvokeTabName } from './tabMap';
|
import type { InvokeTabName } from './tabMap';
|
||||||
import type { UIState } from './uiTypes';
|
import type { UIState } from './uiTypes';
|
||||||
@ -43,9 +42,6 @@ export const uiSlice = createSlice({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
extraReducers(builder) {
|
extraReducers(builder) {
|
||||||
builder.addCase(initialImageChanged, (state) => {
|
|
||||||
state.activeTab = 'img2img';
|
|
||||||
});
|
|
||||||
builder.addCase(workflowLoadRequested, (state) => {
|
builder.addCase(workflowLoadRequested, (state) => {
|
||||||
state.activeTab = 'nodes';
|
state.activeTab = 'nodes';
|
||||||
});
|
});
|
||||||
|
@ -198,10 +198,6 @@ export type IILayerImagePostUploadAction = {
|
|||||||
layerId: string;
|
layerId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type InitialImageAction = {
|
|
||||||
type: 'SET_INITIAL_IMAGE';
|
|
||||||
};
|
|
||||||
|
|
||||||
type NodesAction = {
|
type NodesAction = {
|
||||||
type: 'SET_NODES_IMAGE';
|
type: 'SET_NODES_IMAGE';
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
@ -223,7 +219,6 @@ type AddToBatchAction = {
|
|||||||
|
|
||||||
export type PostUploadAction =
|
export type PostUploadAction =
|
||||||
| ControlAdapterAction
|
| ControlAdapterAction
|
||||||
| InitialImageAction
|
|
||||||
| NodesAction
|
| NodesAction
|
||||||
| CanvasInitialImageAction
|
| CanvasInitialImageAction
|
||||||
| ToastAction
|
| ToastAction
|
||||||
|
Reference in New Issue
Block a user