mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into enhance/invokeai-logs
This commit is contained in:
commit
4687ad4ed6
@ -46,8 +46,8 @@ class TextToImageInvocation(BaseInvocation, SDImageInvocation):
|
||||
prompt: Optional[str] = Field(description="The prompt to generate an image from")
|
||||
seed: int = Field(default=-1,ge=-1, le=np.iinfo(np.uint32).max, description="The seed to use (-1 for a random seed)", )
|
||||
steps: int = Field(default=10, gt=0, description="The number of steps to use to generate the image")
|
||||
width: int = Field(default=512, multiple_of=64, gt=0, description="The width of the resulting image", )
|
||||
height: int = Field(default=512, multiple_of=64, gt=0, description="The height of the resulting image", )
|
||||
width: int = Field(default=512, multiple_of=8, gt=0, description="The width of the resulting image", )
|
||||
height: int = Field(default=512, multiple_of=8, gt=0, description="The height of the resulting image", )
|
||||
cfg_scale: float = Field(default=7.5, gt=0, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", )
|
||||
scheduler: SAMPLER_NAME_VALUES = Field(default="k_lms", description="The scheduler to use" )
|
||||
seamless: bool = Field(default=False, description="Whether or not to generate an image that can tile without seams", )
|
||||
@ -150,6 +150,9 @@ class ImageToImageInvocation(TextToImageInvocation):
|
||||
)
|
||||
mask = None
|
||||
|
||||
if self.fit:
|
||||
image = image.resize((self.width, self.height))
|
||||
|
||||
# Handle invalid model parameter
|
||||
model = choose_model(context.services.model_manager, self.model)
|
||||
|
||||
|
@ -113,8 +113,8 @@ class NoiseInvocation(BaseInvocation):
|
||||
|
||||
# Inputs
|
||||
seed: int = Field(ge=0, le=np.iinfo(np.uint32).max, description="The seed to use", default_factory=random_seed)
|
||||
width: int = Field(default=512, multiple_of=64, gt=0, description="The width of the resulting noise", )
|
||||
height: int = Field(default=512, multiple_of=64, gt=0, description="The height of the resulting noise", )
|
||||
width: int = Field(default=512, multiple_of=8, gt=0, description="The width of the resulting noise", )
|
||||
height: int = Field(default=512, multiple_of=8, gt=0, description="The height of the resulting noise", )
|
||||
|
||||
|
||||
# Schema customisation
|
||||
|
@ -23,8 +23,6 @@ def create_text_to_image() -> LibraryGraph:
|
||||
edges=[
|
||||
Edge(source=EdgeConnection(node_id='width', field='a'), destination=EdgeConnection(node_id='3', field='width')),
|
||||
Edge(source=EdgeConnection(node_id='height', field='a'), destination=EdgeConnection(node_id='3', field='height')),
|
||||
Edge(source=EdgeConnection(node_id='width', field='a'), destination=EdgeConnection(node_id='4', field='width')),
|
||||
Edge(source=EdgeConnection(node_id='height', field='a'), destination=EdgeConnection(node_id='4', field='height')),
|
||||
Edge(source=EdgeConnection(node_id='3', field='noise'), destination=EdgeConnection(node_id='4', field='noise')),
|
||||
Edge(source=EdgeConnection(node_id='4', field='latents'), destination=EdgeConnection(node_id='5', field='latents')),
|
||||
]
|
||||
|
@ -49,7 +49,7 @@ class Invoker:
|
||||
new_state = GraphExecutionState(graph=Graph() if graph is None else graph)
|
||||
self.services.graph_execution_manager.set(new_state)
|
||||
return new_state
|
||||
|
||||
|
||||
def cancel(self, graph_execution_state_id: str) -> None:
|
||||
"""Cancels the given execution state"""
|
||||
self.services.queue.cancel(graph_execution_state_id)
|
||||
@ -71,18 +71,12 @@ class Invoker:
|
||||
for service in vars(self.services):
|
||||
self.__start_service(getattr(self.services, service))
|
||||
|
||||
for service in vars(self.services):
|
||||
self.__start_service(getattr(self.services, service))
|
||||
|
||||
def stop(self) -> None:
|
||||
"""Stops the invoker. A new invoker will have to be created to execute further."""
|
||||
# First stop all services
|
||||
for service in vars(self.services):
|
||||
self.__stop_service(getattr(self.services, service))
|
||||
|
||||
for service in vars(self.services):
|
||||
self.__stop_service(getattr(self.services, service))
|
||||
|
||||
self.services.queue.put(None)
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import traceback
|
||||
from threading import Event, Thread
|
||||
from threading import Event, Thread, BoundedSemaphore
|
||||
|
||||
from ..invocations.baseinvocation import InvocationContext
|
||||
from .invocation_queue import InvocationQueueItem
|
||||
@ -10,8 +10,11 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
||||
__invoker_thread: Thread
|
||||
__stop_event: Event
|
||||
__invoker: Invoker
|
||||
__threadLimit: BoundedSemaphore
|
||||
|
||||
def start(self, invoker) -> None:
|
||||
# if we do want multithreading at some point, we could make this configurable
|
||||
self.__threadLimit = BoundedSemaphore(1)
|
||||
self.__invoker = invoker
|
||||
self.__stop_event = Event()
|
||||
self.__invoker_thread = Thread(
|
||||
@ -20,7 +23,7 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
||||
kwargs=dict(stop_event=self.__stop_event),
|
||||
)
|
||||
self.__invoker_thread.daemon = (
|
||||
True # TODO: probably better to just not use threads?
|
||||
True # TODO: make async and do not use threads
|
||||
)
|
||||
self.__invoker_thread.start()
|
||||
|
||||
@ -29,6 +32,7 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
||||
|
||||
def __process(self, stop_event: Event):
|
||||
try:
|
||||
self.__threadLimit.acquire()
|
||||
while not stop_event.is_set():
|
||||
queue_item: InvocationQueueItem = self.__invoker.services.queue.get()
|
||||
if not queue_item: # Probably stopping
|
||||
@ -110,7 +114,7 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
||||
)
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# Check queue to see if this is canceled, and skip if so
|
||||
if self.__invoker.services.queue.is_canceled(
|
||||
graph_execution_state.id
|
||||
@ -127,4 +131,6 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
... # Log something?
|
||||
pass # Log something? KeyboardInterrupt is probably not going to be seen by the processor
|
||||
finally:
|
||||
self.__threadLimit.release()
|
||||
|
@ -76,6 +76,8 @@
|
||||
"i18next-http-backend": "^2.2.0",
|
||||
"konva": "^9.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"overlayscrollbars": "^2.1.1",
|
||||
"overlayscrollbars-react": "^0.5.0",
|
||||
"patch-package": "^7.0.0",
|
||||
"re-resizable": "^6.9.9",
|
||||
"react": "^18.2.0",
|
||||
@ -88,13 +90,17 @@
|
||||
"react-konva": "^18.2.7",
|
||||
"react-konva-utils": "^1.0.4",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-rnd": "^10.4.1",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-use": "^17.4.0",
|
||||
"react-virtuoso": "^4.3.5",
|
||||
"react-zoom-pan-pinch": "^3.0.7",
|
||||
"reactflow": "^11.7.0",
|
||||
"redux-deep-persist": "^1.0.7",
|
||||
"redux-dynamic-middlewares": "^2.2.0",
|
||||
"redux-persist": "^6.0.0",
|
||||
"roarr": "^7.15.0",
|
||||
"serialize-error": "^11.0.0",
|
||||
"socket.io-client": "^4.6.0",
|
||||
"use-image": "^1.1.0",
|
||||
"uuid": "^9.0.0"
|
||||
|
@ -527,6 +527,7 @@
|
||||
"useCanvasBeta": "Use Canvas Beta Layout",
|
||||
"enableImageDebugging": "Enable Image Debugging",
|
||||
"useSlidersForAll": "Use Sliders For All Options",
|
||||
"autoShowProgress": "Auto Show Progress Images",
|
||||
"resetWebUI": "Reset Web UI",
|
||||
"resetWebUIDesc1": "Resetting the web UI only resets the browser's local cache of your images and remembered settings. It does not delete any images from disk.",
|
||||
"resetWebUIDesc2": "If images aren't showing up in the gallery or something else isn't working, please try resetting before submitting an issue on GitHub.",
|
||||
@ -645,5 +646,9 @@
|
||||
"betaDarkenOutside": "Darken Outside",
|
||||
"betaLimitToBox": "Limit To Box",
|
||||
"betaPreserveMasked": "Preserve Masked"
|
||||
},
|
||||
"ui": {
|
||||
"showProgressImages": "Show Progress Images",
|
||||
"hideProgressImages": "Hide Progress Images"
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys';
|
||||
import { configChanged } from 'features/system/store/configSlice';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { useLogger } from 'app/logging/useLogger';
|
||||
import ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
|
||||
|
||||
const DEFAULT_CONFIG = {};
|
||||
|
||||
@ -64,7 +65,7 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Grid w="100vw" h="100vh" position="relative">
|
||||
<Grid w="100vw" h="100vh" position="relative" overflow="hidden">
|
||||
{isLightboxEnabled && <Lightbox />}
|
||||
<ImageUploader>
|
||||
<ProgressBar />
|
||||
@ -120,6 +121,7 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => {
|
||||
<Portal>
|
||||
<FloatingGalleryButton />
|
||||
</Portal>
|
||||
<ProgressImagePreview />
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { lazy, memo, PropsWithChildren, useEffect } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { buildMiddleware, store } from 'app/store/store';
|
||||
import { store } from 'app/store/store';
|
||||
import { persistor } from '../store/persistor';
|
||||
import { OpenAPI } from 'services/api';
|
||||
import '@fontsource/inter/100.css';
|
||||
@ -19,6 +19,7 @@ import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
|
||||
import { PartialAppConfig } from 'app/types/invokeai';
|
||||
|
||||
import '../../i18n';
|
||||
import { socketMiddleware } from 'services/events/middleware';
|
||||
|
||||
const App = lazy(() => import('./App'));
|
||||
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||
@ -50,7 +51,7 @@ const InvokeAIUI = ({ apiUrl, token, config, children }: Props) => {
|
||||
// the `apiUrl`/`token` dynamically.
|
||||
|
||||
// rebuild socket middleware with token and apiUrl
|
||||
addMiddleware(buildMiddleware());
|
||||
addMiddleware(socketMiddleware());
|
||||
}, [apiUrl, token]);
|
||||
|
||||
return (
|
||||
|
@ -18,6 +18,8 @@ import '@fontsource/inter/600.css';
|
||||
import '@fontsource/inter/700.css';
|
||||
import '@fontsource/inter/800.css';
|
||||
import '@fontsource/inter/900.css';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import 'theme/css/overlayscrollbars.css';
|
||||
|
||||
type ThemeLocaleProviderProps = {
|
||||
children: ReactNode;
|
||||
|
@ -1,7 +1,5 @@
|
||||
// TODO: use Enums?
|
||||
|
||||
import { InProgressImageType } from 'features/system/store/systemSlice';
|
||||
|
||||
export const DIFFUSERS_SCHEDULERS: Array<string> = [
|
||||
'ddim',
|
||||
'plms',
|
||||
@ -33,17 +31,8 @@ export const UPSCALING_LEVELS: Array<{ key: string; value: number }> = [
|
||||
|
||||
export const NUMPY_RAND_MIN = 0;
|
||||
|
||||
export const NUMPY_RAND_MAX = 4294967295;
|
||||
export const NUMPY_RAND_MAX = 2147483647;
|
||||
|
||||
export const FACETOOL_TYPES = ['gfpgan', 'codeformer'] as const;
|
||||
|
||||
export const IN_PROGRESS_IMAGE_TYPES: Array<{
|
||||
key: string;
|
||||
value: InProgressImageType;
|
||||
}> = [
|
||||
{ key: 'None', value: 'none' },
|
||||
{ key: 'Fast', value: 'latents' },
|
||||
{ key: 'Accurate', value: 'full-res' },
|
||||
];
|
||||
|
||||
export const NODE_MIN_WIDTH = 250;
|
||||
|
@ -1,65 +1,67 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
// import { createAction } from '@reduxjs/toolkit';
|
||||
// import * as InvokeAI from 'app/types/invokeai';
|
||||
// import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
||||
// import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
|
||||
/**
|
||||
* We can't use redux-toolkit's createSlice() to make these actions,
|
||||
* because they have no associated reducer. They only exist to dispatch
|
||||
* requests to the server via socketio. These actions will be handled
|
||||
* by the middleware.
|
||||
*/
|
||||
// /**
|
||||
// * We can't use redux-toolkit's createSlice() to make these actions,
|
||||
// * because they have no associated reducer. They only exist to dispatch
|
||||
// * requests to the server via socketio. These actions will be handled
|
||||
// * by the middleware.
|
||||
// */
|
||||
|
||||
export const generateImage = createAction<InvokeTabName>(
|
||||
'socketio/generateImage'
|
||||
);
|
||||
export const runESRGAN = createAction<InvokeAI._Image>('socketio/runESRGAN');
|
||||
export const runFacetool = createAction<InvokeAI._Image>(
|
||||
'socketio/runFacetool'
|
||||
);
|
||||
export const deleteImage = createAction<InvokeAI._Image>(
|
||||
'socketio/deleteImage'
|
||||
);
|
||||
export const requestImages = createAction<GalleryCategory>(
|
||||
'socketio/requestImages'
|
||||
);
|
||||
export const requestNewImages = createAction<GalleryCategory>(
|
||||
'socketio/requestNewImages'
|
||||
);
|
||||
export const cancelProcessing = createAction<undefined>(
|
||||
'socketio/cancelProcessing'
|
||||
);
|
||||
// export const generateImage = createAction<InvokeTabName>(
|
||||
// 'socketio/generateImage'
|
||||
// );
|
||||
// export const runESRGAN = createAction<InvokeAI._Image>('socketio/runESRGAN');
|
||||
// export const runFacetool = createAction<InvokeAI._Image>(
|
||||
// 'socketio/runFacetool'
|
||||
// );
|
||||
// export const deleteImage = createAction<InvokeAI._Image>(
|
||||
// 'socketio/deleteImage'
|
||||
// );
|
||||
// export const requestImages = createAction<GalleryCategory>(
|
||||
// 'socketio/requestImages'
|
||||
// );
|
||||
// export const requestNewImages = createAction<GalleryCategory>(
|
||||
// 'socketio/requestNewImages'
|
||||
// );
|
||||
// export const cancelProcessing = createAction<undefined>(
|
||||
// 'socketio/cancelProcessing'
|
||||
// );
|
||||
|
||||
export const requestSystemConfig = createAction<undefined>(
|
||||
'socketio/requestSystemConfig'
|
||||
);
|
||||
// export const requestSystemConfig = createAction<undefined>(
|
||||
// 'socketio/requestSystemConfig'
|
||||
// );
|
||||
|
||||
export const searchForModels = createAction<string>('socketio/searchForModels');
|
||||
// export const searchForModels = createAction<string>('socketio/searchForModels');
|
||||
|
||||
export const addNewModel = createAction<
|
||||
InvokeAI.InvokeModelConfigProps | InvokeAI.InvokeDiffusersModelConfigProps
|
||||
>('socketio/addNewModel');
|
||||
// export const addNewModel = createAction<
|
||||
// InvokeAI.InvokeModelConfigProps | InvokeAI.InvokeDiffusersModelConfigProps
|
||||
// >('socketio/addNewModel');
|
||||
|
||||
export const deleteModel = createAction<string>('socketio/deleteModel');
|
||||
// export const deleteModel = createAction<string>('socketio/deleteModel');
|
||||
|
||||
export const convertToDiffusers =
|
||||
createAction<InvokeAI.InvokeModelConversionProps>(
|
||||
'socketio/convertToDiffusers'
|
||||
);
|
||||
// export const convertToDiffusers =
|
||||
// createAction<InvokeAI.InvokeModelConversionProps>(
|
||||
// 'socketio/convertToDiffusers'
|
||||
// );
|
||||
|
||||
export const mergeDiffusersModels =
|
||||
createAction<InvokeAI.InvokeModelMergingProps>(
|
||||
'socketio/mergeDiffusersModels'
|
||||
);
|
||||
// export const mergeDiffusersModels =
|
||||
// createAction<InvokeAI.InvokeModelMergingProps>(
|
||||
// 'socketio/mergeDiffusersModels'
|
||||
// );
|
||||
|
||||
export const requestModelChange = createAction<string>(
|
||||
'socketio/requestModelChange'
|
||||
);
|
||||
// export const requestModelChange = createAction<string>(
|
||||
// 'socketio/requestModelChange'
|
||||
// );
|
||||
|
||||
export const saveStagingAreaImageToGallery = createAction<string>(
|
||||
'socketio/saveStagingAreaImageToGallery'
|
||||
);
|
||||
// export const saveStagingAreaImageToGallery = createAction<string>(
|
||||
// 'socketio/saveStagingAreaImageToGallery'
|
||||
// );
|
||||
|
||||
export const emptyTempFolder = createAction<undefined>(
|
||||
'socketio/requestEmptyTempFolder'
|
||||
);
|
||||
// export const emptyTempFolder = createAction<undefined>(
|
||||
// 'socketio/requestEmptyTempFolder'
|
||||
// );
|
||||
|
||||
export default {};
|
||||
|
@ -1,207 +1,209 @@
|
||||
import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import {
|
||||
frontendToBackendParameters,
|
||||
FrontendToBackendParametersConfig,
|
||||
} from 'common/util/parameterTranslation';
|
||||
import dateFormat from 'dateformat';
|
||||
import {
|
||||
GalleryCategory,
|
||||
GalleryState,
|
||||
removeImage,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import {
|
||||
generationRequested,
|
||||
modelChangeRequested,
|
||||
modelConvertRequested,
|
||||
modelMergingRequested,
|
||||
setIsProcessing,
|
||||
} from 'features/system/store/systemSlice';
|
||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
import { Socket } from 'socket.io-client';
|
||||
// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||
// import * as InvokeAI from 'app/types/invokeai';
|
||||
// import type { RootState } from 'app/store/store';
|
||||
// import {
|
||||
// frontendToBackendParameters,
|
||||
// FrontendToBackendParametersConfig,
|
||||
// } from 'common/util/parameterTranslation';
|
||||
// import dateFormat from 'dateformat';
|
||||
// import {
|
||||
// GalleryCategory,
|
||||
// GalleryState,
|
||||
// removeImage,
|
||||
// } from 'features/gallery/store/gallerySlice';
|
||||
// import {
|
||||
// generationRequested,
|
||||
// modelChangeRequested,
|
||||
// modelConvertRequested,
|
||||
// modelMergingRequested,
|
||||
// setIsProcessing,
|
||||
// } from 'features/system/store/systemSlice';
|
||||
// import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
// import { Socket } from 'socket.io-client';
|
||||
|
||||
/**
|
||||
* Returns an object containing all functions which use `socketio.emit()`.
|
||||
* i.e. those which make server requests.
|
||||
*/
|
||||
const makeSocketIOEmitters = (
|
||||
store: MiddlewareAPI<Dispatch<AnyAction>, RootState>,
|
||||
socketio: Socket
|
||||
) => {
|
||||
// We need to dispatch actions to redux and get pieces of state from the store.
|
||||
const { dispatch, getState } = store;
|
||||
// /**
|
||||
// * Returns an object containing all functions which use `socketio.emit()`.
|
||||
// * i.e. those which make server requests.
|
||||
// */
|
||||
// const makeSocketIOEmitters = (
|
||||
// store: MiddlewareAPI<Dispatch<AnyAction>, RootState>,
|
||||
// socketio: Socket
|
||||
// ) => {
|
||||
// // We need to dispatch actions to redux and get pieces of state from the store.
|
||||
// const { dispatch, getState } = store;
|
||||
|
||||
return {
|
||||
emitGenerateImage: (generationMode: InvokeTabName) => {
|
||||
dispatch(setIsProcessing(true));
|
||||
// return {
|
||||
// emitGenerateImage: (generationMode: InvokeTabName) => {
|
||||
// dispatch(setIsProcessing(true));
|
||||
|
||||
const state: RootState = getState();
|
||||
// const state: RootState = getState();
|
||||
|
||||
const {
|
||||
generation: generationState,
|
||||
postprocessing: postprocessingState,
|
||||
system: systemState,
|
||||
canvas: canvasState,
|
||||
} = state;
|
||||
// const {
|
||||
// generation: generationState,
|
||||
// postprocessing: postprocessingState,
|
||||
// system: systemState,
|
||||
// canvas: canvasState,
|
||||
// } = state;
|
||||
|
||||
const frontendToBackendParametersConfig: FrontendToBackendParametersConfig =
|
||||
{
|
||||
generationMode,
|
||||
generationState,
|
||||
postprocessingState,
|
||||
canvasState,
|
||||
systemState,
|
||||
};
|
||||
// const frontendToBackendParametersConfig: FrontendToBackendParametersConfig =
|
||||
// {
|
||||
// generationMode,
|
||||
// generationState,
|
||||
// postprocessingState,
|
||||
// canvasState,
|
||||
// systemState,
|
||||
// };
|
||||
|
||||
dispatch(generationRequested());
|
||||
// dispatch(generationRequested());
|
||||
|
||||
const { generationParameters, esrganParameters, facetoolParameters } =
|
||||
frontendToBackendParameters(frontendToBackendParametersConfig);
|
||||
// const { generationParameters, esrganParameters, facetoolParameters } =
|
||||
// frontendToBackendParameters(frontendToBackendParametersConfig);
|
||||
|
||||
socketio.emit(
|
||||
'generateImage',
|
||||
generationParameters,
|
||||
esrganParameters,
|
||||
facetoolParameters
|
||||
);
|
||||
// socketio.emit(
|
||||
// 'generateImage',
|
||||
// generationParameters,
|
||||
// esrganParameters,
|
||||
// facetoolParameters
|
||||
// );
|
||||
|
||||
// we need to truncate the init_mask base64 else it takes up the whole log
|
||||
// TODO: handle maintaining masks for reproducibility in future
|
||||
if (generationParameters.init_mask) {
|
||||
generationParameters.init_mask = generationParameters.init_mask
|
||||
.substr(0, 64)
|
||||
.concat('...');
|
||||
}
|
||||
if (generationParameters.init_img) {
|
||||
generationParameters.init_img = generationParameters.init_img
|
||||
.substr(0, 64)
|
||||
.concat('...');
|
||||
}
|
||||
// // we need to truncate the init_mask base64 else it takes up the whole log
|
||||
// // TODO: handle maintaining masks for reproducibility in future
|
||||
// if (generationParameters.init_mask) {
|
||||
// generationParameters.init_mask = generationParameters.init_mask
|
||||
// .substr(0, 64)
|
||||
// .concat('...');
|
||||
// }
|
||||
// if (generationParameters.init_img) {
|
||||
// generationParameters.init_img = generationParameters.init_img
|
||||
// .substr(0, 64)
|
||||
// .concat('...');
|
||||
// }
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Image generation requested: ${JSON.stringify({
|
||||
...generationParameters,
|
||||
...esrganParameters,
|
||||
...facetoolParameters,
|
||||
})}`,
|
||||
})
|
||||
);
|
||||
},
|
||||
emitRunESRGAN: (imageToProcess: InvokeAI._Image) => {
|
||||
dispatch(setIsProcessing(true));
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Image generation requested: ${JSON.stringify({
|
||||
// ...generationParameters,
|
||||
// ...esrganParameters,
|
||||
// ...facetoolParameters,
|
||||
// })}`,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// emitRunESRGAN: (imageToProcess: InvokeAI._Image) => {
|
||||
// dispatch(setIsProcessing(true));
|
||||
|
||||
const {
|
||||
postprocessing: {
|
||||
upscalingLevel,
|
||||
upscalingDenoising,
|
||||
upscalingStrength,
|
||||
},
|
||||
} = getState();
|
||||
// const {
|
||||
// postprocessing: {
|
||||
// upscalingLevel,
|
||||
// upscalingDenoising,
|
||||
// upscalingStrength,
|
||||
// },
|
||||
// } = getState();
|
||||
|
||||
const esrganParameters = {
|
||||
upscale: [upscalingLevel, upscalingDenoising, upscalingStrength],
|
||||
};
|
||||
socketio.emit('runPostprocessing', imageToProcess, {
|
||||
type: 'esrgan',
|
||||
...esrganParameters,
|
||||
});
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `ESRGAN upscale requested: ${JSON.stringify({
|
||||
file: imageToProcess.url,
|
||||
...esrganParameters,
|
||||
})}`,
|
||||
})
|
||||
);
|
||||
},
|
||||
emitRunFacetool: (imageToProcess: InvokeAI._Image) => {
|
||||
dispatch(setIsProcessing(true));
|
||||
// const esrganParameters = {
|
||||
// upscale: [upscalingLevel, upscalingDenoising, upscalingStrength],
|
||||
// };
|
||||
// socketio.emit('runPostprocessing', imageToProcess, {
|
||||
// type: 'esrgan',
|
||||
// ...esrganParameters,
|
||||
// });
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `ESRGAN upscale requested: ${JSON.stringify({
|
||||
// file: imageToProcess.url,
|
||||
// ...esrganParameters,
|
||||
// })}`,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// emitRunFacetool: (imageToProcess: InvokeAI._Image) => {
|
||||
// dispatch(setIsProcessing(true));
|
||||
|
||||
const {
|
||||
postprocessing: { facetoolType, facetoolStrength, codeformerFidelity },
|
||||
} = getState();
|
||||
// const {
|
||||
// postprocessing: { facetoolType, facetoolStrength, codeformerFidelity },
|
||||
// } = getState();
|
||||
|
||||
const facetoolParameters: Record<string, unknown> = {
|
||||
facetool_strength: facetoolStrength,
|
||||
};
|
||||
// const facetoolParameters: Record<string, unknown> = {
|
||||
// facetool_strength: facetoolStrength,
|
||||
// };
|
||||
|
||||
if (facetoolType === 'codeformer') {
|
||||
facetoolParameters.codeformer_fidelity = codeformerFidelity;
|
||||
}
|
||||
// if (facetoolType === 'codeformer') {
|
||||
// facetoolParameters.codeformer_fidelity = codeformerFidelity;
|
||||
// }
|
||||
|
||||
socketio.emit('runPostprocessing', imageToProcess, {
|
||||
type: facetoolType,
|
||||
...facetoolParameters,
|
||||
});
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Face restoration (${facetoolType}) requested: ${JSON.stringify(
|
||||
{
|
||||
file: imageToProcess.url,
|
||||
...facetoolParameters,
|
||||
}
|
||||
)}`,
|
||||
})
|
||||
);
|
||||
},
|
||||
emitDeleteImage: (imageToDelete: InvokeAI._Image) => {
|
||||
const { url, uuid, category, thumbnail } = imageToDelete;
|
||||
dispatch(removeImage(imageToDelete));
|
||||
socketio.emit('deleteImage', url, thumbnail, uuid, category);
|
||||
},
|
||||
emitRequestImages: (category: GalleryCategory) => {
|
||||
const gallery: GalleryState = getState().gallery;
|
||||
const { earliest_mtime } = gallery.categories[category];
|
||||
socketio.emit('requestImages', category, earliest_mtime);
|
||||
},
|
||||
emitRequestNewImages: (category: GalleryCategory) => {
|
||||
const gallery: GalleryState = getState().gallery;
|
||||
const { latest_mtime } = gallery.categories[category];
|
||||
socketio.emit('requestLatestImages', category, latest_mtime);
|
||||
},
|
||||
emitCancelProcessing: () => {
|
||||
socketio.emit('cancel');
|
||||
},
|
||||
emitRequestSystemConfig: () => {
|
||||
socketio.emit('requestSystemConfig');
|
||||
},
|
||||
emitSearchForModels: (modelFolder: string) => {
|
||||
socketio.emit('searchForModels', modelFolder);
|
||||
},
|
||||
emitAddNewModel: (modelConfig: InvokeAI.InvokeModelConfigProps) => {
|
||||
socketio.emit('addNewModel', modelConfig);
|
||||
},
|
||||
emitDeleteModel: (modelName: string) => {
|
||||
socketio.emit('deleteModel', modelName);
|
||||
},
|
||||
emitConvertToDiffusers: (
|
||||
modelToConvert: InvokeAI.InvokeModelConversionProps
|
||||
) => {
|
||||
dispatch(modelConvertRequested());
|
||||
socketio.emit('convertToDiffusers', modelToConvert);
|
||||
},
|
||||
emitMergeDiffusersModels: (
|
||||
modelMergeInfo: InvokeAI.InvokeModelMergingProps
|
||||
) => {
|
||||
dispatch(modelMergingRequested());
|
||||
socketio.emit('mergeDiffusersModels', modelMergeInfo);
|
||||
},
|
||||
emitRequestModelChange: (modelName: string) => {
|
||||
dispatch(modelChangeRequested());
|
||||
socketio.emit('requestModelChange', modelName);
|
||||
},
|
||||
emitSaveStagingAreaImageToGallery: (url: string) => {
|
||||
socketio.emit('requestSaveStagingAreaImageToGallery', url);
|
||||
},
|
||||
emitRequestEmptyTempFolder: () => {
|
||||
socketio.emit('requestEmptyTempFolder');
|
||||
},
|
||||
};
|
||||
};
|
||||
// socketio.emit('runPostprocessing', imageToProcess, {
|
||||
// type: facetoolType,
|
||||
// ...facetoolParameters,
|
||||
// });
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Face restoration (${facetoolType}) requested: ${JSON.stringify(
|
||||
// {
|
||||
// file: imageToProcess.url,
|
||||
// ...facetoolParameters,
|
||||
// }
|
||||
// )}`,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// emitDeleteImage: (imageToDelete: InvokeAI._Image) => {
|
||||
// const { url, uuid, category, thumbnail } = imageToDelete;
|
||||
// dispatch(removeImage(imageToDelete));
|
||||
// socketio.emit('deleteImage', url, thumbnail, uuid, category);
|
||||
// },
|
||||
// emitRequestImages: (category: GalleryCategory) => {
|
||||
// const gallery: GalleryState = getState().gallery;
|
||||
// const { earliest_mtime } = gallery.categories[category];
|
||||
// socketio.emit('requestImages', category, earliest_mtime);
|
||||
// },
|
||||
// emitRequestNewImages: (category: GalleryCategory) => {
|
||||
// const gallery: GalleryState = getState().gallery;
|
||||
// const { latest_mtime } = gallery.categories[category];
|
||||
// socketio.emit('requestLatestImages', category, latest_mtime);
|
||||
// },
|
||||
// emitCancelProcessing: () => {
|
||||
// socketio.emit('cancel');
|
||||
// },
|
||||
// emitRequestSystemConfig: () => {
|
||||
// socketio.emit('requestSystemConfig');
|
||||
// },
|
||||
// emitSearchForModels: (modelFolder: string) => {
|
||||
// socketio.emit('searchForModels', modelFolder);
|
||||
// },
|
||||
// emitAddNewModel: (modelConfig: InvokeAI.InvokeModelConfigProps) => {
|
||||
// socketio.emit('addNewModel', modelConfig);
|
||||
// },
|
||||
// emitDeleteModel: (modelName: string) => {
|
||||
// socketio.emit('deleteModel', modelName);
|
||||
// },
|
||||
// emitConvertToDiffusers: (
|
||||
// modelToConvert: InvokeAI.InvokeModelConversionProps
|
||||
// ) => {
|
||||
// dispatch(modelConvertRequested());
|
||||
// socketio.emit('convertToDiffusers', modelToConvert);
|
||||
// },
|
||||
// emitMergeDiffusersModels: (
|
||||
// modelMergeInfo: InvokeAI.InvokeModelMergingProps
|
||||
// ) => {
|
||||
// dispatch(modelMergingRequested());
|
||||
// socketio.emit('mergeDiffusersModels', modelMergeInfo);
|
||||
// },
|
||||
// emitRequestModelChange: (modelName: string) => {
|
||||
// dispatch(modelChangeRequested());
|
||||
// socketio.emit('requestModelChange', modelName);
|
||||
// },
|
||||
// emitSaveStagingAreaImageToGallery: (url: string) => {
|
||||
// socketio.emit('requestSaveStagingAreaImageToGallery', url);
|
||||
// },
|
||||
// emitRequestEmptyTempFolder: () => {
|
||||
// socketio.emit('requestEmptyTempFolder');
|
||||
// },
|
||||
// };
|
||||
// };
|
||||
|
||||
export default makeSocketIOEmitters;
|
||||
// export default makeSocketIOEmitters;
|
||||
|
||||
export default {};
|
||||
|
@ -1,500 +1,502 @@
|
||||
import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||
import dateFormat from 'dateformat';
|
||||
import i18n from 'i18n';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||
// import dateFormat from 'dateformat';
|
||||
// import i18n from 'i18n';
|
||||
// import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
// import * as InvokeAI from 'app/types/invokeai';
|
||||
|
||||
import {
|
||||
addToast,
|
||||
errorOccurred,
|
||||
processingCanceled,
|
||||
setCurrentStatus,
|
||||
setFoundModels,
|
||||
setIsCancelable,
|
||||
setIsConnected,
|
||||
setIsProcessing,
|
||||
setModelList,
|
||||
setSearchFolder,
|
||||
setSystemConfig,
|
||||
setSystemStatus,
|
||||
} from 'features/system/store/systemSlice';
|
||||
// import {
|
||||
// addToast,
|
||||
// errorOccurred,
|
||||
// processingCanceled,
|
||||
// setCurrentStatus,
|
||||
// setFoundModels,
|
||||
// setIsCancelable,
|
||||
// setIsConnected,
|
||||
// setIsProcessing,
|
||||
// setModelList,
|
||||
// setSearchFolder,
|
||||
// setSystemConfig,
|
||||
// setSystemStatus,
|
||||
// } from 'features/system/store/systemSlice';
|
||||
|
||||
import {
|
||||
addGalleryImages,
|
||||
addImage,
|
||||
clearIntermediateImage,
|
||||
GalleryState,
|
||||
removeImage,
|
||||
setIntermediateImage,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
// import {
|
||||
// addGalleryImages,
|
||||
// addImage,
|
||||
// clearIntermediateImage,
|
||||
// GalleryState,
|
||||
// removeImage,
|
||||
// setIntermediateImage,
|
||||
// } from 'features/gallery/store/gallerySlice';
|
||||
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
clearInitialImage,
|
||||
initialImageSelected,
|
||||
setInfillMethod,
|
||||
// setInitialImage,
|
||||
setMaskPath,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { tabMap } from 'features/ui/store/tabMap';
|
||||
import {
|
||||
requestImages,
|
||||
requestNewImages,
|
||||
requestSystemConfig,
|
||||
} from './actions';
|
||||
// import type { RootState } from 'app/store/store';
|
||||
// import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
|
||||
// import {
|
||||
// clearInitialImage,
|
||||
// initialImageSelected,
|
||||
// setInfillMethod,
|
||||
// // setInitialImage,
|
||||
// setMaskPath,
|
||||
// } from 'features/parameters/store/generationSlice';
|
||||
// import { tabMap } from 'features/ui/store/tabMap';
|
||||
// import {
|
||||
// requestImages,
|
||||
// requestNewImages,
|
||||
// requestSystemConfig,
|
||||
// } from './actions';
|
||||
|
||||
/**
|
||||
* Returns an object containing listener callbacks for socketio events.
|
||||
* TODO: This file is large, but simple. Should it be split up further?
|
||||
*/
|
||||
const makeSocketIOListeners = (
|
||||
store: MiddlewareAPI<Dispatch<AnyAction>, RootState>
|
||||
) => {
|
||||
const { dispatch, getState } = store;
|
||||
// /**
|
||||
// * Returns an object containing listener callbacks for socketio events.
|
||||
// * TODO: This file is large, but simple. Should it be split up further?
|
||||
// */
|
||||
// const makeSocketIOListeners = (
|
||||
// store: MiddlewareAPI<Dispatch<AnyAction>, RootState>
|
||||
// ) => {
|
||||
// const { dispatch, getState } = store;
|
||||
|
||||
return {
|
||||
/**
|
||||
* Callback to run when we receive a 'connect' event.
|
||||
*/
|
||||
onConnect: () => {
|
||||
try {
|
||||
dispatch(setIsConnected(true));
|
||||
dispatch(setCurrentStatus(i18n.t('common.statusConnected')));
|
||||
dispatch(requestSystemConfig());
|
||||
const gallery: GalleryState = getState().gallery;
|
||||
// return {
|
||||
// /**
|
||||
// * Callback to run when we receive a 'connect' event.
|
||||
// */
|
||||
// onConnect: () => {
|
||||
// try {
|
||||
// dispatch(setIsConnected(true));
|
||||
// dispatch(setCurrentStatus(i18n.t('common.statusConnected')));
|
||||
// dispatch(requestSystemConfig());
|
||||
// const gallery: GalleryState = getState().gallery;
|
||||
|
||||
if (gallery.categories.result.latest_mtime) {
|
||||
dispatch(requestNewImages('result'));
|
||||
} else {
|
||||
dispatch(requestImages('result'));
|
||||
}
|
||||
// if (gallery.categories.result.latest_mtime) {
|
||||
// dispatch(requestNewImages('result'));
|
||||
// } else {
|
||||
// dispatch(requestImages('result'));
|
||||
// }
|
||||
|
||||
if (gallery.categories.user.latest_mtime) {
|
||||
dispatch(requestNewImages('user'));
|
||||
} else {
|
||||
dispatch(requestImages('user'));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'disconnect' event.
|
||||
*/
|
||||
onDisconnect: () => {
|
||||
try {
|
||||
dispatch(setIsConnected(false));
|
||||
dispatch(setCurrentStatus(i18n.t('common.statusDisconnected')));
|
||||
// if (gallery.categories.user.latest_mtime) {
|
||||
// dispatch(requestNewImages('user'));
|
||||
// } else {
|
||||
// dispatch(requestImages('user'));
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'disconnect' event.
|
||||
// */
|
||||
// onDisconnect: () => {
|
||||
// try {
|
||||
// dispatch(setIsConnected(false));
|
||||
// dispatch(setCurrentStatus(i18n.t('common.statusDisconnected')));
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Disconnected from server`,
|
||||
level: 'warning',
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'generationResult' event.
|
||||
*/
|
||||
onGenerationResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
try {
|
||||
const state = getState();
|
||||
const { activeTab } = state.ui;
|
||||
const { shouldLoopback } = state.postprocessing;
|
||||
const { boundingBox: _, generationMode, ...rest } = data;
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Disconnected from server`,
|
||||
// level: 'warning',
|
||||
// })
|
||||
// );
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'generationResult' event.
|
||||
// */
|
||||
// onGenerationResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
// try {
|
||||
// const state = getState();
|
||||
// const { activeTab } = state.ui;
|
||||
// const { shouldLoopback } = state.postprocessing;
|
||||
// const { boundingBox: _, generationMode, ...rest } = data;
|
||||
|
||||
const newImage = {
|
||||
uuid: uuidv4(),
|
||||
...rest,
|
||||
};
|
||||
// const newImage = {
|
||||
// uuid: uuidv4(),
|
||||
// ...rest,
|
||||
// };
|
||||
|
||||
if (['txt2img', 'img2img'].includes(generationMode)) {
|
||||
dispatch(
|
||||
addImage({
|
||||
category: 'result',
|
||||
image: { ...newImage, category: 'result' },
|
||||
})
|
||||
);
|
||||
}
|
||||
// if (['txt2img', 'img2img'].includes(generationMode)) {
|
||||
// dispatch(
|
||||
// addImage({
|
||||
// category: 'result',
|
||||
// image: { ...newImage, category: 'result' },
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
if (generationMode === 'unifiedCanvas' && data.boundingBox) {
|
||||
const { boundingBox } = data;
|
||||
dispatch(
|
||||
addImageToStagingArea({
|
||||
image: { ...newImage, category: 'temp' },
|
||||
boundingBox,
|
||||
})
|
||||
);
|
||||
// if (generationMode === 'unifiedCanvas' && data.boundingBox) {
|
||||
// const { boundingBox } = data;
|
||||
// dispatch(
|
||||
// addImageToStagingArea({
|
||||
// image: { ...newImage, category: 'temp' },
|
||||
// boundingBox,
|
||||
// })
|
||||
// );
|
||||
|
||||
if (state.canvas.shouldAutoSave) {
|
||||
dispatch(
|
||||
addImage({
|
||||
image: { ...newImage, category: 'result' },
|
||||
category: 'result',
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
// if (state.canvas.shouldAutoSave) {
|
||||
// dispatch(
|
||||
// addImage({
|
||||
// image: { ...newImage, category: 'result' },
|
||||
// category: 'result',
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: fix
|
||||
// if (shouldLoopback) {
|
||||
// const activeTabName = tabMap[activeTab];
|
||||
// switch (activeTabName) {
|
||||
// case 'img2img': {
|
||||
// dispatch(initialImageSelected(newImage.uuid));
|
||||
// // dispatch(setInitialImage(newImage));
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // TODO: fix
|
||||
// // if (shouldLoopback) {
|
||||
// // const activeTabName = tabMap[activeTab];
|
||||
// // switch (activeTabName) {
|
||||
// // case 'img2img': {
|
||||
// // dispatch(initialImageSelected(newImage.uuid));
|
||||
// // // dispatch(setInitialImage(newImage));
|
||||
// // break;
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
dispatch(clearIntermediateImage());
|
||||
// dispatch(clearIntermediateImage());
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Image generated: ${data.url}`,
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'intermediateResult' event.
|
||||
*/
|
||||
onIntermediateResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
try {
|
||||
dispatch(
|
||||
setIntermediateImage({
|
||||
uuid: uuidv4(),
|
||||
...data,
|
||||
category: 'result',
|
||||
})
|
||||
);
|
||||
if (!data.isBase64) {
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Intermediate image generated: ${data.url}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive an 'esrganResult' event.
|
||||
*/
|
||||
onPostprocessingResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
try {
|
||||
dispatch(
|
||||
addImage({
|
||||
category: 'result',
|
||||
image: {
|
||||
uuid: uuidv4(),
|
||||
...data,
|
||||
category: 'result',
|
||||
},
|
||||
})
|
||||
);
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Image generated: ${data.url}`,
|
||||
// })
|
||||
// );
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'intermediateResult' event.
|
||||
// */
|
||||
// onIntermediateResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
// try {
|
||||
// dispatch(
|
||||
// setIntermediateImage({
|
||||
// uuid: uuidv4(),
|
||||
// ...data,
|
||||
// category: 'result',
|
||||
// })
|
||||
// );
|
||||
// if (!data.isBase64) {
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Intermediate image generated: ${data.url}`,
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive an 'esrganResult' event.
|
||||
// */
|
||||
// onPostprocessingResult: (data: InvokeAI.ImageResultResponse) => {
|
||||
// try {
|
||||
// dispatch(
|
||||
// addImage({
|
||||
// category: 'result',
|
||||
// image: {
|
||||
// uuid: uuidv4(),
|
||||
// ...data,
|
||||
// category: 'result',
|
||||
// },
|
||||
// })
|
||||
// );
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Postprocessed: ${data.url}`,
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'progressUpdate' event.
|
||||
* TODO: Add additional progress phases
|
||||
*/
|
||||
onProgressUpdate: (data: InvokeAI.SystemStatus) => {
|
||||
try {
|
||||
dispatch(setIsProcessing(true));
|
||||
dispatch(setSystemStatus(data));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'progressUpdate' event.
|
||||
*/
|
||||
onError: (data: InvokeAI.ErrorResponse) => {
|
||||
const { message, additionalData } = data;
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Postprocessed: ${data.url}`,
|
||||
// })
|
||||
// );
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'progressUpdate' event.
|
||||
// * TODO: Add additional progress phases
|
||||
// */
|
||||
// onProgressUpdate: (data: InvokeAI.SystemStatus) => {
|
||||
// try {
|
||||
// dispatch(setIsProcessing(true));
|
||||
// dispatch(setSystemStatus(data));
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'progressUpdate' event.
|
||||
// */
|
||||
// onError: (data: InvokeAI.ErrorResponse) => {
|
||||
// const { message, additionalData } = data;
|
||||
|
||||
if (additionalData) {
|
||||
// TODO: handle more data than short message
|
||||
}
|
||||
// if (additionalData) {
|
||||
// // TODO: handle more data than short message
|
||||
// }
|
||||
|
||||
try {
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Server error: ${message}`,
|
||||
level: 'error',
|
||||
})
|
||||
);
|
||||
dispatch(errorOccurred());
|
||||
dispatch(clearIntermediateImage());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'galleryImages' event.
|
||||
*/
|
||||
onGalleryImages: (data: InvokeAI.GalleryImagesResponse) => {
|
||||
const { images, areMoreImagesAvailable, category } = data;
|
||||
// try {
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Server error: ${message}`,
|
||||
// level: 'error',
|
||||
// })
|
||||
// );
|
||||
// dispatch(errorOccurred());
|
||||
// dispatch(clearIntermediateImage());
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'galleryImages' event.
|
||||
// */
|
||||
// onGalleryImages: (data: InvokeAI.GalleryImagesResponse) => {
|
||||
// const { images, areMoreImagesAvailable, category } = data;
|
||||
|
||||
/**
|
||||
* the logic here ideally would be in the reducer but we have a side effect:
|
||||
* generating a uuid. so the logic needs to be here, outside redux.
|
||||
*/
|
||||
// /**
|
||||
// * the logic here ideally would be in the reducer but we have a side effect:
|
||||
// * generating a uuid. so the logic needs to be here, outside redux.
|
||||
// */
|
||||
|
||||
// Generate a UUID for each image
|
||||
const preparedImages = images.map((image): InvokeAI._Image => {
|
||||
return {
|
||||
uuid: uuidv4(),
|
||||
...image,
|
||||
};
|
||||
});
|
||||
// // Generate a UUID for each image
|
||||
// const preparedImages = images.map((image): InvokeAI._Image => {
|
||||
// return {
|
||||
// uuid: uuidv4(),
|
||||
// ...image,
|
||||
// };
|
||||
// });
|
||||
|
||||
dispatch(
|
||||
addGalleryImages({
|
||||
images: preparedImages,
|
||||
areMoreImagesAvailable,
|
||||
category,
|
||||
})
|
||||
);
|
||||
// dispatch(
|
||||
// addGalleryImages({
|
||||
// images: preparedImages,
|
||||
// areMoreImagesAvailable,
|
||||
// category,
|
||||
// })
|
||||
// );
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Loaded ${images.length} images`,
|
||||
})
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'processingCanceled' event.
|
||||
*/
|
||||
onProcessingCanceled: () => {
|
||||
dispatch(processingCanceled());
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Loaded ${images.length} images`,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'processingCanceled' event.
|
||||
// */
|
||||
// onProcessingCanceled: () => {
|
||||
// dispatch(processingCanceled());
|
||||
|
||||
const { intermediateImage } = getState().gallery;
|
||||
// const { intermediateImage } = getState().gallery;
|
||||
|
||||
if (intermediateImage) {
|
||||
if (!intermediateImage.isBase64) {
|
||||
dispatch(
|
||||
addImage({
|
||||
category: 'result',
|
||||
image: intermediateImage,
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Intermediate image saved: ${intermediateImage.url}`,
|
||||
})
|
||||
);
|
||||
}
|
||||
dispatch(clearIntermediateImage());
|
||||
}
|
||||
// if (intermediateImage) {
|
||||
// if (!intermediateImage.isBase64) {
|
||||
// dispatch(
|
||||
// addImage({
|
||||
// category: 'result',
|
||||
// image: intermediateImage,
|
||||
// })
|
||||
// );
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Intermediate image saved: ${intermediateImage.url}`,
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
// dispatch(clearIntermediateImage());
|
||||
// }
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Processing canceled`,
|
||||
level: 'warning',
|
||||
})
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Callback to run when we receive a 'imageDeleted' event.
|
||||
*/
|
||||
onImageDeleted: (data: InvokeAI.ImageDeletedResponse) => {
|
||||
const { url } = data;
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Processing canceled`,
|
||||
// level: 'warning',
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// /**
|
||||
// * Callback to run when we receive a 'imageDeleted' event.
|
||||
// */
|
||||
// onImageDeleted: (data: InvokeAI.ImageDeletedResponse) => {
|
||||
// const { url } = data;
|
||||
|
||||
// remove image from gallery
|
||||
dispatch(removeImage(data));
|
||||
// // remove image from gallery
|
||||
// dispatch(removeImage(data));
|
||||
|
||||
// remove references to image in options
|
||||
const {
|
||||
generation: { initialImage, maskPath },
|
||||
} = getState();
|
||||
// // remove references to image in options
|
||||
// const {
|
||||
// generation: { initialImage, maskPath },
|
||||
// } = getState();
|
||||
|
||||
if (
|
||||
initialImage === url ||
|
||||
(initialImage as InvokeAI._Image)?.url === url
|
||||
) {
|
||||
dispatch(clearInitialImage());
|
||||
}
|
||||
// if (
|
||||
// initialImage === url ||
|
||||
// (initialImage as InvokeAI._Image)?.url === url
|
||||
// ) {
|
||||
// dispatch(clearInitialImage());
|
||||
// }
|
||||
|
||||
if (maskPath === url) {
|
||||
dispatch(setMaskPath(''));
|
||||
}
|
||||
// if (maskPath === url) {
|
||||
// dispatch(setMaskPath(''));
|
||||
// }
|
||||
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Image deleted: ${url}`,
|
||||
})
|
||||
);
|
||||
},
|
||||
onSystemConfig: (data: InvokeAI.SystemConfig) => {
|
||||
dispatch(setSystemConfig(data));
|
||||
if (!data.infill_methods.includes('patchmatch')) {
|
||||
dispatch(setInfillMethod(data.infill_methods[0]));
|
||||
}
|
||||
},
|
||||
onFoundModels: (data: InvokeAI.FoundModelResponse) => {
|
||||
const { search_folder, found_models } = data;
|
||||
dispatch(setSearchFolder(search_folder));
|
||||
dispatch(setFoundModels(found_models));
|
||||
},
|
||||
onNewModelAdded: (data: InvokeAI.ModelAddedResponse) => {
|
||||
const { new_model_name, model_list, update } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setCurrentStatus(i18n.t('modelManager.modelAdded')));
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Model Added: ${new_model_name}`,
|
||||
level: 'info',
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
addToast({
|
||||
title: !update
|
||||
? `${i18n.t('modelManager.modelAdded')}: ${new_model_name}`
|
||||
: `${i18n.t('modelManager.modelUpdated')}: ${new_model_name}`,
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
})
|
||||
);
|
||||
},
|
||||
onModelDeleted: (data: InvokeAI.ModelDeletedResponse) => {
|
||||
const { deleted_model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `${i18n.t(
|
||||
'modelManager.modelAdded'
|
||||
)}: ${deleted_model_name}`,
|
||||
level: 'info',
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
addToast({
|
||||
title: `${i18n.t(
|
||||
'modelManager.modelEntryDeleted'
|
||||
)}: ${deleted_model_name}`,
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
})
|
||||
);
|
||||
},
|
||||
onModelConverted: (data: InvokeAI.ModelConvertedResponse) => {
|
||||
const { converted_model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setCurrentStatus(i18n.t('common.statusModelConverted')));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setIsCancelable(true));
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Model converted: ${converted_model_name}`,
|
||||
level: 'info',
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
addToast({
|
||||
title: `${i18n.t(
|
||||
'modelManager.modelConverted'
|
||||
)}: ${converted_model_name}`,
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
})
|
||||
);
|
||||
},
|
||||
onModelsMerged: (data: InvokeAI.ModelsMergedResponse) => {
|
||||
const { merged_models, merged_model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setCurrentStatus(i18n.t('common.statusMergedModels')));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setIsCancelable(true));
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Models merged: ${merged_models}`,
|
||||
level: 'info',
|
||||
})
|
||||
);
|
||||
dispatch(
|
||||
addToast({
|
||||
title: `${i18n.t('modelManager.modelsMerged')}: ${merged_model_name}`,
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
})
|
||||
);
|
||||
},
|
||||
onModelChanged: (data: InvokeAI.ModelChangeResponse) => {
|
||||
const { model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setCurrentStatus(i18n.t('common.statusModelChanged')));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setIsCancelable(true));
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Model changed: ${model_name}`,
|
||||
level: 'info',
|
||||
})
|
||||
);
|
||||
},
|
||||
onModelChangeFailed: (data: InvokeAI.ModelChangeResponse) => {
|
||||
const { model_name, model_list } = data;
|
||||
dispatch(setModelList(model_list));
|
||||
dispatch(setIsProcessing(false));
|
||||
dispatch(setIsCancelable(true));
|
||||
dispatch(errorOccurred());
|
||||
dispatch(
|
||||
addLogEntry({
|
||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
message: `Model change failed: ${model_name}`,
|
||||
level: 'error',
|
||||
})
|
||||
);
|
||||
},
|
||||
onTempFolderEmptied: () => {
|
||||
dispatch(
|
||||
addToast({
|
||||
title: i18n.t('toast.tempFoldersEmptied'),
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
})
|
||||
);
|
||||
},
|
||||
};
|
||||
};
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Image deleted: ${url}`,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onSystemConfig: (data: InvokeAI.SystemConfig) => {
|
||||
// dispatch(setSystemConfig(data));
|
||||
// if (!data.infill_methods.includes('patchmatch')) {
|
||||
// dispatch(setInfillMethod(data.infill_methods[0]));
|
||||
// }
|
||||
// },
|
||||
// onFoundModels: (data: InvokeAI.FoundModelResponse) => {
|
||||
// const { search_folder, found_models } = data;
|
||||
// dispatch(setSearchFolder(search_folder));
|
||||
// dispatch(setFoundModels(found_models));
|
||||
// },
|
||||
// onNewModelAdded: (data: InvokeAI.ModelAddedResponse) => {
|
||||
// const { new_model_name, model_list, update } = data;
|
||||
// dispatch(setModelList(model_list));
|
||||
// dispatch(setIsProcessing(false));
|
||||
// dispatch(setCurrentStatus(i18n.t('modelManager.modelAdded')));
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Model Added: ${new_model_name}`,
|
||||
// level: 'info',
|
||||
// })
|
||||
// );
|
||||
// dispatch(
|
||||
// addToast({
|
||||
// title: !update
|
||||
// ? `${i18n.t('modelManager.modelAdded')}: ${new_model_name}`
|
||||
// : `${i18n.t('modelManager.modelUpdated')}: ${new_model_name}`,
|
||||
// status: 'success',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onModelDeleted: (data: InvokeAI.ModelDeletedResponse) => {
|
||||
// const { deleted_model_name, model_list } = data;
|
||||
// dispatch(setModelList(model_list));
|
||||
// dispatch(setIsProcessing(false));
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `${i18n.t(
|
||||
// 'modelManager.modelAdded'
|
||||
// )}: ${deleted_model_name}`,
|
||||
// level: 'info',
|
||||
// })
|
||||
// );
|
||||
// dispatch(
|
||||
// addToast({
|
||||
// title: `${i18n.t(
|
||||
// 'modelManager.modelEntryDeleted'
|
||||
// )}: ${deleted_model_name}`,
|
||||
// status: 'success',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onModelConverted: (data: InvokeAI.ModelConvertedResponse) => {
|
||||
// const { converted_model_name, model_list } = data;
|
||||
// dispatch(setModelList(model_list));
|
||||
// dispatch(setCurrentStatus(i18n.t('common.statusModelConverted')));
|
||||
// dispatch(setIsProcessing(false));
|
||||
// dispatch(setIsCancelable(true));
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Model converted: ${converted_model_name}`,
|
||||
// level: 'info',
|
||||
// })
|
||||
// );
|
||||
// dispatch(
|
||||
// addToast({
|
||||
// title: `${i18n.t(
|
||||
// 'modelManager.modelConverted'
|
||||
// )}: ${converted_model_name}`,
|
||||
// status: 'success',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onModelsMerged: (data: InvokeAI.ModelsMergedResponse) => {
|
||||
// const { merged_models, merged_model_name, model_list } = data;
|
||||
// dispatch(setModelList(model_list));
|
||||
// dispatch(setCurrentStatus(i18n.t('common.statusMergedModels')));
|
||||
// dispatch(setIsProcessing(false));
|
||||
// dispatch(setIsCancelable(true));
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Models merged: ${merged_models}`,
|
||||
// level: 'info',
|
||||
// })
|
||||
// );
|
||||
// dispatch(
|
||||
// addToast({
|
||||
// title: `${i18n.t('modelManager.modelsMerged')}: ${merged_model_name}`,
|
||||
// status: 'success',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onModelChanged: (data: InvokeAI.ModelChangeResponse) => {
|
||||
// const { model_name, model_list } = data;
|
||||
// dispatch(setModelList(model_list));
|
||||
// dispatch(setCurrentStatus(i18n.t('common.statusModelChanged')));
|
||||
// dispatch(setIsProcessing(false));
|
||||
// dispatch(setIsCancelable(true));
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Model changed: ${model_name}`,
|
||||
// level: 'info',
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onModelChangeFailed: (data: InvokeAI.ModelChangeResponse) => {
|
||||
// const { model_name, model_list } = data;
|
||||
// dispatch(setModelList(model_list));
|
||||
// dispatch(setIsProcessing(false));
|
||||
// dispatch(setIsCancelable(true));
|
||||
// dispatch(errorOccurred());
|
||||
// dispatch(
|
||||
// addLogEntry({
|
||||
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||
// message: `Model change failed: ${model_name}`,
|
||||
// level: 'error',
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// onTempFolderEmptied: () => {
|
||||
// dispatch(
|
||||
// addToast({
|
||||
// title: i18n.t('toast.tempFoldersEmptied'),
|
||||
// status: 'success',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// })
|
||||
// );
|
||||
// },
|
||||
// };
|
||||
// };
|
||||
|
||||
export default makeSocketIOListeners;
|
||||
// export default makeSocketIOListeners;
|
||||
|
||||
export default {};
|
||||
|
@ -1,246 +1,248 @@
|
||||
import { Middleware } from '@reduxjs/toolkit';
|
||||
import { io } from 'socket.io-client';
|
||||
// import { Middleware } from '@reduxjs/toolkit';
|
||||
// import { io } from 'socket.io-client';
|
||||
|
||||
import makeSocketIOEmitters from './emitters';
|
||||
import makeSocketIOListeners from './listeners';
|
||||
// import makeSocketIOEmitters from './emitters';
|
||||
// import makeSocketIOListeners from './listeners';
|
||||
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
// import * as InvokeAI from 'app/types/invokeai';
|
||||
|
||||
/**
|
||||
* Creates a socketio middleware to handle communication with server.
|
||||
*
|
||||
* Special `socketio/actionName` actions are created in actions.ts and
|
||||
* exported for use by the application, which treats them like any old
|
||||
* action, using `dispatch` to dispatch them.
|
||||
*
|
||||
* These actions are intercepted here, where `socketio.emit()` calls are
|
||||
* made on their behalf - see `emitters.ts`. The emitter functions
|
||||
* are the outbound communication to the server.
|
||||
*
|
||||
* Listeners are also established here - see `listeners.ts`. The listener
|
||||
* functions receive communication from the server and usually dispatch
|
||||
* some new action to handle whatever data was sent from the server.
|
||||
*/
|
||||
export const socketioMiddleware = () => {
|
||||
const { origin } = new URL(window.location.href);
|
||||
// /**
|
||||
// * Creates a socketio middleware to handle communication with server.
|
||||
// *
|
||||
// * Special `socketio/actionName` actions are created in actions.ts and
|
||||
// * exported for use by the application, which treats them like any old
|
||||
// * action, using `dispatch` to dispatch them.
|
||||
// *
|
||||
// * These actions are intercepted here, where `socketio.emit()` calls are
|
||||
// * made on their behalf - see `emitters.ts`. The emitter functions
|
||||
// * are the outbound communication to the server.
|
||||
// *
|
||||
// * Listeners are also established here - see `listeners.ts`. The listener
|
||||
// * functions receive communication from the server and usually dispatch
|
||||
// * some new action to handle whatever data was sent from the server.
|
||||
// */
|
||||
// export const socketioMiddleware = () => {
|
||||
// const { origin } = new URL(window.location.href);
|
||||
|
||||
const socketio = io(origin, {
|
||||
timeout: 60000,
|
||||
path: `${window.location.pathname}socket.io`,
|
||||
});
|
||||
// const socketio = io(origin, {
|
||||
// timeout: 60000,
|
||||
// path: `${window.location.pathname}socket.io`,
|
||||
// });
|
||||
|
||||
socketio.disconnect();
|
||||
// socketio.disconnect();
|
||||
|
||||
let areListenersSet = false;
|
||||
// let areListenersSet = false;
|
||||
|
||||
const middleware: Middleware = (store) => (next) => (action) => {
|
||||
const {
|
||||
onConnect,
|
||||
onDisconnect,
|
||||
onError,
|
||||
onPostprocessingResult,
|
||||
onGenerationResult,
|
||||
onIntermediateResult,
|
||||
onProgressUpdate,
|
||||
onGalleryImages,
|
||||
onProcessingCanceled,
|
||||
onImageDeleted,
|
||||
onSystemConfig,
|
||||
onModelChanged,
|
||||
onFoundModels,
|
||||
onNewModelAdded,
|
||||
onModelDeleted,
|
||||
onModelConverted,
|
||||
onModelsMerged,
|
||||
onModelChangeFailed,
|
||||
onTempFolderEmptied,
|
||||
} = makeSocketIOListeners(store);
|
||||
// const middleware: Middleware = (store) => (next) => (action) => {
|
||||
// const {
|
||||
// onConnect,
|
||||
// onDisconnect,
|
||||
// onError,
|
||||
// onPostprocessingResult,
|
||||
// onGenerationResult,
|
||||
// onIntermediateResult,
|
||||
// onProgressUpdate,
|
||||
// onGalleryImages,
|
||||
// onProcessingCanceled,
|
||||
// onImageDeleted,
|
||||
// onSystemConfig,
|
||||
// onModelChanged,
|
||||
// onFoundModels,
|
||||
// onNewModelAdded,
|
||||
// onModelDeleted,
|
||||
// onModelConverted,
|
||||
// onModelsMerged,
|
||||
// onModelChangeFailed,
|
||||
// onTempFolderEmptied,
|
||||
// } = makeSocketIOListeners(store);
|
||||
|
||||
const {
|
||||
emitGenerateImage,
|
||||
emitRunESRGAN,
|
||||
emitRunFacetool,
|
||||
emitDeleteImage,
|
||||
emitRequestImages,
|
||||
emitRequestNewImages,
|
||||
emitCancelProcessing,
|
||||
emitRequestSystemConfig,
|
||||
emitSearchForModels,
|
||||
emitAddNewModel,
|
||||
emitDeleteModel,
|
||||
emitConvertToDiffusers,
|
||||
emitMergeDiffusersModels,
|
||||
emitRequestModelChange,
|
||||
emitSaveStagingAreaImageToGallery,
|
||||
emitRequestEmptyTempFolder,
|
||||
} = makeSocketIOEmitters(store, socketio);
|
||||
// const {
|
||||
// emitGenerateImage,
|
||||
// emitRunESRGAN,
|
||||
// emitRunFacetool,
|
||||
// emitDeleteImage,
|
||||
// emitRequestImages,
|
||||
// emitRequestNewImages,
|
||||
// emitCancelProcessing,
|
||||
// emitRequestSystemConfig,
|
||||
// emitSearchForModels,
|
||||
// emitAddNewModel,
|
||||
// emitDeleteModel,
|
||||
// emitConvertToDiffusers,
|
||||
// emitMergeDiffusersModels,
|
||||
// emitRequestModelChange,
|
||||
// emitSaveStagingAreaImageToGallery,
|
||||
// emitRequestEmptyTempFolder,
|
||||
// } = makeSocketIOEmitters(store, socketio);
|
||||
|
||||
/**
|
||||
* If this is the first time the middleware has been called (e.g. during store setup),
|
||||
* initialize all our socket.io listeners.
|
||||
*/
|
||||
if (!areListenersSet) {
|
||||
socketio.on('connect', () => onConnect());
|
||||
// /**
|
||||
// * If this is the first time the middleware has been called (e.g. during store setup),
|
||||
// * initialize all our socket.io listeners.
|
||||
// */
|
||||
// if (!areListenersSet) {
|
||||
// socketio.on('connect', () => onConnect());
|
||||
|
||||
socketio.on('disconnect', () => onDisconnect());
|
||||
// socketio.on('disconnect', () => onDisconnect());
|
||||
|
||||
socketio.on('error', (data: InvokeAI.ErrorResponse) => onError(data));
|
||||
// socketio.on('error', (data: InvokeAI.ErrorResponse) => onError(data));
|
||||
|
||||
socketio.on('generationResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
onGenerationResult(data)
|
||||
);
|
||||
// socketio.on('generationResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
// onGenerationResult(data)
|
||||
// );
|
||||
|
||||
socketio.on(
|
||||
'postprocessingResult',
|
||||
(data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data)
|
||||
);
|
||||
// socketio.on(
|
||||
// 'postprocessingResult',
|
||||
// (data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data)
|
||||
// );
|
||||
|
||||
socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
onIntermediateResult(data)
|
||||
);
|
||||
// socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) =>
|
||||
// onIntermediateResult(data)
|
||||
// );
|
||||
|
||||
socketio.on('progressUpdate', (data: InvokeAI.SystemStatus) =>
|
||||
onProgressUpdate(data)
|
||||
);
|
||||
// socketio.on('progressUpdate', (data: InvokeAI.SystemStatus) =>
|
||||
// onProgressUpdate(data)
|
||||
// );
|
||||
|
||||
socketio.on('galleryImages', (data: InvokeAI.GalleryImagesResponse) =>
|
||||
onGalleryImages(data)
|
||||
);
|
||||
// socketio.on('galleryImages', (data: InvokeAI.GalleryImagesResponse) =>
|
||||
// onGalleryImages(data)
|
||||
// );
|
||||
|
||||
socketio.on('processingCanceled', () => {
|
||||
onProcessingCanceled();
|
||||
});
|
||||
// socketio.on('processingCanceled', () => {
|
||||
// onProcessingCanceled();
|
||||
// });
|
||||
|
||||
socketio.on('imageDeleted', (data: InvokeAI.ImageDeletedResponse) => {
|
||||
onImageDeleted(data);
|
||||
});
|
||||
// socketio.on('imageDeleted', (data: InvokeAI.ImageDeletedResponse) => {
|
||||
// onImageDeleted(data);
|
||||
// });
|
||||
|
||||
socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => {
|
||||
onSystemConfig(data);
|
||||
});
|
||||
// socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => {
|
||||
// onSystemConfig(data);
|
||||
// });
|
||||
|
||||
socketio.on('foundModels', (data: InvokeAI.FoundModelResponse) => {
|
||||
onFoundModels(data);
|
||||
});
|
||||
// socketio.on('foundModels', (data: InvokeAI.FoundModelResponse) => {
|
||||
// onFoundModels(data);
|
||||
// });
|
||||
|
||||
socketio.on('newModelAdded', (data: InvokeAI.ModelAddedResponse) => {
|
||||
onNewModelAdded(data);
|
||||
});
|
||||
// socketio.on('newModelAdded', (data: InvokeAI.ModelAddedResponse) => {
|
||||
// onNewModelAdded(data);
|
||||
// });
|
||||
|
||||
socketio.on('modelDeleted', (data: InvokeAI.ModelDeletedResponse) => {
|
||||
onModelDeleted(data);
|
||||
});
|
||||
// socketio.on('modelDeleted', (data: InvokeAI.ModelDeletedResponse) => {
|
||||
// onModelDeleted(data);
|
||||
// });
|
||||
|
||||
socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => {
|
||||
onModelConverted(data);
|
||||
});
|
||||
// socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => {
|
||||
// onModelConverted(data);
|
||||
// });
|
||||
|
||||
socketio.on('modelsMerged', (data: InvokeAI.ModelsMergedResponse) => {
|
||||
onModelsMerged(data);
|
||||
});
|
||||
// socketio.on('modelsMerged', (data: InvokeAI.ModelsMergedResponse) => {
|
||||
// onModelsMerged(data);
|
||||
// });
|
||||
|
||||
socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => {
|
||||
onModelChanged(data);
|
||||
});
|
||||
// socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => {
|
||||
// onModelChanged(data);
|
||||
// });
|
||||
|
||||
socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => {
|
||||
onModelChangeFailed(data);
|
||||
});
|
||||
// socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => {
|
||||
// onModelChangeFailed(data);
|
||||
// });
|
||||
|
||||
socketio.on('tempFolderEmptied', () => {
|
||||
onTempFolderEmptied();
|
||||
});
|
||||
// socketio.on('tempFolderEmptied', () => {
|
||||
// onTempFolderEmptied();
|
||||
// });
|
||||
|
||||
areListenersSet = true;
|
||||
}
|
||||
// areListenersSet = true;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Handle redux actions caught by middleware.
|
||||
*/
|
||||
switch (action.type) {
|
||||
case 'socketio/generateImage': {
|
||||
emitGenerateImage(action.payload);
|
||||
break;
|
||||
}
|
||||
// /**
|
||||
// * Handle redux actions caught by middleware.
|
||||
// */
|
||||
// switch (action.type) {
|
||||
// case 'socketio/generateImage': {
|
||||
// emitGenerateImage(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/runESRGAN': {
|
||||
emitRunESRGAN(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/runESRGAN': {
|
||||
// emitRunESRGAN(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/runFacetool': {
|
||||
emitRunFacetool(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/runFacetool': {
|
||||
// emitRunFacetool(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/deleteImage': {
|
||||
emitDeleteImage(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/deleteImage': {
|
||||
// emitDeleteImage(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/requestImages': {
|
||||
emitRequestImages(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/requestImages': {
|
||||
// emitRequestImages(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/requestNewImages': {
|
||||
emitRequestNewImages(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/requestNewImages': {
|
||||
// emitRequestNewImages(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/cancelProcessing': {
|
||||
emitCancelProcessing();
|
||||
break;
|
||||
}
|
||||
// case 'socketio/cancelProcessing': {
|
||||
// emitCancelProcessing();
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/requestSystemConfig': {
|
||||
emitRequestSystemConfig();
|
||||
break;
|
||||
}
|
||||
// case 'socketio/requestSystemConfig': {
|
||||
// emitRequestSystemConfig();
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/searchForModels': {
|
||||
emitSearchForModels(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/searchForModels': {
|
||||
// emitSearchForModels(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/addNewModel': {
|
||||
emitAddNewModel(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/addNewModel': {
|
||||
// emitAddNewModel(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/deleteModel': {
|
||||
emitDeleteModel(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/deleteModel': {
|
||||
// emitDeleteModel(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/convertToDiffusers': {
|
||||
emitConvertToDiffusers(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/convertToDiffusers': {
|
||||
// emitConvertToDiffusers(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/mergeDiffusersModels': {
|
||||
emitMergeDiffusersModels(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/mergeDiffusersModels': {
|
||||
// emitMergeDiffusersModels(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/requestModelChange': {
|
||||
emitRequestModelChange(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/requestModelChange': {
|
||||
// emitRequestModelChange(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/saveStagingAreaImageToGallery': {
|
||||
emitSaveStagingAreaImageToGallery(action.payload);
|
||||
break;
|
||||
}
|
||||
// case 'socketio/saveStagingAreaImageToGallery': {
|
||||
// emitSaveStagingAreaImageToGallery(action.payload);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case 'socketio/requestEmptyTempFolder': {
|
||||
emitRequestEmptyTempFolder();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// case 'socketio/requestEmptyTempFolder': {
|
||||
// emitRequestEmptyTempFolder();
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
next(action);
|
||||
};
|
||||
// next(action);
|
||||
// };
|
||||
|
||||
return middleware;
|
||||
};
|
||||
// return middleware;
|
||||
// };
|
||||
|
||||
export default {};
|
||||
|
@ -19,8 +19,6 @@ import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||
import modelsReducer from 'features/system/store/modelSlice';
|
||||
import nodesReducer from 'features/nodes/store/nodesSlice';
|
||||
|
||||
import { socketioMiddleware } from '../socketio/middleware';
|
||||
import { socketMiddleware } from 'services/events/middleware';
|
||||
import { canvasDenylist } from 'features/canvas/store/canvasPersistDenylist';
|
||||
import { galleryDenylist } from 'features/gallery/store/galleryPersistDenylist';
|
||||
import { generationDenylist } from 'features/parameters/store/generationPersistDenylist';
|
||||
@ -30,6 +28,8 @@ import { nodesDenylist } from 'features/nodes/store/nodesPersistDenylist';
|
||||
import { postprocessingDenylist } from 'features/parameters/store/postprocessingPersistDenylist';
|
||||
import { systemDenylist } from 'features/system/store/systemPersistDenylist';
|
||||
import { uiDenylist } from 'features/ui/store/uiPersistDenylist';
|
||||
import { resultsDenylist } from 'features/gallery/store/resultsPersistDenylist';
|
||||
import { uploadsDenylist } from 'features/gallery/store/uploadsPersistDenylist';
|
||||
|
||||
/**
|
||||
* redux-persist provides an easy and reliable way to persist state across reloads.
|
||||
@ -87,13 +87,13 @@ const rootPersistConfig = getPersistConfig({
|
||||
const persistedReducer = persistReducer(rootPersistConfig, rootReducer);
|
||||
|
||||
// TODO: rip the old middleware out when nodes is complete
|
||||
export function buildMiddleware() {
|
||||
if (import.meta.env.MODE === 'nodes' || import.meta.env.MODE === 'package') {
|
||||
return socketMiddleware();
|
||||
} else {
|
||||
return socketioMiddleware();
|
||||
}
|
||||
}
|
||||
// export function buildMiddleware() {
|
||||
// if (import.meta.env.MODE === 'nodes' || import.meta.env.MODE === 'package') {
|
||||
// return socketMiddleware();
|
||||
// } else {
|
||||
// return socketioMiddleware();
|
||||
// }
|
||||
// }
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: persistedReducer,
|
||||
|
@ -111,24 +111,9 @@ export type FacetoolMetadata = CommonPostProcessedImageMetadata & {
|
||||
export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
|
||||
|
||||
// Metadata includes the system config and image metadata.
|
||||
export type Metadata = SystemGenerationMetadata & {
|
||||
image: GeneratedImageMetadata | PostProcessedImageMetadata;
|
||||
};
|
||||
|
||||
// An Image has a UUID, url, modified timestamp, width, height and maybe metadata
|
||||
export type _Image = {
|
||||
uuid: string;
|
||||
url: string;
|
||||
thumbnail: string;
|
||||
mtime: number;
|
||||
metadata?: Metadata;
|
||||
width: number;
|
||||
height: number;
|
||||
category: GalleryCategory;
|
||||
isBase64?: boolean;
|
||||
dreamPrompt?: 'string';
|
||||
name?: string;
|
||||
};
|
||||
// export type Metadata = SystemGenerationMetadata & {
|
||||
// image: GeneratedImageMetadata | PostProcessedImageMetadata;
|
||||
// };
|
||||
|
||||
/**
|
||||
* ResultImage
|
||||
@ -141,40 +126,35 @@ export type Image = {
|
||||
metadata: ImageResponseMetadata;
|
||||
};
|
||||
|
||||
// GalleryImages is an array of Image.
|
||||
export type GalleryImages = {
|
||||
images: Array<_Image>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Types related to the system status.
|
||||
*/
|
||||
|
||||
// This represents the processing status of the backend.
|
||||
export type SystemStatus = {
|
||||
isProcessing: boolean;
|
||||
currentStep: number;
|
||||
totalSteps: number;
|
||||
currentIteration: number;
|
||||
totalIterations: number;
|
||||
currentStatus: string;
|
||||
currentStatusHasSteps: boolean;
|
||||
hasError: boolean;
|
||||
};
|
||||
// // This represents the processing status of the backend.
|
||||
// export type SystemStatus = {
|
||||
// isProcessing: boolean;
|
||||
// currentStep: number;
|
||||
// totalSteps: number;
|
||||
// currentIteration: number;
|
||||
// totalIterations: number;
|
||||
// currentStatus: string;
|
||||
// currentStatusHasSteps: boolean;
|
||||
// hasError: boolean;
|
||||
// };
|
||||
|
||||
export type SystemGenerationMetadata = {
|
||||
model: string;
|
||||
model_weights?: string;
|
||||
model_id?: string;
|
||||
model_hash: string;
|
||||
app_id: string;
|
||||
app_version: string;
|
||||
};
|
||||
// export type SystemGenerationMetadata = {
|
||||
// model: string;
|
||||
// model_weights?: string;
|
||||
// model_id?: string;
|
||||
// model_hash: string;
|
||||
// app_id: string;
|
||||
// app_version: string;
|
||||
// };
|
||||
|
||||
export type SystemConfig = SystemGenerationMetadata & {
|
||||
model_list: ModelList;
|
||||
infill_methods: string[];
|
||||
};
|
||||
// export type SystemConfig = SystemGenerationMetadata & {
|
||||
// model_list: ModelList;
|
||||
// infill_methods: string[];
|
||||
// };
|
||||
|
||||
export type ModelStatus = 'active' | 'cached' | 'not loaded';
|
||||
|
||||
@ -286,9 +266,9 @@ export type FoundModelResponse = {
|
||||
found_models: FoundModel[];
|
||||
};
|
||||
|
||||
export type SystemStatusResponse = SystemStatus;
|
||||
// export type SystemStatusResponse = SystemStatus;
|
||||
|
||||
export type SystemConfigResponse = SystemConfig;
|
||||
// export type SystemConfigResponse = SystemConfig;
|
||||
|
||||
export type ImageResultResponse = Omit<_Image, 'uuid'> & {
|
||||
boundingBox?: IRect;
|
||||
@ -310,27 +290,10 @@ export type ErrorResponse = {
|
||||
additionalData?: string;
|
||||
};
|
||||
|
||||
export type GalleryImagesResponse = {
|
||||
images: Array<Omit<_Image, 'uuid'>>;
|
||||
areMoreImagesAvailable: boolean;
|
||||
category: GalleryCategory;
|
||||
};
|
||||
|
||||
export type ImageDeletedResponse = {
|
||||
uuid: string;
|
||||
url: string;
|
||||
category: GalleryCategory;
|
||||
};
|
||||
|
||||
export type ImageUrlResponse = {
|
||||
url: string;
|
||||
};
|
||||
|
||||
// export type UploadImagePayload = {
|
||||
// file: File;
|
||||
// destination?: ImageUploadDestination;
|
||||
// };
|
||||
|
||||
export type UploadOutpaintingMergeImagePayload = {
|
||||
dataURL: string;
|
||||
name: string;
|
||||
|
@ -233,7 +233,7 @@ const IAISlider = (props: IAIFullSliderProps) => {
|
||||
hidden={hideTooltip}
|
||||
{...sliderTooltipProps}
|
||||
>
|
||||
<SliderThumb {...sliderThumbProps} />
|
||||
<SliderThumb {...sliderThumbProps} zIndex={0} />
|
||||
</Tooltip>
|
||||
</Slider>
|
||||
|
||||
|
@ -1,32 +1,11 @@
|
||||
import { Badge, Box, ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
||||
import { useCallback } from 'react';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { FaUndo, FaUpload } from 'react-icons/fa';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Badge, Box, Flex } from '@chakra-ui/react';
|
||||
import { Image } from 'app/types/invokeai';
|
||||
|
||||
type ImageToImageOverlayProps = {
|
||||
setIsLoaded: (isLoaded: boolean) => void;
|
||||
image: Image;
|
||||
};
|
||||
|
||||
const ImageToImageOverlay = ({
|
||||
setIsLoaded,
|
||||
image,
|
||||
}: ImageToImageOverlayProps) => {
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const handleResetInitialImage = useCallback(() => {
|
||||
dispatch(clearInitialImage());
|
||||
setIsLoaded(false);
|
||||
}, [dispatch, setIsLoaded]);
|
||||
|
||||
const ImageToImageOverlay = ({ image }: ImageToImageOverlayProps) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -1,34 +1,13 @@
|
||||
import {
|
||||
Box,
|
||||
ButtonGroup,
|
||||
Collapse,
|
||||
Flex,
|
||||
Heading,
|
||||
HStack,
|
||||
Image,
|
||||
Spacer,
|
||||
Text,
|
||||
useDisclosure,
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
|
||||
import ImageToImageStrength from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageToImageStrength';
|
||||
import { ButtonGroup, Flex, Spacer, Text } from '@chakra-ui/react';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaUndo, FaUpload } from 'react-icons/fa';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { useCallback } from 'react';
|
||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
||||
|
||||
const ImageToImageSettingsHeader = () => {
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { saveStagingAreaImageToGallery } from 'app/socketio/actions';
|
||||
// import { saveStagingAreaImageToGallery } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { addImage } from 'features/gallery/store/gallerySlice';
|
||||
// import { addImage } from 'features/gallery/store/gallerySlice';
|
||||
import {
|
||||
addToast,
|
||||
setCurrentStatus,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { get, isEqual, isNumber, isString } from 'lodash-es';
|
||||
|
||||
import {
|
||||
ButtonGroup,
|
||||
@ -10,7 +10,7 @@ import {
|
||||
useDisclosure,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { runESRGAN, runFacetool } from 'app/socketio/actions';
|
||||
// import { runESRGAN, runFacetool } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
@ -63,11 +63,11 @@ import {
|
||||
} from '../store/gallerySelectors';
|
||||
import DeleteImageModal from './DeleteImageModal';
|
||||
import { useCallback } from 'react';
|
||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
import { useGetUrl } from 'common/util/getUrl';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { imageDeleted } from 'services/thunks/image';
|
||||
import { useParameters } from 'features/parameters/hooks/useParameters';
|
||||
|
||||
const currentImageButtonsSelector = createSelector(
|
||||
[
|
||||
@ -112,6 +112,8 @@ const currentImageButtonsSelector = createSelector(
|
||||
isLightboxOpen,
|
||||
shouldHidePreview,
|
||||
image,
|
||||
seed: image?.metadata?.invokeai?.node?.seed,
|
||||
prompt: image?.metadata?.invokeai?.node?.prompt,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -161,16 +163,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
|
||||
const toast = useToast();
|
||||
const { t } = useTranslation();
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
|
||||
const handleClickUseAsInitialImage = useCallback(() => {
|
||||
if (!image) return;
|
||||
if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
|
||||
dispatch(initialImageSelected(image.name));
|
||||
// dispatch(setInitialImage(currentImage));
|
||||
|
||||
// dispatch(setActiveTab('img2img'));
|
||||
}, [dispatch, image, isLightboxOpen]);
|
||||
const { recallPrompt, recallSeed, sendToImageToImage } = useParameters();
|
||||
|
||||
const handleCopyImage = useCallback(async () => {
|
||||
if (!image?.url) {
|
||||
@ -217,30 +211,6 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
});
|
||||
}, [toast, shouldTransformUrls, getUrl, t, image]);
|
||||
|
||||
useHotkeys(
|
||||
'shift+i',
|
||||
() => {
|
||||
if (image) {
|
||||
handleClickUseAsInitialImage();
|
||||
toast({
|
||||
title: t('toast.sentToImageToImage'),
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: t('toast.imageNotLoaded'),
|
||||
description: t('toast.imageNotLoadedDesc'),
|
||||
status: 'error',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[image]
|
||||
);
|
||||
|
||||
const handlePreviewVisibility = useCallback(() => {
|
||||
dispatch(setShouldHidePreview(!shouldHidePreview));
|
||||
}, [dispatch, shouldHidePreview]);
|
||||
@ -259,7 +229,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
useHotkeys(
|
||||
'a',
|
||||
() => {
|
||||
if (['txt2img', 'img2img'].includes(image?.metadata?.sd_metadata?.type)) {
|
||||
const type = image?.metadata?.invokeai?.node?.types;
|
||||
if (isString(type) && ['txt2img', 'img2img'].includes(type)) {
|
||||
handleClickUseAllParameters();
|
||||
toast({
|
||||
title: t('toast.parametersSet'),
|
||||
@ -280,63 +251,23 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
[image]
|
||||
);
|
||||
|
||||
const handleClickUseSeed = () => {
|
||||
image?.metadata && dispatch(setSeed(image.metadata.sd_metadata.seed));
|
||||
};
|
||||
const handleUseSeed = useCallback(() => {
|
||||
recallSeed(image?.metadata?.invokeai?.node?.seed);
|
||||
}, [image, recallSeed]);
|
||||
|
||||
useHotkeys(
|
||||
's',
|
||||
() => {
|
||||
if (image?.metadata?.sd_metadata?.seed) {
|
||||
handleClickUseSeed();
|
||||
toast({
|
||||
title: t('toast.seedSet'),
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: t('toast.seedNotSet'),
|
||||
description: t('toast.seedNotSetDesc'),
|
||||
status: 'error',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[image]
|
||||
);
|
||||
useHotkeys('s', handleUseSeed, [image]);
|
||||
|
||||
const handleClickUsePrompt = useCallback(() => {
|
||||
if (image?.metadata?.sd_metadata?.prompt) {
|
||||
setBothPrompts(image?.metadata?.sd_metadata?.prompt);
|
||||
}
|
||||
}, [image?.metadata?.sd_metadata?.prompt, setBothPrompts]);
|
||||
const handleUsePrompt = useCallback(() => {
|
||||
recallPrompt(image?.metadata?.invokeai?.node?.prompt);
|
||||
}, [image, recallPrompt]);
|
||||
|
||||
useHotkeys(
|
||||
'p',
|
||||
() => {
|
||||
if (image?.metadata?.sd_metadata?.prompt) {
|
||||
handleClickUsePrompt();
|
||||
toast({
|
||||
title: t('toast.promptSet'),
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
} else {
|
||||
toast({
|
||||
title: t('toast.promptNotSet'),
|
||||
description: t('toast.promptNotSetDesc'),
|
||||
status: 'error',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
[image]
|
||||
);
|
||||
useHotkeys('p', handleUsePrompt, [image]);
|
||||
|
||||
const handleSendToImageToImage = useCallback(() => {
|
||||
sendToImageToImage(image);
|
||||
}, [image, sendToImageToImage]);
|
||||
|
||||
useHotkeys('shift+i', handleSendToImageToImage, [image]);
|
||||
|
||||
const handleClickUpscale = useCallback(() => {
|
||||
// selectedImage && dispatch(runESRGAN(selectedImage));
|
||||
@ -496,7 +427,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
>
|
||||
<IAIButton
|
||||
size="sm"
|
||||
onClick={handleClickUseAsInitialImage}
|
||||
onClick={handleSendToImageToImage}
|
||||
leftIcon={<FaShare />}
|
||||
>
|
||||
{t('parameters.sendToImg2Img')}
|
||||
@ -570,16 +501,16 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
||||
icon={<FaQuoteRight />}
|
||||
tooltip={`${t('parameters.usePrompt')} (P)`}
|
||||
aria-label={`${t('parameters.usePrompt')} (P)`}
|
||||
isDisabled={!image?.metadata?.sd_metadata?.prompt}
|
||||
onClick={handleClickUsePrompt}
|
||||
isDisabled={!image?.metadata?.invokeai?.node?.prompt}
|
||||
onClick={handleUsePrompt}
|
||||
/>
|
||||
|
||||
<IAIIconButton
|
||||
icon={<FaSeedling />}
|
||||
tooltip={`${t('parameters.useSeed')} (S)`}
|
||||
aria-label={`${t('parameters.useSeed')} (S)`}
|
||||
isDisabled={!image?.metadata?.sd_metadata?.seed}
|
||||
onClick={handleClickUseSeed}
|
||||
isDisabled={!image?.metadata?.invokeai?.node?.seed}
|
||||
onClick={handleUseSeed}
|
||||
/>
|
||||
|
||||
<IAIIconButton
|
||||
|
@ -17,31 +17,11 @@ export const imagesSelector = createSelector(
|
||||
[uiSelector, selectedImageSelector, systemSelector],
|
||||
(ui, selectedImage, system) => {
|
||||
const { shouldShowImageDetails, shouldHidePreview } = ui;
|
||||
const { progressImage } = system;
|
||||
|
||||
// TODO: Clean this up, this is really gross
|
||||
const imageToDisplay = progressImage
|
||||
? {
|
||||
url: progressImage.dataURL,
|
||||
width: progressImage.width,
|
||||
height: progressImage.height,
|
||||
isProgressImage: true,
|
||||
image: progressImage,
|
||||
}
|
||||
: selectedImage
|
||||
? {
|
||||
url: selectedImage.url,
|
||||
width: selectedImage.metadata.width,
|
||||
height: selectedImage.metadata.height,
|
||||
isProgressImage: false,
|
||||
image: selectedImage,
|
||||
}
|
||||
: null;
|
||||
|
||||
return {
|
||||
shouldShowImageDetails,
|
||||
shouldHidePreview,
|
||||
imageToDisplay,
|
||||
image: selectedImage,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -52,7 +32,7 @@ export const imagesSelector = createSelector(
|
||||
);
|
||||
|
||||
const CurrentImagePreview = () => {
|
||||
const { shouldShowImageDetails, imageToDisplay, shouldHidePreview } =
|
||||
const { shouldShowImageDetails, image, shouldHidePreview } =
|
||||
useAppSelector(imagesSelector);
|
||||
const { getUrl } = useGetUrl();
|
||||
|
||||
@ -66,54 +46,37 @@ const CurrentImagePreview = () => {
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
{imageToDisplay && (
|
||||
{image && (
|
||||
<Image
|
||||
src={
|
||||
shouldHidePreview
|
||||
? undefined
|
||||
: imageToDisplay.isProgressImage
|
||||
? imageToDisplay.url
|
||||
: getUrl(imageToDisplay.url)
|
||||
}
|
||||
width={imageToDisplay.width}
|
||||
height={imageToDisplay.height}
|
||||
fallback={
|
||||
shouldHidePreview ? (
|
||||
<CurrentImageHidden />
|
||||
) : !imageToDisplay.isProgressImage ? (
|
||||
<CurrentImageFallback />
|
||||
) : undefined
|
||||
}
|
||||
src={shouldHidePreview ? undefined : getUrl(image.url)}
|
||||
width={image.metadata.width}
|
||||
height={image.metadata.height}
|
||||
fallback={shouldHidePreview ? <CurrentImageHidden /> : undefined}
|
||||
sx={{
|
||||
objectFit: 'contain',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
height: 'auto',
|
||||
position: 'absolute',
|
||||
imageRendering: imageToDisplay.isProgressImage
|
||||
? 'pixelated'
|
||||
: 'initial',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{shouldShowImageDetails && image && 'metadata' in image && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 'base',
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
<ImageMetadataViewer image={image} />
|
||||
</Box>
|
||||
)}
|
||||
{!shouldShowImageDetails && <NextPrevImageButtons />}
|
||||
{shouldShowImageDetails &&
|
||||
imageToDisplay &&
|
||||
'metadata' in imageToDisplay.image && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 'base',
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
<ImageMetadataViewer image={imageToDisplay.image} />
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
@ -5,65 +5,38 @@ import {
|
||||
Image,
|
||||
MenuItem,
|
||||
MenuList,
|
||||
Text,
|
||||
Skeleton,
|
||||
useDisclosure,
|
||||
useTheme,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
imageSelected,
|
||||
setCurrentImage,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import {
|
||||
initialImageSelected,
|
||||
setAllImageToImageParameters,
|
||||
setAllParameters,
|
||||
setSeed,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { DragEvent, memo, useState } from 'react';
|
||||
import {
|
||||
FaCheck,
|
||||
FaExpand,
|
||||
FaLink,
|
||||
FaShare,
|
||||
FaTrash,
|
||||
FaTrashAlt,
|
||||
} from 'react-icons/fa';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { DragEvent, memo, useCallback, useState } from 'react';
|
||||
import { FaCheck, FaExpand, FaImage, FaShare, FaTrash } from 'react-icons/fa';
|
||||
import DeleteImageModal from './DeleteImageModal';
|
||||
import { ContextMenu } from 'chakra-ui-contextmenu';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import {
|
||||
resizeAndScaleCanvas,
|
||||
setInitialCanvasImage,
|
||||
} from 'features/canvas/store/canvasSlice';
|
||||
import { resizeAndScaleCanvas } from 'features/canvas/store/canvasSlice';
|
||||
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
||||
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { useGetUrl } from 'common/util/getUrl';
|
||||
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
||||
import { BiZoomIn } from 'react-icons/bi';
|
||||
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
|
||||
import { imageDeleted } from 'services/thunks/image';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { configSelector } from 'features/system/store/configSelectors';
|
||||
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { useParameters } from 'features/parameters/hooks/useParameters';
|
||||
|
||||
export const selector = createSelector(
|
||||
[
|
||||
gallerySelector,
|
||||
systemSelector,
|
||||
configSelector,
|
||||
lightboxSelector,
|
||||
activeTabNameSelector,
|
||||
],
|
||||
(gallery, system, config, lightbox, activeTabName) => {
|
||||
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
|
||||
(gallery, system, lightbox, activeTabName) => {
|
||||
const {
|
||||
galleryImageObjectFit,
|
||||
galleryImageMinimumWidth,
|
||||
@ -71,7 +44,6 @@ export const selector = createSelector(
|
||||
} = gallery;
|
||||
|
||||
const { isLightboxOpen } = lightbox;
|
||||
const { disabledFeatures } = config;
|
||||
const { isConnected, isProcessing, shouldConfirmOnDelete } = system;
|
||||
|
||||
return {
|
||||
@ -82,7 +54,6 @@ export const selector = createSelector(
|
||||
shouldUseSingleGalleryColumn,
|
||||
activeTabName,
|
||||
isLightboxOpen,
|
||||
disabledFeatures,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -113,14 +84,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
galleryImageMinimumWidth,
|
||||
canDeleteImage,
|
||||
shouldUseSingleGalleryColumn,
|
||||
disabledFeatures,
|
||||
shouldConfirmOnDelete,
|
||||
} = useAppSelector(selector);
|
||||
|
||||
const {
|
||||
isOpen: isDeleteDialogOpen,
|
||||
onOpen: onDeleteDialogOpen,
|
||||
onClose: onDeleteDialogClose,
|
||||
} = useDisclosure();
|
||||
|
||||
const { image, isSelected } = props;
|
||||
const { url, thumbnail, name, metadata } = image;
|
||||
const { getUrl } = useGetUrl();
|
||||
@ -130,53 +102,62 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
const toast = useToast();
|
||||
const { direction } = useTheme();
|
||||
const { t } = useTranslation();
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
const { isFeatureEnabled: isLightboxEnabled } = useFeatureStatus('lightbox');
|
||||
const { recallSeed, recallPrompt, sendToImageToImage, recallInitialImage } =
|
||||
useParameters();
|
||||
|
||||
const handleMouseOver = () => setIsHovered(true);
|
||||
|
||||
const handleMouseOut = () => setIsHovered(false);
|
||||
|
||||
const handleInitiateDelete = () => {
|
||||
// Immediately deletes an image
|
||||
const handleDelete = useCallback(() => {
|
||||
if (canDeleteImage && image) {
|
||||
dispatch(imageDeleted({ imageType: image.type, imageName: image.name }));
|
||||
}
|
||||
}, [dispatch, image, canDeleteImage]);
|
||||
|
||||
// Opens the alert dialog to check if user is sure they want to delete
|
||||
const handleInitiateDelete = useCallback(() => {
|
||||
if (shouldConfirmOnDelete) {
|
||||
onDeleteDialogOpen();
|
||||
} else {
|
||||
handleDelete();
|
||||
}
|
||||
};
|
||||
}, [handleDelete, onDeleteDialogOpen, shouldConfirmOnDelete]);
|
||||
|
||||
const handleDelete = () => {
|
||||
if (canDeleteImage && image) {
|
||||
dispatch(imageDeleted({ imageType: image.type, imageName: image.name }));
|
||||
}
|
||||
};
|
||||
const handleSelectImage = useCallback(() => {
|
||||
dispatch(imageSelected(image));
|
||||
}, [image, dispatch]);
|
||||
|
||||
const handleUsePrompt = () => {
|
||||
if (typeof image.metadata?.invokeai?.node?.prompt === 'string') {
|
||||
setBothPrompts(image.metadata?.invokeai?.node?.prompt);
|
||||
}
|
||||
toast({
|
||||
title: t('toast.promptSet'),
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
};
|
||||
const handleDragStart = useCallback(
|
||||
(e: DragEvent<HTMLDivElement>) => {
|
||||
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
},
|
||||
[image]
|
||||
);
|
||||
|
||||
const handleUseSeed = () => {
|
||||
typeof image.metadata.invokeai?.node?.seed === 'number' &&
|
||||
dispatch(setSeed(image.metadata.invokeai?.node?.seed));
|
||||
toast({
|
||||
title: t('toast.seedSet'),
|
||||
status: 'success',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
};
|
||||
// Recall parameters handlers
|
||||
const handleRecallPrompt = useCallback(() => {
|
||||
recallPrompt(image.metadata?.invokeai?.node?.prompt);
|
||||
}, [image, recallPrompt]);
|
||||
|
||||
const handleSendToImageToImage = () => {
|
||||
dispatch(initialImageSelected(image.name));
|
||||
};
|
||||
const handleRecallSeed = useCallback(() => {
|
||||
recallSeed(image.metadata.invokeai?.node?.seed);
|
||||
}, [image, recallSeed]);
|
||||
|
||||
const handleSendToImageToImage = useCallback(() => {
|
||||
sendToImageToImage(image);
|
||||
}, [image, sendToImageToImage]);
|
||||
|
||||
const handleRecallInitialImage = useCallback(() => {
|
||||
recallInitialImage(image.metadata.invokeai?.node?.image);
|
||||
}, [image, recallInitialImage]);
|
||||
|
||||
/**
|
||||
* TODO: the rest of these
|
||||
*/
|
||||
const handleSendToCanvas = () => {
|
||||
// dispatch(setInitialCanvasImage(image));
|
||||
|
||||
@ -205,41 +186,6 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
// });
|
||||
};
|
||||
|
||||
const handleUseInitialImage = async () => {
|
||||
// if (metadata.invokeai?.node?.image?.init_image_path) {
|
||||
// const response = await fetch(
|
||||
// metadata.invokeai?.node?.image?.init_image_path
|
||||
// );
|
||||
// if (response.ok) {
|
||||
// dispatch(setAllImageToImageParameters(metadata?.invokeai?.node));
|
||||
// toast({
|
||||
// title: t('toast.initialImageSet'),
|
||||
// status: 'success',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// toast({
|
||||
// title: t('toast.initialImageNotSet'),
|
||||
// description: t('toast.initialImageNotSetDesc'),
|
||||
// status: 'error',
|
||||
// duration: 2500,
|
||||
// isClosable: true,
|
||||
// });
|
||||
};
|
||||
|
||||
const handleSelectImage = () => {
|
||||
dispatch(imageSelected(image.name));
|
||||
};
|
||||
|
||||
const handleDragStart = (e: DragEvent<HTMLDivElement>) => {
|
||||
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
};
|
||||
|
||||
const handleLightBox = () => {
|
||||
// dispatch(setCurrentImage(image));
|
||||
// dispatch(setIsLightboxOpen(true));
|
||||
@ -254,21 +200,21 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
<ContextMenu<HTMLDivElement>
|
||||
menuProps={{ size: 'sm', isLazy: true }}
|
||||
renderMenu={() => (
|
||||
<MenuList>
|
||||
<MenuList sx={{ visibility: 'visible !important' }}>
|
||||
<MenuItem
|
||||
icon={<ExternalLinkIcon />}
|
||||
onClickCapture={handleOpenInNewTab}
|
||||
>
|
||||
{t('common.openInNewTab')}
|
||||
</MenuItem>
|
||||
{!disabledFeatures.includes('lightbox') && (
|
||||
{isLightboxEnabled && (
|
||||
<MenuItem icon={<FaExpand />} onClickCapture={handleLightBox}>
|
||||
{t('parameters.openInViewer')}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleUsePrompt}
|
||||
onClickCapture={handleRecallPrompt}
|
||||
isDisabled={image?.metadata?.invokeai?.node?.prompt === undefined}
|
||||
>
|
||||
{t('parameters.usePrompt')}
|
||||
@ -276,14 +222,14 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
|
||||
<MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleUseSeed}
|
||||
onClickCapture={handleRecallSeed}
|
||||
isDisabled={image?.metadata?.invokeai?.node?.seed === undefined}
|
||||
>
|
||||
{t('parameters.useSeed')}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<IoArrowUndoCircleOutline />}
|
||||
onClickCapture={handleUseInitialImage}
|
||||
onClickCapture={handleRecallInitialImage}
|
||||
isDisabled={image?.metadata?.invokeai?.node?.type !== 'img2img'}
|
||||
>
|
||||
{t('parameters.useInitImg')}
|
||||
@ -323,58 +269,48 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
userSelect="none"
|
||||
draggable={true}
|
||||
onDragStart={handleDragStart}
|
||||
onClick={handleSelectImage}
|
||||
ref={ref}
|
||||
sx={{
|
||||
padding: 2,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
transition: 'transform 0.2s ease-out',
|
||||
_hover: {
|
||||
cursor: 'pointer',
|
||||
|
||||
zIndex: 2,
|
||||
},
|
||||
_before: {
|
||||
content: '""',
|
||||
display: 'block',
|
||||
paddingBottom: '100%',
|
||||
},
|
||||
aspectRatio: '1/1',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
loading="lazy"
|
||||
objectFit={
|
||||
shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit
|
||||
}
|
||||
rounded="md"
|
||||
src={getUrl(thumbnail || url)}
|
||||
loading="lazy"
|
||||
fallback={<FaImage />}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%,-50%)',
|
||||
...(direction === 'rtl'
|
||||
? { insetInlineEnd: '50%' }
|
||||
: { insetInlineStart: '50%' }),
|
||||
}}
|
||||
/>
|
||||
<Flex
|
||||
onClick={handleSelectImage}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
insetInlineStart: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{isSelected && (
|
||||
{isSelected && (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
insetInlineStart: '0',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
filter={'drop-shadow(0px 0px 1rem black)'}
|
||||
as={FaCheck}
|
||||
sx={{
|
||||
width: '50%',
|
||||
@ -382,9 +318,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
||||
fill: 'ok.500',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
{isHovered && galleryImageMinimumWidth >= 64 && (
|
||||
</Flex>
|
||||
)}
|
||||
{isHovered && galleryImageMinimumWidth >= 100 && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
|
@ -1,5 +1,13 @@
|
||||
import { ButtonGroup, Flex, Grid, Icon, Image, Text } from '@chakra-ui/react';
|
||||
import { requestImages } from 'app/socketio/actions';
|
||||
import {
|
||||
Box,
|
||||
ButtonGroup,
|
||||
Flex,
|
||||
FlexProps,
|
||||
Grid,
|
||||
Icon,
|
||||
Text,
|
||||
forwardRef,
|
||||
} from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAICheckbox from 'common/components/IAICheckbox';
|
||||
@ -15,28 +23,33 @@ import {
|
||||
setShouldUseSingleGalleryColumn,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import { togglePinGalleryPanel } from 'features/ui/store/uiSlice';
|
||||
import { useOverlayScrollbars } from 'overlayscrollbars-react';
|
||||
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
ChangeEvent,
|
||||
PropsWithChildren,
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
||||
import { FaImage, FaUser, FaWrench } from 'react-icons/fa';
|
||||
import { MdPhotoLibrary } from 'react-icons/md';
|
||||
import HoverableImage from './HoverableImage';
|
||||
|
||||
import Scrollable from 'features/ui/components/common/Scrollable';
|
||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||
import {
|
||||
resultsAdapter,
|
||||
selectResultsAll,
|
||||
selectResultsTotal,
|
||||
} from '../store/resultsSlice';
|
||||
import { resultsAdapter } from '../store/resultsSlice';
|
||||
import {
|
||||
receivedResultImagesPage,
|
||||
receivedUploadImagesPage,
|
||||
} from 'services/thunks/gallery';
|
||||
import { selectUploadsAll, uploadsAdapter } from '../store/uploadsSlice';
|
||||
import { uploadsAdapter } from '../store/uploadsSlice';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { Virtuoso, VirtuosoGrid } from 'react-virtuoso';
|
||||
|
||||
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 290;
|
||||
|
||||
@ -49,7 +62,7 @@ const gallerySelector = createSelector(
|
||||
(uploads, results, gallery) => {
|
||||
const { currentCategory } = gallery;
|
||||
|
||||
return currentCategory === 'result'
|
||||
return currentCategory === 'results'
|
||||
? {
|
||||
images: resultsAdapter.getSelectors().selectAll(results),
|
||||
isLoading: results.isLoading,
|
||||
@ -68,32 +81,41 @@ const ImageGalleryContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const resizeObserverRef = useRef<HTMLDivElement>(null);
|
||||
const [shouldShouldIconButtons, setShouldShouldIconButtons] = useState(true);
|
||||
const rootRef = useRef(null);
|
||||
const [scroller, setScroller] = useState<HTMLElement | null>(null);
|
||||
const [initialize, osInstance] = useOverlayScrollbars({
|
||||
defer: true,
|
||||
options: {
|
||||
scrollbars: {
|
||||
visibility: 'auto',
|
||||
autoHide: 'leave',
|
||||
autoHideDelay: 1300,
|
||||
theme: 'os-theme-dark',
|
||||
},
|
||||
overflow: { x: 'hidden' },
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
// images,
|
||||
currentCategory,
|
||||
currentImageUuid,
|
||||
shouldPinGallery,
|
||||
galleryImageMinimumWidth,
|
||||
galleryGridTemplateColumns,
|
||||
galleryImageObjectFit,
|
||||
shouldAutoSwitchToNewImages,
|
||||
// areMoreImagesAvailable,
|
||||
shouldUseSingleGalleryColumn,
|
||||
selectedImage,
|
||||
} = useAppSelector(imageGallerySelector);
|
||||
|
||||
const { images, areMoreImagesAvailable, isLoading } =
|
||||
useAppSelector(gallerySelector);
|
||||
|
||||
// const handleClickLoadMore = () => {
|
||||
// dispatch(requestImages(currentCategory));
|
||||
// };
|
||||
const handleClickLoadMore = () => {
|
||||
if (currentCategory === 'result') {
|
||||
if (currentCategory === 'results') {
|
||||
dispatch(receivedResultImagesPage());
|
||||
}
|
||||
|
||||
if (currentCategory === 'user') {
|
||||
if (currentCategory === 'uploads') {
|
||||
dispatch(receivedUploadImagesPage());
|
||||
}
|
||||
};
|
||||
@ -129,6 +151,25 @@ const ImageGalleryContent = () => {
|
||||
return () => resizeObserver.disconnect(); // clean up
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const { current: root } = rootRef;
|
||||
if (scroller && root) {
|
||||
initialize({
|
||||
target: root,
|
||||
elements: {
|
||||
viewport: scroller,
|
||||
},
|
||||
});
|
||||
}
|
||||
return () => osInstance()?.destroy();
|
||||
}, [scroller, initialize, osInstance]);
|
||||
|
||||
const setScrollerRef = useCallback((ref: HTMLElement | Window | null) => {
|
||||
if (ref instanceof HTMLElement) {
|
||||
setScroller(ref);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Flex flexDirection="column" w="full" h="full" gap={4}>
|
||||
<Flex
|
||||
@ -147,34 +188,34 @@ const ImageGalleryContent = () => {
|
||||
<IAIIconButton
|
||||
aria-label={t('gallery.showGenerations')}
|
||||
tooltip={t('gallery.showGenerations')}
|
||||
isChecked={currentCategory === 'result'}
|
||||
isChecked={currentCategory === 'results'}
|
||||
role="radio"
|
||||
icon={<FaImage />}
|
||||
onClick={() => dispatch(setCurrentCategory('result'))}
|
||||
onClick={() => dispatch(setCurrentCategory('results'))}
|
||||
/>
|
||||
<IAIIconButton
|
||||
aria-label={t('gallery.showUploads')}
|
||||
tooltip={t('gallery.showUploads')}
|
||||
role="radio"
|
||||
isChecked={currentCategory === 'user'}
|
||||
isChecked={currentCategory === 'uploads'}
|
||||
icon={<FaUser />}
|
||||
onClick={() => dispatch(setCurrentCategory('user'))}
|
||||
onClick={() => dispatch(setCurrentCategory('uploads'))}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<IAIButton
|
||||
size="sm"
|
||||
isChecked={currentCategory === 'result'}
|
||||
onClick={() => dispatch(setCurrentCategory('result'))}
|
||||
isChecked={currentCategory === 'results'}
|
||||
onClick={() => dispatch(setCurrentCategory('results'))}
|
||||
flexGrow={1}
|
||||
>
|
||||
{t('gallery.generations')}
|
||||
</IAIButton>
|
||||
<IAIButton
|
||||
size="sm"
|
||||
isChecked={currentCategory === 'user'}
|
||||
onClick={() => dispatch(setCurrentCategory('user'))}
|
||||
isChecked={currentCategory === 'uploads'}
|
||||
onClick={() => dispatch(setCurrentCategory('uploads'))}
|
||||
flexGrow={1}
|
||||
>
|
||||
{t('gallery.uploads')}
|
||||
@ -241,65 +282,119 @@ const ImageGalleryContent = () => {
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Scrollable>
|
||||
<Flex direction="column" gap={2} h="full">
|
||||
{images.length || areMoreImagesAvailable ? (
|
||||
<>
|
||||
<Grid
|
||||
gap={2}
|
||||
style={{ gridTemplateColumns: galleryGridTemplateColumns }}
|
||||
>
|
||||
{images.map((image) => {
|
||||
const { name } = image;
|
||||
const isSelected = currentImageUuid === name;
|
||||
return (
|
||||
<HoverableImage
|
||||
key={`${name}-${image.thumbnail}`}
|
||||
image={image}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
<IAIButton
|
||||
onClick={handleClickLoadMore}
|
||||
isDisabled={!areMoreImagesAvailable}
|
||||
isLoading={isLoading}
|
||||
flexShrink={0}
|
||||
>
|
||||
{areMoreImagesAvailable
|
||||
? t('gallery.loadMore')
|
||||
: t('gallery.allImagesLoaded')}
|
||||
</IAIButton>
|
||||
</>
|
||||
) : (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 2,
|
||||
padding: 8,
|
||||
h: '100%',
|
||||
w: '100%',
|
||||
color: 'base.500',
|
||||
}}
|
||||
<Flex direction="column" gap={2} h="full">
|
||||
{images.length || areMoreImagesAvailable ? (
|
||||
<>
|
||||
<Box ref={rootRef} data-overlayscrollbars="" h="100%">
|
||||
{shouldUseSingleGalleryColumn ? (
|
||||
<Virtuoso
|
||||
style={{ height: '100%' }}
|
||||
data={images}
|
||||
scrollerRef={(ref) => setScrollerRef(ref)}
|
||||
itemContent={(index, image) => {
|
||||
const { name } = image;
|
||||
const isSelected = selectedImage?.name === name;
|
||||
|
||||
return (
|
||||
<Flex sx={{ pb: 2 }}>
|
||||
<HoverableImage
|
||||
key={`${name}-${image.thumbnail}`}
|
||||
image={image}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<VirtuosoGrid
|
||||
style={{ height: '100%' }}
|
||||
data={images}
|
||||
components={{
|
||||
Item: ItemContainer,
|
||||
List: ListContainer,
|
||||
}}
|
||||
scrollerRef={setScroller}
|
||||
itemContent={(index, image) => {
|
||||
const { name } = image;
|
||||
const isSelected = selectedImage?.name === name;
|
||||
|
||||
return (
|
||||
<HoverableImage
|
||||
key={`${name}-${image.thumbnail}`}
|
||||
image={image}
|
||||
isSelected={isSelected}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<IAIButton
|
||||
onClick={handleClickLoadMore}
|
||||
isDisabled={!areMoreImagesAvailable}
|
||||
isLoading={isLoading}
|
||||
flexShrink={0}
|
||||
>
|
||||
<Icon
|
||||
as={MdPhotoLibrary}
|
||||
sx={{
|
||||
w: 16,
|
||||
h: 16,
|
||||
}}
|
||||
/>
|
||||
<Text textAlign="center">{t('gallery.noImagesInGallery')}</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Scrollable>
|
||||
{areMoreImagesAvailable
|
||||
? t('gallery.loadMore')
|
||||
: t('gallery.allImagesLoaded')}
|
||||
</IAIButton>
|
||||
</>
|
||||
) : (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 2,
|
||||
padding: 8,
|
||||
h: '100%',
|
||||
w: '100%',
|
||||
color: 'base.500',
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
as={MdPhotoLibrary}
|
||||
sx={{
|
||||
w: 16,
|
||||
h: 16,
|
||||
}}
|
||||
/>
|
||||
<Text textAlign="center">{t('gallery.noImagesInGallery')}</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
ImageGalleryContent.displayName = 'ImageGalleryContent';
|
||||
export default ImageGalleryContent;
|
||||
type ItemContainerProps = PropsWithChildren & FlexProps;
|
||||
const ItemContainer = forwardRef((props: ItemContainerProps, ref) => (
|
||||
<Box className="item-container" ref={ref}>
|
||||
{props.children}
|
||||
</Box>
|
||||
));
|
||||
|
||||
type ListContainerProps = PropsWithChildren & FlexProps;
|
||||
const ListContainer = forwardRef((props: ListContainerProps, ref) => {
|
||||
const galleryImageMinimumWidth = useAppSelector(
|
||||
(state: RootState) => state.gallery.galleryImageMinimumWidth
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
{...props}
|
||||
className="list-container"
|
||||
ref={ref}
|
||||
sx={{
|
||||
gap: 2,
|
||||
gridTemplateColumns: `repeat(auto-fit, minmax(${galleryImageMinimumWidth}px, 1fr));`,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
|
||||
export default memo(ImageGalleryContent);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
||||
import {
|
||||
selectNextImage,
|
||||
selectPrevImage,
|
||||
// selectNextImage,
|
||||
// selectPrevImage,
|
||||
setGalleryImageMinimumWidth,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
@ -110,28 +110,6 @@ export const ImageGalleryPanel = () => {
|
||||
[shouldPinGallery]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
'left',
|
||||
() => {
|
||||
dispatch(selectPrevImage());
|
||||
},
|
||||
{
|
||||
enabled: !isStaging || activeTabName !== 'unifiedCanvas',
|
||||
},
|
||||
[isStaging, activeTabName]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
'right',
|
||||
() => {
|
||||
dispatch(selectNextImage());
|
||||
},
|
||||
{
|
||||
enabled: !isStaging || activeTabName !== 'unifiedCanvas',
|
||||
},
|
||||
[isStaging, activeTabName]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
'shift+g',
|
||||
() => {
|
||||
|
@ -159,6 +159,7 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
||||
_dark: {
|
||||
bg: 'blackAlpha.600',
|
||||
},
|
||||
overflow: 'scroll',
|
||||
}}
|
||||
>
|
||||
<Flex gap={2}>
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { ChakraProps, Flex, Grid, IconButton } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useState } from 'react';
|
||||
import { clamp, isEqual } from 'lodash-es';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
|
||||
import { gallerySelector } from '../store/gallerySelectors';
|
||||
import {
|
||||
GalleryCategory,
|
||||
selectNextImage,
|
||||
selectPrevImage,
|
||||
} from '../store/gallerySlice';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { imageSelected } from '../store/gallerySlice';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const nextPrevButtonTriggerAreaStyles: ChakraProps['sx'] = {
|
||||
height: '100%',
|
||||
@ -23,24 +21,47 @@ const nextPrevButtonStyles: ChakraProps['sx'] = {
|
||||
};
|
||||
|
||||
export const nextPrevImageButtonsSelector = createSelector(
|
||||
gallerySelector,
|
||||
(gallery) => {
|
||||
const { currentImage } = gallery;
|
||||
[(state: RootState) => state, gallerySelector],
|
||||
(state, gallery) => {
|
||||
const { selectedImage, currentCategory } = gallery;
|
||||
|
||||
const tempImages =
|
||||
gallery.categories[
|
||||
currentImage ? (currentImage.category as GalleryCategory) : 'result'
|
||||
].images;
|
||||
if (!selectedImage) {
|
||||
return {
|
||||
isOnFirstImage: true,
|
||||
isOnLastImage: true,
|
||||
};
|
||||
}
|
||||
|
||||
const currentImageIndex = tempImages.findIndex(
|
||||
(i) => i.uuid === gallery?.currentImage?.uuid
|
||||
const currentImageIndex = state[currentCategory].ids.findIndex(
|
||||
(i) => i === selectedImage.name
|
||||
);
|
||||
const imagesLength = tempImages.length;
|
||||
|
||||
const nextImageIndex = clamp(
|
||||
currentImageIndex + 1,
|
||||
0,
|
||||
state[currentCategory].ids.length - 1
|
||||
);
|
||||
|
||||
const prevImageIndex = clamp(
|
||||
currentImageIndex - 1,
|
||||
0,
|
||||
state[currentCategory].ids.length - 1
|
||||
);
|
||||
|
||||
const nextImageId = state[currentCategory].ids[nextImageIndex];
|
||||
const prevImageId = state[currentCategory].ids[prevImageIndex];
|
||||
|
||||
const nextImage = state[currentCategory].entities[nextImageId];
|
||||
const prevImage = state[currentCategory].entities[prevImageId];
|
||||
|
||||
const imagesLength = state[currentCategory].ids.length;
|
||||
|
||||
return {
|
||||
isOnFirstImage: currentImageIndex === 0,
|
||||
isOnLastImage:
|
||||
!isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1,
|
||||
nextImage,
|
||||
prevImage,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -54,34 +75,48 @@ const NextPrevImageButtons = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { isOnFirstImage, isOnLastImage } = useAppSelector(
|
||||
nextPrevImageButtonsSelector
|
||||
);
|
||||
const { isOnFirstImage, isOnLastImage, nextImage, prevImage } =
|
||||
useAppSelector(nextPrevImageButtonsSelector);
|
||||
|
||||
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const handleCurrentImagePreviewMouseOver = () => {
|
||||
const handleCurrentImagePreviewMouseOver = useCallback(() => {
|
||||
setShouldShowNextPrevButtons(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleCurrentImagePreviewMouseOut = () => {
|
||||
const handleCurrentImagePreviewMouseOut = useCallback(() => {
|
||||
setShouldShowNextPrevButtons(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleClickPrevButton = () => {
|
||||
dispatch(selectPrevImage());
|
||||
};
|
||||
const handlePrevImage = useCallback(() => {
|
||||
dispatch(imageSelected(prevImage));
|
||||
}, [dispatch, prevImage]);
|
||||
|
||||
const handleClickNextButton = () => {
|
||||
dispatch(selectNextImage());
|
||||
};
|
||||
const handleNextImage = useCallback(() => {
|
||||
dispatch(imageSelected(nextImage));
|
||||
}, [dispatch, nextImage]);
|
||||
|
||||
useHotkeys(
|
||||
'left',
|
||||
() => {
|
||||
handlePrevImage();
|
||||
},
|
||||
[prevImage]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
'right',
|
||||
() => {
|
||||
handleNextImage();
|
||||
},
|
||||
[nextImage]
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
justifyContent: 'space-between',
|
||||
zIndex: 1,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
pointerEvents: 'none',
|
||||
@ -100,7 +135,7 @@ const NextPrevImageButtons = () => {
|
||||
aria-label={t('accessibility.previousImage')}
|
||||
icon={<FaAngleLeft size={64} />}
|
||||
variant="unstyled"
|
||||
onClick={handleClickPrevButton}
|
||||
onClick={handlePrevImage}
|
||||
boxSize={16}
|
||||
sx={nextPrevButtonStyles}
|
||||
/>
|
||||
@ -119,7 +154,7 @@ const NextPrevImageButtons = () => {
|
||||
aria-label={t('accessibility.nextImage')}
|
||||
icon={<FaAngleRight size={64} />}
|
||||
variant="unstyled"
|
||||
onClick={handleClickNextButton}
|
||||
onClick={handleNextImage}
|
||||
boxSize={16}
|
||||
sx={nextPrevButtonStyles}
|
||||
/>
|
||||
|
@ -22,17 +22,22 @@ import {
|
||||
export const gallerySelector = (state: RootState) => state.gallery;
|
||||
|
||||
export const imageGallerySelector = createSelector(
|
||||
[gallerySelector, uiSelector, lightboxSelector, activeTabNameSelector],
|
||||
(gallery, ui, lightbox, activeTabName) => {
|
||||
[
|
||||
(state: RootState) => state,
|
||||
gallerySelector,
|
||||
uiSelector,
|
||||
lightboxSelector,
|
||||
activeTabNameSelector,
|
||||
],
|
||||
(state, gallery, ui, lightbox, activeTabName) => {
|
||||
const {
|
||||
categories,
|
||||
currentCategory,
|
||||
currentImageUuid,
|
||||
galleryImageMinimumWidth,
|
||||
galleryImageObjectFit,
|
||||
shouldAutoSwitchToNewImages,
|
||||
galleryWidth,
|
||||
shouldUseSingleGalleryColumn,
|
||||
selectedImage,
|
||||
} = gallery;
|
||||
|
||||
const { shouldPinGallery } = ui;
|
||||
@ -40,7 +45,6 @@ export const imageGallerySelector = createSelector(
|
||||
const { isLightboxOpen } = lightbox;
|
||||
|
||||
return {
|
||||
currentImageUuid,
|
||||
shouldPinGallery,
|
||||
galleryImageMinimumWidth,
|
||||
galleryImageObjectFit,
|
||||
@ -49,9 +53,7 @@ export const imageGallerySelector = createSelector(
|
||||
: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`,
|
||||
shouldAutoSwitchToNewImages,
|
||||
currentCategory,
|
||||
images: categories[currentCategory].images,
|
||||
areMoreImagesAvailable:
|
||||
categories[currentCategory].areMoreImagesAvailable,
|
||||
images: state[currentCategory].entities,
|
||||
galleryWidth,
|
||||
shouldEnableResize:
|
||||
isLightboxOpen ||
|
||||
@ -59,6 +61,7 @@ export const imageGallerySelector = createSelector(
|
||||
? false
|
||||
: true,
|
||||
shouldUseSingleGalleryColumn,
|
||||
selectedImage,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -69,16 +72,16 @@ export const imageGallerySelector = createSelector(
|
||||
);
|
||||
|
||||
export const selectedImageSelector = createSelector(
|
||||
[gallerySelector, selectResultsEntities, selectUploadsEntities],
|
||||
(gallery, allResults, allUploads) => {
|
||||
const selectedImageName = gallery.selectedImageName;
|
||||
[(state: RootState) => state, gallerySelector],
|
||||
(state, gallery) => {
|
||||
const selectedImage = gallery.selectedImage;
|
||||
|
||||
if (selectedImageName in allResults) {
|
||||
return allResults[selectedImageName];
|
||||
if (selectedImage?.type === 'results') {
|
||||
return selectResultsById(state, selectedImage.name);
|
||||
}
|
||||
|
||||
if (selectedImageName in allUploads) {
|
||||
return allUploads[selectedImageName];
|
||||
if (selectedImage?.type === 'uploads') {
|
||||
return selectUploadsById(state, selectedImage.name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -1,259 +1,47 @@
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import { invocationComplete } from 'services/events/actions';
|
||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
import { IRect } from 'konva/lib/types';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { isImageOutput } from 'services/types/guards';
|
||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||
import { imageUploaded } from 'services/thunks/image';
|
||||
|
||||
export type GalleryCategory = 'user' | 'result';
|
||||
|
||||
export type AddImagesPayload = {
|
||||
images: Array<InvokeAI._Image>;
|
||||
areMoreImagesAvailable: boolean;
|
||||
category: GalleryCategory;
|
||||
};
|
||||
import { SelectedImage } from 'features/parameters/store/generationSlice';
|
||||
|
||||
type GalleryImageObjectFitType = 'contain' | 'cover';
|
||||
|
||||
export type Gallery = {
|
||||
images: InvokeAI._Image[];
|
||||
latest_mtime?: number;
|
||||
earliest_mtime?: number;
|
||||
areMoreImagesAvailable: boolean;
|
||||
};
|
||||
|
||||
export interface GalleryState {
|
||||
/**
|
||||
* The selected image's unique name
|
||||
* Use `selectedImageSelector` to access the image
|
||||
* The selected image
|
||||
*/
|
||||
selectedImageName: string;
|
||||
/**
|
||||
* The currently selected image
|
||||
* @deprecated See `state.gallery.selectedImageName`
|
||||
*/
|
||||
currentImage?: InvokeAI._Image;
|
||||
/**
|
||||
* The currently selected image's uuid.
|
||||
* @deprecated See `state.gallery.selectedImageName`, use `selectedImageSelector` to access the image
|
||||
*/
|
||||
currentImageUuid: string;
|
||||
/**
|
||||
* The current progress image
|
||||
* @deprecated See `state.system.progressImage`
|
||||
*/
|
||||
intermediateImage?: InvokeAI._Image & {
|
||||
boundingBox?: IRect;
|
||||
generationMode?: InvokeTabName;
|
||||
};
|
||||
selectedImage?: SelectedImage;
|
||||
galleryImageMinimumWidth: number;
|
||||
galleryImageObjectFit: GalleryImageObjectFitType;
|
||||
shouldAutoSwitchToNewImages: boolean;
|
||||
categories: {
|
||||
user: Gallery;
|
||||
result: Gallery;
|
||||
};
|
||||
currentCategory: GalleryCategory;
|
||||
galleryWidth: number;
|
||||
shouldUseSingleGalleryColumn: boolean;
|
||||
currentCategory: 'results' | 'uploads';
|
||||
}
|
||||
|
||||
const initialState: GalleryState = {
|
||||
selectedImageName: '',
|
||||
currentImageUuid: '',
|
||||
selectedImage: undefined,
|
||||
galleryImageMinimumWidth: 64,
|
||||
galleryImageObjectFit: 'cover',
|
||||
shouldAutoSwitchToNewImages: true,
|
||||
currentCategory: 'result',
|
||||
categories: {
|
||||
user: {
|
||||
images: [],
|
||||
latest_mtime: undefined,
|
||||
earliest_mtime: undefined,
|
||||
areMoreImagesAvailable: true,
|
||||
},
|
||||
result: {
|
||||
images: [],
|
||||
latest_mtime: undefined,
|
||||
earliest_mtime: undefined,
|
||||
areMoreImagesAvailable: true,
|
||||
},
|
||||
},
|
||||
galleryWidth: 300,
|
||||
shouldUseSingleGalleryColumn: false,
|
||||
currentCategory: 'results',
|
||||
};
|
||||
|
||||
export const gallerySlice = createSlice({
|
||||
name: 'gallery',
|
||||
initialState,
|
||||
reducers: {
|
||||
imageSelected: (state, action: PayloadAction<string>) => {
|
||||
state.selectedImageName = action.payload;
|
||||
},
|
||||
setCurrentImage: (state, action: PayloadAction<InvokeAI._Image>) => {
|
||||
state.currentImage = action.payload;
|
||||
state.currentImageUuid = action.payload.uuid;
|
||||
},
|
||||
removeImage: (
|
||||
imageSelected: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.ImageDeletedResponse>
|
||||
action: PayloadAction<SelectedImage | undefined>
|
||||
) => {
|
||||
const { uuid, category } = action.payload;
|
||||
|
||||
const tempImages = state.categories[category as GalleryCategory].images;
|
||||
|
||||
const newImages = tempImages.filter((image) => image.uuid !== uuid);
|
||||
|
||||
if (uuid === state.currentImageUuid) {
|
||||
/**
|
||||
* We are deleting the currently selected image.
|
||||
*
|
||||
* We want the new currentl selected image to be under the cursor in the
|
||||
* gallery, so we need to do some fanagling. The currently selected image
|
||||
* is set by its UUID, not its index in the image list.
|
||||
*
|
||||
* Get the currently selected image's index.
|
||||
*/
|
||||
const imageToDeleteIndex = tempImages.findIndex(
|
||||
(image) => image.uuid === uuid
|
||||
);
|
||||
|
||||
/**
|
||||
* New current image needs to be in the same spot, but because the gallery
|
||||
* is sorted in reverse order, the new current image's index will actuall be
|
||||
* one less than the deleted image's index.
|
||||
*
|
||||
* Clamp the new index to ensure it is valid..
|
||||
*/
|
||||
const newCurrentImageIndex = clamp(
|
||||
imageToDeleteIndex,
|
||||
0,
|
||||
newImages.length - 1
|
||||
);
|
||||
|
||||
state.currentImage = newImages.length
|
||||
? newImages[newCurrentImageIndex]
|
||||
: undefined;
|
||||
|
||||
state.currentImageUuid = newImages.length
|
||||
? newImages[newCurrentImageIndex].uuid
|
||||
: '';
|
||||
}
|
||||
|
||||
state.categories[category as GalleryCategory].images = newImages;
|
||||
},
|
||||
addImage: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
image: InvokeAI._Image;
|
||||
category: GalleryCategory;
|
||||
}>
|
||||
) => {
|
||||
const { image: newImage, category } = action.payload;
|
||||
const { uuid, url, mtime } = newImage;
|
||||
|
||||
const tempCategory = state.categories[category as GalleryCategory];
|
||||
|
||||
// Do not add duplicate images
|
||||
if (tempCategory.images.find((i) => i.url === url && i.mtime === mtime)) {
|
||||
return;
|
||||
}
|
||||
|
||||
tempCategory.images.unshift(newImage);
|
||||
if (state.shouldAutoSwitchToNewImages) {
|
||||
state.currentImageUuid = uuid;
|
||||
state.currentImage = newImage;
|
||||
state.currentCategory = category;
|
||||
}
|
||||
state.intermediateImage = undefined;
|
||||
tempCategory.latest_mtime = mtime;
|
||||
},
|
||||
setIntermediateImage: (
|
||||
state,
|
||||
action: PayloadAction<
|
||||
InvokeAI._Image & {
|
||||
boundingBox?: IRect;
|
||||
generationMode?: InvokeTabName;
|
||||
}
|
||||
>
|
||||
) => {
|
||||
state.intermediateImage = action.payload;
|
||||
},
|
||||
clearIntermediateImage: (state) => {
|
||||
state.intermediateImage = undefined;
|
||||
},
|
||||
selectNextImage: (state) => {
|
||||
const { currentImage } = state;
|
||||
if (!currentImage) return;
|
||||
const tempImages =
|
||||
state.categories[currentImage.category as GalleryCategory].images;
|
||||
|
||||
if (currentImage) {
|
||||
const currentImageIndex = tempImages.findIndex(
|
||||
(i) => i.uuid === currentImage.uuid
|
||||
);
|
||||
if (currentImageIndex < tempImages.length - 1) {
|
||||
const newCurrentImage = tempImages[currentImageIndex + 1];
|
||||
state.currentImage = newCurrentImage;
|
||||
state.currentImageUuid = newCurrentImage.uuid;
|
||||
}
|
||||
}
|
||||
},
|
||||
selectPrevImage: (state) => {
|
||||
const { currentImage } = state;
|
||||
if (!currentImage) return;
|
||||
const tempImages =
|
||||
state.categories[currentImage.category as GalleryCategory].images;
|
||||
|
||||
if (currentImage) {
|
||||
const currentImageIndex = tempImages.findIndex(
|
||||
(i) => i.uuid === currentImage.uuid
|
||||
);
|
||||
if (currentImageIndex > 0) {
|
||||
const newCurrentImage = tempImages[currentImageIndex - 1];
|
||||
state.currentImage = newCurrentImage;
|
||||
state.currentImageUuid = newCurrentImage.uuid;
|
||||
}
|
||||
}
|
||||
},
|
||||
addGalleryImages: (state, action: PayloadAction<AddImagesPayload>) => {
|
||||
const { images, areMoreImagesAvailable, category } = action.payload;
|
||||
const tempImages = state.categories[category].images;
|
||||
|
||||
// const prevImages = category === 'user' ? state.userImages : state.resultImages
|
||||
|
||||
if (images.length > 0) {
|
||||
// Filter images that already exist in the gallery
|
||||
const newImages = images.filter(
|
||||
(newImage) =>
|
||||
!tempImages.find(
|
||||
(i) => i.url === newImage.url && i.mtime === newImage.mtime
|
||||
)
|
||||
);
|
||||
state.categories[category].images = tempImages
|
||||
.concat(newImages)
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
|
||||
if (!state.currentImage) {
|
||||
const newCurrentImage = images[0];
|
||||
state.currentImage = newCurrentImage;
|
||||
state.currentImageUuid = newCurrentImage.uuid;
|
||||
}
|
||||
|
||||
// keep track of the timestamps of latest and earliest images received
|
||||
state.categories[category].latest_mtime = images[0].mtime;
|
||||
state.categories[category].earliest_mtime =
|
||||
images[images.length - 1].mtime;
|
||||
}
|
||||
|
||||
if (areMoreImagesAvailable !== undefined) {
|
||||
state.categories[category].areMoreImagesAvailable =
|
||||
areMoreImagesAvailable;
|
||||
}
|
||||
state.selectedImage = action.payload;
|
||||
// TODO: if the user selects an image, disable the auto switch?
|
||||
// state.shouldAutoSwitchToNewImages = false;
|
||||
},
|
||||
setGalleryImageMinimumWidth: (state, action: PayloadAction<number>) => {
|
||||
state.galleryImageMinimumWidth = action.payload;
|
||||
@ -267,7 +55,10 @@ export const gallerySlice = createSlice({
|
||||
setShouldAutoSwitchToNewImages: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldAutoSwitchToNewImages = action.payload;
|
||||
},
|
||||
setCurrentCategory: (state, action: PayloadAction<GalleryCategory>) => {
|
||||
setCurrentCategory: (
|
||||
state,
|
||||
action: PayloadAction<'results' | 'uploads'>
|
||||
) => {
|
||||
state.currentCategory = action.payload;
|
||||
},
|
||||
setGalleryWidth: (state, action: PayloadAction<number>) => {
|
||||
@ -286,9 +77,11 @@ export const gallerySlice = createSlice({
|
||||
*/
|
||||
builder.addCase(invocationComplete, (state, action) => {
|
||||
const { data } = action.payload;
|
||||
if (isImageOutput(data.result)) {
|
||||
state.selectedImageName = data.result.image.image_name;
|
||||
state.intermediateImage = undefined;
|
||||
if (isImageOutput(data.result) && state.shouldAutoSwitchToNewImages) {
|
||||
state.selectedImage = {
|
||||
name: data.result.image.image_name,
|
||||
type: 'results',
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@ -299,27 +92,19 @@ export const gallerySlice = createSlice({
|
||||
const { response } = action.payload;
|
||||
|
||||
const uploadedImage = deserializeImageResponse(response);
|
||||
state.selectedImageName = uploadedImage.name;
|
||||
state.selectedImage = { name: uploadedImage.name, type: 'uploads' };
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
imageSelected,
|
||||
addImage,
|
||||
clearIntermediateImage,
|
||||
removeImage,
|
||||
setCurrentImage,
|
||||
addGalleryImages,
|
||||
setIntermediateImage,
|
||||
selectNextImage,
|
||||
selectPrevImage,
|
||||
setGalleryImageMinimumWidth,
|
||||
setGalleryImageObjectFit,
|
||||
setShouldAutoSwitchToNewImages,
|
||||
setCurrentCategory,
|
||||
setGalleryWidth,
|
||||
setShouldUseSingleGalleryColumn,
|
||||
setCurrentCategory,
|
||||
} = gallerySlice.actions;
|
||||
|
||||
export default gallerySlice.reducer;
|
||||
|
@ -5,7 +5,7 @@ import { ResultsState } from './resultsSlice';
|
||||
*
|
||||
* Currently denylisting results slice entirely, see persist config in store.ts
|
||||
*/
|
||||
const itemsToDenylist: (keyof ResultsState)[] = [];
|
||||
const itemsToDenylist: (keyof ResultsState)[] = ['isLoading'];
|
||||
|
||||
export const resultsDenylist = itemsToDenylist.map(
|
||||
(denylistItem) => `results.${denylistItem}`
|
||||
|
@ -65,7 +65,7 @@ const resultsSlice = createSlice({
|
||||
deserializeImageResponse(image)
|
||||
);
|
||||
|
||||
resultsAdapter.addMany(state, resultImages);
|
||||
resultsAdapter.setMany(state, resultImages);
|
||||
|
||||
state.page = page;
|
||||
state.pages = pages;
|
||||
@ -107,7 +107,7 @@ const resultsSlice = createSlice({
|
||||
},
|
||||
};
|
||||
|
||||
resultsAdapter.addOne(state, image);
|
||||
resultsAdapter.setOne(state, image);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { UploadsState } from './uploadsSlice';
|
||||
*
|
||||
* Currently denylisting uploads slice entirely, see persist config in store.ts
|
||||
*/
|
||||
const itemsToDenylist: (keyof UploadsState)[] = [];
|
||||
const itemsToDenylist: (keyof UploadsState)[] = ['isLoading'];
|
||||
|
||||
export const uploadsDenylist = itemsToDenylist.map(
|
||||
(denylistItem) => `uploads.${denylistItem}`
|
||||
|
@ -53,7 +53,7 @@ const uploadsSlice = createSlice({
|
||||
|
||||
const images = items.map((image) => deserializeImageResponse(image));
|
||||
|
||||
uploadsAdapter.addMany(state, images);
|
||||
uploadsAdapter.setMany(state, images);
|
||||
|
||||
state.page = page;
|
||||
state.pages = pages;
|
||||
@ -69,7 +69,7 @@ const uploadsSlice = createSlice({
|
||||
|
||||
const uploadedImage = deserializeImageResponse(response);
|
||||
|
||||
uploadsAdapter.addOne(state, uploadedImage);
|
||||
uploadsAdapter.setOne(state, uploadedImage);
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,6 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
|
||||
import { useGetUrl } from 'common/util/getUrl';
|
||||
import useGetImageByNameAndType from 'features/gallery/hooks/useGetImageByName';
|
||||
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
||||
import {
|
||||
clearInitialImage,
|
||||
initialImageSelected,
|
||||
@ -16,15 +15,13 @@ import { DragEvent, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ImageType } from 'services/api';
|
||||
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
|
||||
import { initialImageSelector } from 'features/parameters/store/generationSelectors';
|
||||
|
||||
const initialImagePreviewSelector = createSelector(
|
||||
[(state: RootState) => state],
|
||||
(state) => {
|
||||
const { initialImage } = state.generation;
|
||||
const image = selectResultsById(state, initialImage as string);
|
||||
|
||||
const selector = createSelector(
|
||||
[initialImageSelector],
|
||||
(initialImage) => {
|
||||
return {
|
||||
initialImage: image,
|
||||
initialImage,
|
||||
};
|
||||
},
|
||||
{ memoizeOptions: { resultEqualityCheck: isEqual } }
|
||||
@ -34,7 +31,7 @@ const InitialImagePreview = () => {
|
||||
const isImageToImageEnabled = useAppSelector(
|
||||
(state: RootState) => state.generation.isImageToImageEnabled
|
||||
);
|
||||
const { initialImage } = useAppSelector(initialImagePreviewSelector);
|
||||
const { initialImage } = useAppSelector(selector);
|
||||
const { getUrl } = useGetUrl();
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
@ -71,7 +68,7 @@ const InitialImagePreview = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(initialImageSelected(image.name));
|
||||
dispatch(initialImageSelected({ name, type }));
|
||||
},
|
||||
[getImageByNameAndType, dispatch]
|
||||
);
|
||||
@ -116,12 +113,7 @@ const InitialImagePreview = () => {
|
||||
</Flex>
|
||||
}
|
||||
/>
|
||||
{isLoaded && (
|
||||
<ImageToImageOverlay
|
||||
setIsLoaded={setIsLoaded}
|
||||
image={initialImage}
|
||||
/>
|
||||
)}
|
||||
{isLoaded && <ImageToImageOverlay image={initialImage} />}
|
||||
</Box>
|
||||
)}
|
||||
{!initialImage?.url && <SelectImagePlaceholder />}
|
||||
|
@ -15,9 +15,9 @@ const AnimatedImageToImagePanel = () => {
|
||||
<AnimatePresence>
|
||||
{isImageToImageEnabled && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scaleX: 0, width: 0 }}
|
||||
animate={{ opacity: 1, scaleX: 1, width: '28rem' }}
|
||||
exit={{ opacity: 0, scaleX: 0, width: 0 }}
|
||||
initial={{ opacity: 0, scale: 0, width: 0 }}
|
||||
animate={{ opacity: 1, scale: 1, width: '28rem' }}
|
||||
exit={{ opacity: 0, scale: 0, width: 0 }}
|
||||
transition={{ type: 'spring', bounce: 0, duration: 0.35 }}
|
||||
>
|
||||
<Box sx={{ h: 'full', w: 'full', pl: 4 }}>
|
||||
|
@ -13,17 +13,35 @@ const selector = createSelector(
|
||||
(generation, hotkeys, config) => {
|
||||
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
||||
config.sd.height;
|
||||
const { height } = generation;
|
||||
const { height, shouldFitToWidthHeight, isImageToImageEnabled } =
|
||||
generation;
|
||||
|
||||
const step = hotkeys.shift ? fineStep : coarseStep;
|
||||
|
||||
return { height, initial, min, sliderMax, inputMax, step };
|
||||
return {
|
||||
height,
|
||||
initial,
|
||||
min,
|
||||
sliderMax,
|
||||
inputMax,
|
||||
step,
|
||||
shouldFitToWidthHeight,
|
||||
isImageToImageEnabled,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const HeightSlider = () => {
|
||||
const { height, initial, min, sliderMax, inputMax, step } =
|
||||
useAppSelector(selector);
|
||||
const {
|
||||
height,
|
||||
initial,
|
||||
min,
|
||||
sliderMax,
|
||||
inputMax,
|
||||
step,
|
||||
shouldFitToWidthHeight,
|
||||
isImageToImageEnabled,
|
||||
} = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -40,6 +58,7 @@ const HeightSlider = () => {
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
isDisabled={!shouldFitToWidthHeight && isImageToImageEnabled}
|
||||
label={t('parameters.height')}
|
||||
value={height}
|
||||
min={min}
|
||||
|
@ -13,17 +13,34 @@ const selector = createSelector(
|
||||
(generation, hotkeys, config) => {
|
||||
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
||||
config.sd.width;
|
||||
const { width } = generation;
|
||||
const { width, shouldFitToWidthHeight, isImageToImageEnabled } = generation;
|
||||
|
||||
const step = hotkeys.shift ? fineStep : coarseStep;
|
||||
|
||||
return { width, initial, min, sliderMax, inputMax, step };
|
||||
return {
|
||||
width,
|
||||
initial,
|
||||
min,
|
||||
sliderMax,
|
||||
inputMax,
|
||||
step,
|
||||
shouldFitToWidthHeight,
|
||||
isImageToImageEnabled,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
const WidthSlider = () => {
|
||||
const { width, initial, min, sliderMax, inputMax, step } =
|
||||
useAppSelector(selector);
|
||||
const {
|
||||
width,
|
||||
initial,
|
||||
min,
|
||||
sliderMax,
|
||||
inputMax,
|
||||
step,
|
||||
shouldFitToWidthHeight,
|
||||
isImageToImageEnabled,
|
||||
} = useAppSelector(selector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@ -40,6 +57,7 @@ const WidthSlider = () => {
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
isDisabled={!shouldFitToWidthHeight && isImageToImageEnabled}
|
||||
label={t('parameters.width')}
|
||||
value={width}
|
||||
min={min}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
SystemState,
|
||||
cancelScheduled,
|
||||
cancelTypeChanged,
|
||||
CancelType,
|
||||
CancelStrategy,
|
||||
} from 'features/system/store/systemSlice';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useCallback, memo } from 'react';
|
||||
@ -87,7 +87,7 @@ const CancelButton = (
|
||||
const handleCancelTypeChanged = useCallback(
|
||||
(value: string | string[]) => {
|
||||
const newCancelType = Array.isArray(value) ? value[0] : value;
|
||||
dispatch(cancelTypeChanged(newCancelType as CancelType));
|
||||
dispatch(cancelTypeChanged(newCancelType as CancelStrategy));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||
import { generateImage } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton, { IAIButtonProps } from 'common/components/IAIButton';
|
||||
import IAIIconButton, {
|
||||
@ -8,10 +7,11 @@ import IAIIconButton, {
|
||||
} from 'common/components/IAIIconButton';
|
||||
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { useCallback } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FaPlay } from 'react-icons/fa';
|
||||
import { generateGraphBuilt, sessionCreated } from 'services/thunks/session';
|
||||
import { generateGraphBuilt } from 'services/thunks/session';
|
||||
|
||||
interface InvokeButton
|
||||
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
||||
@ -24,19 +24,16 @@ export default function InvokeButton(props: InvokeButton) {
|
||||
const { isReady } = useAppSelector(readinessSelector);
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
|
||||
const handleClickGenerate = () => {
|
||||
// dispatch(generateImage(activeTabName));
|
||||
const handleInvoke = useCallback(() => {
|
||||
dispatch(clampSymmetrySteps());
|
||||
dispatch(generateGraphBuilt());
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useHotkeys(
|
||||
['ctrl+enter', 'meta+enter'],
|
||||
() => {
|
||||
dispatch(clampSymmetrySteps());
|
||||
dispatch(generateImage(activeTabName));
|
||||
},
|
||||
handleInvoke,
|
||||
{
|
||||
enabled: () => isReady,
|
||||
preventDefault: true,
|
||||
@ -53,7 +50,7 @@ export default function InvokeButton(props: InvokeButton) {
|
||||
type="submit"
|
||||
icon={<FaPlay />}
|
||||
isDisabled={!isReady}
|
||||
onClick={handleClickGenerate}
|
||||
onClick={handleInvoke}
|
||||
flexGrow={1}
|
||||
w="100%"
|
||||
tooltip={t('parameters.invoke')}
|
||||
@ -66,7 +63,7 @@ export default function InvokeButton(props: InvokeButton) {
|
||||
aria-label={t('parameters.invoke')}
|
||||
type="submit"
|
||||
isDisabled={!isReady}
|
||||
onClick={handleClickGenerate}
|
||||
onClick={handleInvoke}
|
||||
flexGrow={1}
|
||||
w="100%"
|
||||
colorScheme="accent"
|
||||
|
@ -0,0 +1,67 @@
|
||||
import { Flex, Icon, Image } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { memo } from 'react';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector],
|
||||
(system) => {
|
||||
const { progressImage } = system;
|
||||
|
||||
return {
|
||||
progressImage,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const ProgressImage = () => {
|
||||
const { progressImage } = useAppSelector(selector);
|
||||
return progressImage ? (
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
draggable={false}
|
||||
src={progressImage.dataURL}
|
||||
width={progressImage.width}
|
||||
height={progressImage.height}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
objectFit: 'contain',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
height: 'auto',
|
||||
borderRadius: 'base',
|
||||
p: 2,
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
) : (
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Icon color="base.400" boxSize={32} as={FaImage} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ProgressImage);
|
@ -0,0 +1,160 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { memo } from 'react';
|
||||
import { FaStopwatch } from 'react-icons/fa';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import { CloseIcon } from '@chakra-ui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
floatingProgressImageMoved,
|
||||
floatingProgressImageResized,
|
||||
setShouldShowProgressImages,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { Rnd } from 'react-rnd';
|
||||
import { Rect } from 'features/ui/store/uiTypes';
|
||||
import { isEqual } from 'lodash';
|
||||
import ProgressImage from './ProgressImage';
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector, uiSelector],
|
||||
(system, ui) => {
|
||||
const { isProcessing } = system;
|
||||
const {
|
||||
floatingProgressImageRect,
|
||||
shouldShowProgressImages,
|
||||
shouldAutoShowProgressImages,
|
||||
} = ui;
|
||||
|
||||
const showProgressWindow =
|
||||
shouldAutoShowProgressImages && isProcessing
|
||||
? true
|
||||
: shouldShowProgressImages;
|
||||
|
||||
return {
|
||||
floatingProgressImageRect,
|
||||
showProgressWindow,
|
||||
};
|
||||
},
|
||||
{
|
||||
memoizeOptions: {
|
||||
resultEqualityCheck: isEqual,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const ProgressImagePreview = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { showProgressWindow, floatingProgressImageRect } =
|
||||
useAppSelector(selector);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
{' '}
|
||||
<IAIIconButton
|
||||
onClick={() =>
|
||||
dispatch(setShouldShowProgressImages(!showProgressWindow))
|
||||
}
|
||||
tooltip={t('ui.showProgressImages')}
|
||||
isChecked={showProgressWindow}
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
bottom: 4,
|
||||
insetInlineStart: 4,
|
||||
}}
|
||||
aria-label={t('ui.showProgressImages')}
|
||||
icon={<FaStopwatch />}
|
||||
/>
|
||||
{showProgressWindow && (
|
||||
<Rnd
|
||||
bounds="window"
|
||||
minHeight={200}
|
||||
minWidth={200}
|
||||
size={{
|
||||
width: floatingProgressImageRect.width,
|
||||
height: floatingProgressImageRect.height,
|
||||
}}
|
||||
position={{
|
||||
x: floatingProgressImageRect.x,
|
||||
y: floatingProgressImageRect.y,
|
||||
}}
|
||||
onDragStop={(e, d) => {
|
||||
dispatch(floatingProgressImageMoved({ x: d.x, y: d.y }));
|
||||
}}
|
||||
onResizeStop={(e, direction, ref, delta, position) => {
|
||||
const newRect: Partial<Rect> = {};
|
||||
|
||||
if (ref.style.width) {
|
||||
newRect.width = ref.style.width;
|
||||
}
|
||||
if (ref.style.height) {
|
||||
newRect.height = ref.style.height;
|
||||
}
|
||||
if (position.x) {
|
||||
newRect.x = position.x;
|
||||
}
|
||||
if (position.x) {
|
||||
newRect.y = position.y;
|
||||
}
|
||||
|
||||
dispatch(floatingProgressImageResized(newRect));
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
position: 'relative',
|
||||
w: 'full',
|
||||
h: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDir: 'column',
|
||||
boxShadow: 'dark-lg',
|
||||
bg: 'base.800',
|
||||
borderRadius: 'base',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
w: 'full',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
p: 1.5,
|
||||
pl: 4,
|
||||
pr: 3,
|
||||
bg: 'base.700',
|
||||
borderTopRadius: 'base',
|
||||
}}
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
userSelect: 'none',
|
||||
cursor: 'move',
|
||||
}}
|
||||
>
|
||||
<Text fontSize="sm" fontWeight={500}>
|
||||
Progress Images
|
||||
</Text>
|
||||
</Flex>
|
||||
<IAIIconButton
|
||||
onClick={() => dispatch(setShouldShowProgressImages(false))}
|
||||
aria-label={t('ui.hideProgressImages')}
|
||||
size="xs"
|
||||
icon={<CloseIcon />}
|
||||
variant="ghost"
|
||||
/>
|
||||
</Flex>
|
||||
<ProgressImage />
|
||||
</Flex>
|
||||
</Rnd>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ProgressImagePreview);
|
@ -1,5 +1,4 @@
|
||||
import { Box, FormControl, Textarea } from '@chakra-ui/react';
|
||||
import { generateImage } from 'app/socketio/actions';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { ChangeEvent, KeyboardEvent, useRef } from 'react';
|
||||
@ -8,6 +7,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||
import {
|
||||
GenerationState,
|
||||
clampSymmetrySteps,
|
||||
setPrompt,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
@ -15,6 +15,7 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { generateGraphBuilt } from 'services/thunks/session';
|
||||
|
||||
const promptInputSelector = createSelector(
|
||||
[(state: RootState) => state.generation, activeTabNameSelector],
|
||||
@ -36,7 +37,7 @@ const promptInputSelector = createSelector(
|
||||
*/
|
||||
const PromptInput = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { prompt, activeTabName } = useAppSelector(promptInputSelector);
|
||||
const { prompt } = useAppSelector(promptInputSelector);
|
||||
const { isReady } = useAppSelector(readinessSelector);
|
||||
|
||||
const promptRef = useRef<HTMLTextAreaElement>(null);
|
||||
@ -58,7 +59,8 @@ const PromptInput = () => {
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && e.shiftKey === false && isReady) {
|
||||
e.preventDefault();
|
||||
dispatch(generateImage(activeTabName));
|
||||
dispatch(clampSymmetrySteps());
|
||||
dispatch(generateGraphBuilt());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,129 @@
|
||||
import { UseToastOptions, useToast } from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { isFinite, isString } from 'lodash-es';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useSetBothPrompts from './usePrompt';
|
||||
import { initialImageSelected, setSeed } from '../store/generationSlice';
|
||||
import { isImage, isImageField } from 'services/types/guards';
|
||||
import { NUMPY_RAND_MAX } from 'app/constants';
|
||||
|
||||
export const useParameters = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const toast = useToast();
|
||||
const { t } = useTranslation();
|
||||
const setBothPrompts = useSetBothPrompts();
|
||||
|
||||
/**
|
||||
* Sets prompt with toast
|
||||
*/
|
||||
const recallPrompt = useCallback(
|
||||
(prompt: unknown) => {
|
||||
if (!isString(prompt)) {
|
||||
toast({
|
||||
title: t('toast.promptNotSet'),
|
||||
description: t('toast.promptNotSetDesc'),
|
||||
status: 'warning',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setBothPrompts(prompt);
|
||||
toast({
|
||||
title: t('toast.promptSet'),
|
||||
status: 'info',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
[t, toast, setBothPrompts]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets seed with toast
|
||||
*/
|
||||
const recallSeed = useCallback(
|
||||
(seed: unknown) => {
|
||||
const s = Number(seed);
|
||||
if (!isFinite(s) || (isFinite(s) && !(s >= 0 && s <= NUMPY_RAND_MAX))) {
|
||||
toast({
|
||||
title: t('toast.seedNotSet'),
|
||||
description: t('toast.seedNotSetDesc'),
|
||||
status: 'warning',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(setSeed(s));
|
||||
toast({
|
||||
title: t('toast.seedSet'),
|
||||
status: 'info',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
[t, toast, dispatch]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets initial image with toast
|
||||
*/
|
||||
const recallInitialImage = useCallback(
|
||||
async (image: unknown) => {
|
||||
if (!isImageField(image)) {
|
||||
toast({
|
||||
title: t('toast.initialImageNotSet'),
|
||||
description: t('toast.initialImageNotSetDesc'),
|
||||
status: 'warning',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(
|
||||
initialImageSelected({ name: image.image_name, type: image.image_type })
|
||||
);
|
||||
toast({
|
||||
title: t('toast.initialImageSet'),
|
||||
status: 'info',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
[t, toast, dispatch]
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets image as initial image with toast
|
||||
*/
|
||||
const sendToImageToImage = useCallback(
|
||||
(image: unknown) => {
|
||||
if (!isImage(image)) {
|
||||
toast({
|
||||
title: t('toast.imageNotLoaded'),
|
||||
description: t('toast.imageNotLoadedDesc'),
|
||||
status: 'warning',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(initialImageSelected({ name: image.name, type: image.type }));
|
||||
toast({
|
||||
title: t('toast.sentToImageToImage'),
|
||||
status: 'info',
|
||||
duration: 2500,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
[t, toast, dispatch]
|
||||
);
|
||||
|
||||
return { recallPrompt, recallSeed, recallInitialImage, sendToImageToImage };
|
||||
};
|
@ -1,10 +1,6 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
||||
import {
|
||||
selectResultsById,
|
||||
selectResultsEntities,
|
||||
} from 'features/gallery/store/resultsSlice';
|
||||
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
||||
import { selectUploadsById } from 'features/gallery/store/uploadsSlice';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
@ -25,11 +21,14 @@ export const mayGenerateMultipleImagesSelector = createSelector(
|
||||
export const initialImageSelector = createSelector(
|
||||
[(state: RootState) => state, generationSelector],
|
||||
(state, generation) => {
|
||||
const { initialImage: initialImageName } = generation;
|
||||
const { initialImage } = generation;
|
||||
|
||||
return (
|
||||
selectResultsById(state, initialImageName as string) ??
|
||||
selectUploadsById(state, initialImageName as string)
|
||||
);
|
||||
if (initialImage?.type === 'results') {
|
||||
return selectResultsById(state, initialImage.name);
|
||||
}
|
||||
|
||||
if (initialImage?.type === 'uploads') {
|
||||
return selectUploadsById(state, initialImage.name);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -5,13 +5,19 @@ import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
|
||||
import promptToString from 'common/util/promptToString';
|
||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { ImageField, ImageType } from 'services/api';
|
||||
|
||||
export type SelectedImage = {
|
||||
name: string;
|
||||
type: ImageType;
|
||||
};
|
||||
|
||||
export interface GenerationState {
|
||||
cfgScale: number;
|
||||
height: number;
|
||||
img2imgStrength: number;
|
||||
infillMethod: string;
|
||||
initialImage?: InvokeAI._Image | string; // can be an Image or url
|
||||
initialImage?: SelectedImage; // can be an Image or url
|
||||
iterations: number;
|
||||
maskPath: string;
|
||||
perlin: number;
|
||||
@ -345,7 +351,7 @@ export const generationSlice = createSlice({
|
||||
setVerticalSymmetrySteps: (state, action: PayloadAction<number>) => {
|
||||
state.verticalSymmetrySteps = action.payload;
|
||||
},
|
||||
initialImageSelected: (state, action: PayloadAction<string>) => {
|
||||
initialImageSelected: (state, action: PayloadAction<SelectedImage>) => {
|
||||
state.initialImage = action.payload;
|
||||
state.isImageToImageEnabled = true;
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { emptyTempFolder } from 'app/socketio/actions';
|
||||
// import { emptyTempFolder } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
|
@ -17,7 +17,7 @@ import React from 'react';
|
||||
|
||||
import SearchModels from './SearchModels';
|
||||
|
||||
import { addNewModel } from 'app/socketio/actions';
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
import { InvokeDiffusersModelConfigProps } from 'app/types/invokeai';
|
||||
import { addNewModel } from 'app/socketio/actions';
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAIInput from 'common/components/IAIInput';
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
VStack,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { addNewModel } from 'app/socketio/actions';
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
import { Field, Formik } from 'formik';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
|
||||
import { Flex, FormControl, FormLabel, Text, VStack } from '@chakra-ui/react';
|
||||
|
||||
import { addNewModel } from 'app/socketio/actions';
|
||||
// import { addNewModel } from 'app/socketio/actions';
|
||||
import { Field, Formik } from 'formik';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
Tooltip,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { mergeDiffusersModels } from 'app/socketio/actions';
|
||||
// import { mergeDiffusersModels } from 'app/socketio/actions';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
UnorderedList,
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { convertToDiffusers } from 'app/socketio/actions';
|
||||
// import { convertToDiffusers } from 'app/socketio/actions';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
||||
import { Box, Button, Flex, Spacer, Text, Tooltip } from '@chakra-ui/react';
|
||||
import { ModelStatus } from 'app/types/invokeai';
|
||||
import { deleteModel, requestModelChange } from 'app/socketio/actions';
|
||||
// import { deleteModel, requestModelChange } from 'app/socketio/actions';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||
|
@ -20,7 +20,7 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { FaSearch, FaTrash } from 'react-icons/fa';
|
||||
|
||||
import { addNewModel, searchForModels } from 'app/socketio/actions';
|
||||
// import { addNewModel, searchForModels } from 'app/socketio/actions';
|
||||
import {
|
||||
setFoundModels,
|
||||
setSearchFolder,
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {
|
||||
ChakraProps,
|
||||
Flex,
|
||||
Grid,
|
||||
Heading,
|
||||
Modal,
|
||||
ModalBody,
|
||||
@ -14,64 +13,57 @@ import {
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { IN_PROGRESS_IMAGE_TYPES } from 'app/constants';
|
||||
import { RootState } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAISelect from 'common/components/IAISelect';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import {
|
||||
consoleLogLevelChanged,
|
||||
InProgressImageType,
|
||||
setEnableImageDebugging,
|
||||
setSaveIntermediatesInterval,
|
||||
setShouldConfirmOnDelete,
|
||||
setShouldDisplayGuides,
|
||||
setShouldDisplayInProgressType,
|
||||
shouldLogToConsoleChanged,
|
||||
SystemState,
|
||||
} from 'features/system/store/systemSlice';
|
||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||
import {
|
||||
setShouldAutoShowProgressImages,
|
||||
setShouldUseCanvasBetaLayout,
|
||||
setShouldUseSliders,
|
||||
} from 'features/ui/store/uiSlice';
|
||||
import { UIState } from 'features/ui/store/uiTypes';
|
||||
import { isEqual, map } from 'lodash-es';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { persistor } from 'app/store/persistor';
|
||||
import { ChangeEvent, cloneElement, ReactElement, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { InvokeLogLevel, VALID_LOG_LEVELS } from 'app/logging/useLogger';
|
||||
import { VALID_LOG_LEVELS } from 'app/logging/useLogger';
|
||||
import { LogLevelName } from 'roarr';
|
||||
import { F } from 'ts-toolbelt';
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector, uiSelector],
|
||||
(system: SystemState, ui: UIState) => {
|
||||
const {
|
||||
shouldDisplayInProgressType,
|
||||
shouldConfirmOnDelete,
|
||||
shouldDisplayGuides,
|
||||
model_list,
|
||||
saveIntermediatesInterval,
|
||||
enableImageDebugging,
|
||||
consoleLogLevel,
|
||||
shouldLogToConsole,
|
||||
} = system;
|
||||
|
||||
const { shouldUseCanvasBetaLayout, shouldUseSliders } = ui;
|
||||
const {
|
||||
shouldUseCanvasBetaLayout,
|
||||
shouldUseSliders,
|
||||
shouldAutoShowProgressImages,
|
||||
} = ui;
|
||||
|
||||
return {
|
||||
shouldDisplayInProgressType,
|
||||
shouldConfirmOnDelete,
|
||||
shouldDisplayGuides,
|
||||
models: map(model_list, (_model, key) => key),
|
||||
saveIntermediatesInterval,
|
||||
enableImageDebugging,
|
||||
shouldUseCanvasBetaLayout,
|
||||
shouldUseSliders,
|
||||
shouldAutoShowProgressImages,
|
||||
consoleLogLevel,
|
||||
shouldLogToConsole,
|
||||
};
|
||||
@ -104,8 +96,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const steps = useAppSelector((state: RootState) => state.generation.steps);
|
||||
|
||||
const {
|
||||
isOpen: isSettingsModalOpen,
|
||||
onOpen: onSettingsModalOpen,
|
||||
@ -119,13 +109,12 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
shouldDisplayInProgressType,
|
||||
shouldConfirmOnDelete,
|
||||
shouldDisplayGuides,
|
||||
saveIntermediatesInterval,
|
||||
enableImageDebugging,
|
||||
shouldUseCanvasBetaLayout,
|
||||
shouldUseSliders,
|
||||
shouldAutoShowProgressImages,
|
||||
consoleLogLevel,
|
||||
shouldLogToConsole,
|
||||
} = useAppSelector(selector);
|
||||
@ -134,18 +123,12 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
* Resets localstorage, then opens a secondary modal informing user to
|
||||
* refresh their browser.
|
||||
* */
|
||||
const handleClickResetWebUI = () => {
|
||||
const handleClickResetWebUI = useCallback(() => {
|
||||
persistor.purge().then(() => {
|
||||
onSettingsModalClose();
|
||||
onRefreshModalOpen();
|
||||
});
|
||||
};
|
||||
|
||||
const handleChangeIntermediateSteps = (value: number) => {
|
||||
if (value > steps) value = steps;
|
||||
if (value < 1) value = 1;
|
||||
dispatch(setSaveIntermediatesInterval(value));
|
||||
};
|
||||
}, [onSettingsModalClose, onRefreshModalOpen]);
|
||||
|
||||
const handleLogLevelChanged = useCallback(
|
||||
(e: ChangeEvent<HTMLSelectElement>) => {
|
||||
@ -182,32 +165,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
<Flex sx={modalSectionStyles}>
|
||||
<Heading size="sm">{t('settings.general')}</Heading>
|
||||
|
||||
<IAISelect
|
||||
horizontal
|
||||
spaceEvenly
|
||||
label={t('settings.displayInProgress')}
|
||||
validValues={IN_PROGRESS_IMAGE_TYPES}
|
||||
value={shouldDisplayInProgressType}
|
||||
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
|
||||
dispatch(
|
||||
setShouldDisplayInProgressType(
|
||||
e.target.value as InProgressImageType
|
||||
)
|
||||
)
|
||||
}
|
||||
/>
|
||||
{shouldDisplayInProgressType === 'full-res' && (
|
||||
<IAINumberInput
|
||||
label={t('settings.saveSteps')}
|
||||
min={1}
|
||||
max={steps}
|
||||
step={1}
|
||||
onChange={handleChangeIntermediateSteps}
|
||||
value={saveIntermediatesInterval}
|
||||
width="auto"
|
||||
textAlign="center"
|
||||
/>
|
||||
)}
|
||||
<IAISwitch
|
||||
label={t('settings.confirmOnDelete')}
|
||||
isChecked={shouldConfirmOnDelete}
|
||||
@ -236,6 +193,13 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
||||
dispatch(setShouldUseSliders(e.target.checked))
|
||||
}
|
||||
/>
|
||||
<IAISwitch
|
||||
label={t('settings.autoShowProgress')}
|
||||
isChecked={shouldAutoShowProgressImages}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||
dispatch(setShouldAutoShowProgressImages(e.target.checked))
|
||||
}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex sx={modalSectionStyles}>
|
||||
|
@ -1,22 +1,34 @@
|
||||
import { Text, Tooltip } from '@chakra-ui/react';
|
||||
import { Flex, Icon, Text } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { errorSeen, SystemState } from 'features/system/store/systemSlice';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { systemSelector } from '../store/systemSelectors';
|
||||
import { ResourceKey } from 'i18next';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { FaCircle } from 'react-icons/fa';
|
||||
import { useHoverDirty } from 'react-use';
|
||||
|
||||
const statusIndicatorSelector = createSelector(
|
||||
systemSelector,
|
||||
(system: SystemState) => {
|
||||
(system) => {
|
||||
const {
|
||||
isConnected,
|
||||
isProcessing,
|
||||
statusTranslationKey,
|
||||
currentIteration,
|
||||
totalIterations,
|
||||
currentStatusHasSteps,
|
||||
} = system;
|
||||
|
||||
return {
|
||||
isConnected: system.isConnected,
|
||||
isProcessing: system.isProcessing,
|
||||
currentIteration: system.currentIteration,
|
||||
totalIterations: system.totalIterations,
|
||||
currentStatus: system.currentStatus,
|
||||
hasError: system.hasError,
|
||||
wasErrorSeen: system.wasErrorSeen,
|
||||
isConnected,
|
||||
isProcessing,
|
||||
currentIteration,
|
||||
totalIterations,
|
||||
statusTranslationKey,
|
||||
currentStatusHasSteps,
|
||||
};
|
||||
},
|
||||
{
|
||||
@ -30,64 +42,69 @@ const StatusIndicator = () => {
|
||||
isProcessing,
|
||||
currentIteration,
|
||||
totalIterations,
|
||||
currentStatus,
|
||||
hasError,
|
||||
wasErrorSeen,
|
||||
statusTranslationKey,
|
||||
currentStatusHasSteps,
|
||||
} = useAppSelector(statusIndicatorSelector);
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef(null);
|
||||
|
||||
let statusIdentifier;
|
||||
|
||||
if (isConnected && !hasError) {
|
||||
statusIdentifier = 'ok';
|
||||
} else {
|
||||
statusIdentifier = 'error';
|
||||
}
|
||||
|
||||
let statusMessage = currentStatus;
|
||||
|
||||
if (isProcessing) {
|
||||
statusIdentifier = 'working';
|
||||
}
|
||||
|
||||
if (statusMessage)
|
||||
const statusColorScheme = useMemo(() => {
|
||||
if (isProcessing) {
|
||||
if (totalIterations > 1) {
|
||||
statusMessage = `${t(
|
||||
statusMessage as keyof typeof t
|
||||
)} (${currentIteration}/${totalIterations})`;
|
||||
}
|
||||
return 'working';
|
||||
}
|
||||
|
||||
const tooltipLabel =
|
||||
hasError && !wasErrorSeen
|
||||
? 'Click to clear, check logs for details'
|
||||
: undefined;
|
||||
|
||||
const statusIndicatorCursor =
|
||||
hasError && !wasErrorSeen ? 'pointer' : 'initial';
|
||||
|
||||
const handleClickStatusIndicator = () => {
|
||||
if (hasError || !wasErrorSeen) {
|
||||
dispatch(errorSeen());
|
||||
if (isConnected) {
|
||||
return 'ok';
|
||||
}
|
||||
};
|
||||
|
||||
return 'error';
|
||||
}, [isProcessing, isConnected]);
|
||||
|
||||
const iterationsText = useMemo(() => {
|
||||
if (!(currentIteration && totalIterations)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return ` (${currentIteration}/${totalIterations})`;
|
||||
}, [currentIteration, totalIterations]);
|
||||
|
||||
const isHovered = useHoverDirty(ref);
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltipLabel}>
|
||||
<Text
|
||||
cursor={statusIndicatorCursor}
|
||||
onClick={handleClickStatusIndicator}
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontWeight: '600',
|
||||
color: `${statusIdentifier}.400`,
|
||||
}}
|
||||
>
|
||||
{t(statusMessage as keyof typeof t)}
|
||||
</Text>
|
||||
</Tooltip>
|
||||
<Flex ref={ref} h="full" px={2} alignItems="center" gap={5}>
|
||||
<AnimatePresence>
|
||||
{isHovered && (
|
||||
<motion.div
|
||||
key="statusText"
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
transition: { duration: 0.15 },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { delay: 0.8 },
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
fontWeight: '600',
|
||||
color: `${statusColorScheme}.400`,
|
||||
pb: '1px',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{t(statusTranslationKey as ResourceKey)}
|
||||
{iterationsText}
|
||||
</Text>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<Icon as={FaCircle} boxSize="0.5rem" color={`${statusColorScheme}.400`} />
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ExpandedIndex, UseToastOptions } from '@chakra-ui/react';
|
||||
import { UseToastOptions } from '@chakra-ui/react';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import * as InvokeAI from 'app/types/invokeai';
|
||||
import {
|
||||
generatorProgress,
|
||||
graphExecutionStateComplete,
|
||||
invocationComplete,
|
||||
invocationError,
|
||||
invocationStarted,
|
||||
@ -13,7 +14,6 @@ import {
|
||||
socketUnsubscribed,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import i18n from 'i18n';
|
||||
import { ProgressImage } from 'services/events/types';
|
||||
import { initialImageSelected } from 'features/parameters/store/generationSlice';
|
||||
import { makeToast } from '../hooks/useToastWatcher';
|
||||
@ -22,53 +22,29 @@ 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';
|
||||
|
||||
export type LogLevel = 'info' | 'warning' | 'error';
|
||||
export type CancelStrategy = 'immediate' | 'scheduled';
|
||||
|
||||
export interface LogEntry {
|
||||
timestamp: string;
|
||||
level: LogLevel;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface Log {
|
||||
[index: number]: LogEntry;
|
||||
}
|
||||
|
||||
export type InProgressImageType = 'none' | 'full-res' | 'latents';
|
||||
|
||||
export type CancelType = 'immediate' | 'scheduled';
|
||||
|
||||
export interface SystemState
|
||||
extends InvokeAI.SystemStatus,
|
||||
InvokeAI.SystemConfig {
|
||||
shouldDisplayInProgressType: InProgressImageType;
|
||||
shouldShowLogViewer: boolean;
|
||||
export interface SystemState {
|
||||
isGFPGANAvailable: boolean;
|
||||
isESRGANAvailable: boolean;
|
||||
isConnected: boolean;
|
||||
socketId: string;
|
||||
isProcessing: boolean;
|
||||
shouldConfirmOnDelete: boolean;
|
||||
openAccordions: ExpandedIndex;
|
||||
currentStep: number;
|
||||
totalSteps: number;
|
||||
currentIteration: number;
|
||||
totalIterations: number;
|
||||
currentStatus: string;
|
||||
currentStatusHasSteps: boolean;
|
||||
shouldDisplayGuides: boolean;
|
||||
wasErrorSeen: boolean;
|
||||
isCancelable: boolean;
|
||||
saveIntermediatesInterval: number;
|
||||
enableImageDebugging: boolean;
|
||||
toastQueue: UseToastOptions[];
|
||||
searchFolder: string | null;
|
||||
foundModels: InvokeAI.FoundModel[] | null;
|
||||
openModel: string | null;
|
||||
cancelOptions: {
|
||||
cancelType: CancelType;
|
||||
cancelAfter: number | null;
|
||||
};
|
||||
/**
|
||||
* The current progress image
|
||||
*/
|
||||
@ -80,7 +56,7 @@ export interface SystemState
|
||||
/**
|
||||
* Cancel strategy
|
||||
*/
|
||||
cancelType: CancelType;
|
||||
cancelType: CancelStrategy;
|
||||
/**
|
||||
* Whether or not a scheduled cancelation is pending
|
||||
*/
|
||||
@ -102,47 +78,28 @@ export interface SystemState
|
||||
*/
|
||||
consoleLogLevel: InvokeLogLevel;
|
||||
shouldLogToConsole: boolean;
|
||||
statusTranslationKey: TFuncKey;
|
||||
canceledSession: string;
|
||||
}
|
||||
|
||||
const initialSystemState: SystemState = {
|
||||
isConnected: false,
|
||||
isProcessing: false,
|
||||
shouldShowLogViewer: false,
|
||||
shouldDisplayInProgressType: 'latents',
|
||||
shouldDisplayGuides: true,
|
||||
isGFPGANAvailable: true,
|
||||
isESRGANAvailable: true,
|
||||
socketId: '',
|
||||
shouldConfirmOnDelete: true,
|
||||
openAccordions: [0],
|
||||
currentStep: 0,
|
||||
totalSteps: 0,
|
||||
currentIteration: 0,
|
||||
totalIterations: 0,
|
||||
currentStatus: i18n.isInitialized
|
||||
? i18n.t('common.statusDisconnected')
|
||||
: 'Disconnected',
|
||||
currentStatusHasSteps: false,
|
||||
model: '',
|
||||
model_id: '',
|
||||
model_hash: '',
|
||||
app_id: '',
|
||||
app_version: '',
|
||||
model_list: {},
|
||||
infill_methods: [],
|
||||
hasError: false,
|
||||
wasErrorSeen: true,
|
||||
isCancelable: true,
|
||||
saveIntermediatesInterval: 5,
|
||||
enableImageDebugging: false,
|
||||
toastQueue: [],
|
||||
searchFolder: null,
|
||||
foundModels: null,
|
||||
openModel: null,
|
||||
cancelOptions: {
|
||||
cancelType: 'immediate',
|
||||
cancelAfter: null,
|
||||
},
|
||||
progressImage: null,
|
||||
sessionId: null,
|
||||
cancelType: 'immediate',
|
||||
@ -152,29 +109,21 @@ const initialSystemState: SystemState = {
|
||||
wasSchemaParsed: false,
|
||||
consoleLogLevel: 'error',
|
||||
shouldLogToConsole: true,
|
||||
statusTranslationKey: 'common.statusDisconnected',
|
||||
canceledSession: '',
|
||||
};
|
||||
|
||||
export const systemSlice = createSlice({
|
||||
name: 'system',
|
||||
initialState: initialSystemState,
|
||||
reducers: {
|
||||
setShouldDisplayInProgressType: (
|
||||
state,
|
||||
action: PayloadAction<InProgressImageType>
|
||||
) => {
|
||||
state.shouldDisplayInProgressType = action.payload;
|
||||
},
|
||||
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
||||
state.isProcessing = action.payload;
|
||||
},
|
||||
setCurrentStatus: (state, action: PayloadAction<string>) => {
|
||||
state.currentStatus = action.payload;
|
||||
},
|
||||
setSystemStatus: (state, action: PayloadAction<InvokeAI.SystemStatus>) => {
|
||||
return { ...state, ...action.payload };
|
||||
setCurrentStatus: (state, action: PayloadAction<TFuncKey>) => {
|
||||
state.statusTranslationKey = action.payload;
|
||||
},
|
||||
errorOccurred: (state) => {
|
||||
state.hasError = true;
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = true;
|
||||
state.currentStep = 0;
|
||||
@ -182,18 +131,7 @@ export const systemSlice = createSlice({
|
||||
state.currentIteration = 0;
|
||||
state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStatus = i18n.t('common.statusError');
|
||||
state.wasErrorSeen = false;
|
||||
},
|
||||
errorSeen: (state) => {
|
||||
state.hasError = false;
|
||||
state.wasErrorSeen = true;
|
||||
state.currentStatus = state.isConnected
|
||||
? i18n.t('common.statusConnected')
|
||||
: i18n.t('common.statusDisconnected');
|
||||
},
|
||||
setShouldShowLogViewer: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldShowLogViewer = action.payload;
|
||||
state.statusTranslationKey = 'common.statusError';
|
||||
},
|
||||
setIsConnected: (state, action: PayloadAction<boolean>) => {
|
||||
state.isConnected = action.payload;
|
||||
@ -204,23 +142,10 @@ export const systemSlice = createSlice({
|
||||
state.currentIteration = 0;
|
||||
state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.hasError = false;
|
||||
},
|
||||
setSocketId: (state, action: PayloadAction<string>) => {
|
||||
state.socketId = action.payload;
|
||||
},
|
||||
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldConfirmOnDelete = action.payload;
|
||||
},
|
||||
setOpenAccordions: (state, action: PayloadAction<ExpandedIndex>) => {
|
||||
state.openAccordions = action.payload;
|
||||
},
|
||||
setSystemConfig: (state, action: PayloadAction<InvokeAI.SystemConfig>) => {
|
||||
return {
|
||||
...state,
|
||||
...action.payload,
|
||||
};
|
||||
},
|
||||
setShouldDisplayGuides: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldDisplayGuides = action.payload;
|
||||
},
|
||||
@ -232,7 +157,7 @@ export const systemSlice = createSlice({
|
||||
state.currentIteration = 0;
|
||||
state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStatus = i18n.t('common.statusProcessingCanceled');
|
||||
state.statusTranslationKey = 'common.statusProcessingCanceled';
|
||||
},
|
||||
generationRequested: (state) => {
|
||||
state.isProcessing = true;
|
||||
@ -242,38 +167,29 @@ export const systemSlice = createSlice({
|
||||
state.currentIteration = 0;
|
||||
state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStatus = i18n.t('common.statusPreparing');
|
||||
},
|
||||
setModelList: (
|
||||
state,
|
||||
action: PayloadAction<InvokeAI.ModelList | Record<string, never>>
|
||||
) => {
|
||||
state.model_list = action.payload;
|
||||
state.statusTranslationKey = 'common.statusPreparing';
|
||||
},
|
||||
setIsCancelable: (state, action: PayloadAction<boolean>) => {
|
||||
state.isCancelable = action.payload;
|
||||
},
|
||||
modelChangeRequested: (state) => {
|
||||
state.currentStatus = i18n.t('common.statusLoadingModel');
|
||||
state.statusTranslationKey = 'common.statusLoadingModel';
|
||||
state.isCancelable = false;
|
||||
state.isProcessing = true;
|
||||
state.currentStatusHasSteps = false;
|
||||
},
|
||||
modelConvertRequested: (state) => {
|
||||
state.currentStatus = i18n.t('common.statusConvertingModel');
|
||||
state.statusTranslationKey = 'common.statusConvertingModel';
|
||||
state.isCancelable = false;
|
||||
state.isProcessing = true;
|
||||
state.currentStatusHasSteps = false;
|
||||
},
|
||||
modelMergingRequested: (state) => {
|
||||
state.currentStatus = i18n.t('common.statusMergingModels');
|
||||
state.statusTranslationKey = 'common.statusMergingModels';
|
||||
state.isCancelable = false;
|
||||
state.isProcessing = true;
|
||||
state.currentStatusHasSteps = false;
|
||||
},
|
||||
setSaveIntermediatesInterval: (state, action: PayloadAction<number>) => {
|
||||
state.saveIntermediatesInterval = action.payload;
|
||||
},
|
||||
setEnableImageDebugging: (state, action: PayloadAction<boolean>) => {
|
||||
state.enableImageDebugging = action.payload;
|
||||
},
|
||||
@ -283,9 +199,12 @@ export const systemSlice = createSlice({
|
||||
clearToastQueue: (state) => {
|
||||
state.toastQueue = [];
|
||||
},
|
||||
setProcessingIndeterminateTask: (state, action: PayloadAction<string>) => {
|
||||
setProcessingIndeterminateTask: (
|
||||
state,
|
||||
action: PayloadAction<TFuncKey>
|
||||
) => {
|
||||
state.isProcessing = true;
|
||||
state.currentStatus = action.payload;
|
||||
state.statusTranslationKey = action.payload;
|
||||
state.currentStatusHasSteps = false;
|
||||
},
|
||||
setSearchFolder: (state, action: PayloadAction<string | null>) => {
|
||||
@ -300,12 +219,6 @@ export const systemSlice = createSlice({
|
||||
setOpenModel: (state, action: PayloadAction<string | null>) => {
|
||||
state.openModel = action.payload;
|
||||
},
|
||||
setCancelType: (state, action: PayloadAction<CancelType>) => {
|
||||
state.cancelOptions.cancelType = action.payload;
|
||||
},
|
||||
setCancelAfter: (state, action: PayloadAction<number | null>) => {
|
||||
state.cancelOptions.cancelAfter = action.payload;
|
||||
},
|
||||
/**
|
||||
* A cancel was scheduled
|
||||
*/
|
||||
@ -321,7 +234,7 @@ export const systemSlice = createSlice({
|
||||
/**
|
||||
* The cancel type was changed
|
||||
*/
|
||||
cancelTypeChanged: (state, action: PayloadAction<CancelType>) => {
|
||||
cancelTypeChanged: (state, action: PayloadAction<CancelStrategy>) => {
|
||||
state.cancelType = action.payload;
|
||||
},
|
||||
/**
|
||||
@ -343,6 +256,7 @@ export const systemSlice = createSlice({
|
||||
*/
|
||||
builder.addCase(socketSubscribed, (state, action) => {
|
||||
state.sessionId = action.payload.sessionId;
|
||||
state.canceledSession = '';
|
||||
});
|
||||
|
||||
/**
|
||||
@ -357,9 +271,15 @@ export const systemSlice = createSlice({
|
||||
*/
|
||||
builder.addCase(socketConnected, (state, action) => {
|
||||
const { timestamp } = action.payload;
|
||||
|
||||
state.isConnected = true;
|
||||
state.currentStatus = i18n.t('common.statusConnected');
|
||||
state.isCancelable = true;
|
||||
state.isProcessing = false;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
state.currentIteration = 0;
|
||||
state.totalIterations = 0;
|
||||
state.statusTranslationKey = 'common.statusConnected';
|
||||
});
|
||||
|
||||
/**
|
||||
@ -369,17 +289,28 @@ export const systemSlice = createSlice({
|
||||
const { timestamp } = action.payload;
|
||||
|
||||
state.isConnected = false;
|
||||
state.currentStatus = i18n.t('common.statusDisconnected');
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = true;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
// state.currentIteration = 0;
|
||||
// state.totalIterations = 0;
|
||||
state.statusTranslationKey = 'common.statusDisconnected';
|
||||
});
|
||||
|
||||
/**
|
||||
* Invocation Started
|
||||
*/
|
||||
builder.addCase(invocationStarted, (state) => {
|
||||
state.isProcessing = true;
|
||||
builder.addCase(invocationStarted, (state, action) => {
|
||||
state.isCancelable = true;
|
||||
state.isProcessing = true;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStatus = i18n.t('common.statusGenerating');
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
// state.currentIteration = 0;
|
||||
// state.totalIterations = 0;
|
||||
state.statusTranslationKey = 'common.statusGenerating';
|
||||
});
|
||||
|
||||
/**
|
||||
@ -395,10 +326,15 @@ export const systemSlice = createSlice({
|
||||
graph_execution_state_id,
|
||||
} = action.payload.data;
|
||||
|
||||
state.isProcessing = true;
|
||||
state.isCancelable = true;
|
||||
// state.currentIteration = 0;
|
||||
// state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = true;
|
||||
state.currentStep = step + 1; // TODO: step starts at -1, think this is a bug
|
||||
state.totalSteps = total_steps;
|
||||
state.progressImage = progress_image ?? null;
|
||||
state.statusTranslationKey = 'common.statusGenerating';
|
||||
});
|
||||
|
||||
/**
|
||||
@ -407,11 +343,17 @@ export const systemSlice = createSlice({
|
||||
builder.addCase(invocationComplete, (state, action) => {
|
||||
const { data, timestamp } = action.payload;
|
||||
|
||||
state.isProcessing = false;
|
||||
// state.currentIteration = 0;
|
||||
// state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
state.progressImage = null;
|
||||
state.currentStatus = i18n.t('common.statusProcessingComplete');
|
||||
state.statusTranslationKey = 'common.statusProcessingComplete';
|
||||
|
||||
if (state.canceledSession === data.graph_execution_state_id) {
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = true;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@ -420,12 +362,17 @@ export const systemSlice = createSlice({
|
||||
builder.addCase(invocationError, (state, action) => {
|
||||
const { data, timestamp } = action.payload;
|
||||
|
||||
state.wasErrorSeen = true;
|
||||
state.progressImage = null;
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = true;
|
||||
// state.currentIteration = 0;
|
||||
// state.totalIterations = 0;
|
||||
state.currentStatusHasSteps = false;
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
state.statusTranslationKey = 'common.statusError';
|
||||
|
||||
state.toastQueue.push(
|
||||
makeToast({ title: i18n.t('toast.serverError'), status: 'error' })
|
||||
makeToast({ title: t('toast.serverError'), status: 'error' })
|
||||
);
|
||||
});
|
||||
|
||||
@ -434,7 +381,7 @@ export const systemSlice = createSlice({
|
||||
*/
|
||||
|
||||
builder.addCase(sessionInvoked.pending, (state) => {
|
||||
state.currentStatus = i18n.t('common.statusPreparing');
|
||||
state.statusTranslationKey = 'common.statusPreparing';
|
||||
});
|
||||
|
||||
/**
|
||||
@ -443,23 +390,38 @@ export const systemSlice = createSlice({
|
||||
builder.addCase(sessionCanceled.fulfilled, (state, action) => {
|
||||
const { timestamp } = action.payload;
|
||||
|
||||
state.canceledSession = action.meta.arg.sessionId;
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = false;
|
||||
state.isCancelScheduled = false;
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
state.progressImage = null;
|
||||
state.statusTranslationKey = 'common.statusConnected';
|
||||
|
||||
state.toastQueue.push(
|
||||
makeToast({ title: i18n.t('toast.canceled'), status: 'warning' })
|
||||
makeToast({ title: t('toast.canceled'), status: 'warning' })
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Session Canceled
|
||||
*/
|
||||
builder.addCase(graphExecutionStateComplete, (state, action) => {
|
||||
const { timestamp } = action.payload;
|
||||
|
||||
state.isProcessing = false;
|
||||
state.isCancelable = false;
|
||||
state.isCancelScheduled = false;
|
||||
state.currentStep = 0;
|
||||
state.totalSteps = 0;
|
||||
state.statusTranslationKey = 'common.statusConnected';
|
||||
});
|
||||
|
||||
/**
|
||||
* Initial Image Selected
|
||||
*/
|
||||
builder.addCase(initialImageSelected, (state) => {
|
||||
state.toastQueue.push(makeToast(i18n.t('toast.sentToImageToImage')));
|
||||
state.toastQueue.push(makeToast(t('toast.sentToImageToImage')));
|
||||
});
|
||||
|
||||
/**
|
||||
@ -479,26 +441,17 @@ export const systemSlice = createSlice({
|
||||
});
|
||||
|
||||
export const {
|
||||
setShouldDisplayInProgressType,
|
||||
setIsProcessing,
|
||||
setShouldShowLogViewer,
|
||||
setIsConnected,
|
||||
setSocketId,
|
||||
setShouldConfirmOnDelete,
|
||||
setOpenAccordions,
|
||||
setSystemStatus,
|
||||
setCurrentStatus,
|
||||
setSystemConfig,
|
||||
setShouldDisplayGuides,
|
||||
processingCanceled,
|
||||
errorOccurred,
|
||||
errorSeen,
|
||||
setModelList,
|
||||
setIsCancelable,
|
||||
modelChangeRequested,
|
||||
modelConvertRequested,
|
||||
modelMergingRequested,
|
||||
setSaveIntermediatesInterval,
|
||||
setEnableImageDebugging,
|
||||
generationRequested,
|
||||
addToast,
|
||||
@ -507,8 +460,6 @@ export const {
|
||||
setSearchFolder,
|
||||
setFoundModels,
|
||||
setOpenModel,
|
||||
setCancelType,
|
||||
setCancelAfter,
|
||||
cancelScheduled,
|
||||
scheduledCancelAborted,
|
||||
cancelTypeChanged,
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import CurrentImageDisplay from 'features/gallery/components/CurrentImageDisplay';
|
||||
import ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
|
||||
|
||||
const GenerateContent = () => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: 'base',
|
||||
|
@ -3,7 +3,7 @@ import { UIState } from './uiTypes';
|
||||
/**
|
||||
* UI slice persist denylist
|
||||
*/
|
||||
const itemsToDenylist: (keyof UIState)[] = [];
|
||||
const itemsToDenylist: (keyof UIState)[] = ['floatingProgressImageRect'];
|
||||
|
||||
export const uiDenylist = itemsToDenylist.map(
|
||||
(denylistItem) => `ui.${denylistItem}`
|
||||
|
@ -2,7 +2,7 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { setActiveTabReducer } from './extraReducers';
|
||||
import { InvokeTabName, tabMap } from './tabMap';
|
||||
import { AddNewModelType, UIState } from './uiTypes';
|
||||
import { AddNewModelType, Coordinates, Rect, UIState } from './uiTypes';
|
||||
|
||||
const initialUIState: UIState = {
|
||||
activeTab: 0,
|
||||
@ -21,6 +21,9 @@ const initialUIState: UIState = {
|
||||
openLinearAccordionItems: [],
|
||||
openGenerateAccordionItems: [],
|
||||
openUnifiedCanvasAccordionItems: [],
|
||||
floatingProgressImageRect: { x: 0, y: 0, width: 0, height: 0 },
|
||||
shouldShowProgressImages: false,
|
||||
shouldAutoShowProgressImages: false,
|
||||
};
|
||||
|
||||
const initialState: UIState = initialUIState;
|
||||
@ -105,6 +108,30 @@ export const uiSlice = createSlice({
|
||||
state.openUnifiedCanvasAccordionItems = action.payload;
|
||||
}
|
||||
},
|
||||
floatingProgressImageMoved: (state, action: PayloadAction<Coordinates>) => {
|
||||
state.floatingProgressImageRect = {
|
||||
...state.floatingProgressImageRect,
|
||||
...action.payload,
|
||||
};
|
||||
},
|
||||
floatingProgressImageResized: (
|
||||
state,
|
||||
action: PayloadAction<Partial<Rect>>
|
||||
) => {
|
||||
state.floatingProgressImageRect = {
|
||||
...state.floatingProgressImageRect,
|
||||
...action.payload,
|
||||
};
|
||||
},
|
||||
setShouldShowProgressImages: (state, action: PayloadAction<boolean>) => {
|
||||
state.shouldShowProgressImages = action.payload;
|
||||
},
|
||||
setShouldAutoShowProgressImages: (
|
||||
state,
|
||||
action: PayloadAction<boolean>
|
||||
) => {
|
||||
state.shouldAutoShowProgressImages = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -128,6 +155,10 @@ export const {
|
||||
toggleParametersPanel,
|
||||
toggleGalleryPanel,
|
||||
openAccordionItemsChanged,
|
||||
floatingProgressImageMoved,
|
||||
floatingProgressImageResized,
|
||||
setShouldShowProgressImages,
|
||||
setShouldAutoShowProgressImages,
|
||||
} = uiSlice.actions;
|
||||
|
||||
export default uiSlice.reducer;
|
||||
|
@ -1,7 +1,17 @@
|
||||
import { InvokeTabName } from './tabMap';
|
||||
|
||||
export type AddNewModelType = 'ckpt' | 'diffusers' | null;
|
||||
|
||||
export type Coordinates = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type Dimensions = {
|
||||
width: number | string;
|
||||
height: number | string;
|
||||
};
|
||||
|
||||
export type Rect = Coordinates & Dimensions;
|
||||
|
||||
export interface UIState {
|
||||
activeTab: number;
|
||||
currentTheme: string;
|
||||
@ -19,4 +29,7 @@ export interface UIState {
|
||||
openLinearAccordionItems: number[];
|
||||
openGenerateAccordionItems: number[];
|
||||
openUnifiedCanvasAccordionItems: number[];
|
||||
floatingProgressImageRect: Rect;
|
||||
shouldShowProgressImages: boolean;
|
||||
shouldAutoShowProgressImages: boolean;
|
||||
}
|
||||
|
@ -22,15 +22,13 @@ export interface OnCancel {
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
readonly [Symbol.toStringTag]!: string;
|
||||
|
||||
private _isResolved: boolean;
|
||||
private _isRejected: boolean;
|
||||
private _isCancelled: boolean;
|
||||
private readonly _cancelHandlers: (() => void)[];
|
||||
private readonly _promise: Promise<T>;
|
||||
private _resolve?: (value: T | PromiseLike<T>) => void;
|
||||
private _reject?: (reason?: any) => void;
|
||||
#isResolved: boolean;
|
||||
#isRejected: boolean;
|
||||
#isCancelled: boolean;
|
||||
readonly #cancelHandlers: (() => void)[];
|
||||
readonly #promise: Promise<T>;
|
||||
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||
#reject?: (reason?: any) => void;
|
||||
|
||||
constructor(
|
||||
executor: (
|
||||
@ -39,78 +37,82 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this._isResolved = false;
|
||||
this._isRejected = false;
|
||||
this._isCancelled = false;
|
||||
this._cancelHandlers = [];
|
||||
this._promise = new Promise<T>((resolve, reject) => {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
this.#isResolved = false;
|
||||
this.#isRejected = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._isResolved = true;
|
||||
this._resolve?.(value);
|
||||
this.#isResolved = true;
|
||||
this.#resolve?.(value);
|
||||
};
|
||||
|
||||
const onReject = (reason?: any): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._isRejected = true;
|
||||
this._reject?.(reason);
|
||||
this.#isRejected = true;
|
||||
this.#reject?.(reason);
|
||||
};
|
||||
|
||||
const onCancel = (cancelHandler: () => void): void => {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._cancelHandlers.push(cancelHandler);
|
||||
this.#cancelHandlers.push(cancelHandler);
|
||||
};
|
||||
|
||||
Object.defineProperty(onCancel, 'isResolved', {
|
||||
get: (): boolean => this._isResolved,
|
||||
get: (): boolean => this.#isResolved,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isRejected', {
|
||||
get: (): boolean => this._isRejected,
|
||||
get: (): boolean => this.#isRejected,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this._isCancelled,
|
||||
get: (): boolean => this.#isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag]() {
|
||||
return "Cancellable Promise";
|
||||
}
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this._promise.then(onFulfilled, onRejected);
|
||||
return this.#promise.then(onFulfilled, onRejected);
|
||||
}
|
||||
|
||||
public catch<TResult = never>(
|
||||
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
||||
): Promise<T | TResult> {
|
||||
return this._promise.catch(onRejected);
|
||||
return this.#promise.catch(onRejected);
|
||||
}
|
||||
|
||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this._promise.finally(onFinally);
|
||||
return this.#promise.finally(onFinally);
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this._isCancelled = true;
|
||||
if (this._cancelHandlers.length) {
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this._cancelHandlers) {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
cancelHandler();
|
||||
}
|
||||
} catch (error) {
|
||||
@ -118,11 +120,11 @@ export class CancelablePromise<T> implements Promise<T> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._cancelHandlers.length = 0;
|
||||
this._reject?.(new CancelError('Request aborted'));
|
||||
this.#cancelHandlers.length = 0;
|
||||
this.#reject?.(new CancelError('Request aborted'));
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return this._isCancelled;
|
||||
return this.#isCancelled;
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,9 @@ export type { PasteImageInvocation } from './models/PasteImageInvocation';
|
||||
export type { PromptOutput } from './models/PromptOutput';
|
||||
export type { RandomRangeInvocation } from './models/RandomRangeInvocation';
|
||||
export type { RangeInvocation } from './models/RangeInvocation';
|
||||
export type { ResizeLatentsInvocation } from './models/ResizeLatentsInvocation';
|
||||
export type { RestoreFaceInvocation } from './models/RestoreFaceInvocation';
|
||||
export type { ScaleLatentsInvocation } from './models/ScaleLatentsInvocation';
|
||||
export type { ShowImageInvocation } from './models/ShowImageInvocation';
|
||||
export type { SubtractInvocation } from './models/SubtractInvocation';
|
||||
export type { TextToImageInvocation } from './models/TextToImageInvocation';
|
||||
@ -119,7 +121,9 @@ export { $PasteImageInvocation } from './schemas/$PasteImageInvocation';
|
||||
export { $PromptOutput } from './schemas/$PromptOutput';
|
||||
export { $RandomRangeInvocation } from './schemas/$RandomRangeInvocation';
|
||||
export { $RangeInvocation } from './schemas/$RangeInvocation';
|
||||
export { $ResizeLatentsInvocation } from './schemas/$ResizeLatentsInvocation';
|
||||
export { $RestoreFaceInvocation } from './schemas/$RestoreFaceInvocation';
|
||||
export { $ScaleLatentsInvocation } from './schemas/$ScaleLatentsInvocation';
|
||||
export { $ShowImageInvocation } from './schemas/$ShowImageInvocation';
|
||||
export { $SubtractInvocation } from './schemas/$SubtractInvocation';
|
||||
export { $TextToImageInvocation } from './schemas/$TextToImageInvocation';
|
||||
|
@ -25,7 +25,9 @@ import type { ParamIntInvocation } from './ParamIntInvocation';
|
||||
import type { PasteImageInvocation } from './PasteImageInvocation';
|
||||
import type { RandomRangeInvocation } from './RandomRangeInvocation';
|
||||
import type { RangeInvocation } from './RangeInvocation';
|
||||
import type { ResizeLatentsInvocation } from './ResizeLatentsInvocation';
|
||||
import type { RestoreFaceInvocation } from './RestoreFaceInvocation';
|
||||
import type { ScaleLatentsInvocation } from './ScaleLatentsInvocation';
|
||||
import type { ShowImageInvocation } from './ShowImageInvocation';
|
||||
import type { SubtractInvocation } from './SubtractInvocation';
|
||||
import type { TextToImageInvocation } from './TextToImageInvocation';
|
||||
@ -40,7 +42,7 @@ export type Graph = {
|
||||
/**
|
||||
* The nodes in this graph
|
||||
*/
|
||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation)>;
|
||||
nodes?: Record<string, (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation)>;
|
||||
/**
|
||||
* The connections between nodes and their fields in this graph
|
||||
*/
|
||||
|
@ -17,10 +17,6 @@ export type LatentsToLatentsInvocation = {
|
||||
* The prompt to generate an image from
|
||||
*/
|
||||
prompt?: string;
|
||||
/**
|
||||
* The seed to use (-1 for a random seed)
|
||||
*/
|
||||
seed?: number;
|
||||
/**
|
||||
* The noise to use
|
||||
*/
|
||||
@ -29,14 +25,6 @@ export type LatentsToLatentsInvocation = {
|
||||
* The number of steps to use to generate the image
|
||||
*/
|
||||
steps?: number;
|
||||
/**
|
||||
* The width of the resulting image
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* The height of the resulting image
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* The Classifier-Free Guidance, higher values may result in a result closer to the prompt
|
||||
*/
|
||||
|
@ -0,0 +1,37 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { LatentsField } from './LatentsField';
|
||||
|
||||
/**
|
||||
* Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8.
|
||||
*/
|
||||
export type ResizeLatentsInvocation = {
|
||||
/**
|
||||
* The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
id: string;
|
||||
type?: 'lresize';
|
||||
/**
|
||||
* The latents to resize
|
||||
*/
|
||||
latents?: LatentsField;
|
||||
/**
|
||||
* The width to resize to (px)
|
||||
*/
|
||||
width: number;
|
||||
/**
|
||||
* The height to resize to (px)
|
||||
*/
|
||||
height: number;
|
||||
/**
|
||||
* The interpolation mode
|
||||
*/
|
||||
mode?: 'nearest' | 'linear' | 'bilinear' | 'bicubic' | 'trilinear' | 'area' | 'nearest-exact';
|
||||
/**
|
||||
* Whether or not to antialias (applied in bilinear and bicubic modes only)
|
||||
*/
|
||||
antialias?: boolean;
|
||||
};
|
||||
|
@ -0,0 +1,33 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { LatentsField } from './LatentsField';
|
||||
|
||||
/**
|
||||
* Scales latents by a given factor.
|
||||
*/
|
||||
export type ScaleLatentsInvocation = {
|
||||
/**
|
||||
* The id of this node. Must be unique among all nodes.
|
||||
*/
|
||||
id: string;
|
||||
type?: 'lscale';
|
||||
/**
|
||||
* The latents to scale
|
||||
*/
|
||||
latents?: LatentsField;
|
||||
/**
|
||||
* The factor by which to scale the latents
|
||||
*/
|
||||
scale_factor: number;
|
||||
/**
|
||||
* The interpolation mode
|
||||
*/
|
||||
mode?: 'nearest' | 'linear' | 'bilinear' | 'bicubic' | 'trilinear' | 'area' | 'nearest-exact';
|
||||
/**
|
||||
* Whether or not to antialias (applied in bilinear and bicubic modes only)
|
||||
*/
|
||||
antialias?: boolean;
|
||||
};
|
||||
|
@ -17,10 +17,6 @@ export type TextToLatentsInvocation = {
|
||||
* The prompt to generate an image from
|
||||
*/
|
||||
prompt?: string;
|
||||
/**
|
||||
* The seed to use (-1 for a random seed)
|
||||
*/
|
||||
seed?: number;
|
||||
/**
|
||||
* The noise to use
|
||||
*/
|
||||
@ -29,14 +25,6 @@ export type TextToLatentsInvocation = {
|
||||
* The number of steps to use to generate the image
|
||||
*/
|
||||
steps?: number;
|
||||
/**
|
||||
* The width of the resulting image
|
||||
*/
|
||||
width?: number;
|
||||
/**
|
||||
* The height of the resulting image
|
||||
*/
|
||||
height?: number;
|
||||
/**
|
||||
* The Classifier-Free Guidance, higher values may result in a result closer to the prompt
|
||||
*/
|
||||
|
@ -33,6 +33,10 @@ export const $Graph = {
|
||||
type: 'TextToLatentsInvocation',
|
||||
}, {
|
||||
type: 'LatentsToImageInvocation',
|
||||
}, {
|
||||
type: 'ResizeLatentsInvocation',
|
||||
}, {
|
||||
type: 'ScaleLatentsInvocation',
|
||||
}, {
|
||||
type: 'AddInvocation',
|
||||
}, {
|
||||
|
@ -16,12 +16,6 @@ export const $LatentsToLatentsInvocation = {
|
||||
type: 'string',
|
||||
description: `The prompt to generate an image from`,
|
||||
},
|
||||
seed: {
|
||||
type: 'number',
|
||||
description: `The seed to use (-1 for a random seed)`,
|
||||
maximum: 4294967295,
|
||||
minimum: -1,
|
||||
},
|
||||
noise: {
|
||||
type: 'all-of',
|
||||
description: `The noise to use`,
|
||||
@ -33,16 +27,6 @@ export const $LatentsToLatentsInvocation = {
|
||||
type: 'number',
|
||||
description: `The number of steps to use to generate the image`,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the resulting image`,
|
||||
multipleOf: 64,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the resulting image`,
|
||||
multipleOf: 64,
|
||||
},
|
||||
cfg_scale: {
|
||||
type: 'number',
|
||||
description: `The Classifier-Free Guidance, higher values may result in a result closer to the prompt`,
|
||||
|
@ -0,0 +1,44 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ResizeLatentsInvocation = {
|
||||
description: `Resizes latents to explicit width/height (in pixels). Provided dimensions are floor-divided by 8.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
latents: {
|
||||
type: 'all-of',
|
||||
description: `The latents to resize`,
|
||||
contains: [{
|
||||
type: 'LatentsField',
|
||||
}],
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width to resize to (px)`,
|
||||
isRequired: true,
|
||||
minimum: 64,
|
||||
multipleOf: 8,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height to resize to (px)`,
|
||||
isRequired: true,
|
||||
minimum: 64,
|
||||
multipleOf: 8,
|
||||
},
|
||||
mode: {
|
||||
type: 'Enum',
|
||||
},
|
||||
antialias: {
|
||||
type: 'boolean',
|
||||
description: `Whether or not to antialias (applied in bilinear and bicubic modes only)`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -0,0 +1,35 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const $ScaleLatentsInvocation = {
|
||||
description: `Scales latents by a given factor.`,
|
||||
properties: {
|
||||
id: {
|
||||
type: 'string',
|
||||
description: `The id of this node. Must be unique among all nodes.`,
|
||||
isRequired: true,
|
||||
},
|
||||
type: {
|
||||
type: 'Enum',
|
||||
},
|
||||
latents: {
|
||||
type: 'all-of',
|
||||
description: `The latents to scale`,
|
||||
contains: [{
|
||||
type: 'LatentsField',
|
||||
}],
|
||||
},
|
||||
scale_factor: {
|
||||
type: 'number',
|
||||
description: `The factor by which to scale the latents`,
|
||||
isRequired: true,
|
||||
},
|
||||
mode: {
|
||||
type: 'Enum',
|
||||
},
|
||||
antialias: {
|
||||
type: 'boolean',
|
||||
description: `Whether or not to antialias (applied in bilinear and bicubic modes only)`,
|
||||
},
|
||||
},
|
||||
} as const;
|
@ -16,12 +16,6 @@ export const $TextToLatentsInvocation = {
|
||||
type: 'string',
|
||||
description: `The prompt to generate an image from`,
|
||||
},
|
||||
seed: {
|
||||
type: 'number',
|
||||
description: `The seed to use (-1 for a random seed)`,
|
||||
maximum: 4294967295,
|
||||
minimum: -1,
|
||||
},
|
||||
noise: {
|
||||
type: 'all-of',
|
||||
description: `The noise to use`,
|
||||
@ -33,16 +27,6 @@ export const $TextToLatentsInvocation = {
|
||||
type: 'number',
|
||||
description: `The number of steps to use to generate the image`,
|
||||
},
|
||||
width: {
|
||||
type: 'number',
|
||||
description: `The width of the resulting image`,
|
||||
multipleOf: 64,
|
||||
},
|
||||
height: {
|
||||
type: 'number',
|
||||
description: `The height of the resulting image`,
|
||||
multipleOf: 64,
|
||||
},
|
||||
cfg_scale: {
|
||||
type: 'number',
|
||||
description: `The Classifier-Free Guidance, higher values may result in a result closer to the prompt`,
|
||||
|
@ -27,7 +27,9 @@ import type { ParamIntInvocation } from '../models/ParamIntInvocation';
|
||||
import type { PasteImageInvocation } from '../models/PasteImageInvocation';
|
||||
import type { RandomRangeInvocation } from '../models/RandomRangeInvocation';
|
||||
import type { RangeInvocation } from '../models/RangeInvocation';
|
||||
import type { ResizeLatentsInvocation } from '../models/ResizeLatentsInvocation';
|
||||
import type { RestoreFaceInvocation } from '../models/RestoreFaceInvocation';
|
||||
import type { ScaleLatentsInvocation } from '../models/ScaleLatentsInvocation';
|
||||
import type { ShowImageInvocation } from '../models/ShowImageInvocation';
|
||||
import type { SubtractInvocation } from '../models/SubtractInvocation';
|
||||
import type { TextToImageInvocation } from '../models/TextToImageInvocation';
|
||||
@ -142,7 +144,7 @@ export class SessionsService {
|
||||
* The id of the session
|
||||
*/
|
||||
sessionId: string,
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
||||
}): CancelablePromise<string> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
@ -179,7 +181,7 @@ export class SessionsService {
|
||||
* The path to the node in the graph
|
||||
*/
|
||||
nodePath: string,
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
||||
requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation),
|
||||
}): CancelablePromise<GraphExecutionState> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
|
@ -7,15 +7,9 @@ import {
|
||||
} from 'services/events/types';
|
||||
import {
|
||||
invocationComplete,
|
||||
socketConnected,
|
||||
socketDisconnected,
|
||||
socketSubscribed,
|
||||
socketUnsubscribed,
|
||||
} from './actions';
|
||||
import {
|
||||
receivedResultImagesPage,
|
||||
receivedUploadImagesPage,
|
||||
} from 'services/thunks/gallery';
|
||||
import { AppDispatch, RootState } from 'app/store/store';
|
||||
import { getTimestamp } from 'common/util/getTimestamp';
|
||||
import {
|
||||
@ -23,14 +17,12 @@ import {
|
||||
isFulfilledSessionCreatedAction,
|
||||
} from 'services/thunks/session';
|
||||
import { OpenAPI } from 'services/api';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
||||
import { isImageOutput } from 'services/types/guards';
|
||||
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
||||
import { setEventListeners } from 'services/events/util/setEventListeners';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
|
||||
const moduleLog = log.child({ namespace: 'socketio' });
|
||||
const socketioLog = log.child({ namespace: 'socketio' });
|
||||
|
||||
export const socketMiddleware = () => {
|
||||
let areListenersSet = false;
|
||||
@ -65,106 +57,27 @@ export const socketMiddleware = () => {
|
||||
(store: MiddlewareAPI<AppDispatch, RootState>) => (next) => (action) => {
|
||||
const { dispatch, getState } = store;
|
||||
|
||||
// Nothing dispatches `socketReset` actions yet
|
||||
// if (socketReset.match(action)) {
|
||||
// const { sessionId } = getState().system;
|
||||
|
||||
// if (sessionId) {
|
||||
// socket.emit('unsubscribe', { session: sessionId });
|
||||
// dispatch(
|
||||
// socketUnsubscribed({ sessionId, timestamp: getTimestamp() })
|
||||
// );
|
||||
// }
|
||||
|
||||
// if (socket.connected) {
|
||||
// socket.disconnect();
|
||||
// dispatch(socketDisconnected({ timestamp: getTimestamp() }));
|
||||
// }
|
||||
|
||||
// socket.removeAllListeners();
|
||||
// areListenersSet = false;
|
||||
// }
|
||||
|
||||
// Set listeners for `connect` and `disconnect` events once
|
||||
// Must happen in middleware to get access to `dispatch`
|
||||
if (!areListenersSet) {
|
||||
socket.on('connect', () => {
|
||||
moduleLog.debug('Connected');
|
||||
|
||||
dispatch(socketConnected({ timestamp: getTimestamp() }));
|
||||
|
||||
const { results, uploads, models, nodes, config, system } =
|
||||
getState();
|
||||
|
||||
const { disabledTabs } = config;
|
||||
|
||||
// These thunks need to be dispatch in middleware; cannot handle in a reducer
|
||||
if (!results.ids.length) {
|
||||
dispatch(receivedResultImagesPage());
|
||||
}
|
||||
|
||||
if (!uploads.ids.length) {
|
||||
dispatch(receivedUploadImagesPage());
|
||||
}
|
||||
|
||||
if (!models.ids.length) {
|
||||
dispatch(receivedModels());
|
||||
}
|
||||
|
||||
if (!nodes.schema && !disabledTabs.includes('nodes')) {
|
||||
dispatch(receivedOpenAPISchema());
|
||||
}
|
||||
|
||||
if (system.sessionId) {
|
||||
const sessionLog = moduleLog.child({ sessionId: system.sessionId });
|
||||
|
||||
sessionLog.debug(
|
||||
`Subscribed to existing session (${system.sessionId})`
|
||||
);
|
||||
|
||||
socket.emit('subscribe', { session: system.sessionId });
|
||||
dispatch(
|
||||
socketSubscribed({
|
||||
sessionId: system.sessionId,
|
||||
timestamp: getTimestamp(),
|
||||
})
|
||||
);
|
||||
setEventListeners({ socket, store, sessionLog });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
moduleLog.debug('Disconnected');
|
||||
dispatch(socketDisconnected({ timestamp: getTimestamp() }));
|
||||
});
|
||||
setEventListeners({ store, socket, log: socketioLog });
|
||||
|
||||
areListenersSet = true;
|
||||
|
||||
// must manually connect
|
||||
socket.connect();
|
||||
}
|
||||
|
||||
// Everything else only happens once we have created a session
|
||||
if (isFulfilledSessionCreatedAction(action)) {
|
||||
const sessionId = action.payload.id;
|
||||
const sessionLog = moduleLog.child({ sessionId });
|
||||
const sessionLog = socketioLog.child({ sessionId });
|
||||
const oldSessionId = getState().system.sessionId;
|
||||
|
||||
// const subscribedNodeIds = getState().system.subscribedNodeIds;
|
||||
// const shouldHandleEvent = (id: string): boolean => {
|
||||
// if (subscribedNodeIds.length === 1 && subscribedNodeIds[0] === '*') {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return subscribedNodeIds.includes(id);
|
||||
// };
|
||||
|
||||
if (oldSessionId) {
|
||||
sessionLog.debug(
|
||||
{ oldSessionId },
|
||||
`Unsubscribed from old session (${oldSessionId})`
|
||||
);
|
||||
// Unsubscribe when invocations complete
|
||||
|
||||
socket.emit('unsubscribe', {
|
||||
session: oldSessionId,
|
||||
});
|
||||
@ -175,28 +88,18 @@ export const socketMiddleware = () => {
|
||||
timestamp: getTimestamp(),
|
||||
})
|
||||
);
|
||||
const listenersToRemove: (keyof ServerToClientEvents)[] = [
|
||||
'invocation_started',
|
||||
'generator_progress',
|
||||
'invocation_error',
|
||||
'invocation_complete',
|
||||
];
|
||||
|
||||
// Remove listeners for these events; we need to set them up fresh whenever we subscribe
|
||||
listenersToRemove.forEach((event: keyof ServerToClientEvents) => {
|
||||
socket.removeAllListeners(event);
|
||||
});
|
||||
}
|
||||
|
||||
sessionLog.debug(`Subscribe to new session (${sessionId})`);
|
||||
|
||||
socket.emit('subscribe', { session: sessionId });
|
||||
|
||||
dispatch(
|
||||
socketSubscribed({
|
||||
sessionId: sessionId,
|
||||
timestamp: getTimestamp(),
|
||||
})
|
||||
);
|
||||
setEventListeners({ socket, store, sessionLog });
|
||||
|
||||
// Finally we actually invoke the session, starting processing
|
||||
dispatch(sessionInvoked({ sessionId }));
|
||||
@ -222,7 +125,6 @@ export const socketMiddleware = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Always pass the action on so other middleware and reducers can handle it
|
||||
next(action);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Graph, GraphExecutionState } from '../api';
|
||||
import { Graph, GraphExecutionState, InvokeAIMetadata } from '../api';
|
||||
|
||||
/**
|
||||
* A progress image, we get one for each step in the generation
|
||||
@ -17,6 +17,12 @@ export type AnyInvocation = NonNullable<Graph['nodes']>[string];
|
||||
|
||||
export type AnyResult = GraphExecutionState['results'][string];
|
||||
|
||||
export type BaseNode = {
|
||||
id: string;
|
||||
type: string;
|
||||
[key: string]: NonNullable<InvokeAIMetadata['node']>[string];
|
||||
};
|
||||
|
||||
/**
|
||||
* A `generator_progress` socket.io event.
|
||||
*
|
||||
@ -24,7 +30,7 @@ export type AnyResult = GraphExecutionState['results'][string];
|
||||
*/
|
||||
export type GeneratorProgressEvent = {
|
||||
graph_execution_state_id: string;
|
||||
node: AnyInvocation;
|
||||
node: BaseNode;
|
||||
source_node_id: string;
|
||||
progress_image?: ProgressImage;
|
||||
step: number;
|
||||
@ -40,7 +46,7 @@ export type GeneratorProgressEvent = {
|
||||
*/
|
||||
export type InvocationCompleteEvent = {
|
||||
graph_execution_state_id: string;
|
||||
node: AnyInvocation;
|
||||
node: BaseNode;
|
||||
source_node_id: string;
|
||||
result: AnyResult;
|
||||
};
|
||||
@ -52,7 +58,7 @@ export type InvocationCompleteEvent = {
|
||||
*/
|
||||
export type InvocationErrorEvent = {
|
||||
graph_execution_state_id: string;
|
||||
node: AnyInvocation;
|
||||
node: BaseNode;
|
||||
source_node_id: string;
|
||||
error: string;
|
||||
};
|
||||
@ -64,7 +70,7 @@ export type InvocationErrorEvent = {
|
||||
*/
|
||||
export type InvocationStartedEvent = {
|
||||
graph_execution_state_id: string;
|
||||
node: AnyInvocation;
|
||||
node: BaseNode;
|
||||
source_node_id: string;
|
||||
};
|
||||
|
||||
|
@ -9,38 +9,140 @@ import {
|
||||
invocationComplete,
|
||||
invocationError,
|
||||
invocationStarted,
|
||||
socketConnected,
|
||||
socketDisconnected,
|
||||
socketSubscribed,
|
||||
} from '../actions';
|
||||
import { ClientToServerEvents, ServerToClientEvents } from '../types';
|
||||
import { Logger } from 'roarr';
|
||||
import { JsonObject } from 'roarr/dist/types';
|
||||
import {
|
||||
receivedResultImagesPage,
|
||||
receivedUploadImagesPage,
|
||||
} from 'services/thunks/gallery';
|
||||
import { receivedModels } from 'services/thunks/model';
|
||||
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
||||
|
||||
type SetEventListenersArg = {
|
||||
socket: Socket<ServerToClientEvents, ClientToServerEvents>;
|
||||
store: MiddlewareAPI<AppDispatch, RootState>;
|
||||
sessionLog: Logger<JsonObject>;
|
||||
log: Logger<JsonObject>;
|
||||
};
|
||||
|
||||
export const setEventListeners = (arg: SetEventListenersArg) => {
|
||||
const { socket, store, sessionLog } = arg;
|
||||
const { socket, store, log } = arg;
|
||||
const { dispatch, getState } = store;
|
||||
// Set up listeners for the present subscription
|
||||
|
||||
/**
|
||||
* Connect
|
||||
*/
|
||||
socket.on('connect', () => {
|
||||
log.debug('Connected');
|
||||
|
||||
dispatch(socketConnected({ timestamp: getTimestamp() }));
|
||||
|
||||
const { results, uploads, models, nodes, config, system } = getState();
|
||||
|
||||
const { disabledTabs } = config;
|
||||
|
||||
// These thunks need to be dispatch in middleware; cannot handle in a reducer
|
||||
if (!results.ids.length) {
|
||||
dispatch(receivedResultImagesPage());
|
||||
}
|
||||
|
||||
if (!uploads.ids.length) {
|
||||
dispatch(receivedUploadImagesPage());
|
||||
}
|
||||
|
||||
if (!models.ids.length) {
|
||||
dispatch(receivedModels());
|
||||
}
|
||||
|
||||
if (!nodes.schema && !disabledTabs.includes('nodes')) {
|
||||
dispatch(receivedOpenAPISchema());
|
||||
}
|
||||
|
||||
if (system.sessionId) {
|
||||
log.debug(
|
||||
{ sessionId: system.sessionId },
|
||||
`Subscribed to existing session (${system.sessionId})`
|
||||
);
|
||||
|
||||
socket.emit('subscribe', { session: system.sessionId });
|
||||
dispatch(
|
||||
socketSubscribed({
|
||||
sessionId: system.sessionId,
|
||||
timestamp: getTimestamp(),
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Disconnect
|
||||
*/
|
||||
socket.on('disconnect', () => {
|
||||
log.debug('Disconnected');
|
||||
dispatch(socketDisconnected({ timestamp: getTimestamp() }));
|
||||
});
|
||||
|
||||
/**
|
||||
* Invocation started
|
||||
*/
|
||||
socket.on('invocation_started', (data) => {
|
||||
sessionLog.child({ data }).info(`Invocation started (${data.node.type})`);
|
||||
if (getState().system.canceledSession === data.graph_execution_state_id) {
|
||||
log.trace(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Ignored invocation started (${data.node.type}) for canceled session (${data.graph_execution_state_id})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Invocation started (${data.node.type})`
|
||||
);
|
||||
dispatch(invocationStarted({ data, timestamp: getTimestamp() }));
|
||||
});
|
||||
|
||||
/**
|
||||
* Generator progress
|
||||
*/
|
||||
socket.on('generator_progress', (data) => {
|
||||
sessionLog.child({ data }).trace(`Generator progress (${data.node.type})`);
|
||||
if (getState().system.canceledSession === data.graph_execution_state_id) {
|
||||
log.trace(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Ignored generator progress (${data.node.type}) for canceled session (${data.graph_execution_state_id})`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
log.trace(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Generator progress (${data.node.type})`
|
||||
);
|
||||
dispatch(generatorProgress({ data, timestamp: getTimestamp() }));
|
||||
});
|
||||
|
||||
/**
|
||||
* Invocation error
|
||||
*/
|
||||
socket.on('invocation_error', (data) => {
|
||||
sessionLog.child({ data }).error(`Invocation error (${data.node.type})`);
|
||||
log.error(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Invocation error (${data.node.type})`
|
||||
);
|
||||
dispatch(invocationError({ data, timestamp: getTimestamp() }));
|
||||
});
|
||||
|
||||
/**
|
||||
* Invocation complete
|
||||
*/
|
||||
socket.on('invocation_complete', (data) => {
|
||||
sessionLog.child({ data }).info(`Invocation complete (${data.node.type})`);
|
||||
log.info(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Invocation complete (${data.node.type})`
|
||||
);
|
||||
const sessionId = data.graph_execution_state_id;
|
||||
|
||||
const { cancelType, isCancelScheduled } = getState().system;
|
||||
@ -60,12 +162,14 @@ export const setEventListeners = (arg: SetEventListenersArg) => {
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Graph complete
|
||||
*/
|
||||
socket.on('graph_execution_state_complete', (data) => {
|
||||
sessionLog
|
||||
.child({ data })
|
||||
.info(
|
||||
`Graph execution state complete (${data.graph_execution_state_id})`
|
||||
);
|
||||
log.info(
|
||||
{ data, sessionId: data.graph_execution_state_id },
|
||||
`Graph execution state complete (${data.graph_execution_state_id})`
|
||||
);
|
||||
dispatch(graphExecutionStateComplete({ data, timestamp: getTimestamp() }));
|
||||
});
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import { isFulfilled, isRejected } from '@reduxjs/toolkit';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { createAppAsyncThunk } from 'app/store/storeUtils';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { clamp } from 'lodash-es';
|
||||
import { clamp, isString } from 'lodash-es';
|
||||
import { ImagesService } from 'services/api';
|
||||
import { getHeaders } from 'services/util/getHeaders';
|
||||
|
||||
@ -85,7 +85,7 @@ export const imageDeleted = createAppAsyncThunk(
|
||||
// Determine which image should replace the deleted image, if the deleted image is the selected image.
|
||||
// Unfortunately, we have to do this here, because the resultsSlice and uploadsSlice cannot change
|
||||
// the selected image.
|
||||
const selectedImageName = getState().gallery.selectedImageName;
|
||||
const selectedImageName = getState().gallery.selectedImage?.name;
|
||||
|
||||
if (selectedImageName === imageName) {
|
||||
const allIds = getState()[imageType].ids;
|
||||
@ -104,9 +104,13 @@ export const imageDeleted = createAppAsyncThunk(
|
||||
|
||||
const newSelectedImageId = filteredIds[newSelectedImageIndex];
|
||||
|
||||
dispatch(
|
||||
imageSelected(newSelectedImageId ? newSelectedImageId.toString() : '')
|
||||
);
|
||||
if (newSelectedImageId) {
|
||||
dispatch(
|
||||
imageSelected({ name: newSelectedImageId as string, type: imageType })
|
||||
);
|
||||
} else {
|
||||
dispatch(imageSelected());
|
||||
}
|
||||
}
|
||||
|
||||
const response = await ImagesService.deleteImage(arg);
|
||||
|
@ -4,28 +4,41 @@ import { buildLinearGraph as buildGenerateGraph } from 'features/nodes/util/line
|
||||
import { isAnyOf, isFulfilled } from '@reduxjs/toolkit';
|
||||
import { buildNodesGraph } from 'features/nodes/util/nodesGraphBuilder/buildNodesGraph';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { serializeError } from 'serialize-error';
|
||||
|
||||
const sessionLog = log.child({ namespace: 'session' });
|
||||
|
||||
export const generateGraphBuilt = createAppAsyncThunk(
|
||||
'api/generateGraphBuilt',
|
||||
async (_, { dispatch, getState }) => {
|
||||
const graph = buildGenerateGraph(getState());
|
||||
|
||||
dispatch(sessionCreated({ graph }));
|
||||
|
||||
return graph;
|
||||
async (_, { dispatch, getState, rejectWithValue }) => {
|
||||
try {
|
||||
const graph = buildGenerateGraph(getState());
|
||||
dispatch(sessionCreated({ graph }));
|
||||
return graph;
|
||||
} catch (err: any) {
|
||||
sessionLog.error(
|
||||
{ error: serializeError(err) },
|
||||
'Problem building graph'
|
||||
);
|
||||
return rejectWithValue(err.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const nodesGraphBuilt = createAppAsyncThunk(
|
||||
'api/nodesGraphBuilt',
|
||||
async (_, { dispatch, getState }) => {
|
||||
const graph = buildNodesGraph(getState());
|
||||
|
||||
dispatch(sessionCreated({ graph }));
|
||||
|
||||
return graph;
|
||||
async (_, { dispatch, getState, rejectWithValue }) => {
|
||||
try {
|
||||
const graph = buildNodesGraph(getState());
|
||||
dispatch(sessionCreated({ graph }));
|
||||
return graph;
|
||||
} catch (err: any) {
|
||||
sessionLog.error(
|
||||
{ error: serializeError(err) },
|
||||
'Problem building graph'
|
||||
);
|
||||
return rejectWithValue(err.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { Image } from 'app/types/invokeai';
|
||||
import { get, isObject, isString } from 'lodash-es';
|
||||
import {
|
||||
GraphExecutionState,
|
||||
GraphInvocationOutput,
|
||||
@ -6,6 +8,8 @@ import {
|
||||
PromptOutput,
|
||||
IterateInvocationOutput,
|
||||
CollectInvocationOutput,
|
||||
ImageType,
|
||||
ImageField,
|
||||
} from 'services/api';
|
||||
|
||||
export const isImageOutput = (
|
||||
@ -31,3 +35,16 @@ export const isIterateOutput = (
|
||||
export const isCollectOutput = (
|
||||
output: GraphExecutionState['results'][string]
|
||||
): output is CollectInvocationOutput => output.type === 'collect_output';
|
||||
|
||||
export const isImageType = (t: unknown): t is ImageType =>
|
||||
isString(t) && ['results', 'uploads', 'intermediates'].includes(t);
|
||||
|
||||
export const isImage = (image: unknown): image is Image =>
|
||||
isObject(image) &&
|
||||
isString(get(image, 'name')) &&
|
||||
isImageType(get(image, 'type'));
|
||||
|
||||
export const isImageField = (imageField: unknown): imageField is ImageField =>
|
||||
isObject(imageField) &&
|
||||
isString(get(imageField, 'image_name')) &&
|
||||
isImageType(get(imageField, 'image_type'));
|
||||
|
48
invokeai/frontend/web/src/theme/css/overlayscrollbars.css
Normal file
48
invokeai/frontend/web/src/theme/css/overlayscrollbars.css
Normal file
@ -0,0 +1,48 @@
|
||||
.os-scrollbar {
|
||||
/* The size of the scrollbar */
|
||||
/* --os-size: 0; */
|
||||
/* The axis-perpedicular padding of the scrollbar (horizontal: padding-y, vertical: padding-x) */
|
||||
/* --os-padding-perpendicular: 0; */
|
||||
/* The axis padding of the scrollbar (horizontal: padding-x, vertical: padding-y) */
|
||||
/* --os-padding-axis: 0; */
|
||||
/* The border radius of the scrollbar track */
|
||||
/* --os-track-border-radius: 0; */
|
||||
/* The background of the scrollbar track */
|
||||
--os-track-bg: rgba(0, 0, 0, 0.3);
|
||||
/* The :hover background of the scrollbar track */
|
||||
--os-track-bg-hover: rgba(0, 0, 0, 0.3);
|
||||
/* The :active background of the scrollbar track */
|
||||
--os-track-bg-active: rgba(0, 0, 0, 0.3);
|
||||
/* The border of the scrollbar track */
|
||||
/* --os-track-border: none; */
|
||||
/* The :hover background of the scrollbar track */
|
||||
/* --os-track-border-hover: none; */
|
||||
/* The :active background of the scrollbar track */
|
||||
/* --os-track-border-active: none; */
|
||||
/* The border radius of the scrollbar handle */
|
||||
/* --os-handle-border-radius: 0; */
|
||||
/* The background of the scrollbar handle */
|
||||
--os-handle-bg: var(--invokeai-colors-accent-500);
|
||||
/* The :hover background of the scrollbar handle */
|
||||
--os-handle-bg-hover: var(--invokeai-colors-accent-450);
|
||||
/* The :active background of the scrollbar handle */
|
||||
--os-handle-bg-active: var(--invokeai-colors-accent-400);
|
||||
/* The border of the scrollbar handle */
|
||||
/* --os-handle-border: none; */
|
||||
/* The :hover border of the scrollbar handle */
|
||||
/* --os-handle-border-hover: none; */
|
||||
/* The :active border of the scrollbar handle */
|
||||
/* --os-handle-border-active: none; */
|
||||
/* The min size of the scrollbar handle */
|
||||
--os-handle-min-size: 50px;
|
||||
/* The max size of the scrollbar handle */
|
||||
/* --os-handle-max-size: none; */
|
||||
/* The axis-perpedicular size of the scrollbar handle (horizontal: height, vertical: width) */
|
||||
/* --os-handle-perpendicular-size: 100%; */
|
||||
/* The :hover axis-perpedicular size of the scrollbar handle (horizontal: height, vertical: width) */
|
||||
/* --os-handle-perpendicular-size-hover: 100%; */
|
||||
/* The :active axis-perpedicular size of the scrollbar handle (horizontal: height, vertical: width) */
|
||||
/* --os-handle-perpendicular-size-active: 100%; */
|
||||
/* Increases the interactive area of the scrollbar handle. */
|
||||
/* --os-handle-interactive-area-offset: 0; */
|
||||
}
|
@ -57,6 +57,13 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@babel/runtime@^7.1.2":
|
||||
version "7.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
|
||||
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.11"
|
||||
|
||||
"@babel/types@^7.21.4", "@babel/types@^7.4":
|
||||
version "7.21.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4"
|
||||
@ -1837,6 +1844,11 @@
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/js-cookie@^2.2.6":
|
||||
version "2.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3"
|
||||
integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
@ -2048,6 +2060,11 @@
|
||||
dependencies:
|
||||
"@swc/core" "^1.3.42"
|
||||
|
||||
"@xobotyi/scrollbar-width@^1.9.5":
|
||||
version "1.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
|
||||
integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==
|
||||
|
||||
"@yarnpkg/lockfile@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
|
||||
@ -2567,6 +2584,11 @@ clone@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
|
||||
|
||||
clsx@^1.1.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
|
||||
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
|
||||
|
||||
code-block-writer@^12.0.0:
|
||||
version "12.0.0"
|
||||
resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770"
|
||||
@ -2685,7 +2707,7 @@ convert-source-map@^1.5.0:
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
||||
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
|
||||
|
||||
copy-to-clipboard@3.3.3:
|
||||
copy-to-clipboard@3.3.3, copy-to-clipboard@^3.3.1:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
|
||||
integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
|
||||
@ -2736,7 +2758,22 @@ css-box-model@1.2.1:
|
||||
dependencies:
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
csstype@^3.0.11, csstype@^3.0.2:
|
||||
css-in-js-utils@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb"
|
||||
integrity sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==
|
||||
dependencies:
|
||||
hyphenate-style-name "^1.0.3"
|
||||
|
||||
css-tree@^1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
|
||||
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
|
||||
dependencies:
|
||||
mdn-data "2.0.14"
|
||||
source-map "^0.6.1"
|
||||
|
||||
csstype@^3.0.11, csstype@^3.0.2, csstype@^3.0.6:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
||||
@ -3142,6 +3179,13 @@ error-ex@^1.3.1:
|
||||
dependencies:
|
||||
is-arrayish "^0.2.1"
|
||||
|
||||
error-stack-parser@^2.0.6:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
|
||||
integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
|
||||
dependencies:
|
||||
stackframe "^1.3.4"
|
||||
|
||||
es-abstract@^1.19.0, es-abstract@^1.20.4:
|
||||
version "1.21.2"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff"
|
||||
@ -3481,6 +3525,16 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
||||
|
||||
fast-loops@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-loops/-/fast-loops-1.1.3.tgz#ce96adb86d07e7bf9b4822ab9c6fac9964981f75"
|
||||
integrity sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==
|
||||
|
||||
fast-memoize@^2.5.1:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e"
|
||||
integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==
|
||||
|
||||
fast-printf@^1.6.9:
|
||||
version "1.6.9"
|
||||
resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.9.tgz#212f56570d2dc8ccdd057ee93d50dd414d07d676"
|
||||
@ -3488,6 +3542,16 @@ fast-printf@^1.6.9:
|
||||
dependencies:
|
||||
boolean "^3.1.4"
|
||||
|
||||
fast-shallow-equal@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b"
|
||||
integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==
|
||||
|
||||
fastest-stable-stringify@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76"
|
||||
integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
|
||||
@ -3966,6 +4030,11 @@ husky@^8.0.3:
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
|
||||
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
|
||||
|
||||
hyphenate-style-name@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
|
||||
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
|
||||
|
||||
i18next-browser-languagedetector@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz#ead34592edc96c6c3a618a51cb57ad027c5b5d87"
|
||||
@ -4058,6 +4127,14 @@ ini@~1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
||||
|
||||
inline-style-prefixer@^6.0.0:
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.4.tgz#4290ed453ab0e4441583284ad86e41ad88384f44"
|
||||
integrity sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==
|
||||
dependencies:
|
||||
css-in-js-utils "^3.1.0"
|
||||
fast-loops "^1.1.3"
|
||||
|
||||
internal-slot@^1.0.3, internal-slot@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
||||
@ -4350,6 +4427,11 @@ jju@~1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
|
||||
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
|
||||
|
||||
js-cookie@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
|
||||
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
|
||||
|
||||
js-sdsl@^4.1.4:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430"
|
||||
@ -4667,6 +4749,11 @@ make-error@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
||||
mdn-data@2.0.14:
|
||||
version "2.0.14"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
@ -4773,6 +4860,20 @@ ms@2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
nano-css@^5.3.1:
|
||||
version "5.3.5"
|
||||
resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.5.tgz#3075ea29ffdeb0c7cb6d25edb21d8f7fa8e8fe8e"
|
||||
integrity sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg==
|
||||
dependencies:
|
||||
css-tree "^1.1.2"
|
||||
csstype "^3.0.6"
|
||||
fastest-stable-stringify "^2.0.2"
|
||||
inline-style-prefixer "^6.0.0"
|
||||
rtl-css-js "^1.14.0"
|
||||
sourcemap-codec "^1.4.8"
|
||||
stacktrace-js "^2.0.2"
|
||||
stylis "^4.0.6"
|
||||
|
||||
nanoid@^3.3.6:
|
||||
version "3.3.6"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
||||
@ -4999,6 +5100,16 @@ os-tmpdir@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
|
||||
|
||||
overlayscrollbars-react@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/overlayscrollbars-react/-/overlayscrollbars-react-0.5.0.tgz#0272bdc6304c7228a58d30e5b678e97fd5c5d8dd"
|
||||
integrity sha512-uCNTnkfWW74veoiEv3kSwoLelKt4e8gTNv65D771X3il0x5g5Yo0fUbro7SpQzR9yNgi23cvB2mQHTTdQH96pA==
|
||||
|
||||
overlayscrollbars@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/overlayscrollbars/-/overlayscrollbars-2.1.1.tgz#a7414fe9c96cf140dbe4975bbe9312861750388d"
|
||||
integrity sha512-xvs2g8Tcq9+CZDpLEUchN3YUzjJhnTWw9kwqT/qcC53FIkOyP9mqnRMot5sW16tcsPT1KaMyzF0AMXw/7E4a8g==
|
||||
|
||||
p-cancelable@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
||||
@ -5302,6 +5413,13 @@ rc@1.2.8, rc@^1.2.7, rc@^1.2.8:
|
||||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
re-resizable@6.9.6:
|
||||
version "6.9.6"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.6.tgz#b95d37e3821481b56ddfb1e12862940a791e827d"
|
||||
integrity sha512-0xYKS5+Z0zk+vICQlcZW+g54CcJTTmHluA7JUUgvERDxnKAnytylcyPsA+BSFi759s5hPlHmBRegFrwXs2FuBQ==
|
||||
dependencies:
|
||||
fast-memoize "^2.5.1"
|
||||
|
||||
re-resizable@^6.9.9:
|
||||
version "6.9.9"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.9.tgz#99e8b31c67a62115dc9c5394b7e55892265be216"
|
||||
@ -5327,6 +5445,14 @@ react-dom@^18.2.0:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-draggable@4.4.5:
|
||||
version "4.4.5"
|
||||
resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.5.tgz#9e37fe7ce1a4cf843030f521a0a4cc41886d7e7c"
|
||||
integrity sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==
|
||||
dependencies:
|
||||
clsx "^1.1.1"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
react-dropzone@^14.2.3:
|
||||
version "14.2.3"
|
||||
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.3.tgz#0acab68308fda2d54d1273a1e626264e13d4e84b"
|
||||
@ -5443,6 +5569,15 @@ react-remove-scroll@^2.5.5:
|
||||
use-callback-ref "^1.3.0"
|
||||
use-sidecar "^1.1.2"
|
||||
|
||||
react-rnd@^10.4.1:
|
||||
version "10.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react-rnd/-/react-rnd-10.4.1.tgz#9e1c3f244895d7862ef03be98b2a620848c3fba1"
|
||||
integrity sha512-0m887AjQZr6p2ADLNnipquqsDq4XJu/uqVqI3zuoGD19tRm6uB83HmZWydtkilNp5EWsOHbLGF4IjWMdd5du8Q==
|
||||
dependencies:
|
||||
re-resizable "6.9.6"
|
||||
react-draggable "4.4.5"
|
||||
tslib "2.3.1"
|
||||
|
||||
react-style-singleton@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
|
||||
@ -5462,6 +5597,36 @@ react-transition-group@^4.4.5:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-universal-interface@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
|
||||
integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==
|
||||
|
||||
react-use@^17.4.0:
|
||||
version "17.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.4.0.tgz#cefef258b0a6c534a5c8021c2528ac6e1a4cdc6d"
|
||||
integrity sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q==
|
||||
dependencies:
|
||||
"@types/js-cookie" "^2.2.6"
|
||||
"@xobotyi/scrollbar-width" "^1.9.5"
|
||||
copy-to-clipboard "^3.3.1"
|
||||
fast-deep-equal "^3.1.3"
|
||||
fast-shallow-equal "^1.0.0"
|
||||
js-cookie "^2.2.1"
|
||||
nano-css "^5.3.1"
|
||||
react-universal-interface "^0.6.2"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
screenfull "^5.1.0"
|
||||
set-harmonic-interval "^1.0.1"
|
||||
throttle-debounce "^3.0.1"
|
||||
ts-easing "^0.2.0"
|
||||
tslib "^2.1.0"
|
||||
|
||||
react-virtuoso@^4.3.5:
|
||||
version "4.3.5"
|
||||
resolved "https://registry.yarnpkg.com/react-virtuoso/-/react-virtuoso-4.3.5.tgz#1e882d435b2d3d8abf7c4b85235199cbfadd935d"
|
||||
integrity sha512-MdWzmM9d8Gt5YGPIgGzRoqnYygTsriWlZrq+SqxphJTiiHs9cffnjf2Beo3SA3wRYzQJD8FI2HXtN5ACWzPFbQ==
|
||||
|
||||
react-zoom-pan-pinch@^3.0.7:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.0.7.tgz#def52f6886bc11e1b160dedf4250aae95470b94d"
|
||||
@ -5580,6 +5745,11 @@ reselect@^4.1.8:
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
|
||||
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
|
||||
|
||||
resize-observer-polyfill@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||
|
||||
resolve-dependency-path@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736"
|
||||
@ -5696,6 +5866,13 @@ rollup@^3.21.0:
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rtl-css-js@^1.14.0:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.1.tgz#4b48b4354b0ff917a30488d95100fbf7219a3e80"
|
||||
integrity sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.1.2"
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||
@ -5743,6 +5920,11 @@ scheduler@^0.23.0:
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
screenfull@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba"
|
||||
integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==
|
||||
|
||||
semver-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
@ -5779,6 +5961,18 @@ semver@~7.3.0:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
serialize-error@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-11.0.0.tgz#0129f2b07b19b09bc7a5f2d850ffe9cd2d561582"
|
||||
integrity sha512-YKrURWDqcT3VGX/s/pCwaWtpfJEEaEw5Y4gAnQDku92b/HjVj4r4UhA5QrMVMFotymK2wIWs5xthny5SMFu7Vw==
|
||||
dependencies:
|
||||
type-fest "^2.12.2"
|
||||
|
||||
set-harmonic-interval@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249"
|
||||
integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
@ -5877,6 +6071,11 @@ source-map-support@~0.5.20:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@0.5.6:
|
||||
version "0.5.6"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
|
||||
integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==
|
||||
|
||||
source-map@^0.5.7:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
@ -5892,6 +6091,11 @@ source-map@^0.7.4:
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
||||
|
||||
sourcemap-codec@^1.4.8:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
|
||||
|
||||
spawn-command@0.0.2-1:
|
||||
version "0.0.2-1"
|
||||
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
|
||||
@ -5902,6 +6106,35 @@ sprintf-js@~1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
|
||||
|
||||
stack-generator@^2.0.5:
|
||||
version "2.0.10"
|
||||
resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d"
|
||||
integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==
|
||||
dependencies:
|
||||
stackframe "^1.3.4"
|
||||
|
||||
stackframe@^1.3.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
|
||||
integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
|
||||
|
||||
stacktrace-gps@^3.0.4:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0"
|
||||
integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==
|
||||
dependencies:
|
||||
source-map "0.5.6"
|
||||
stackframe "^1.3.4"
|
||||
|
||||
stacktrace-js@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b"
|
||||
integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==
|
||||
dependencies:
|
||||
error-stack-parser "^2.0.6"
|
||||
stack-generator "^2.0.5"
|
||||
stacktrace-gps "^3.0.4"
|
||||
|
||||
stream-to-array@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353"
|
||||
@ -6028,7 +6261,7 @@ strip-json-comments@~2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
||||
|
||||
stylis@4.1.4:
|
||||
stylis@4.1.4, stylis@^4.0.6:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b"
|
||||
integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ==
|
||||
@ -6087,6 +6320,11 @@ text-table@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||
|
||||
throttle-debounce@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
|
||||
integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
|
||||
|
||||
through@^2.3.8:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
@ -6141,6 +6379,11 @@ tree-kill@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||
|
||||
ts-easing@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec"
|
||||
integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==
|
||||
|
||||
ts-error@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/ts-error/-/ts-error-1.0.6.tgz#277496f2a28de6c184cfce8dfd5cdd03a4e6b0fc"
|
||||
@ -6207,6 +6450,11 @@ tsconfig-paths@^4.0.0:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
||||
tslib@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
@ -6253,6 +6501,11 @@ type-fest@^0.21.3:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
||||
|
||||
type-fest@^2.12.2:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b"
|
||||
integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==
|
||||
|
||||
typed-array-length@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
||||
|
Loading…
Reference in New Issue
Block a user