mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into lstein/installer-for-new-model-layout
This commit is contained in:
@ -23,6 +23,8 @@ import GlobalHotkeys from './GlobalHotkeys';
|
||||
import Toaster from './Toaster';
|
||||
import DeleteImageModal from 'features/gallery/components/DeleteImageModal';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
import UpdateImageBoardModal from '../../features/gallery/components/Boards/UpdateImageBoardModal';
|
||||
import { useListModelsQuery } from 'services/apiSlice';
|
||||
|
||||
const DEFAULT_CONFIG = {};
|
||||
|
||||
@ -45,6 +47,18 @@ const App = ({
|
||||
|
||||
const isApplicationReady = useIsApplicationReady();
|
||||
|
||||
const { data: pipelineModels } = useListModelsQuery({
|
||||
model_type: 'pipeline',
|
||||
});
|
||||
const { data: controlnetModels } = useListModelsQuery({
|
||||
model_type: 'controlnet',
|
||||
});
|
||||
const { data: vaeModels } = useListModelsQuery({ model_type: 'vae' });
|
||||
const { data: loraModels } = useListModelsQuery({ model_type: 'lora' });
|
||||
const { data: embeddingModels } = useListModelsQuery({
|
||||
model_type: 'embedding',
|
||||
});
|
||||
|
||||
const [loadingOverridden, setLoadingOverridden] = useState(false);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
@ -143,6 +157,7 @@ const App = ({
|
||||
</Portal>
|
||||
</Grid>
|
||||
<DeleteImageModal />
|
||||
<UpdateImageBoardModal />
|
||||
<Toaster />
|
||||
<GlobalHotkeys />
|
||||
</>
|
||||
|
@ -21,6 +21,8 @@ import {
|
||||
DeleteImageContext,
|
||||
DeleteImageContextProvider,
|
||||
} from 'app/contexts/DeleteImageContext';
|
||||
import UpdateImageBoardModal from '../../features/gallery/components/Boards/UpdateImageBoardModal';
|
||||
import { AddImageToBoardContextProvider } from '../contexts/AddImageToBoardContext';
|
||||
|
||||
const App = lazy(() => import('./App'));
|
||||
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||
@ -76,11 +78,13 @@ const InvokeAIUI = ({
|
||||
<ThemeLocaleProvider>
|
||||
<ImageDndContext>
|
||||
<DeleteImageContextProvider>
|
||||
<App
|
||||
config={config}
|
||||
headerComponent={headerComponent}
|
||||
setIsReady={setIsReady}
|
||||
/>
|
||||
<AddImageToBoardContextProvider>
|
||||
<App
|
||||
config={config}
|
||||
headerComponent={headerComponent}
|
||||
setIsReady={setIsReady}
|
||||
/>
|
||||
</AddImageToBoardContextProvider>
|
||||
</DeleteImageContextProvider>
|
||||
</ImageDndContext>
|
||||
</ThemeLocaleProvider>
|
||||
|
@ -0,0 +1,89 @@
|
||||
import { useDisclosure } from '@chakra-ui/react';
|
||||
import { PropsWithChildren, createContext, useCallback, useState } from 'react';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { useAddImageToBoardMutation } from 'services/apiSlice';
|
||||
|
||||
export type ImageUsage = {
|
||||
isInitialImage: boolean;
|
||||
isCanvasImage: boolean;
|
||||
isNodesImage: boolean;
|
||||
isControlNetImage: boolean;
|
||||
};
|
||||
|
||||
type AddImageToBoardContextValue = {
|
||||
/**
|
||||
* Whether the move image dialog is open.
|
||||
*/
|
||||
isOpen: boolean;
|
||||
/**
|
||||
* Closes the move image dialog.
|
||||
*/
|
||||
onClose: () => void;
|
||||
/**
|
||||
* The image pending movement
|
||||
*/
|
||||
image?: ImageDTO;
|
||||
onClickAddToBoard: (image: ImageDTO) => void;
|
||||
handleAddToBoard: (boardId: string) => void;
|
||||
};
|
||||
|
||||
export const AddImageToBoardContext =
|
||||
createContext<AddImageToBoardContextValue>({
|
||||
isOpen: false,
|
||||
onClose: () => undefined,
|
||||
onClickAddToBoard: () => undefined,
|
||||
handleAddToBoard: () => undefined,
|
||||
});
|
||||
|
||||
type Props = PropsWithChildren;
|
||||
|
||||
export const AddImageToBoardContextProvider = (props: Props) => {
|
||||
const [imageToMove, setImageToMove] = useState<ImageDTO>();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const [addImageToBoard, result] = useAddImageToBoardMutation();
|
||||
|
||||
// Clean up after deleting or dismissing the modal
|
||||
const closeAndClearImageToDelete = useCallback(() => {
|
||||
setImageToMove(undefined);
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
const onClickAddToBoard = useCallback(
|
||||
(image?: ImageDTO) => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
setImageToMove(image);
|
||||
onOpen();
|
||||
},
|
||||
[setImageToMove, onOpen]
|
||||
);
|
||||
|
||||
const handleAddToBoard = useCallback(
|
||||
(boardId: string) => {
|
||||
if (imageToMove) {
|
||||
addImageToBoard({
|
||||
board_id: boardId,
|
||||
image_name: imageToMove.image_name,
|
||||
});
|
||||
closeAndClearImageToDelete();
|
||||
}
|
||||
},
|
||||
[addImageToBoard, closeAndClearImageToDelete, imageToMove]
|
||||
);
|
||||
|
||||
return (
|
||||
<AddImageToBoardContext.Provider
|
||||
value={{
|
||||
isOpen,
|
||||
image: imageToMove,
|
||||
onClose: closeAndClearImageToDelete,
|
||||
onClickAddToBoard,
|
||||
handleAddToBoard,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</AddImageToBoardContext.Provider>
|
||||
);
|
||||
};
|
@ -35,25 +35,23 @@ export const selectImageUsage = createSelector(
|
||||
(state: RootState, image_name?: string) => image_name,
|
||||
],
|
||||
(generation, canvas, nodes, controlNet, image_name) => {
|
||||
const isInitialImage = generation.initialImage?.image_name === image_name;
|
||||
const isInitialImage = generation.initialImage?.imageName === image_name;
|
||||
|
||||
const isCanvasImage = canvas.layerState.objects.some(
|
||||
(obj) => obj.kind === 'image' && obj.image.image_name === image_name
|
||||
(obj) => obj.kind === 'image' && obj.imageName === image_name
|
||||
);
|
||||
|
||||
const isNodesImage = nodes.nodes.some((node) => {
|
||||
return some(
|
||||
node.data.inputs,
|
||||
(input) =>
|
||||
input.type === 'image' && input.value?.image_name === image_name
|
||||
(input) => input.type === 'image' && input.value === image_name
|
||||
);
|
||||
});
|
||||
|
||||
const isControlNetImage = some(
|
||||
controlNet.controlNets,
|
||||
(c) =>
|
||||
c.controlImage?.image_name === image_name ||
|
||||
c.processedControlImage?.image_name === image_name
|
||||
c.controlImage === image_name || c.processedControlImage === image_name
|
||||
);
|
||||
|
||||
const imageUsage: ImageUsage = {
|
||||
|
@ -5,7 +5,6 @@ import { lightboxPersistDenylist } from 'features/lightbox/store/lightboxPersist
|
||||
import { nodesPersistDenylist } from 'features/nodes/store/nodesPersistDenylist';
|
||||
import { generationPersistDenylist } from 'features/parameters/store/generationPersistDenylist';
|
||||
import { postprocessingPersistDenylist } from 'features/parameters/store/postprocessingPersistDenylist';
|
||||
import { modelsPersistDenylist } from 'features/system/store/modelsPersistDenylist';
|
||||
import { systemPersistDenylist } from 'features/system/store/systemPersistDenylist';
|
||||
import { uiPersistDenylist } from 'features/ui/store/uiPersistDenylist';
|
||||
import { omit } from 'lodash-es';
|
||||
@ -18,7 +17,6 @@ const serializationDenylist: {
|
||||
gallery: galleryPersistDenylist,
|
||||
generation: generationPersistDenylist,
|
||||
lightbox: lightboxPersistDenylist,
|
||||
models: modelsPersistDenylist,
|
||||
nodes: nodesPersistDenylist,
|
||||
postprocessing: postprocessingPersistDenylist,
|
||||
system: systemPersistDenylist,
|
||||
|
@ -7,7 +7,6 @@ import { initialNodesState } from 'features/nodes/store/nodesSlice';
|
||||
import { initialGenerationState } from 'features/parameters/store/generationSlice';
|
||||
import { initialPostprocessingState } from 'features/parameters/store/postprocessingSlice';
|
||||
import { initialConfigState } from 'features/system/store/configSlice';
|
||||
import { initialModelsState } from 'features/system/store/modelSlice';
|
||||
import { initialSystemState } from 'features/system/store/systemSlice';
|
||||
import { initialHotkeysState } from 'features/ui/store/hotkeysSlice';
|
||||
import { initialUIState } from 'features/ui/store/uiSlice';
|
||||
@ -21,7 +20,6 @@ const initialStates: {
|
||||
gallery: initialGalleryState,
|
||||
generation: initialGenerationState,
|
||||
lightbox: initialLightboxState,
|
||||
models: initialModelsState,
|
||||
nodes: initialNodesState,
|
||||
postprocessing: initialPostprocessingState,
|
||||
system: initialSystemState,
|
||||
|
@ -73,6 +73,15 @@ import { addImageCategoriesChangedListener } from './listeners/imageCategoriesCh
|
||||
import { addControlNetImageProcessedListener } from './listeners/controlNetImageProcessed';
|
||||
import { addControlNetAutoProcessListener } from './listeners/controlNetAutoProcess';
|
||||
import { addUpdateImageUrlsOnConnectListener } from './listeners/updateImageUrlsOnConnect';
|
||||
import {
|
||||
addImageAddedToBoardFulfilledListener,
|
||||
addImageAddedToBoardRejectedListener,
|
||||
} from './listeners/imageAddedToBoard';
|
||||
import { addBoardIdSelectedListener } from './listeners/boardIdSelected';
|
||||
import {
|
||||
addImageRemovedFromBoardFulfilledListener,
|
||||
addImageRemovedFromBoardRejectedListener,
|
||||
} from './listeners/imageRemovedFromBoard';
|
||||
|
||||
export const listenerMiddleware = createListenerMiddleware();
|
||||
|
||||
@ -92,6 +101,12 @@ export type AppListenerEffect = ListenerEffect<
|
||||
AppDispatch
|
||||
>;
|
||||
|
||||
/**
|
||||
* The RTK listener middleware is a lightweight alternative sagas/observables.
|
||||
*
|
||||
* Most side effect logic should live in a listener.
|
||||
*/
|
||||
|
||||
// Image uploaded
|
||||
addImageUploadedFulfilledListener();
|
||||
addImageUploadedRejectedListener();
|
||||
@ -183,3 +198,10 @@ addControlNetAutoProcessListener();
|
||||
|
||||
// Update image URLs on connect
|
||||
addUpdateImageUrlsOnConnectListener();
|
||||
|
||||
// Boards
|
||||
addImageAddedToBoardFulfilledListener();
|
||||
addImageAddedToBoardRejectedListener();
|
||||
addImageRemovedFromBoardFulfilledListener();
|
||||
addImageRemovedFromBoardRejectedListener();
|
||||
addBoardIdSelectedListener();
|
||||
|
@ -0,0 +1,99 @@
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { startAppListening } from '..';
|
||||
import { boardIdSelected } from 'features/gallery/store/boardSlice';
|
||||
import { selectImagesAll } from 'features/gallery/store/imagesSlice';
|
||||
import { IMAGES_PER_PAGE, receivedPageOfImages } from 'services/thunks/image';
|
||||
import { api } from 'services/apiSlice';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'boards' });
|
||||
|
||||
export const addBoardIdSelectedListener = () => {
|
||||
startAppListening({
|
||||
actionCreator: boardIdSelected,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const boardId = action.payload;
|
||||
|
||||
// we need to check if we need to fetch more images
|
||||
|
||||
const state = getState();
|
||||
const allImages = selectImagesAll(state);
|
||||
|
||||
if (!boardId) {
|
||||
// a board was unselected
|
||||
dispatch(imageSelected(allImages[0]?.image_name));
|
||||
return;
|
||||
}
|
||||
|
||||
const { categories } = state.images;
|
||||
|
||||
const filteredImages = allImages.filter((i) => {
|
||||
const isInCategory = categories.includes(i.image_category);
|
||||
const isInSelectedBoard = boardId ? i.board_id === boardId : true;
|
||||
return isInCategory && isInSelectedBoard;
|
||||
});
|
||||
|
||||
// get the board from the cache
|
||||
const { data: boards } = api.endpoints.listAllBoards.select()(state);
|
||||
const board = boards?.find((b) => b.board_id === boardId);
|
||||
|
||||
if (!board) {
|
||||
// can't find the board in cache...
|
||||
dispatch(imageSelected(allImages[0]?.image_name));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(imageSelected(board.cover_image_name));
|
||||
|
||||
// if we haven't loaded one full page of images from this board, load more
|
||||
if (
|
||||
filteredImages.length < board.image_count &&
|
||||
filteredImages.length < IMAGES_PER_PAGE
|
||||
) {
|
||||
dispatch(receivedPageOfImages({ categories, boardId }));
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const addBoardIdSelected_changeSelectedImage_listener = () => {
|
||||
startAppListening({
|
||||
actionCreator: boardIdSelected,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const boardId = action.payload;
|
||||
|
||||
const state = getState();
|
||||
|
||||
// we need to check if we need to fetch more images
|
||||
|
||||
if (!boardId) {
|
||||
// a board was unselected - we don't need to do anything
|
||||
return;
|
||||
}
|
||||
|
||||
const { categories } = state.images;
|
||||
|
||||
const filteredImages = selectImagesAll(state).filter((i) => {
|
||||
const isInCategory = categories.includes(i.image_category);
|
||||
const isInSelectedBoard = boardId ? i.board_id === boardId : true;
|
||||
return isInCategory && isInSelectedBoard;
|
||||
});
|
||||
|
||||
// get the board from the cache
|
||||
const { data: boards } = api.endpoints.listAllBoards.select()(state);
|
||||
const board = boards?.find((b) => b.board_id === boardId);
|
||||
if (!board) {
|
||||
// can't find the board in cache...
|
||||
return;
|
||||
}
|
||||
|
||||
// if we haven't loaded one full page of images from this board, load more
|
||||
if (
|
||||
filteredImages.length < board.image_count &&
|
||||
filteredImages.length < IMAGES_PER_PAGE
|
||||
) {
|
||||
dispatch(receivedPageOfImages({ categories, boardId }));
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
@ -34,7 +34,7 @@ export const addControlNetImageProcessedListener = () => {
|
||||
[controlNet.processorNode.id]: {
|
||||
...controlNet.processorNode,
|
||||
is_intermediate: true,
|
||||
image: pick(controlNet.controlImage, ['image_name']),
|
||||
image: { image_name: controlNet.controlImage },
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -81,7 +81,7 @@ export const addControlNetImageProcessedListener = () => {
|
||||
dispatch(
|
||||
controlNetProcessedImageChanged({
|
||||
controlNetId,
|
||||
processedControlImage,
|
||||
processedControlImage: processedControlImage.image_name,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { startAppListening } from '..';
|
||||
import { imageMetadataReceived } from 'services/thunks/image';
|
||||
import { api } from 'services/apiSlice';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'boards' });
|
||||
|
||||
export const addImageAddedToBoardFulfilledListener = () => {
|
||||
startAppListening({
|
||||
matcher: api.endpoints.addImageToBoard.matchFulfilled,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const { board_id, image_name } = action.meta.arg.originalArgs;
|
||||
|
||||
moduleLog.debug(
|
||||
{ data: { board_id, image_name } },
|
||||
'Image added to board'
|
||||
);
|
||||
|
||||
dispatch(
|
||||
imageMetadataReceived({
|
||||
imageName: image_name,
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const addImageAddedToBoardRejectedListener = () => {
|
||||
startAppListening({
|
||||
matcher: api.endpoints.addImageToBoard.matchRejected,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const { board_id, image_name } = action.meta.arg.originalArgs;
|
||||
|
||||
moduleLog.debug(
|
||||
{ data: { board_id, image_name } },
|
||||
'Problem adding image to board'
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
@ -12,12 +12,16 @@ export const addImageCategoriesChangedListener = () => {
|
||||
startAppListening({
|
||||
actionCreator: imageCategoriesChanged,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const filteredImagesCount = selectFilteredImagesAsArray(
|
||||
getState()
|
||||
).length;
|
||||
const state = getState();
|
||||
const filteredImagesCount = selectFilteredImagesAsArray(state).length;
|
||||
|
||||
if (!filteredImagesCount) {
|
||||
dispatch(receivedPageOfImages());
|
||||
dispatch(
|
||||
receivedPageOfImages({
|
||||
categories: action.payload,
|
||||
boardId: state.boards.selectedBoardId,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -6,15 +6,15 @@ import { clamp } from 'lodash-es';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import {
|
||||
imageRemoved,
|
||||
selectImagesEntities,
|
||||
selectImagesIds,
|
||||
} from 'features/gallery/store/imagesSlice';
|
||||
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
||||
import { controlNetReset } from 'features/controlNet/store/controlNetSlice';
|
||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
||||
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||
import { api } from 'services/apiSlice';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'addRequestedImageDeletionListener' });
|
||||
const moduleLog = log.child({ namespace: 'image' });
|
||||
|
||||
/**
|
||||
* Called when the user requests an image deletion
|
||||
@ -22,7 +22,7 @@ const moduleLog = log.child({ namespace: 'addRequestedImageDeletionListener' });
|
||||
export const addRequestedImageDeletionListener = () => {
|
||||
startAppListening({
|
||||
actionCreator: requestedImageDeletion,
|
||||
effect: (action, { dispatch, getState }) => {
|
||||
effect: async (action, { dispatch, getState, condition }) => {
|
||||
const { image, imageUsage } = action.payload;
|
||||
|
||||
const { image_name } = image;
|
||||
@ -30,9 +30,8 @@ export const addRequestedImageDeletionListener = () => {
|
||||
const state = getState();
|
||||
const selectedImage = state.gallery.selectedImage;
|
||||
|
||||
if (selectedImage && selectedImage.image_name === image_name) {
|
||||
if (selectedImage === image_name) {
|
||||
const ids = selectImagesIds(state);
|
||||
const entities = selectImagesEntities(state);
|
||||
|
||||
const deletedImageIndex = ids.findIndex(
|
||||
(result) => result.toString() === image_name
|
||||
@ -48,10 +47,8 @@ export const addRequestedImageDeletionListener = () => {
|
||||
|
||||
const newSelectedImageId = filteredIds[newSelectedImageIndex];
|
||||
|
||||
const newSelectedImage = entities[newSelectedImageId];
|
||||
|
||||
if (newSelectedImageId) {
|
||||
dispatch(imageSelected(newSelectedImage));
|
||||
dispatch(imageSelected(newSelectedImageId as string));
|
||||
} else {
|
||||
dispatch(imageSelected());
|
||||
}
|
||||
@ -79,7 +76,21 @@ export const addRequestedImageDeletionListener = () => {
|
||||
dispatch(imageRemoved(image_name));
|
||||
|
||||
// Delete from server
|
||||
dispatch(imageDeleted({ imageName: image_name }));
|
||||
const { requestId } = dispatch(imageDeleted({ imageName: image_name }));
|
||||
|
||||
// Wait for successful deletion, then trigger boards to re-fetch
|
||||
const wasImageDeleted = await condition(
|
||||
(action): action is ReturnType<typeof imageDeleted.fulfilled> =>
|
||||
imageDeleted.fulfilled.match(action) &&
|
||||
action.meta.requestId === requestId,
|
||||
30000
|
||||
);
|
||||
|
||||
if (wasImageDeleted) {
|
||||
dispatch(
|
||||
api.util.invalidateTags([{ type: 'Board', id: image.board_id }])
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -0,0 +1,40 @@
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { startAppListening } from '..';
|
||||
import { imageMetadataReceived } from 'services/thunks/image';
|
||||
import { api } from 'services/apiSlice';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'boards' });
|
||||
|
||||
export const addImageRemovedFromBoardFulfilledListener = () => {
|
||||
startAppListening({
|
||||
matcher: api.endpoints.removeImageFromBoard.matchFulfilled,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const { board_id, image_name } = action.meta.arg.originalArgs;
|
||||
|
||||
moduleLog.debug(
|
||||
{ data: { board_id, image_name } },
|
||||
'Image added to board'
|
||||
);
|
||||
|
||||
dispatch(
|
||||
imageMetadataReceived({
|
||||
imageName: image_name,
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const addImageRemovedFromBoardRejectedListener = () => {
|
||||
startAppListening({
|
||||
matcher: api.endpoints.removeImageFromBoard.matchRejected,
|
||||
effect: (action, { getState, dispatch }) => {
|
||||
const { board_id, image_name } = action.meta.arg.originalArgs;
|
||||
|
||||
moduleLog.debug(
|
||||
{ data: { board_id, image_name } },
|
||||
'Problem adding image to board'
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
@ -46,7 +46,12 @@ export const addImageUploadedFulfilledListener = () => {
|
||||
|
||||
if (postUploadAction?.type === 'SET_CONTROLNET_IMAGE') {
|
||||
const { controlNetId } = postUploadAction;
|
||||
dispatch(controlNetImageChanged({ controlNetId, controlImage: image }));
|
||||
dispatch(
|
||||
controlNetImageChanged({
|
||||
controlNetId,
|
||||
controlImage: image.image_name,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { startAppListening } from '../..';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { appSocketConnected, socketConnected } from 'services/events/actions';
|
||||
import { receivedPageOfImages } from 'services/thunks/image';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'socketio' });
|
||||
|
||||
@ -15,16 +14,17 @@ export const addSocketConnectedEventListener = () => {
|
||||
|
||||
moduleLog.debug({ timestamp }, 'Connected');
|
||||
|
||||
const { models, nodes, config, images } = getState();
|
||||
const { nodes, config, images } = getState();
|
||||
|
||||
const { disabledTabs } = config;
|
||||
|
||||
if (!images.ids.length) {
|
||||
dispatch(receivedPageOfImages());
|
||||
}
|
||||
|
||||
if (!models.ids.length) {
|
||||
dispatch(receivedModels());
|
||||
dispatch(
|
||||
receivedPageOfImages({
|
||||
categories: ['general'],
|
||||
isIntermediate: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (!nodes.schema && !disabledTabs.includes('nodes')) {
|
||||
|
@ -9,6 +9,7 @@ import { imageMetadataReceived } from 'services/thunks/image';
|
||||
import { sessionCanceled } from 'services/thunks/session';
|
||||
import { isImageOutput } from 'services/types/guards';
|
||||
import { progressImageSet } from 'features/system/store/systemSlice';
|
||||
import { api } from 'services/apiSlice';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'socketio' });
|
||||
const nodeDenylist = ['dataURL_image'];
|
||||
@ -24,7 +25,8 @@ export const addInvocationCompleteEventListener = () => {
|
||||
|
||||
const sessionId = action.payload.data.graph_execution_state_id;
|
||||
|
||||
const { cancelType, isCancelScheduled } = getState().system;
|
||||
const { cancelType, isCancelScheduled, boardIdToAddTo } =
|
||||
getState().system;
|
||||
|
||||
// Handle scheduled cancelation
|
||||
if (cancelType === 'scheduled' && isCancelScheduled) {
|
||||
@ -57,6 +59,15 @@ export const addInvocationCompleteEventListener = () => {
|
||||
dispatch(addImageToStagingArea(imageDTO));
|
||||
}
|
||||
|
||||
if (boardIdToAddTo && !imageDTO.is_intermediate) {
|
||||
dispatch(
|
||||
api.endpoints.addImageToBoard.initiate({
|
||||
board_id: boardIdToAddTo,
|
||||
image_name,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
dispatch(progressImageSet(null));
|
||||
}
|
||||
// pass along the socket event as an application action
|
||||
|
@ -22,15 +22,15 @@ const selectAllUsedImages = createSelector(
|
||||
selectImagesEntities,
|
||||
],
|
||||
(generation, canvas, nodes, controlNet, imageEntities) => {
|
||||
const allUsedImages: ImageDTO[] = [];
|
||||
const allUsedImages: string[] = [];
|
||||
|
||||
if (generation.initialImage) {
|
||||
allUsedImages.push(generation.initialImage);
|
||||
allUsedImages.push(generation.initialImage.imageName);
|
||||
}
|
||||
|
||||
canvas.layerState.objects.forEach((obj) => {
|
||||
if (obj.kind === 'image') {
|
||||
allUsedImages.push(obj.image);
|
||||
allUsedImages.push(obj.imageName);
|
||||
}
|
||||
});
|
||||
|
||||
@ -53,7 +53,7 @@ const selectAllUsedImages = createSelector(
|
||||
|
||||
forEach(imageEntities, (image) => {
|
||||
if (image) {
|
||||
allUsedImages.push(image);
|
||||
allUsedImages.push(image.image_name);
|
||||
}
|
||||
});
|
||||
|
||||
@ -80,7 +80,7 @@ export const addUpdateImageUrlsOnConnectListener = () => {
|
||||
`Fetching new image URLs for ${allUsedImages.length} images`
|
||||
);
|
||||
|
||||
allUsedImages.forEach(({ image_name }) => {
|
||||
allUsedImages.forEach((image_name) => {
|
||||
dispatch(
|
||||
imageUrlsReceived({
|
||||
imageName: image_name,
|
||||
|
@ -5,40 +5,39 @@ import {
|
||||
configureStore,
|
||||
} from '@reduxjs/toolkit';
|
||||
|
||||
import { rememberReducer, rememberEnhancer } from 'redux-remember';
|
||||
import dynamicMiddlewares from 'redux-dynamic-middlewares';
|
||||
import { rememberEnhancer, rememberReducer } from 'redux-remember';
|
||||
|
||||
import canvasReducer from 'features/canvas/store/canvasSlice';
|
||||
import controlNetReducer from 'features/controlNet/store/controlNetSlice';
|
||||
import galleryReducer from 'features/gallery/store/gallerySlice';
|
||||
import imagesReducer from 'features/gallery/store/imagesSlice';
|
||||
import lightboxReducer from 'features/lightbox/store/lightboxSlice';
|
||||
import generationReducer from 'features/parameters/store/generationSlice';
|
||||
import controlNetReducer from 'features/controlNet/store/controlNetSlice';
|
||||
import postprocessingReducer from 'features/parameters/store/postprocessingSlice';
|
||||
import systemReducer from 'features/system/store/systemSlice';
|
||||
// import sessionReducer from 'features/system/store/sessionSlice';
|
||||
import configReducer from 'features/system/store/configSlice';
|
||||
import uiReducer from 'features/ui/store/uiSlice';
|
||||
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||
import modelsReducer from 'features/system/store/modelSlice';
|
||||
import nodesReducer from 'features/nodes/store/nodesSlice';
|
||||
import boardsReducer from 'features/gallery/store/boardSlice';
|
||||
import configReducer from 'features/system/store/configSlice';
|
||||
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||
import uiReducer from 'features/ui/store/uiSlice';
|
||||
|
||||
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||
|
||||
import { actionSanitizer } from './middleware/devtools/actionSanitizer';
|
||||
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
||||
import { actionsDenylist } from './middleware/devtools/actionsDenylist';
|
||||
|
||||
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
||||
import { LOCALSTORAGE_PREFIX } from './constants';
|
||||
import { serialize } from './enhancers/reduxRemember/serialize';
|
||||
import { unserialize } from './enhancers/reduxRemember/unserialize';
|
||||
import { LOCALSTORAGE_PREFIX } from './constants';
|
||||
import { api } from 'services/apiSlice';
|
||||
|
||||
const allReducers = {
|
||||
canvas: canvasReducer,
|
||||
gallery: galleryReducer,
|
||||
generation: generationReducer,
|
||||
lightbox: lightboxReducer,
|
||||
models: modelsReducer,
|
||||
nodes: nodesReducer,
|
||||
postprocessing: postprocessingReducer,
|
||||
system: systemReducer,
|
||||
@ -47,7 +46,9 @@ const allReducers = {
|
||||
hotkeys: hotkeysReducer,
|
||||
images: imagesReducer,
|
||||
controlNet: controlNetReducer,
|
||||
boards: boardsReducer,
|
||||
// session: sessionReducer,
|
||||
[api.reducerPath]: api.reducer,
|
||||
};
|
||||
|
||||
const rootReducer = combineReducers(allReducers);
|
||||
@ -59,12 +60,12 @@ const rememberedKeys: (keyof typeof allReducers)[] = [
|
||||
'gallery',
|
||||
'generation',
|
||||
'lightbox',
|
||||
// 'models',
|
||||
'nodes',
|
||||
'postprocessing',
|
||||
'system',
|
||||
'ui',
|
||||
'controlNet',
|
||||
// 'boards',
|
||||
// 'hotkeys',
|
||||
// 'config',
|
||||
];
|
||||
@ -84,6 +85,7 @@ export const store = configureStore({
|
||||
immutableCheck: false,
|
||||
serializableCheck: false,
|
||||
})
|
||||
.concat(api.middleware)
|
||||
.concat(dynamicMiddlewares)
|
||||
.prepend(listenerMiddleware.middleware),
|
||||
devTools: {
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
||||
import { useCombinedRefs } from '@dnd-kit/utilities';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { IAIImageLoadingFallback } from 'common/components/IAIImageFallback';
|
||||
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { ReactElement, SyntheticEvent, useCallback } from 'react';
|
||||
@ -53,7 +53,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
isDropDisabled = false,
|
||||
isDragDisabled = false,
|
||||
isUploadDisabled = false,
|
||||
fallback = <IAIImageFallback />,
|
||||
fallback = <IAIImageLoadingFallback />,
|
||||
payloadImage,
|
||||
minSize = 24,
|
||||
postUploadAction,
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { Flex, FlexProps, Spinner, SpinnerProps } from '@chakra-ui/react';
|
||||
import {
|
||||
As,
|
||||
Flex,
|
||||
FlexProps,
|
||||
Icon,
|
||||
IconProps,
|
||||
Spinner,
|
||||
SpinnerProps,
|
||||
} from '@chakra-ui/react';
|
||||
import { ReactElement } from 'react';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
|
||||
type Props = FlexProps & {
|
||||
spinnerProps?: SpinnerProps;
|
||||
};
|
||||
|
||||
export const IAIImageFallback = (props: Props) => {
|
||||
export const IAIImageLoadingFallback = (props: Props) => {
|
||||
const { spinnerProps, ...rest } = props;
|
||||
const { sx, ...restFlexProps } = rest;
|
||||
return (
|
||||
@ -25,3 +35,35 @@ export const IAIImageFallback = (props: Props) => {
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
type IAINoImageFallbackProps = {
|
||||
flexProps?: FlexProps;
|
||||
iconProps?: IconProps;
|
||||
as?: As;
|
||||
};
|
||||
|
||||
export const IAINoImageFallback = (props: IAINoImageFallbackProps) => {
|
||||
const { sx: flexSx, ...restFlexProps } = props.flexProps ?? { sx: {} };
|
||||
const { sx: iconSx, ...restIconProps } = props.iconProps ?? { sx: {} };
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
bg: 'base.900',
|
||||
opacity: 0.7,
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 'base',
|
||||
...flexSx,
|
||||
}}
|
||||
{...restFlexProps}
|
||||
>
|
||||
<Icon
|
||||
as={props.as ?? FaImage}
|
||||
sx={{ color: 'base.700', ...iconSx }}
|
||||
{...restIconProps}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
@ -1,14 +1,21 @@
|
||||
import { Image } from 'react-konva';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
import { Image, Rect } from 'react-konva';
|
||||
import { useGetImageDTOQuery } from 'services/apiSlice';
|
||||
import useImage from 'use-image';
|
||||
import { CanvasImage } from '../store/canvasTypes';
|
||||
|
||||
type IAICanvasImageProps = {
|
||||
url: string;
|
||||
x: number;
|
||||
y: number;
|
||||
canvasImage: CanvasImage;
|
||||
};
|
||||
const IAICanvasImage = (props: IAICanvasImageProps) => {
|
||||
const { url, x, y } = props;
|
||||
const [image] = useImage(url, 'anonymous');
|
||||
const { width, height, x, y, imageName } = props.canvasImage;
|
||||
const { data: imageDTO } = useGetImageDTOQuery(imageName ?? skipToken);
|
||||
const [image] = useImage(imageDTO?.image_url ?? '', 'anonymous');
|
||||
|
||||
if (!imageDTO) {
|
||||
return <Rect x={x} y={y} width={width} height={height} fill="red" />;
|
||||
}
|
||||
|
||||
return <Image x={x} y={y} image={image} listening={false} />;
|
||||
};
|
||||
|
||||
|
@ -39,14 +39,7 @@ const IAICanvasObjectRenderer = () => {
|
||||
<Group name="outpainting-objects" listening={false}>
|
||||
{objects.map((obj, i) => {
|
||||
if (isCanvasBaseImage(obj)) {
|
||||
return (
|
||||
<IAICanvasImage
|
||||
key={i}
|
||||
x={obj.x}
|
||||
y={obj.y}
|
||||
url={obj.image.image_url}
|
||||
/>
|
||||
);
|
||||
return <IAICanvasImage key={i} canvasImage={obj} />;
|
||||
} else if (isCanvasBaseLine(obj)) {
|
||||
const line = (
|
||||
<Line
|
||||
|
@ -59,11 +59,7 @@ const IAICanvasStagingArea = (props: Props) => {
|
||||
return (
|
||||
<Group {...rest}>
|
||||
{shouldShowStagingImage && currentStagingAreaImage && (
|
||||
<IAICanvasImage
|
||||
url={currentStagingAreaImage.image.image_url}
|
||||
x={x}
|
||||
y={y}
|
||||
/>
|
||||
<IAICanvasImage canvasImage={currentStagingAreaImage} />
|
||||
)}
|
||||
{shouldShowStagingOutline && (
|
||||
<Group>
|
||||
|
@ -203,7 +203,7 @@ export const canvasSlice = createSlice({
|
||||
y: 0,
|
||||
width: width,
|
||||
height: height,
|
||||
image: image,
|
||||
imageName: image.image_name,
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -325,7 +325,7 @@ export const canvasSlice = createSlice({
|
||||
kind: 'image',
|
||||
layer: 'base',
|
||||
...state.layerState.stagingArea.boundingBox,
|
||||
image,
|
||||
imageName: image.image_name,
|
||||
});
|
||||
|
||||
state.layerState.stagingArea.selectedImageIndex =
|
||||
@ -865,25 +865,25 @@ export const canvasSlice = createSlice({
|
||||
state.doesCanvasNeedScaling = true;
|
||||
});
|
||||
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
// const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
|
||||
state.layerState.objects.forEach((object) => {
|
||||
if (object.kind === 'image') {
|
||||
if (object.image.image_name === image_name) {
|
||||
object.image.image_url = image_url;
|
||||
object.image.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
}
|
||||
});
|
||||
// state.layerState.objects.forEach((object) => {
|
||||
// if (object.kind === 'image') {
|
||||
// if (object.image.image_name === image_name) {
|
||||
// object.image.image_url = image_url;
|
||||
// object.image.thumbnail_url = thumbnail_url;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
state.layerState.stagingArea.images.forEach((stagedImage) => {
|
||||
if (stagedImage.image.image_name === image_name) {
|
||||
stagedImage.image.image_url = image_url;
|
||||
stagedImage.image.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
});
|
||||
});
|
||||
// state.layerState.stagingArea.images.forEach((stagedImage) => {
|
||||
// if (stagedImage.image.image_name === image_name) {
|
||||
// stagedImage.image.image_url = image_url;
|
||||
// stagedImage.image.thumbnail_url = thumbnail_url;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -38,7 +38,7 @@ export type CanvasImage = {
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
image: ImageDTO;
|
||||
imageName: string;
|
||||
};
|
||||
|
||||
export type CanvasMaskLine = {
|
||||
|
@ -11,9 +11,11 @@ import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { IAIImageLoadingFallback } from 'common/components/IAIImageFallback';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { FaUndo } from 'react-icons/fa';
|
||||
import { useGetImageDTOQuery } from 'services/apiSlice';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
|
||||
const selector = createSelector(
|
||||
controlNetSelector,
|
||||
@ -31,24 +33,45 @@ type Props = {
|
||||
|
||||
const ControlNetImagePreview = (props: Props) => {
|
||||
const { imageSx } = props;
|
||||
const { controlNetId, controlImage, processedControlImage, processorType } =
|
||||
props.controlNet;
|
||||
const {
|
||||
controlNetId,
|
||||
controlImage: controlImageName,
|
||||
processedControlImage: processedControlImageName,
|
||||
processorType,
|
||||
} = props.controlNet;
|
||||
const dispatch = useAppDispatch();
|
||||
const { pendingControlImages } = useAppSelector(selector);
|
||||
|
||||
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
||||
|
||||
const {
|
||||
data: controlImage,
|
||||
isLoading: isLoadingControlImage,
|
||||
isError: isErrorControlImage,
|
||||
isSuccess: isSuccessControlImage,
|
||||
} = useGetImageDTOQuery(controlImageName ?? skipToken);
|
||||
|
||||
const {
|
||||
data: processedControlImage,
|
||||
isLoading: isLoadingProcessedControlImage,
|
||||
isError: isErrorProcessedControlImage,
|
||||
isSuccess: isSuccessProcessedControlImage,
|
||||
} = useGetImageDTOQuery(processedControlImageName ?? skipToken);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(droppedImage: ImageDTO) => {
|
||||
if (controlImage?.image_name === droppedImage.image_name) {
|
||||
if (controlImageName === droppedImage.image_name) {
|
||||
return;
|
||||
}
|
||||
setIsMouseOverImage(false);
|
||||
dispatch(
|
||||
controlNetImageChanged({ controlNetId, controlImage: droppedImage })
|
||||
controlNetImageChanged({
|
||||
controlNetId,
|
||||
controlImage: droppedImage.image_name,
|
||||
})
|
||||
);
|
||||
},
|
||||
[controlImage, controlNetId, dispatch]
|
||||
[controlImageName, controlNetId, dispatch]
|
||||
);
|
||||
|
||||
const handleResetControlImage = useCallback(() => {
|
||||
@ -150,7 +173,7 @@ const ControlNetImagePreview = (props: Props) => {
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<IAIImageFallback />
|
||||
<IAIImageLoadingFallback />
|
||||
</Box>
|
||||
)}
|
||||
{controlImage && (
|
||||
|
@ -39,8 +39,8 @@ export type ControlNetConfig = {
|
||||
weight: number;
|
||||
beginStepPct: number;
|
||||
endStepPct: number;
|
||||
controlImage: ImageDTO | null;
|
||||
processedControlImage: ImageDTO | null;
|
||||
controlImage: string | null;
|
||||
processedControlImage: string | null;
|
||||
processorType: ControlNetProcessorType;
|
||||
processorNode: RequiredControlNetProcessorNode;
|
||||
shouldAutoConfig: boolean;
|
||||
@ -80,7 +80,7 @@ export const controlNetSlice = createSlice({
|
||||
},
|
||||
controlNetAddedFromImage: (
|
||||
state,
|
||||
action: PayloadAction<{ controlNetId: string; controlImage: ImageDTO }>
|
||||
action: PayloadAction<{ controlNetId: string; controlImage: string }>
|
||||
) => {
|
||||
const { controlNetId, controlImage } = action.payload;
|
||||
state.controlNets[controlNetId] = {
|
||||
@ -108,7 +108,7 @@ export const controlNetSlice = createSlice({
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
controlNetId: string;
|
||||
controlImage: ImageDTO | null;
|
||||
controlImage: string | null;
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, controlImage } = action.payload;
|
||||
@ -125,7 +125,7 @@ export const controlNetSlice = createSlice({
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
controlNetId: string;
|
||||
processedControlImage: ImageDTO | null;
|
||||
processedControlImage: string | null;
|
||||
}>
|
||||
) => {
|
||||
const { controlNetId, processedControlImage } = action.payload;
|
||||
@ -260,30 +260,30 @@ export const controlNetSlice = createSlice({
|
||||
// Preemptively remove the image from the gallery
|
||||
const { imageName } = action.meta.arg;
|
||||
forEach(state.controlNets, (c) => {
|
||||
if (c.controlImage?.image_name === imageName) {
|
||||
if (c.controlImage === imageName) {
|
||||
c.controlImage = null;
|
||||
c.processedControlImage = null;
|
||||
}
|
||||
if (c.processedControlImage?.image_name === imageName) {
|
||||
if (c.processedControlImage === imageName) {
|
||||
c.processedControlImage = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
// const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
|
||||
forEach(state.controlNets, (c) => {
|
||||
if (c.controlImage?.image_name === image_name) {
|
||||
c.controlImage.image_url = image_url;
|
||||
c.controlImage.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
if (c.processedControlImage?.image_name === image_name) {
|
||||
c.processedControlImage.image_url = image_url;
|
||||
c.processedControlImage.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
});
|
||||
});
|
||||
// forEach(state.controlNets, (c) => {
|
||||
// if (c.controlImage?.image_name === image_name) {
|
||||
// c.controlImage.image_url = image_url;
|
||||
// c.controlImage.thumbnail_url = thumbnail_url;
|
||||
// }
|
||||
// if (c.processedControlImage?.image_name === image_name) {
|
||||
// c.processedControlImage.image_url = image_url;
|
||||
// c.processedControlImage.thumbnail_url = thumbnail_url;
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
builder.addCase(appSocketInvocationError, (state, action) => {
|
||||
state.pendingControlImages = [];
|
||||
|
@ -0,0 +1,27 @@
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { useCallback } from 'react';
|
||||
import { useCreateBoardMutation } from 'services/apiSlice';
|
||||
|
||||
const DEFAULT_BOARD_NAME = 'My Board';
|
||||
|
||||
const AddBoardButton = () => {
|
||||
const [createBoard, { isLoading }] = useCreateBoardMutation();
|
||||
|
||||
const handleCreateBoard = useCallback(() => {
|
||||
createBoard(DEFAULT_BOARD_NAME);
|
||||
}, [createBoard]);
|
||||
|
||||
return (
|
||||
<IAIButton
|
||||
isLoading={isLoading}
|
||||
aria-label="Add Board"
|
||||
onClick={handleCreateBoard}
|
||||
size="sm"
|
||||
sx={{ px: 4 }}
|
||||
>
|
||||
Add Board
|
||||
</IAIButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddBoardButton;
|
@ -0,0 +1,93 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { FaImages } from 'react-icons/fa';
|
||||
import { boardIdSelected } from '../../store/boardSlice';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { IAINoImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { SelectedItemOverlay } from '../SelectedItemOverlay';
|
||||
import { useCallback } from 'react';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { useRemoveImageFromBoardMutation } from 'services/apiSlice';
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import IAIDropOverlay from 'common/components/IAIDropOverlay';
|
||||
|
||||
const AllImagesBoard = ({ isSelected }: { isSelected: boolean }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleAllImagesBoardClick = () => {
|
||||
dispatch(boardIdSelected());
|
||||
};
|
||||
|
||||
const [removeImageFromBoard, { isLoading }] =
|
||||
useRemoveImageFromBoardMutation();
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(droppedImage: ImageDTO) => {
|
||||
if (!droppedImage.board_id) {
|
||||
return;
|
||||
}
|
||||
removeImageFromBoard({
|
||||
board_id: droppedImage.board_id,
|
||||
image_name: droppedImage.image_name,
|
||||
});
|
||||
},
|
||||
[removeImageFromBoard]
|
||||
);
|
||||
|
||||
const {
|
||||
isOver,
|
||||
setNodeRef,
|
||||
active: isDropActive,
|
||||
} = useDroppable({
|
||||
id: `board_droppable_all_images`,
|
||||
data: {
|
||||
handleDrop,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
onClick={handleAllImagesBoardClick}
|
||||
>
|
||||
<Flex
|
||||
ref={setNodeRef}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 'base',
|
||||
w: 'full',
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
>
|
||||
<IAINoImageFallback iconProps={{ boxSize: 8 }} as={FaImages} />
|
||||
<AnimatePresence>
|
||||
{isSelected && <SelectedItemOverlay />}
|
||||
</AnimatePresence>
|
||||
<AnimatePresence>
|
||||
{isDropActive && <IAIDropOverlay isOver={isOver} />}
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
<Text
|
||||
sx={{
|
||||
color: isSelected ? 'base.50' : 'base.200',
|
||||
fontWeight: isSelected ? 600 : undefined,
|
||||
fontSize: 'xs',
|
||||
}}
|
||||
>
|
||||
All Images
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default AllImagesBoard;
|
@ -0,0 +1,134 @@
|
||||
import {
|
||||
Collapse,
|
||||
Flex,
|
||||
Grid,
|
||||
IconButton,
|
||||
Input,
|
||||
InputGroup,
|
||||
InputRightElement,
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import {
|
||||
boardsSelector,
|
||||
setBoardSearchText,
|
||||
} from 'features/gallery/store/boardSlice';
|
||||
import { memo, useState } from 'react';
|
||||
import HoverableBoard from './HoverableBoard';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
import AddBoardButton from './AddBoardButton';
|
||||
import AllImagesBoard from './AllImagesBoard';
|
||||
import { CloseIcon } from '@chakra-ui/icons';
|
||||
import { useListAllBoardsQuery } from 'services/apiSlice';
|
||||
|
||||
const selector = createSelector(
|
||||
[boardsSelector],
|
||||
(boardsState) => {
|
||||
const { selectedBoardId, searchText } = boardsState;
|
||||
return { selectedBoardId, searchText };
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
};
|
||||
|
||||
const BoardsList = (props: Props) => {
|
||||
const { isOpen } = props;
|
||||
const dispatch = useAppDispatch();
|
||||
const { selectedBoardId, searchText } = useAppSelector(selector);
|
||||
|
||||
const { data: boards } = useListAllBoardsQuery();
|
||||
|
||||
const filteredBoards = searchText
|
||||
? boards?.filter((board) =>
|
||||
board.board_name.toLowerCase().includes(searchText.toLowerCase())
|
||||
)
|
||||
: boards;
|
||||
|
||||
const [searchMode, setSearchMode] = useState(false);
|
||||
|
||||
const handleBoardSearch = (searchTerm: string) => {
|
||||
setSearchMode(searchTerm.length > 0);
|
||||
dispatch(setBoardSearchText(searchTerm));
|
||||
};
|
||||
const clearBoardSearch = () => {
|
||||
setSearchMode(false);
|
||||
dispatch(setBoardSearchText(''));
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapse in={isOpen} animateOpacity>
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
bg: 'base.800',
|
||||
borderRadius: 'base',
|
||||
p: 2,
|
||||
mt: 2,
|
||||
}}
|
||||
>
|
||||
<Flex sx={{ gap: 2, alignItems: 'center' }}>
|
||||
<InputGroup>
|
||||
<Input
|
||||
placeholder="Search Boards..."
|
||||
value={searchText}
|
||||
onChange={(e) => {
|
||||
handleBoardSearch(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{searchText && searchText.length && (
|
||||
<InputRightElement>
|
||||
<IconButton
|
||||
onClick={clearBoardSearch}
|
||||
size="xs"
|
||||
variant="ghost"
|
||||
aria-label="Clear Search"
|
||||
icon={<CloseIcon boxSize={3} />}
|
||||
/>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
<AddBoardButton />
|
||||
</Flex>
|
||||
<OverlayScrollbarsComponent
|
||||
defer
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
options={{
|
||||
scrollbars: {
|
||||
visibility: 'auto',
|
||||
autoHide: 'move',
|
||||
autoHideDelay: 1300,
|
||||
theme: 'os-theme-dark',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
className="list-container"
|
||||
sx={{
|
||||
gap: 2,
|
||||
gridTemplateRows: '5.5rem 5.5rem',
|
||||
gridAutoFlow: 'column dense',
|
||||
gridAutoColumns: '4rem',
|
||||
}}
|
||||
>
|
||||
{!searchMode && <AllImagesBoard isSelected={!selectedBoardId} />}
|
||||
{filteredBoards &&
|
||||
filteredBoards.map((board) => (
|
||||
<HoverableBoard
|
||||
key={board.board_id}
|
||||
board={board}
|
||||
isSelected={selectedBoardId === board.board_id}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
</OverlayScrollbarsComponent>
|
||||
</Flex>
|
||||
</Collapse>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(BoardsList);
|
@ -0,0 +1,193 @@
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Editable,
|
||||
EditableInput,
|
||||
EditablePreview,
|
||||
Flex,
|
||||
Image,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { FaFolder, FaTrash } from 'react-icons/fa';
|
||||
import { ContextMenu } from 'chakra-ui-contextmenu';
|
||||
import { BoardDTO, ImageDTO } from 'services/api';
|
||||
import { IAINoImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { boardIdSelected } from 'features/gallery/store/boardSlice';
|
||||
import {
|
||||
useAddImageToBoardMutation,
|
||||
useDeleteBoardMutation,
|
||||
useGetImageDTOQuery,
|
||||
useUpdateBoardMutation,
|
||||
} from 'services/apiSlice';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
import { useDroppable } from '@dnd-kit/core';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import IAIDropOverlay from 'common/components/IAIDropOverlay';
|
||||
import { SelectedItemOverlay } from '../SelectedItemOverlay';
|
||||
|
||||
interface HoverableBoardProps {
|
||||
board: BoardDTO;
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { data: coverImage } = useGetImageDTOQuery(
|
||||
board.cover_image_name ?? skipToken
|
||||
);
|
||||
|
||||
const { board_name, board_id } = board;
|
||||
|
||||
const handleSelectBoard = useCallback(() => {
|
||||
dispatch(boardIdSelected(board_id));
|
||||
}, [board_id, dispatch]);
|
||||
|
||||
const [updateBoard, { isLoading: isUpdateBoardLoading }] =
|
||||
useUpdateBoardMutation();
|
||||
|
||||
const [deleteBoard, { isLoading: isDeleteBoardLoading }] =
|
||||
useDeleteBoardMutation();
|
||||
|
||||
const [addImageToBoard, { isLoading: isAddImageToBoardLoading }] =
|
||||
useAddImageToBoardMutation();
|
||||
|
||||
const handleUpdateBoardName = (newBoardName: string) => {
|
||||
updateBoard({ board_id, changes: { board_name: newBoardName } });
|
||||
};
|
||||
|
||||
const handleDeleteBoard = useCallback(() => {
|
||||
deleteBoard(board_id);
|
||||
}, [board_id, deleteBoard]);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(droppedImage: ImageDTO) => {
|
||||
if (droppedImage.board_id === board_id) {
|
||||
return;
|
||||
}
|
||||
addImageToBoard({ board_id, image_name: droppedImage.image_name });
|
||||
},
|
||||
[addImageToBoard, board_id]
|
||||
);
|
||||
|
||||
const {
|
||||
isOver,
|
||||
setNodeRef,
|
||||
active: isDropActive,
|
||||
} = useDroppable({
|
||||
id: `board_droppable_${board_id}`,
|
||||
data: {
|
||||
handleDrop,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Box sx={{ touchAction: 'none' }}>
|
||||
<ContextMenu<HTMLDivElement>
|
||||
menuProps={{ size: 'sm', isLazy: true }}
|
||||
renderMenu={() => (
|
||||
<MenuList sx={{ visibility: 'visible !important' }}>
|
||||
<MenuItem
|
||||
sx={{ color: 'error.300' }}
|
||||
icon={<FaTrash />}
|
||||
onClickCapture={handleDeleteBoard}
|
||||
>
|
||||
Delete Board
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
)}
|
||||
>
|
||||
{(ref) => (
|
||||
<Flex
|
||||
key={board_id}
|
||||
userSelect="none"
|
||||
ref={ref}
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
ref={setNodeRef}
|
||||
onClick={handleSelectBoard}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: 'base',
|
||||
w: 'full',
|
||||
aspectRatio: '1/1',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{board.cover_image_name && coverImage?.image_url && (
|
||||
<Image src={coverImage?.image_url} draggable={false} />
|
||||
)}
|
||||
{!(board.cover_image_name && coverImage?.image_url) && (
|
||||
<IAINoImageFallback iconProps={{ boxSize: 8 }} as={FaFolder} />
|
||||
)}
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
insetInlineEnd: 0,
|
||||
top: 0,
|
||||
p: 1,
|
||||
}}
|
||||
>
|
||||
<Badge variant="solid">{board.image_count}</Badge>
|
||||
</Flex>
|
||||
<AnimatePresence>
|
||||
{isSelected && <SelectedItemOverlay />}
|
||||
</AnimatePresence>
|
||||
<AnimatePresence>
|
||||
{isDropActive && <IAIDropOverlay isOver={isOver} />}
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
|
||||
<Box sx={{ width: 'full' }}>
|
||||
<Editable
|
||||
defaultValue={board_name}
|
||||
submitOnBlur={false}
|
||||
onSubmit={(nextValue) => {
|
||||
handleUpdateBoardName(nextValue);
|
||||
}}
|
||||
>
|
||||
<EditablePreview
|
||||
sx={{
|
||||
color: isSelected ? 'base.50' : 'base.200',
|
||||
fontWeight: isSelected ? 600 : undefined,
|
||||
fontSize: 'xs',
|
||||
textAlign: 'center',
|
||||
p: 0,
|
||||
}}
|
||||
noOfLines={1}
|
||||
/>
|
||||
<EditableInput
|
||||
sx={{
|
||||
color: 'base.50',
|
||||
fontSize: 'xs',
|
||||
borderColor: 'base.500',
|
||||
p: 0,
|
||||
outline: 0,
|
||||
}}
|
||||
/>
|
||||
</Editable>
|
||||
</Box>
|
||||
</Flex>
|
||||
)}
|
||||
</ContextMenu>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
HoverableBoard.displayName = 'HoverableBoard';
|
||||
|
||||
export default HoverableBoard;
|
@ -0,0 +1,93 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogContent,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogOverlay,
|
||||
Box,
|
||||
Flex,
|
||||
Spinner,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
|
||||
import { memo, useContext, useRef, useState } from 'react';
|
||||
import { AddImageToBoardContext } from '../../../../app/contexts/AddImageToBoardContext';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { useListAllBoardsQuery } from 'services/apiSlice';
|
||||
|
||||
const UpdateImageBoardModal = () => {
|
||||
// const boards = useSelector(selectBoardsAll);
|
||||
const { data: boards, isFetching } = useListAllBoardsQuery();
|
||||
const { isOpen, onClose, handleAddToBoard, image } = useContext(
|
||||
AddImageToBoardContext
|
||||
);
|
||||
const [selectedBoard, setSelectedBoard] = useState<string | null>();
|
||||
|
||||
const cancelRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const currentBoard = boards?.find(
|
||||
(board) => board.board_id === image?.board_id
|
||||
);
|
||||
|
||||
return (
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
onClose={onClose}
|
||||
isCentered
|
||||
>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
{currentBoard ? 'Move Image to Board' : 'Add Image to Board'}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>
|
||||
<Box>
|
||||
<Flex direction="column" gap={3}>
|
||||
{currentBoard && (
|
||||
<Text>
|
||||
Moving this image from{' '}
|
||||
<strong>{currentBoard.board_name}</strong> to
|
||||
</Text>
|
||||
)}
|
||||
{isFetching ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<IAIMantineSelect
|
||||
placeholder="Select Board"
|
||||
onChange={(v) => setSelectedBoard(v)}
|
||||
value={selectedBoard}
|
||||
data={(boards ?? []).map((board) => ({
|
||||
label: board.board_name,
|
||||
value: board.board_id,
|
||||
}))}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
</AlertDialogBody>
|
||||
<AlertDialogFooter>
|
||||
<IAIButton onClick={onClose}>Cancel</IAIButton>
|
||||
<IAIButton
|
||||
isDisabled={!selectedBoard}
|
||||
colorScheme="accent"
|
||||
onClick={() => {
|
||||
if (selectedBoard) {
|
||||
handleAddToBoard(selectedBoard);
|
||||
}
|
||||
}}
|
||||
ml={3}
|
||||
>
|
||||
{currentBoard ? 'Move' : 'Add'}
|
||||
</IAIButton>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(UpdateImageBoardModal);
|
@ -51,9 +51,12 @@ import { useAppToaster } from 'app/components/Toaster';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import { DeleteImageContext } from 'app/contexts/DeleteImageContext';
|
||||
import { DeleteImageButton } from './DeleteImageModal';
|
||||
import { selectImagesById } from '../store/imagesSlice';
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
const currentImageButtonsSelector = createSelector(
|
||||
[
|
||||
(state: RootState) => state,
|
||||
systemSelector,
|
||||
gallerySelector,
|
||||
postprocessingSelector,
|
||||
@ -61,7 +64,7 @@ const currentImageButtonsSelector = createSelector(
|
||||
lightboxSelector,
|
||||
activeTabNameSelector,
|
||||
],
|
||||
(system, gallery, postprocessing, ui, lightbox, activeTabName) => {
|
||||
(state, system, gallery, postprocessing, ui, lightbox, activeTabName) => {
|
||||
const {
|
||||
isProcessing,
|
||||
isConnected,
|
||||
@ -81,6 +84,8 @@ const currentImageButtonsSelector = createSelector(
|
||||
shouldShowProgressInViewer,
|
||||
} = ui;
|
||||
|
||||
const imageDTO = selectImagesById(state, gallery.selectedImage ?? '');
|
||||
|
||||
const { selectedImage } = gallery;
|
||||
|
||||
return {
|
||||
@ -97,10 +102,10 @@ const currentImageButtonsSelector = createSelector(
|
||||
activeTabName,
|
||||
isLightboxOpen,
|
||||
shouldHidePreview,
|
||||
image: selectedImage,
|
||||
seed: selectedImage?.metadata?.seed,
|
||||
prompt: selectedImage?.metadata?.positive_conditioning,
|
||||
negativePrompt: selectedImage?.metadata?.negative_conditioning,
|
||||
image: imageDTO,
|
||||
seed: imageDTO?.metadata?.seed,
|
||||
prompt: imageDTO?.metadata?.positive_conditioning,
|
||||
negativePrompt: imageDTO?.metadata?.negative_conditioning,
|
||||
shouldShowProgressInViewer,
|
||||
};
|
||||
},
|
||||
|
@ -9,12 +9,12 @@ import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
|
||||
import NextPrevImageButtons from './NextPrevImageButtons';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { configSelector } from '../../system/store/configSelectors';
|
||||
import { useAppToaster } from 'app/components/Toaster';
|
||||
import { imageSelected } from '../store/gallerySlice';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { IAIImageLoadingFallback } from 'common/components/IAIImageFallback';
|
||||
import { useGetImageDTOQuery } from 'services/apiSlice';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
|
||||
export const imagesSelector = createSelector(
|
||||
[uiSelector, gallerySelector, systemSelector],
|
||||
@ -29,7 +29,7 @@ export const imagesSelector = createSelector(
|
||||
return {
|
||||
shouldShowImageDetails,
|
||||
shouldHidePreview,
|
||||
image: selectedImage,
|
||||
selectedImage,
|
||||
progressImage,
|
||||
shouldShowProgressInViewer,
|
||||
shouldAntialiasProgressImage,
|
||||
@ -45,11 +45,23 @@ export const imagesSelector = createSelector(
|
||||
const CurrentImagePreview = () => {
|
||||
const {
|
||||
shouldShowImageDetails,
|
||||
image,
|
||||
selectedImage,
|
||||
progressImage,
|
||||
shouldShowProgressInViewer,
|
||||
shouldAntialiasProgressImage,
|
||||
} = useAppSelector(imagesSelector);
|
||||
|
||||
// const image = useAppSelector((state: RootState) =>
|
||||
// selectImagesById(state, selectedImage ?? '')
|
||||
// );
|
||||
|
||||
const {
|
||||
data: image,
|
||||
isLoading,
|
||||
isError,
|
||||
isSuccess,
|
||||
} = useGetImageDTOQuery(selectedImage ?? skipToken);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const handleDrop = useCallback(
|
||||
@ -57,7 +69,7 @@ const CurrentImagePreview = () => {
|
||||
if (droppedImage.image_name === image?.image_name) {
|
||||
return;
|
||||
}
|
||||
dispatch(imageSelected(droppedImage));
|
||||
dispatch(imageSelected(droppedImage.image_name));
|
||||
},
|
||||
[dispatch, image?.image_name]
|
||||
);
|
||||
@ -98,14 +110,14 @@ const CurrentImagePreview = () => {
|
||||
}}
|
||||
>
|
||||
<IAIDndImage
|
||||
image={image}
|
||||
image={selectedImage && image ? image : undefined}
|
||||
onDrop={handleDrop}
|
||||
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
|
||||
fallback={<IAIImageLoadingFallback sx={{ bg: 'none' }} />}
|
||||
isUploadDisabled={true}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
{shouldShowImageDetails && image && (
|
||||
{shouldShowImageDetails && image && selectedImage && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
@ -119,7 +131,7 @@ const CurrentImagePreview = () => {
|
||||
<ImageMetadataViewer image={image} />
|
||||
</Box>
|
||||
)}
|
||||
{!shouldShowImageDetails && image && (
|
||||
{!shouldShowImageDetails && image && selectedImage && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
|
@ -2,7 +2,14 @@ import { Box, Flex, Icon, Image, MenuItem, MenuList } from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { memo, useCallback, useContext, useState } from 'react';
|
||||
import { FaCheck, FaExpand, FaImage, FaShare, FaTrash } from 'react-icons/fa';
|
||||
import {
|
||||
FaCheck,
|
||||
FaExpand,
|
||||
FaFolder,
|
||||
FaImage,
|
||||
FaShare,
|
||||
FaTrash,
|
||||
} from 'react-icons/fa';
|
||||
import { ContextMenu } from 'chakra-ui-contextmenu';
|
||||
import {
|
||||
resizeAndScaleCanvas,
|
||||
@ -27,6 +34,8 @@ import { useAppToaster } from 'app/components/Toaster';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { useDraggable } from '@dnd-kit/core';
|
||||
import { DeleteImageContext } from 'app/contexts/DeleteImageContext';
|
||||
import { AddImageToBoardContext } from '../../../app/contexts/AddImageToBoardContext';
|
||||
import { useRemoveImageFromBoardMutation } from 'services/apiSlice';
|
||||
|
||||
export const selector = createSelector(
|
||||
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
|
||||
@ -62,17 +71,10 @@ interface HoverableImageProps {
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const memoEqualityCheck = (
|
||||
prev: HoverableImageProps,
|
||||
next: HoverableImageProps
|
||||
) =>
|
||||
prev.image.image_name === next.image.image_name &&
|
||||
prev.isSelected === next.isSelected;
|
||||
|
||||
/**
|
||||
* Gallery image component with delete/use all/use seed buttons on hover.
|
||||
*/
|
||||
const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const HoverableImage = (props: HoverableImageProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
activeTabName,
|
||||
@ -93,6 +95,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
||||
|
||||
const { onDelete } = useContext(DeleteImageContext);
|
||||
const { onClickAddToBoard } = useContext(AddImageToBoardContext);
|
||||
const handleDelete = useCallback(() => {
|
||||
onDelete(image);
|
||||
}, [image, onDelete]);
|
||||
@ -106,11 +109,13 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const [removeFromBoard] = useRemoveImageFromBoardMutation();
|
||||
|
||||
const handleMouseOver = () => setIsHovered(true);
|
||||
const handleMouseOut = () => setIsHovered(false);
|
||||
|
||||
const handleSelectImage = useCallback(() => {
|
||||
dispatch(imageSelected(image));
|
||||
dispatch(imageSelected(image.image_name));
|
||||
}, [image, dispatch]);
|
||||
|
||||
// Recall parameters handlers
|
||||
@ -168,6 +173,17 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
// dispatch(setIsLightboxOpen(true));
|
||||
};
|
||||
|
||||
const handleAddToBoard = useCallback(() => {
|
||||
onClickAddToBoard(image);
|
||||
}, [image, onClickAddToBoard]);
|
||||
|
||||
const handleRemoveFromBoard = useCallback(() => {
|
||||
if (!image.board_id) {
|
||||
return;
|
||||
}
|
||||
removeFromBoard({ board_id: image.board_id, image_name: image.image_name });
|
||||
}, [image.board_id, image.image_name, removeFromBoard]);
|
||||
|
||||
const handleOpenInNewTab = () => {
|
||||
window.open(image.image_url, '_blank');
|
||||
};
|
||||
@ -244,6 +260,17 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
{t('parameters.sendToUnifiedCanvas')}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem icon={<FaFolder />} onClickCapture={handleAddToBoard}>
|
||||
{image.board_id ? 'Change Board' : 'Add to Board'}
|
||||
</MenuItem>
|
||||
{image.board_id && (
|
||||
<MenuItem
|
||||
icon={<FaFolder />}
|
||||
onClickCapture={handleRemoveFromBoard}
|
||||
>
|
||||
Remove from Board
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
sx={{ color: 'error.300' }}
|
||||
icon={<FaTrash />}
|
||||
@ -339,8 +366,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
</ContextMenu>
|
||||
</Box>
|
||||
);
|
||||
}, memoEqualityCheck);
|
||||
};
|
||||
|
||||
HoverableImage.displayName = 'HoverableImage';
|
||||
|
||||
export default HoverableImage;
|
||||
export default memo(HoverableImage);
|
||||
|
@ -1,12 +1,15 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Flex,
|
||||
FlexProps,
|
||||
Grid,
|
||||
Icon,
|
||||
Text,
|
||||
VStack,
|
||||
forwardRef,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
@ -20,6 +23,7 @@ import {
|
||||
setGalleryImageObjectFit,
|
||||
setShouldAutoSwitchToNewImages,
|
||||
setShouldUseSingleGalleryColumn,
|
||||
setGalleryView,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import { togglePinGalleryPanel } from 'features/ui/store/uiSlice';
|
||||
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
||||
@ -53,41 +57,51 @@ import {
|
||||
selectImagesAll,
|
||||
} from '../store/imagesSlice';
|
||||
import { receivedPageOfImages } from 'services/thunks/image';
|
||||
import BoardsList from './Boards/BoardsList';
|
||||
import { boardsSelector } from '../store/boardSlice';
|
||||
import { ChevronUpIcon } from '@chakra-ui/icons';
|
||||
import { useListAllBoardsQuery } from 'services/apiSlice';
|
||||
|
||||
const categorySelector = createSelector(
|
||||
const itemSelector = createSelector(
|
||||
[(state: RootState) => state],
|
||||
(state) => {
|
||||
const { images } = state;
|
||||
const { categories } = images;
|
||||
const { categories, total: allImagesTotal, isLoading } = state.images;
|
||||
const { selectedBoardId } = state.boards;
|
||||
|
||||
const allImages = selectImagesAll(state);
|
||||
const filteredImages = allImages.filter((i) =>
|
||||
categories.includes(i.image_category)
|
||||
);
|
||||
|
||||
const images = allImages.filter((i) => {
|
||||
const isInCategory = categories.includes(i.image_category);
|
||||
const isInSelectedBoard = selectedBoardId
|
||||
? i.board_id === selectedBoardId
|
||||
: true;
|
||||
return isInCategory && isInSelectedBoard;
|
||||
});
|
||||
|
||||
return {
|
||||
images: filteredImages,
|
||||
isLoading: images.isLoading,
|
||||
areMoreImagesAvailable: filteredImages.length < images.total,
|
||||
categories: images.categories,
|
||||
images,
|
||||
allImagesTotal,
|
||||
isLoading,
|
||||
categories,
|
||||
selectedBoardId,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
const mainSelector = createSelector(
|
||||
[gallerySelector, uiSelector],
|
||||
(gallery, ui) => {
|
||||
[gallerySelector, uiSelector, boardsSelector],
|
||||
(gallery, ui, boards) => {
|
||||
const {
|
||||
galleryImageMinimumWidth,
|
||||
galleryImageObjectFit,
|
||||
shouldAutoSwitchToNewImages,
|
||||
shouldUseSingleGalleryColumn,
|
||||
selectedImage,
|
||||
galleryView,
|
||||
} = gallery;
|
||||
|
||||
const { shouldPinGallery } = ui;
|
||||
|
||||
return {
|
||||
shouldPinGallery,
|
||||
galleryImageMinimumWidth,
|
||||
@ -95,6 +109,8 @@ const mainSelector = createSelector(
|
||||
shouldAutoSwitchToNewImages,
|
||||
shouldUseSingleGalleryColumn,
|
||||
selectedImage,
|
||||
galleryView,
|
||||
selectedBoardId: boards.selectedBoardId,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
@ -126,21 +142,44 @@ const ImageGalleryContent = () => {
|
||||
shouldAutoSwitchToNewImages,
|
||||
shouldUseSingleGalleryColumn,
|
||||
selectedImage,
|
||||
galleryView,
|
||||
} = useAppSelector(mainSelector);
|
||||
|
||||
const { images, areMoreImagesAvailable, isLoading, categories } =
|
||||
useAppSelector(categorySelector);
|
||||
const { images, isLoading, allImagesTotal, categories, selectedBoardId } =
|
||||
useAppSelector(itemSelector);
|
||||
|
||||
const { selectedBoard } = useListAllBoardsQuery(undefined, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
selectedBoard: data?.find((b) => b.board_id === selectedBoardId),
|
||||
}),
|
||||
});
|
||||
|
||||
const filteredImagesTotal = useMemo(
|
||||
() => selectedBoard?.image_count ?? allImagesTotal,
|
||||
[allImagesTotal, selectedBoard?.image_count]
|
||||
);
|
||||
|
||||
const areMoreAvailable = useMemo(() => {
|
||||
return images.length < filteredImagesTotal;
|
||||
}, [images.length, filteredImagesTotal]);
|
||||
|
||||
const handleLoadMoreImages = useCallback(() => {
|
||||
dispatch(receivedPageOfImages());
|
||||
}, [dispatch]);
|
||||
dispatch(
|
||||
receivedPageOfImages({
|
||||
categories,
|
||||
boardId: selectedBoardId,
|
||||
})
|
||||
);
|
||||
}, [categories, dispatch, selectedBoardId]);
|
||||
|
||||
const handleEndReached = useMemo(() => {
|
||||
if (areMoreImagesAvailable && !isLoading) {
|
||||
if (areMoreAvailable && !isLoading) {
|
||||
return handleLoadMoreImages;
|
||||
}
|
||||
return undefined;
|
||||
}, [areMoreImagesAvailable, handleLoadMoreImages, isLoading]);
|
||||
}, [areMoreAvailable, handleLoadMoreImages, isLoading]);
|
||||
|
||||
const { isOpen: isBoardListOpen, onToggle } = useDisclosure();
|
||||
|
||||
const handleChangeGalleryImageMinimumWidth = (v: number) => {
|
||||
dispatch(setGalleryImageMinimumWidth(v));
|
||||
@ -172,46 +211,79 @@ const ImageGalleryContent = () => {
|
||||
|
||||
const handleClickImagesCategory = useCallback(() => {
|
||||
dispatch(imageCategoriesChanged(IMAGE_CATEGORIES));
|
||||
dispatch(setGalleryView('images'));
|
||||
}, [dispatch]);
|
||||
|
||||
const handleClickAssetsCategory = useCallback(() => {
|
||||
dispatch(imageCategoriesChanged(ASSETS_CATEGORIES));
|
||||
dispatch(setGalleryView('assets'));
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
<VStack
|
||||
sx={{
|
||||
gap: 2,
|
||||
flexDirection: 'column',
|
||||
h: 'full',
|
||||
w: 'full',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
ref={resizeObserverRef}
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<ButtonGroup isAttached>
|
||||
<IAIIconButton
|
||||
tooltip={t('gallery.images')}
|
||||
aria-label={t('gallery.images')}
|
||||
onClick={handleClickImagesCategory}
|
||||
isChecked={categories === IMAGE_CATEGORIES}
|
||||
<Box sx={{ w: 'full' }}>
|
||||
<Flex
|
||||
ref={resizeObserverRef}
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<ButtonGroup isAttached>
|
||||
<IAIIconButton
|
||||
tooltip={t('gallery.images')}
|
||||
aria-label={t('gallery.images')}
|
||||
onClick={handleClickImagesCategory}
|
||||
isChecked={galleryView === 'images'}
|
||||
size="sm"
|
||||
icon={<FaImage />}
|
||||
/>
|
||||
<IAIIconButton
|
||||
tooltip={t('gallery.assets')}
|
||||
aria-label={t('gallery.assets')}
|
||||
onClick={handleClickAssetsCategory}
|
||||
isChecked={galleryView === 'assets'}
|
||||
size="sm"
|
||||
icon={<FaServer />}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<Flex
|
||||
as={Button}
|
||||
onClick={onToggle}
|
||||
size="sm"
|
||||
icon={<FaImage />}
|
||||
/>
|
||||
<IAIIconButton
|
||||
tooltip={t('gallery.assets')}
|
||||
aria-label={t('gallery.assets')}
|
||||
onClick={handleClickAssetsCategory}
|
||||
isChecked={categories === ASSETS_CATEGORIES}
|
||||
size="sm"
|
||||
icon={<FaServer />}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
<Flex gap={2}>
|
||||
variant="ghost"
|
||||
sx={{
|
||||
w: 'full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
px: 2,
|
||||
_hover: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
noOfLines={1}
|
||||
sx={{ w: 'full', color: 'base.200', fontWeight: 600 }}
|
||||
>
|
||||
{selectedBoard ? selectedBoard.board_name : 'All Images'}
|
||||
</Text>
|
||||
<ChevronUpIcon
|
||||
sx={{
|
||||
transform: isBoardListOpen ? 'rotate(0deg)' : 'rotate(180deg)',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<IAIPopover
|
||||
triggerComponent={
|
||||
<IAIIconButton
|
||||
@ -269,9 +341,12 @@ const ImageGalleryContent = () => {
|
||||
icon={shouldPinGallery ? <BsPinAngleFill /> : <BsPinAngle />}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Flex direction="column" gap={2} h="full">
|
||||
{images.length || areMoreImagesAvailable ? (
|
||||
<Box>
|
||||
<BoardsList isOpen={isBoardListOpen} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Flex direction="column" gap={2} h="full" w="full">
|
||||
{images.length || areMoreAvailable ? (
|
||||
<>
|
||||
<Box ref={rootRef} data-overlayscrollbars="" h="100%">
|
||||
{shouldUseSingleGalleryColumn ? (
|
||||
@ -280,14 +355,12 @@ const ImageGalleryContent = () => {
|
||||
data={images}
|
||||
endReached={handleEndReached}
|
||||
scrollerRef={(ref) => setScrollerRef(ref)}
|
||||
itemContent={(index, image) => (
|
||||
itemContent={(index, item) => (
|
||||
<Flex sx={{ pb: 2 }}>
|
||||
<HoverableImage
|
||||
key={`${image.image_name}-${image.thumbnail_url}`}
|
||||
image={image}
|
||||
isSelected={
|
||||
selectedImage?.image_name === image?.image_name
|
||||
}
|
||||
key={`${item.image_name}-${item.thumbnail_url}`}
|
||||
image={item}
|
||||
isSelected={selectedImage === item?.image_name}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
@ -302,13 +375,11 @@ const ImageGalleryContent = () => {
|
||||
List: ListContainer,
|
||||
}}
|
||||
scrollerRef={setScroller}
|
||||
itemContent={(index, image) => (
|
||||
itemContent={(index, item) => (
|
||||
<HoverableImage
|
||||
key={`${image.image_name}-${image.thumbnail_url}`}
|
||||
image={image}
|
||||
isSelected={
|
||||
selectedImage?.image_name === image?.image_name
|
||||
}
|
||||
key={`${item.image_name}-${item.thumbnail_url}`}
|
||||
image={item}
|
||||
isSelected={selectedImage === item?.image_name}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
@ -316,12 +387,12 @@ const ImageGalleryContent = () => {
|
||||
</Box>
|
||||
<IAIButton
|
||||
onClick={handleLoadMoreImages}
|
||||
isDisabled={!areMoreImagesAvailable}
|
||||
isDisabled={!areMoreAvailable}
|
||||
isLoading={isLoading}
|
||||
loadingText="Loading"
|
||||
flexShrink={0}
|
||||
>
|
||||
{areMoreImagesAvailable
|
||||
{areMoreAvailable
|
||||
? t('gallery.loadMore')
|
||||
: t('gallery.allImagesLoaded')}
|
||||
</IAIButton>
|
||||
@ -350,7 +421,7 @@ const ImageGalleryContent = () => {
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -93,19 +93,11 @@ type ImageMetadataViewerProps = {
|
||||
image: ImageDTO;
|
||||
};
|
||||
|
||||
// TODO: I don't know if this is needed.
|
||||
const memoEqualityCheck = (
|
||||
prev: ImageMetadataViewerProps,
|
||||
next: ImageMetadataViewerProps
|
||||
) => prev.image.image_name === next.image.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 ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const {
|
||||
recallBothPrompts,
|
||||
@ -333,8 +325,6 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}, memoEqualityCheck);
|
||||
};
|
||||
|
||||
ImageMetadataViewer.displayName = 'ImageMetadataViewer';
|
||||
|
||||
export default ImageMetadataViewer;
|
||||
export default memo(ImageMetadataViewer);
|
||||
|
@ -42,7 +42,7 @@ export const nextPrevImageButtonsSelector = createSelector(
|
||||
}
|
||||
|
||||
const currentImageIndex = filteredImageIds.findIndex(
|
||||
(i) => i === selectedImage.image_name
|
||||
(i) => i === selectedImage
|
||||
);
|
||||
|
||||
const nextImageIndex = clamp(
|
||||
@ -71,6 +71,8 @@ export const nextPrevImageButtonsSelector = createSelector(
|
||||
!isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1,
|
||||
nextImage,
|
||||
prevImage,
|
||||
nextImageId,
|
||||
prevImageId,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -84,7 +86,7 @@ const NextPrevImageButtons = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { isOnFirstImage, isOnLastImage, nextImage, prevImage } =
|
||||
const { isOnFirstImage, isOnLastImage, nextImageId, prevImageId } =
|
||||
useAppSelector(nextPrevImageButtonsSelector);
|
||||
|
||||
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
|
||||
@ -99,19 +101,19 @@ const NextPrevImageButtons = () => {
|
||||
}, []);
|
||||
|
||||
const handlePrevImage = useCallback(() => {
|
||||
dispatch(imageSelected(prevImage));
|
||||
}, [dispatch, prevImage]);
|
||||
dispatch(imageSelected(prevImageId));
|
||||
}, [dispatch, prevImageId]);
|
||||
|
||||
const handleNextImage = useCallback(() => {
|
||||
dispatch(imageSelected(nextImage));
|
||||
}, [dispatch, nextImage]);
|
||||
dispatch(imageSelected(nextImageId));
|
||||
}, [dispatch, nextImageId]);
|
||||
|
||||
useHotkeys(
|
||||
'left',
|
||||
() => {
|
||||
handlePrevImage();
|
||||
},
|
||||
[prevImage]
|
||||
[prevImageId]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
@ -119,7 +121,7 @@ const NextPrevImageButtons = () => {
|
||||
() => {
|
||||
handleNextImage();
|
||||
},
|
||||
[nextImage]
|
||||
[nextImageId]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
export const SelectedItemOverlay = () => (
|
||||
<motion.div
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineStart: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
boxShadow: 'inset 0px 0px 0px 2px var(--invokeai-colors-accent-300)',
|
||||
borderRadius: 'var(--invokeai-radii-base)',
|
||||
}}
|
||||
/>
|
||||
);
|
@ -0,0 +1,23 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { selectBoardsAll } from './boardSlice';
|
||||
|
||||
export const boardSelector = (state: RootState) => state.boards.entities;
|
||||
|
||||
export const searchBoardsSelector = createSelector(
|
||||
(state: RootState) => state,
|
||||
(state) => {
|
||||
const {
|
||||
boards: { searchText },
|
||||
} = state;
|
||||
|
||||
if (!searchText) {
|
||||
// If no search text provided, return all entities
|
||||
return selectBoardsAll(state);
|
||||
}
|
||||
|
||||
return selectBoardsAll(state).filter((i) =>
|
||||
i.board_name.toLowerCase().includes(searchText.toLowerCase())
|
||||
);
|
||||
}
|
||||
);
|
@ -0,0 +1,47 @@
|
||||
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { api } from 'services/apiSlice';
|
||||
|
||||
type BoardsState = {
|
||||
searchText: string;
|
||||
selectedBoardId?: string;
|
||||
updateBoardModalOpen: boolean;
|
||||
};
|
||||
|
||||
export const initialBoardsState: BoardsState = {
|
||||
updateBoardModalOpen: false,
|
||||
searchText: '',
|
||||
};
|
||||
|
||||
const boardsSlice = createSlice({
|
||||
name: 'boards',
|
||||
initialState: initialBoardsState,
|
||||
reducers: {
|
||||
boardIdSelected: (state, action: PayloadAction<string | undefined>) => {
|
||||
state.selectedBoardId = action.payload;
|
||||
},
|
||||
setBoardSearchText: (state, action: PayloadAction<string>) => {
|
||||
state.searchText = action.payload;
|
||||
},
|
||||
setUpdateBoardModalOpen: (state, action: PayloadAction<boolean>) => {
|
||||
state.updateBoardModalOpen = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addMatcher(
|
||||
api.endpoints.deleteBoard.matchFulfilled,
|
||||
(state, action) => {
|
||||
if (action.meta.arg.originalArgs === state.selectedBoardId) {
|
||||
state.selectedBoardId = undefined;
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const { boardIdSelected, setBoardSearchText, setUpdateBoardModalOpen } =
|
||||
boardsSlice.actions;
|
||||
|
||||
export const boardsSelector = (state: RootState) => state.boards;
|
||||
|
||||
export default boardsSlice.reducer;
|
@ -1,17 +1,16 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { imageUpserted } from './imagesSlice';
|
||||
import { imageUrlsReceived } from 'services/thunks/image';
|
||||
|
||||
type GalleryImageObjectFitType = 'contain' | 'cover';
|
||||
|
||||
export interface GalleryState {
|
||||
selectedImage?: ImageDTO;
|
||||
selectedImage?: string;
|
||||
galleryImageMinimumWidth: number;
|
||||
galleryImageObjectFit: GalleryImageObjectFitType;
|
||||
shouldAutoSwitchToNewImages: boolean;
|
||||
shouldUseSingleGalleryColumn: boolean;
|
||||
galleryView: 'images' | 'assets' | 'boards';
|
||||
}
|
||||
|
||||
export const initialGalleryState: GalleryState = {
|
||||
@ -19,13 +18,14 @@ export const initialGalleryState: GalleryState = {
|
||||
galleryImageObjectFit: 'cover',
|
||||
shouldAutoSwitchToNewImages: true,
|
||||
shouldUseSingleGalleryColumn: false,
|
||||
galleryView: 'images',
|
||||
};
|
||||
|
||||
export const gallerySlice = createSlice({
|
||||
name: 'gallery',
|
||||
initialState: initialGalleryState,
|
||||
reducers: {
|
||||
imageSelected: (state, action: PayloadAction<ImageDTO | undefined>) => {
|
||||
imageSelected: (state, action: PayloadAction<string | undefined>) => {
|
||||
state.selectedImage = action.payload;
|
||||
// TODO: if the user selects an image, disable the auto switch?
|
||||
// state.shouldAutoSwitchToNewImages = false;
|
||||
@ -48,6 +48,12 @@ export const gallerySlice = createSlice({
|
||||
) => {
|
||||
state.shouldUseSingleGalleryColumn = action.payload;
|
||||
},
|
||||
setGalleryView: (
|
||||
state,
|
||||
action: PayloadAction<'images' | 'assets' | 'boards'>
|
||||
) => {
|
||||
state.galleryView = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(imageUpserted, (state, action) => {
|
||||
@ -55,17 +61,17 @@ export const gallerySlice = createSlice({
|
||||
state.shouldAutoSwitchToNewImages &&
|
||||
action.payload.image_category === 'general'
|
||||
) {
|
||||
state.selectedImage = action.payload;
|
||||
state.selectedImage = action.payload.image_name;
|
||||
}
|
||||
});
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
// builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
// const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
|
||||
if (state.selectedImage?.image_name === image_name) {
|
||||
state.selectedImage.image_url = image_url;
|
||||
state.selectedImage.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
});
|
||||
// if (state.selectedImage?.image_name === image_name) {
|
||||
// state.selectedImage.image_url = image_url;
|
||||
// state.selectedImage.thumbnail_url = thumbnail_url;
|
||||
// }
|
||||
// });
|
||||
},
|
||||
});
|
||||
|
||||
@ -75,6 +81,7 @@ export const {
|
||||
setGalleryImageObjectFit,
|
||||
setShouldAutoSwitchToNewImages,
|
||||
setShouldUseSingleGalleryColumn,
|
||||
setGalleryView,
|
||||
} = gallerySlice.actions;
|
||||
|
||||
export default gallerySlice.reducer;
|
||||
|
@ -11,7 +11,6 @@ import { dateComparator } from 'common/util/dateComparator';
|
||||
import { keyBy } from 'lodash-es';
|
||||
import {
|
||||
imageDeleted,
|
||||
imageMetadataReceived,
|
||||
imageUrlsReceived,
|
||||
receivedPageOfImages,
|
||||
} from 'services/thunks/image';
|
||||
@ -74,11 +73,21 @@ const imagesSlice = createSlice({
|
||||
});
|
||||
builder.addCase(receivedPageOfImages.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
const { boardId, categories, imageOrigin, isIntermediate } =
|
||||
action.meta.arg;
|
||||
|
||||
const { items, offset, limit, total } = action.payload;
|
||||
imagesAdapter.upsertMany(state, items);
|
||||
|
||||
if (!categories?.includes('general') || boardId) {
|
||||
// need to skip updating the total images count if the images recieved were for a specific board
|
||||
// TODO: this doesn't work when on the Asset tab/category...
|
||||
return;
|
||||
}
|
||||
|
||||
state.offset = offset;
|
||||
state.limit = limit;
|
||||
state.total = total;
|
||||
imagesAdapter.upsertMany(state, items);
|
||||
});
|
||||
builder.addCase(imageDeleted.pending, (state, action) => {
|
||||
// Image deleted
|
||||
@ -154,3 +163,16 @@ export const selectFilteredImagesIds = createSelector(
|
||||
.map((i) => i.image_name);
|
||||
}
|
||||
);
|
||||
|
||||
// export const selectImageById = createSelector(
|
||||
// (state: RootState, imageId) => state,
|
||||
// (state) => {
|
||||
// const {
|
||||
// images: { categories },
|
||||
// } = state;
|
||||
|
||||
// return selectImagesAll(state)
|
||||
// .filter((i) => categories.includes(i.image_category))
|
||||
// .map((i) => i.image_name);
|
||||
// }
|
||||
// );
|
||||
|
@ -11,6 +11,8 @@ import { FieldComponentProps } from './types';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { useGetImageDTOQuery } from 'services/apiSlice';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
|
||||
const ImageInputFieldComponent = (
|
||||
props: FieldComponentProps<ImageInputFieldValue, ImageInputFieldTemplate>
|
||||
@ -19,9 +21,16 @@ const ImageInputFieldComponent = (
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const {
|
||||
data: image,
|
||||
isLoading,
|
||||
isError,
|
||||
isSuccess,
|
||||
} = useGetImageDTOQuery(field.value ?? skipToken);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(droppedImage: ImageDTO) => {
|
||||
if (field.value?.image_name === droppedImage.image_name) {
|
||||
if (field.value === droppedImage.image_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -29,11 +38,11 @@ const ImageInputFieldComponent = (
|
||||
fieldValueChanged({
|
||||
nodeId,
|
||||
fieldName: field.name,
|
||||
value: droppedImage,
|
||||
value: droppedImage.image_name,
|
||||
})
|
||||
);
|
||||
},
|
||||
[dispatch, field.name, field.value?.image_name, nodeId]
|
||||
[dispatch, field.name, field.value, nodeId]
|
||||
);
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
@ -56,7 +65,7 @@ const ImageInputFieldComponent = (
|
||||
}}
|
||||
>
|
||||
<IAIDndImage
|
||||
image={field.value}
|
||||
image={image}
|
||||
onDrop={handleDrop}
|
||||
onReset={handleReset}
|
||||
resetIconSize="sm"
|
||||
|
@ -1,28 +1,18 @@
|
||||
import { Select } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { SelectItem } from '@mantine/core';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { fieldValueChanged } from 'features/nodes/store/nodesSlice';
|
||||
import {
|
||||
ModelInputFieldTemplate,
|
||||
ModelInputFieldValue,
|
||||
} from 'features/nodes/types/types';
|
||||
import { selectModelsIds } from 'features/system/store/modelSlice';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { ChangeEvent, memo } from 'react';
|
||||
import { FieldComponentProps } from './types';
|
||||
|
||||
const availableModelsSelector = createSelector(
|
||||
[selectModelsIds],
|
||||
(allModelNames) => {
|
||||
return { allModelNames };
|
||||
// return map(modelList, (_, name) => name);
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import { FieldComponentProps } from './types';
|
||||
import { forEach, isString } from 'lodash-es';
|
||||
import { MODEL_TYPE_MAP as BASE_MODEL_NAME_MAP } from 'features/system/components/ModelSelect';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useListModelsQuery } from 'services/apiSlice';
|
||||
|
||||
const ModelInputFieldComponent = (
|
||||
props: FieldComponentProps<ModelInputFieldValue, ModelInputFieldTemplate>
|
||||
@ -30,28 +20,82 @@ const ModelInputFieldComponent = (
|
||||
const { nodeId, field } = props;
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { allModelNames } = useAppSelector(availableModelsSelector);
|
||||
const { data: pipelineModels } = useListModelsQuery({
|
||||
model_type: 'pipeline',
|
||||
});
|
||||
|
||||
const handleValueChanged = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||
dispatch(
|
||||
fieldValueChanged({
|
||||
nodeId,
|
||||
fieldName: field.name,
|
||||
value: e.target.value,
|
||||
})
|
||||
);
|
||||
};
|
||||
const data = useMemo(() => {
|
||||
if (!pipelineModels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const data: SelectItem[] = [];
|
||||
|
||||
forEach(pipelineModels.entities, (model, id) => {
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.push({
|
||||
value: id,
|
||||
label: model.name,
|
||||
group: BASE_MODEL_NAME_MAP[model.base_model],
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
}, [pipelineModels]);
|
||||
|
||||
const selectedModel = useMemo(
|
||||
() => pipelineModels?.entities[field.value ?? pipelineModels.ids[0]],
|
||||
[pipelineModels?.entities, pipelineModels?.ids, field.value]
|
||||
);
|
||||
|
||||
const handleValueChanged = useCallback(
|
||||
(v: string | null) => {
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(
|
||||
fieldValueChanged({
|
||||
nodeId,
|
||||
fieldName: field.name,
|
||||
value: v,
|
||||
})
|
||||
);
|
||||
},
|
||||
[dispatch, field.name, nodeId]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (field.value && pipelineModels?.ids.includes(field.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstModel = pipelineModels?.ids[0];
|
||||
|
||||
if (!isString(firstModel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleValueChanged(firstModel);
|
||||
}, [field.value, handleValueChanged, pipelineModels?.ids]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
<IAIMantineSelect
|
||||
tooltip={selectedModel?.description}
|
||||
label={
|
||||
selectedModel?.base_model &&
|
||||
BASE_MODEL_NAME_MAP[selectedModel?.base_model]
|
||||
}
|
||||
value={field.value}
|
||||
placeholder="Pick one"
|
||||
data={data}
|
||||
onChange={handleValueChanged}
|
||||
value={field.value || allModelNames[0]}
|
||||
>
|
||||
{allModelNames.map((option) => (
|
||||
<option key={option}>{option}</option>
|
||||
))}
|
||||
</Select>
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -101,21 +101,6 @@ const nodesSlice = createSlice({
|
||||
builder.addCase(receivedOpenAPISchema.fulfilled, (state, action) => {
|
||||
state.schema = action.payload;
|
||||
});
|
||||
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
|
||||
state.nodes.forEach((node) => {
|
||||
forEach(node.data.inputs, (input) => {
|
||||
if (input.type === 'image') {
|
||||
if (input.value?.image_name === image_name) {
|
||||
input.value.image_url = image_url;
|
||||
input.value.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -214,7 +214,7 @@ export type VaeInputFieldValue = FieldValueBase & {
|
||||
|
||||
export type ImageInputFieldValue = FieldValueBase & {
|
||||
type: 'image';
|
||||
value?: ImageDTO;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
export type ModelInputFieldValue = FieldValueBase & {
|
||||
|
@ -65,15 +65,13 @@ export const addControlNetToLinearGraph = (
|
||||
|
||||
if (processedControlImage && processorType !== 'none') {
|
||||
// We've already processed the image in the app, so we can just use the processed image
|
||||
const { image_name } = processedControlImage;
|
||||
controlNetNode.image = {
|
||||
image_name,
|
||||
image_name: processedControlImage,
|
||||
};
|
||||
} else if (controlImage) {
|
||||
// The control image is preprocessed
|
||||
const { image_name } = controlImage;
|
||||
controlNetNode.image = {
|
||||
image_name,
|
||||
image_name: controlImage,
|
||||
};
|
||||
} else {
|
||||
// Skip ControlNets without an unprocessed image - should never happen if everything is working correctly
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
} from './constants';
|
||||
import { set } from 'lodash-es';
|
||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'nodes' });
|
||||
|
||||
@ -36,7 +37,7 @@ export const buildCanvasImageToImageGraph = (
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model: model_name,
|
||||
model: modelId,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
@ -49,6 +50,8 @@ export const buildCanvasImageToImageGraph = (
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
const model = modelIdToPipelineModelField(modelId);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
@ -85,9 +88,9 @@ export const buildCanvasImageToImageGraph = (
|
||||
id: NOISE,
|
||||
},
|
||||
[MODEL_LOADER]: {
|
||||
type: 'sd1_model_loader',
|
||||
type: 'pipeline_model_loader',
|
||||
id: MODEL_LOADER,
|
||||
model_name,
|
||||
model,
|
||||
},
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
INPAINT_GRAPH,
|
||||
INPAINT,
|
||||
} from './constants';
|
||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'nodes' });
|
||||
|
||||
@ -31,7 +32,7 @@ export const buildCanvasInpaintGraph = (
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model: model_name,
|
||||
model: modelId,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
@ -54,6 +55,8 @@ export const buildCanvasInpaintGraph = (
|
||||
// We may need to set the inpaint width and height to scale the image
|
||||
const { scaledBoundingBoxDimensions, boundingBoxScaleMethod } = state.canvas;
|
||||
|
||||
const model = modelIdToPipelineModelField(modelId);
|
||||
|
||||
const graph: NonNullableGraph = {
|
||||
id: INPAINT_GRAPH,
|
||||
nodes: {
|
||||
@ -99,9 +102,9 @@ export const buildCanvasInpaintGraph = (
|
||||
prompt: negativePrompt,
|
||||
},
|
||||
[MODEL_LOADER]: {
|
||||
type: 'sd1_model_loader',
|
||||
type: 'pipeline_model_loader',
|
||||
id: MODEL_LOADER,
|
||||
model_name,
|
||||
model,
|
||||
},
|
||||
[RANGE_OF_SIZE]: {
|
||||
type: 'range_of_size',
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
TEXT_TO_LATENTS,
|
||||
} from './constants';
|
||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||
|
||||
/**
|
||||
* Builds the Canvas tab's Text to Image graph.
|
||||
@ -24,7 +25,7 @@ export const buildCanvasTextToImageGraph = (
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model: model_name,
|
||||
model: modelId,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
@ -36,6 +37,8 @@ export const buildCanvasTextToImageGraph = (
|
||||
// The bounding box determines width and height, not the width and height params
|
||||
const { width, height } = state.canvas.boundingBoxDimensions;
|
||||
|
||||
const model = modelIdToPipelineModelField(modelId);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
@ -80,9 +83,9 @@ export const buildCanvasTextToImageGraph = (
|
||||
steps,
|
||||
},
|
||||
[MODEL_LOADER]: {
|
||||
type: 'sd1_model_loader',
|
||||
type: 'pipeline_model_loader',
|
||||
id: MODEL_LOADER,
|
||||
model_name,
|
||||
model,
|
||||
},
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
} from './constants';
|
||||
import { set } from 'lodash-es';
|
||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'nodes' });
|
||||
|
||||
@ -34,7 +35,7 @@ export const buildLinearImageToImageGraph = (
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model: model_name,
|
||||
model: modelId,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
@ -62,6 +63,8 @@ export const buildLinearImageToImageGraph = (
|
||||
throw new Error('No initial image found in state');
|
||||
}
|
||||
|
||||
const model = modelIdToPipelineModelField(modelId);
|
||||
|
||||
// copy-pasted graph from node editor, filled in with state values & friendly node ids
|
||||
const graph: NonNullableGraph = {
|
||||
id: IMAGE_TO_IMAGE_GRAPH,
|
||||
@ -89,9 +92,9 @@ export const buildLinearImageToImageGraph = (
|
||||
id: NOISE,
|
||||
},
|
||||
[MODEL_LOADER]: {
|
||||
type: 'sd1_model_loader',
|
||||
type: 'pipeline_model_loader',
|
||||
id: MODEL_LOADER,
|
||||
model_name,
|
||||
model,
|
||||
},
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
@ -274,7 +277,7 @@ export const buildLinearImageToImageGraph = (
|
||||
id: RESIZE,
|
||||
type: 'img_resize',
|
||||
image: {
|
||||
image_name: initialImage.image_name,
|
||||
image_name: initialImage.imageName,
|
||||
},
|
||||
is_intermediate: true,
|
||||
width,
|
||||
@ -311,7 +314,7 @@ export const buildLinearImageToImageGraph = (
|
||||
} else {
|
||||
// We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly
|
||||
set(graph.nodes[IMAGE_TO_LATENTS], 'image', {
|
||||
image_name: initialImage.image_name,
|
||||
image_name: initialImage.imageName,
|
||||
});
|
||||
|
||||
// Pass the image's dimensions to the `NOISE` node
|
||||
|
@ -1,6 +1,10 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
import { NonNullableGraph } from 'features/nodes/types/types';
|
||||
import { RandomIntInvocation, RangeOfSizeInvocation } from 'services/api';
|
||||
import {
|
||||
BaseModelType,
|
||||
RandomIntInvocation,
|
||||
RangeOfSizeInvocation,
|
||||
} from 'services/api';
|
||||
import {
|
||||
ITERATE,
|
||||
LATENTS_TO_IMAGE,
|
||||
@ -14,6 +18,7 @@ import {
|
||||
TEXT_TO_LATENTS,
|
||||
} from './constants';
|
||||
import { addControlNetToLinearGraph } from '../addControlNetToLinearGraph';
|
||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||
|
||||
type TextToImageGraphOverrides = {
|
||||
width: number;
|
||||
@ -27,7 +32,7 @@ export const buildLinearTextToImageGraph = (
|
||||
const {
|
||||
positivePrompt,
|
||||
negativePrompt,
|
||||
model: model_name,
|
||||
model: modelId,
|
||||
cfgScale: cfg_scale,
|
||||
scheduler,
|
||||
steps,
|
||||
@ -38,6 +43,8 @@ export const buildLinearTextToImageGraph = (
|
||||
shouldRandomizeSeed,
|
||||
} = state.generation;
|
||||
|
||||
const model = modelIdToPipelineModelField(modelId);
|
||||
|
||||
/**
|
||||
* The easiest way to build linear graphs is to do it in the node editor, then copy and paste the
|
||||
* full graph here as a template. Then use the parameters from app state and set friendlier node
|
||||
@ -82,9 +89,9 @@ export const buildLinearTextToImageGraph = (
|
||||
steps,
|
||||
},
|
||||
[MODEL_LOADER]: {
|
||||
type: 'sd1_model_loader',
|
||||
type: 'pipeline_model_loader',
|
||||
id: MODEL_LOADER,
|
||||
model_name,
|
||||
model,
|
||||
},
|
||||
[LATENTS_TO_IMAGE]: {
|
||||
type: 'l2i',
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Graph } from 'services/api';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { cloneDeep, forEach, omit, reduce, values } from 'lodash-es';
|
||||
import { cloneDeep, omit, reduce } from 'lodash-es';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { InputFieldValue } from 'features/nodes/types/types';
|
||||
import { AnyInvocation } from 'services/events/types';
|
||||
import { modelIdToPipelineModelField } from '../modelIdToPipelineModelField';
|
||||
|
||||
/**
|
||||
* We need to do special handling for some fields
|
||||
@ -24,6 +25,12 @@ export const parseFieldValue = (field: InputFieldValue) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'model') {
|
||||
if (field.value) {
|
||||
return modelIdToPipelineModelField(field.value);
|
||||
}
|
||||
}
|
||||
|
||||
return field.value;
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ export const NOISE = 'noise';
|
||||
export const RANDOM_INT = 'rand_int';
|
||||
export const RANGE_OF_SIZE = 'range_of_size';
|
||||
export const ITERATE = 'iterate';
|
||||
export const MODEL_LOADER = 'model_loader';
|
||||
export const MODEL_LOADER = 'pipeline_model_loader';
|
||||
export const IMAGE_TO_LATENTS = 'image_to_latents';
|
||||
export const LATENTS_TO_LATENTS = 'latents_to_latents';
|
||||
export const RESIZE = 'resize_image';
|
||||
|
@ -0,0 +1,18 @@
|
||||
import { BaseModelType, PipelineModelField } from 'services/api';
|
||||
|
||||
/**
|
||||
* Crudely converts a model id to a pipeline model field
|
||||
* TODO: Make better
|
||||
*/
|
||||
export const modelIdToPipelineModelField = (
|
||||
modelId: string
|
||||
): PipelineModelField => {
|
||||
const [base_model, model_type, model_name] = modelId.split('/');
|
||||
|
||||
const field: PipelineModelField = {
|
||||
base_model: base_model as BaseModelType,
|
||||
model_name,
|
||||
};
|
||||
|
||||
return field;
|
||||
};
|
@ -57,7 +57,7 @@ export const buildImg2ImgNode = (
|
||||
}
|
||||
|
||||
imageToImageNode.image = {
|
||||
image_name: initialImage.image_name,
|
||||
image_name: initialImage.imageName,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import ParamScheduler from './ParamScheduler';
|
||||
const ParamSchedulerAndModel = () => {
|
||||
return (
|
||||
<Flex gap={3} w="full">
|
||||
<Box w="20rem">
|
||||
<Box w="25rem">
|
||||
<ParamScheduler />
|
||||
</Box>
|
||||
<Box w="full">
|
||||
|
@ -10,7 +10,9 @@ import { generationSelector } from 'features/parameters/store/generationSelector
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { IAIImageFallback } from 'common/components/IAIImageFallback';
|
||||
import { IAIImageLoadingFallback } from 'common/components/IAIImageFallback';
|
||||
import { useGetImageDTOQuery } from 'services/apiSlice';
|
||||
import { skipToken } from '@reduxjs/toolkit/dist/query';
|
||||
|
||||
const selector = createSelector(
|
||||
[generationSelector],
|
||||
@ -27,14 +29,21 @@ const InitialImagePreview = () => {
|
||||
const { initialImage } = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const {
|
||||
data: image,
|
||||
isLoading,
|
||||
isError,
|
||||
isSuccess,
|
||||
} = useGetImageDTOQuery(initialImage?.imageName ?? skipToken);
|
||||
|
||||
const handleDrop = useCallback(
|
||||
(droppedImage: ImageDTO) => {
|
||||
if (droppedImage.image_name === initialImage?.image_name) {
|
||||
if (droppedImage.image_name === initialImage?.imageName) {
|
||||
return;
|
||||
}
|
||||
dispatch(initialImageChanged(droppedImage));
|
||||
},
|
||||
[dispatch, initialImage?.image_name]
|
||||
[dispatch, initialImage]
|
||||
);
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
@ -53,10 +62,10 @@ const InitialImagePreview = () => {
|
||||
}}
|
||||
>
|
||||
<IAIDndImage
|
||||
image={initialImage}
|
||||
image={image}
|
||||
onDrop={handleDrop}
|
||||
onReset={handleReset}
|
||||
fallback={<IAIImageFallback sx={{ bg: 'none' }} />}
|
||||
fallback={<IAIImageLoadingFallback sx={{ bg: 'none' }} />}
|
||||
postUploadAction={{ type: 'SET_INITIAL_IMAGE' }}
|
||||
withResetIcon
|
||||
/>
|
||||
|
@ -1,10 +1,9 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { DEFAULT_SCHEDULER_NAME } from 'app/constants';
|
||||
import { configChanged } from 'features/system/store/configSlice';
|
||||
import { clamp, sortBy } from 'lodash-es';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { ImageDTO } from 'services/api';
|
||||
import { imageUrlsReceived } from 'services/thunks/image';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
import {
|
||||
CfgScaleParam,
|
||||
HeightParam,
|
||||
@ -17,14 +16,13 @@ import {
|
||||
StrengthParam,
|
||||
WidthParam,
|
||||
} from './parameterZodSchemas';
|
||||
import { DEFAULT_SCHEDULER_NAME } from 'app/constants';
|
||||
|
||||
export interface GenerationState {
|
||||
cfgScale: CfgScaleParam;
|
||||
height: HeightParam;
|
||||
img2imgStrength: StrengthParam;
|
||||
infillMethod: string;
|
||||
initialImage?: ImageDTO;
|
||||
initialImage?: { imageName: string; width: number; height: number };
|
||||
iterations: number;
|
||||
perlin: number;
|
||||
positivePrompt: PositivePromptParam;
|
||||
@ -212,35 +210,20 @@ export const generationSlice = createSlice({
|
||||
state.shouldUseNoiseSettings = action.payload;
|
||||
},
|
||||
initialImageChanged: (state, action: PayloadAction<ImageDTO>) => {
|
||||
state.initialImage = action.payload;
|
||||
const { image_name, width, height } = action.payload;
|
||||
state.initialImage = { imageName: image_name, width, height };
|
||||
},
|
||||
modelSelected: (state, action: PayloadAction<string>) => {
|
||||
state.model = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(receivedModels.fulfilled, (state, action) => {
|
||||
if (!state.model) {
|
||||
const firstModel = sortBy(action.payload, 'name')[0];
|
||||
state.model = firstModel.name;
|
||||
}
|
||||
});
|
||||
|
||||
builder.addCase(configChanged, (state, action) => {
|
||||
const defaultModel = action.payload.sd?.defaultModel;
|
||||
if (defaultModel && !state.model) {
|
||||
state.model = defaultModel;
|
||||
}
|
||||
});
|
||||
|
||||
builder.addCase(imageUrlsReceived.fulfilled, (state, action) => {
|
||||
const { image_name, image_url, thumbnail_url } = action.payload;
|
||||
|
||||
if (state.initialImage?.image_name === image_name) {
|
||||
state.initialImage.image_url = image_url;
|
||||
state.initialImage.thumbnail_url = thumbnail_url;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -154,3 +154,17 @@ export type StrengthParam = z.infer<typeof zStrength>;
|
||||
*/
|
||||
export const isValidStrength = (val: unknown): val is StrengthParam =>
|
||||
zStrength.safeParse(val).success;
|
||||
|
||||
// /**
|
||||
// * Zod schema for BaseModelType
|
||||
// */
|
||||
// export const zBaseModelType = z.enum(['sd-1', 'sd-2']);
|
||||
// /**
|
||||
// * Type alias for base model type, inferred from its zod schema. Should be identical to the type alias from OpenAPI.
|
||||
// */
|
||||
// export type BaseModelType = z.infer<typeof zBaseModelType>;
|
||||
// /**
|
||||
// * Validates/type-guards a value as a base model type
|
||||
// */
|
||||
// export const isValidBaseModelType = (val: unknown): val is BaseModelType =>
|
||||
// zBaseModelType.safeParse(val).success;
|
||||
|
@ -1,44 +1,59 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIMantineSelect, {
|
||||
IAISelectDataType,
|
||||
} from 'common/components/IAIMantineSelect';
|
||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { modelSelected } from 'features/parameters/store/generationSlice';
|
||||
import { selectModelsAll, selectModelsById } from '../store/modelSlice';
|
||||
|
||||
const selector = createSelector(
|
||||
[(state: RootState) => state, generationSelector],
|
||||
(state, generation) => {
|
||||
const selectedModel = selectModelsById(state, generation.model);
|
||||
import { forEach, isString } from 'lodash-es';
|
||||
import { SelectItem } from '@mantine/core';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useListModelsQuery } from 'services/apiSlice';
|
||||
|
||||
const modelData = selectModelsAll(state)
|
||||
.map<IAISelectDataType>((m) => ({
|
||||
value: m.name,
|
||||
label: m.name,
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label));
|
||||
return {
|
||||
selectedModel,
|
||||
modelData,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
export const MODEL_TYPE_MAP = {
|
||||
'sd-1': 'Stable Diffusion 1.x',
|
||||
'sd-2': 'Stable Diffusion 2.x',
|
||||
};
|
||||
|
||||
const ModelSelect = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const { selectedModel, modelData } = useAppSelector(selector);
|
||||
|
||||
const selectedModelId = useAppSelector(
|
||||
(state: RootState) => state.generation.model
|
||||
);
|
||||
|
||||
const { data: pipelineModels } = useListModelsQuery({
|
||||
model_type: 'pipeline',
|
||||
});
|
||||
|
||||
const data = useMemo(() => {
|
||||
if (!pipelineModels) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const data: SelectItem[] = [];
|
||||
|
||||
forEach(pipelineModels.entities, (model, id) => {
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.push({
|
||||
value: id,
|
||||
label: model.name,
|
||||
group: MODEL_TYPE_MAP[model.base_model],
|
||||
});
|
||||
});
|
||||
|
||||
return data;
|
||||
}, [pipelineModels]);
|
||||
|
||||
const selectedModel = useMemo(
|
||||
() => pipelineModels?.entities[selectedModelId],
|
||||
[pipelineModels?.entities, selectedModelId]
|
||||
);
|
||||
|
||||
const handleChangeModel = useCallback(
|
||||
(v: string | null) => {
|
||||
if (!v) {
|
||||
@ -49,13 +64,27 @@ const ModelSelect = () => {
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedModelId && pipelineModels?.ids.includes(selectedModelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstModel = pipelineModels?.ids[0];
|
||||
|
||||
if (!isString(firstModel)) {
|
||||
return;
|
||||
}
|
||||
|
||||
handleChangeModel(firstModel);
|
||||
}, [handleChangeModel, pipelineModels?.ids, selectedModelId]);
|
||||
|
||||
return (
|
||||
<IAIMantineSelect
|
||||
tooltip={selectedModel?.description}
|
||||
label={t('modelManager.model')}
|
||||
value={selectedModel?.name ?? ''}
|
||||
value={selectedModelId}
|
||||
placeholder="Pick one"
|
||||
data={modelData}
|
||||
data={data}
|
||||
onChange={handleChangeModel}
|
||||
/>
|
||||
);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { SCHEDULER_LABEL_MAP, SCHEDULER_NAMES } from 'app/constants';
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIMantineMultiSelect from 'common/components/IAIMantineMultiSelect';
|
||||
import { SchedulerParam } from 'features/parameters/store/parameterZodSchemas';
|
||||
@ -16,6 +15,7 @@ const data = map(SCHEDULER_NAMES, (s) => ({
|
||||
|
||||
export default function SettingsSchedulers() {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const enabledSchedulers = useAppSelector(
|
||||
|
@ -7,13 +7,12 @@ import { systemSelector } from '../store/systemSelectors';
|
||||
const isApplicationReadySelector = createSelector(
|
||||
[systemSelector, configSelector],
|
||||
(system, config) => {
|
||||
const { wereModelsReceived, wasSchemaParsed } = system;
|
||||
const { wasSchemaParsed } = system;
|
||||
|
||||
const { disabledTabs } = config;
|
||||
|
||||
return {
|
||||
disabledTabs,
|
||||
wereModelsReceived,
|
||||
wasSchemaParsed,
|
||||
};
|
||||
}
|
||||
@ -23,21 +22,17 @@ const isApplicationReadySelector = createSelector(
|
||||
* Checks if the application is ready to be used, i.e. if the initial startup process is finished.
|
||||
*/
|
||||
export const useIsApplicationReady = () => {
|
||||
const { disabledTabs, wereModelsReceived, wasSchemaParsed } = useAppSelector(
|
||||
const { disabledTabs, wasSchemaParsed } = useAppSelector(
|
||||
isApplicationReadySelector
|
||||
);
|
||||
|
||||
const isApplicationReady = useMemo(() => {
|
||||
if (!wereModelsReceived) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!disabledTabs.includes('nodes') && !wasSchemaParsed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, [disabledTabs, wereModelsReceived, wasSchemaParsed]);
|
||||
}, [disabledTabs, wasSchemaParsed]);
|
||||
|
||||
return isApplicationReady;
|
||||
};
|
||||
|
@ -1,3 +0,0 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
export const modelSelector = (state: RootState) => state.models;
|
@ -1,47 +0,0 @@
|
||||
import { createEntityAdapter } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { CkptModelInfo, DiffusersModelInfo } from 'services/api';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
|
||||
export type Model = (CkptModelInfo | DiffusersModelInfo) & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const modelsAdapter = createEntityAdapter<Model>({
|
||||
selectId: (model) => model.name,
|
||||
sortComparer: (a, b) => a.name.localeCompare(b.name),
|
||||
});
|
||||
|
||||
export const initialModelsState = modelsAdapter.getInitialState();
|
||||
|
||||
export type ModelsState = typeof initialModelsState;
|
||||
|
||||
export const modelsSlice = createSlice({
|
||||
name: 'models',
|
||||
initialState: initialModelsState,
|
||||
reducers: {
|
||||
modelAdded: modelsAdapter.upsertOne,
|
||||
},
|
||||
extraReducers(builder) {
|
||||
/**
|
||||
* Received Models - FULFILLED
|
||||
*/
|
||||
builder.addCase(receivedModels.fulfilled, (state, action) => {
|
||||
const models = action.payload;
|
||||
modelsAdapter.setAll(state, models);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
selectAll: selectModelsAll,
|
||||
selectById: selectModelsById,
|
||||
selectEntities: selectModelsEntities,
|
||||
selectIds: selectModelsIds,
|
||||
selectTotal: selectModelsTotal,
|
||||
} = modelsAdapter.getSelectors<RootState>((state) => state.models);
|
||||
|
||||
export const { modelAdded } = modelsSlice.actions;
|
||||
|
||||
export default modelsSlice.reducer;
|
@ -1,6 +0,0 @@
|
||||
import { ModelsState } from './modelSlice';
|
||||
|
||||
/**
|
||||
* Models slice persist denylist
|
||||
*/
|
||||
export const modelsPersistDenylist: (keyof ModelsState)[] = ['entities', 'ids'];
|
@ -1,20 +1,12 @@
|
||||
import { UseToastOptions } from '@chakra-ui/react';
|
||||
import { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { PayloadAction, createSlice } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
|
||||
import { ProgressImage } from 'services/events/types';
|
||||
import { makeToast } from '../../../app/components/Toaster';
|
||||
import { isAnySessionRejected, sessionCanceled } from 'services/thunks/session';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
import { parsedOpenAPISchema } from 'features/nodes/store/nodesSlice';
|
||||
import { LogLevelName } from 'roarr';
|
||||
import { InvokeLogLevel } from 'app/logging/useLogger';
|
||||
import { TFuncKey } from 'i18next';
|
||||
import { t } from 'i18next';
|
||||
import { userInvoked } from 'app/store/actions';
|
||||
import { LANGUAGES } from '../components/LanguagePicker';
|
||||
import { imageUploaded } from 'services/thunks/image';
|
||||
import { parsedOpenAPISchema } from 'features/nodes/store/nodesSlice';
|
||||
import { TFuncKey, t } from 'i18next';
|
||||
import { LogLevelName } from 'roarr';
|
||||
import {
|
||||
appSocketConnected,
|
||||
appSocketDisconnected,
|
||||
@ -26,6 +18,11 @@ import {
|
||||
appSocketSubscribed,
|
||||
appSocketUnsubscribed,
|
||||
} from 'services/events/actions';
|
||||
import { ProgressImage } from 'services/events/types';
|
||||
import { imageUploaded } from 'services/thunks/image';
|
||||
import { isAnySessionRejected, sessionCanceled } from 'services/thunks/session';
|
||||
import { makeToast } from '../../../app/components/Toaster';
|
||||
import { LANGUAGES } from '../components/LanguagePicker';
|
||||
|
||||
export type CancelStrategy = 'immediate' | 'scheduled';
|
||||
|
||||
@ -95,6 +92,7 @@ export interface SystemState {
|
||||
shouldAntialiasProgressImage: boolean;
|
||||
language: keyof typeof LANGUAGES;
|
||||
isUploading: boolean;
|
||||
boardIdToAddTo?: string;
|
||||
}
|
||||
|
||||
export const initialSystemState: SystemState = {
|
||||
@ -225,6 +223,7 @@ export const systemSlice = createSlice({
|
||||
*/
|
||||
builder.addCase(appSocketSubscribed, (state, action) => {
|
||||
state.sessionId = action.payload.sessionId;
|
||||
state.boardIdToAddTo = action.payload.boardId;
|
||||
state.canceledSession = '';
|
||||
});
|
||||
|
||||
@ -233,6 +232,7 @@ export const systemSlice = createSlice({
|
||||
*/
|
||||
builder.addCase(appSocketUnsubscribed, (state) => {
|
||||
state.sessionId = null;
|
||||
state.boardIdToAddTo = undefined;
|
||||
});
|
||||
|
||||
/**
|
||||
@ -376,13 +376,6 @@ export const systemSlice = createSlice({
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Received available models from the backend
|
||||
*/
|
||||
builder.addCase(receivedModels.fulfilled, (state) => {
|
||||
state.wereModelsReceived = true;
|
||||
});
|
||||
|
||||
/**
|
||||
* OpenAPI schema was parsed
|
||||
*/
|
||||
|
@ -8,6 +8,10 @@ export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
export type { AddInvocation } from './models/AddInvocation';
|
||||
export type { BaseModelType } from './models/BaseModelType';
|
||||
export type { BoardChanges } from './models/BoardChanges';
|
||||
export type { BoardDTO } from './models/BoardDTO';
|
||||
export type { Body_create_board_image } from './models/Body_create_board_image';
|
||||
export type { Body_remove_board_image } from './models/Body_remove_board_image';
|
||||
export type { Body_upload_image } from './models/Body_upload_image';
|
||||
export type { CannyImageProcessorInvocation } from './models/CannyImageProcessorInvocation';
|
||||
export type { CkptModelInfo } from './models/CkptModelInfo';
|
||||
@ -21,6 +25,8 @@ export type { ConditioningField } from './models/ConditioningField';
|
||||
export type { ContentShuffleImageProcessorInvocation } from './models/ContentShuffleImageProcessorInvocation';
|
||||
export type { ControlField } from './models/ControlField';
|
||||
export type { ControlNetInvocation } from './models/ControlNetInvocation';
|
||||
export type { ControlNetModelConfig } from './models/ControlNetModelConfig';
|
||||
export type { ControlNetModelFormat } from './models/ControlNetModelFormat';
|
||||
export type { ControlOutput } from './models/ControlOutput';
|
||||
export type { CreateModelRequest } from './models/CreateModelRequest';
|
||||
export type { CvInpaintInvocation } from './models/CvInpaintInvocation';
|
||||
@ -63,14 +69,6 @@ export type { InfillTileInvocation } from './models/InfillTileInvocation';
|
||||
export type { InpaintInvocation } from './models/InpaintInvocation';
|
||||
export type { IntCollectionOutput } from './models/IntCollectionOutput';
|
||||
export type { IntOutput } from './models/IntOutput';
|
||||
export type { invokeai__backend__model_management__models__controlnet__ControlNetModel__Config } from './models/invokeai__backend__model_management__models__controlnet__ControlNetModel__Config';
|
||||
export type { invokeai__backend__model_management__models__lora__LoRAModel__Config } from './models/invokeai__backend__model_management__models__lora__LoRAModel__Config';
|
||||
export type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__CheckpointConfig } from './models/invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__CheckpointConfig';
|
||||
export type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__DiffusersConfig } from './models/invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__DiffusersConfig';
|
||||
export type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__CheckpointConfig } from './models/invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__CheckpointConfig';
|
||||
export type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__DiffusersConfig } from './models/invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__DiffusersConfig';
|
||||
export type { invokeai__backend__model_management__models__textual_inversion__TextualInversionModel__Config } from './models/invokeai__backend__model_management__models__textual_inversion__TextualInversionModel__Config';
|
||||
export type { invokeai__backend__model_management__models__vae__VaeModel__Config } from './models/invokeai__backend__model_management__models__vae__VaeModel__Config';
|
||||
export type { IterateInvocation } from './models/IterateInvocation';
|
||||
export type { IterateInvocationOutput } from './models/IterateInvocationOutput';
|
||||
export type { LatentsField } from './models/LatentsField';
|
||||
@ -83,6 +81,8 @@ export type { LoadImageInvocation } from './models/LoadImageInvocation';
|
||||
export type { LoraInfo } from './models/LoraInfo';
|
||||
export type { LoraLoaderInvocation } from './models/LoraLoaderInvocation';
|
||||
export type { LoraLoaderOutput } from './models/LoraLoaderOutput';
|
||||
export type { LoRAModelConfig } from './models/LoRAModelConfig';
|
||||
export type { LoRAModelFormat } from './models/LoRAModelFormat';
|
||||
export type { MaskFromAlphaInvocation } from './models/MaskFromAlphaInvocation';
|
||||
export type { MaskOutput } from './models/MaskOutput';
|
||||
export type { MediapipeFaceProcessorInvocation } from './models/MediapipeFaceProcessorInvocation';
|
||||
@ -98,12 +98,15 @@ export type { MultiplyInvocation } from './models/MultiplyInvocation';
|
||||
export type { NoiseInvocation } from './models/NoiseInvocation';
|
||||
export type { NoiseOutput } from './models/NoiseOutput';
|
||||
export type { NormalbaeImageProcessorInvocation } from './models/NormalbaeImageProcessorInvocation';
|
||||
export type { OffsetPaginatedResults_BoardDTO_ } from './models/OffsetPaginatedResults_BoardDTO_';
|
||||
export type { OffsetPaginatedResults_ImageDTO_ } from './models/OffsetPaginatedResults_ImageDTO_';
|
||||
export type { OpenposeImageProcessorInvocation } from './models/OpenposeImageProcessorInvocation';
|
||||
export type { PaginatedResults_GraphExecutionState_ } from './models/PaginatedResults_GraphExecutionState_';
|
||||
export type { ParamFloatInvocation } from './models/ParamFloatInvocation';
|
||||
export type { ParamIntInvocation } from './models/ParamIntInvocation';
|
||||
export type { PidiImageProcessorInvocation } from './models/PidiImageProcessorInvocation';
|
||||
export type { PipelineModelField } from './models/PipelineModelField';
|
||||
export type { PipelineModelLoaderInvocation } from './models/PipelineModelLoaderInvocation';
|
||||
export type { PromptCollectionOutput } from './models/PromptCollectionOutput';
|
||||
export type { PromptOutput } from './models/PromptOutput';
|
||||
export type { RandomIntInvocation } from './models/RandomIntInvocation';
|
||||
@ -115,20 +118,28 @@ export type { ResourceOrigin } from './models/ResourceOrigin';
|
||||
export type { RestoreFaceInvocation } from './models/RestoreFaceInvocation';
|
||||
export type { ScaleLatentsInvocation } from './models/ScaleLatentsInvocation';
|
||||
export type { SchedulerPredictionType } from './models/SchedulerPredictionType';
|
||||
export type { SD1ModelLoaderInvocation } from './models/SD1ModelLoaderInvocation';
|
||||
export type { SD2ModelLoaderInvocation } from './models/SD2ModelLoaderInvocation';
|
||||
export type { ShowImageInvocation } from './models/ShowImageInvocation';
|
||||
export type { StableDiffusion1ModelCheckpointConfig } from './models/StableDiffusion1ModelCheckpointConfig';
|
||||
export type { StableDiffusion1ModelDiffusersConfig } from './models/StableDiffusion1ModelDiffusersConfig';
|
||||
export type { StableDiffusion1ModelFormat } from './models/StableDiffusion1ModelFormat';
|
||||
export type { StableDiffusion2ModelCheckpointConfig } from './models/StableDiffusion2ModelCheckpointConfig';
|
||||
export type { StableDiffusion2ModelDiffusersConfig } from './models/StableDiffusion2ModelDiffusersConfig';
|
||||
export type { StableDiffusion2ModelFormat } from './models/StableDiffusion2ModelFormat';
|
||||
export type { StepParamEasingInvocation } from './models/StepParamEasingInvocation';
|
||||
export type { SubModelType } from './models/SubModelType';
|
||||
export type { SubtractInvocation } from './models/SubtractInvocation';
|
||||
export type { TextToLatentsInvocation } from './models/TextToLatentsInvocation';
|
||||
export type { TextualInversionModelConfig } from './models/TextualInversionModelConfig';
|
||||
export type { UNetField } from './models/UNetField';
|
||||
export type { UpscaleInvocation } from './models/UpscaleInvocation';
|
||||
export type { VaeField } from './models/VaeField';
|
||||
export type { VaeModelConfig } from './models/VaeModelConfig';
|
||||
export type { VaeModelFormat } from './models/VaeModelFormat';
|
||||
export type { VaeRepo } from './models/VaeRepo';
|
||||
export type { ValidationError } from './models/ValidationError';
|
||||
export type { ZoeDepthImageProcessorInvocation } from './models/ZoeDepthImageProcessorInvocation';
|
||||
|
||||
export { BoardsService } from './services/BoardsService';
|
||||
export { ImagesService } from './services/ImagesService';
|
||||
export { ModelsService } from './services/ModelsService';
|
||||
export { SessionsService } from './services/SessionsService';
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type BoardChanges = {
|
||||
/**
|
||||
* The board's new name.
|
||||
*/
|
||||
board_name?: string;
|
||||
/**
|
||||
* The name of the board's new cover image.
|
||||
*/
|
||||
cover_image_name?: string;
|
||||
};
|
||||
|
38
invokeai/frontend/web/src/services/api/models/BoardDTO.ts
Normal file
38
invokeai/frontend/web/src/services/api/models/BoardDTO.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Deserialized board record with cover image URL and image count.
|
||||
*/
|
||||
export type BoardDTO = {
|
||||
/**
|
||||
* The unique ID of the board.
|
||||
*/
|
||||
board_id: string;
|
||||
/**
|
||||
* The name of the board.
|
||||
*/
|
||||
board_name: string;
|
||||
/**
|
||||
* The created timestamp of the board.
|
||||
*/
|
||||
created_at: string;
|
||||
/**
|
||||
* The updated timestamp of the board.
|
||||
*/
|
||||
updated_at: string;
|
||||
/**
|
||||
* The deleted timestamp of the board.
|
||||
*/
|
||||
deleted_at?: string;
|
||||
/**
|
||||
* The name of the board's cover image.
|
||||
*/
|
||||
cover_image_name?: string;
|
||||
/**
|
||||
* The number of images in the board.
|
||||
*/
|
||||
image_count: number;
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type Body_create_board_image = {
|
||||
/**
|
||||
* The id of the board to add to
|
||||
*/
|
||||
board_id: string;
|
||||
/**
|
||||
* The name of the image to add
|
||||
*/
|
||||
image_name: string;
|
||||
};
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type Body_remove_board_image = {
|
||||
/**
|
||||
* The id of the board
|
||||
*/
|
||||
board_id: string;
|
||||
/**
|
||||
* The name of the image to remove
|
||||
*/
|
||||
image_name: string;
|
||||
};
|
||||
|
@ -0,0 +1,18 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ControlNetModelFormat } from './ControlNetModelFormat';
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type ControlNetModelConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'controlnet';
|
||||
path: string;
|
||||
description?: string;
|
||||
model_format: ControlNetModelFormat;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* An enumeration.
|
||||
*/
|
||||
export type ControlNetModelFormat = 'checkpoint' | 'diffusers';
|
@ -49,6 +49,7 @@ import type { OpenposeImageProcessorInvocation } from './OpenposeImageProcessorI
|
||||
import type { ParamFloatInvocation } from './ParamFloatInvocation';
|
||||
import type { ParamIntInvocation } from './ParamIntInvocation';
|
||||
import type { PidiImageProcessorInvocation } from './PidiImageProcessorInvocation';
|
||||
import type { PipelineModelLoaderInvocation } from './PipelineModelLoaderInvocation';
|
||||
import type { RandomIntInvocation } from './RandomIntInvocation';
|
||||
import type { RandomRangeInvocation } from './RandomRangeInvocation';
|
||||
import type { RangeInvocation } from './RangeInvocation';
|
||||
@ -56,8 +57,6 @@ import type { RangeOfSizeInvocation } from './RangeOfSizeInvocation';
|
||||
import type { ResizeLatentsInvocation } from './ResizeLatentsInvocation';
|
||||
import type { RestoreFaceInvocation } from './RestoreFaceInvocation';
|
||||
import type { ScaleLatentsInvocation } from './ScaleLatentsInvocation';
|
||||
import type { SD1ModelLoaderInvocation } from './SD1ModelLoaderInvocation';
|
||||
import type { SD2ModelLoaderInvocation } from './SD2ModelLoaderInvocation';
|
||||
import type { ShowImageInvocation } from './ShowImageInvocation';
|
||||
import type { StepParamEasingInvocation } from './StepParamEasingInvocation';
|
||||
import type { SubtractInvocation } from './SubtractInvocation';
|
||||
@ -73,7 +72,7 @@ export type Graph = {
|
||||
/**
|
||||
* The nodes in this graph
|
||||
*/
|
||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | SD1ModelLoaderInvocation | SD2ModelLoaderInvocation | LoraLoaderInvocation | DynamicPromptInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | FloatLinearRangeInvocation | StepParamEasingInvocation | UpscaleInvocation | RestoreFaceInvocation | InpaintInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageProcessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation)>;
|
||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | PipelineModelLoaderInvocation | LoraLoaderInvocation | DynamicPromptInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | FloatLinearRangeInvocation | StepParamEasingInvocation | UpscaleInvocation | RestoreFaceInvocation | InpaintInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageProcessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation)>;
|
||||
/**
|
||||
* The connections between nodes and their fields in this graph
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@ import type { ImageMetadata } from './ImageMetadata';
|
||||
import type { ResourceOrigin } from './ResourceOrigin';
|
||||
|
||||
/**
|
||||
* Deserialized image record, enriched for the frontend with URLs.
|
||||
* Deserialized image record, enriched for the frontend.
|
||||
*/
|
||||
export type ImageDTO = {
|
||||
/**
|
||||
@ -66,5 +66,9 @@ export type ImageDTO = {
|
||||
* A limited subset of the image's generation metadata. Retrieve the image's session for full metadata.
|
||||
*/
|
||||
metadata?: ImageMetadata;
|
||||
/**
|
||||
* The id of the board the image belongs to, if one exists.
|
||||
*/
|
||||
board_id?: string;
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,18 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { LoRAModelFormat } from './LoRAModelFormat';
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type LoRAModelConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'lora';
|
||||
path: string;
|
||||
description?: string;
|
||||
model_format: LoRAModelFormat;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* An enumeration.
|
||||
*/
|
||||
export type LoRAModelFormat = 'lycoris' | 'diffusers';
|
@ -2,16 +2,16 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { invokeai__backend__model_management__models__controlnet__ControlNetModel__Config } from './invokeai__backend__model_management__models__controlnet__ControlNetModel__Config';
|
||||
import type { invokeai__backend__model_management__models__lora__LoRAModel__Config } from './invokeai__backend__model_management__models__lora__LoRAModel__Config';
|
||||
import type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__CheckpointConfig } from './invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__CheckpointConfig';
|
||||
import type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__DiffusersConfig } from './invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__DiffusersConfig';
|
||||
import type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__CheckpointConfig } from './invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__CheckpointConfig';
|
||||
import type { invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__DiffusersConfig } from './invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__DiffusersConfig';
|
||||
import type { invokeai__backend__model_management__models__textual_inversion__TextualInversionModel__Config } from './invokeai__backend__model_management__models__textual_inversion__TextualInversionModel__Config';
|
||||
import type { invokeai__backend__model_management__models__vae__VaeModel__Config } from './invokeai__backend__model_management__models__vae__VaeModel__Config';
|
||||
import type { ControlNetModelConfig } from './ControlNetModelConfig';
|
||||
import type { LoRAModelConfig } from './LoRAModelConfig';
|
||||
import type { StableDiffusion1ModelCheckpointConfig } from './StableDiffusion1ModelCheckpointConfig';
|
||||
import type { StableDiffusion1ModelDiffusersConfig } from './StableDiffusion1ModelDiffusersConfig';
|
||||
import type { StableDiffusion2ModelCheckpointConfig } from './StableDiffusion2ModelCheckpointConfig';
|
||||
import type { StableDiffusion2ModelDiffusersConfig } from './StableDiffusion2ModelDiffusersConfig';
|
||||
import type { TextualInversionModelConfig } from './TextualInversionModelConfig';
|
||||
import type { VaeModelConfig } from './VaeModelConfig';
|
||||
|
||||
export type ModelsList = {
|
||||
models: Record<string, Record<string, Record<string, (invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__DiffusersConfig | invokeai__backend__model_management__models__controlnet__ControlNetModel__Config | invokeai__backend__model_management__models__lora__LoRAModel__Config | invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__DiffusersConfig | invokeai__backend__model_management__models__textual_inversion__TextualInversionModel__Config | invokeai__backend__model_management__models__vae__VaeModel__Config | invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__CheckpointConfig | invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__CheckpointConfig)>>>;
|
||||
models: Array<(StableDiffusion1ModelCheckpointConfig | StableDiffusion1ModelDiffusersConfig | VaeModelConfig | LoRAModelConfig | ControlNetModelConfig | TextualInversionModelConfig | StableDiffusion2ModelCheckpointConfig | StableDiffusion2ModelDiffusersConfig)>;
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,28 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BoardDTO } from './BoardDTO';
|
||||
|
||||
/**
|
||||
* Offset-paginated results
|
||||
*/
|
||||
export type OffsetPaginatedResults_BoardDTO_ = {
|
||||
/**
|
||||
* Items
|
||||
*/
|
||||
items: Array<BoardDTO>;
|
||||
/**
|
||||
* Offset from which to retrieve items
|
||||
*/
|
||||
offset: number;
|
||||
/**
|
||||
* Limit of items to get
|
||||
*/
|
||||
limit: number;
|
||||
/**
|
||||
* Total number of items in result
|
||||
*/
|
||||
total: number;
|
||||
};
|
||||
|
@ -0,0 +1,20 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
|
||||
/**
|
||||
* Pipeline model field
|
||||
*/
|
||||
export type PipelineModelField = {
|
||||
/**
|
||||
* Name of the model
|
||||
*/
|
||||
model_name: string;
|
||||
/**
|
||||
* Base model
|
||||
*/
|
||||
base_model: BaseModelType;
|
||||
};
|
||||
|
@ -2,10 +2,12 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { PipelineModelField } from './PipelineModelField';
|
||||
|
||||
/**
|
||||
* Loading submodels of selected model.
|
||||
* Loads a pipeline model, outputting its submodels.
|
||||
*/
|
||||
export type SD2ModelLoaderInvocation = {
|
||||
export type PipelineModelLoaderInvocation = {
|
||||
/**
|
||||
* The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
@ -14,10 +16,10 @@ export type SD2ModelLoaderInvocation = {
|
||||
* Whether or not this node is an intermediate node.
|
||||
*/
|
||||
is_intermediate?: boolean;
|
||||
type?: 'sd2_model_loader';
|
||||
type?: 'pipeline_model_loader';
|
||||
/**
|
||||
* Model to load
|
||||
* The model to load
|
||||
*/
|
||||
model_name?: string;
|
||||
model: PipelineModelField;
|
||||
};
|
||||
|
@ -1,23 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* Loading submodels of selected model.
|
||||
*/
|
||||
export type SD1ModelLoaderInvocation = {
|
||||
/**
|
||||
* The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Whether or not this node is an intermediate node.
|
||||
*/
|
||||
is_intermediate?: boolean;
|
||||
type?: 'sd1_model_loader';
|
||||
/**
|
||||
* Model to load
|
||||
*/
|
||||
model_name?: string;
|
||||
};
|
||||
|
@ -2,14 +2,17 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ModelError } from './ModelError';
|
||||
import type { ModelVariantType } from './ModelVariantType';
|
||||
|
||||
export type invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__CheckpointConfig = {
|
||||
export type StableDiffusion1ModelCheckpointConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'pipeline';
|
||||
path: string;
|
||||
description?: string;
|
||||
format: 'checkpoint';
|
||||
default?: boolean;
|
||||
model_format: 'checkpoint';
|
||||
error?: ModelError;
|
||||
vae?: string;
|
||||
config?: string;
|
@ -2,14 +2,17 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ModelError } from './ModelError';
|
||||
import type { ModelVariantType } from './ModelVariantType';
|
||||
|
||||
export type invokeai__backend__model_management__models__stable_diffusion__StableDiffusion1Model__DiffusersConfig = {
|
||||
export type StableDiffusion1ModelDiffusersConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'pipeline';
|
||||
path: string;
|
||||
description?: string;
|
||||
format: 'diffusers';
|
||||
default?: boolean;
|
||||
model_format: 'diffusers';
|
||||
error?: ModelError;
|
||||
vae?: string;
|
||||
variant: ModelVariantType;
|
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* An enumeration.
|
||||
*/
|
||||
export type StableDiffusion1ModelFormat = 'checkpoint' | 'diffusers';
|
@ -2,15 +2,18 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ModelError } from './ModelError';
|
||||
import type { ModelVariantType } from './ModelVariantType';
|
||||
import type { SchedulerPredictionType } from './SchedulerPredictionType';
|
||||
|
||||
export type invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__CheckpointConfig = {
|
||||
export type StableDiffusion2ModelCheckpointConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'pipeline';
|
||||
path: string;
|
||||
description?: string;
|
||||
format: 'checkpoint';
|
||||
default?: boolean;
|
||||
model_format: 'checkpoint';
|
||||
error?: ModelError;
|
||||
vae?: string;
|
||||
config?: string;
|
@ -2,15 +2,18 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ModelError } from './ModelError';
|
||||
import type { ModelVariantType } from './ModelVariantType';
|
||||
import type { SchedulerPredictionType } from './SchedulerPredictionType';
|
||||
|
||||
export type invokeai__backend__model_management__models__stable_diffusion__StableDiffusion2Model__DiffusersConfig = {
|
||||
export type StableDiffusion2ModelDiffusersConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'pipeline';
|
||||
path: string;
|
||||
description?: string;
|
||||
format: 'diffusers';
|
||||
default?: boolean;
|
||||
model_format: 'diffusers';
|
||||
error?: ModelError;
|
||||
vae?: string;
|
||||
variant: ModelVariantType;
|
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* An enumeration.
|
||||
*/
|
||||
export type StableDiffusion2ModelFormat = 'checkpoint' | 'diffusers';
|
@ -0,0 +1,17 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type TextualInversionModelConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'embedding';
|
||||
path: string;
|
||||
description?: string;
|
||||
model_format: null;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -0,0 +1,18 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { BaseModelType } from './BaseModelType';
|
||||
import type { ModelError } from './ModelError';
|
||||
import type { VaeModelFormat } from './VaeModelFormat';
|
||||
|
||||
export type VaeModelConfig = {
|
||||
name: string;
|
||||
base_model: BaseModelType;
|
||||
type: 'vae';
|
||||
path: string;
|
||||
description?: string;
|
||||
model_format: VaeModelFormat;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/**
|
||||
* An enumeration.
|
||||
*/
|
||||
export type VaeModelFormat = 'checkpoint' | 'diffusers';
|
@ -1,14 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type invokeai__backend__model_management__models__controlnet__ControlNetModel__Config = {
|
||||
path: string;
|
||||
description?: string;
|
||||
format: ('checkpoint' | 'diffusers');
|
||||
default?: boolean;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -1,14 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type invokeai__backend__model_management__models__lora__LoRAModel__Config = {
|
||||
path: string;
|
||||
description?: string;
|
||||
format: ('lycoris' | 'diffusers');
|
||||
default?: boolean;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -1,14 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type invokeai__backend__model_management__models__textual_inversion__TextualInversionModel__Config = {
|
||||
path: string;
|
||||
description?: string;
|
||||
format: null;
|
||||
default?: boolean;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
@ -1,14 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ModelError } from './ModelError';
|
||||
|
||||
export type invokeai__backend__model_management__models__vae__VaeModel__Config = {
|
||||
path: string;
|
||||
description?: string;
|
||||
format: ('checkpoint' | 'diffusers');
|
||||
default?: boolean;
|
||||
error?: ModelError;
|
||||
};
|
||||
|
247
invokeai/frontend/web/src/services/api/services/BoardsService.ts
Normal file
247
invokeai/frontend/web/src/services/api/services/BoardsService.ts
Normal file
@ -0,0 +1,247 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { BoardChanges } from '../models/BoardChanges';
|
||||
import type { BoardDTO } from '../models/BoardDTO';
|
||||
import type { Body_create_board_image } from '../models/Body_create_board_image';
|
||||
import type { Body_remove_board_image } from '../models/Body_remove_board_image';
|
||||
import type { OffsetPaginatedResults_BoardDTO_ } from '../models/OffsetPaginatedResults_BoardDTO_';
|
||||
import type { OffsetPaginatedResults_ImageDTO_ } from '../models/OffsetPaginatedResults_ImageDTO_';
|
||||
|
||||
import type { CancelablePromise } from '../core/CancelablePromise';
|
||||
import { OpenAPI } from '../core/OpenAPI';
|
||||
import { request as __request } from '../core/request';
|
||||
|
||||
export class BoardsService {
|
||||
|
||||
/**
|
||||
* List Boards
|
||||
* Gets a list of boards
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static listBoards({
|
||||
all,
|
||||
offset,
|
||||
limit,
|
||||
}: {
|
||||
/**
|
||||
* Whether to list all boards
|
||||
*/
|
||||
all?: boolean,
|
||||
/**
|
||||
* The page offset
|
||||
*/
|
||||
offset?: number,
|
||||
/**
|
||||
* The number of boards per page
|
||||
*/
|
||||
limit?: number,
|
||||
}): CancelablePromise<(OffsetPaginatedResults_BoardDTO_ | Array<BoardDTO>)> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/api/v1/boards/',
|
||||
query: {
|
||||
'all': all,
|
||||
'offset': offset,
|
||||
'limit': limit,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Board
|
||||
* Creates a board
|
||||
* @returns BoardDTO The board was created successfully
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createBoard({
|
||||
boardName,
|
||||
}: {
|
||||
/**
|
||||
* The name of the board to create
|
||||
*/
|
||||
boardName: string,
|
||||
}): CancelablePromise<BoardDTO> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/api/v1/boards/',
|
||||
query: {
|
||||
'board_name': boardName,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Board
|
||||
* Gets a board
|
||||
* @returns BoardDTO Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getBoard({
|
||||
boardId,
|
||||
}: {
|
||||
/**
|
||||
* The id of board to get
|
||||
*/
|
||||
boardId: string,
|
||||
}): CancelablePromise<BoardDTO> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/api/v1/boards/{board_id}',
|
||||
path: {
|
||||
'board_id': boardId,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Board
|
||||
* Deletes a board
|
||||
* @returns any Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteBoard({
|
||||
boardId,
|
||||
}: {
|
||||
/**
|
||||
* The id of board to delete
|
||||
*/
|
||||
boardId: string,
|
||||
}): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'DELETE',
|
||||
url: '/api/v1/boards/{board_id}',
|
||||
path: {
|
||||
'board_id': boardId,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Board
|
||||
* Updates a board
|
||||
* @returns BoardDTO The board was updated successfully
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateBoard({
|
||||
boardId,
|
||||
requestBody,
|
||||
}: {
|
||||
/**
|
||||
* The id of board to update
|
||||
*/
|
||||
boardId: string,
|
||||
requestBody: BoardChanges,
|
||||
}): CancelablePromise<BoardDTO> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PATCH',
|
||||
url: '/api/v1/boards/{board_id}',
|
||||
path: {
|
||||
'board_id': boardId,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Board Image
|
||||
* Creates a board_image
|
||||
* @returns any The image was added to a board successfully
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static createBoardImage({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: Body_create_board_image,
|
||||
}): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/api/v1/board_images/',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Board Image
|
||||
* Deletes a board_image
|
||||
* @returns any The image was removed from the board successfully
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static removeBoardImage({
|
||||
requestBody,
|
||||
}: {
|
||||
requestBody: Body_remove_board_image,
|
||||
}): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'DELETE',
|
||||
url: '/api/v1/board_images/',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* List Board Images
|
||||
* Gets a list of images for a board
|
||||
* @returns OffsetPaginatedResults_ImageDTO_ Successful Response
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static listBoardImages({
|
||||
boardId,
|
||||
offset,
|
||||
limit = 10,
|
||||
}: {
|
||||
/**
|
||||
* The id of the board
|
||||
*/
|
||||
boardId: string,
|
||||
/**
|
||||
* The page offset
|
||||
*/
|
||||
offset?: number,
|
||||
/**
|
||||
* The number of boards per page
|
||||
*/
|
||||
limit?: number,
|
||||
}): CancelablePromise<OffsetPaginatedResults_ImageDTO_> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/api/v1/board_images/{board_id}',
|
||||
path: {
|
||||
'board_id': boardId,
|
||||
},
|
||||
query: {
|
||||
'offset': offset,
|
||||
'limit': limit,
|
||||
},
|
||||
errors: {
|
||||
422: `Validation Error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -25,6 +25,7 @@ export class ImagesService {
|
||||
imageOrigin,
|
||||
categories,
|
||||
isIntermediate,
|
||||
boardId,
|
||||
offset,
|
||||
limit = 10,
|
||||
}: {
|
||||
@ -40,6 +41,10 @@ export class ImagesService {
|
||||
* Whether to list intermediate images
|
||||
*/
|
||||
isIntermediate?: boolean,
|
||||
/**
|
||||
* The board id to filter by
|
||||
*/
|
||||
boardId?: string,
|
||||
/**
|
||||
* The page offset
|
||||
*/
|
||||
@ -56,6 +61,7 @@ export class ImagesService {
|
||||
'image_origin': imageOrigin,
|
||||
'categories': categories,
|
||||
'is_intermediate': isIntermediate,
|
||||
'board_id': boardId,
|
||||
'offset': offset,
|
||||
'limit': limit,
|
||||
},
|
||||
|
@ -51,6 +51,7 @@ import type { PaginatedResults_GraphExecutionState_ } from '../models/PaginatedR
|
||||
import type { ParamFloatInvocation } from '../models/ParamFloatInvocation';
|
||||
import type { ParamIntInvocation } from '../models/ParamIntInvocation';
|
||||
import type { PidiImageProcessorInvocation } from '../models/PidiImageProcessorInvocation';
|
||||
import type { PipelineModelLoaderInvocation } from '../models/PipelineModelLoaderInvocation';
|
||||
import type { RandomIntInvocation } from '../models/RandomIntInvocation';
|
||||
import type { RandomRangeInvocation } from '../models/RandomRangeInvocation';
|
||||
import type { RangeInvocation } from '../models/RangeInvocation';
|
||||
@ -58,8 +59,6 @@ import type { RangeOfSizeInvocation } from '../models/RangeOfSizeInvocation';
|
||||
import type { ResizeLatentsInvocation } from '../models/ResizeLatentsInvocation';
|
||||
import type { RestoreFaceInvocation } from '../models/RestoreFaceInvocation';
|
||||
import type { ScaleLatentsInvocation } from '../models/ScaleLatentsInvocation';
|
||||
import type { SD1ModelLoaderInvocation } from '../models/SD1ModelLoaderInvocation';
|
||||
import type { SD2ModelLoaderInvocation } from '../models/SD2ModelLoaderInvocation';
|
||||
import type { ShowImageInvocation } from '../models/ShowImageInvocation';
|
||||
import type { StepParamEasingInvocation } from '../models/StepParamEasingInvocation';
|
||||
import type { SubtractInvocation } from '../models/SubtractInvocation';
|
||||
@ -175,7 +174,7 @@ export class SessionsService {
|
||||
* The id of the session
|
||||
*/
|
||||
sessionId: string,
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | SD1ModelLoaderInvocation | SD2ModelLoaderInvocation | LoraLoaderInvocation | DynamicPromptInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | FloatLinearRangeInvocation | StepParamEasingInvocation | UpscaleInvocation | RestoreFaceInvocation | InpaintInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageProcessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation),
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | PipelineModelLoaderInvocation | LoraLoaderInvocation | DynamicPromptInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | FloatLinearRangeInvocation | StepParamEasingInvocation | UpscaleInvocation | RestoreFaceInvocation | InpaintInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageProcessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation),
|
||||
}): CancelablePromise<string> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
@ -212,7 +211,7 @@ export class SessionsService {
|
||||
* The path to the node in the graph
|
||||
*/
|
||||
nodePath: string,
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | SD1ModelLoaderInvocation | SD2ModelLoaderInvocation | LoraLoaderInvocation | DynamicPromptInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | FloatLinearRangeInvocation | StepParamEasingInvocation | UpscaleInvocation | RestoreFaceInvocation | InpaintInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageProcessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation),
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | ImageCropInvocation | ImagePasteInvocation | MaskFromAlphaInvocation | ImageMultiplyInvocation | ImageChannelInvocation | ImageConvertInvocation | ImageBlurInvocation | ImageResizeInvocation | ImageScaleInvocation | ImageLerpInvocation | ImageInverseLerpInvocation | ControlNetInvocation | ImageProcessorInvocation | PipelineModelLoaderInvocation | LoraLoaderInvocation | DynamicPromptInvocation | CompelInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | RandomIntInvocation | ParamIntInvocation | ParamFloatInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | CvInpaintInvocation | RangeInvocation | RangeOfSizeInvocation | RandomRangeInvocation | FloatLinearRangeInvocation | StepParamEasingInvocation | UpscaleInvocation | RestoreFaceInvocation | InpaintInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | CannyImageProcessorInvocation | HedImageProcessorInvocation | LineartImageProcessorInvocation | LineartAnimeImageProcessorInvocation | OpenposeImageProcessorInvocation | MidasDepthImageProcessorInvocation | NormalbaeImageProcessorInvocation | MlsdImageProcessorInvocation | PidiImageProcessorInvocation | ContentShuffleImageProcessorInvocation | ZoeDepthImageProcessorInvocation | MediapipeFaceProcessorInvocation | LatentsToLatentsInvocation),
|
||||
}): CancelablePromise<GraphExecutionState> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user