mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into release/make-web-dist-startable
This commit is contained in:
@ -23,8 +23,8 @@
|
||||
"dev": "concurrently \"vite dev\" \"yarn run theme:watch\"",
|
||||
"dev:host": "concurrently \"vite dev --host\" \"yarn run theme:watch\"",
|
||||
"build": "yarn run lint && vite build",
|
||||
"api:web": "openapi -i http://localhost:9090/openapi.json -o src/services/api --client axios --useOptions --useUnionTypes --exportSchemas true --indent 2 --request src/services/fixtures/request.ts",
|
||||
"api:file": "openapi -i src/services/fixtures/openapi.json -o src/services/api --client axios --useOptions --useUnionTypes --exportSchemas true --indent 2 --request src/services/fixtures/request.ts",
|
||||
"api:web": "openapi -i http://localhost:9090/openapi.json -o src/services/api --client axios --useOptions --useUnionTypes --indent 2 --request src/services/fixtures/request.ts",
|
||||
"api:file": "openapi -i src/services/fixtures/openapi.json -o src/services/api --client axios --useOptions --useUnionTypes --indent 2 --request src/services/fixtures/request.ts",
|
||||
"preview": "vite preview",
|
||||
"lint:madge": "madge --circular src/main.tsx",
|
||||
"lint:eslint": "eslint --max-warnings=0 .",
|
||||
|
@ -10,7 +10,7 @@ export const readinessSelector = createSelector(
|
||||
[generationSelector, systemSelector, activeTabNameSelector],
|
||||
(generation, system, activeTabName) => {
|
||||
const {
|
||||
prompt,
|
||||
positivePrompt: prompt,
|
||||
shouldGenerateVariations,
|
||||
seedWeights,
|
||||
initialImage,
|
||||
|
@ -5,7 +5,6 @@ import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { imageUploaded } from 'services/thunks/image';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||
import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
|
||||
@ -66,7 +65,7 @@ export const addCanvasMergedListener = () => {
|
||||
action.meta.arg.formData.file.name === filename
|
||||
);
|
||||
|
||||
const mergedCanvasImage = deserializeImageResponse(payload.response);
|
||||
const mergedCanvasImage = payload.response;
|
||||
|
||||
dispatch(
|
||||
setMergedCanvas({
|
||||
|
@ -17,24 +17,24 @@ export const addRequestedImageDeletionListener = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, type } = image;
|
||||
const { image_name, image_type } = image;
|
||||
|
||||
if (type !== 'uploads' && type !== 'results') {
|
||||
moduleLog.warn({ data: image }, `Invalid image type ${type}`);
|
||||
if (image_type !== 'uploads' && image_type !== 'results') {
|
||||
moduleLog.warn({ data: image }, `Invalid image type ${image_type}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedImageName = getState().gallery.selectedImage?.name;
|
||||
const selectedImageName = getState().gallery.selectedImage?.image_name;
|
||||
|
||||
if (selectedImageName === name) {
|
||||
const allIds = getState()[type].ids;
|
||||
const allEntities = getState()[type].entities;
|
||||
if (selectedImageName === image_name) {
|
||||
const allIds = getState()[image_type].ids;
|
||||
const allEntities = getState()[image_type].entities;
|
||||
|
||||
const deletedImageIndex = allIds.findIndex(
|
||||
(result) => result.toString() === name
|
||||
(result) => result.toString() === image_name
|
||||
);
|
||||
|
||||
const filteredIds = allIds.filter((id) => id.toString() !== name);
|
||||
const filteredIds = allIds.filter((id) => id.toString() !== image_name);
|
||||
|
||||
const newSelectedImageIndex = clamp(
|
||||
deletedImageIndex,
|
||||
@ -53,7 +53,7 @@ export const addRequestedImageDeletionListener = () => {
|
||||
}
|
||||
}
|
||||
|
||||
dispatch(imageDeleted({ imageName: name, imageType: type }));
|
||||
dispatch(imageDeleted({ imageName: image_name, imageType: image_type }));
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||
import { startAppListening } from '..';
|
||||
import { uploadAdded } from 'features/gallery/store/uploadsSlice';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
@ -7,6 +6,7 @@ import { addToast } from 'features/system/store/systemSlice';
|
||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import { resultAdded } from 'features/gallery/store/resultsSlice';
|
||||
import { isResultsImageDTO, isUploadsImageDTO } from 'services/types/guards';
|
||||
|
||||
export const addImageUploadedListener = () => {
|
||||
startAppListening({
|
||||
@ -14,13 +14,11 @@ export const addImageUploadedListener = () => {
|
||||
imageUploaded.fulfilled.match(action) &&
|
||||
action.payload.response.image_type !== 'intermediates',
|
||||
effect: (action, { dispatch, getState }) => {
|
||||
const { response } = action.payload;
|
||||
const { imageType } = action.meta.arg;
|
||||
const { response: image } = action.payload;
|
||||
|
||||
const state = getState();
|
||||
const image = deserializeImageResponse(response);
|
||||
|
||||
if (imageType === 'uploads') {
|
||||
if (isUploadsImageDTO(image)) {
|
||||
dispatch(uploadAdded(image));
|
||||
|
||||
dispatch(addToast({ title: 'Image Uploaded', status: 'success' }));
|
||||
@ -38,7 +36,7 @@ export const addImageUploadedListener = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (imageType === 'results') {
|
||||
if (isResultsImageDTO(image)) {
|
||||
dispatch(resultAdded(image));
|
||||
}
|
||||
},
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||
import { Image, isInvokeAIImage } from 'app/types/invokeai';
|
||||
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
||||
import { selectUploadsById } from 'features/gallery/store/uploadsSlice';
|
||||
import { t } from 'i18next';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { startAppListening } from '..';
|
||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
||||
import {
|
||||
initialImageSelected,
|
||||
isImageDTO,
|
||||
} from 'features/parameters/store/actions';
|
||||
import { makeToast } from 'app/components/Toaster';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export const addInitialImageSelectedListener = () => {
|
||||
startAppListening({
|
||||
@ -21,21 +24,21 @@ export const addInitialImageSelectedListener = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isInvokeAIImage(action.payload)) {
|
||||
if (isImageDTO(action.payload)) {
|
||||
dispatch(initialImageChanged(action.payload));
|
||||
dispatch(addToast(makeToast(t('toast.sentToImageToImage'))));
|
||||
return;
|
||||
}
|
||||
|
||||
const { name, type } = action.payload;
|
||||
const { image_name, image_type } = action.payload;
|
||||
|
||||
let image: Image | undefined;
|
||||
let image: ImageDTO | undefined;
|
||||
const state = getState();
|
||||
|
||||
if (type === 'results') {
|
||||
image = selectResultsById(state, name);
|
||||
} else if (type === 'uploads') {
|
||||
image = selectUploadsById(state, name);
|
||||
if (image_type === 'results') {
|
||||
image = selectResultsById(state, image_name);
|
||||
} else if (image_type === 'uploads') {
|
||||
image = selectUploadsById(state, image_name);
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
|
@ -1,14 +1,10 @@
|
||||
import { invocationComplete } from 'services/events/actions';
|
||||
import { isImageOutput } from 'services/types/guards';
|
||||
import {
|
||||
buildImageUrls,
|
||||
extractTimestampFromImageName,
|
||||
} from 'services/util/deserializeImageField';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { resultAdded } from 'features/gallery/store/resultsSlice';
|
||||
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
||||
imageMetadataReceived,
|
||||
imageUrlsReceived,
|
||||
} from 'services/thunks/image';
|
||||
import { startAppListening } from '..';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
|
||||
|
||||
const nodeDenylist = ['dataURL_image'];
|
||||
@ -24,62 +20,40 @@ export const addImageResultReceivedListener = () => {
|
||||
}
|
||||
return false;
|
||||
},
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
effect: async (action, { getState, dispatch, take }) => {
|
||||
if (!invocationComplete.match(action)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data, shouldFetchImages } = action.payload;
|
||||
const { data } = action.payload;
|
||||
const { result, node, graph_execution_state_id } = data;
|
||||
|
||||
if (isImageOutput(result) && !nodeDenylist.includes(node.type)) {
|
||||
const name = result.image.image_name;
|
||||
const type = result.image.image_type;
|
||||
const state = getState();
|
||||
const { image_name, image_type } = result.image;
|
||||
|
||||
// if we need to refetch, set URLs to placeholder for now
|
||||
const { url, thumbnail } = shouldFetchImages
|
||||
? { url: '', thumbnail: '' }
|
||||
: buildImageUrls(type, name);
|
||||
dispatch(
|
||||
imageUrlsReceived({ imageName: image_name, imageType: image_type })
|
||||
);
|
||||
|
||||
const timestamp = extractTimestampFromImageName(name);
|
||||
|
||||
const image: Image = {
|
||||
name,
|
||||
type,
|
||||
url,
|
||||
thumbnail,
|
||||
metadata: {
|
||||
created: timestamp,
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
invokeai: {
|
||||
session_id: graph_execution_state_id,
|
||||
...(node ? { node } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
dispatch(resultAdded(image));
|
||||
|
||||
if (state.gallery.shouldAutoSwitchToNewImages) {
|
||||
dispatch(imageSelected(image));
|
||||
}
|
||||
|
||||
if (state.config.shouldFetchImages) {
|
||||
dispatch(imageReceived({ imageName: name, imageType: type }));
|
||||
dispatch(
|
||||
thumbnailReceived({
|
||||
thumbnailName: name,
|
||||
thumbnailType: type,
|
||||
})
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
imageMetadataReceived({
|
||||
imageName: image_name,
|
||||
imageType: image_type,
|
||||
})
|
||||
);
|
||||
|
||||
// Handle canvas image
|
||||
if (
|
||||
graph_execution_state_id ===
|
||||
state.canvas.layerState.stagingArea.sessionId
|
||||
getState().canvas.layerState.stagingArea.sessionId
|
||||
) {
|
||||
const [{ payload: image }] = await take(
|
||||
(
|
||||
action
|
||||
): action is ReturnType<typeof imageMetadataReceived.fulfilled> =>
|
||||
imageMetadataReceived.fulfilled.match(action) &&
|
||||
action.payload.image_name === image_name
|
||||
);
|
||||
dispatch(addImageToStagingArea(image));
|
||||
}
|
||||
}
|
||||
|
@ -122,21 +122,21 @@ export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
|
||||
/**
|
||||
* ResultImage
|
||||
*/
|
||||
export type Image = {
|
||||
name: string;
|
||||
type: ImageType;
|
||||
url: string;
|
||||
thumbnail: string;
|
||||
metadata: ImageResponseMetadata;
|
||||
};
|
||||
// export ty`pe Image = {
|
||||
// name: string;
|
||||
// type: ImageType;
|
||||
// url: string;
|
||||
// thumbnail: string;
|
||||
// metadata: ImageResponseMetadata;
|
||||
// };
|
||||
|
||||
export const isInvokeAIImage = (obj: Image | SelectedImage): obj is Image => {
|
||||
if ('url' in obj && 'thumbnail' in obj) {
|
||||
return true;
|
||||
}
|
||||
// export const isInvokeAIImage = (obj: Image | SelectedImage): obj is Image => {
|
||||
// if ('url' in obj && 'thumbnail' in obj) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
return false;
|
||||
};
|
||||
// return false;
|
||||
// };
|
||||
|
||||
/**
|
||||
* Types related to the system status.
|
||||
@ -346,7 +346,6 @@ export type AppConfig = {
|
||||
/**
|
||||
* Whether or not we need to re-fetch images
|
||||
*/
|
||||
shouldFetchImages: boolean;
|
||||
disabledTabs: InvokeTabName[];
|
||||
disabledFeatures: AppFeature[];
|
||||
disabledSDFeatures: SDFeature[];
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Badge, Flex } from '@chakra-ui/react';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { isNumber, isString } from 'lodash-es';
|
||||
import { useMemo } from 'react';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
type ImageMetadataOverlayProps = {
|
||||
image: Image;
|
||||
image: ImageDTO;
|
||||
};
|
||||
|
||||
const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
|
||||
@ -17,11 +17,11 @@ const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => {
|
||||
}, [image.metadata]);
|
||||
|
||||
const model = useMemo(() => {
|
||||
if (!isString(image.metadata?.invokeai?.node?.model)) {
|
||||
if (!isString(image.metadata?.model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return image.metadata?.invokeai?.node?.model;
|
||||
return image.metadata?.model;
|
||||
}, [image.metadata]);
|
||||
|
||||
return (
|
||||
|
12
invokeai/frontend/web/src/common/util/dateComparator.ts
Normal file
12
invokeai/frontend/web/src/common/util/dateComparator.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Comparator function for sorting dates in ascending order
|
||||
*/
|
||||
export const dateComparator = (a: string, b: string) => {
|
||||
const dateA = new Date(a);
|
||||
const dateB = new Date(b);
|
||||
|
||||
// sort in ascending order
|
||||
if (dateA > dateB) return 1;
|
||||
if (dateA < dateB) return -1;
|
||||
return 0;
|
||||
};
|
@ -46,7 +46,7 @@ const IAICanvasObjectRenderer = () => {
|
||||
key={i}
|
||||
x={obj.x}
|
||||
y={obj.y}
|
||||
url={getUrl(obj.image.url)}
|
||||
url={getUrl(obj.image.image_url)}
|
||||
/>
|
||||
);
|
||||
} else if (isCanvasBaseLine(obj)) {
|
||||
|
@ -62,7 +62,7 @@ const IAICanvasStagingArea = (props: Props) => {
|
||||
<Group {...rest}>
|
||||
{shouldShowStagingImage && currentStagingAreaImage && (
|
||||
<IAICanvasImage
|
||||
url={getUrl(currentStagingAreaImage.image.url)}
|
||||
url={getUrl(currentStagingAreaImage.image.image_url)}
|
||||
x={x}
|
||||
y={y}
|
||||
/>
|
||||
|
@ -157,17 +157,19 @@ const IAICanvasStagingAreaToolbar = () => {
|
||||
}
|
||||
colorScheme="accent"
|
||||
/>
|
||||
<IAIIconButton
|
||||
{/* <IAIIconButton
|
||||
tooltip={t('unifiedCanvas.saveToGallery')}
|
||||
aria-label={t('unifiedCanvas.saveToGallery')}
|
||||
icon={<FaSave />}
|
||||
onClick={() =>
|
||||
dispatch(
|
||||
saveStagingAreaImageToGallery(currentStagingAreaImage.image.url)
|
||||
saveStagingAreaImageToGallery(
|
||||
currentStagingAreaImage.image.image_url
|
||||
)
|
||||
)
|
||||
}
|
||||
colorScheme="accent"
|
||||
/>
|
||||
/> */}
|
||||
<IAIIconButton
|
||||
tooltip={t('unifiedCanvas.discardAll')}
|
||||
aria-label={t('unifiedCanvas.discardAll')}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import {
|
||||
roundDownToMultiple,
|
||||
roundToMultiple,
|
||||
@ -29,6 +28,7 @@ import {
|
||||
isCanvasBaseImage,
|
||||
isCanvasMaskLine,
|
||||
} from './canvasTypes';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export const initialLayerState: CanvasLayerState = {
|
||||
objects: [],
|
||||
@ -157,9 +157,9 @@ export const canvasSlice = createSlice({
|
||||
setCursorPosition: (state, action: PayloadAction<Vector2d | null>) => {
|
||||
state.cursorPosition = action.payload;
|
||||
},
|
||||
setInitialCanvasImage: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||
setInitialCanvasImage: (state, action: PayloadAction<ImageDTO>) => {
|
||||
const image = action.payload;
|
||||
const { width, height } = image.metadata;
|
||||
const { width, height } = image;
|
||||
const { stageDimensions } = state;
|
||||
|
||||
const newBoundingBoxDimensions = {
|
||||
@ -302,7 +302,7 @@ export const canvasSlice = createSlice({
|
||||
selectedImageIndex: -1,
|
||||
};
|
||||
},
|
||||
addImageToStagingArea: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||
addImageToStagingArea: (state, action: PayloadAction<ImageDTO>) => {
|
||||
const image = action.payload;
|
||||
|
||||
if (!image || !state.layerState.stagingArea.boundingBox) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { IRect, Vector2d } from 'konva/lib/types';
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export const LAYER_NAMES_DICT = [
|
||||
{ key: 'Base', value: 'base' },
|
||||
@ -37,7 +38,7 @@ export type CanvasImage = {
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
image: InvokeAI.Image;
|
||||
image: ImageDTO;
|
||||
};
|
||||
|
||||
export type CanvasMaskLine = {
|
||||
|
@ -195,14 +195,14 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
}
|
||||
|
||||
if (shouldTransformUrls) {
|
||||
return getUrl(image.url);
|
||||
return getUrl(image.image_url);
|
||||
}
|
||||
|
||||
if (image.url.startsWith('http')) {
|
||||
return image.url;
|
||||
if (image.image_url.startsWith('http')) {
|
||||
return image.image_url;
|
||||
}
|
||||
|
||||
return window.location.toString() + image.url;
|
||||
return window.location.toString() + image.image_url;
|
||||
};
|
||||
|
||||
const url = getImageUrl();
|
||||
|
@ -61,8 +61,8 @@ const CurrentImagePreview = () => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||
e.dataTransfer.setData('invokeai/imageName', image.image_name);
|
||||
e.dataTransfer.setData('invokeai/imageType', image.image_type);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
},
|
||||
[image]
|
||||
@ -108,7 +108,7 @@ const CurrentImagePreview = () => {
|
||||
image && (
|
||||
<>
|
||||
<Image
|
||||
src={getUrl(image.url)}
|
||||
src={getUrl(image.image_url)}
|
||||
fallbackStrategy="beforeLoadOrError"
|
||||
fallback={<ImageFallbackSpinner />}
|
||||
onDragStart={handleDragStart}
|
||||
|
@ -13,7 +13,6 @@ import { DragEvent, MouseEvent, memo, useCallback, useState } from 'react';
|
||||
import { FaCheck, FaExpand, FaImage, FaShare, FaTrash } from 'react-icons/fa';
|
||||
import DeleteImageModal from './DeleteImageModal';
|
||||
import { ContextMenu } from 'chakra-ui-contextmenu';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import {
|
||||
resizeAndScaleCanvas,
|
||||
setInitialCanvasImage,
|
||||
@ -39,6 +38,7 @@ import {
|
||||
sentImageToImg2Img,
|
||||
} from '../store/actions';
|
||||
import { useAppToaster } from 'app/components/Toaster';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export const selector = createSelector(
|
||||
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
|
||||
@ -70,14 +70,16 @@ export const selector = createSelector(
|
||||
);
|
||||
|
||||
interface HoverableImageProps {
|
||||
image: InvokeAI.Image;
|
||||
image: ImageDTO;
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const memoEqualityCheck = (
|
||||
prev: HoverableImageProps,
|
||||
next: HoverableImageProps
|
||||
) => prev.image.name === next.image.name && prev.isSelected === next.isSelected;
|
||||
) =>
|
||||
prev.image.image_name === next.image.image_name &&
|
||||
prev.isSelected === next.isSelected;
|
||||
|
||||
/**
|
||||
* Gallery image component with delete/use all/use seed buttons on hover.
|
||||
@ -100,7 +102,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
} = useDisclosure();
|
||||
|
||||
const { image, isSelected } = props;
|
||||
const { url, thumbnail, name } = image;
|
||||
const { image_url, thumbnail_url, image_name } = image;
|
||||
const { getUrl } = useGetUrl();
|
||||
|
||||
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||
@ -144,8 +146,8 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
|
||||
const handleDragStart = useCallback(
|
||||
(e: DragEvent<HTMLDivElement>) => {
|
||||
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||
e.dataTransfer.setData('invokeai/imageName', image.image_name);
|
||||
e.dataTransfer.setData('invokeai/imageType', image.image_type);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
},
|
||||
[image]
|
||||
@ -153,11 +155,11 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
|
||||
// Recall parameters handlers
|
||||
const handleRecallPrompt = useCallback(() => {
|
||||
recallPrompt(image.metadata?.invokeai?.node?.prompt);
|
||||
recallPrompt(image.metadata?.positive_conditioning);
|
||||
}, [image, recallPrompt]);
|
||||
|
||||
const handleRecallSeed = useCallback(() => {
|
||||
recallSeed(image.metadata.invokeai?.node?.seed);
|
||||
recallSeed(image.metadata?.seed);
|
||||
}, [image, recallSeed]);
|
||||
|
||||
const handleSendToImageToImage = useCallback(() => {
|
||||
@ -165,9 +167,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
dispatch(initialImageSelected(image));
|
||||
}, [dispatch, image]);
|
||||
|
||||
const handleRecallInitialImage = useCallback(() => {
|
||||
recallInitialImage(image.metadata.invokeai?.node?.image);
|
||||
}, [image, recallInitialImage]);
|
||||
// const handleRecallInitialImage = useCallback(() => {
|
||||
// recallInitialImage(image.metadata.invokeai?.node?.image);
|
||||
// }, [image, recallInitialImage]);
|
||||
|
||||
/**
|
||||
* TODO: the rest of these
|
||||
@ -200,7 +202,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
};
|
||||
|
||||
const handleOpenInNewTab = () => {
|
||||
window.open(getUrl(image.url), '_blank');
|
||||
window.open(getUrl(image.image_url), '_blank');
|
||||
};
|
||||
|
||||
return (
|
||||
@ -223,7 +225,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
<MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleRecallPrompt}
|
||||
isDisabled={image?.metadata?.invokeai?.node?.prompt === undefined}
|
||||
isDisabled={image?.metadata?.positive_conditioning === undefined}
|
||||
>
|
||||
{t('parameters.usePrompt')}
|
||||
</MenuItem>
|
||||
@ -231,23 +233,23 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
<MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleRecallSeed}
|
||||
isDisabled={image?.metadata?.invokeai?.node?.seed === undefined}
|
||||
isDisabled={image?.metadata?.seed === undefined}
|
||||
>
|
||||
{t('parameters.useSeed')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
{/* <MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleRecallInitialImage}
|
||||
isDisabled={image?.metadata?.invokeai?.node?.type !== 'img2img'}
|
||||
isDisabled={image?.metadata?.type !== 'img2img'}
|
||||
>
|
||||
{t('parameters.useInitImg')}
|
||||
</MenuItem>
|
||||
</MenuItem> */}
|
||||
<MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleUseAllParameters}
|
||||
isDisabled={
|
||||
!['txt2img', 'img2img', 'inpaint'].includes(
|
||||
String(image?.metadata?.invokeai?.node?.type)
|
||||
String(image?.metadata?.type)
|
||||
)
|
||||
}
|
||||
>
|
||||
@ -278,7 +280,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
{(ref) => (
|
||||
<Box
|
||||
position="relative"
|
||||
key={name}
|
||||
key={image_name}
|
||||
onMouseOver={handleMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
userSelect="none"
|
||||
@ -303,7 +305,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit
|
||||
}
|
||||
rounded="md"
|
||||
src={getUrl(thumbnail || url)}
|
||||
src={getUrl(thumbnail_url || image_url)}
|
||||
fallback={<FaImage />}
|
||||
sx={{
|
||||
width: '100%',
|
||||
|
@ -12,7 +12,7 @@ import { memo, useCallback } from 'react';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import DeleteImageModal from '../DeleteImageModal';
|
||||
import { requestedImageDeletion } from 'features/gallery/store/actions';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector],
|
||||
@ -30,7 +30,7 @@ const selector = createSelector(
|
||||
);
|
||||
|
||||
type DeleteImageButtonProps = {
|
||||
image: Image | undefined;
|
||||
image: ImageDTO | undefined;
|
||||
};
|
||||
|
||||
const DeleteImageButton = (props: DeleteImageButtonProps) => {
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
FlexProps,
|
||||
Grid,
|
||||
Icon,
|
||||
Image,
|
||||
Text,
|
||||
forwardRef,
|
||||
} from '@chakra-ui/react';
|
||||
@ -51,10 +50,10 @@ import { uploadsAdapter } from '../store/uploadsSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { Virtuoso, VirtuosoGrid } from 'react-virtuoso';
|
||||
import { Image as ImageType } from 'app/types/invokeai';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import GalleryProgressImage from './GalleryProgressImage';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 290;
|
||||
const PROGRESS_IMAGE_PLACEHOLDER = 'PROGRESS_IMAGE_PLACEHOLDER';
|
||||
@ -66,7 +65,7 @@ const categorySelector = createSelector(
|
||||
const { currentCategory } = gallery;
|
||||
|
||||
if (currentCategory === 'results') {
|
||||
const tempImages: (ImageType | typeof PROGRESS_IMAGE_PLACEHOLDER)[] = [];
|
||||
const tempImages: (ImageDTO | typeof PROGRESS_IMAGE_PLACEHOLDER)[] = [];
|
||||
|
||||
if (system.progressImage) {
|
||||
tempImages.push(PROGRESS_IMAGE_PLACEHOLDER);
|
||||
@ -352,7 +351,7 @@ const ImageGalleryContent = () => {
|
||||
const isSelected =
|
||||
image === PROGRESS_IMAGE_PLACEHOLDER
|
||||
? false
|
||||
: selectedImage?.name === image?.name;
|
||||
: selectedImage?.image_name === image?.image_name;
|
||||
|
||||
return (
|
||||
<Flex sx={{ pb: 2 }}>
|
||||
@ -362,7 +361,7 @@ const ImageGalleryContent = () => {
|
||||
/>
|
||||
) : (
|
||||
<HoverableImage
|
||||
key={`${image.name}-${image.thumbnail}`}
|
||||
key={`${image.image_name}-${image.thumbnail_url}`}
|
||||
image={image}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
@ -385,13 +384,13 @@ const ImageGalleryContent = () => {
|
||||
const isSelected =
|
||||
image === PROGRESS_IMAGE_PLACEHOLDER
|
||||
? false
|
||||
: selectedImage?.name === image?.name;
|
||||
: selectedImage?.image_name === image?.image_name;
|
||||
|
||||
return image === PROGRESS_IMAGE_PLACEHOLDER ? (
|
||||
<GalleryProgressImage key={PROGRESS_IMAGE_PLACEHOLDER} />
|
||||
) : (
|
||||
<HoverableImage
|
||||
key={`${image.name}-${image.thumbnail}`}
|
||||
key={`${image.image_name}-${image.thumbnail_url}`}
|
||||
image={image}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
|
@ -18,7 +18,9 @@ import {
|
||||
setCfgScale,
|
||||
setHeight,
|
||||
setImg2imgStrength,
|
||||
setNegativePrompt,
|
||||
setPerlin,
|
||||
setPositivePrompt,
|
||||
setScheduler,
|
||||
setSeamless,
|
||||
setSeed,
|
||||
@ -36,6 +38,9 @@ import { useTranslation } from 'react-i18next';
|
||||
import { FaCopy } from 'react-icons/fa';
|
||||
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { filter } from 'lodash-es';
|
||||
import { Scheduler } from 'app/constants';
|
||||
|
||||
type MetadataItemProps = {
|
||||
isLink?: boolean;
|
||||
@ -58,7 +63,6 @@ const MetadataItem = ({
|
||||
withCopy = false,
|
||||
}: MetadataItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex gap={2}>
|
||||
{onClick && (
|
||||
@ -104,14 +108,14 @@ const MetadataItem = ({
|
||||
};
|
||||
|
||||
type ImageMetadataViewerProps = {
|
||||
image: InvokeAI.Image;
|
||||
image: ImageDTO;
|
||||
};
|
||||
|
||||
// TODO: I don't know if this is needed.
|
||||
const memoEqualityCheck = (
|
||||
prev: ImageMetadataViewerProps,
|
||||
next: ImageMetadataViewerProps
|
||||
) => prev.image.name === next.image.name;
|
||||
) => prev.image.image_name === next.image.image_name;
|
||||
|
||||
// TODO: Show more interesting information in this component.
|
||||
|
||||
@ -128,8 +132,9 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||
dispatch(setShouldShowImageDetails(false));
|
||||
});
|
||||
|
||||
const sessionId = image.metadata.invokeai?.session_id;
|
||||
const node = image.metadata.invokeai?.node as Record<string, any>;
|
||||
const sessionId = image?.session_id;
|
||||
|
||||
const metadata = image?.metadata;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { getUrl } = useGetUrl();
|
||||
@ -154,110 +159,131 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||
>
|
||||
<Flex gap={2}>
|
||||
<Text fontWeight="semibold">File:</Text>
|
||||
<Link href={getUrl(image.url)} isExternal maxW="calc(100% - 3rem)">
|
||||
{image.url.length > 64
|
||||
? image.url.substring(0, 64).concat('...')
|
||||
: image.url}
|
||||
<Link
|
||||
href={getUrl(image.image_url)}
|
||||
isExternal
|
||||
maxW="calc(100% - 3rem)"
|
||||
>
|
||||
{image.image_name}
|
||||
<ExternalLinkIcon mx="2px" />
|
||||
</Link>
|
||||
</Flex>
|
||||
{node && Object.keys(node).length > 0 ? (
|
||||
{metadata && Object.keys(metadata).length > 0 ? (
|
||||
<>
|
||||
{node.type && (
|
||||
<MetadataItem label="Invocation type" value={node.type} />
|
||||
{metadata.type && (
|
||||
<MetadataItem label="Invocation type" value={metadata.type} />
|
||||
)}
|
||||
{node.model && <MetadataItem label="Model" value={node.model} />}
|
||||
{node.prompt && (
|
||||
{metadata.width && (
|
||||
<MetadataItem
|
||||
label="Width"
|
||||
value={metadata.width}
|
||||
onClick={() => dispatch(setWidth(Number(metadata.width)))}
|
||||
/>
|
||||
)}
|
||||
{metadata.height && (
|
||||
<MetadataItem
|
||||
label="Height"
|
||||
value={metadata.height}
|
||||
onClick={() => dispatch(setHeight(Number(metadata.height)))}
|
||||
/>
|
||||
)}
|
||||
{metadata.model && (
|
||||
<MetadataItem label="Model" value={metadata.model} />
|
||||
)}
|
||||
{metadata.positive_conditioning && (
|
||||
<MetadataItem
|
||||
label="Prompt"
|
||||
labelPosition="top"
|
||||
value={
|
||||
typeof node.prompt === 'string'
|
||||
? node.prompt
|
||||
: promptToString(node.prompt)
|
||||
typeof metadata.positive_conditioning === 'string'
|
||||
? metadata.positive_conditioning
|
||||
: promptToString(metadata.positive_conditioning)
|
||||
}
|
||||
onClick={() => setBothPrompts(node.prompt)}
|
||||
onClick={() => setPositivePrompt(metadata.positive_conditioning!)}
|
||||
/>
|
||||
)}
|
||||
{node.seed !== undefined && (
|
||||
{metadata.negative_conditioning && (
|
||||
<MetadataItem
|
||||
label="Prompt"
|
||||
labelPosition="top"
|
||||
value={
|
||||
typeof metadata.negative_conditioning === 'string'
|
||||
? metadata.negative_conditioning
|
||||
: promptToString(metadata.negative_conditioning)
|
||||
}
|
||||
onClick={() => setNegativePrompt(metadata.negative_conditioning!)}
|
||||
/>
|
||||
)}
|
||||
{metadata.seed !== undefined && (
|
||||
<MetadataItem
|
||||
label="Seed"
|
||||
value={node.seed}
|
||||
onClick={() => dispatch(setSeed(Number(node.seed)))}
|
||||
value={metadata.seed}
|
||||
onClick={() => dispatch(setSeed(Number(metadata.seed)))}
|
||||
/>
|
||||
)}
|
||||
{node.threshold !== undefined && (
|
||||
{/* {metadata.threshold !== undefined && (
|
||||
<MetadataItem
|
||||
label="Noise Threshold"
|
||||
value={node.threshold}
|
||||
onClick={() => dispatch(setThreshold(Number(node.threshold)))}
|
||||
value={metadata.threshold}
|
||||
onClick={() => dispatch(setThreshold(Number(metadata.threshold)))}
|
||||
/>
|
||||
)}
|
||||
{node.perlin !== undefined && (
|
||||
{metadata.perlin !== undefined && (
|
||||
<MetadataItem
|
||||
label="Perlin Noise"
|
||||
value={node.perlin}
|
||||
onClick={() => dispatch(setPerlin(Number(node.perlin)))}
|
||||
value={metadata.perlin}
|
||||
onClick={() => dispatch(setPerlin(Number(metadata.perlin)))}
|
||||
/>
|
||||
)}
|
||||
{node.scheduler && (
|
||||
)} */}
|
||||
{metadata.scheduler && (
|
||||
<MetadataItem
|
||||
label="Scheduler"
|
||||
value={node.scheduler}
|
||||
onClick={() => dispatch(setScheduler(node.scheduler))}
|
||||
/>
|
||||
)}
|
||||
{node.steps && (
|
||||
<MetadataItem
|
||||
label="Steps"
|
||||
value={node.steps}
|
||||
onClick={() => dispatch(setSteps(Number(node.steps)))}
|
||||
/>
|
||||
)}
|
||||
{node.cfg_scale !== undefined && (
|
||||
<MetadataItem
|
||||
label="CFG scale"
|
||||
value={node.cfg_scale}
|
||||
onClick={() => dispatch(setCfgScale(Number(node.cfg_scale)))}
|
||||
/>
|
||||
)}
|
||||
{node.variations && node.variations.length > 0 && (
|
||||
<MetadataItem
|
||||
label="Seed-weight pairs"
|
||||
value={seedWeightsToString(node.variations)}
|
||||
value={metadata.scheduler}
|
||||
onClick={() =>
|
||||
dispatch(setSeedWeights(seedWeightsToString(node.variations)))
|
||||
dispatch(setScheduler(metadata.scheduler as Scheduler))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{node.seamless && (
|
||||
{metadata.steps && (
|
||||
<MetadataItem
|
||||
label="Steps"
|
||||
value={metadata.steps}
|
||||
onClick={() => dispatch(setSteps(Number(metadata.steps)))}
|
||||
/>
|
||||
)}
|
||||
{metadata.cfg_scale !== undefined && (
|
||||
<MetadataItem
|
||||
label="CFG scale"
|
||||
value={metadata.cfg_scale}
|
||||
onClick={() => dispatch(setCfgScale(Number(metadata.cfg_scale)))}
|
||||
/>
|
||||
)}
|
||||
{/* {metadata.variations && metadata.variations.length > 0 && (
|
||||
<MetadataItem
|
||||
label="Seed-weight pairs"
|
||||
value={seedWeightsToString(metadata.variations)}
|
||||
onClick={() =>
|
||||
dispatch(
|
||||
setSeedWeights(seedWeightsToString(metadata.variations))
|
||||
)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{metadata.seamless && (
|
||||
<MetadataItem
|
||||
label="Seamless"
|
||||
value={node.seamless}
|
||||
onClick={() => dispatch(setSeamless(node.seamless))}
|
||||
value={metadata.seamless}
|
||||
onClick={() => dispatch(setSeamless(metadata.seamless))}
|
||||
/>
|
||||
)}
|
||||
{node.hires_fix && (
|
||||
{metadata.hires_fix && (
|
||||
<MetadataItem
|
||||
label="High Resolution Optimization"
|
||||
value={node.hires_fix}
|
||||
onClick={() => dispatch(setHiresFix(node.hires_fix))}
|
||||
value={metadata.hires_fix}
|
||||
onClick={() => dispatch(setHiresFix(metadata.hires_fix))}
|
||||
/>
|
||||
)}
|
||||
{node.width && (
|
||||
<MetadataItem
|
||||
label="Width"
|
||||
value={node.width}
|
||||
onClick={() => dispatch(setWidth(Number(node.width)))}
|
||||
/>
|
||||
)}
|
||||
{node.height && (
|
||||
<MetadataItem
|
||||
label="Height"
|
||||
value={node.height}
|
||||
onClick={() => dispatch(setHeight(Number(node.height)))}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
{/* {init_image_path && (
|
||||
<MetadataItem
|
||||
label="Initial image"
|
||||
@ -266,22 +292,22 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||
onClick={() => dispatch(setInitialImage(init_image_path))}
|
||||
/>
|
||||
)} */}
|
||||
{node.strength && (
|
||||
{metadata.strength && (
|
||||
<MetadataItem
|
||||
label="Image to image strength"
|
||||
value={node.strength}
|
||||
value={metadata.strength}
|
||||
onClick={() =>
|
||||
dispatch(setImg2imgStrength(Number(node.strength)))
|
||||
dispatch(setImg2imgStrength(Number(metadata.strength)))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{node.fit && (
|
||||
{/* {metadata.fit && (
|
||||
<MetadataItem
|
||||
label="Image to image fit"
|
||||
value={node.fit}
|
||||
onClick={() => dispatch(setShouldFitToWidthHeight(node.fit))}
|
||||
value={metadata.fit}
|
||||
onClick={() => dispatch(setShouldFitToWidthHeight(metadata.fit))}
|
||||
/>
|
||||
)}
|
||||
)} */}
|
||||
</>
|
||||
) : (
|
||||
<Center width="100%" pt={10}>
|
||||
|
@ -1,470 +0,0 @@
|
||||
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
Box,
|
||||
Center,
|
||||
Flex,
|
||||
Heading,
|
||||
IconButton,
|
||||
Link,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useGetUrl } from 'common/util/getUrl';
|
||||
import promptToString from 'common/util/promptToString';
|
||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||
import {
|
||||
setCfgScale,
|
||||
setHeight,
|
||||
setImg2imgStrength,
|
||||
// setInitialImage,
|
||||
setMaskPath,
|
||||
setPerlin,
|
||||
setSampler,
|
||||
setSeamless,
|
||||
setSeed,
|
||||
setSeedWeights,
|
||||
setShouldFitToWidthHeight,
|
||||
setSteps,
|
||||
setThreshold,
|
||||
setWidth,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import {
|
||||
setCodeformerFidelity,
|
||||
setFacetoolStrength,
|
||||
setFacetoolType,
|
||||
setHiresFix,
|
||||
setUpscalingDenoising,
|
||||
setUpscalingLevel,
|
||||
setUpscalingStrength,
|
||||
} from 'features/parameters/store/postprocessingSlice';
|
||||
import { setShouldShowImageDetails } from 'features/ui/store/uiSlice';
|
||||
import { memo } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaCopy } from 'react-icons/fa';
|
||||
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
|
||||
import * as png from '@stevebel/png';
|
||||
|
||||
type MetadataItemProps = {
|
||||
isLink?: boolean;
|
||||
label: string;
|
||||
onClick?: () => void;
|
||||
value: number | string | boolean;
|
||||
labelPosition?: string;
|
||||
withCopy?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component to display an individual metadata item or parameter.
|
||||
*/
|
||||
const MetadataItem = ({
|
||||
label,
|
||||
value,
|
||||
onClick,
|
||||
isLink,
|
||||
labelPosition,
|
||||
withCopy = false,
|
||||
}: MetadataItemProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Flex gap={2}>
|
||||
{onClick && (
|
||||
<Tooltip label={`Recall ${label}`}>
|
||||
<IconButton
|
||||
aria-label={t('accessibility.useThisParameter')}
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
fontSize={20}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{withCopy && (
|
||||
<Tooltip label={`Copy ${label}`}>
|
||||
<IconButton
|
||||
aria-label={`Copy ${label}`}
|
||||
icon={<FaCopy />}
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
fontSize={14}
|
||||
onClick={() => navigator.clipboard.writeText(value.toString())}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Flex direction={labelPosition ? 'column' : 'row'}>
|
||||
<Text fontWeight="semibold" whiteSpace="pre-wrap" pr={2}>
|
||||
{label}:
|
||||
</Text>
|
||||
{isLink ? (
|
||||
<Link href={value.toString()} isExternal wordBreak="break-all">
|
||||
{value.toString()} <ExternalLinkIcon mx="2px" />
|
||||
</Link>
|
||||
) : (
|
||||
<Text overflowY="scroll" wordBreak="break-all">
|
||||
{value.toString()}
|
||||
</Text>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
type ImageMetadataViewerProps = {
|
||||
image: InvokeAI.Image;
|
||||
};
|
||||
|
||||
// TODO: I don't know if this is needed.
|
||||
const memoEqualityCheck = (
|
||||
prev: ImageMetadataViewerProps,
|
||||
next: ImageMetadataViewerProps
|
||||
) => prev.image.name === next.image.name;
|
||||
|
||||
// TODO: Show more interesting information in this component.
|
||||
|
||||
/**
|
||||
* Image metadata viewer overlays currently selected image and provides
|
||||
* access to any of its metadata for use in processing.
|
||||
*/
|
||||
const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
|
||||
useHotkeys('esc', () => {
|
||||
dispatch(setShouldShowImageDetails(false));
|
||||
});
|
||||
|
||||
const metadata = image?.metadata.sd_metadata || {};
|
||||
const dreamPrompt = image?.metadata.sd_metadata?.dreamPrompt;
|
||||
|
||||
const {
|
||||
cfg_scale,
|
||||
fit,
|
||||
height,
|
||||
hires_fix,
|
||||
init_image_path,
|
||||
mask_image_path,
|
||||
orig_path,
|
||||
perlin,
|
||||
postprocessing,
|
||||
prompt,
|
||||
sampler,
|
||||
seamless,
|
||||
seed,
|
||||
steps,
|
||||
strength,
|
||||
threshold,
|
||||
type,
|
||||
variations,
|
||||
width,
|
||||
model_weights,
|
||||
} = metadata;
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { getUrl } = useGetUrl();
|
||||
|
||||
const metadataJSON = JSON.stringify(image, null, 2);
|
||||
|
||||
// fetch(getUrl(image.url))
|
||||
// .then((r) => r.arrayBuffer())
|
||||
// .then((buffer) => {
|
||||
// const { text } = png.decode(buffer);
|
||||
// const metadata = text?.['sd-metadata']
|
||||
// ? JSON.parse(text['sd-metadata'] ?? {})
|
||||
// : {};
|
||||
// console.log(metadata);
|
||||
// });
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
padding: 4,
|
||||
gap: 1,
|
||||
flexDirection: 'column',
|
||||
width: 'full',
|
||||
height: 'full',
|
||||
backdropFilter: 'blur(20px)',
|
||||
bg: 'whiteAlpha.600',
|
||||
_dark: {
|
||||
bg: 'blackAlpha.600',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex gap={2}>
|
||||
<Text fontWeight="semibold">File:</Text>
|
||||
<Link href={getUrl(image.url)} isExternal maxW="calc(100% - 3rem)">
|
||||
{image.url.length > 64
|
||||
? image.url.substring(0, 64).concat('...')
|
||||
: image.url}
|
||||
<ExternalLinkIcon mx="2px" />
|
||||
</Link>
|
||||
</Flex>
|
||||
<Flex gap={2} direction="column">
|
||||
<Flex gap={2}>
|
||||
<Tooltip label="Copy metadata JSON">
|
||||
<IconButton
|
||||
aria-label={t('accessibility.copyMetadataJson')}
|
||||
icon={<FaCopy />}
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
fontSize={14}
|
||||
onClick={() => navigator.clipboard.writeText(metadataJSON)}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Text fontWeight="semibold">Metadata JSON:</Text>
|
||||
</Flex>
|
||||
<Box
|
||||
sx={{
|
||||
mt: 0,
|
||||
mr: 2,
|
||||
mb: 4,
|
||||
ml: 2,
|
||||
padding: 4,
|
||||
borderRadius: 'base',
|
||||
overflowX: 'scroll',
|
||||
wordBreak: 'break-all',
|
||||
bg: 'whiteAlpha.500',
|
||||
_dark: { bg: 'blackAlpha.500' },
|
||||
}}
|
||||
>
|
||||
<pre>{metadataJSON}</pre>
|
||||
</Box>
|
||||
</Flex>
|
||||
{Object.keys(metadata).length > 0 ? (
|
||||
<>
|
||||
{type && <MetadataItem label="Generation type" value={type} />}
|
||||
{model_weights && (
|
||||
<MetadataItem label="Model" value={model_weights} />
|
||||
)}
|
||||
{['esrgan', 'gfpgan'].includes(type) && (
|
||||
<MetadataItem label="Original image" value={orig_path} />
|
||||
)}
|
||||
{prompt && (
|
||||
<MetadataItem
|
||||
label="Prompt"
|
||||
labelPosition="top"
|
||||
value={
|
||||
typeof prompt === 'string' ? prompt : promptToString(prompt)
|
||||
}
|
||||
onClick={() => setBothPrompts(prompt)}
|
||||
/>
|
||||
)}
|
||||
{seed !== undefined && (
|
||||
<MetadataItem
|
||||
label="Seed"
|
||||
value={seed}
|
||||
onClick={() => dispatch(setSeed(seed))}
|
||||
/>
|
||||
)}
|
||||
{threshold !== undefined && (
|
||||
<MetadataItem
|
||||
label="Noise Threshold"
|
||||
value={threshold}
|
||||
onClick={() => dispatch(setThreshold(threshold))}
|
||||
/>
|
||||
)}
|
||||
{perlin !== undefined && (
|
||||
<MetadataItem
|
||||
label="Perlin Noise"
|
||||
value={perlin}
|
||||
onClick={() => dispatch(setPerlin(perlin))}
|
||||
/>
|
||||
)}
|
||||
{sampler && (
|
||||
<MetadataItem
|
||||
label="Sampler"
|
||||
value={sampler}
|
||||
onClick={() => dispatch(setSampler(sampler))}
|
||||
/>
|
||||
)}
|
||||
{steps && (
|
||||
<MetadataItem
|
||||
label="Steps"
|
||||
value={steps}
|
||||
onClick={() => dispatch(setSteps(steps))}
|
||||
/>
|
||||
)}
|
||||
{cfg_scale !== undefined && (
|
||||
<MetadataItem
|
||||
label="CFG scale"
|
||||
value={cfg_scale}
|
||||
onClick={() => dispatch(setCfgScale(cfg_scale))}
|
||||
/>
|
||||
)}
|
||||
{variations && variations.length > 0 && (
|
||||
<MetadataItem
|
||||
label="Seed-weight pairs"
|
||||
value={seedWeightsToString(variations)}
|
||||
onClick={() =>
|
||||
dispatch(setSeedWeights(seedWeightsToString(variations)))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{seamless && (
|
||||
<MetadataItem
|
||||
label="Seamless"
|
||||
value={seamless}
|
||||
onClick={() => dispatch(setSeamless(seamless))}
|
||||
/>
|
||||
)}
|
||||
{hires_fix && (
|
||||
<MetadataItem
|
||||
label="High Resolution Optimization"
|
||||
value={hires_fix}
|
||||
onClick={() => dispatch(setHiresFix(hires_fix))}
|
||||
/>
|
||||
)}
|
||||
{width && (
|
||||
<MetadataItem
|
||||
label="Width"
|
||||
value={width}
|
||||
onClick={() => dispatch(setWidth(width))}
|
||||
/>
|
||||
)}
|
||||
{height && (
|
||||
<MetadataItem
|
||||
label="Height"
|
||||
value={height}
|
||||
onClick={() => dispatch(setHeight(height))}
|
||||
/>
|
||||
)}
|
||||
{/* {init_image_path && (
|
||||
<MetadataItem
|
||||
label="Initial image"
|
||||
value={init_image_path}
|
||||
isLink
|
||||
onClick={() => dispatch(setInitialImage(init_image_path))}
|
||||
/>
|
||||
)} */}
|
||||
{mask_image_path && (
|
||||
<MetadataItem
|
||||
label="Mask image"
|
||||
value={mask_image_path}
|
||||
isLink
|
||||
onClick={() => dispatch(setMaskPath(mask_image_path))}
|
||||
/>
|
||||
)}
|
||||
{type === 'img2img' && strength && (
|
||||
<MetadataItem
|
||||
label="Image to image strength"
|
||||
value={strength}
|
||||
onClick={() => dispatch(setImg2imgStrength(strength))}
|
||||
/>
|
||||
)}
|
||||
{fit && (
|
||||
<MetadataItem
|
||||
label="Image to image fit"
|
||||
value={fit}
|
||||
onClick={() => dispatch(setShouldFitToWidthHeight(fit))}
|
||||
/>
|
||||
)}
|
||||
{postprocessing && postprocessing.length > 0 && (
|
||||
<>
|
||||
<Heading size="sm">Postprocessing</Heading>
|
||||
{postprocessing.map(
|
||||
(
|
||||
postprocess: InvokeAI.PostProcessedImageMetadata,
|
||||
i: number
|
||||
) => {
|
||||
if (postprocess.type === 'esrgan') {
|
||||
const { scale, strength, denoise_str } = postprocess;
|
||||
return (
|
||||
<Flex key={i} pl={8} gap={1} direction="column">
|
||||
<Text size="md">{`${i + 1}: Upscale (ESRGAN)`}</Text>
|
||||
<MetadataItem
|
||||
label="Scale"
|
||||
value={scale}
|
||||
onClick={() => dispatch(setUpscalingLevel(scale))}
|
||||
/>
|
||||
<MetadataItem
|
||||
label="Strength"
|
||||
value={strength}
|
||||
onClick={() =>
|
||||
dispatch(setUpscalingStrength(strength))
|
||||
}
|
||||
/>
|
||||
{denoise_str !== undefined && (
|
||||
<MetadataItem
|
||||
label="Denoising strength"
|
||||
value={denoise_str}
|
||||
onClick={() =>
|
||||
dispatch(setUpscalingDenoising(denoise_str))
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
} else if (postprocess.type === 'gfpgan') {
|
||||
const { strength } = postprocess;
|
||||
return (
|
||||
<Flex key={i} pl={8} gap={1} direction="column">
|
||||
<Text size="md">{`${
|
||||
i + 1
|
||||
}: Face restoration (GFPGAN)`}</Text>
|
||||
|
||||
<MetadataItem
|
||||
label="Strength"
|
||||
value={strength}
|
||||
onClick={() => {
|
||||
dispatch(setFacetoolStrength(strength));
|
||||
dispatch(setFacetoolType('gfpgan'));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
} else if (postprocess.type === 'codeformer') {
|
||||
const { strength, fidelity } = postprocess;
|
||||
return (
|
||||
<Flex key={i} pl={8} gap={1} direction="column">
|
||||
<Text size="md">{`${
|
||||
i + 1
|
||||
}: Face restoration (Codeformer)`}</Text>
|
||||
|
||||
<MetadataItem
|
||||
label="Strength"
|
||||
value={strength}
|
||||
onClick={() => {
|
||||
dispatch(setFacetoolStrength(strength));
|
||||
dispatch(setFacetoolType('codeformer'));
|
||||
}}
|
||||
/>
|
||||
{fidelity && (
|
||||
<MetadataItem
|
||||
label="Fidelity"
|
||||
value={fidelity}
|
||||
onClick={() => {
|
||||
dispatch(setCodeformerFidelity(fidelity));
|
||||
dispatch(setFacetoolType('codeformer'));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
}
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{dreamPrompt && (
|
||||
<MetadataItem withCopy label="Dream Prompt" value={dreamPrompt} />
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Center width="100%" pt={10}>
|
||||
<Text fontSize="lg" fontWeight="semibold">
|
||||
No metadata available
|
||||
</Text>
|
||||
</Center>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}, memoEqualityCheck);
|
||||
|
||||
ImageMetadataViewer.displayName = 'ImageMetadataViewer';
|
||||
|
||||
export default ImageMetadataViewer;
|
@ -13,11 +13,9 @@ const useGetImageByNameSelector = createSelector(
|
||||
|
||||
const useGetImageByNameAndType = () => {
|
||||
const { allResults, allUploads } = useAppSelector(useGetImageByNameSelector);
|
||||
|
||||
return (name: string, type: ImageType) => {
|
||||
if (type === 'results') {
|
||||
const resultImagesResult = allResults[name];
|
||||
|
||||
if (resultImagesResult) {
|
||||
return resultImagesResult;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { SelectedImage } from 'features/parameters/store/actions';
|
||||
import { ImageNameAndType } from 'features/parameters/store/actions';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export const requestedImageDeletion = createAction<
|
||||
Image | SelectedImage | undefined
|
||||
ImageDTO | ImageNameAndType | undefined
|
||||
>('gallery/requestedImageDeletion');
|
||||
|
||||
export const sentImageToCanvas = createAction('gallery/sentImageToCanvas');
|
||||
|
@ -1,16 +1,15 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
||||
import {
|
||||
receivedResultImagesPage,
|
||||
receivedUploadImagesPage,
|
||||
} from '../../../services/thunks/gallery';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
type GalleryImageObjectFitType = 'contain' | 'cover';
|
||||
|
||||
export interface GalleryState {
|
||||
selectedImage?: Image;
|
||||
selectedImage?: ImageDTO;
|
||||
galleryImageMinimumWidth: number;
|
||||
galleryImageObjectFit: GalleryImageObjectFitType;
|
||||
shouldAutoSwitchToNewImages: boolean;
|
||||
@ -30,7 +29,7 @@ export const gallerySlice = createSlice({
|
||||
name: 'gallery',
|
||||
initialState: initialGalleryState,
|
||||
reducers: {
|
||||
imageSelected: (state, action: PayloadAction<Image | undefined>) => {
|
||||
imageSelected: (state, action: PayloadAction<ImageDTO | undefined>) => {
|
||||
state.selectedImage = action.payload;
|
||||
// TODO: if the user selects an image, disable the auto switch?
|
||||
// state.shouldAutoSwitchToNewImages = false;
|
||||
@ -61,37 +60,18 @@ export const gallerySlice = createSlice({
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder.addCase(imageReceived.fulfilled, (state, action) => {
|
||||
// When we get an updated URL for an image, we need to update the selectedImage in gallery,
|
||||
// which is currently its own object (instead of a reference to an image in results/uploads)
|
||||
const { imagePath } = action.payload;
|
||||
const { imageName } = action.meta.arg;
|
||||
|
||||
if (state.selectedImage?.name === imageName) {
|
||||
state.selectedImage.url = imagePath;
|
||||
}
|
||||
});
|
||||
|
||||
builder.addCase(thumbnailReceived.fulfilled, (state, action) => {
|
||||
// When we get an updated URL for an image, we need to update the selectedImage in gallery,
|
||||
// which is currently its own object (instead of a reference to an image in results/uploads)
|
||||
const { thumbnailPath } = action.payload;
|
||||
const { thumbnailName } = action.meta.arg;
|
||||
|
||||
if (state.selectedImage?.name === thumbnailName) {
|
||||
state.selectedImage.thumbnail = thumbnailPath;
|
||||
}
|
||||
});
|
||||
builder.addCase(receivedResultImagesPage.fulfilled, (state, action) => {
|
||||
// rehydrate selectedImage URL when results list comes in
|
||||
// solves case when outdated URL is in local storage
|
||||
const selectedImage = state.selectedImage;
|
||||
if (selectedImage) {
|
||||
const selectedImageInResults = action.payload.items.find(
|
||||
(image) => image.image_name === selectedImage.name
|
||||
(image) => image.image_name === selectedImage.image_name
|
||||
);
|
||||
|
||||
if (selectedImageInResults) {
|
||||
selectedImage.url = selectedImageInResults.image_url;
|
||||
selectedImage.image_url = selectedImageInResults.image_url;
|
||||
selectedImage.thumbnail_url = selectedImageInResults.thumbnail_url;
|
||||
state.selectedImage = selectedImage;
|
||||
}
|
||||
}
|
||||
@ -102,10 +82,12 @@ export const gallerySlice = createSlice({
|
||||
const selectedImage = state.selectedImage;
|
||||
if (selectedImage) {
|
||||
const selectedImageInResults = action.payload.items.find(
|
||||
(image) => image.image_name === selectedImage.name
|
||||
(image) => image.image_name === selectedImage.image_name
|
||||
);
|
||||
|
||||
if (selectedImageInResults) {
|
||||
selectedImage.url = selectedImageInResults.image_url;
|
||||
selectedImage.image_url = selectedImageInResults.image_url;
|
||||
selectedImage.thumbnail_url = selectedImageInResults.thumbnail_url;
|
||||
state.selectedImage = selectedImage;
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,24 @@
|
||||
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
|
||||
import { RootState } from 'app/store/store';
|
||||
import {
|
||||
receivedResultImagesPage,
|
||||
IMAGES_PER_PAGE,
|
||||
} from 'services/thunks/gallery';
|
||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||
import {
|
||||
imageDeleted,
|
||||
imageReceived,
|
||||
thumbnailReceived,
|
||||
imageMetadataReceived,
|
||||
imageUrlsReceived,
|
||||
} from 'services/thunks/image';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { dateComparator } from 'common/util/dateComparator';
|
||||
|
||||
export const resultsAdapter = createEntityAdapter<Image>({
|
||||
selectId: (image) => image.name,
|
||||
sortComparer: (a, b) => b.metadata.created - a.metadata.created,
|
||||
export type ResultsImageDTO = Omit<ImageDTO, 'image_type'> & {
|
||||
image_type: 'results';
|
||||
};
|
||||
|
||||
export const resultsAdapter = createEntityAdapter<ResultsImageDTO>({
|
||||
selectId: (image) => image.image_name,
|
||||
sortComparer: (a, b) => dateComparator(b.created_at, a.created_at),
|
||||
});
|
||||
|
||||
type AdditionalResultsState = {
|
||||
@ -53,13 +56,12 @@ const resultsSlice = createSlice({
|
||||
* Received Result Images Page - FULFILLED
|
||||
*/
|
||||
builder.addCase(receivedResultImagesPage.fulfilled, (state, action) => {
|
||||
const { items, page, pages } = action.payload;
|
||||
const { page, pages } = action.payload;
|
||||
|
||||
const resultImages = items.map((image) =>
|
||||
deserializeImageResponse(image)
|
||||
);
|
||||
// We know these will all be of the results type, but it's not represented in the API types
|
||||
const items = action.payload.items as ResultsImageDTO[];
|
||||
|
||||
resultsAdapter.setMany(state, resultImages);
|
||||
resultsAdapter.setMany(state, items);
|
||||
|
||||
state.page = page;
|
||||
state.pages = pages;
|
||||
@ -68,33 +70,32 @@ const resultsSlice = createSlice({
|
||||
});
|
||||
|
||||
/**
|
||||
* Image Received - FULFILLED
|
||||
* Image Metadata Received - FULFILLED
|
||||
*/
|
||||
builder.addCase(imageReceived.fulfilled, (state, action) => {
|
||||
const { imagePath } = action.payload;
|
||||
const { imageName } = action.meta.arg;
|
||||
builder.addCase(imageMetadataReceived.fulfilled, (state, action) => {
|
||||
const { image_type } = action.payload;
|
||||
|
||||
resultsAdapter.updateOne(state, {
|
||||
id: imageName,
|
||||
changes: {
|
||||
url: imagePath,
|
||||
},
|
||||
});
|
||||
if (image_type === 'results') {
|
||||
resultsAdapter.upsertOne(state, action.payload as ResultsImageDTO);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Thumbnail Received - FULFILLED
|
||||
* Image URLs Received - FULFILLED
|
||||
*/
|
||||
builder.addCase(thumbnailReceived.fulfilled, (state, action) => {
|
||||
const { thumbnailPath } = action.payload;
|
||||
const { thumbnailName } = action.meta.arg;
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_type, image_url, thumbnail_url } =
|
||||
action.payload;
|
||||
|
||||
resultsAdapter.updateOne(state, {
|
||||
id: thumbnailName,
|
||||
changes: {
|
||||
thumbnail: thumbnailPath,
|
||||
},
|
||||
});
|
||||
if (image_type === 'results') {
|
||||
resultsAdapter.updateOne(state, {
|
||||
id: image_name,
|
||||
changes: {
|
||||
image_url: image_url,
|
||||
thumbnail_url: thumbnail_url,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -1,17 +1,21 @@
|
||||
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
|
||||
import { RootState } from 'app/store/store';
|
||||
import {
|
||||
receivedUploadImagesPage,
|
||||
IMAGES_PER_PAGE,
|
||||
} from 'services/thunks/gallery';
|
||||
import { imageDeleted } from 'services/thunks/image';
|
||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||
import { imageDeleted, imageUrlsReceived } from 'services/thunks/image';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { dateComparator } from 'common/util/dateComparator';
|
||||
|
||||
export const uploadsAdapter = createEntityAdapter<Image>({
|
||||
selectId: (image) => image.name,
|
||||
sortComparer: (a, b) => b.metadata.created - a.metadata.created,
|
||||
export type UploadsImageDTO = Omit<ImageDTO, 'image_type'> & {
|
||||
image_type: 'uploads';
|
||||
};
|
||||
|
||||
export const uploadsAdapter = createEntityAdapter<UploadsImageDTO>({
|
||||
selectId: (image) => image.image_name,
|
||||
sortComparer: (a, b) => dateComparator(b.created_at, a.created_at),
|
||||
});
|
||||
|
||||
type AdditionalUploadsState = {
|
||||
@ -49,11 +53,12 @@ const uploadsSlice = createSlice({
|
||||
* Received Upload Images Page - FULFILLED
|
||||
*/
|
||||
builder.addCase(receivedUploadImagesPage.fulfilled, (state, action) => {
|
||||
const { items, page, pages } = action.payload;
|
||||
const { page, pages } = action.payload;
|
||||
|
||||
const images = items.map((image) => deserializeImageResponse(image));
|
||||
// We know these will all be of the uploads type, but it's not represented in the API types
|
||||
const items = action.payload.items as UploadsImageDTO[];
|
||||
|
||||
uploadsAdapter.setMany(state, images);
|
||||
uploadsAdapter.setMany(state, items);
|
||||
|
||||
state.page = page;
|
||||
state.pages = pages;
|
||||
@ -61,6 +66,24 @@ const uploadsSlice = createSlice({
|
||||
state.isLoading = false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Image URLs Received - FULFILLED
|
||||
*/
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_type, image_url, thumbnail_url } =
|
||||
action.payload;
|
||||
|
||||
if (image_type === 'uploads') {
|
||||
uploadsAdapter.updateOne(state, {
|
||||
id: image_name,
|
||||
changes: {
|
||||
image_url: image_url,
|
||||
thumbnail_url: thumbnail_url,
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Delete Image - pending
|
||||
* Pre-emptively remove the image from the gallery
|
||||
|
@ -1,10 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import { TransformComponent, useTransformContext } from 'react-zoom-pan-pinch';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { useGetUrl } from 'common/util/getUrl';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
type ReactPanZoomProps = {
|
||||
image: InvokeAI.Image;
|
||||
image: ImageDTO;
|
||||
styleClass?: string;
|
||||
alt?: string;
|
||||
ref?: React.Ref<HTMLImageElement>;
|
||||
@ -37,7 +37,7 @@ export default function ReactPanZoomImage({
|
||||
transform: `rotate(${rotation}deg) scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||
width: '100%',
|
||||
}}
|
||||
src={getUrl(image.url)}
|
||||
src={getUrl(image.image_url)}
|
||||
alt={alt}
|
||||
ref={ref}
|
||||
className={styleClass ? styleClass : ''}
|
||||
|
@ -21,7 +21,7 @@ const ImageInputFieldComponent = (
|
||||
|
||||
const getImageByNameAndType = useGetImageByNameAndType();
|
||||
const dispatch = useAppDispatch();
|
||||
const [url, setUrl] = useState<string>();
|
||||
const [url, setUrl] = useState<string | undefined>(field.value?.image_url);
|
||||
const { getUrl } = useGetUrl();
|
||||
|
||||
const handleDrop = useCallback(
|
||||
@ -39,16 +39,13 @@ const ImageInputFieldComponent = (
|
||||
return;
|
||||
}
|
||||
|
||||
setUrl(image.url);
|
||||
setUrl(image.image_url);
|
||||
|
||||
dispatch(
|
||||
fieldValueChanged({
|
||||
nodeId,
|
||||
fieldName: field.name,
|
||||
value: {
|
||||
image_name: name,
|
||||
image_type: type,
|
||||
},
|
||||
value: image,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
NodeChange,
|
||||
OnConnectStartParams,
|
||||
} from 'reactflow';
|
||||
import { ImageField } from 'services/api';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
||||
import { InvocationTemplate, InvocationValue } from '../types/types';
|
||||
import { parseSchema } from '../util/parseSchema';
|
||||
@ -65,13 +65,7 @@ const nodesSlice = createSlice({
|
||||
action: PayloadAction<{
|
||||
nodeId: string;
|
||||
fieldName: string;
|
||||
value:
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| Pick<ImageField, 'image_name' | 'image_type'>
|
||||
| RgbaColor
|
||||
| undefined;
|
||||
value: string | number | boolean | ImageDTO | RgbaColor | undefined;
|
||||
}>
|
||||
) => {
|
||||
const { nodeId, fieldName, value } = action.payload;
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import { RgbaColor } from 'react-colorful';
|
||||
import { ImageField } from 'services/api';
|
||||
import { Graph, ImageDTO } from 'services/api';
|
||||
import { AnyInvocationType } from 'services/events/types';
|
||||
import { O } from 'ts-toolbelt';
|
||||
|
||||
export type NonNullableGraph = O.Required<Graph, 'nodes' | 'edges'>;
|
||||
|
||||
export type InvocationValue = {
|
||||
id: string;
|
||||
@ -179,7 +182,7 @@ export type ConditioningInputFieldValue = FieldValueBase & {
|
||||
|
||||
export type ImageInputFieldValue = FieldValueBase & {
|
||||
type: 'image';
|
||||
value?: Pick<ImageField, 'image_name' | 'image_type'>;
|
||||
value?: ImageDTO;
|
||||
};
|
||||
|
||||
export type ModelInputFieldValue = FieldValueBase & {
|
||||
@ -245,7 +248,7 @@ export type BooleanInputFieldTemplate = InputFieldTemplateBase & {
|
||||
};
|
||||
|
||||
export type ImageInputFieldTemplate = InputFieldTemplateBase & {
|
||||
default: Pick<ImageField, 'image_name' | 'image_type'>;
|
||||
default: ImageDTO;
|
||||
type: 'image';
|
||||
};
|
||||
|
||||
|
@ -1,35 +1,131 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { Graph } from 'services/api';
|
||||
import { buildImg2ImgNode } from '../nodeBuilders/buildImageToImageNode';
|
||||
import { buildRangeNode } from '../nodeBuilders/buildRangeNode';
|
||||
import { buildIterateNode } from '../nodeBuilders/buildIterateNode';
|
||||
import { buildEdges } from '../edgeBuilders/buildEdges';
|
||||
import {
|
||||
CompelInvocation,
|
||||
Graph,
|
||||
ImageToLatentsInvocation,
|
||||
LatentsToImageInvocation,
|
||||
LatentsToLatentsInvocation,
|
||||
} from 'services/api';
|
||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||
import { addNoiseNodes } from '../nodeBuilders/addNoiseNodes';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'buildImageToImageGraph' });
|
||||
|
||||
const POSITIVE_CONDITIONING = 'positive_conditioning';
|
||||
const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
||||
const IMAGE_TO_LATENTS = 'image_to_latents';
|
||||
const LATENTS_TO_LATENTS = 'latents_to_latents';
|
||||
const LATENTS_TO_IMAGE = 'latents_to_image';
|
||||
|
||||
/**
|
||||
* Builds the Linear workflow graph.
|
||||
* Builds the Image to Image tab graph.
|
||||
*/
|
||||
export const buildImageToImageGraph = (state: RootState): Graph => {
|
||||
const baseNode = buildImg2ImgNode(state);
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
initialImage,
|
||||
img2imgStrength: strength,
|
||||
} = state.generation;
|
||||
|
||||
// We always range and iterate nodes, no matter the iteration count
|
||||
// This is required to provide the correct seeds to the backend engine
|
||||
const rangeNode = buildRangeNode(state);
|
||||
const iterateNode = buildIterateNode();
|
||||
if (!initialImage) {
|
||||
moduleLog.error('No initial image found in state');
|
||||
throw new Error('No initial image found in state');
|
||||
}
|
||||
|
||||
// Build the edges for the nodes selected.
|
||||
const edges = buildEdges(baseNode, rangeNode, iterateNode);
|
||||
|
||||
// Assemble!
|
||||
const graph = {
|
||||
nodes: {
|
||||
[rangeNode.id]: rangeNode,
|
||||
[iterateNode.id]: iterateNode,
|
||||
[baseNode.id]: baseNode,
|
||||
},
|
||||
edges,
|
||||
let graph: NonNullableGraph = {
|
||||
nodes: {},
|
||||
edges: [],
|
||||
};
|
||||
|
||||
// TODO: hires fix requires latent space upscaling; we don't have nodes for this yet
|
||||
// Create the conditioning, t2l and l2i nodes
|
||||
const positiveConditioningNode: CompelInvocation = {
|
||||
id: POSITIVE_CONDITIONING,
|
||||
type: 'compel',
|
||||
prompt: positivePrompt,
|
||||
model,
|
||||
};
|
||||
|
||||
const negativeConditioningNode: CompelInvocation = {
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
type: 'compel',
|
||||
prompt: negativePrompt,
|
||||
model,
|
||||
};
|
||||
|
||||
const imageToLatentsNode: ImageToLatentsInvocation = {
|
||||
id: IMAGE_TO_LATENTS,
|
||||
type: 'i2l',
|
||||
model,
|
||||
image: {
|
||||
image_name: initialImage?.image_name,
|
||||
image_type: initialImage?.image_type,
|
||||
},
|
||||
};
|
||||
|
||||
const latentsToLatentsNode: LatentsToLatentsInvocation = {
|
||||
id: LATENTS_TO_LATENTS,
|
||||
type: 'l2l',
|
||||
cfg_scale,
|
||||
model,
|
||||
scheduler,
|
||||
steps,
|
||||
strength,
|
||||
};
|
||||
|
||||
const latentsToImageNode: LatentsToImageInvocation = {
|
||||
id: LATENTS_TO_IMAGE,
|
||||
type: 'l2i',
|
||||
model,
|
||||
};
|
||||
|
||||
// Add to the graph
|
||||
graph.nodes[POSITIVE_CONDITIONING] = positiveConditioningNode;
|
||||
graph.nodes[NEGATIVE_CONDITIONING] = negativeConditioningNode;
|
||||
graph.nodes[IMAGE_TO_LATENTS] = imageToLatentsNode;
|
||||
graph.nodes[LATENTS_TO_LATENTS] = latentsToLatentsNode;
|
||||
graph.nodes[LATENTS_TO_IMAGE] = latentsToImageNode;
|
||||
|
||||
// Connect them
|
||||
graph.edges.push({
|
||||
source: { node_id: POSITIVE_CONDITIONING, field: 'conditioning' },
|
||||
destination: {
|
||||
node_id: LATENTS_TO_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: NEGATIVE_CONDITIONING, field: 'conditioning' },
|
||||
destination: {
|
||||
node_id: LATENTS_TO_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: IMAGE_TO_LATENTS, field: 'latents' },
|
||||
destination: {
|
||||
node_id: LATENTS_TO_LATENTS,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: LATENTS_TO_LATENTS, field: 'latents' },
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
|
||||
// Create and add the noise nodes
|
||||
graph = addNoiseNodes(graph, latentsToLatentsNode.id, state);
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
@ -1,35 +1,99 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { Graph } from 'services/api';
|
||||
import { buildTxt2ImgNode } from '../nodeBuilders/buildTextToImageNode';
|
||||
import { buildRangeNode } from '../nodeBuilders/buildRangeNode';
|
||||
import { buildIterateNode } from '../nodeBuilders/buildIterateNode';
|
||||
import { buildEdges } from '../edgeBuilders/buildEdges';
|
||||
import {
|
||||
CompelInvocation,
|
||||
Graph,
|
||||
LatentsToImageInvocation,
|
||||
TextToLatentsInvocation,
|
||||
} from 'services/api';
|
||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||
import { addNoiseNodes } from '../nodeBuilders/addNoiseNodes';
|
||||
|
||||
const POSITIVE_CONDITIONING = 'positive_conditioning';
|
||||
const NEGATIVE_CONDITIONING = 'negative_conditioning';
|
||||
const TEXT_TO_LATENTS = 'text_to_latents';
|
||||
const LATENTS_TO_IMAGE = 'latnets_to_image';
|
||||
|
||||
/**
|
||||
* Builds the Linear workflow graph.
|
||||
* Builds the Text to Image tab graph.
|
||||
*/
|
||||
export const buildTextToImageGraph = (state: RootState): Graph => {
|
||||
const baseNode = buildTxt2ImgNode(state);
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
} = state.generation;
|
||||
|
||||
// We always range and iterate nodes, no matter the iteration count
|
||||
// This is required to provide the correct seeds to the backend engine
|
||||
const rangeNode = buildRangeNode(state);
|
||||
const iterateNode = buildIterateNode();
|
||||
|
||||
// Build the edges for the nodes selected.
|
||||
const edges = buildEdges(baseNode, rangeNode, iterateNode);
|
||||
|
||||
// Assemble!
|
||||
const graph = {
|
||||
nodes: {
|
||||
[rangeNode.id]: rangeNode,
|
||||
[iterateNode.id]: iterateNode,
|
||||
[baseNode.id]: baseNode,
|
||||
},
|
||||
edges,
|
||||
let graph: NonNullableGraph = {
|
||||
nodes: {},
|
||||
edges: [],
|
||||
};
|
||||
|
||||
// TODO: hires fix requires latent space upscaling; we don't have nodes for this yet
|
||||
// Create the conditioning, t2l and l2i nodes
|
||||
const positiveConditioningNode: CompelInvocation = {
|
||||
id: POSITIVE_CONDITIONING,
|
||||
type: 'compel',
|
||||
prompt: positivePrompt,
|
||||
model,
|
||||
};
|
||||
|
||||
const negativeConditioningNode: CompelInvocation = {
|
||||
id: NEGATIVE_CONDITIONING,
|
||||
type: 'compel',
|
||||
prompt: negativePrompt,
|
||||
model,
|
||||
};
|
||||
|
||||
const textToLatentsNode: TextToLatentsInvocation = {
|
||||
id: TEXT_TO_LATENTS,
|
||||
type: 't2l',
|
||||
cfg_scale,
|
||||
model,
|
||||
scheduler,
|
||||
steps,
|
||||
};
|
||||
|
||||
const latentsToImageNode: LatentsToImageInvocation = {
|
||||
id: LATENTS_TO_IMAGE,
|
||||
type: 'l2i',
|
||||
model,
|
||||
};
|
||||
|
||||
// Add to the graph
|
||||
graph.nodes[POSITIVE_CONDITIONING] = positiveConditioningNode;
|
||||
graph.nodes[NEGATIVE_CONDITIONING] = negativeConditioningNode;
|
||||
graph.nodes[TEXT_TO_LATENTS] = textToLatentsNode;
|
||||
graph.nodes[LATENTS_TO_IMAGE] = latentsToImageNode;
|
||||
|
||||
// Connect them
|
||||
graph.edges.push({
|
||||
source: { node_id: POSITIVE_CONDITIONING, field: 'conditioning' },
|
||||
destination: {
|
||||
node_id: TEXT_TO_LATENTS,
|
||||
field: 'positive_conditioning',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: NEGATIVE_CONDITIONING, field: 'conditioning' },
|
||||
destination: {
|
||||
node_id: TEXT_TO_LATENTS,
|
||||
field: 'negative_conditioning',
|
||||
},
|
||||
});
|
||||
|
||||
graph.edges.push({
|
||||
source: { node_id: TEXT_TO_LATENTS, field: 'latents' },
|
||||
destination: {
|
||||
node_id: LATENTS_TO_IMAGE,
|
||||
field: 'latents',
|
||||
},
|
||||
});
|
||||
|
||||
// Create and add the noise nodes
|
||||
graph = addNoiseNodes(graph, TEXT_TO_LATENTS, state);
|
||||
|
||||
return graph;
|
||||
};
|
||||
|
@ -0,0 +1,208 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import {
|
||||
IterateInvocation,
|
||||
NoiseInvocation,
|
||||
RandomIntInvocation,
|
||||
RangeOfSizeInvocation,
|
||||
} from 'services/api';
|
||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const NOISE = 'noise';
|
||||
const RANDOM_INT = 'rand_int';
|
||||
const RANGE_OF_SIZE = 'range_of_size';
|
||||
const ITERATE = 'iterate';
|
||||
/**
|
||||
* Adds the appropriate noise nodes to a linear UI t2l or l2l graph.
|
||||
*
|
||||
* @param graph The graph to add the noise nodes to.
|
||||
* @param baseNodeId The id of the base node to connect the noise nodes to.
|
||||
* @param state The app state..
|
||||
*/
|
||||
export const addNoiseNodes = (
|
||||
graph: NonNullableGraph,
|
||||
baseNodeId: string,
|
||||
state: RootState
|
||||
): NonNullableGraph => {
|
||||
const graphClone = cloneDeep(graph);
|
||||
|
||||
// Create and add the noise nodes
|
||||
const { width, height, seed, iterations, shouldRandomizeSeed } =
|
||||
state.generation;
|
||||
|
||||
// Single iteration, explicit seed
|
||||
if (!shouldRandomizeSeed && iterations === 1) {
|
||||
const noiseNode: NoiseInvocation = {
|
||||
id: NOISE,
|
||||
type: 'noise',
|
||||
seed: seed,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
|
||||
graphClone.nodes[NOISE] = noiseNode;
|
||||
|
||||
// Connect them
|
||||
graphClone.edges.push({
|
||||
source: { node_id: NOISE, field: 'noise' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'noise',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Single iteration, random seed
|
||||
if (shouldRandomizeSeed && iterations === 1) {
|
||||
// TODO: This assumes the `high` value is the max seed value
|
||||
const randomIntNode: RandomIntInvocation = {
|
||||
id: RANDOM_INT,
|
||||
type: 'rand_int',
|
||||
};
|
||||
|
||||
const noiseNode: NoiseInvocation = {
|
||||
id: NOISE,
|
||||
type: 'noise',
|
||||
width,
|
||||
height,
|
||||
};
|
||||
|
||||
graphClone.nodes[RANDOM_INT] = randomIntNode;
|
||||
graphClone.nodes[NOISE] = noiseNode;
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: RANDOM_INT, field: 'a' },
|
||||
destination: {
|
||||
node_id: NOISE,
|
||||
field: 'seed',
|
||||
},
|
||||
});
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: NOISE, field: 'noise' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'noise',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Multiple iterations, explicit seed
|
||||
if (!shouldRandomizeSeed && iterations > 1) {
|
||||
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
||||
id: RANGE_OF_SIZE,
|
||||
type: 'range_of_size',
|
||||
start: seed,
|
||||
size: iterations,
|
||||
};
|
||||
|
||||
const iterateNode: IterateInvocation = {
|
||||
id: ITERATE,
|
||||
type: 'iterate',
|
||||
};
|
||||
|
||||
const noiseNode: NoiseInvocation = {
|
||||
id: NOISE,
|
||||
type: 'noise',
|
||||
width,
|
||||
height,
|
||||
};
|
||||
|
||||
graphClone.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
||||
graphClone.nodes[ITERATE] = iterateNode;
|
||||
graphClone.nodes[NOISE] = noiseNode;
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
||||
destination: {
|
||||
node_id: ITERATE,
|
||||
field: 'collection',
|
||||
},
|
||||
});
|
||||
|
||||
graphClone.edges.push({
|
||||
source: {
|
||||
node_id: ITERATE,
|
||||
field: 'item',
|
||||
},
|
||||
destination: {
|
||||
node_id: NOISE,
|
||||
field: 'seed',
|
||||
},
|
||||
});
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: NOISE, field: 'noise' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'noise',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Multiple iterations, random seed
|
||||
if (shouldRandomizeSeed && iterations > 1) {
|
||||
// TODO: This assumes the `high` value is the max seed value
|
||||
const randomIntNode: RandomIntInvocation = {
|
||||
id: RANDOM_INT,
|
||||
type: 'rand_int',
|
||||
};
|
||||
|
||||
const rangeOfSizeNode: RangeOfSizeInvocation = {
|
||||
id: RANGE_OF_SIZE,
|
||||
type: 'range_of_size',
|
||||
size: iterations,
|
||||
};
|
||||
|
||||
const iterateNode: IterateInvocation = {
|
||||
id: ITERATE,
|
||||
type: 'iterate',
|
||||
};
|
||||
|
||||
const noiseNode: NoiseInvocation = {
|
||||
id: NOISE,
|
||||
type: 'noise',
|
||||
width,
|
||||
height,
|
||||
};
|
||||
|
||||
graphClone.nodes[RANDOM_INT] = randomIntNode;
|
||||
graphClone.nodes[RANGE_OF_SIZE] = rangeOfSizeNode;
|
||||
graphClone.nodes[ITERATE] = iterateNode;
|
||||
graphClone.nodes[NOISE] = noiseNode;
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: RANDOM_INT, field: 'a' },
|
||||
destination: { node_id: RANGE_OF_SIZE, field: 'start' },
|
||||
});
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: RANGE_OF_SIZE, field: 'collection' },
|
||||
destination: {
|
||||
node_id: ITERATE,
|
||||
field: 'collection',
|
||||
},
|
||||
});
|
||||
|
||||
graphClone.edges.push({
|
||||
source: {
|
||||
node_id: ITERATE,
|
||||
field: 'item',
|
||||
},
|
||||
destination: {
|
||||
node_id: NOISE,
|
||||
field: 'seed',
|
||||
},
|
||||
});
|
||||
|
||||
graphClone.edges.push({
|
||||
source: { node_id: NOISE, field: 'noise' },
|
||||
destination: {
|
||||
node_id: baseNodeId,
|
||||
field: 'noise',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return graphClone;
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { CompelInvocation } from 'services/api';
|
||||
import { O } from 'ts-toolbelt';
|
||||
|
||||
export const buildCompelNode = (
|
||||
prompt: string,
|
||||
state: RootState,
|
||||
overrides: O.Partial<CompelInvocation, 'deep'> = {}
|
||||
): CompelInvocation => {
|
||||
const nodeId = uuidv4();
|
||||
const { generation } = state;
|
||||
|
||||
const { model } = generation;
|
||||
|
||||
const compelNode: CompelInvocation = {
|
||||
id: nodeId,
|
||||
type: 'compel',
|
||||
prompt,
|
||||
model,
|
||||
};
|
||||
|
||||
Object.assign(compelNode, overrides);
|
||||
|
||||
return compelNode;
|
||||
};
|
@ -18,8 +18,8 @@ export const buildImg2ImgNode = (
|
||||
const activeTabName = activeTabNameSelector(state);
|
||||
|
||||
const {
|
||||
prompt,
|
||||
negativePrompt,
|
||||
positivePrompt: prompt,
|
||||
negativePrompt: negativePrompt,
|
||||
seed,
|
||||
steps,
|
||||
width,
|
||||
|
@ -13,8 +13,8 @@ export const buildInpaintNode = (
|
||||
const activeTabName = activeTabNameSelector(state);
|
||||
|
||||
const {
|
||||
prompt,
|
||||
negativePrompt,
|
||||
positivePrompt: prompt,
|
||||
negativePrompt: negativePrompt,
|
||||
seed,
|
||||
steps,
|
||||
width,
|
||||
|
@ -11,8 +11,8 @@ export const buildTxt2ImgNode = (
|
||||
const { generation } = state;
|
||||
|
||||
const {
|
||||
prompt,
|
||||
negativePrompt,
|
||||
positivePrompt: prompt,
|
||||
negativePrompt: negativePrompt,
|
||||
seed,
|
||||
steps,
|
||||
width,
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
buildOutputFieldTemplates,
|
||||
} from './fieldTemplateBuilders';
|
||||
|
||||
const invocationDenylist = ['Graph', 'LoadImage'];
|
||||
const invocationDenylist = ['Graph'];
|
||||
|
||||
export const parseSchema = (openAPI: OpenAPIV3.Document) => {
|
||||
// filter out non-invocation schemas, plus some tricky invocations for now
|
||||
|
@ -8,7 +8,7 @@ import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||
import {
|
||||
GenerationState,
|
||||
clampSymmetrySteps,
|
||||
setPrompt,
|
||||
setPositivePrompt,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
|
||||
@ -22,7 +22,7 @@ const promptInputSelector = createSelector(
|
||||
[(state: RootState) => state.generation, activeTabNameSelector],
|
||||
(parameters: GenerationState, activeTabName) => {
|
||||
return {
|
||||
prompt: parameters.prompt,
|
||||
prompt: parameters.positivePrompt,
|
||||
activeTabName,
|
||||
};
|
||||
},
|
||||
@ -46,7 +46,7 @@ const ParamPositiveConditioning = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleChangePrompt = (e: ChangeEvent<HTMLTextAreaElement>) => {
|
||||
dispatch(setPrompt(e.target.value));
|
||||
dispatch(setPositivePrompt(e.target.value));
|
||||
};
|
||||
|
||||
useHotkeys(
|
||||
|
@ -57,7 +57,7 @@ const InitialImagePreview = () => {
|
||||
const name = e.dataTransfer.getData('invokeai/imageName');
|
||||
const type = e.dataTransfer.getData('invokeai/imageType') as ImageType;
|
||||
|
||||
dispatch(initialImageSelected({ name, type }));
|
||||
dispatch(initialImageSelected({ image_name: name, image_type: type }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
@ -73,10 +73,10 @@ const InitialImagePreview = () => {
|
||||
}}
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
{initialImage?.url && (
|
||||
{initialImage?.image_url && (
|
||||
<>
|
||||
<Image
|
||||
src={getUrl(initialImage?.url)}
|
||||
src={getUrl(initialImage?.image_url)}
|
||||
fallbackStrategy="beforeLoadOrError"
|
||||
fallback={<ImageFallbackSpinner />}
|
||||
onError={handleError}
|
||||
@ -92,7 +92,7 @@ const InitialImagePreview = () => {
|
||||
<ImageMetadataOverlay image={initialImage} />
|
||||
</>
|
||||
)}
|
||||
{!initialImage?.url && (
|
||||
{!initialImage?.image_url && (
|
||||
<Icon
|
||||
as={FaImage}
|
||||
sx={{
|
||||
|
@ -7,9 +7,9 @@ import { allParametersSet, setSeed } from '../store/generationSlice';
|
||||
import { isImageField } from 'services/types/guards';
|
||||
import { NUMPY_RAND_MAX } from 'app/constants';
|
||||
import { initialImageSelected } from '../store/actions';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { useAppToaster } from 'app/components/Toaster';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export const useParameters = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@ -88,9 +88,7 @@ export const useParameters = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(
|
||||
initialImageSelected({ name: image.image_name, type: image.image_type })
|
||||
);
|
||||
dispatch(initialImageSelected(image));
|
||||
toaster({
|
||||
title: t('toast.initialImageSet'),
|
||||
status: 'info',
|
||||
@ -105,21 +103,21 @@ export const useParameters = () => {
|
||||
* Sets image as initial image with toast
|
||||
*/
|
||||
const sendToImageToImage = useCallback(
|
||||
(image: Image) => {
|
||||
dispatch(initialImageSelected({ name: image.name, type: image.type }));
|
||||
(image: ImageDTO) => {
|
||||
dispatch(initialImageSelected(image));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const recallAllParameters = useCallback(
|
||||
(image: Image | undefined) => {
|
||||
const type = image?.metadata?.invokeai?.node?.type;
|
||||
(image: ImageDTO | undefined) => {
|
||||
const type = image?.metadata?.type;
|
||||
if (['txt2img', 'img2img', 'inpaint'].includes(String(type))) {
|
||||
dispatch(allParametersSet(image));
|
||||
|
||||
if (image?.metadata?.invokeai?.node?.type === 'img2img') {
|
||||
if (image?.metadata?.type === 'img2img') {
|
||||
dispatch(setActiveTab('img2img'));
|
||||
} else if (image?.metadata?.invokeai?.node?.type === 'txt2img') {
|
||||
} else if (image?.metadata?.type === 'txt2img') {
|
||||
dispatch(setActiveTab('txt2img'));
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import promptToString from 'common/util/promptToString';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { setNegativePrompt, setPrompt } from '../store/generationSlice';
|
||||
import { setNegativePrompt, setPositivePrompt } from '../store/generationSlice';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
// TECHDEBT: We have two metadata prompt formats and need to handle recalling either of them.
|
||||
@ -20,7 +20,7 @@ const useSetBothPrompts = () => {
|
||||
|
||||
const [prompt, negativePrompt] = getPromptAndNegative(promptString);
|
||||
|
||||
dispatch(setPrompt(prompt));
|
||||
dispatch(setPositivePrompt(prompt));
|
||||
dispatch(setNegativePrompt(negativePrompt));
|
||||
},
|
||||
[dispatch]
|
||||
|
@ -1,12 +1,31 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { ImageType } from 'services/api';
|
||||
import { isObject } from 'lodash-es';
|
||||
import { ImageDTO, ImageType } from 'services/api';
|
||||
|
||||
export type SelectedImage = {
|
||||
name: string;
|
||||
type: ImageType;
|
||||
export type ImageNameAndType = {
|
||||
image_name: string;
|
||||
image_type: ImageType;
|
||||
};
|
||||
|
||||
export const isImageDTO = (image: any): image is ImageDTO => {
|
||||
return (
|
||||
image &&
|
||||
isObject(image) &&
|
||||
'image_name' in image &&
|
||||
image?.image_name !== undefined &&
|
||||
'image_type' in image &&
|
||||
image?.image_type !== undefined &&
|
||||
'image_url' in image &&
|
||||
image?.image_url !== undefined &&
|
||||
'thumbnail_url' in image &&
|
||||
image?.thumbnail_url !== undefined &&
|
||||
'image_category' in image &&
|
||||
image?.image_category !== undefined &&
|
||||
'created_at' in image &&
|
||||
image?.created_at !== undefined
|
||||
);
|
||||
};
|
||||
|
||||
export const initialImageSelected = createAction<
|
||||
Image | SelectedImage | undefined
|
||||
ImageDTO | ImageNameAndType | undefined
|
||||
>('generation/initialImageSelected');
|
||||
|
@ -6,16 +6,17 @@ import { clamp, sample } from 'lodash-es';
|
||||
import { setAllParametersReducer } from './setAllParametersReducer';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
import { Scheduler } from 'app/constants';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
export interface GenerationState {
|
||||
cfgScale: number;
|
||||
height: number;
|
||||
img2imgStrength: number;
|
||||
infillMethod: string;
|
||||
initialImage?: InvokeAI.Image;
|
||||
initialImage?: ImageDTO;
|
||||
iterations: number;
|
||||
perlin: number;
|
||||
prompt: string;
|
||||
positivePrompt: string;
|
||||
negativePrompt: string;
|
||||
scheduler: Scheduler;
|
||||
seamBlur: number;
|
||||
@ -49,7 +50,7 @@ export const initialGenerationState: GenerationState = {
|
||||
infillMethod: 'patchmatch',
|
||||
iterations: 1,
|
||||
perlin: 0,
|
||||
prompt: '',
|
||||
positivePrompt: '',
|
||||
negativePrompt: '',
|
||||
scheduler: 'lms',
|
||||
seamBlur: 16,
|
||||
@ -82,12 +83,15 @@ export const generationSlice = createSlice({
|
||||
name: 'generation',
|
||||
initialState,
|
||||
reducers: {
|
||||
setPrompt: (state, action: PayloadAction<string | InvokeAI.Prompt>) => {
|
||||
setPositivePrompt: (
|
||||
state,
|
||||
action: PayloadAction<string | InvokeAI.Prompt>
|
||||
) => {
|
||||
const newPrompt = action.payload;
|
||||
if (typeof newPrompt === 'string') {
|
||||
state.prompt = newPrompt;
|
||||
state.positivePrompt = newPrompt;
|
||||
} else {
|
||||
state.prompt = promptToString(newPrompt);
|
||||
state.positivePrompt = promptToString(newPrompt);
|
||||
}
|
||||
},
|
||||
setNegativePrompt: (
|
||||
@ -213,7 +217,7 @@ export const generationSlice = createSlice({
|
||||
setShouldUseNoiseSettings: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldUseNoiseSettings = action.payload;
|
||||
},
|
||||
initialImageChanged: (state, action: PayloadAction<InvokeAI.Image>) => {
|
||||
initialImageChanged: (state, action: PayloadAction<ImageDTO>) => {
|
||||
state.initialImage = action.payload;
|
||||
},
|
||||
modelSelected: (state, action: PayloadAction<string>) => {
|
||||
@ -243,7 +247,7 @@ export const {
|
||||
setInfillMethod,
|
||||
setIterations,
|
||||
setPerlin,
|
||||
setPrompt,
|
||||
setPositivePrompt,
|
||||
setNegativePrompt,
|
||||
setScheduler,
|
||||
setSeamBlur,
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { Draft, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { GenerationState } from './generationSlice';
|
||||
import { ImageToImageInvocation } from 'services/api';
|
||||
import { ImageDTO, ImageToImageInvocation } from 'services/api';
|
||||
import { isScheduler } from 'app/constants';
|
||||
|
||||
export const setAllParametersReducer = (
|
||||
state: Draft<GenerationState>,
|
||||
action: PayloadAction<Image | undefined>
|
||||
action: PayloadAction<ImageDTO | undefined>
|
||||
) => {
|
||||
const node = action.payload?.metadata.invokeai?.node;
|
||||
|
||||
@ -32,7 +31,7 @@ export const setAllParametersReducer = (
|
||||
state.model = String(model);
|
||||
}
|
||||
if (prompt !== undefined) {
|
||||
state.prompt = String(prompt);
|
||||
state.positivePrompt = String(prompt);
|
||||
}
|
||||
if (scheduler !== undefined) {
|
||||
const schedulerString = String(scheduler);
|
||||
|
@ -5,7 +5,6 @@ import { merge } from 'lodash-es';
|
||||
|
||||
export const initialConfigState: AppConfig = {
|
||||
shouldTransformUrls: false,
|
||||
shouldFetchImages: false,
|
||||
disabledTabs: [],
|
||||
disabledFeatures: [],
|
||||
disabledSDFeatures: [],
|
||||
|
@ -3,4 +3,4 @@ import { UIState } from './uiTypes';
|
||||
/**
|
||||
* UI slice persist denylist
|
||||
*/
|
||||
export const uiPersistDenylist: (keyof UIState)[] = [];
|
||||
export const uiPersistDenylist: (keyof UIState)[] = ['shouldShowImageDetails'];
|
||||
|
@ -28,13 +28,15 @@ export type { GraphExecutionState } from './models/GraphExecutionState';
|
||||
export type { GraphInvocation } from './models/GraphInvocation';
|
||||
export type { GraphInvocationOutput } from './models/GraphInvocationOutput';
|
||||
export type { HTTPValidationError } from './models/HTTPValidationError';
|
||||
export type { ImageCategory } from './models/ImageCategory';
|
||||
export type { ImageDTO } from './models/ImageDTO';
|
||||
export type { ImageField } from './models/ImageField';
|
||||
export type { ImageMetadata } from './models/ImageMetadata';
|
||||
export type { ImageOutput } from './models/ImageOutput';
|
||||
export type { ImageResponse } from './models/ImageResponse';
|
||||
export type { ImageResponseMetadata } from './models/ImageResponseMetadata';
|
||||
export type { ImageToImageInvocation } from './models/ImageToImageInvocation';
|
||||
export type { ImageToLatentsInvocation } from './models/ImageToLatentsInvocation';
|
||||
export type { ImageType } from './models/ImageType';
|
||||
export type { ImageUrlsDTO } from './models/ImageUrlsDTO';
|
||||
export type { InfillColorInvocation } from './models/InfillColorInvocation';
|
||||
export type { InfillPatchMatchInvocation } from './models/InfillPatchMatchInvocation';
|
||||
export type { InfillTileInvocation } from './models/InfillTileInvocation';
|
||||
@ -42,7 +44,6 @@ export type { InpaintInvocation } from './models/InpaintInvocation';
|
||||
export type { IntCollectionOutput } from './models/IntCollectionOutput';
|
||||
export type { IntOutput } from './models/IntOutput';
|
||||
export type { InverseLerpInvocation } from './models/InverseLerpInvocation';
|
||||
export type { InvokeAIMetadata } from './models/InvokeAIMetadata';
|
||||
export type { IterateInvocation } from './models/IterateInvocation';
|
||||
export type { IterateInvocationOutput } from './models/IterateInvocationOutput';
|
||||
export type { LatentsField } from './models/LatentsField';
|
||||
@ -53,21 +54,19 @@ export type { LerpInvocation } from './models/LerpInvocation';
|
||||
export type { LoadImageInvocation } from './models/LoadImageInvocation';
|
||||
export type { MaskFromAlphaInvocation } from './models/MaskFromAlphaInvocation';
|
||||
export type { MaskOutput } from './models/MaskOutput';
|
||||
export type { MetadataColorField } from './models/MetadataColorField';
|
||||
export type { MetadataImageField } from './models/MetadataImageField';
|
||||
export type { MetadataLatentsField } from './models/MetadataLatentsField';
|
||||
export type { ModelsList } from './models/ModelsList';
|
||||
export type { MultiplyInvocation } from './models/MultiplyInvocation';
|
||||
export type { NoiseInvocation } from './models/NoiseInvocation';
|
||||
export type { NoiseOutput } from './models/NoiseOutput';
|
||||
export type { PaginatedResults_GraphExecutionState_ } from './models/PaginatedResults_GraphExecutionState_';
|
||||
export type { PaginatedResults_ImageResponse_ } from './models/PaginatedResults_ImageResponse_';
|
||||
export type { PaginatedResults_ImageDTO_ } from './models/PaginatedResults_ImageDTO_';
|
||||
export type { ParamIntInvocation } from './models/ParamIntInvocation';
|
||||
export type { PasteImageInvocation } from './models/PasteImageInvocation';
|
||||
export type { PromptOutput } from './models/PromptOutput';
|
||||
export type { RandomIntInvocation } from './models/RandomIntInvocation';
|
||||
export type { RandomRangeInvocation } from './models/RandomRangeInvocation';
|
||||
export type { RangeInvocation } from './models/RangeInvocation';
|
||||
export type { RangeOfSizeInvocation } from './models/RangeOfSizeInvocation';
|
||||
export type { ResizeLatentsInvocation } from './models/ResizeLatentsInvocation';
|
||||
export type { RestoreFaceInvocation } from './models/RestoreFaceInvocation';
|
||||
export type { ScaleLatentsInvocation } from './models/ScaleLatentsInvocation';
|
||||
@ -79,79 +78,6 @@ export type { UpscaleInvocation } from './models/UpscaleInvocation';
|
||||
export type { VaeRepo } from './models/VaeRepo';
|
||||
export type { ValidationError } from './models/ValidationError';
|
||||
|
||||
export { $AddInvocation } from './schemas/$AddInvocation';
|
||||
export { $BlurInvocation } from './schemas/$BlurInvocation';
|
||||
export { $Body_upload_image } from './schemas/$Body_upload_image';
|
||||
export { $CkptModelInfo } from './schemas/$CkptModelInfo';
|
||||
export { $CollectInvocation } from './schemas/$CollectInvocation';
|
||||
export { $CollectInvocationOutput } from './schemas/$CollectInvocationOutput';
|
||||
export { $ColorField } from './schemas/$ColorField';
|
||||
export { $CompelInvocation } from './schemas/$CompelInvocation';
|
||||
export { $CompelOutput } from './schemas/$CompelOutput';
|
||||
export { $ConditioningField } from './schemas/$ConditioningField';
|
||||
export { $CreateModelRequest } from './schemas/$CreateModelRequest';
|
||||
export { $CropImageInvocation } from './schemas/$CropImageInvocation';
|
||||
export { $CvInpaintInvocation } from './schemas/$CvInpaintInvocation';
|
||||
export { $DiffusersModelInfo } from './schemas/$DiffusersModelInfo';
|
||||
export { $DivideInvocation } from './schemas/$DivideInvocation';
|
||||
export { $Edge } from './schemas/$Edge';
|
||||
export { $EdgeConnection } from './schemas/$EdgeConnection';
|
||||
export { $Graph } from './schemas/$Graph';
|
||||
export { $GraphExecutionState } from './schemas/$GraphExecutionState';
|
||||
export { $GraphInvocation } from './schemas/$GraphInvocation';
|
||||
export { $GraphInvocationOutput } from './schemas/$GraphInvocationOutput';
|
||||
export { $HTTPValidationError } from './schemas/$HTTPValidationError';
|
||||
export { $ImageField } from './schemas/$ImageField';
|
||||
export { $ImageOutput } from './schemas/$ImageOutput';
|
||||
export { $ImageResponse } from './schemas/$ImageResponse';
|
||||
export { $ImageResponseMetadata } from './schemas/$ImageResponseMetadata';
|
||||
export { $ImageToImageInvocation } from './schemas/$ImageToImageInvocation';
|
||||
export { $ImageToLatentsInvocation } from './schemas/$ImageToLatentsInvocation';
|
||||
export { $ImageType } from './schemas/$ImageType';
|
||||
export { $InfillColorInvocation } from './schemas/$InfillColorInvocation';
|
||||
export { $InfillPatchMatchInvocation } from './schemas/$InfillPatchMatchInvocation';
|
||||
export { $InfillTileInvocation } from './schemas/$InfillTileInvocation';
|
||||
export { $InpaintInvocation } from './schemas/$InpaintInvocation';
|
||||
export { $IntCollectionOutput } from './schemas/$IntCollectionOutput';
|
||||
export { $IntOutput } from './schemas/$IntOutput';
|
||||
export { $InverseLerpInvocation } from './schemas/$InverseLerpInvocation';
|
||||
export { $InvokeAIMetadata } from './schemas/$InvokeAIMetadata';
|
||||
export { $IterateInvocation } from './schemas/$IterateInvocation';
|
||||
export { $IterateInvocationOutput } from './schemas/$IterateInvocationOutput';
|
||||
export { $LatentsField } from './schemas/$LatentsField';
|
||||
export { $LatentsOutput } from './schemas/$LatentsOutput';
|
||||
export { $LatentsToImageInvocation } from './schemas/$LatentsToImageInvocation';
|
||||
export { $LatentsToLatentsInvocation } from './schemas/$LatentsToLatentsInvocation';
|
||||
export { $LerpInvocation } from './schemas/$LerpInvocation';
|
||||
export { $LoadImageInvocation } from './schemas/$LoadImageInvocation';
|
||||
export { $MaskFromAlphaInvocation } from './schemas/$MaskFromAlphaInvocation';
|
||||
export { $MaskOutput } from './schemas/$MaskOutput';
|
||||
export { $MetadataColorField } from './schemas/$MetadataColorField';
|
||||
export { $MetadataImageField } from './schemas/$MetadataImageField';
|
||||
export { $MetadataLatentsField } from './schemas/$MetadataLatentsField';
|
||||
export { $ModelsList } from './schemas/$ModelsList';
|
||||
export { $MultiplyInvocation } from './schemas/$MultiplyInvocation';
|
||||
export { $NoiseInvocation } from './schemas/$NoiseInvocation';
|
||||
export { $NoiseOutput } from './schemas/$NoiseOutput';
|
||||
export { $PaginatedResults_GraphExecutionState_ } from './schemas/$PaginatedResults_GraphExecutionState_';
|
||||
export { $PaginatedResults_ImageResponse_ } from './schemas/$PaginatedResults_ImageResponse_';
|
||||
export { $ParamIntInvocation } from './schemas/$ParamIntInvocation';
|
||||
export { $PasteImageInvocation } from './schemas/$PasteImageInvocation';
|
||||
export { $PromptOutput } from './schemas/$PromptOutput';
|
||||
export { $RandomIntInvocation } from './schemas/$RandomIntInvocation';
|
||||
export { $RandomRangeInvocation } from './schemas/$RandomRangeInvocation';
|
||||
export { $RangeInvocation } from './schemas/$RangeInvocation';
|
||||
export { $ResizeLatentsInvocation } from './schemas/$ResizeLatentsInvocation';
|
||||
export { $RestoreFaceInvocation } from './schemas/$RestoreFaceInvocation';
|
||||
export { $ScaleLatentsInvocation } from './schemas/$ScaleLatentsInvocation';
|
||||
export { $ShowImageInvocation } from './schemas/$ShowImageInvocation';
|
||||
export { $SubtractInvocation } from './schemas/$SubtractInvocation';
|
||||
export { $TextToImageInvocation } from './schemas/$TextToImageInvocation';
|
||||
export { $TextToLatentsInvocation } from './schemas/$TextToLatentsInvocation';
|
||||
export { $UpscaleInvocation } from './schemas/$UpscaleInvocation';
|
||||
export { $VaeRepo } from './schemas/$VaeRepo';
|
||||
export { $ValidationError } from './schemas/$ValidationError';
|
||||
|
||||
export { ImagesService } from './services/ImagesService';
|
||||
export { ModelsService } from './services/ModelsService';
|
||||
export { SessionsService } from './services/SessionsService';
|
||||
|
@ -31,6 +31,7 @@ import type { PasteImageInvocation } from './PasteImageInvocation';
|
||||
import type { RandomIntInvocation } from './RandomIntInvocation';
|
||||
import type { RandomRangeInvocation } from './RandomRangeInvocation';
|
||||
import type { RangeInvocation } from './RangeInvocation';
|
||||
import type { RangeOfSizeInvocation } from './RangeOfSizeInvocation';
|
||||
import type { ResizeLatentsInvocation } from './ResizeLatentsInvocation';
|
||||
import type { RestoreFaceInvocation } from './RestoreFaceInvocation';
|
||||
import type { ScaleLatentsInvocation } from './ScaleLatentsInvocation';
|
||||
@ -48,7 +49,7 @@ export type Graph = {
|
||||
/**
|
||||
* The nodes in this graph
|
||||
*/
|
||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation)>;
|
||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation)>;
|
||||
/**
|
||||
* The connections between nodes and their fields in this graph
|
||||
*/
|
||||
|
@ -42,7 +42,7 @@ export type GraphExecutionState = {
|
||||
/**
|
||||
* The results of node executions
|
||||
*/
|
||||
results: Record<string, (ImageOutput | MaskOutput | CompelOutput | LatentsOutput | NoiseOutput | IntOutput | PromptOutput | IntCollectionOutput | GraphInvocationOutput | IterateInvocationOutput | CollectInvocationOutput)>;
|
||||
results: Record<string, (ImageOutput | MaskOutput | PromptOutput | CompelOutput | IntOutput | LatentsOutput | NoiseOutput | IntCollectionOutput | GraphInvocationOutput | IterateInvocationOutput | CollectInvocationOutput)>;
|
||||
/**
|
||||
* Errors raised when executing nodes
|
||||
*/
|
||||
|
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* The category of an image. Use ImageCategory.OTHER for non-default categories.
|
||||
*/
|
||||
export type ImageCategory = 'general' | 'control' | 'other';
|
66
invokeai/frontend/web/src/services/api/models/ImageDTO.ts
Normal file
66
invokeai/frontend/web/src/services/api/models/ImageDTO.ts
Normal file
@ -0,0 +1,66 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ImageCategory } from './ImageCategory';
|
||||
import type { ImageMetadata } from './ImageMetadata';
|
||||
import type { ImageType } from './ImageType';
|
||||
|
||||
/**
|
||||
* Deserialized image record, enriched for the frontend with URLs.
|
||||
*/
|
||||
export type ImageDTO = {
|
||||
/**
|
||||
* The unique name of the image.
|
||||
*/
|
||||
image_name: string;
|
||||
/**
|
||||
* The type of the image.
|
||||
*/
|
||||
image_type: ImageType;
|
||||
/**
|
||||
* The URL of the image.
|
||||
*/
|
||||
image_url: string;
|
||||
/**
|
||||
* The URL of the image's thumbnail.
|
||||
*/
|
||||
thumbnail_url: string;
|
||||
/**
|
||||
* The category of the image.
|
||||
*/
|
||||
image_category: ImageCategory;
|
||||
/**
|
||||
* The width of the image in px.
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* The height of the image in px.
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* The created timestamp of the image.
|
||||
*/
|
||||
created_at: string;
|
||||
/**
|
||||
* The updated timestamp of the image.
|
||||
*/
|
||||
updated_at: string;
|
||||
/**
|
||||
* The deleted timestamp of the image.
|
||||
*/
|
||||
deleted_at?: string;
|
||||
/**
|
||||
* The session ID that generated this image, if it is a generated image.
|
||||
*/
|
||||
session_id?: string;
|
||||
/**
|
||||
* The node ID that generated this image, if it is a generated image.
|
||||
*/
|
||||
node_id?: string;
|
||||
/**
|
||||
* A limited subset of the image's generation metadata. Retrieve the image's session for full metadata.
|
||||
*/
|
||||
metadata?: ImageMetadata;
|
||||
};
|
||||
|
@ -0,0 +1,81 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Core generation metadata for an image/tensor generated in InvokeAI.
|
||||
*
|
||||
* Also includes any metadata from the image's PNG tEXt chunks.
|
||||
*
|
||||
* Generated by traversing the execution graph, collecting the parameters of the nearest ancestors
|
||||
* of a given node.
|
||||
*
|
||||
* Full metadata may be accessed by querying for the session in the `graph_executions` table.
|
||||
*/
|
||||
export type ImageMetadata = {
|
||||
/**
|
||||
* The type of the ancestor node of the image output node.
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* The positive conditioning.
|
||||
*/
|
||||
positive_conditioning?: string;
|
||||
/**
|
||||
* The negative conditioning.
|
||||
*/
|
||||
negative_conditioning?: string;
|
||||
/**
|
||||
* Width of the image/latents in pixels.
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* Height of the image/latents in pixels.
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* The seed used for noise generation.
|
||||
*/
|
||||
seed?: number;
|
||||
/**
|
||||
* The classifier-free guidance scale.
|
||||
*/
|
||||
cfg_scale?: number;
|
||||
/**
|
||||
* The number of steps used for inference.
|
||||
*/
|
||||
steps?: number;
|
||||
/**
|
||||
* The scheduler used for inference.
|
||||
*/
|
||||
scheduler?: string;
|
||||
/**
|
||||
* The model used for inference.
|
||||
*/
|
||||
model?: string;
|
||||
/**
|
||||
* The strength used for image-to-image/latents-to-latents.
|
||||
*/
|
||||
strength?: number;
|
||||
/**
|
||||
* The ID of the initial latents.
|
||||
*/
|
||||
latents?: string;
|
||||
/**
|
||||
* The VAE used for decoding.
|
||||
*/
|
||||
vae?: string;
|
||||
/**
|
||||
* The UNet used dor inference.
|
||||
*/
|
||||
unet?: string;
|
||||
/**
|
||||
* The CLIP Encoder used for conditioning.
|
||||
*/
|
||||
clip?: string;
|
||||
/**
|
||||
* Uploaded image metadata, extracted from the PNG tEXt chunk.
|
||||
*/
|
||||
extra?: string;
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ import type { ImageField } from './ImageField';
|
||||
* Base class for invocations that output an image
|
||||
*/
|
||||
export type ImageOutput = {
|
||||
type: 'image';
|
||||
type: 'image_output';
|
||||
/**
|
||||
* The output image
|
||||
*/
|
||||
|
@ -1,33 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ImageResponseMetadata } from './ImageResponseMetadata';
|
||||
import type { ImageType } from './ImageType';
|
||||
|
||||
/**
|
||||
* The response type for images
|
||||
*/
|
||||
export type ImageResponse = {
|
||||
/**
|
||||
* The type of the image
|
||||
*/
|
||||
image_type: ImageType;
|
||||
/**
|
||||
* The name of the image
|
||||
*/
|
||||
image_name: string;
|
||||
/**
|
||||
* The url of the image
|
||||
*/
|
||||
image_url: string;
|
||||
/**
|
||||
* The url of the image's thumbnail
|
||||
*/
|
||||
thumbnail_url: string;
|
||||
/**
|
||||
* The image's metadata
|
||||
*/
|
||||
metadata: ImageResponseMetadata;
|
||||
};
|
||||
|
@ -1,28 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { InvokeAIMetadata } from './InvokeAIMetadata';
|
||||
|
||||
/**
|
||||
* An image's metadata. Used only in HTTP responses.
|
||||
*/
|
||||
export type ImageResponseMetadata = {
|
||||
/**
|
||||
* The creation timestamp of the image
|
||||
*/
|
||||
created: number;
|
||||
/**
|
||||
* The width of the image in pixels
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* The height of the image in pixels
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* The image's InvokeAI-specific metadata
|
||||
*/
|
||||
invokeai?: InvokeAIMetadata;
|
||||
};
|
||||
|
@ -3,6 +3,6 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* An enumeration.
|
||||
* The type of an image.
|
||||
*/
|
||||
export type ImageType = 'results' | 'intermediates' | 'uploads';
|
||||
export type ImageType = 'results' | 'uploads' | 'intermediates';
|
||||
|
@ -0,0 +1,28 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ImageType } from './ImageType';
|
||||
|
||||
/**
|
||||
* The URLs for an image and its thumbnail.
|
||||
*/
|
||||
export type ImageUrlsDTO = {
|
||||
/**
|
||||
* The unique name of the image.
|
||||
*/
|
||||
image_name: string;
|
||||
/**
|
||||
* The type of the image.
|
||||
*/
|
||||
image_type: ImageType;
|
||||
/**
|
||||
* The URL of the image.
|
||||
*/
|
||||
image_url: string;
|
||||
/**
|
||||
* The URL of the image's thumbnail.
|
||||
*/
|
||||
thumbnail_url: string;
|
||||
};
|
||||
|
@ -1,13 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { MetadataColorField } from './MetadataColorField';
|
||||
import type { MetadataImageField } from './MetadataImageField';
|
||||
import type { MetadataLatentsField } from './MetadataLatentsField';
|
||||
|
||||
export type InvokeAIMetadata = {
|
||||
session_id?: string;
|
||||
node?: Record<string, (string | number | boolean | MetadataImageField | MetadataLatentsField | MetadataColorField)>;
|
||||
};
|
||||
|
@ -13,5 +13,13 @@ export type MaskOutput = {
|
||||
* The output mask
|
||||
*/
|
||||
mask: ImageField;
|
||||
/**
|
||||
* The width of the mask in pixels
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* The height of the mask in pixels
|
||||
*/
|
||||
height?: number;
|
||||
};
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type MetadataColorField = {
|
||||
'r': number;
|
||||
'g': number;
|
||||
'b': number;
|
||||
'a': number;
|
||||
};
|
||||
|
@ -1,11 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ImageType } from './ImageType';
|
||||
|
||||
export type MetadataImageField = {
|
||||
image_type: ImageType;
|
||||
image_name: string;
|
||||
};
|
||||
|
@ -1,8 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type MetadataLatentsField = {
|
||||
latents_name: string;
|
||||
};
|
||||
|
@ -2,16 +2,16 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ImageResponse } from './ImageResponse';
|
||||
import type { ImageDTO } from './ImageDTO';
|
||||
|
||||
/**
|
||||
* Paginated results
|
||||
*/
|
||||
export type PaginatedResults_ImageResponse_ = {
|
||||
export type PaginatedResults_ImageDTO_ = {
|
||||
/**
|
||||
* Items
|
||||
*/
|
||||
items: Array<ImageResponse>;
|
||||
items: Array<ImageDTO>;
|
||||
/**
|
||||
* Current Page
|
||||
*/
|
@ -11,5 +11,13 @@ export type RandomIntInvocation = {
|
||||
*/
|
||||
id: string;
|
||||
type?: 'rand_int';
|
||||
/**
|
||||
* The inclusive low value
|
||||
*/
|
||||
low?: number;
|
||||
/**
|
||||
* The exclusive high value
|
||||
*/
|
||||
high?: number;
|
||||
};
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Creates a range
|
||||
* Creates a range of numbers from start to stop with step
|
||||
*/
|
||||
export type RangeInvocation = {
|
||||
/**
|
||||
|
@ -0,0 +1,27 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Creates a range from start to start + size with step
|
||||
*/
|
||||
export type RangeOfSizeInvocation = {
|
||||
/**
|
||||
* The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
id: string;
|
||||
type?: 'range_of_size';
|
||||
/**
|
||||
* The start of the range
|
||||
*/
|
||||
start?: number;
|
||||
/**
|
||||
* The number of values
|
||||
*/
|
||||
size?: number;
|
||||
/**
|
||||
* The step of the range
|
||||
*/
|
||||
step?: number;
|
||||
};
|
||||
|
@ -1,24 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $AddInvocation = {
|
||||
description: `Adds two numbers`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: `The first number`,
|
||||
},
|
||||
'b': {
|
||||
type: 'number',
|
||||
description: `The second number`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,30 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $BlurInvocation = {
|
||||
description: `Blurs an image`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to blur`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
radius: {
|
||||
type: 'number',
|
||||
description: `The blur radius`,
|
||||
},
|
||||
blur_type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,12 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $Body_upload_image = {
|
||||
properties: {
|
||||
file: {
|
||||
type: 'binary',
|
||||
isRequired: true,
|
||||
format: 'binary',
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,37 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CkptModelInfo = {
|
||||
properties: {
|
||||
description: {
|
||||
type: 'string',
|
||||
description: `A description of the model`,
|
||||
},
|
||||
format: {
|
||||
type: 'Enum',
|
||||
},
|
||||
config: {
|
||||
type: 'string',
|
||||
description: `The path to the model config`,
|
||||
isRequired: true,
|
||||
},
|
||||
weights: {
|
||||
type: 'string',
|
||||
description: `The path to the model weights`,
|
||||
isRequired: true,
|
||||
},
|
||||
vae: {
|
||||
type: 'string',
|
||||
description: `The path to the model VAE`,
|
||||
isRequired: true,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the model`,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the model`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,28 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CollectInvocation = {
|
||||
description: `Collects values into a collection`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
item: {
|
||||
description: `The item to collect (all inputs must be of the same type)`,
|
||||
properties: {
|
||||
},
|
||||
},
|
||||
collection: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
properties: {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,20 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CollectInvocationOutput = {
|
||||
description: `Base class for all invocation outputs`,
|
||||
properties: {
|
||||
type: {
|
||||
type: 'Enum',
|
||||
isRequired: true,
|
||||
},
|
||||
collection: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
properties: {
|
||||
},
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,31 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ColorField = {
|
||||
properties: {
|
||||
'r': {
|
||||
type: 'number',
|
||||
description: `The red component`,
|
||||
isRequired: true,
|
||||
maximum: 255,
|
||||
},
|
||||
'g': {
|
||||
type: 'number',
|
||||
description: `The green component`,
|
||||
isRequired: true,
|
||||
maximum: 255,
|
||||
},
|
||||
'b': {
|
||||
type: 'number',
|
||||
description: `The blue component`,
|
||||
isRequired: true,
|
||||
maximum: 255,
|
||||
},
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: `The alpha component`,
|
||||
isRequired: true,
|
||||
maximum: 255,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,24 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CompelInvocation = {
|
||||
description: `Parse prompt using compel package to conditioning.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
prompt: {
|
||||
type: 'string',
|
||||
description: `Prompt`,
|
||||
},
|
||||
model: {
|
||||
type: 'string',
|
||||
description: `Model to use`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,18 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CompelOutput = {
|
||||
description: `Compel parser output`,
|
||||
properties: {
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
conditioning: {
|
||||
type: 'all-of',
|
||||
description: `Conditioning`,
|
||||
contains: [{
|
||||
type: 'ConditioningField',
|
||||
}],
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,12 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ConditioningField = {
|
||||
properties: {
|
||||
conditioning_name: {
|
||||
type: 'string',
|
||||
description: `The name of conditioning data`,
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,22 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CreateModelRequest = {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: `The name of the model`,
|
||||
isRequired: true,
|
||||
},
|
||||
info: {
|
||||
type: 'one-of',
|
||||
description: `The model info`,
|
||||
contains: [{
|
||||
type: 'CkptModelInfo',
|
||||
}, {
|
||||
type: 'DiffusersModelInfo',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,39 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CropImageInvocation = {
|
||||
description: `Crops an image to a specified box. The box can be outside of the image.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to crop`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
'x': {
|
||||
type: 'number',
|
||||
description: `The left x coordinate of the crop rectangle`,
|
||||
},
|
||||
'y': {
|
||||
type: 'number',
|
||||
description: `The top y coordinate of the crop rectangle`,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the crop rectangle`,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the crop rectangle`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,30 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $CvInpaintInvocation = {
|
||||
description: `Simple inpaint using opencv.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to inpaint`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
mask: {
|
||||
type: 'all-of',
|
||||
description: `The mask to use when inpainting`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,29 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $DiffusersModelInfo = {
|
||||
properties: {
|
||||
description: {
|
||||
type: 'string',
|
||||
description: `A description of the model`,
|
||||
},
|
||||
format: {
|
||||
type: 'Enum',
|
||||
},
|
||||
vae: {
|
||||
type: 'all-of',
|
||||
description: `The VAE repo to use for this model`,
|
||||
contains: [{
|
||||
type: 'VaeRepo',
|
||||
}],
|
||||
},
|
||||
repo_id: {
|
||||
type: 'string',
|
||||
description: `The repo ID to use for this model`,
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
description: `The path to the model`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,24 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $DivideInvocation = {
|
||||
description: `Divides two numbers`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
'a': {
|
||||
type: 'number',
|
||||
description: `The first number`,
|
||||
},
|
||||
'b': {
|
||||
type: 'number',
|
||||
description: `The second number`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,23 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $Edge = {
|
||||
properties: {
|
||||
source: {
|
||||
type: 'all-of',
|
||||
description: `The connection for the edge's from node and field`,
|
||||
contains: [{
|
||||
type: 'EdgeConnection',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
destination: {
|
||||
type: 'all-of',
|
||||
description: `The connection for the edge's to node and field`,
|
||||
contains: [{
|
||||
type: 'EdgeConnection',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,17 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $EdgeConnection = {
|
||||
properties: {
|
||||
node_id: {
|
||||
type: 'string',
|
||||
description: `The id of the node for this edge connection`,
|
||||
isRequired: true,
|
||||
},
|
||||
field: {
|
||||
type: 'string',
|
||||
description: `The field for this connection`,
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,96 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $Graph = {
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this graph`,
|
||||
},
|
||||
nodes: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'one-of',
|
||||
contains: [{
|
||||
type: 'LoadImageInvocation',
|
||||
}, {
|
||||
type: 'ShowImageInvocation',
|
||||
}, {
|
||||
type: 'CropImageInvocation',
|
||||
}, {
|
||||
type: 'PasteImageInvocation',
|
||||
}, {
|
||||
type: 'MaskFromAlphaInvocation',
|
||||
}, {
|
||||
type: 'BlurInvocation',
|
||||
}, {
|
||||
type: 'LerpInvocation',
|
||||
}, {
|
||||
type: 'InverseLerpInvocation',
|
||||
}, {
|
||||
type: 'CompelInvocation',
|
||||
}, {
|
||||
type: 'NoiseInvocation',
|
||||
}, {
|
||||
type: 'TextToLatentsInvocation',
|
||||
}, {
|
||||
type: 'LatentsToImageInvocation',
|
||||
}, {
|
||||
type: 'ResizeLatentsInvocation',
|
||||
}, {
|
||||
type: 'ScaleLatentsInvocation',
|
||||
}, {
|
||||
type: 'ImageToLatentsInvocation',
|
||||
}, {
|
||||
type: 'AddInvocation',
|
||||
}, {
|
||||
type: 'SubtractInvocation',
|
||||
}, {
|
||||
type: 'MultiplyInvocation',
|
||||
}, {
|
||||
type: 'DivideInvocation',
|
||||
}, {
|
||||
type: 'RandomIntInvocation',
|
||||
}, {
|
||||
type: 'ParamIntInvocation',
|
||||
}, {
|
||||
type: 'CvInpaintInvocation',
|
||||
}, {
|
||||
type: 'RangeInvocation',
|
||||
}, {
|
||||
type: 'RandomRangeInvocation',
|
||||
}, {
|
||||
type: 'UpscaleInvocation',
|
||||
}, {
|
||||
type: 'RestoreFaceInvocation',
|
||||
}, {
|
||||
type: 'TextToImageInvocation',
|
||||
}, {
|
||||
type: 'InfillColorInvocation',
|
||||
}, {
|
||||
type: 'InfillTileInvocation',
|
||||
}, {
|
||||
type: 'InfillPatchMatchInvocation',
|
||||
}, {
|
||||
type: 'GraphInvocation',
|
||||
}, {
|
||||
type: 'IterateInvocation',
|
||||
}, {
|
||||
type: 'CollectInvocation',
|
||||
}, {
|
||||
type: 'LatentsToLatentsInvocation',
|
||||
}, {
|
||||
type: 'ImageToImageInvocation',
|
||||
}, {
|
||||
type: 'InpaintInvocation',
|
||||
}],
|
||||
},
|
||||
},
|
||||
edges: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'Edge',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,97 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $GraphExecutionState = {
|
||||
description: `Tracks the state of a graph execution`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of the execution state`,
|
||||
isRequired: true,
|
||||
},
|
||||
graph: {
|
||||
type: 'all-of',
|
||||
description: `The graph being executed`,
|
||||
contains: [{
|
||||
type: 'Graph',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
execution_graph: {
|
||||
type: 'all-of',
|
||||
description: `The expanded graph of activated and executed nodes`,
|
||||
contains: [{
|
||||
type: 'Graph',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
executed: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'string',
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
executed_history: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'string',
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
results: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'one-of',
|
||||
contains: [{
|
||||
type: 'ImageOutput',
|
||||
}, {
|
||||
type: 'MaskOutput',
|
||||
}, {
|
||||
type: 'CompelOutput',
|
||||
}, {
|
||||
type: 'LatentsOutput',
|
||||
}, {
|
||||
type: 'NoiseOutput',
|
||||
}, {
|
||||
type: 'IntOutput',
|
||||
}, {
|
||||
type: 'PromptOutput',
|
||||
}, {
|
||||
type: 'IntCollectionOutput',
|
||||
}, {
|
||||
type: 'GraphInvocationOutput',
|
||||
}, {
|
||||
type: 'IterateInvocationOutput',
|
||||
}, {
|
||||
type: 'CollectInvocationOutput',
|
||||
}],
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
errors: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'string',
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
prepared_source_mapping: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'string',
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
source_prepared_mapping: {
|
||||
type: 'dictionary',
|
||||
contains: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,24 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $GraphInvocation = {
|
||||
description: `A node to process inputs and produce outputs.
|
||||
May use dependency injection in __init__ to receive providers.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
graph: {
|
||||
type: 'all-of',
|
||||
description: `The graph to run`,
|
||||
contains: [{
|
||||
type: 'Graph',
|
||||
}],
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,12 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $GraphInvocationOutput = {
|
||||
description: `Base class for all invocation outputs`,
|
||||
properties: {
|
||||
type: {
|
||||
type: 'Enum',
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,13 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $HTTPValidationError = {
|
||||
properties: {
|
||||
detail: {
|
||||
type: 'array',
|
||||
contains: {
|
||||
type: 'ValidationError',
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,21 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageField = {
|
||||
description: `An image field used for passing image objects between invocations`,
|
||||
properties: {
|
||||
image_type: {
|
||||
type: 'all-of',
|
||||
description: `The type of the image`,
|
||||
contains: [{
|
||||
type: 'ImageType',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
image_name: {
|
||||
type: 'string',
|
||||
description: `The name of the image`,
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,30 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageOutput = {
|
||||
description: `Base class for invocations that output an image`,
|
||||
properties: {
|
||||
type: {
|
||||
type: 'Enum',
|
||||
isRequired: true,
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The output image`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the image in pixels`,
|
||||
isRequired: true,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the image in pixels`,
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,39 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageResponse = {
|
||||
description: `The response type for images`,
|
||||
properties: {
|
||||
image_type: {
|
||||
type: 'all-of',
|
||||
description: `The type of the image`,
|
||||
contains: [{
|
||||
type: 'ImageType',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
image_name: {
|
||||
type: 'string',
|
||||
description: `The name of the image`,
|
||||
isRequired: true,
|
||||
},
|
||||
image_url: {
|
||||
type: 'string',
|
||||
description: `The url of the image`,
|
||||
isRequired: true,
|
||||
},
|
||||
thumbnail_url: {
|
||||
type: 'string',
|
||||
description: `The url of the image's thumbnail`,
|
||||
isRequired: true,
|
||||
},
|
||||
metadata: {
|
||||
type: 'all-of',
|
||||
description: `The image's metadata`,
|
||||
contains: [{
|
||||
type: 'ImageResponseMetadata',
|
||||
}],
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,30 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageResponseMetadata = {
|
||||
description: `An image's metadata. Used only in HTTP responses.`,
|
||||
properties: {
|
||||
created: {
|
||||
type: 'number',
|
||||
description: `The creation timestamp of the image`,
|
||||
isRequired: true,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the image in pixels`,
|
||||
isRequired: true,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the image in pixels`,
|
||||
isRequired: true,
|
||||
},
|
||||
invokeai: {
|
||||
type: 'all-of',
|
||||
description: `The image's InvokeAI-specific metadata`,
|
||||
contains: [{
|
||||
type: 'InvokeAIMetadata',
|
||||
}],
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,67 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageToImageInvocation = {
|
||||
description: `Generates an image using img2img.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
prompt: {
|
||||
type: 'string',
|
||||
description: `The prompt to generate an image from`,
|
||||
},
|
||||
seed: {
|
||||
type: 'number',
|
||||
description: `The seed to use (omit for random)`,
|
||||
maximum: 2147483647,
|
||||
},
|
||||
steps: {
|
||||
type: 'number',
|
||||
description: `The number of steps to use to generate the image`,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the resulting image`,
|
||||
multipleOf: 8,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the resulting image`,
|
||||
multipleOf: 8,
|
||||
},
|
||||
cfg_scale: {
|
||||
type: 'number',
|
||||
description: `The Classifier-Free Guidance, higher values may result in a result closer to the prompt`,
|
||||
minimum: 1,
|
||||
},
|
||||
scheduler: {
|
||||
type: 'Enum',
|
||||
},
|
||||
model: {
|
||||
type: 'string',
|
||||
description: `The model to use (currently ignored)`,
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The input image`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
strength: {
|
||||
type: 'number',
|
||||
description: `The strength of the original image`,
|
||||
maximum: 1,
|
||||
},
|
||||
fit: {
|
||||
type: 'boolean',
|
||||
description: `Whether or not the result should be fit to the aspect ratio of the input image`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,27 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageToLatentsInvocation = {
|
||||
description: `Encodes an image into latents.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to encode`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
model: {
|
||||
type: 'string',
|
||||
description: `The model to use`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,6 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ImageType = {
|
||||
type: 'Enum',
|
||||
} as const;
|
@ -1,30 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $InfillColorInvocation = {
|
||||
description: `Infills transparent areas of an image with a solid color`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to infill`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
color: {
|
||||
type: 'all-of',
|
||||
description: `The color to use to infill`,
|
||||
contains: [{
|
||||
type: 'ColorField',
|
||||
}],
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,23 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $InfillPatchMatchInvocation = {
|
||||
description: `Infills transparent areas of an image using the PatchMatch algorithm`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to infill`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -1,33 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $InfillTileInvocation = {
|
||||
description: `Infills transparent areas of an image with tiles of the image`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
image: {
|
||||
type: 'all-of',
|
||||
description: `The image to infill`,
|
||||
contains: [{
|
||||
type: 'ImageField',
|
||||
}],
|
||||
},
|
||||
tile_size: {
|
||||
type: 'number',
|
||||
description: `The tile size (px)`,
|
||||
minimum: 1,
|
||||
},
|
||||
seed: {
|
||||
type: 'number',
|
||||
description: `The seed to use for tile generation (omit for random)`,
|
||||
maximum: 2147483647,
|
||||
},
|
||||
},
|
||||
} as const;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user