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")
|
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)", )
|
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")
|
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", )
|
width: int = Field(default=512, multiple_of=8, 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", )
|
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", )
|
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" )
|
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", )
|
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
|
mask = None
|
||||||
|
|
||||||
|
if self.fit:
|
||||||
|
image = image.resize((self.width, self.height))
|
||||||
|
|
||||||
# Handle invalid model parameter
|
# Handle invalid model parameter
|
||||||
model = choose_model(context.services.model_manager, self.model)
|
model = choose_model(context.services.model_manager, self.model)
|
||||||
|
|
||||||
|
@ -113,8 +113,8 @@ class NoiseInvocation(BaseInvocation):
|
|||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
seed: int = Field(ge=0, le=np.iinfo(np.uint32).max, description="The seed to use", default_factory=random_seed)
|
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", )
|
width: int = Field(default=512, multiple_of=8, 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", )
|
height: int = Field(default=512, multiple_of=8, gt=0, description="The height of the resulting noise", )
|
||||||
|
|
||||||
|
|
||||||
# Schema customisation
|
# Schema customisation
|
||||||
|
@ -23,8 +23,6 @@ def create_text_to_image() -> LibraryGraph:
|
|||||||
edges=[
|
edges=[
|
||||||
Edge(source=EdgeConnection(node_id='width', field='a'), destination=EdgeConnection(node_id='3', field='width')),
|
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='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='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')),
|
Edge(source=EdgeConnection(node_id='4', field='latents'), destination=EdgeConnection(node_id='5', field='latents')),
|
||||||
]
|
]
|
||||||
|
@ -71,18 +71,12 @@ class Invoker:
|
|||||||
for service in vars(self.services):
|
for service in vars(self.services):
|
||||||
self.__start_service(getattr(self.services, service))
|
self.__start_service(getattr(self.services, service))
|
||||||
|
|
||||||
for service in vars(self.services):
|
|
||||||
self.__start_service(getattr(self.services, service))
|
|
||||||
|
|
||||||
def stop(self) -> None:
|
def stop(self) -> None:
|
||||||
"""Stops the invoker. A new invoker will have to be created to execute further."""
|
"""Stops the invoker. A new invoker will have to be created to execute further."""
|
||||||
# First stop all services
|
# First stop all services
|
||||||
for service in vars(self.services):
|
for service in vars(self.services):
|
||||||
self.__stop_service(getattr(self.services, service))
|
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)
|
self.services.queue.put(None)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import traceback
|
import traceback
|
||||||
from threading import Event, Thread
|
from threading import Event, Thread, BoundedSemaphore
|
||||||
|
|
||||||
from ..invocations.baseinvocation import InvocationContext
|
from ..invocations.baseinvocation import InvocationContext
|
||||||
from .invocation_queue import InvocationQueueItem
|
from .invocation_queue import InvocationQueueItem
|
||||||
@ -10,8 +10,11 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
|||||||
__invoker_thread: Thread
|
__invoker_thread: Thread
|
||||||
__stop_event: Event
|
__stop_event: Event
|
||||||
__invoker: Invoker
|
__invoker: Invoker
|
||||||
|
__threadLimit: BoundedSemaphore
|
||||||
|
|
||||||
def start(self, invoker) -> None:
|
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.__invoker = invoker
|
||||||
self.__stop_event = Event()
|
self.__stop_event = Event()
|
||||||
self.__invoker_thread = Thread(
|
self.__invoker_thread = Thread(
|
||||||
@ -20,7 +23,7 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
|||||||
kwargs=dict(stop_event=self.__stop_event),
|
kwargs=dict(stop_event=self.__stop_event),
|
||||||
)
|
)
|
||||||
self.__invoker_thread.daemon = (
|
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()
|
self.__invoker_thread.start()
|
||||||
|
|
||||||
@ -29,6 +32,7 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
|||||||
|
|
||||||
def __process(self, stop_event: Event):
|
def __process(self, stop_event: Event):
|
||||||
try:
|
try:
|
||||||
|
self.__threadLimit.acquire()
|
||||||
while not stop_event.is_set():
|
while not stop_event.is_set():
|
||||||
queue_item: InvocationQueueItem = self.__invoker.services.queue.get()
|
queue_item: InvocationQueueItem = self.__invoker.services.queue.get()
|
||||||
if not queue_item: # Probably stopping
|
if not queue_item: # Probably stopping
|
||||||
@ -127,4 +131,6 @@ class DefaultInvocationProcessor(InvocationProcessorABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
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",
|
"i18next-http-backend": "^2.2.0",
|
||||||
"konva": "^9.0.1",
|
"konva": "^9.0.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"overlayscrollbars": "^2.1.1",
|
||||||
|
"overlayscrollbars-react": "^0.5.0",
|
||||||
"patch-package": "^7.0.0",
|
"patch-package": "^7.0.0",
|
||||||
"re-resizable": "^6.9.9",
|
"re-resizable": "^6.9.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@ -88,13 +90,17 @@
|
|||||||
"react-konva": "^18.2.7",
|
"react-konva": "^18.2.7",
|
||||||
"react-konva-utils": "^1.0.4",
|
"react-konva-utils": "^1.0.4",
|
||||||
"react-redux": "^8.0.5",
|
"react-redux": "^8.0.5",
|
||||||
|
"react-rnd": "^10.4.1",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
|
"react-use": "^17.4.0",
|
||||||
|
"react-virtuoso": "^4.3.5",
|
||||||
"react-zoom-pan-pinch": "^3.0.7",
|
"react-zoom-pan-pinch": "^3.0.7",
|
||||||
"reactflow": "^11.7.0",
|
"reactflow": "^11.7.0",
|
||||||
"redux-deep-persist": "^1.0.7",
|
"redux-deep-persist": "^1.0.7",
|
||||||
"redux-dynamic-middlewares": "^2.2.0",
|
"redux-dynamic-middlewares": "^2.2.0",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"roarr": "^7.15.0",
|
"roarr": "^7.15.0",
|
||||||
|
"serialize-error": "^11.0.0",
|
||||||
"socket.io-client": "^4.6.0",
|
"socket.io-client": "^4.6.0",
|
||||||
"use-image": "^1.1.0",
|
"use-image": "^1.1.0",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
|
@ -527,6 +527,7 @@
|
|||||||
"useCanvasBeta": "Use Canvas Beta Layout",
|
"useCanvasBeta": "Use Canvas Beta Layout",
|
||||||
"enableImageDebugging": "Enable Image Debugging",
|
"enableImageDebugging": "Enable Image Debugging",
|
||||||
"useSlidersForAll": "Use Sliders For All Options",
|
"useSlidersForAll": "Use Sliders For All Options",
|
||||||
|
"autoShowProgress": "Auto Show Progress Images",
|
||||||
"resetWebUI": "Reset Web UI",
|
"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.",
|
"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.",
|
"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",
|
"betaDarkenOutside": "Darken Outside",
|
||||||
"betaLimitToBox": "Limit To Box",
|
"betaLimitToBox": "Limit To Box",
|
||||||
"betaPreserveMasked": "Preserve Masked"
|
"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 { configChanged } from 'features/system/store/configSlice';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { useLogger } from 'app/logging/useLogger';
|
import { useLogger } from 'app/logging/useLogger';
|
||||||
|
import ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {};
|
const DEFAULT_CONFIG = {};
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid w="100vw" h="100vh" position="relative">
|
<Grid w="100vw" h="100vh" position="relative" overflow="hidden">
|
||||||
{isLightboxEnabled && <Lightbox />}
|
{isLightboxEnabled && <Lightbox />}
|
||||||
<ImageUploader>
|
<ImageUploader>
|
||||||
<ProgressBar />
|
<ProgressBar />
|
||||||
@ -120,6 +121,7 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => {
|
|||||||
<Portal>
|
<Portal>
|
||||||
<FloatingGalleryButton />
|
<FloatingGalleryButton />
|
||||||
</Portal>
|
</Portal>
|
||||||
|
<ProgressImagePreview />
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { lazy, memo, PropsWithChildren, useEffect } from 'react';
|
import React, { lazy, memo, PropsWithChildren, useEffect } from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
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 { persistor } from '../store/persistor';
|
||||||
import { OpenAPI } from 'services/api';
|
import { OpenAPI } from 'services/api';
|
||||||
import '@fontsource/inter/100.css';
|
import '@fontsource/inter/100.css';
|
||||||
@ -19,6 +19,7 @@ import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
|
|||||||
import { PartialAppConfig } from 'app/types/invokeai';
|
import { PartialAppConfig } from 'app/types/invokeai';
|
||||||
|
|
||||||
import '../../i18n';
|
import '../../i18n';
|
||||||
|
import { socketMiddleware } from 'services/events/middleware';
|
||||||
|
|
||||||
const App = lazy(() => import('./App'));
|
const App = lazy(() => import('./App'));
|
||||||
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||||
@ -50,7 +51,7 @@ const InvokeAIUI = ({ apiUrl, token, config, children }: Props) => {
|
|||||||
// the `apiUrl`/`token` dynamically.
|
// the `apiUrl`/`token` dynamically.
|
||||||
|
|
||||||
// rebuild socket middleware with token and apiUrl
|
// rebuild socket middleware with token and apiUrl
|
||||||
addMiddleware(buildMiddleware());
|
addMiddleware(socketMiddleware());
|
||||||
}, [apiUrl, token]);
|
}, [apiUrl, token]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -18,6 +18,8 @@ import '@fontsource/inter/600.css';
|
|||||||
import '@fontsource/inter/700.css';
|
import '@fontsource/inter/700.css';
|
||||||
import '@fontsource/inter/800.css';
|
import '@fontsource/inter/800.css';
|
||||||
import '@fontsource/inter/900.css';
|
import '@fontsource/inter/900.css';
|
||||||
|
import 'overlayscrollbars/overlayscrollbars.css';
|
||||||
|
import 'theme/css/overlayscrollbars.css';
|
||||||
|
|
||||||
type ThemeLocaleProviderProps = {
|
type ThemeLocaleProviderProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
// TODO: use Enums?
|
// TODO: use Enums?
|
||||||
|
|
||||||
import { InProgressImageType } from 'features/system/store/systemSlice';
|
|
||||||
|
|
||||||
export const DIFFUSERS_SCHEDULERS: Array<string> = [
|
export const DIFFUSERS_SCHEDULERS: Array<string> = [
|
||||||
'ddim',
|
'ddim',
|
||||||
'plms',
|
'plms',
|
||||||
@ -33,17 +31,8 @@ export const UPSCALING_LEVELS: Array<{ key: string; value: number }> = [
|
|||||||
|
|
||||||
export const NUMPY_RAND_MIN = 0;
|
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 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;
|
export const NODE_MIN_WIDTH = 250;
|
||||||
|
@ -1,65 +1,67 @@
|
|||||||
import { createAction } from '@reduxjs/toolkit';
|
// import { createAction } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
// import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
// import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
// import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* We can't use redux-toolkit's createSlice() to make these actions,
|
// * We can't use redux-toolkit's createSlice() to make these actions,
|
||||||
* because they have no associated reducer. They only exist to dispatch
|
// * because they have no associated reducer. They only exist to dispatch
|
||||||
* requests to the server via socketio. These actions will be handled
|
// * requests to the server via socketio. These actions will be handled
|
||||||
* by the middleware.
|
// * by the middleware.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
export const generateImage = createAction<InvokeTabName>(
|
// export const generateImage = createAction<InvokeTabName>(
|
||||||
'socketio/generateImage'
|
// 'socketio/generateImage'
|
||||||
);
|
// );
|
||||||
export const runESRGAN = createAction<InvokeAI._Image>('socketio/runESRGAN');
|
// export const runESRGAN = createAction<InvokeAI._Image>('socketio/runESRGAN');
|
||||||
export const runFacetool = createAction<InvokeAI._Image>(
|
// export const runFacetool = createAction<InvokeAI._Image>(
|
||||||
'socketio/runFacetool'
|
// 'socketio/runFacetool'
|
||||||
);
|
// );
|
||||||
export const deleteImage = createAction<InvokeAI._Image>(
|
// export const deleteImage = createAction<InvokeAI._Image>(
|
||||||
'socketio/deleteImage'
|
// 'socketio/deleteImage'
|
||||||
);
|
// );
|
||||||
export const requestImages = createAction<GalleryCategory>(
|
// export const requestImages = createAction<GalleryCategory>(
|
||||||
'socketio/requestImages'
|
// 'socketio/requestImages'
|
||||||
);
|
// );
|
||||||
export const requestNewImages = createAction<GalleryCategory>(
|
// export const requestNewImages = createAction<GalleryCategory>(
|
||||||
'socketio/requestNewImages'
|
// 'socketio/requestNewImages'
|
||||||
);
|
// );
|
||||||
export const cancelProcessing = createAction<undefined>(
|
// export const cancelProcessing = createAction<undefined>(
|
||||||
'socketio/cancelProcessing'
|
// 'socketio/cancelProcessing'
|
||||||
);
|
// );
|
||||||
|
|
||||||
export const requestSystemConfig = createAction<undefined>(
|
// export const requestSystemConfig = createAction<undefined>(
|
||||||
'socketio/requestSystemConfig'
|
// 'socketio/requestSystemConfig'
|
||||||
);
|
// );
|
||||||
|
|
||||||
export const searchForModels = createAction<string>('socketio/searchForModels');
|
// export const searchForModels = createAction<string>('socketio/searchForModels');
|
||||||
|
|
||||||
export const addNewModel = createAction<
|
// export const addNewModel = createAction<
|
||||||
InvokeAI.InvokeModelConfigProps | InvokeAI.InvokeDiffusersModelConfigProps
|
// InvokeAI.InvokeModelConfigProps | InvokeAI.InvokeDiffusersModelConfigProps
|
||||||
>('socketio/addNewModel');
|
// >('socketio/addNewModel');
|
||||||
|
|
||||||
export const deleteModel = createAction<string>('socketio/deleteModel');
|
// export const deleteModel = createAction<string>('socketio/deleteModel');
|
||||||
|
|
||||||
export const convertToDiffusers =
|
// export const convertToDiffusers =
|
||||||
createAction<InvokeAI.InvokeModelConversionProps>(
|
// createAction<InvokeAI.InvokeModelConversionProps>(
|
||||||
'socketio/convertToDiffusers'
|
// 'socketio/convertToDiffusers'
|
||||||
);
|
// );
|
||||||
|
|
||||||
export const mergeDiffusersModels =
|
// export const mergeDiffusersModels =
|
||||||
createAction<InvokeAI.InvokeModelMergingProps>(
|
// createAction<InvokeAI.InvokeModelMergingProps>(
|
||||||
'socketio/mergeDiffusersModels'
|
// 'socketio/mergeDiffusersModels'
|
||||||
);
|
// );
|
||||||
|
|
||||||
export const requestModelChange = createAction<string>(
|
// export const requestModelChange = createAction<string>(
|
||||||
'socketio/requestModelChange'
|
// 'socketio/requestModelChange'
|
||||||
);
|
// );
|
||||||
|
|
||||||
export const saveStagingAreaImageToGallery = createAction<string>(
|
// export const saveStagingAreaImageToGallery = createAction<string>(
|
||||||
'socketio/saveStagingAreaImageToGallery'
|
// 'socketio/saveStagingAreaImageToGallery'
|
||||||
);
|
// );
|
||||||
|
|
||||||
export const emptyTempFolder = createAction<undefined>(
|
// export const emptyTempFolder = createAction<undefined>(
|
||||||
'socketio/requestEmptyTempFolder'
|
// 'socketio/requestEmptyTempFolder'
|
||||||
);
|
// );
|
||||||
|
|
||||||
|
export default {};
|
||||||
|
@ -1,207 +1,209 @@
|
|||||||
import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
// import * as InvokeAI from 'app/types/invokeai';
|
||||||
import type { RootState } from 'app/store/store';
|
// import type { RootState } from 'app/store/store';
|
||||||
import {
|
// import {
|
||||||
frontendToBackendParameters,
|
// frontendToBackendParameters,
|
||||||
FrontendToBackendParametersConfig,
|
// FrontendToBackendParametersConfig,
|
||||||
} from 'common/util/parameterTranslation';
|
// } from 'common/util/parameterTranslation';
|
||||||
import dateFormat from 'dateformat';
|
// import dateFormat from 'dateformat';
|
||||||
import {
|
// import {
|
||||||
GalleryCategory,
|
// GalleryCategory,
|
||||||
GalleryState,
|
// GalleryState,
|
||||||
removeImage,
|
// removeImage,
|
||||||
} from 'features/gallery/store/gallerySlice';
|
// } from 'features/gallery/store/gallerySlice';
|
||||||
import {
|
// import {
|
||||||
generationRequested,
|
// generationRequested,
|
||||||
modelChangeRequested,
|
// modelChangeRequested,
|
||||||
modelConvertRequested,
|
// modelConvertRequested,
|
||||||
modelMergingRequested,
|
// modelMergingRequested,
|
||||||
setIsProcessing,
|
// setIsProcessing,
|
||||||
} from 'features/system/store/systemSlice';
|
// } from 'features/system/store/systemSlice';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
// import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
import { Socket } from 'socket.io-client';
|
// import { Socket } from 'socket.io-client';
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Returns an object containing all functions which use `socketio.emit()`.
|
// * Returns an object containing all functions which use `socketio.emit()`.
|
||||||
* i.e. those which make server requests.
|
// * i.e. those which make server requests.
|
||||||
*/
|
// */
|
||||||
const makeSocketIOEmitters = (
|
// const makeSocketIOEmitters = (
|
||||||
store: MiddlewareAPI<Dispatch<AnyAction>, RootState>,
|
// store: MiddlewareAPI<Dispatch<AnyAction>, RootState>,
|
||||||
socketio: Socket
|
// socketio: Socket
|
||||||
) => {
|
// ) => {
|
||||||
// We need to dispatch actions to redux and get pieces of state from the store.
|
// // We need to dispatch actions to redux and get pieces of state from the store.
|
||||||
const { dispatch, getState } = store;
|
// const { dispatch, getState } = store;
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
emitGenerateImage: (generationMode: InvokeTabName) => {
|
// emitGenerateImage: (generationMode: InvokeTabName) => {
|
||||||
dispatch(setIsProcessing(true));
|
// dispatch(setIsProcessing(true));
|
||||||
|
|
||||||
const state: RootState = getState();
|
// const state: RootState = getState();
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
generation: generationState,
|
// generation: generationState,
|
||||||
postprocessing: postprocessingState,
|
// postprocessing: postprocessingState,
|
||||||
system: systemState,
|
// system: systemState,
|
||||||
canvas: canvasState,
|
// canvas: canvasState,
|
||||||
} = state;
|
// } = state;
|
||||||
|
|
||||||
const frontendToBackendParametersConfig: FrontendToBackendParametersConfig =
|
// const frontendToBackendParametersConfig: FrontendToBackendParametersConfig =
|
||||||
{
|
// {
|
||||||
generationMode,
|
// generationMode,
|
||||||
generationState,
|
// generationState,
|
||||||
postprocessingState,
|
// postprocessingState,
|
||||||
canvasState,
|
// canvasState,
|
||||||
systemState,
|
// systemState,
|
||||||
};
|
// };
|
||||||
|
|
||||||
dispatch(generationRequested());
|
// dispatch(generationRequested());
|
||||||
|
|
||||||
const { generationParameters, esrganParameters, facetoolParameters } =
|
// const { generationParameters, esrganParameters, facetoolParameters } =
|
||||||
frontendToBackendParameters(frontendToBackendParametersConfig);
|
// frontendToBackendParameters(frontendToBackendParametersConfig);
|
||||||
|
|
||||||
socketio.emit(
|
// socketio.emit(
|
||||||
'generateImage',
|
// 'generateImage',
|
||||||
generationParameters,
|
// generationParameters,
|
||||||
esrganParameters,
|
// esrganParameters,
|
||||||
facetoolParameters
|
// facetoolParameters
|
||||||
);
|
// );
|
||||||
|
|
||||||
// we need to truncate the init_mask base64 else it takes up the whole log
|
// // we need to truncate the init_mask base64 else it takes up the whole log
|
||||||
// TODO: handle maintaining masks for reproducibility in future
|
// // TODO: handle maintaining masks for reproducibility in future
|
||||||
if (generationParameters.init_mask) {
|
// if (generationParameters.init_mask) {
|
||||||
generationParameters.init_mask = generationParameters.init_mask
|
// generationParameters.init_mask = generationParameters.init_mask
|
||||||
.substr(0, 64)
|
// .substr(0, 64)
|
||||||
.concat('...');
|
// .concat('...');
|
||||||
}
|
// }
|
||||||
if (generationParameters.init_img) {
|
// if (generationParameters.init_img) {
|
||||||
generationParameters.init_img = generationParameters.init_img
|
// generationParameters.init_img = generationParameters.init_img
|
||||||
.substr(0, 64)
|
// .substr(0, 64)
|
||||||
.concat('...');
|
// .concat('...');
|
||||||
}
|
// }
|
||||||
|
|
||||||
dispatch(
|
// dispatch(
|
||||||
addLogEntry({
|
// addLogEntry({
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
message: `Image generation requested: ${JSON.stringify({
|
// message: `Image generation requested: ${JSON.stringify({
|
||||||
...generationParameters,
|
// ...generationParameters,
|
||||||
...esrganParameters,
|
// ...esrganParameters,
|
||||||
...facetoolParameters,
|
// ...facetoolParameters,
|
||||||
})}`,
|
// })}`,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
emitRunESRGAN: (imageToProcess: InvokeAI._Image) => {
|
// emitRunESRGAN: (imageToProcess: InvokeAI._Image) => {
|
||||||
dispatch(setIsProcessing(true));
|
// dispatch(setIsProcessing(true));
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
postprocessing: {
|
// postprocessing: {
|
||||||
upscalingLevel,
|
// upscalingLevel,
|
||||||
upscalingDenoising,
|
// upscalingDenoising,
|
||||||
upscalingStrength,
|
// upscalingStrength,
|
||||||
},
|
// },
|
||||||
} = getState();
|
// } = getState();
|
||||||
|
|
||||||
const esrganParameters = {
|
// const esrganParameters = {
|
||||||
upscale: [upscalingLevel, upscalingDenoising, upscalingStrength],
|
// upscale: [upscalingLevel, upscalingDenoising, upscalingStrength],
|
||||||
};
|
// };
|
||||||
socketio.emit('runPostprocessing', imageToProcess, {
|
// socketio.emit('runPostprocessing', imageToProcess, {
|
||||||
type: 'esrgan',
|
// type: 'esrgan',
|
||||||
...esrganParameters,
|
// ...esrganParameters,
|
||||||
});
|
// });
|
||||||
dispatch(
|
// dispatch(
|
||||||
addLogEntry({
|
// addLogEntry({
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
message: `ESRGAN upscale requested: ${JSON.stringify({
|
// message: `ESRGAN upscale requested: ${JSON.stringify({
|
||||||
file: imageToProcess.url,
|
// file: imageToProcess.url,
|
||||||
...esrganParameters,
|
// ...esrganParameters,
|
||||||
})}`,
|
// })}`,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
emitRunFacetool: (imageToProcess: InvokeAI._Image) => {
|
// emitRunFacetool: (imageToProcess: InvokeAI._Image) => {
|
||||||
dispatch(setIsProcessing(true));
|
// dispatch(setIsProcessing(true));
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
postprocessing: { facetoolType, facetoolStrength, codeformerFidelity },
|
// postprocessing: { facetoolType, facetoolStrength, codeformerFidelity },
|
||||||
} = getState();
|
// } = getState();
|
||||||
|
|
||||||
const facetoolParameters: Record<string, unknown> = {
|
// const facetoolParameters: Record<string, unknown> = {
|
||||||
facetool_strength: facetoolStrength,
|
// facetool_strength: facetoolStrength,
|
||||||
};
|
// };
|
||||||
|
|
||||||
if (facetoolType === 'codeformer') {
|
// if (facetoolType === 'codeformer') {
|
||||||
facetoolParameters.codeformer_fidelity = codeformerFidelity;
|
// facetoolParameters.codeformer_fidelity = codeformerFidelity;
|
||||||
}
|
// }
|
||||||
|
|
||||||
socketio.emit('runPostprocessing', imageToProcess, {
|
// socketio.emit('runPostprocessing', imageToProcess, {
|
||||||
type: facetoolType,
|
// type: facetoolType,
|
||||||
...facetoolParameters,
|
// ...facetoolParameters,
|
||||||
});
|
// });
|
||||||
dispatch(
|
// dispatch(
|
||||||
addLogEntry({
|
// addLogEntry({
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
message: `Face restoration (${facetoolType}) requested: ${JSON.stringify(
|
// message: `Face restoration (${facetoolType}) requested: ${JSON.stringify(
|
||||||
{
|
// {
|
||||||
file: imageToProcess.url,
|
// file: imageToProcess.url,
|
||||||
...facetoolParameters,
|
// ...facetoolParameters,
|
||||||
}
|
// }
|
||||||
)}`,
|
// )}`,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
emitDeleteImage: (imageToDelete: InvokeAI._Image) => {
|
// emitDeleteImage: (imageToDelete: InvokeAI._Image) => {
|
||||||
const { url, uuid, category, thumbnail } = imageToDelete;
|
// const { url, uuid, category, thumbnail } = imageToDelete;
|
||||||
dispatch(removeImage(imageToDelete));
|
// dispatch(removeImage(imageToDelete));
|
||||||
socketio.emit('deleteImage', url, thumbnail, uuid, category);
|
// socketio.emit('deleteImage', url, thumbnail, uuid, category);
|
||||||
},
|
// },
|
||||||
emitRequestImages: (category: GalleryCategory) => {
|
// emitRequestImages: (category: GalleryCategory) => {
|
||||||
const gallery: GalleryState = getState().gallery;
|
// const gallery: GalleryState = getState().gallery;
|
||||||
const { earliest_mtime } = gallery.categories[category];
|
// const { earliest_mtime } = gallery.categories[category];
|
||||||
socketio.emit('requestImages', category, earliest_mtime);
|
// socketio.emit('requestImages', category, earliest_mtime);
|
||||||
},
|
// },
|
||||||
emitRequestNewImages: (category: GalleryCategory) => {
|
// emitRequestNewImages: (category: GalleryCategory) => {
|
||||||
const gallery: GalleryState = getState().gallery;
|
// const gallery: GalleryState = getState().gallery;
|
||||||
const { latest_mtime } = gallery.categories[category];
|
// const { latest_mtime } = gallery.categories[category];
|
||||||
socketio.emit('requestLatestImages', category, latest_mtime);
|
// socketio.emit('requestLatestImages', category, latest_mtime);
|
||||||
},
|
// },
|
||||||
emitCancelProcessing: () => {
|
// emitCancelProcessing: () => {
|
||||||
socketio.emit('cancel');
|
// socketio.emit('cancel');
|
||||||
},
|
// },
|
||||||
emitRequestSystemConfig: () => {
|
// emitRequestSystemConfig: () => {
|
||||||
socketio.emit('requestSystemConfig');
|
// socketio.emit('requestSystemConfig');
|
||||||
},
|
// },
|
||||||
emitSearchForModels: (modelFolder: string) => {
|
// emitSearchForModels: (modelFolder: string) => {
|
||||||
socketio.emit('searchForModels', modelFolder);
|
// socketio.emit('searchForModels', modelFolder);
|
||||||
},
|
// },
|
||||||
emitAddNewModel: (modelConfig: InvokeAI.InvokeModelConfigProps) => {
|
// emitAddNewModel: (modelConfig: InvokeAI.InvokeModelConfigProps) => {
|
||||||
socketio.emit('addNewModel', modelConfig);
|
// socketio.emit('addNewModel', modelConfig);
|
||||||
},
|
// },
|
||||||
emitDeleteModel: (modelName: string) => {
|
// emitDeleteModel: (modelName: string) => {
|
||||||
socketio.emit('deleteModel', modelName);
|
// socketio.emit('deleteModel', modelName);
|
||||||
},
|
// },
|
||||||
emitConvertToDiffusers: (
|
// emitConvertToDiffusers: (
|
||||||
modelToConvert: InvokeAI.InvokeModelConversionProps
|
// modelToConvert: InvokeAI.InvokeModelConversionProps
|
||||||
) => {
|
// ) => {
|
||||||
dispatch(modelConvertRequested());
|
// dispatch(modelConvertRequested());
|
||||||
socketio.emit('convertToDiffusers', modelToConvert);
|
// socketio.emit('convertToDiffusers', modelToConvert);
|
||||||
},
|
// },
|
||||||
emitMergeDiffusersModels: (
|
// emitMergeDiffusersModels: (
|
||||||
modelMergeInfo: InvokeAI.InvokeModelMergingProps
|
// modelMergeInfo: InvokeAI.InvokeModelMergingProps
|
||||||
) => {
|
// ) => {
|
||||||
dispatch(modelMergingRequested());
|
// dispatch(modelMergingRequested());
|
||||||
socketio.emit('mergeDiffusersModels', modelMergeInfo);
|
// socketio.emit('mergeDiffusersModels', modelMergeInfo);
|
||||||
},
|
// },
|
||||||
emitRequestModelChange: (modelName: string) => {
|
// emitRequestModelChange: (modelName: string) => {
|
||||||
dispatch(modelChangeRequested());
|
// dispatch(modelChangeRequested());
|
||||||
socketio.emit('requestModelChange', modelName);
|
// socketio.emit('requestModelChange', modelName);
|
||||||
},
|
// },
|
||||||
emitSaveStagingAreaImageToGallery: (url: string) => {
|
// emitSaveStagingAreaImageToGallery: (url: string) => {
|
||||||
socketio.emit('requestSaveStagingAreaImageToGallery', url);
|
// socketio.emit('requestSaveStagingAreaImageToGallery', url);
|
||||||
},
|
// },
|
||||||
emitRequestEmptyTempFolder: () => {
|
// emitRequestEmptyTempFolder: () => {
|
||||||
socketio.emit('requestEmptyTempFolder');
|
// socketio.emit('requestEmptyTempFolder');
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
};
|
// };
|
||||||
|
|
||||||
export default makeSocketIOEmitters;
|
// export default makeSocketIOEmitters;
|
||||||
|
|
||||||
|
export default {};
|
||||||
|
@ -1,500 +1,502 @@
|
|||||||
import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||||
import dateFormat from 'dateformat';
|
// import dateFormat from 'dateformat';
|
||||||
import i18n from 'i18n';
|
// import i18n from 'i18n';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
// import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
// import * as InvokeAI from 'app/types/invokeai';
|
||||||
|
|
||||||
import {
|
// import {
|
||||||
addToast,
|
// addToast,
|
||||||
errorOccurred,
|
// errorOccurred,
|
||||||
processingCanceled,
|
// processingCanceled,
|
||||||
setCurrentStatus,
|
// setCurrentStatus,
|
||||||
setFoundModels,
|
// setFoundModels,
|
||||||
setIsCancelable,
|
// setIsCancelable,
|
||||||
setIsConnected,
|
// setIsConnected,
|
||||||
setIsProcessing,
|
// setIsProcessing,
|
||||||
setModelList,
|
// setModelList,
|
||||||
setSearchFolder,
|
// setSearchFolder,
|
||||||
setSystemConfig,
|
// setSystemConfig,
|
||||||
setSystemStatus,
|
// setSystemStatus,
|
||||||
} from 'features/system/store/systemSlice';
|
// } from 'features/system/store/systemSlice';
|
||||||
|
|
||||||
import {
|
// import {
|
||||||
addGalleryImages,
|
// addGalleryImages,
|
||||||
addImage,
|
// addImage,
|
||||||
clearIntermediateImage,
|
// clearIntermediateImage,
|
||||||
GalleryState,
|
// GalleryState,
|
||||||
removeImage,
|
// removeImage,
|
||||||
setIntermediateImage,
|
// setIntermediateImage,
|
||||||
} from 'features/gallery/store/gallerySlice';
|
// } from 'features/gallery/store/gallerySlice';
|
||||||
|
|
||||||
import type { RootState } from 'app/store/store';
|
// import type { RootState } from 'app/store/store';
|
||||||
import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
|
// import { addImageToStagingArea } from 'features/canvas/store/canvasSlice';
|
||||||
import {
|
// import {
|
||||||
clearInitialImage,
|
// clearInitialImage,
|
||||||
initialImageSelected,
|
// initialImageSelected,
|
||||||
setInfillMethod,
|
// setInfillMethod,
|
||||||
// setInitialImage,
|
// // setInitialImage,
|
||||||
setMaskPath,
|
// setMaskPath,
|
||||||
} from 'features/parameters/store/generationSlice';
|
// } from 'features/parameters/store/generationSlice';
|
||||||
import { tabMap } from 'features/ui/store/tabMap';
|
// import { tabMap } from 'features/ui/store/tabMap';
|
||||||
import {
|
// import {
|
||||||
requestImages,
|
// requestImages,
|
||||||
requestNewImages,
|
// requestNewImages,
|
||||||
requestSystemConfig,
|
// requestSystemConfig,
|
||||||
} from './actions';
|
// } from './actions';
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Returns an object containing listener callbacks for socketio events.
|
// * Returns an object containing listener callbacks for socketio events.
|
||||||
* TODO: This file is large, but simple. Should it be split up further?
|
// * TODO: This file is large, but simple. Should it be split up further?
|
||||||
*/
|
// */
|
||||||
const makeSocketIOListeners = (
|
// const makeSocketIOListeners = (
|
||||||
store: MiddlewareAPI<Dispatch<AnyAction>, RootState>
|
// store: MiddlewareAPI<Dispatch<AnyAction>, RootState>
|
||||||
) => {
|
// ) => {
|
||||||
const { dispatch, getState } = store;
|
// const { dispatch, getState } = store;
|
||||||
|
|
||||||
return {
|
// return {
|
||||||
/**
|
// /**
|
||||||
* Callback to run when we receive a 'connect' event.
|
// * Callback to run when we receive a 'connect' event.
|
||||||
*/
|
// */
|
||||||
onConnect: () => {
|
// onConnect: () => {
|
||||||
try {
|
// try {
|
||||||
dispatch(setIsConnected(true));
|
// dispatch(setIsConnected(true));
|
||||||
dispatch(setCurrentStatus(i18n.t('common.statusConnected')));
|
// dispatch(setCurrentStatus(i18n.t('common.statusConnected')));
|
||||||
dispatch(requestSystemConfig());
|
// dispatch(requestSystemConfig());
|
||||||
const gallery: GalleryState = getState().gallery;
|
// const gallery: GalleryState = getState().gallery;
|
||||||
|
|
||||||
if (gallery.categories.result.latest_mtime) {
|
// if (gallery.categories.result.latest_mtime) {
|
||||||
dispatch(requestNewImages('result'));
|
// dispatch(requestNewImages('result'));
|
||||||
} else {
|
// } else {
|
||||||
dispatch(requestImages('result'));
|
// 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')));
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (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;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
// }
|
||||||
|
|
||||||
dispatch(clearIntermediateImage());
|
// 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(
|
// dispatch(
|
||||||
addLogEntry({
|
// addLogEntry({
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
message: `Image generated: ${data.url}`,
|
// message: `Disconnected from server`,
|
||||||
})
|
// level: 'warning',
|
||||||
);
|
// })
|
||||||
} catch (e) {
|
// );
|
||||||
console.error(e);
|
// } catch (e) {
|
||||||
}
|
// console.error(e);
|
||||||
},
|
// }
|
||||||
/**
|
// },
|
||||||
* Callback to run when we receive a 'intermediateResult' event.
|
// /**
|
||||||
*/
|
// * Callback to run when we receive a 'generationResult' event.
|
||||||
onIntermediateResult: (data: InvokeAI.ImageResultResponse) => {
|
// */
|
||||||
try {
|
// onGenerationResult: (data: InvokeAI.ImageResultResponse) => {
|
||||||
dispatch(
|
// try {
|
||||||
setIntermediateImage({
|
// const state = getState();
|
||||||
uuid: uuidv4(),
|
// const { activeTab } = state.ui;
|
||||||
...data,
|
// const { shouldLoopback } = state.postprocessing;
|
||||||
category: 'result',
|
// const { boundingBox: _, generationMode, ...rest } = data;
|
||||||
})
|
|
||||||
);
|
|
||||||
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(
|
// const newImage = {
|
||||||
addLogEntry({
|
// uuid: uuidv4(),
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// ...rest,
|
||||||
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) {
|
// if (['txt2img', 'img2img'].includes(generationMode)) {
|
||||||
// TODO: handle more data than short message
|
// dispatch(
|
||||||
}
|
// addImage({
|
||||||
|
// category: 'result',
|
||||||
|
// image: { ...newImage, category: 'result' },
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
try {
|
// if (generationMode === 'unifiedCanvas' && data.boundingBox) {
|
||||||
dispatch(
|
// const { boundingBox } = data;
|
||||||
addLogEntry({
|
// dispatch(
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// addImageToStagingArea({
|
||||||
message: `Server error: ${message}`,
|
// image: { ...newImage, category: 'temp' },
|
||||||
level: 'error',
|
// boundingBox,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
// if (state.canvas.shouldAutoSave) {
|
||||||
* the logic here ideally would be in the reducer but we have a side effect:
|
// dispatch(
|
||||||
* generating a uuid. so the logic needs to be here, outside redux.
|
// addImage({
|
||||||
*/
|
// image: { ...newImage, category: 'result' },
|
||||||
|
// category: 'result',
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// Generate a UUID for each image
|
// // TODO: fix
|
||||||
const preparedImages = images.map((image): InvokeAI._Image => {
|
// // if (shouldLoopback) {
|
||||||
return {
|
// // const activeTabName = tabMap[activeTab];
|
||||||
uuid: uuidv4(),
|
// // switch (activeTabName) {
|
||||||
...image,
|
// // case 'img2img': {
|
||||||
};
|
// // dispatch(initialImageSelected(newImage.uuid));
|
||||||
});
|
// // // dispatch(setInitialImage(newImage));
|
||||||
|
// // break;
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
dispatch(
|
// dispatch(clearIntermediateImage());
|
||||||
addGalleryImages({
|
|
||||||
images: preparedImages,
|
|
||||||
areMoreImagesAvailable,
|
|
||||||
category,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
dispatch(
|
// dispatch(
|
||||||
addLogEntry({
|
// addLogEntry({
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
message: `Loaded ${images.length} images`,
|
// message: `Image generated: ${data.url}`,
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
},
|
// } catch (e) {
|
||||||
/**
|
// console.error(e);
|
||||||
* Callback to run when we receive a 'processingCanceled' event.
|
// }
|
||||||
*/
|
// },
|
||||||
onProcessingCanceled: () => {
|
// /**
|
||||||
dispatch(processingCanceled());
|
// * 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',
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
|
||||||
const { intermediateImage } = getState().gallery;
|
// 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 (intermediateImage) {
|
// if (additionalData) {
|
||||||
if (!intermediateImage.isBase64) {
|
// // TODO: handle more data than short message
|
||||||
dispatch(
|
// }
|
||||||
addImage({
|
|
||||||
category: 'result',
|
|
||||||
image: intermediateImage,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
addLogEntry({
|
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
|
||||||
message: `Intermediate image saved: ${intermediateImage.url}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch(clearIntermediateImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
// try {
|
||||||
addLogEntry({
|
// dispatch(
|
||||||
timestamp: dateFormat(new Date(), 'isoDateTime'),
|
// addLogEntry({
|
||||||
message: `Processing canceled`,
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
level: 'warning',
|
// message: `Server error: ${message}`,
|
||||||
})
|
// level: 'error',
|
||||||
);
|
// })
|
||||||
},
|
// );
|
||||||
/**
|
// dispatch(errorOccurred());
|
||||||
* Callback to run when we receive a 'imageDeleted' event.
|
// dispatch(clearIntermediateImage());
|
||||||
*/
|
// } catch (e) {
|
||||||
onImageDeleted: (data: InvokeAI.ImageDeletedResponse) => {
|
// console.error(e);
|
||||||
const { url } = data;
|
// }
|
||||||
|
// },
|
||||||
|
// /**
|
||||||
|
// * Callback to run when we receive a 'galleryImages' event.
|
||||||
|
// */
|
||||||
|
// onGalleryImages: (data: InvokeAI.GalleryImagesResponse) => {
|
||||||
|
// const { images, areMoreImagesAvailable, category } = data;
|
||||||
|
|
||||||
// remove image from gallery
|
// /**
|
||||||
dispatch(removeImage(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.
|
||||||
|
// */
|
||||||
|
|
||||||
// remove references to image in options
|
// // Generate a UUID for each image
|
||||||
const {
|
// const preparedImages = images.map((image): InvokeAI._Image => {
|
||||||
generation: { initialImage, maskPath },
|
// return {
|
||||||
} = getState();
|
// uuid: uuidv4(),
|
||||||
|
// ...image,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
if (
|
// dispatch(
|
||||||
initialImage === url ||
|
// addGalleryImages({
|
||||||
(initialImage as InvokeAI._Image)?.url === url
|
// images: preparedImages,
|
||||||
) {
|
// areMoreImagesAvailable,
|
||||||
dispatch(clearInitialImage());
|
// category,
|
||||||
}
|
// })
|
||||||
|
// );
|
||||||
|
|
||||||
if (maskPath === url) {
|
// dispatch(
|
||||||
dispatch(setMaskPath(''));
|
// addLogEntry({
|
||||||
}
|
// timestamp: dateFormat(new Date(), 'isoDateTime'),
|
||||||
|
// message: `Loaded ${images.length} images`,
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// /**
|
||||||
|
// * Callback to run when we receive a 'processingCanceled' event.
|
||||||
|
// */
|
||||||
|
// onProcessingCanceled: () => {
|
||||||
|
// dispatch(processingCanceled());
|
||||||
|
|
||||||
dispatch(
|
// const { intermediateImage } = getState().gallery;
|
||||||
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;
|
// 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;
|
||||||
|
|
||||||
|
// // remove image from gallery
|
||||||
|
// dispatch(removeImage(data));
|
||||||
|
|
||||||
|
// // remove references to image in options
|
||||||
|
// const {
|
||||||
|
// generation: { initialImage, maskPath },
|
||||||
|
// } = getState();
|
||||||
|
|
||||||
|
// if (
|
||||||
|
// initialImage === url ||
|
||||||
|
// (initialImage as InvokeAI._Image)?.url === url
|
||||||
|
// ) {
|
||||||
|
// dispatch(clearInitialImage());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// })
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
|
||||||
|
// export default makeSocketIOListeners;
|
||||||
|
|
||||||
|
export default {};
|
||||||
|
@ -1,246 +1,248 @@
|
|||||||
import { Middleware } from '@reduxjs/toolkit';
|
// import { Middleware } from '@reduxjs/toolkit';
|
||||||
import { io } from 'socket.io-client';
|
// import { io } from 'socket.io-client';
|
||||||
|
|
||||||
import makeSocketIOEmitters from './emitters';
|
// import makeSocketIOEmitters from './emitters';
|
||||||
import makeSocketIOListeners from './listeners';
|
// 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.
|
// * Creates a socketio middleware to handle communication with server.
|
||||||
*
|
// *
|
||||||
* Special `socketio/actionName` actions are created in actions.ts and
|
// * Special `socketio/actionName` actions are created in actions.ts and
|
||||||
* exported for use by the application, which treats them like any old
|
// * exported for use by the application, which treats them like any old
|
||||||
* action, using `dispatch` to dispatch them.
|
// * action, using `dispatch` to dispatch them.
|
||||||
*
|
// *
|
||||||
* These actions are intercepted here, where `socketio.emit()` calls are
|
// * These actions are intercepted here, where `socketio.emit()` calls are
|
||||||
* made on their behalf - see `emitters.ts`. The emitter functions
|
// * made on their behalf - see `emitters.ts`. The emitter functions
|
||||||
* are the outbound communication to the server.
|
// * are the outbound communication to the server.
|
||||||
*
|
// *
|
||||||
* Listeners are also established here - see `listeners.ts`. The listener
|
// * Listeners are also established here - see `listeners.ts`. The listener
|
||||||
* functions receive communication from the server and usually dispatch
|
// * functions receive communication from the server and usually dispatch
|
||||||
* some new action to handle whatever data was sent from the server.
|
// * some new action to handle whatever data was sent from the server.
|
||||||
*/
|
// */
|
||||||
export const socketioMiddleware = () => {
|
// export const socketioMiddleware = () => {
|
||||||
const { origin } = new URL(window.location.href);
|
// const { origin } = new URL(window.location.href);
|
||||||
|
|
||||||
const socketio = io(origin, {
|
// const socketio = io(origin, {
|
||||||
timeout: 60000,
|
// timeout: 60000,
|
||||||
path: `${window.location.pathname}socket.io`,
|
// path: `${window.location.pathname}socket.io`,
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.disconnect();
|
// socketio.disconnect();
|
||||||
|
|
||||||
let areListenersSet = false;
|
// let areListenersSet = false;
|
||||||
|
|
||||||
const middleware: Middleware = (store) => (next) => (action) => {
|
// const middleware: Middleware = (store) => (next) => (action) => {
|
||||||
const {
|
// const {
|
||||||
onConnect,
|
// onConnect,
|
||||||
onDisconnect,
|
// onDisconnect,
|
||||||
onError,
|
// onError,
|
||||||
onPostprocessingResult,
|
// onPostprocessingResult,
|
||||||
onGenerationResult,
|
// onGenerationResult,
|
||||||
onIntermediateResult,
|
// onIntermediateResult,
|
||||||
onProgressUpdate,
|
// onProgressUpdate,
|
||||||
onGalleryImages,
|
// onGalleryImages,
|
||||||
onProcessingCanceled,
|
// onProcessingCanceled,
|
||||||
onImageDeleted,
|
// onImageDeleted,
|
||||||
onSystemConfig,
|
// onSystemConfig,
|
||||||
onModelChanged,
|
// onModelChanged,
|
||||||
onFoundModels,
|
// onFoundModels,
|
||||||
onNewModelAdded,
|
// onNewModelAdded,
|
||||||
onModelDeleted,
|
// onModelDeleted,
|
||||||
onModelConverted,
|
// onModelConverted,
|
||||||
onModelsMerged,
|
// onModelsMerged,
|
||||||
onModelChangeFailed,
|
// onModelChangeFailed,
|
||||||
onTempFolderEmptied,
|
// onTempFolderEmptied,
|
||||||
} = makeSocketIOListeners(store);
|
// } = makeSocketIOListeners(store);
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
emitGenerateImage,
|
// emitGenerateImage,
|
||||||
emitRunESRGAN,
|
// emitRunESRGAN,
|
||||||
emitRunFacetool,
|
// emitRunFacetool,
|
||||||
emitDeleteImage,
|
// emitDeleteImage,
|
||||||
emitRequestImages,
|
// emitRequestImages,
|
||||||
emitRequestNewImages,
|
// emitRequestNewImages,
|
||||||
emitCancelProcessing,
|
// emitCancelProcessing,
|
||||||
emitRequestSystemConfig,
|
// emitRequestSystemConfig,
|
||||||
emitSearchForModels,
|
// emitSearchForModels,
|
||||||
emitAddNewModel,
|
// emitAddNewModel,
|
||||||
emitDeleteModel,
|
// emitDeleteModel,
|
||||||
emitConvertToDiffusers,
|
// emitConvertToDiffusers,
|
||||||
emitMergeDiffusersModels,
|
// emitMergeDiffusersModels,
|
||||||
emitRequestModelChange,
|
// emitRequestModelChange,
|
||||||
emitSaveStagingAreaImageToGallery,
|
// emitSaveStagingAreaImageToGallery,
|
||||||
emitRequestEmptyTempFolder,
|
// emitRequestEmptyTempFolder,
|
||||||
} = makeSocketIOEmitters(store, socketio);
|
// } = makeSocketIOEmitters(store, socketio);
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* If this is the first time the middleware has been called (e.g. during store setup),
|
// * If this is the first time the middleware has been called (e.g. during store setup),
|
||||||
* initialize all our socket.io listeners.
|
// * initialize all our socket.io listeners.
|
||||||
*/
|
// */
|
||||||
if (!areListenersSet) {
|
// if (!areListenersSet) {
|
||||||
socketio.on('connect', () => onConnect());
|
// 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) =>
|
// socketio.on('generationResult', (data: InvokeAI.ImageResultResponse) =>
|
||||||
onGenerationResult(data)
|
// onGenerationResult(data)
|
||||||
);
|
// );
|
||||||
|
|
||||||
socketio.on(
|
// socketio.on(
|
||||||
'postprocessingResult',
|
// 'postprocessingResult',
|
||||||
(data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data)
|
// (data: InvokeAI.ImageResultResponse) => onPostprocessingResult(data)
|
||||||
);
|
// );
|
||||||
|
|
||||||
socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) =>
|
// socketio.on('intermediateResult', (data: InvokeAI.ImageResultResponse) =>
|
||||||
onIntermediateResult(data)
|
// onIntermediateResult(data)
|
||||||
);
|
// );
|
||||||
|
|
||||||
socketio.on('progressUpdate', (data: InvokeAI.SystemStatus) =>
|
// socketio.on('progressUpdate', (data: InvokeAI.SystemStatus) =>
|
||||||
onProgressUpdate(data)
|
// onProgressUpdate(data)
|
||||||
);
|
// );
|
||||||
|
|
||||||
socketio.on('galleryImages', (data: InvokeAI.GalleryImagesResponse) =>
|
// socketio.on('galleryImages', (data: InvokeAI.GalleryImagesResponse) =>
|
||||||
onGalleryImages(data)
|
// onGalleryImages(data)
|
||||||
);
|
// );
|
||||||
|
|
||||||
socketio.on('processingCanceled', () => {
|
// socketio.on('processingCanceled', () => {
|
||||||
onProcessingCanceled();
|
// onProcessingCanceled();
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('imageDeleted', (data: InvokeAI.ImageDeletedResponse) => {
|
// socketio.on('imageDeleted', (data: InvokeAI.ImageDeletedResponse) => {
|
||||||
onImageDeleted(data);
|
// onImageDeleted(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => {
|
// socketio.on('systemConfig', (data: InvokeAI.SystemConfig) => {
|
||||||
onSystemConfig(data);
|
// onSystemConfig(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('foundModels', (data: InvokeAI.FoundModelResponse) => {
|
// socketio.on('foundModels', (data: InvokeAI.FoundModelResponse) => {
|
||||||
onFoundModels(data);
|
// onFoundModels(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('newModelAdded', (data: InvokeAI.ModelAddedResponse) => {
|
// socketio.on('newModelAdded', (data: InvokeAI.ModelAddedResponse) => {
|
||||||
onNewModelAdded(data);
|
// onNewModelAdded(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('modelDeleted', (data: InvokeAI.ModelDeletedResponse) => {
|
// socketio.on('modelDeleted', (data: InvokeAI.ModelDeletedResponse) => {
|
||||||
onModelDeleted(data);
|
// onModelDeleted(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => {
|
// socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => {
|
||||||
onModelConverted(data);
|
// onModelConverted(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('modelsMerged', (data: InvokeAI.ModelsMergedResponse) => {
|
// socketio.on('modelsMerged', (data: InvokeAI.ModelsMergedResponse) => {
|
||||||
onModelsMerged(data);
|
// onModelsMerged(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => {
|
// socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => {
|
||||||
onModelChanged(data);
|
// onModelChanged(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => {
|
// socketio.on('modelChangeFailed', (data: InvokeAI.ModelChangeResponse) => {
|
||||||
onModelChangeFailed(data);
|
// onModelChangeFailed(data);
|
||||||
});
|
// });
|
||||||
|
|
||||||
socketio.on('tempFolderEmptied', () => {
|
// socketio.on('tempFolderEmptied', () => {
|
||||||
onTempFolderEmptied();
|
// onTempFolderEmptied();
|
||||||
});
|
// });
|
||||||
|
|
||||||
areListenersSet = true;
|
// areListenersSet = true;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* Handle redux actions caught by middleware.
|
// * Handle redux actions caught by middleware.
|
||||||
*/
|
// */
|
||||||
switch (action.type) {
|
// switch (action.type) {
|
||||||
case 'socketio/generateImage': {
|
// case 'socketio/generateImage': {
|
||||||
emitGenerateImage(action.payload);
|
// emitGenerateImage(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/runESRGAN': {
|
// case 'socketio/runESRGAN': {
|
||||||
emitRunESRGAN(action.payload);
|
// emitRunESRGAN(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/runFacetool': {
|
// case 'socketio/runFacetool': {
|
||||||
emitRunFacetool(action.payload);
|
// emitRunFacetool(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/deleteImage': {
|
// case 'socketio/deleteImage': {
|
||||||
emitDeleteImage(action.payload);
|
// emitDeleteImage(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/requestImages': {
|
// case 'socketio/requestImages': {
|
||||||
emitRequestImages(action.payload);
|
// emitRequestImages(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/requestNewImages': {
|
// case 'socketio/requestNewImages': {
|
||||||
emitRequestNewImages(action.payload);
|
// emitRequestNewImages(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/cancelProcessing': {
|
// case 'socketio/cancelProcessing': {
|
||||||
emitCancelProcessing();
|
// emitCancelProcessing();
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/requestSystemConfig': {
|
// case 'socketio/requestSystemConfig': {
|
||||||
emitRequestSystemConfig();
|
// emitRequestSystemConfig();
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/searchForModels': {
|
// case 'socketio/searchForModels': {
|
||||||
emitSearchForModels(action.payload);
|
// emitSearchForModels(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/addNewModel': {
|
// case 'socketio/addNewModel': {
|
||||||
emitAddNewModel(action.payload);
|
// emitAddNewModel(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/deleteModel': {
|
// case 'socketio/deleteModel': {
|
||||||
emitDeleteModel(action.payload);
|
// emitDeleteModel(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/convertToDiffusers': {
|
// case 'socketio/convertToDiffusers': {
|
||||||
emitConvertToDiffusers(action.payload);
|
// emitConvertToDiffusers(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/mergeDiffusersModels': {
|
// case 'socketio/mergeDiffusersModels': {
|
||||||
emitMergeDiffusersModels(action.payload);
|
// emitMergeDiffusersModels(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/requestModelChange': {
|
// case 'socketio/requestModelChange': {
|
||||||
emitRequestModelChange(action.payload);
|
// emitRequestModelChange(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/saveStagingAreaImageToGallery': {
|
// case 'socketio/saveStagingAreaImageToGallery': {
|
||||||
emitSaveStagingAreaImageToGallery(action.payload);
|
// emitSaveStagingAreaImageToGallery(action.payload);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
|
|
||||||
case 'socketio/requestEmptyTempFolder': {
|
// case 'socketio/requestEmptyTempFolder': {
|
||||||
emitRequestEmptyTempFolder();
|
// emitRequestEmptyTempFolder();
|
||||||
break;
|
// 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 modelsReducer from 'features/system/store/modelSlice';
|
||||||
import nodesReducer from 'features/nodes/store/nodesSlice';
|
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 { canvasDenylist } from 'features/canvas/store/canvasPersistDenylist';
|
||||||
import { galleryDenylist } from 'features/gallery/store/galleryPersistDenylist';
|
import { galleryDenylist } from 'features/gallery/store/galleryPersistDenylist';
|
||||||
import { generationDenylist } from 'features/parameters/store/generationPersistDenylist';
|
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 { postprocessingDenylist } from 'features/parameters/store/postprocessingPersistDenylist';
|
||||||
import { systemDenylist } from 'features/system/store/systemPersistDenylist';
|
import { systemDenylist } from 'features/system/store/systemPersistDenylist';
|
||||||
import { uiDenylist } from 'features/ui/store/uiPersistDenylist';
|
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.
|
* 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);
|
const persistedReducer = persistReducer(rootPersistConfig, rootReducer);
|
||||||
|
|
||||||
// TODO: rip the old middleware out when nodes is complete
|
// TODO: rip the old middleware out when nodes is complete
|
||||||
export function buildMiddleware() {
|
// export function buildMiddleware() {
|
||||||
if (import.meta.env.MODE === 'nodes' || import.meta.env.MODE === 'package') {
|
// if (import.meta.env.MODE === 'nodes' || import.meta.env.MODE === 'package') {
|
||||||
return socketMiddleware();
|
// return socketMiddleware();
|
||||||
} else {
|
// } else {
|
||||||
return socketioMiddleware();
|
// return socketioMiddleware();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: persistedReducer,
|
reducer: persistedReducer,
|
||||||
|
@ -111,24 +111,9 @@ export type FacetoolMetadata = CommonPostProcessedImageMetadata & {
|
|||||||
export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
|
export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
|
||||||
|
|
||||||
// Metadata includes the system config and image metadata.
|
// Metadata includes the system config and image metadata.
|
||||||
export type Metadata = SystemGenerationMetadata & {
|
// export type Metadata = SystemGenerationMetadata & {
|
||||||
image: GeneratedImageMetadata | PostProcessedImageMetadata;
|
// 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResultImage
|
* ResultImage
|
||||||
@ -141,40 +126,35 @@ export type Image = {
|
|||||||
metadata: ImageResponseMetadata;
|
metadata: ImageResponseMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
// GalleryImages is an array of Image.
|
|
||||||
export type GalleryImages = {
|
|
||||||
images: Array<_Image>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types related to the system status.
|
* Types related to the system status.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This represents the processing status of the backend.
|
// // This represents the processing status of the backend.
|
||||||
export type SystemStatus = {
|
// export type SystemStatus = {
|
||||||
isProcessing: boolean;
|
// isProcessing: boolean;
|
||||||
currentStep: number;
|
// currentStep: number;
|
||||||
totalSteps: number;
|
// totalSteps: number;
|
||||||
currentIteration: number;
|
// currentIteration: number;
|
||||||
totalIterations: number;
|
// totalIterations: number;
|
||||||
currentStatus: string;
|
// currentStatus: string;
|
||||||
currentStatusHasSteps: boolean;
|
// currentStatusHasSteps: boolean;
|
||||||
hasError: boolean;
|
// hasError: boolean;
|
||||||
};
|
// };
|
||||||
|
|
||||||
export type SystemGenerationMetadata = {
|
// export type SystemGenerationMetadata = {
|
||||||
model: string;
|
// model: string;
|
||||||
model_weights?: string;
|
// model_weights?: string;
|
||||||
model_id?: string;
|
// model_id?: string;
|
||||||
model_hash: string;
|
// model_hash: string;
|
||||||
app_id: string;
|
// app_id: string;
|
||||||
app_version: string;
|
// app_version: string;
|
||||||
};
|
// };
|
||||||
|
|
||||||
export type SystemConfig = SystemGenerationMetadata & {
|
// export type SystemConfig = SystemGenerationMetadata & {
|
||||||
model_list: ModelList;
|
// model_list: ModelList;
|
||||||
infill_methods: string[];
|
// infill_methods: string[];
|
||||||
};
|
// };
|
||||||
|
|
||||||
export type ModelStatus = 'active' | 'cached' | 'not loaded';
|
export type ModelStatus = 'active' | 'cached' | 'not loaded';
|
||||||
|
|
||||||
@ -286,9 +266,9 @@ export type FoundModelResponse = {
|
|||||||
found_models: FoundModel[];
|
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'> & {
|
export type ImageResultResponse = Omit<_Image, 'uuid'> & {
|
||||||
boundingBox?: IRect;
|
boundingBox?: IRect;
|
||||||
@ -310,27 +290,10 @@ export type ErrorResponse = {
|
|||||||
additionalData?: string;
|
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 = {
|
export type ImageUrlResponse = {
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// export type UploadImagePayload = {
|
|
||||||
// file: File;
|
|
||||||
// destination?: ImageUploadDestination;
|
|
||||||
// };
|
|
||||||
|
|
||||||
export type UploadOutpaintingMergeImagePayload = {
|
export type UploadOutpaintingMergeImagePayload = {
|
||||||
dataURL: string;
|
dataURL: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -233,7 +233,7 @@ const IAISlider = (props: IAIFullSliderProps) => {
|
|||||||
hidden={hideTooltip}
|
hidden={hideTooltip}
|
||||||
{...sliderTooltipProps}
|
{...sliderTooltipProps}
|
||||||
>
|
>
|
||||||
<SliderThumb {...sliderThumbProps} />
|
<SliderThumb {...sliderThumbProps} zIndex={0} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Slider>
|
</Slider>
|
||||||
|
|
||||||
|
@ -1,32 +1,11 @@
|
|||||||
import { Badge, Box, ButtonGroup, Flex } from '@chakra-ui/react';
|
import { Badge, Box, 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 { Image } from 'app/types/invokeai';
|
import { Image } from 'app/types/invokeai';
|
||||||
|
|
||||||
type ImageToImageOverlayProps = {
|
type ImageToImageOverlayProps = {
|
||||||
setIsLoaded: (isLoaded: boolean) => void;
|
|
||||||
image: Image;
|
image: Image;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ImageToImageOverlay = ({
|
const ImageToImageOverlay = ({ image }: ImageToImageOverlayProps) => {
|
||||||
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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -1,34 +1,13 @@
|
|||||||
import {
|
import { ButtonGroup, Flex, Spacer, Text } from '@chakra-ui/react';
|
||||||
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 IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaUndo, FaUpload } from 'react-icons/fa';
|
import { FaUndo, FaUpload } from 'react-icons/fa';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { RootState } from 'app/store/store';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
||||||
|
|
||||||
const ImageToImageSettingsHeader = () => {
|
const ImageToImageSettingsHeader = () => {
|
||||||
const isImageToImageEnabled = useAppSelector(
|
|
||||||
(state: RootState) => state.generation.isImageToImageEnabled
|
|
||||||
);
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
|
import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { addImage } from 'features/gallery/store/gallerySlice';
|
// import { addImage } from 'features/gallery/store/gallerySlice';
|
||||||
import {
|
import {
|
||||||
addToast,
|
addToast,
|
||||||
setCurrentStatus,
|
setCurrentStatus,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { isEqual } from 'lodash-es';
|
import { get, isEqual, isNumber, isString } from 'lodash-es';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
useToast,
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} 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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
@ -63,11 +63,11 @@ import {
|
|||||||
} from '../store/gallerySelectors';
|
} from '../store/gallerySelectors';
|
||||||
import DeleteImageModal from './DeleteImageModal';
|
import DeleteImageModal from './DeleteImageModal';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import useSetBothPrompts from 'features/parameters/hooks/usePrompt';
|
|
||||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
import { imageDeleted } from 'services/thunks/image';
|
import { imageDeleted } from 'services/thunks/image';
|
||||||
|
import { useParameters } from 'features/parameters/hooks/useParameters';
|
||||||
|
|
||||||
const currentImageButtonsSelector = createSelector(
|
const currentImageButtonsSelector = createSelector(
|
||||||
[
|
[
|
||||||
@ -112,6 +112,8 @@ const currentImageButtonsSelector = createSelector(
|
|||||||
isLightboxOpen,
|
isLightboxOpen,
|
||||||
shouldHidePreview,
|
shouldHidePreview,
|
||||||
image,
|
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 toast = useToast();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const setBothPrompts = useSetBothPrompts();
|
|
||||||
|
|
||||||
const handleClickUseAsInitialImage = useCallback(() => {
|
const { recallPrompt, recallSeed, sendToImageToImage } = useParameters();
|
||||||
if (!image) return;
|
|
||||||
if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
|
|
||||||
dispatch(initialImageSelected(image.name));
|
|
||||||
// dispatch(setInitialImage(currentImage));
|
|
||||||
|
|
||||||
// dispatch(setActiveTab('img2img'));
|
|
||||||
}, [dispatch, image, isLightboxOpen]);
|
|
||||||
|
|
||||||
const handleCopyImage = useCallback(async () => {
|
const handleCopyImage = useCallback(async () => {
|
||||||
if (!image?.url) {
|
if (!image?.url) {
|
||||||
@ -217,30 +211,6 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
});
|
});
|
||||||
}, [toast, shouldTransformUrls, getUrl, t, image]);
|
}, [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(() => {
|
const handlePreviewVisibility = useCallback(() => {
|
||||||
dispatch(setShouldHidePreview(!shouldHidePreview));
|
dispatch(setShouldHidePreview(!shouldHidePreview));
|
||||||
}, [dispatch, shouldHidePreview]);
|
}, [dispatch, shouldHidePreview]);
|
||||||
@ -259,7 +229,8 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
useHotkeys(
|
useHotkeys(
|
||||||
'a',
|
'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();
|
handleClickUseAllParameters();
|
||||||
toast({
|
toast({
|
||||||
title: t('toast.parametersSet'),
|
title: t('toast.parametersSet'),
|
||||||
@ -280,63 +251,23 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
[image]
|
[image]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickUseSeed = () => {
|
const handleUseSeed = useCallback(() => {
|
||||||
image?.metadata && dispatch(setSeed(image.metadata.sd_metadata.seed));
|
recallSeed(image?.metadata?.invokeai?.node?.seed);
|
||||||
};
|
}, [image, recallSeed]);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys('s', handleUseSeed, [image]);
|
||||||
'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]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickUsePrompt = useCallback(() => {
|
const handleUsePrompt = useCallback(() => {
|
||||||
if (image?.metadata?.sd_metadata?.prompt) {
|
recallPrompt(image?.metadata?.invokeai?.node?.prompt);
|
||||||
setBothPrompts(image?.metadata?.sd_metadata?.prompt);
|
}, [image, recallPrompt]);
|
||||||
}
|
|
||||||
}, [image?.metadata?.sd_metadata?.prompt, setBothPrompts]);
|
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys('p', handleUsePrompt, [image]);
|
||||||
'p',
|
|
||||||
() => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
if (image?.metadata?.sd_metadata?.prompt) {
|
sendToImageToImage(image);
|
||||||
handleClickUsePrompt();
|
}, [image, sendToImageToImage]);
|
||||||
toast({
|
|
||||||
title: t('toast.promptSet'),
|
useHotkeys('shift+i', handleSendToImageToImage, [image]);
|
||||||
status: 'success',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
title: t('toast.promptNotSet'),
|
|
||||||
description: t('toast.promptNotSetDesc'),
|
|
||||||
status: 'error',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[image]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickUpscale = useCallback(() => {
|
const handleClickUpscale = useCallback(() => {
|
||||||
// selectedImage && dispatch(runESRGAN(selectedImage));
|
// selectedImage && dispatch(runESRGAN(selectedImage));
|
||||||
@ -496,7 +427,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
>
|
>
|
||||||
<IAIButton
|
<IAIButton
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleClickUseAsInitialImage}
|
onClick={handleSendToImageToImage}
|
||||||
leftIcon={<FaShare />}
|
leftIcon={<FaShare />}
|
||||||
>
|
>
|
||||||
{t('parameters.sendToImg2Img')}
|
{t('parameters.sendToImg2Img')}
|
||||||
@ -570,16 +501,16 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
icon={<FaQuoteRight />}
|
icon={<FaQuoteRight />}
|
||||||
tooltip={`${t('parameters.usePrompt')} (P)`}
|
tooltip={`${t('parameters.usePrompt')} (P)`}
|
||||||
aria-label={`${t('parameters.usePrompt')} (P)`}
|
aria-label={`${t('parameters.usePrompt')} (P)`}
|
||||||
isDisabled={!image?.metadata?.sd_metadata?.prompt}
|
isDisabled={!image?.metadata?.invokeai?.node?.prompt}
|
||||||
onClick={handleClickUsePrompt}
|
onClick={handleUsePrompt}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
icon={<FaSeedling />}
|
icon={<FaSeedling />}
|
||||||
tooltip={`${t('parameters.useSeed')} (S)`}
|
tooltip={`${t('parameters.useSeed')} (S)`}
|
||||||
aria-label={`${t('parameters.useSeed')} (S)`}
|
aria-label={`${t('parameters.useSeed')} (S)`}
|
||||||
isDisabled={!image?.metadata?.sd_metadata?.seed}
|
isDisabled={!image?.metadata?.invokeai?.node?.seed}
|
||||||
onClick={handleClickUseSeed}
|
onClick={handleUseSeed}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
|
@ -17,31 +17,11 @@ export const imagesSelector = createSelector(
|
|||||||
[uiSelector, selectedImageSelector, systemSelector],
|
[uiSelector, selectedImageSelector, systemSelector],
|
||||||
(ui, selectedImage, system) => {
|
(ui, selectedImage, system) => {
|
||||||
const { shouldShowImageDetails, shouldHidePreview } = ui;
|
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 {
|
return {
|
||||||
shouldShowImageDetails,
|
shouldShowImageDetails,
|
||||||
shouldHidePreview,
|
shouldHidePreview,
|
||||||
imageToDisplay,
|
image: selectedImage,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -52,7 +32,7 @@ export const imagesSelector = createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const CurrentImagePreview = () => {
|
const CurrentImagePreview = () => {
|
||||||
const { shouldShowImageDetails, imageToDisplay, shouldHidePreview } =
|
const { shouldShowImageDetails, image, shouldHidePreview } =
|
||||||
useAppSelector(imagesSelector);
|
useAppSelector(imagesSelector);
|
||||||
const { getUrl } = useGetUrl();
|
const { getUrl } = useGetUrl();
|
||||||
|
|
||||||
@ -66,41 +46,23 @@ const CurrentImagePreview = () => {
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{imageToDisplay && (
|
{image && (
|
||||||
<Image
|
<Image
|
||||||
src={
|
src={shouldHidePreview ? undefined : getUrl(image.url)}
|
||||||
shouldHidePreview
|
width={image.metadata.width}
|
||||||
? undefined
|
height={image.metadata.height}
|
||||||
: imageToDisplay.isProgressImage
|
fallback={shouldHidePreview ? <CurrentImageHidden /> : undefined}
|
||||||
? imageToDisplay.url
|
|
||||||
: getUrl(imageToDisplay.url)
|
|
||||||
}
|
|
||||||
width={imageToDisplay.width}
|
|
||||||
height={imageToDisplay.height}
|
|
||||||
fallback={
|
|
||||||
shouldHidePreview ? (
|
|
||||||
<CurrentImageHidden />
|
|
||||||
) : !imageToDisplay.isProgressImage ? (
|
|
||||||
<CurrentImageFallback />
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
sx={{
|
sx={{
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
maxHeight: '100%',
|
maxHeight: '100%',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
imageRendering: imageToDisplay.isProgressImage
|
|
||||||
? 'pixelated'
|
|
||||||
: 'initial',
|
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!shouldShowImageDetails && <NextPrevImageButtons />}
|
{shouldShowImageDetails && image && 'metadata' in image && (
|
||||||
{shouldShowImageDetails &&
|
|
||||||
imageToDisplay &&
|
|
||||||
'metadata' in imageToDisplay.image && (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -111,9 +73,10 @@ const CurrentImagePreview = () => {
|
|||||||
overflow: 'scroll',
|
overflow: 'scroll',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ImageMetadataViewer image={imageToDisplay.image} />
|
<ImageMetadataViewer image={image} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
{!shouldShowImageDetails && <NextPrevImageButtons />}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -5,65 +5,38 @@ import {
|
|||||||
Image,
|
Image,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
MenuList,
|
MenuList,
|
||||||
Text,
|
Skeleton,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
useTheme,
|
useTheme,
|
||||||
useToast,
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
imageSelected,
|
import { DragEvent, memo, useCallback, useState } from 'react';
|
||||||
setCurrentImage,
|
import { FaCheck, FaExpand, FaImage, FaShare, FaTrash } from 'react-icons/fa';
|
||||||
} 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 DeleteImageModal from './DeleteImageModal';
|
import DeleteImageModal from './DeleteImageModal';
|
||||||
import { ContextMenu } from 'chakra-ui-contextmenu';
|
import { ContextMenu } from 'chakra-ui-contextmenu';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import {
|
import { resizeAndScaleCanvas } from 'features/canvas/store/canvasSlice';
|
||||||
resizeAndScaleCanvas,
|
|
||||||
setInitialCanvasImage,
|
|
||||||
} from 'features/canvas/store/canvasSlice';
|
|
||||||
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
||||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
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 IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
import { ExternalLinkIcon } from '@chakra-ui/icons';
|
||||||
import { BiZoomIn } from 'react-icons/bi';
|
|
||||||
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
|
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
|
||||||
import { imageDeleted } from 'services/thunks/image';
|
import { imageDeleted } from 'services/thunks/image';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { configSelector } from 'features/system/store/configSelectors';
|
|
||||||
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
|
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||||
|
import { useParameters } from 'features/parameters/hooks/useParameters';
|
||||||
|
|
||||||
export const selector = createSelector(
|
export const selector = createSelector(
|
||||||
[
|
[gallerySelector, systemSelector, lightboxSelector, activeTabNameSelector],
|
||||||
gallerySelector,
|
(gallery, system, lightbox, activeTabName) => {
|
||||||
systemSelector,
|
|
||||||
configSelector,
|
|
||||||
lightboxSelector,
|
|
||||||
activeTabNameSelector,
|
|
||||||
],
|
|
||||||
(gallery, system, config, lightbox, activeTabName) => {
|
|
||||||
const {
|
const {
|
||||||
galleryImageObjectFit,
|
galleryImageObjectFit,
|
||||||
galleryImageMinimumWidth,
|
galleryImageMinimumWidth,
|
||||||
@ -71,7 +44,6 @@ export const selector = createSelector(
|
|||||||
} = gallery;
|
} = gallery;
|
||||||
|
|
||||||
const { isLightboxOpen } = lightbox;
|
const { isLightboxOpen } = lightbox;
|
||||||
const { disabledFeatures } = config;
|
|
||||||
const { isConnected, isProcessing, shouldConfirmOnDelete } = system;
|
const { isConnected, isProcessing, shouldConfirmOnDelete } = system;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -82,7 +54,6 @@ export const selector = createSelector(
|
|||||||
shouldUseSingleGalleryColumn,
|
shouldUseSingleGalleryColumn,
|
||||||
activeTabName,
|
activeTabName,
|
||||||
isLightboxOpen,
|
isLightboxOpen,
|
||||||
disabledFeatures,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -113,14 +84,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
galleryImageMinimumWidth,
|
galleryImageMinimumWidth,
|
||||||
canDeleteImage,
|
canDeleteImage,
|
||||||
shouldUseSingleGalleryColumn,
|
shouldUseSingleGalleryColumn,
|
||||||
disabledFeatures,
|
|
||||||
shouldConfirmOnDelete,
|
shouldConfirmOnDelete,
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isDeleteDialogOpen,
|
isOpen: isDeleteDialogOpen,
|
||||||
onOpen: onDeleteDialogOpen,
|
onOpen: onDeleteDialogOpen,
|
||||||
onClose: onDeleteDialogClose,
|
onClose: onDeleteDialogClose,
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
const { image, isSelected } = props;
|
const { image, isSelected } = props;
|
||||||
const { url, thumbnail, name, metadata } = image;
|
const { url, thumbnail, name, metadata } = image;
|
||||||
const { getUrl } = useGetUrl();
|
const { getUrl } = useGetUrl();
|
||||||
@ -130,53 +102,62 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const { direction } = useTheme();
|
const { direction } = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const setBothPrompts = useSetBothPrompts();
|
const { isFeatureEnabled: isLightboxEnabled } = useFeatureStatus('lightbox');
|
||||||
|
const { recallSeed, recallPrompt, sendToImageToImage, recallInitialImage } =
|
||||||
|
useParameters();
|
||||||
|
|
||||||
const handleMouseOver = () => setIsHovered(true);
|
const handleMouseOver = () => setIsHovered(true);
|
||||||
|
|
||||||
const handleMouseOut = () => setIsHovered(false);
|
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) {
|
if (shouldConfirmOnDelete) {
|
||||||
onDeleteDialogOpen();
|
onDeleteDialogOpen();
|
||||||
} else {
|
} else {
|
||||||
handleDelete();
|
handleDelete();
|
||||||
}
|
}
|
||||||
};
|
}, [handleDelete, onDeleteDialogOpen, shouldConfirmOnDelete]);
|
||||||
|
|
||||||
const handleDelete = () => {
|
const handleSelectImage = useCallback(() => {
|
||||||
if (canDeleteImage && image) {
|
dispatch(imageSelected(image));
|
||||||
dispatch(imageDeleted({ imageType: image.type, imageName: image.name }));
|
}, [image, dispatch]);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUsePrompt = () => {
|
const handleDragStart = useCallback(
|
||||||
if (typeof image.metadata?.invokeai?.node?.prompt === 'string') {
|
(e: DragEvent<HTMLDivElement>) => {
|
||||||
setBothPrompts(image.metadata?.invokeai?.node?.prompt);
|
e.dataTransfer.setData('invokeai/imageName', image.name);
|
||||||
}
|
e.dataTransfer.setData('invokeai/imageType', image.type);
|
||||||
toast({
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
title: t('toast.promptSet'),
|
},
|
||||||
status: 'success',
|
[image]
|
||||||
duration: 2500,
|
);
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUseSeed = () => {
|
// Recall parameters handlers
|
||||||
typeof image.metadata.invokeai?.node?.seed === 'number' &&
|
const handleRecallPrompt = useCallback(() => {
|
||||||
dispatch(setSeed(image.metadata.invokeai?.node?.seed));
|
recallPrompt(image.metadata?.invokeai?.node?.prompt);
|
||||||
toast({
|
}, [image, recallPrompt]);
|
||||||
title: t('toast.seedSet'),
|
|
||||||
status: 'success',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSendToImageToImage = () => {
|
const handleRecallSeed = useCallback(() => {
|
||||||
dispatch(initialImageSelected(image.name));
|
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 = () => {
|
const handleSendToCanvas = () => {
|
||||||
// dispatch(setInitialCanvasImage(image));
|
// 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 = () => {
|
const handleLightBox = () => {
|
||||||
// dispatch(setCurrentImage(image));
|
// dispatch(setCurrentImage(image));
|
||||||
// dispatch(setIsLightboxOpen(true));
|
// dispatch(setIsLightboxOpen(true));
|
||||||
@ -254,21 +200,21 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
<ContextMenu<HTMLDivElement>
|
<ContextMenu<HTMLDivElement>
|
||||||
menuProps={{ size: 'sm', isLazy: true }}
|
menuProps={{ size: 'sm', isLazy: true }}
|
||||||
renderMenu={() => (
|
renderMenu={() => (
|
||||||
<MenuList>
|
<MenuList sx={{ visibility: 'visible !important' }}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<ExternalLinkIcon />}
|
icon={<ExternalLinkIcon />}
|
||||||
onClickCapture={handleOpenInNewTab}
|
onClickCapture={handleOpenInNewTab}
|
||||||
>
|
>
|
||||||
{t('common.openInNewTab')}
|
{t('common.openInNewTab')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{!disabledFeatures.includes('lightbox') && (
|
{isLightboxEnabled && (
|
||||||
<MenuItem icon={<FaExpand />} onClickCapture={handleLightBox}>
|
<MenuItem icon={<FaExpand />} onClickCapture={handleLightBox}>
|
||||||
{t('parameters.openInViewer')}
|
{t('parameters.openInViewer')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<IoArrowUndoCircleOutline />}
|
icon={<IoArrowUndoCircleOutline />}
|
||||||
onClickCapture={handleUsePrompt}
|
onClickCapture={handleRecallPrompt}
|
||||||
isDisabled={image?.metadata?.invokeai?.node?.prompt === undefined}
|
isDisabled={image?.metadata?.invokeai?.node?.prompt === undefined}
|
||||||
>
|
>
|
||||||
{t('parameters.usePrompt')}
|
{t('parameters.usePrompt')}
|
||||||
@ -276,14 +222,14 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<IoArrowUndoCircleOutline />}
|
icon={<IoArrowUndoCircleOutline />}
|
||||||
onClickCapture={handleUseSeed}
|
onClickCapture={handleRecallSeed}
|
||||||
isDisabled={image?.metadata?.invokeai?.node?.seed === undefined}
|
isDisabled={image?.metadata?.invokeai?.node?.seed === undefined}
|
||||||
>
|
>
|
||||||
{t('parameters.useSeed')}
|
{t('parameters.useSeed')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<IoArrowUndoCircleOutline />}
|
icon={<IoArrowUndoCircleOutline />}
|
||||||
onClickCapture={handleUseInitialImage}
|
onClickCapture={handleRecallInitialImage}
|
||||||
isDisabled={image?.metadata?.invokeai?.node?.type !== 'img2img'}
|
isDisabled={image?.metadata?.invokeai?.node?.type !== 'img2img'}
|
||||||
>
|
>
|
||||||
{t('parameters.useInitImg')}
|
{t('parameters.useInitImg')}
|
||||||
@ -323,46 +269,35 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
userSelect="none"
|
userSelect="none"
|
||||||
draggable={true}
|
draggable={true}
|
||||||
onDragStart={handleDragStart}
|
onDragStart={handleDragStart}
|
||||||
|
onClick={handleSelectImage}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
sx={{
|
sx={{
|
||||||
padding: 2,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
w: 'full',
|
||||||
|
h: 'full',
|
||||||
transition: 'transform 0.2s ease-out',
|
transition: 'transform 0.2s ease-out',
|
||||||
_hover: {
|
aspectRatio: '1/1',
|
||||||
cursor: 'pointer',
|
|
||||||
|
|
||||||
zIndex: 2,
|
|
||||||
},
|
|
||||||
_before: {
|
|
||||||
content: '""',
|
|
||||||
display: 'block',
|
|
||||||
paddingBottom: '100%',
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
|
loading="lazy"
|
||||||
objectFit={
|
objectFit={
|
||||||
shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit
|
shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit
|
||||||
}
|
}
|
||||||
rounded="md"
|
rounded="md"
|
||||||
src={getUrl(thumbnail || url)}
|
src={getUrl(thumbnail || url)}
|
||||||
loading="lazy"
|
fallback={<FaImage />}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
maxHeight: '100%',
|
maxHeight: '100%',
|
||||||
top: '50%',
|
|
||||||
transform: 'translate(-50%,-50%)',
|
|
||||||
...(direction === 'rtl'
|
|
||||||
? { insetInlineEnd: '50%' }
|
|
||||||
: { insetInlineStart: '50%' }),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{isSelected && (
|
||||||
<Flex
|
<Flex
|
||||||
onClick={handleSelectImage}
|
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '0',
|
top: '0',
|
||||||
@ -371,10 +306,11 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSelected && (
|
|
||||||
<Icon
|
<Icon
|
||||||
|
filter={'drop-shadow(0px 0px 1rem black)'}
|
||||||
as={FaCheck}
|
as={FaCheck}
|
||||||
sx={{
|
sx={{
|
||||||
width: '50%',
|
width: '50%',
|
||||||
@ -382,9 +318,9 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
fill: 'ok.500',
|
fill: 'ok.500',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
{isHovered && galleryImageMinimumWidth >= 64 && (
|
)}
|
||||||
|
{isHovered && galleryImageMinimumWidth >= 100 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
import { ButtonGroup, Flex, Grid, Icon, Image, Text } from '@chakra-ui/react';
|
import {
|
||||||
import { requestImages } from 'app/socketio/actions';
|
Box,
|
||||||
|
ButtonGroup,
|
||||||
|
Flex,
|
||||||
|
FlexProps,
|
||||||
|
Grid,
|
||||||
|
Icon,
|
||||||
|
Text,
|
||||||
|
forwardRef,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAICheckbox from 'common/components/IAICheckbox';
|
||||||
@ -15,28 +23,33 @@ import {
|
|||||||
setShouldUseSingleGalleryColumn,
|
setShouldUseSingleGalleryColumn,
|
||||||
} from 'features/gallery/store/gallerySlice';
|
} from 'features/gallery/store/gallerySlice';
|
||||||
import { togglePinGalleryPanel } from 'features/ui/store/uiSlice';
|
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 { useTranslation } from 'react-i18next';
|
||||||
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
import { BsPinAngle, BsPinAngleFill } from 'react-icons/bs';
|
||||||
import { FaImage, FaUser, FaWrench } from 'react-icons/fa';
|
import { FaImage, FaUser, FaWrench } from 'react-icons/fa';
|
||||||
import { MdPhotoLibrary } from 'react-icons/md';
|
import { MdPhotoLibrary } from 'react-icons/md';
|
||||||
import HoverableImage from './HoverableImage';
|
import HoverableImage from './HoverableImage';
|
||||||
|
|
||||||
import Scrollable from 'features/ui/components/common/Scrollable';
|
|
||||||
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
|
||||||
import {
|
import { resultsAdapter } from '../store/resultsSlice';
|
||||||
resultsAdapter,
|
|
||||||
selectResultsAll,
|
|
||||||
selectResultsTotal,
|
|
||||||
} from '../store/resultsSlice';
|
|
||||||
import {
|
import {
|
||||||
receivedResultImagesPage,
|
receivedResultImagesPage,
|
||||||
receivedUploadImagesPage,
|
receivedUploadImagesPage,
|
||||||
} from 'services/thunks/gallery';
|
} from 'services/thunks/gallery';
|
||||||
import { selectUploadsAll, uploadsAdapter } from '../store/uploadsSlice';
|
import { uploadsAdapter } from '../store/uploadsSlice';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
|
import { Virtuoso, VirtuosoGrid } from 'react-virtuoso';
|
||||||
|
|
||||||
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 290;
|
const GALLERY_SHOW_BUTTONS_MIN_WIDTH = 290;
|
||||||
|
|
||||||
@ -49,7 +62,7 @@ const gallerySelector = createSelector(
|
|||||||
(uploads, results, gallery) => {
|
(uploads, results, gallery) => {
|
||||||
const { currentCategory } = gallery;
|
const { currentCategory } = gallery;
|
||||||
|
|
||||||
return currentCategory === 'result'
|
return currentCategory === 'results'
|
||||||
? {
|
? {
|
||||||
images: resultsAdapter.getSelectors().selectAll(results),
|
images: resultsAdapter.getSelectors().selectAll(results),
|
||||||
isLoading: results.isLoading,
|
isLoading: results.isLoading,
|
||||||
@ -68,32 +81,41 @@ const ImageGalleryContent = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const resizeObserverRef = useRef<HTMLDivElement>(null);
|
const resizeObserverRef = useRef<HTMLDivElement>(null);
|
||||||
const [shouldShouldIconButtons, setShouldShouldIconButtons] = useState(true);
|
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 {
|
const {
|
||||||
// images,
|
// images,
|
||||||
currentCategory,
|
currentCategory,
|
||||||
currentImageUuid,
|
|
||||||
shouldPinGallery,
|
shouldPinGallery,
|
||||||
galleryImageMinimumWidth,
|
galleryImageMinimumWidth,
|
||||||
galleryGridTemplateColumns,
|
|
||||||
galleryImageObjectFit,
|
galleryImageObjectFit,
|
||||||
shouldAutoSwitchToNewImages,
|
shouldAutoSwitchToNewImages,
|
||||||
// areMoreImagesAvailable,
|
|
||||||
shouldUseSingleGalleryColumn,
|
shouldUseSingleGalleryColumn,
|
||||||
|
selectedImage,
|
||||||
} = useAppSelector(imageGallerySelector);
|
} = useAppSelector(imageGallerySelector);
|
||||||
|
|
||||||
const { images, areMoreImagesAvailable, isLoading } =
|
const { images, areMoreImagesAvailable, isLoading } =
|
||||||
useAppSelector(gallerySelector);
|
useAppSelector(gallerySelector);
|
||||||
|
|
||||||
// const handleClickLoadMore = () => {
|
|
||||||
// dispatch(requestImages(currentCategory));
|
|
||||||
// };
|
|
||||||
const handleClickLoadMore = () => {
|
const handleClickLoadMore = () => {
|
||||||
if (currentCategory === 'result') {
|
if (currentCategory === 'results') {
|
||||||
dispatch(receivedResultImagesPage());
|
dispatch(receivedResultImagesPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentCategory === 'user') {
|
if (currentCategory === 'uploads') {
|
||||||
dispatch(receivedUploadImagesPage());
|
dispatch(receivedUploadImagesPage());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -129,6 +151,25 @@ const ImageGalleryContent = () => {
|
|||||||
return () => resizeObserver.disconnect(); // clean up
|
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 (
|
return (
|
||||||
<Flex flexDirection="column" w="full" h="full" gap={4}>
|
<Flex flexDirection="column" w="full" h="full" gap={4}>
|
||||||
<Flex
|
<Flex
|
||||||
@ -147,34 +188,34 @@ const ImageGalleryContent = () => {
|
|||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
aria-label={t('gallery.showGenerations')}
|
aria-label={t('gallery.showGenerations')}
|
||||||
tooltip={t('gallery.showGenerations')}
|
tooltip={t('gallery.showGenerations')}
|
||||||
isChecked={currentCategory === 'result'}
|
isChecked={currentCategory === 'results'}
|
||||||
role="radio"
|
role="radio"
|
||||||
icon={<FaImage />}
|
icon={<FaImage />}
|
||||||
onClick={() => dispatch(setCurrentCategory('result'))}
|
onClick={() => dispatch(setCurrentCategory('results'))}
|
||||||
/>
|
/>
|
||||||
<IAIIconButton
|
<IAIIconButton
|
||||||
aria-label={t('gallery.showUploads')}
|
aria-label={t('gallery.showUploads')}
|
||||||
tooltip={t('gallery.showUploads')}
|
tooltip={t('gallery.showUploads')}
|
||||||
role="radio"
|
role="radio"
|
||||||
isChecked={currentCategory === 'user'}
|
isChecked={currentCategory === 'uploads'}
|
||||||
icon={<FaUser />}
|
icon={<FaUser />}
|
||||||
onClick={() => dispatch(setCurrentCategory('user'))}
|
onClick={() => dispatch(setCurrentCategory('uploads'))}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<IAIButton
|
<IAIButton
|
||||||
size="sm"
|
size="sm"
|
||||||
isChecked={currentCategory === 'result'}
|
isChecked={currentCategory === 'results'}
|
||||||
onClick={() => dispatch(setCurrentCategory('result'))}
|
onClick={() => dispatch(setCurrentCategory('results'))}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
>
|
>
|
||||||
{t('gallery.generations')}
|
{t('gallery.generations')}
|
||||||
</IAIButton>
|
</IAIButton>
|
||||||
<IAIButton
|
<IAIButton
|
||||||
size="sm"
|
size="sm"
|
||||||
isChecked={currentCategory === 'user'}
|
isChecked={currentCategory === 'uploads'}
|
||||||
onClick={() => dispatch(setCurrentCategory('user'))}
|
onClick={() => dispatch(setCurrentCategory('uploads'))}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
>
|
>
|
||||||
{t('gallery.uploads')}
|
{t('gallery.uploads')}
|
||||||
@ -241,17 +282,43 @@ const ImageGalleryContent = () => {
|
|||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Scrollable>
|
|
||||||
<Flex direction="column" gap={2} h="full">
|
<Flex direction="column" gap={2} h="full">
|
||||||
{images.length || areMoreImagesAvailable ? (
|
{images.length || areMoreImagesAvailable ? (
|
||||||
<>
|
<>
|
||||||
<Grid
|
<Box ref={rootRef} data-overlayscrollbars="" h="100%">
|
||||||
gap={2}
|
{shouldUseSingleGalleryColumn ? (
|
||||||
style={{ gridTemplateColumns: galleryGridTemplateColumns }}
|
<Virtuoso
|
||||||
>
|
style={{ height: '100%' }}
|
||||||
{images.map((image) => {
|
data={images}
|
||||||
|
scrollerRef={(ref) => setScrollerRef(ref)}
|
||||||
|
itemContent={(index, image) => {
|
||||||
const { name } = image;
|
const { name } = image;
|
||||||
const isSelected = currentImageUuid === name;
|
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 (
|
return (
|
||||||
<HoverableImage
|
<HoverableImage
|
||||||
key={`${name}-${image.thumbnail}`}
|
key={`${name}-${image.thumbnail}`}
|
||||||
@ -259,8 +326,10 @@ const ImageGalleryContent = () => {
|
|||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
}}
|
||||||
</Grid>
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
<IAIButton
|
<IAIButton
|
||||||
onClick={handleClickLoadMore}
|
onClick={handleClickLoadMore}
|
||||||
isDisabled={!areMoreImagesAvailable}
|
isDisabled={!areMoreImagesAvailable}
|
||||||
@ -296,10 +365,36 @@ const ImageGalleryContent = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Scrollable>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageGalleryContent.displayName = 'ImageGalleryContent';
|
type ItemContainerProps = PropsWithChildren & FlexProps;
|
||||||
export default ImageGalleryContent;
|
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
||||||
import {
|
import {
|
||||||
selectNextImage,
|
// selectNextImage,
|
||||||
selectPrevImage,
|
// selectPrevImage,
|
||||||
setGalleryImageMinimumWidth,
|
setGalleryImageMinimumWidth,
|
||||||
} from 'features/gallery/store/gallerySlice';
|
} from 'features/gallery/store/gallerySlice';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
@ -110,28 +110,6 @@ export const ImageGalleryPanel = () => {
|
|||||||
[shouldPinGallery]
|
[shouldPinGallery]
|
||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
'left',
|
|
||||||
() => {
|
|
||||||
dispatch(selectPrevImage());
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: !isStaging || activeTabName !== 'unifiedCanvas',
|
|
||||||
},
|
|
||||||
[isStaging, activeTabName]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
'right',
|
|
||||||
() => {
|
|
||||||
dispatch(selectNextImage());
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: !isStaging || activeTabName !== 'unifiedCanvas',
|
|
||||||
},
|
|
||||||
[isStaging, activeTabName]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'shift+g',
|
'shift+g',
|
||||||
() => {
|
() => {
|
||||||
|
@ -159,6 +159,7 @@ const ImageMetadataViewer = memo(({ image }: ImageMetadataViewerProps) => {
|
|||||||
_dark: {
|
_dark: {
|
||||||
bg: 'blackAlpha.600',
|
bg: 'blackAlpha.600',
|
||||||
},
|
},
|
||||||
|
overflow: 'scroll',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex gap={2}>
|
<Flex gap={2}>
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import { ChakraProps, Flex, Grid, IconButton } from '@chakra-ui/react';
|
import { ChakraProps, Flex, Grid, IconButton } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { isEqual } from 'lodash-es';
|
import { clamp, isEqual } from 'lodash-es';
|
||||||
import { useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
|
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
|
||||||
import { gallerySelector } from '../store/gallerySelectors';
|
import { gallerySelector } from '../store/gallerySelectors';
|
||||||
import {
|
import { RootState } from 'app/store/store';
|
||||||
GalleryCategory,
|
import { imageSelected } from '../store/gallerySlice';
|
||||||
selectNextImage,
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
selectPrevImage,
|
|
||||||
} from '../store/gallerySlice';
|
|
||||||
|
|
||||||
const nextPrevButtonTriggerAreaStyles: ChakraProps['sx'] = {
|
const nextPrevButtonTriggerAreaStyles: ChakraProps['sx'] = {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@ -23,24 +21,47 @@ const nextPrevButtonStyles: ChakraProps['sx'] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const nextPrevImageButtonsSelector = createSelector(
|
export const nextPrevImageButtonsSelector = createSelector(
|
||||||
gallerySelector,
|
[(state: RootState) => state, gallerySelector],
|
||||||
(gallery) => {
|
(state, gallery) => {
|
||||||
const { currentImage } = gallery;
|
const { selectedImage, currentCategory } = gallery;
|
||||||
|
|
||||||
const tempImages =
|
if (!selectedImage) {
|
||||||
gallery.categories[
|
return {
|
||||||
currentImage ? (currentImage.category as GalleryCategory) : 'result'
|
isOnFirstImage: true,
|
||||||
].images;
|
isOnLastImage: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const currentImageIndex = tempImages.findIndex(
|
const currentImageIndex = state[currentCategory].ids.findIndex(
|
||||||
(i) => i.uuid === gallery?.currentImage?.uuid
|
(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 {
|
return {
|
||||||
isOnFirstImage: currentImageIndex === 0,
|
isOnFirstImage: currentImageIndex === 0,
|
||||||
isOnLastImage:
|
isOnLastImage:
|
||||||
!isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1,
|
!isNaN(currentImageIndex) && currentImageIndex === imagesLength - 1,
|
||||||
|
nextImage,
|
||||||
|
prevImage,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -54,34 +75,48 @@ const NextPrevImageButtons = () => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { isOnFirstImage, isOnLastImage } = useAppSelector(
|
const { isOnFirstImage, isOnLastImage, nextImage, prevImage } =
|
||||||
nextPrevImageButtonsSelector
|
useAppSelector(nextPrevImageButtonsSelector);
|
||||||
);
|
|
||||||
|
|
||||||
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
|
const [shouldShowNextPrevButtons, setShouldShowNextPrevButtons] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
|
|
||||||
const handleCurrentImagePreviewMouseOver = () => {
|
const handleCurrentImagePreviewMouseOver = useCallback(() => {
|
||||||
setShouldShowNextPrevButtons(true);
|
setShouldShowNextPrevButtons(true);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleCurrentImagePreviewMouseOut = () => {
|
const handleCurrentImagePreviewMouseOut = useCallback(() => {
|
||||||
setShouldShowNextPrevButtons(false);
|
setShouldShowNextPrevButtons(false);
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
const handleClickPrevButton = () => {
|
const handlePrevImage = useCallback(() => {
|
||||||
dispatch(selectPrevImage());
|
dispatch(imageSelected(prevImage));
|
||||||
};
|
}, [dispatch, prevImage]);
|
||||||
|
|
||||||
const handleClickNextButton = () => {
|
const handleNextImage = useCallback(() => {
|
||||||
dispatch(selectNextImage());
|
dispatch(imageSelected(nextImage));
|
||||||
};
|
}, [dispatch, nextImage]);
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
'left',
|
||||||
|
() => {
|
||||||
|
handlePrevImage();
|
||||||
|
},
|
||||||
|
[prevImage]
|
||||||
|
);
|
||||||
|
|
||||||
|
useHotkeys(
|
||||||
|
'right',
|
||||||
|
() => {
|
||||||
|
handleNextImage();
|
||||||
|
},
|
||||||
|
[nextImage]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
sx={{
|
sx={{
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
zIndex: 1,
|
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
@ -100,7 +135,7 @@ const NextPrevImageButtons = () => {
|
|||||||
aria-label={t('accessibility.previousImage')}
|
aria-label={t('accessibility.previousImage')}
|
||||||
icon={<FaAngleLeft size={64} />}
|
icon={<FaAngleLeft size={64} />}
|
||||||
variant="unstyled"
|
variant="unstyled"
|
||||||
onClick={handleClickPrevButton}
|
onClick={handlePrevImage}
|
||||||
boxSize={16}
|
boxSize={16}
|
||||||
sx={nextPrevButtonStyles}
|
sx={nextPrevButtonStyles}
|
||||||
/>
|
/>
|
||||||
@ -119,7 +154,7 @@ const NextPrevImageButtons = () => {
|
|||||||
aria-label={t('accessibility.nextImage')}
|
aria-label={t('accessibility.nextImage')}
|
||||||
icon={<FaAngleRight size={64} />}
|
icon={<FaAngleRight size={64} />}
|
||||||
variant="unstyled"
|
variant="unstyled"
|
||||||
onClick={handleClickNextButton}
|
onClick={handleNextImage}
|
||||||
boxSize={16}
|
boxSize={16}
|
||||||
sx={nextPrevButtonStyles}
|
sx={nextPrevButtonStyles}
|
||||||
/>
|
/>
|
||||||
|
@ -22,17 +22,22 @@ import {
|
|||||||
export const gallerySelector = (state: RootState) => state.gallery;
|
export const gallerySelector = (state: RootState) => state.gallery;
|
||||||
|
|
||||||
export const imageGallerySelector = createSelector(
|
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 {
|
const {
|
||||||
categories,
|
|
||||||
currentCategory,
|
currentCategory,
|
||||||
currentImageUuid,
|
|
||||||
galleryImageMinimumWidth,
|
galleryImageMinimumWidth,
|
||||||
galleryImageObjectFit,
|
galleryImageObjectFit,
|
||||||
shouldAutoSwitchToNewImages,
|
shouldAutoSwitchToNewImages,
|
||||||
galleryWidth,
|
galleryWidth,
|
||||||
shouldUseSingleGalleryColumn,
|
shouldUseSingleGalleryColumn,
|
||||||
|
selectedImage,
|
||||||
} = gallery;
|
} = gallery;
|
||||||
|
|
||||||
const { shouldPinGallery } = ui;
|
const { shouldPinGallery } = ui;
|
||||||
@ -40,7 +45,6 @@ export const imageGallerySelector = createSelector(
|
|||||||
const { isLightboxOpen } = lightbox;
|
const { isLightboxOpen } = lightbox;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
currentImageUuid,
|
|
||||||
shouldPinGallery,
|
shouldPinGallery,
|
||||||
galleryImageMinimumWidth,
|
galleryImageMinimumWidth,
|
||||||
galleryImageObjectFit,
|
galleryImageObjectFit,
|
||||||
@ -49,9 +53,7 @@ export const imageGallerySelector = createSelector(
|
|||||||
: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`,
|
: `repeat(auto-fill, minmax(${galleryImageMinimumWidth}px, auto))`,
|
||||||
shouldAutoSwitchToNewImages,
|
shouldAutoSwitchToNewImages,
|
||||||
currentCategory,
|
currentCategory,
|
||||||
images: categories[currentCategory].images,
|
images: state[currentCategory].entities,
|
||||||
areMoreImagesAvailable:
|
|
||||||
categories[currentCategory].areMoreImagesAvailable,
|
|
||||||
galleryWidth,
|
galleryWidth,
|
||||||
shouldEnableResize:
|
shouldEnableResize:
|
||||||
isLightboxOpen ||
|
isLightboxOpen ||
|
||||||
@ -59,6 +61,7 @@ export const imageGallerySelector = createSelector(
|
|||||||
? false
|
? false
|
||||||
: true,
|
: true,
|
||||||
shouldUseSingleGalleryColumn,
|
shouldUseSingleGalleryColumn,
|
||||||
|
selectedImage,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -69,16 +72,16 @@ export const imageGallerySelector = createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
export const selectedImageSelector = createSelector(
|
export const selectedImageSelector = createSelector(
|
||||||
[gallerySelector, selectResultsEntities, selectUploadsEntities],
|
[(state: RootState) => state, gallerySelector],
|
||||||
(gallery, allResults, allUploads) => {
|
(state, gallery) => {
|
||||||
const selectedImageName = gallery.selectedImageName;
|
const selectedImage = gallery.selectedImage;
|
||||||
|
|
||||||
if (selectedImageName in allResults) {
|
if (selectedImage?.type === 'results') {
|
||||||
return allResults[selectedImageName];
|
return selectResultsById(state, selectedImage.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedImageName in allUploads) {
|
if (selectedImage?.type === 'uploads') {
|
||||||
return allUploads[selectedImageName];
|
return selectUploadsById(state, selectedImage.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,259 +1,47 @@
|
|||||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
|
||||||
import { invocationComplete } from 'services/events/actions';
|
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 { isImageOutput } from 'services/types/guards';
|
||||||
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
import { deserializeImageResponse } from 'services/util/deserializeImageResponse';
|
||||||
import { imageUploaded } from 'services/thunks/image';
|
import { imageUploaded } from 'services/thunks/image';
|
||||||
|
import { SelectedImage } from 'features/parameters/store/generationSlice';
|
||||||
export type GalleryCategory = 'user' | 'result';
|
|
||||||
|
|
||||||
export type AddImagesPayload = {
|
|
||||||
images: Array<InvokeAI._Image>;
|
|
||||||
areMoreImagesAvailable: boolean;
|
|
||||||
category: GalleryCategory;
|
|
||||||
};
|
|
||||||
|
|
||||||
type GalleryImageObjectFitType = 'contain' | 'cover';
|
type GalleryImageObjectFitType = 'contain' | 'cover';
|
||||||
|
|
||||||
export type Gallery = {
|
|
||||||
images: InvokeAI._Image[];
|
|
||||||
latest_mtime?: number;
|
|
||||||
earliest_mtime?: number;
|
|
||||||
areMoreImagesAvailable: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface GalleryState {
|
export interface GalleryState {
|
||||||
/**
|
/**
|
||||||
* The selected image's unique name
|
* The selected image
|
||||||
* Use `selectedImageSelector` to access the image
|
|
||||||
*/
|
*/
|
||||||
selectedImageName: string;
|
selectedImage?: SelectedImage;
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
};
|
|
||||||
galleryImageMinimumWidth: number;
|
galleryImageMinimumWidth: number;
|
||||||
galleryImageObjectFit: GalleryImageObjectFitType;
|
galleryImageObjectFit: GalleryImageObjectFitType;
|
||||||
shouldAutoSwitchToNewImages: boolean;
|
shouldAutoSwitchToNewImages: boolean;
|
||||||
categories: {
|
|
||||||
user: Gallery;
|
|
||||||
result: Gallery;
|
|
||||||
};
|
|
||||||
currentCategory: GalleryCategory;
|
|
||||||
galleryWidth: number;
|
galleryWidth: number;
|
||||||
shouldUseSingleGalleryColumn: boolean;
|
shouldUseSingleGalleryColumn: boolean;
|
||||||
|
currentCategory: 'results' | 'uploads';
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: GalleryState = {
|
const initialState: GalleryState = {
|
||||||
selectedImageName: '',
|
selectedImage: undefined,
|
||||||
currentImageUuid: '',
|
|
||||||
galleryImageMinimumWidth: 64,
|
galleryImageMinimumWidth: 64,
|
||||||
galleryImageObjectFit: 'cover',
|
galleryImageObjectFit: 'cover',
|
||||||
shouldAutoSwitchToNewImages: true,
|
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,
|
galleryWidth: 300,
|
||||||
shouldUseSingleGalleryColumn: false,
|
shouldUseSingleGalleryColumn: false,
|
||||||
|
currentCategory: 'results',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const gallerySlice = createSlice({
|
export const gallerySlice = createSlice({
|
||||||
name: 'gallery',
|
name: 'gallery',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
imageSelected: (state, action: PayloadAction<string>) => {
|
imageSelected: (
|
||||||
state.selectedImageName = action.payload;
|
|
||||||
},
|
|
||||||
setCurrentImage: (state, action: PayloadAction<InvokeAI._Image>) => {
|
|
||||||
state.currentImage = action.payload;
|
|
||||||
state.currentImageUuid = action.payload.uuid;
|
|
||||||
},
|
|
||||||
removeImage: (
|
|
||||||
state,
|
state,
|
||||||
action: PayloadAction<InvokeAI.ImageDeletedResponse>
|
action: PayloadAction<SelectedImage | undefined>
|
||||||
) => {
|
) => {
|
||||||
const { uuid, category } = action.payload;
|
state.selectedImage = action.payload;
|
||||||
|
// TODO: if the user selects an image, disable the auto switch?
|
||||||
const tempImages = state.categories[category as GalleryCategory].images;
|
// state.shouldAutoSwitchToNewImages = false;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setGalleryImageMinimumWidth: (state, action: PayloadAction<number>) => {
|
setGalleryImageMinimumWidth: (state, action: PayloadAction<number>) => {
|
||||||
state.galleryImageMinimumWidth = action.payload;
|
state.galleryImageMinimumWidth = action.payload;
|
||||||
@ -267,7 +55,10 @@ export const gallerySlice = createSlice({
|
|||||||
setShouldAutoSwitchToNewImages: (state, action: PayloadAction<boolean>) => {
|
setShouldAutoSwitchToNewImages: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldAutoSwitchToNewImages = action.payload;
|
state.shouldAutoSwitchToNewImages = action.payload;
|
||||||
},
|
},
|
||||||
setCurrentCategory: (state, action: PayloadAction<GalleryCategory>) => {
|
setCurrentCategory: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<'results' | 'uploads'>
|
||||||
|
) => {
|
||||||
state.currentCategory = action.payload;
|
state.currentCategory = action.payload;
|
||||||
},
|
},
|
||||||
setGalleryWidth: (state, action: PayloadAction<number>) => {
|
setGalleryWidth: (state, action: PayloadAction<number>) => {
|
||||||
@ -286,9 +77,11 @@ export const gallerySlice = createSlice({
|
|||||||
*/
|
*/
|
||||||
builder.addCase(invocationComplete, (state, action) => {
|
builder.addCase(invocationComplete, (state, action) => {
|
||||||
const { data } = action.payload;
|
const { data } = action.payload;
|
||||||
if (isImageOutput(data.result)) {
|
if (isImageOutput(data.result) && state.shouldAutoSwitchToNewImages) {
|
||||||
state.selectedImageName = data.result.image.image_name;
|
state.selectedImage = {
|
||||||
state.intermediateImage = undefined;
|
name: data.result.image.image_name,
|
||||||
|
type: 'results',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -299,27 +92,19 @@ export const gallerySlice = createSlice({
|
|||||||
const { response } = action.payload;
|
const { response } = action.payload;
|
||||||
|
|
||||||
const uploadedImage = deserializeImageResponse(response);
|
const uploadedImage = deserializeImageResponse(response);
|
||||||
state.selectedImageName = uploadedImage.name;
|
state.selectedImage = { name: uploadedImage.name, type: 'uploads' };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
imageSelected,
|
imageSelected,
|
||||||
addImage,
|
|
||||||
clearIntermediateImage,
|
|
||||||
removeImage,
|
|
||||||
setCurrentImage,
|
|
||||||
addGalleryImages,
|
|
||||||
setIntermediateImage,
|
|
||||||
selectNextImage,
|
|
||||||
selectPrevImage,
|
|
||||||
setGalleryImageMinimumWidth,
|
setGalleryImageMinimumWidth,
|
||||||
setGalleryImageObjectFit,
|
setGalleryImageObjectFit,
|
||||||
setShouldAutoSwitchToNewImages,
|
setShouldAutoSwitchToNewImages,
|
||||||
setCurrentCategory,
|
|
||||||
setGalleryWidth,
|
setGalleryWidth,
|
||||||
setShouldUseSingleGalleryColumn,
|
setShouldUseSingleGalleryColumn,
|
||||||
|
setCurrentCategory,
|
||||||
} = gallerySlice.actions;
|
} = gallerySlice.actions;
|
||||||
|
|
||||||
export default gallerySlice.reducer;
|
export default gallerySlice.reducer;
|
||||||
|
@ -5,7 +5,7 @@ import { ResultsState } from './resultsSlice';
|
|||||||
*
|
*
|
||||||
* Currently denylisting results slice entirely, see persist config in store.ts
|
* 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(
|
export const resultsDenylist = itemsToDenylist.map(
|
||||||
(denylistItem) => `results.${denylistItem}`
|
(denylistItem) => `results.${denylistItem}`
|
||||||
|
@ -65,7 +65,7 @@ const resultsSlice = createSlice({
|
|||||||
deserializeImageResponse(image)
|
deserializeImageResponse(image)
|
||||||
);
|
);
|
||||||
|
|
||||||
resultsAdapter.addMany(state, resultImages);
|
resultsAdapter.setMany(state, resultImages);
|
||||||
|
|
||||||
state.page = page;
|
state.page = page;
|
||||||
state.pages = pages;
|
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
|
* 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(
|
export const uploadsDenylist = itemsToDenylist.map(
|
||||||
(denylistItem) => `uploads.${denylistItem}`
|
(denylistItem) => `uploads.${denylistItem}`
|
||||||
|
@ -53,7 +53,7 @@ const uploadsSlice = createSlice({
|
|||||||
|
|
||||||
const images = items.map((image) => deserializeImageResponse(image));
|
const images = items.map((image) => deserializeImageResponse(image));
|
||||||
|
|
||||||
uploadsAdapter.addMany(state, images);
|
uploadsAdapter.setMany(state, images);
|
||||||
|
|
||||||
state.page = page;
|
state.page = page;
|
||||||
state.pages = pages;
|
state.pages = pages;
|
||||||
@ -69,7 +69,7 @@ const uploadsSlice = createSlice({
|
|||||||
|
|
||||||
const uploadedImage = deserializeImageResponse(response);
|
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 SelectImagePlaceholder from 'common/components/SelectImagePlaceholder';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import useGetImageByNameAndType from 'features/gallery/hooks/useGetImageByName';
|
import useGetImageByNameAndType from 'features/gallery/hooks/useGetImageByName';
|
||||||
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
|
||||||
import {
|
import {
|
||||||
clearInitialImage,
|
clearInitialImage,
|
||||||
initialImageSelected,
|
initialImageSelected,
|
||||||
@ -16,15 +15,13 @@ import { DragEvent, useCallback, useState } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ImageType } from 'services/api';
|
import { ImageType } from 'services/api';
|
||||||
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
|
import ImageToImageOverlay from 'common/components/ImageToImageOverlay';
|
||||||
|
import { initialImageSelector } from 'features/parameters/store/generationSelectors';
|
||||||
|
|
||||||
const initialImagePreviewSelector = createSelector(
|
const selector = createSelector(
|
||||||
[(state: RootState) => state],
|
[initialImageSelector],
|
||||||
(state) => {
|
(initialImage) => {
|
||||||
const { initialImage } = state.generation;
|
|
||||||
const image = selectResultsById(state, initialImage as string);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
initialImage: image,
|
initialImage,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ memoizeOptions: { resultEqualityCheck: isEqual } }
|
{ memoizeOptions: { resultEqualityCheck: isEqual } }
|
||||||
@ -34,7 +31,7 @@ const InitialImagePreview = () => {
|
|||||||
const isImageToImageEnabled = useAppSelector(
|
const isImageToImageEnabled = useAppSelector(
|
||||||
(state: RootState) => state.generation.isImageToImageEnabled
|
(state: RootState) => state.generation.isImageToImageEnabled
|
||||||
);
|
);
|
||||||
const { initialImage } = useAppSelector(initialImagePreviewSelector);
|
const { initialImage } = useAppSelector(selector);
|
||||||
const { getUrl } = useGetUrl();
|
const { getUrl } = useGetUrl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -71,7 +68,7 @@ const InitialImagePreview = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(initialImageSelected(image.name));
|
dispatch(initialImageSelected({ name, type }));
|
||||||
},
|
},
|
||||||
[getImageByNameAndType, dispatch]
|
[getImageByNameAndType, dispatch]
|
||||||
);
|
);
|
||||||
@ -116,12 +113,7 @@ const InitialImagePreview = () => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{isLoaded && (
|
{isLoaded && <ImageToImageOverlay image={initialImage} />}
|
||||||
<ImageToImageOverlay
|
|
||||||
setIsLoaded={setIsLoaded}
|
|
||||||
image={initialImage}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{!initialImage?.url && <SelectImagePlaceholder />}
|
{!initialImage?.url && <SelectImagePlaceholder />}
|
||||||
|
@ -15,9 +15,9 @@ const AnimatedImageToImagePanel = () => {
|
|||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isImageToImageEnabled && (
|
{isImageToImageEnabled && (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, scaleX: 0, width: 0 }}
|
initial={{ opacity: 0, scale: 0, width: 0 }}
|
||||||
animate={{ opacity: 1, scaleX: 1, width: '28rem' }}
|
animate={{ opacity: 1, scale: 1, width: '28rem' }}
|
||||||
exit={{ opacity: 0, scaleX: 0, width: 0 }}
|
exit={{ opacity: 0, scale: 0, width: 0 }}
|
||||||
transition={{ type: 'spring', bounce: 0, duration: 0.35 }}
|
transition={{ type: 'spring', bounce: 0, duration: 0.35 }}
|
||||||
>
|
>
|
||||||
<Box sx={{ h: 'full', w: 'full', pl: 4 }}>
|
<Box sx={{ h: 'full', w: 'full', pl: 4 }}>
|
||||||
|
@ -13,17 +13,35 @@ const selector = createSelector(
|
|||||||
(generation, hotkeys, config) => {
|
(generation, hotkeys, config) => {
|
||||||
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
||||||
config.sd.height;
|
config.sd.height;
|
||||||
const { height } = generation;
|
const { height, shouldFitToWidthHeight, isImageToImageEnabled } =
|
||||||
|
generation;
|
||||||
|
|
||||||
const step = hotkeys.shift ? fineStep : coarseStep;
|
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 HeightSlider = () => {
|
||||||
const { height, initial, min, sliderMax, inputMax, step } =
|
const {
|
||||||
useAppSelector(selector);
|
height,
|
||||||
|
initial,
|
||||||
|
min,
|
||||||
|
sliderMax,
|
||||||
|
inputMax,
|
||||||
|
step,
|
||||||
|
shouldFitToWidthHeight,
|
||||||
|
isImageToImageEnabled,
|
||||||
|
} = useAppSelector(selector);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -40,6 +58,7 @@ const HeightSlider = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IAISlider
|
<IAISlider
|
||||||
|
isDisabled={!shouldFitToWidthHeight && isImageToImageEnabled}
|
||||||
label={t('parameters.height')}
|
label={t('parameters.height')}
|
||||||
value={height}
|
value={height}
|
||||||
min={min}
|
min={min}
|
||||||
|
@ -13,17 +13,34 @@ const selector = createSelector(
|
|||||||
(generation, hotkeys, config) => {
|
(generation, hotkeys, config) => {
|
||||||
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
const { initial, min, sliderMax, inputMax, fineStep, coarseStep } =
|
||||||
config.sd.width;
|
config.sd.width;
|
||||||
const { width } = generation;
|
const { width, shouldFitToWidthHeight, isImageToImageEnabled } = generation;
|
||||||
|
|
||||||
const step = hotkeys.shift ? fineStep : coarseStep;
|
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 WidthSlider = () => {
|
||||||
const { width, initial, min, sliderMax, inputMax, step } =
|
const {
|
||||||
useAppSelector(selector);
|
width,
|
||||||
|
initial,
|
||||||
|
min,
|
||||||
|
sliderMax,
|
||||||
|
inputMax,
|
||||||
|
step,
|
||||||
|
shouldFitToWidthHeight,
|
||||||
|
isImageToImageEnabled,
|
||||||
|
} = useAppSelector(selector);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
@ -40,6 +57,7 @@ const WidthSlider = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<IAISlider
|
<IAISlider
|
||||||
|
isDisabled={!shouldFitToWidthHeight && isImageToImageEnabled}
|
||||||
label={t('parameters.width')}
|
label={t('parameters.width')}
|
||||||
value={width}
|
value={width}
|
||||||
min={min}
|
min={min}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
SystemState,
|
SystemState,
|
||||||
cancelScheduled,
|
cancelScheduled,
|
||||||
cancelTypeChanged,
|
cancelTypeChanged,
|
||||||
CancelType,
|
CancelStrategy,
|
||||||
} from 'features/system/store/systemSlice';
|
} from 'features/system/store/systemSlice';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useCallback, memo } from 'react';
|
import { useCallback, memo } from 'react';
|
||||||
@ -87,7 +87,7 @@ const CancelButton = (
|
|||||||
const handleCancelTypeChanged = useCallback(
|
const handleCancelTypeChanged = useCallback(
|
||||||
(value: string | string[]) => {
|
(value: string | string[]) => {
|
||||||
const newCancelType = Array.isArray(value) ? value[0] : value;
|
const newCancelType = Array.isArray(value) ? value[0] : value;
|
||||||
dispatch(cancelTypeChanged(newCancelType as CancelType));
|
dispatch(cancelTypeChanged(newCancelType as CancelStrategy));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
import { readinessSelector } from 'app/selectors/readinessSelector';
|
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||||
import { generateImage } from 'app/socketio/actions';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton, { IAIButtonProps } from 'common/components/IAIButton';
|
import IAIButton, { IAIButtonProps } from 'common/components/IAIButton';
|
||||||
import IAIIconButton, {
|
import IAIIconButton, {
|
||||||
@ -8,10 +7,11 @@ import IAIIconButton, {
|
|||||||
} from 'common/components/IAIIconButton';
|
} from 'common/components/IAIIconButton';
|
||||||
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
|
import { clampSymmetrySteps } from 'features/parameters/store/generationSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { FaPlay } from 'react-icons/fa';
|
import { FaPlay } from 'react-icons/fa';
|
||||||
import { generateGraphBuilt, sessionCreated } from 'services/thunks/session';
|
import { generateGraphBuilt } from 'services/thunks/session';
|
||||||
|
|
||||||
interface InvokeButton
|
interface InvokeButton
|
||||||
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
extends Omit<IAIButtonProps | IAIIconButtonProps, 'aria-label'> {
|
||||||
@ -24,19 +24,16 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
const { isReady } = useAppSelector(readinessSelector);
|
const { isReady } = useAppSelector(readinessSelector);
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
|
||||||
const handleClickGenerate = () => {
|
const handleInvoke = useCallback(() => {
|
||||||
// dispatch(generateImage(activeTabName));
|
dispatch(clampSymmetrySteps());
|
||||||
dispatch(generateGraphBuilt());
|
dispatch(generateGraphBuilt());
|
||||||
};
|
}, [dispatch]);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
['ctrl+enter', 'meta+enter'],
|
['ctrl+enter', 'meta+enter'],
|
||||||
() => {
|
handleInvoke,
|
||||||
dispatch(clampSymmetrySteps());
|
|
||||||
dispatch(generateImage(activeTabName));
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
enabled: () => isReady,
|
enabled: () => isReady,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
@ -53,7 +50,7 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
type="submit"
|
type="submit"
|
||||||
icon={<FaPlay />}
|
icon={<FaPlay />}
|
||||||
isDisabled={!isReady}
|
isDisabled={!isReady}
|
||||||
onClick={handleClickGenerate}
|
onClick={handleInvoke}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
w="100%"
|
w="100%"
|
||||||
tooltip={t('parameters.invoke')}
|
tooltip={t('parameters.invoke')}
|
||||||
@ -66,7 +63,7 @@ export default function InvokeButton(props: InvokeButton) {
|
|||||||
aria-label={t('parameters.invoke')}
|
aria-label={t('parameters.invoke')}
|
||||||
type="submit"
|
type="submit"
|
||||||
isDisabled={!isReady}
|
isDisabled={!isReady}
|
||||||
onClick={handleClickGenerate}
|
onClick={handleInvoke}
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
w="100%"
|
w="100%"
|
||||||
colorScheme="accent"
|
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 { Box, FormControl, Textarea } from '@chakra-ui/react';
|
||||||
import { generateImage } from 'app/socketio/actions';
|
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { ChangeEvent, KeyboardEvent, useRef } from 'react';
|
import { ChangeEvent, KeyboardEvent, useRef } from 'react';
|
||||||
@ -8,6 +7,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { readinessSelector } from 'app/selectors/readinessSelector';
|
import { readinessSelector } from 'app/selectors/readinessSelector';
|
||||||
import {
|
import {
|
||||||
GenerationState,
|
GenerationState,
|
||||||
|
clampSymmetrySteps,
|
||||||
setPrompt,
|
setPrompt,
|
||||||
} from 'features/parameters/store/generationSlice';
|
} from 'features/parameters/store/generationSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
@ -15,6 +15,7 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
|||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { generateGraphBuilt } from 'services/thunks/session';
|
||||||
|
|
||||||
const promptInputSelector = createSelector(
|
const promptInputSelector = createSelector(
|
||||||
[(state: RootState) => state.generation, activeTabNameSelector],
|
[(state: RootState) => state.generation, activeTabNameSelector],
|
||||||
@ -36,7 +37,7 @@ const promptInputSelector = createSelector(
|
|||||||
*/
|
*/
|
||||||
const PromptInput = () => {
|
const PromptInput = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { prompt, activeTabName } = useAppSelector(promptInputSelector);
|
const { prompt } = useAppSelector(promptInputSelector);
|
||||||
const { isReady } = useAppSelector(readinessSelector);
|
const { isReady } = useAppSelector(readinessSelector);
|
||||||
|
|
||||||
const promptRef = useRef<HTMLTextAreaElement>(null);
|
const promptRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@ -58,7 +59,8 @@ const PromptInput = () => {
|
|||||||
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
if (e.key === 'Enter' && e.shiftKey === false && isReady) {
|
if (e.key === 'Enter' && e.shiftKey === false && isReady) {
|
||||||
e.preventDefault();
|
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 { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
import { selectResultsById } from 'features/gallery/store/resultsSlice';
|
||||||
import {
|
|
||||||
selectResultsById,
|
|
||||||
selectResultsEntities,
|
|
||||||
} from 'features/gallery/store/resultsSlice';
|
|
||||||
import { selectUploadsById } from 'features/gallery/store/uploadsSlice';
|
import { selectUploadsById } from 'features/gallery/store/uploadsSlice';
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
@ -25,11 +21,14 @@ export const mayGenerateMultipleImagesSelector = createSelector(
|
|||||||
export const initialImageSelector = createSelector(
|
export const initialImageSelector = createSelector(
|
||||||
[(state: RootState) => state, generationSelector],
|
[(state: RootState) => state, generationSelector],
|
||||||
(state, generation) => {
|
(state, generation) => {
|
||||||
const { initialImage: initialImageName } = generation;
|
const { initialImage } = generation;
|
||||||
|
|
||||||
return (
|
if (initialImage?.type === 'results') {
|
||||||
selectResultsById(state, initialImageName as string) ??
|
return selectResultsById(state, initialImage.name);
|
||||||
selectUploadsById(state, initialImageName as string)
|
}
|
||||||
);
|
|
||||||
|
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 promptToString from 'common/util/promptToString';
|
||||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp } from 'lodash-es';
|
||||||
|
import { ImageField, ImageType } from 'services/api';
|
||||||
|
|
||||||
|
export type SelectedImage = {
|
||||||
|
name: string;
|
||||||
|
type: ImageType;
|
||||||
|
};
|
||||||
|
|
||||||
export interface GenerationState {
|
export interface GenerationState {
|
||||||
cfgScale: number;
|
cfgScale: number;
|
||||||
height: number;
|
height: number;
|
||||||
img2imgStrength: number;
|
img2imgStrength: number;
|
||||||
infillMethod: string;
|
infillMethod: string;
|
||||||
initialImage?: InvokeAI._Image | string; // can be an Image or url
|
initialImage?: SelectedImage; // can be an Image or url
|
||||||
iterations: number;
|
iterations: number;
|
||||||
maskPath: string;
|
maskPath: string;
|
||||||
perlin: number;
|
perlin: number;
|
||||||
@ -345,7 +351,7 @@ export const generationSlice = createSlice({
|
|||||||
setVerticalSymmetrySteps: (state, action: PayloadAction<number>) => {
|
setVerticalSymmetrySteps: (state, action: PayloadAction<number>) => {
|
||||||
state.verticalSymmetrySteps = action.payload;
|
state.verticalSymmetrySteps = action.payload;
|
||||||
},
|
},
|
||||||
initialImageSelected: (state, action: PayloadAction<string>) => {
|
initialImageSelected: (state, action: PayloadAction<SelectedImage>) => {
|
||||||
state.initialImage = action.payload;
|
state.initialImage = action.payload;
|
||||||
state.isImageToImageEnabled = true;
|
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
|
@ -17,7 +17,7 @@ import React from 'react';
|
|||||||
|
|
||||||
import SearchModels from './SearchModels';
|
import SearchModels from './SearchModels';
|
||||||
|
|
||||||
import { addNewModel } from 'app/socketio/actions';
|
// import { addNewModel } from 'app/socketio/actions';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
VStack,
|
VStack,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { InvokeDiffusersModelConfigProps } from 'app/types/invokeai';
|
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAIInput from 'common/components/IAIInput';
|
import IAIInput from 'common/components/IAIInput';
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
VStack,
|
VStack,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
|
|
||||||
import { addNewModel } from 'app/socketio/actions';
|
// import { addNewModel } from 'app/socketio/actions';
|
||||||
import { Field, Formik } from 'formik';
|
import { Field, Formik } from 'formik';
|
||||||
import { useTranslation } from 'react-i18next';
|
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 { 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 { Field, Formik } from 'formik';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { mergeDiffusersModels } from 'app/socketio/actions';
|
// import { mergeDiffusersModels } from 'app/socketio/actions';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
UnorderedList,
|
UnorderedList,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { convertToDiffusers } from 'app/socketio/actions';
|
// import { convertToDiffusers } from 'app/socketio/actions';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
import { DeleteIcon, EditIcon } from '@chakra-ui/icons';
|
||||||
import { Box, Button, Flex, Spacer, Text, Tooltip } from '@chakra-ui/react';
|
import { Box, Button, Flex, Spacer, Text, Tooltip } from '@chakra-ui/react';
|
||||||
import { ModelStatus } from 'app/types/invokeai';
|
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 { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
import IAIAlertDialog from 'common/components/IAIAlertDialog';
|
||||||
|
@ -20,7 +20,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { FaSearch, FaTrash } from 'react-icons/fa';
|
import { FaSearch, FaTrash } from 'react-icons/fa';
|
||||||
|
|
||||||
import { addNewModel, searchForModels } from 'app/socketio/actions';
|
// import { addNewModel, searchForModels } from 'app/socketio/actions';
|
||||||
import {
|
import {
|
||||||
setFoundModels,
|
setFoundModels,
|
||||||
setSearchFolder,
|
setSearchFolder,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ChakraProps,
|
ChakraProps,
|
||||||
Flex,
|
Flex,
|
||||||
Grid,
|
|
||||||
Heading,
|
Heading,
|
||||||
Modal,
|
Modal,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
@ -14,64 +13,57 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
|
||||||
import IAISelect from 'common/components/IAISelect';
|
import IAISelect from 'common/components/IAISelect';
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import {
|
import {
|
||||||
consoleLogLevelChanged,
|
consoleLogLevelChanged,
|
||||||
InProgressImageType,
|
|
||||||
setEnableImageDebugging,
|
setEnableImageDebugging,
|
||||||
setSaveIntermediatesInterval,
|
|
||||||
setShouldConfirmOnDelete,
|
setShouldConfirmOnDelete,
|
||||||
setShouldDisplayGuides,
|
setShouldDisplayGuides,
|
||||||
setShouldDisplayInProgressType,
|
|
||||||
shouldLogToConsoleChanged,
|
shouldLogToConsoleChanged,
|
||||||
SystemState,
|
SystemState,
|
||||||
} from 'features/system/store/systemSlice';
|
} from 'features/system/store/systemSlice';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
import {
|
import {
|
||||||
|
setShouldAutoShowProgressImages,
|
||||||
setShouldUseCanvasBetaLayout,
|
setShouldUseCanvasBetaLayout,
|
||||||
setShouldUseSliders,
|
setShouldUseSliders,
|
||||||
} from 'features/ui/store/uiSlice';
|
} from 'features/ui/store/uiSlice';
|
||||||
import { UIState } from 'features/ui/store/uiTypes';
|
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 { persistor } from 'app/store/persistor';
|
||||||
import { ChangeEvent, cloneElement, ReactElement, useCallback } from 'react';
|
import { ChangeEvent, cloneElement, ReactElement, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
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 { LogLevelName } from 'roarr';
|
||||||
import { F } from 'ts-toolbelt';
|
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[systemSelector, uiSelector],
|
[systemSelector, uiSelector],
|
||||||
(system: SystemState, ui: UIState) => {
|
(system: SystemState, ui: UIState) => {
|
||||||
const {
|
const {
|
||||||
shouldDisplayInProgressType,
|
|
||||||
shouldConfirmOnDelete,
|
shouldConfirmOnDelete,
|
||||||
shouldDisplayGuides,
|
shouldDisplayGuides,
|
||||||
model_list,
|
|
||||||
saveIntermediatesInterval,
|
|
||||||
enableImageDebugging,
|
enableImageDebugging,
|
||||||
consoleLogLevel,
|
consoleLogLevel,
|
||||||
shouldLogToConsole,
|
shouldLogToConsole,
|
||||||
} = system;
|
} = system;
|
||||||
|
|
||||||
const { shouldUseCanvasBetaLayout, shouldUseSliders } = ui;
|
const {
|
||||||
|
shouldUseCanvasBetaLayout,
|
||||||
|
shouldUseSliders,
|
||||||
|
shouldAutoShowProgressImages,
|
||||||
|
} = ui;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
shouldDisplayInProgressType,
|
|
||||||
shouldConfirmOnDelete,
|
shouldConfirmOnDelete,
|
||||||
shouldDisplayGuides,
|
shouldDisplayGuides,
|
||||||
models: map(model_list, (_model, key) => key),
|
|
||||||
saveIntermediatesInterval,
|
|
||||||
enableImageDebugging,
|
enableImageDebugging,
|
||||||
shouldUseCanvasBetaLayout,
|
shouldUseCanvasBetaLayout,
|
||||||
shouldUseSliders,
|
shouldUseSliders,
|
||||||
|
shouldAutoShowProgressImages,
|
||||||
consoleLogLevel,
|
consoleLogLevel,
|
||||||
shouldLogToConsole,
|
shouldLogToConsole,
|
||||||
};
|
};
|
||||||
@ -104,8 +96,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const steps = useAppSelector((state: RootState) => state.generation.steps);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isSettingsModalOpen,
|
isOpen: isSettingsModalOpen,
|
||||||
onOpen: onSettingsModalOpen,
|
onOpen: onSettingsModalOpen,
|
||||||
@ -119,13 +109,12 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
shouldDisplayInProgressType,
|
|
||||||
shouldConfirmOnDelete,
|
shouldConfirmOnDelete,
|
||||||
shouldDisplayGuides,
|
shouldDisplayGuides,
|
||||||
saveIntermediatesInterval,
|
|
||||||
enableImageDebugging,
|
enableImageDebugging,
|
||||||
shouldUseCanvasBetaLayout,
|
shouldUseCanvasBetaLayout,
|
||||||
shouldUseSliders,
|
shouldUseSliders,
|
||||||
|
shouldAutoShowProgressImages,
|
||||||
consoleLogLevel,
|
consoleLogLevel,
|
||||||
shouldLogToConsole,
|
shouldLogToConsole,
|
||||||
} = useAppSelector(selector);
|
} = useAppSelector(selector);
|
||||||
@ -134,18 +123,12 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
* Resets localstorage, then opens a secondary modal informing user to
|
* Resets localstorage, then opens a secondary modal informing user to
|
||||||
* refresh their browser.
|
* refresh their browser.
|
||||||
* */
|
* */
|
||||||
const handleClickResetWebUI = () => {
|
const handleClickResetWebUI = useCallback(() => {
|
||||||
persistor.purge().then(() => {
|
persistor.purge().then(() => {
|
||||||
onSettingsModalClose();
|
onSettingsModalClose();
|
||||||
onRefreshModalOpen();
|
onRefreshModalOpen();
|
||||||
});
|
});
|
||||||
};
|
}, [onSettingsModalClose, onRefreshModalOpen]);
|
||||||
|
|
||||||
const handleChangeIntermediateSteps = (value: number) => {
|
|
||||||
if (value > steps) value = steps;
|
|
||||||
if (value < 1) value = 1;
|
|
||||||
dispatch(setSaveIntermediatesInterval(value));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLogLevelChanged = useCallback(
|
const handleLogLevelChanged = useCallback(
|
||||||
(e: ChangeEvent<HTMLSelectElement>) => {
|
(e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
@ -182,32 +165,6 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
<Flex sx={modalSectionStyles}>
|
<Flex sx={modalSectionStyles}>
|
||||||
<Heading size="sm">{t('settings.general')}</Heading>
|
<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
|
<IAISwitch
|
||||||
label={t('settings.confirmOnDelete')}
|
label={t('settings.confirmOnDelete')}
|
||||||
isChecked={shouldConfirmOnDelete}
|
isChecked={shouldConfirmOnDelete}
|
||||||
@ -236,6 +193,13 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
|
|||||||
dispatch(setShouldUseSliders(e.target.checked))
|
dispatch(setShouldUseSliders(e.target.checked))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<IAISwitch
|
||||||
|
label={t('settings.autoShowProgress')}
|
||||||
|
isChecked={shouldAutoShowProgressImages}
|
||||||
|
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||||||
|
dispatch(setShouldAutoShowProgressImages(e.target.checked))
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex sx={modalSectionStyles}>
|
<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 { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { errorSeen, SystemState } from 'features/system/store/systemSlice';
|
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { systemSelector } from '../store/systemSelectors';
|
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(
|
const statusIndicatorSelector = createSelector(
|
||||||
systemSelector,
|
systemSelector,
|
||||||
(system: SystemState) => {
|
(system) => {
|
||||||
|
const {
|
||||||
|
isConnected,
|
||||||
|
isProcessing,
|
||||||
|
statusTranslationKey,
|
||||||
|
currentIteration,
|
||||||
|
totalIterations,
|
||||||
|
currentStatusHasSteps,
|
||||||
|
} = system;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isConnected: system.isConnected,
|
isConnected,
|
||||||
isProcessing: system.isProcessing,
|
isProcessing,
|
||||||
currentIteration: system.currentIteration,
|
currentIteration,
|
||||||
totalIterations: system.totalIterations,
|
totalIterations,
|
||||||
currentStatus: system.currentStatus,
|
statusTranslationKey,
|
||||||
hasError: system.hasError,
|
currentStatusHasSteps,
|
||||||
wasErrorSeen: system.wasErrorSeen,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -30,64 +42,69 @@ const StatusIndicator = () => {
|
|||||||
isProcessing,
|
isProcessing,
|
||||||
currentIteration,
|
currentIteration,
|
||||||
totalIterations,
|
totalIterations,
|
||||||
currentStatus,
|
statusTranslationKey,
|
||||||
hasError,
|
currentStatusHasSteps,
|
||||||
wasErrorSeen,
|
|
||||||
} = useAppSelector(statusIndicatorSelector);
|
} = useAppSelector(statusIndicatorSelector);
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
let statusIdentifier;
|
const statusColorScheme = useMemo(() => {
|
||||||
|
|
||||||
if (isConnected && !hasError) {
|
|
||||||
statusIdentifier = 'ok';
|
|
||||||
} else {
|
|
||||||
statusIdentifier = 'error';
|
|
||||||
}
|
|
||||||
|
|
||||||
let statusMessage = currentStatus;
|
|
||||||
|
|
||||||
if (isProcessing) {
|
if (isProcessing) {
|
||||||
statusIdentifier = 'working';
|
return 'working';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusMessage)
|
if (isConnected) {
|
||||||
if (isProcessing) {
|
return 'ok';
|
||||||
if (totalIterations > 1) {
|
|
||||||
statusMessage = `${t(
|
|
||||||
statusMessage as keyof typeof t
|
|
||||||
)} (${currentIteration}/${totalIterations})`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tooltipLabel =
|
return 'error';
|
||||||
hasError && !wasErrorSeen
|
}, [isProcessing, isConnected]);
|
||||||
? 'Click to clear, check logs for details'
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const statusIndicatorCursor =
|
const iterationsText = useMemo(() => {
|
||||||
hasError && !wasErrorSeen ? 'pointer' : 'initial';
|
if (!(currentIteration && totalIterations)) {
|
||||||
|
return;
|
||||||
const handleClickStatusIndicator = () => {
|
|
||||||
if (hasError || !wasErrorSeen) {
|
|
||||||
dispatch(errorSeen());
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return ` (${currentIteration}/${totalIterations})`;
|
||||||
|
}, [currentIteration, totalIterations]);
|
||||||
|
|
||||||
|
const isHovered = useHoverDirty(ref);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltipLabel}>
|
<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
|
<Text
|
||||||
cursor={statusIndicatorCursor}
|
|
||||||
onClick={handleClickStatusIndicator}
|
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: 'sm',
|
fontSize: 'sm',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
color: `${statusIdentifier}.400`,
|
color: `${statusColorScheme}.400`,
|
||||||
|
pb: '1px',
|
||||||
|
userSelect: 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t(statusMessage as keyof typeof t)}
|
{t(statusTranslationKey as ResourceKey)}
|
||||||
|
{iterationsText}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
</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 type { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/types/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import {
|
import {
|
||||||
generatorProgress,
|
generatorProgress,
|
||||||
|
graphExecutionStateComplete,
|
||||||
invocationComplete,
|
invocationComplete,
|
||||||
invocationError,
|
invocationError,
|
||||||
invocationStarted,
|
invocationStarted,
|
||||||
@ -13,7 +14,6 @@ import {
|
|||||||
socketUnsubscribed,
|
socketUnsubscribed,
|
||||||
} from 'services/events/actions';
|
} from 'services/events/actions';
|
||||||
|
|
||||||
import i18n from 'i18n';
|
|
||||||
import { ProgressImage } from 'services/events/types';
|
import { ProgressImage } from 'services/events/types';
|
||||||
import { initialImageSelected } from 'features/parameters/store/generationSlice';
|
import { initialImageSelected } from 'features/parameters/store/generationSlice';
|
||||||
import { makeToast } from '../hooks/useToastWatcher';
|
import { makeToast } from '../hooks/useToastWatcher';
|
||||||
@ -22,53 +22,29 @@ import { receivedModels } from 'services/thunks/model';
|
|||||||
import { parsedOpenAPISchema } from 'features/nodes/store/nodesSlice';
|
import { parsedOpenAPISchema } from 'features/nodes/store/nodesSlice';
|
||||||
import { LogLevelName } from 'roarr';
|
import { LogLevelName } from 'roarr';
|
||||||
import { InvokeLogLevel } from 'app/logging/useLogger';
|
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 {
|
export interface SystemState {
|
||||||
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;
|
|
||||||
isGFPGANAvailable: boolean;
|
isGFPGANAvailable: boolean;
|
||||||
isESRGANAvailable: boolean;
|
isESRGANAvailable: boolean;
|
||||||
isConnected: boolean;
|
isConnected: boolean;
|
||||||
socketId: string;
|
isProcessing: boolean;
|
||||||
shouldConfirmOnDelete: boolean;
|
shouldConfirmOnDelete: boolean;
|
||||||
openAccordions: ExpandedIndex;
|
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
totalSteps: number;
|
totalSteps: number;
|
||||||
currentIteration: number;
|
currentIteration: number;
|
||||||
totalIterations: number;
|
totalIterations: number;
|
||||||
currentStatus: string;
|
|
||||||
currentStatusHasSteps: boolean;
|
currentStatusHasSteps: boolean;
|
||||||
shouldDisplayGuides: boolean;
|
shouldDisplayGuides: boolean;
|
||||||
wasErrorSeen: boolean;
|
|
||||||
isCancelable: boolean;
|
isCancelable: boolean;
|
||||||
saveIntermediatesInterval: number;
|
|
||||||
enableImageDebugging: boolean;
|
enableImageDebugging: boolean;
|
||||||
toastQueue: UseToastOptions[];
|
toastQueue: UseToastOptions[];
|
||||||
searchFolder: string | null;
|
searchFolder: string | null;
|
||||||
foundModels: InvokeAI.FoundModel[] | null;
|
foundModels: InvokeAI.FoundModel[] | null;
|
||||||
openModel: string | null;
|
openModel: string | null;
|
||||||
cancelOptions: {
|
|
||||||
cancelType: CancelType;
|
|
||||||
cancelAfter: number | null;
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* The current progress image
|
* The current progress image
|
||||||
*/
|
*/
|
||||||
@ -80,7 +56,7 @@ export interface SystemState
|
|||||||
/**
|
/**
|
||||||
* Cancel strategy
|
* Cancel strategy
|
||||||
*/
|
*/
|
||||||
cancelType: CancelType;
|
cancelType: CancelStrategy;
|
||||||
/**
|
/**
|
||||||
* Whether or not a scheduled cancelation is pending
|
* Whether or not a scheduled cancelation is pending
|
||||||
*/
|
*/
|
||||||
@ -102,47 +78,28 @@ export interface SystemState
|
|||||||
*/
|
*/
|
||||||
consoleLogLevel: InvokeLogLevel;
|
consoleLogLevel: InvokeLogLevel;
|
||||||
shouldLogToConsole: boolean;
|
shouldLogToConsole: boolean;
|
||||||
|
statusTranslationKey: TFuncKey;
|
||||||
|
canceledSession: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialSystemState: SystemState = {
|
const initialSystemState: SystemState = {
|
||||||
isConnected: false,
|
isConnected: false,
|
||||||
isProcessing: false,
|
isProcessing: false,
|
||||||
shouldShowLogViewer: false,
|
|
||||||
shouldDisplayInProgressType: 'latents',
|
|
||||||
shouldDisplayGuides: true,
|
shouldDisplayGuides: true,
|
||||||
isGFPGANAvailable: true,
|
isGFPGANAvailable: true,
|
||||||
isESRGANAvailable: true,
|
isESRGANAvailable: true,
|
||||||
socketId: '',
|
|
||||||
shouldConfirmOnDelete: true,
|
shouldConfirmOnDelete: true,
|
||||||
openAccordions: [0],
|
|
||||||
currentStep: 0,
|
currentStep: 0,
|
||||||
totalSteps: 0,
|
totalSteps: 0,
|
||||||
currentIteration: 0,
|
currentIteration: 0,
|
||||||
totalIterations: 0,
|
totalIterations: 0,
|
||||||
currentStatus: i18n.isInitialized
|
|
||||||
? i18n.t('common.statusDisconnected')
|
|
||||||
: 'Disconnected',
|
|
||||||
currentStatusHasSteps: false,
|
currentStatusHasSteps: false,
|
||||||
model: '',
|
|
||||||
model_id: '',
|
|
||||||
model_hash: '',
|
|
||||||
app_id: '',
|
|
||||||
app_version: '',
|
|
||||||
model_list: {},
|
|
||||||
infill_methods: [],
|
|
||||||
hasError: false,
|
|
||||||
wasErrorSeen: true,
|
|
||||||
isCancelable: true,
|
isCancelable: true,
|
||||||
saveIntermediatesInterval: 5,
|
|
||||||
enableImageDebugging: false,
|
enableImageDebugging: false,
|
||||||
toastQueue: [],
|
toastQueue: [],
|
||||||
searchFolder: null,
|
searchFolder: null,
|
||||||
foundModels: null,
|
foundModels: null,
|
||||||
openModel: null,
|
openModel: null,
|
||||||
cancelOptions: {
|
|
||||||
cancelType: 'immediate',
|
|
||||||
cancelAfter: null,
|
|
||||||
},
|
|
||||||
progressImage: null,
|
progressImage: null,
|
||||||
sessionId: null,
|
sessionId: null,
|
||||||
cancelType: 'immediate',
|
cancelType: 'immediate',
|
||||||
@ -152,29 +109,21 @@ const initialSystemState: SystemState = {
|
|||||||
wasSchemaParsed: false,
|
wasSchemaParsed: false,
|
||||||
consoleLogLevel: 'error',
|
consoleLogLevel: 'error',
|
||||||
shouldLogToConsole: true,
|
shouldLogToConsole: true,
|
||||||
|
statusTranslationKey: 'common.statusDisconnected',
|
||||||
|
canceledSession: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const systemSlice = createSlice({
|
export const systemSlice = createSlice({
|
||||||
name: 'system',
|
name: 'system',
|
||||||
initialState: initialSystemState,
|
initialState: initialSystemState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setShouldDisplayInProgressType: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<InProgressImageType>
|
|
||||||
) => {
|
|
||||||
state.shouldDisplayInProgressType = action.payload;
|
|
||||||
},
|
|
||||||
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
setIsProcessing: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isProcessing = action.payload;
|
state.isProcessing = action.payload;
|
||||||
},
|
},
|
||||||
setCurrentStatus: (state, action: PayloadAction<string>) => {
|
setCurrentStatus: (state, action: PayloadAction<TFuncKey>) => {
|
||||||
state.currentStatus = action.payload;
|
state.statusTranslationKey = action.payload;
|
||||||
},
|
|
||||||
setSystemStatus: (state, action: PayloadAction<InvokeAI.SystemStatus>) => {
|
|
||||||
return { ...state, ...action.payload };
|
|
||||||
},
|
},
|
||||||
errorOccurred: (state) => {
|
errorOccurred: (state) => {
|
||||||
state.hasError = true;
|
|
||||||
state.isProcessing = false;
|
state.isProcessing = false;
|
||||||
state.isCancelable = true;
|
state.isCancelable = true;
|
||||||
state.currentStep = 0;
|
state.currentStep = 0;
|
||||||
@ -182,18 +131,7 @@ export const systemSlice = createSlice({
|
|||||||
state.currentIteration = 0;
|
state.currentIteration = 0;
|
||||||
state.totalIterations = 0;
|
state.totalIterations = 0;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
state.currentStatus = i18n.t('common.statusError');
|
state.statusTranslationKey = '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;
|
|
||||||
},
|
},
|
||||||
setIsConnected: (state, action: PayloadAction<boolean>) => {
|
setIsConnected: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isConnected = action.payload;
|
state.isConnected = action.payload;
|
||||||
@ -204,23 +142,10 @@ export const systemSlice = createSlice({
|
|||||||
state.currentIteration = 0;
|
state.currentIteration = 0;
|
||||||
state.totalIterations = 0;
|
state.totalIterations = 0;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
state.hasError = false;
|
|
||||||
},
|
|
||||||
setSocketId: (state, action: PayloadAction<string>) => {
|
|
||||||
state.socketId = action.payload;
|
|
||||||
},
|
},
|
||||||
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
setShouldConfirmOnDelete: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldConfirmOnDelete = action.payload;
|
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>) => {
|
setShouldDisplayGuides: (state, action: PayloadAction<boolean>) => {
|
||||||
state.shouldDisplayGuides = action.payload;
|
state.shouldDisplayGuides = action.payload;
|
||||||
},
|
},
|
||||||
@ -232,7 +157,7 @@ export const systemSlice = createSlice({
|
|||||||
state.currentIteration = 0;
|
state.currentIteration = 0;
|
||||||
state.totalIterations = 0;
|
state.totalIterations = 0;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
state.currentStatus = i18n.t('common.statusProcessingCanceled');
|
state.statusTranslationKey = 'common.statusProcessingCanceled';
|
||||||
},
|
},
|
||||||
generationRequested: (state) => {
|
generationRequested: (state) => {
|
||||||
state.isProcessing = true;
|
state.isProcessing = true;
|
||||||
@ -242,38 +167,29 @@ export const systemSlice = createSlice({
|
|||||||
state.currentIteration = 0;
|
state.currentIteration = 0;
|
||||||
state.totalIterations = 0;
|
state.totalIterations = 0;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
state.currentStatus = i18n.t('common.statusPreparing');
|
state.statusTranslationKey = 'common.statusPreparing';
|
||||||
},
|
|
||||||
setModelList: (
|
|
||||||
state,
|
|
||||||
action: PayloadAction<InvokeAI.ModelList | Record<string, never>>
|
|
||||||
) => {
|
|
||||||
state.model_list = action.payload;
|
|
||||||
},
|
},
|
||||||
setIsCancelable: (state, action: PayloadAction<boolean>) => {
|
setIsCancelable: (state, action: PayloadAction<boolean>) => {
|
||||||
state.isCancelable = action.payload;
|
state.isCancelable = action.payload;
|
||||||
},
|
},
|
||||||
modelChangeRequested: (state) => {
|
modelChangeRequested: (state) => {
|
||||||
state.currentStatus = i18n.t('common.statusLoadingModel');
|
state.statusTranslationKey = 'common.statusLoadingModel';
|
||||||
state.isCancelable = false;
|
state.isCancelable = false;
|
||||||
state.isProcessing = true;
|
state.isProcessing = true;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
},
|
},
|
||||||
modelConvertRequested: (state) => {
|
modelConvertRequested: (state) => {
|
||||||
state.currentStatus = i18n.t('common.statusConvertingModel');
|
state.statusTranslationKey = 'common.statusConvertingModel';
|
||||||
state.isCancelable = false;
|
state.isCancelable = false;
|
||||||
state.isProcessing = true;
|
state.isProcessing = true;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
},
|
},
|
||||||
modelMergingRequested: (state) => {
|
modelMergingRequested: (state) => {
|
||||||
state.currentStatus = i18n.t('common.statusMergingModels');
|
state.statusTranslationKey = 'common.statusMergingModels';
|
||||||
state.isCancelable = false;
|
state.isCancelable = false;
|
||||||
state.isProcessing = true;
|
state.isProcessing = true;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
},
|
},
|
||||||
setSaveIntermediatesInterval: (state, action: PayloadAction<number>) => {
|
|
||||||
state.saveIntermediatesInterval = action.payload;
|
|
||||||
},
|
|
||||||
setEnableImageDebugging: (state, action: PayloadAction<boolean>) => {
|
setEnableImageDebugging: (state, action: PayloadAction<boolean>) => {
|
||||||
state.enableImageDebugging = action.payload;
|
state.enableImageDebugging = action.payload;
|
||||||
},
|
},
|
||||||
@ -283,9 +199,12 @@ export const systemSlice = createSlice({
|
|||||||
clearToastQueue: (state) => {
|
clearToastQueue: (state) => {
|
||||||
state.toastQueue = [];
|
state.toastQueue = [];
|
||||||
},
|
},
|
||||||
setProcessingIndeterminateTask: (state, action: PayloadAction<string>) => {
|
setProcessingIndeterminateTask: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<TFuncKey>
|
||||||
|
) => {
|
||||||
state.isProcessing = true;
|
state.isProcessing = true;
|
||||||
state.currentStatus = action.payload;
|
state.statusTranslationKey = action.payload;
|
||||||
state.currentStatusHasSteps = false;
|
state.currentStatusHasSteps = false;
|
||||||
},
|
},
|
||||||
setSearchFolder: (state, action: PayloadAction<string | null>) => {
|
setSearchFolder: (state, action: PayloadAction<string | null>) => {
|
||||||
@ -300,12 +219,6 @@ export const systemSlice = createSlice({
|
|||||||
setOpenModel: (state, action: PayloadAction<string | null>) => {
|
setOpenModel: (state, action: PayloadAction<string | null>) => {
|
||||||
state.openModel = action.payload;
|
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
|
* A cancel was scheduled
|
||||||
*/
|
*/
|
||||||
@ -321,7 +234,7 @@ export const systemSlice = createSlice({
|
|||||||
/**
|
/**
|
||||||
* The cancel type was changed
|
* The cancel type was changed
|
||||||
*/
|
*/
|
||||||
cancelTypeChanged: (state, action: PayloadAction<CancelType>) => {
|
cancelTypeChanged: (state, action: PayloadAction<CancelStrategy>) => {
|
||||||
state.cancelType = action.payload;
|
state.cancelType = action.payload;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -343,6 +256,7 @@ export const systemSlice = createSlice({
|
|||||||
*/
|
*/
|
||||||
builder.addCase(socketSubscribed, (state, action) => {
|
builder.addCase(socketSubscribed, (state, action) => {
|
||||||
state.sessionId = action.payload.sessionId;
|
state.sessionId = action.payload.sessionId;
|
||||||
|
state.canceledSession = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -357,9 +271,15 @@ export const systemSlice = createSlice({
|
|||||||
*/
|
*/
|
||||||
builder.addCase(socketConnected, (state, action) => {
|
builder.addCase(socketConnected, (state, action) => {
|
||||||
const { timestamp } = action.payload;
|
const { timestamp } = action.payload;
|
||||||
|
|
||||||
state.isConnected = true;
|
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;
|
const { timestamp } = action.payload;
|
||||||
|
|
||||||
state.isConnected = false;
|
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
|
* Invocation Started
|
||||||
*/
|
*/
|
||||||
builder.addCase(invocationStarted, (state) => {
|
builder.addCase(invocationStarted, (state, action) => {
|
||||||
state.isProcessing = true;
|
|
||||||
state.isCancelable = true;
|
state.isCancelable = true;
|
||||||
|
state.isProcessing = true;
|
||||||
state.currentStatusHasSteps = false;
|
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,
|
graph_execution_state_id,
|
||||||
} = action.payload.data;
|
} = action.payload.data;
|
||||||
|
|
||||||
|
state.isProcessing = true;
|
||||||
|
state.isCancelable = true;
|
||||||
|
// state.currentIteration = 0;
|
||||||
|
// state.totalIterations = 0;
|
||||||
state.currentStatusHasSteps = true;
|
state.currentStatusHasSteps = true;
|
||||||
state.currentStep = step + 1; // TODO: step starts at -1, think this is a bug
|
state.currentStep = step + 1; // TODO: step starts at -1, think this is a bug
|
||||||
state.totalSteps = total_steps;
|
state.totalSteps = total_steps;
|
||||||
state.progressImage = progress_image ?? null;
|
state.progressImage = progress_image ?? null;
|
||||||
|
state.statusTranslationKey = 'common.statusGenerating';
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,11 +343,17 @@ export const systemSlice = createSlice({
|
|||||||
builder.addCase(invocationComplete, (state, action) => {
|
builder.addCase(invocationComplete, (state, action) => {
|
||||||
const { data, timestamp } = action.payload;
|
const { data, timestamp } = action.payload;
|
||||||
|
|
||||||
state.isProcessing = false;
|
// state.currentIteration = 0;
|
||||||
|
// state.totalIterations = 0;
|
||||||
|
state.currentStatusHasSteps = false;
|
||||||
state.currentStep = 0;
|
state.currentStep = 0;
|
||||||
state.totalSteps = 0;
|
state.totalSteps = 0;
|
||||||
state.progressImage = null;
|
state.statusTranslationKey = 'common.statusProcessingComplete';
|
||||||
state.currentStatus = i18n.t('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) => {
|
builder.addCase(invocationError, (state, action) => {
|
||||||
const { data, timestamp } = action.payload;
|
const { data, timestamp } = action.payload;
|
||||||
|
|
||||||
state.wasErrorSeen = true;
|
|
||||||
state.progressImage = null;
|
|
||||||
state.isProcessing = false;
|
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(
|
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) => {
|
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) => {
|
builder.addCase(sessionCanceled.fulfilled, (state, action) => {
|
||||||
const { timestamp } = action.payload;
|
const { timestamp } = action.payload;
|
||||||
|
|
||||||
|
state.canceledSession = action.meta.arg.sessionId;
|
||||||
state.isProcessing = false;
|
state.isProcessing = false;
|
||||||
state.isCancelable = false;
|
state.isCancelable = false;
|
||||||
state.isCancelScheduled = false;
|
state.isCancelScheduled = false;
|
||||||
state.currentStep = 0;
|
state.currentStep = 0;
|
||||||
state.totalSteps = 0;
|
state.totalSteps = 0;
|
||||||
state.progressImage = null;
|
state.statusTranslationKey = 'common.statusConnected';
|
||||||
|
|
||||||
state.toastQueue.push(
|
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
|
* Initial Image Selected
|
||||||
*/
|
*/
|
||||||
builder.addCase(initialImageSelected, (state) => {
|
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 {
|
export const {
|
||||||
setShouldDisplayInProgressType,
|
|
||||||
setIsProcessing,
|
setIsProcessing,
|
||||||
setShouldShowLogViewer,
|
|
||||||
setIsConnected,
|
setIsConnected,
|
||||||
setSocketId,
|
|
||||||
setShouldConfirmOnDelete,
|
setShouldConfirmOnDelete,
|
||||||
setOpenAccordions,
|
|
||||||
setSystemStatus,
|
|
||||||
setCurrentStatus,
|
setCurrentStatus,
|
||||||
setSystemConfig,
|
|
||||||
setShouldDisplayGuides,
|
setShouldDisplayGuides,
|
||||||
processingCanceled,
|
processingCanceled,
|
||||||
errorOccurred,
|
errorOccurred,
|
||||||
errorSeen,
|
|
||||||
setModelList,
|
|
||||||
setIsCancelable,
|
setIsCancelable,
|
||||||
modelChangeRequested,
|
modelChangeRequested,
|
||||||
modelConvertRequested,
|
modelConvertRequested,
|
||||||
modelMergingRequested,
|
modelMergingRequested,
|
||||||
setSaveIntermediatesInterval,
|
|
||||||
setEnableImageDebugging,
|
setEnableImageDebugging,
|
||||||
generationRequested,
|
generationRequested,
|
||||||
addToast,
|
addToast,
|
||||||
@ -507,8 +460,6 @@ export const {
|
|||||||
setSearchFolder,
|
setSearchFolder,
|
||||||
setFoundModels,
|
setFoundModels,
|
||||||
setOpenModel,
|
setOpenModel,
|
||||||
setCancelType,
|
|
||||||
setCancelAfter,
|
|
||||||
cancelScheduled,
|
cancelScheduled,
|
||||||
scheduledCancelAborted,
|
scheduledCancelAborted,
|
||||||
cancelTypeChanged,
|
cancelTypeChanged,
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import CurrentImageDisplay from 'features/gallery/components/CurrentImageDisplay';
|
import CurrentImageDisplay from 'features/gallery/components/CurrentImageDisplay';
|
||||||
|
import ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
|
||||||
|
|
||||||
const GenerateContent = () => {
|
const GenerateContent = () => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
position: 'relative',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
borderRadius: 'base',
|
borderRadius: 'base',
|
||||||
|
@ -3,7 +3,7 @@ import { UIState } from './uiTypes';
|
|||||||
/**
|
/**
|
||||||
* UI slice persist denylist
|
* UI slice persist denylist
|
||||||
*/
|
*/
|
||||||
const itemsToDenylist: (keyof UIState)[] = [];
|
const itemsToDenylist: (keyof UIState)[] = ['floatingProgressImageRect'];
|
||||||
|
|
||||||
export const uiDenylist = itemsToDenylist.map(
|
export const uiDenylist = itemsToDenylist.map(
|
||||||
(denylistItem) => `ui.${denylistItem}`
|
(denylistItem) => `ui.${denylistItem}`
|
||||||
|
@ -2,7 +2,7 @@ import type { PayloadAction } from '@reduxjs/toolkit';
|
|||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import { setActiveTabReducer } from './extraReducers';
|
import { setActiveTabReducer } from './extraReducers';
|
||||||
import { InvokeTabName, tabMap } from './tabMap';
|
import { InvokeTabName, tabMap } from './tabMap';
|
||||||
import { AddNewModelType, UIState } from './uiTypes';
|
import { AddNewModelType, Coordinates, Rect, UIState } from './uiTypes';
|
||||||
|
|
||||||
const initialUIState: UIState = {
|
const initialUIState: UIState = {
|
||||||
activeTab: 0,
|
activeTab: 0,
|
||||||
@ -21,6 +21,9 @@ const initialUIState: UIState = {
|
|||||||
openLinearAccordionItems: [],
|
openLinearAccordionItems: [],
|
||||||
openGenerateAccordionItems: [],
|
openGenerateAccordionItems: [],
|
||||||
openUnifiedCanvasAccordionItems: [],
|
openUnifiedCanvasAccordionItems: [],
|
||||||
|
floatingProgressImageRect: { x: 0, y: 0, width: 0, height: 0 },
|
||||||
|
shouldShowProgressImages: false,
|
||||||
|
shouldAutoShowProgressImages: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: UIState = initialUIState;
|
const initialState: UIState = initialUIState;
|
||||||
@ -105,6 +108,30 @@ export const uiSlice = createSlice({
|
|||||||
state.openUnifiedCanvasAccordionItems = action.payload;
|
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,
|
toggleParametersPanel,
|
||||||
toggleGalleryPanel,
|
toggleGalleryPanel,
|
||||||
openAccordionItemsChanged,
|
openAccordionItemsChanged,
|
||||||
|
floatingProgressImageMoved,
|
||||||
|
floatingProgressImageResized,
|
||||||
|
setShouldShowProgressImages,
|
||||||
|
setShouldAutoShowProgressImages,
|
||||||
} = uiSlice.actions;
|
} = uiSlice.actions;
|
||||||
|
|
||||||
export default uiSlice.reducer;
|
export default uiSlice.reducer;
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
import { InvokeTabName } from './tabMap';
|
|
||||||
|
|
||||||
export type AddNewModelType = 'ckpt' | 'diffusers' | null;
|
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 {
|
export interface UIState {
|
||||||
activeTab: number;
|
activeTab: number;
|
||||||
currentTheme: string;
|
currentTheme: string;
|
||||||
@ -19,4 +29,7 @@ export interface UIState {
|
|||||||
openLinearAccordionItems: number[];
|
openLinearAccordionItems: number[];
|
||||||
openGenerateAccordionItems: number[];
|
openGenerateAccordionItems: number[];
|
||||||
openUnifiedCanvasAccordionItems: number[];
|
openUnifiedCanvasAccordionItems: number[];
|
||||||
|
floatingProgressImageRect: Rect;
|
||||||
|
shouldShowProgressImages: boolean;
|
||||||
|
shouldAutoShowProgressImages: boolean;
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,13 @@ export interface OnCancel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CancelablePromise<T> implements Promise<T> {
|
export class CancelablePromise<T> implements Promise<T> {
|
||||||
readonly [Symbol.toStringTag]!: string;
|
#isResolved: boolean;
|
||||||
|
#isRejected: boolean;
|
||||||
private _isResolved: boolean;
|
#isCancelled: boolean;
|
||||||
private _isRejected: boolean;
|
readonly #cancelHandlers: (() => void)[];
|
||||||
private _isCancelled: boolean;
|
readonly #promise: Promise<T>;
|
||||||
private readonly _cancelHandlers: (() => void)[];
|
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||||
private readonly _promise: Promise<T>;
|
#reject?: (reason?: any) => void;
|
||||||
private _resolve?: (value: T | PromiseLike<T>) => void;
|
|
||||||
private _reject?: (reason?: any) => void;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
executor: (
|
executor: (
|
||||||
@ -39,78 +37,82 @@ export class CancelablePromise<T> implements Promise<T> {
|
|||||||
onCancel: OnCancel
|
onCancel: OnCancel
|
||||||
) => void
|
) => void
|
||||||
) {
|
) {
|
||||||
this._isResolved = false;
|
this.#isResolved = false;
|
||||||
this._isRejected = false;
|
this.#isRejected = false;
|
||||||
this._isCancelled = false;
|
this.#isCancelled = false;
|
||||||
this._cancelHandlers = [];
|
this.#cancelHandlers = [];
|
||||||
this._promise = new Promise<T>((resolve, reject) => {
|
this.#promise = new Promise<T>((resolve, reject) => {
|
||||||
this._resolve = resolve;
|
this.#resolve = resolve;
|
||||||
this._reject = reject;
|
this.#reject = reject;
|
||||||
|
|
||||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._isResolved = true;
|
this.#isResolved = true;
|
||||||
this._resolve?.(value);
|
this.#resolve?.(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onReject = (reason?: any): void => {
|
const onReject = (reason?: any): void => {
|
||||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._isRejected = true;
|
this.#isRejected = true;
|
||||||
this._reject?.(reason);
|
this.#reject?.(reason);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCancel = (cancelHandler: () => void): void => {
|
const onCancel = (cancelHandler: () => void): void => {
|
||||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._cancelHandlers.push(cancelHandler);
|
this.#cancelHandlers.push(cancelHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperty(onCancel, 'isResolved', {
|
Object.defineProperty(onCancel, 'isResolved', {
|
||||||
get: (): boolean => this._isResolved,
|
get: (): boolean => this.#isResolved,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(onCancel, 'isRejected', {
|
Object.defineProperty(onCancel, 'isRejected', {
|
||||||
get: (): boolean => this._isRejected,
|
get: (): boolean => this.#isRejected,
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(onCancel, 'isCancelled', {
|
Object.defineProperty(onCancel, 'isCancelled', {
|
||||||
get: (): boolean => this._isCancelled,
|
get: (): boolean => this.#isCancelled,
|
||||||
});
|
});
|
||||||
|
|
||||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "Cancellable Promise";
|
||||||
|
}
|
||||||
|
|
||||||
public then<TResult1 = T, TResult2 = never>(
|
public then<TResult1 = T, TResult2 = never>(
|
||||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||||
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
||||||
): Promise<TResult1 | TResult2> {
|
): Promise<TResult1 | TResult2> {
|
||||||
return this._promise.then(onFulfilled, onRejected);
|
return this.#promise.then(onFulfilled, onRejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public catch<TResult = never>(
|
public catch<TResult = never>(
|
||||||
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
||||||
): Promise<T | TResult> {
|
): Promise<T | TResult> {
|
||||||
return this._promise.catch(onRejected);
|
return this.#promise.catch(onRejected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||||
return this._promise.finally(onFinally);
|
return this.#promise.finally(onFinally);
|
||||||
}
|
}
|
||||||
|
|
||||||
public cancel(): void {
|
public cancel(): void {
|
||||||
if (this._isResolved || this._isRejected || this._isCancelled) {
|
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._isCancelled = true;
|
this.#isCancelled = true;
|
||||||
if (this._cancelHandlers.length) {
|
if (this.#cancelHandlers.length) {
|
||||||
try {
|
try {
|
||||||
for (const cancelHandler of this._cancelHandlers) {
|
for (const cancelHandler of this.#cancelHandlers) {
|
||||||
cancelHandler();
|
cancelHandler();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -118,11 +120,11 @@ export class CancelablePromise<T> implements Promise<T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._cancelHandlers.length = 0;
|
this.#cancelHandlers.length = 0;
|
||||||
this._reject?.(new CancelError('Request aborted'));
|
this.#reject?.(new CancelError('Request aborted'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isCancelled(): boolean {
|
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 { PromptOutput } from './models/PromptOutput';
|
||||||
export type { RandomRangeInvocation } from './models/RandomRangeInvocation';
|
export type { RandomRangeInvocation } from './models/RandomRangeInvocation';
|
||||||
export type { RangeInvocation } from './models/RangeInvocation';
|
export type { RangeInvocation } from './models/RangeInvocation';
|
||||||
|
export type { ResizeLatentsInvocation } from './models/ResizeLatentsInvocation';
|
||||||
export type { RestoreFaceInvocation } from './models/RestoreFaceInvocation';
|
export type { RestoreFaceInvocation } from './models/RestoreFaceInvocation';
|
||||||
|
export type { ScaleLatentsInvocation } from './models/ScaleLatentsInvocation';
|
||||||
export type { ShowImageInvocation } from './models/ShowImageInvocation';
|
export type { ShowImageInvocation } from './models/ShowImageInvocation';
|
||||||
export type { SubtractInvocation } from './models/SubtractInvocation';
|
export type { SubtractInvocation } from './models/SubtractInvocation';
|
||||||
export type { TextToImageInvocation } from './models/TextToImageInvocation';
|
export type { TextToImageInvocation } from './models/TextToImageInvocation';
|
||||||
@ -119,7 +121,9 @@ export { $PasteImageInvocation } from './schemas/$PasteImageInvocation';
|
|||||||
export { $PromptOutput } from './schemas/$PromptOutput';
|
export { $PromptOutput } from './schemas/$PromptOutput';
|
||||||
export { $RandomRangeInvocation } from './schemas/$RandomRangeInvocation';
|
export { $RandomRangeInvocation } from './schemas/$RandomRangeInvocation';
|
||||||
export { $RangeInvocation } from './schemas/$RangeInvocation';
|
export { $RangeInvocation } from './schemas/$RangeInvocation';
|
||||||
|
export { $ResizeLatentsInvocation } from './schemas/$ResizeLatentsInvocation';
|
||||||
export { $RestoreFaceInvocation } from './schemas/$RestoreFaceInvocation';
|
export { $RestoreFaceInvocation } from './schemas/$RestoreFaceInvocation';
|
||||||
|
export { $ScaleLatentsInvocation } from './schemas/$ScaleLatentsInvocation';
|
||||||
export { $ShowImageInvocation } from './schemas/$ShowImageInvocation';
|
export { $ShowImageInvocation } from './schemas/$ShowImageInvocation';
|
||||||
export { $SubtractInvocation } from './schemas/$SubtractInvocation';
|
export { $SubtractInvocation } from './schemas/$SubtractInvocation';
|
||||||
export { $TextToImageInvocation } from './schemas/$TextToImageInvocation';
|
export { $TextToImageInvocation } from './schemas/$TextToImageInvocation';
|
||||||
|
@ -25,7 +25,9 @@ import type { ParamIntInvocation } from './ParamIntInvocation';
|
|||||||
import type { PasteImageInvocation } from './PasteImageInvocation';
|
import type { PasteImageInvocation } from './PasteImageInvocation';
|
||||||
import type { RandomRangeInvocation } from './RandomRangeInvocation';
|
import type { RandomRangeInvocation } from './RandomRangeInvocation';
|
||||||
import type { RangeInvocation } from './RangeInvocation';
|
import type { RangeInvocation } from './RangeInvocation';
|
||||||
|
import type { ResizeLatentsInvocation } from './ResizeLatentsInvocation';
|
||||||
import type { RestoreFaceInvocation } from './RestoreFaceInvocation';
|
import type { RestoreFaceInvocation } from './RestoreFaceInvocation';
|
||||||
|
import type { ScaleLatentsInvocation } from './ScaleLatentsInvocation';
|
||||||
import type { ShowImageInvocation } from './ShowImageInvocation';
|
import type { ShowImageInvocation } from './ShowImageInvocation';
|
||||||
import type { SubtractInvocation } from './SubtractInvocation';
|
import type { SubtractInvocation } from './SubtractInvocation';
|
||||||
import type { TextToImageInvocation } from './TextToImageInvocation';
|
import type { TextToImageInvocation } from './TextToImageInvocation';
|
||||||
@ -40,7 +42,7 @@ export type Graph = {
|
|||||||
/**
|
/**
|
||||||
* The nodes in this 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
|
* The connections between nodes and their fields in this graph
|
||||||
*/
|
*/
|
||||||
|
@ -17,10 +17,6 @@ export type LatentsToLatentsInvocation = {
|
|||||||
* The prompt to generate an image from
|
* The prompt to generate an image from
|
||||||
*/
|
*/
|
||||||
prompt?: string;
|
prompt?: string;
|
||||||
/**
|
|
||||||
* The seed to use (-1 for a random seed)
|
|
||||||
*/
|
|
||||||
seed?: number;
|
|
||||||
/**
|
/**
|
||||||
* The noise to use
|
* The noise to use
|
||||||
*/
|
*/
|
||||||
@ -29,14 +25,6 @@ export type LatentsToLatentsInvocation = {
|
|||||||
* The number of steps to use to generate the image
|
* The number of steps to use to generate the image
|
||||||
*/
|
*/
|
||||||
steps?: number;
|
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
|
* 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
|
* The prompt to generate an image from
|
||||||
*/
|
*/
|
||||||
prompt?: string;
|
prompt?: string;
|
||||||
/**
|
|
||||||
* The seed to use (-1 for a random seed)
|
|
||||||
*/
|
|
||||||
seed?: number;
|
|
||||||
/**
|
/**
|
||||||
* The noise to use
|
* The noise to use
|
||||||
*/
|
*/
|
||||||
@ -29,14 +25,6 @@ export type TextToLatentsInvocation = {
|
|||||||
* The number of steps to use to generate the image
|
* The number of steps to use to generate the image
|
||||||
*/
|
*/
|
||||||
steps?: number;
|
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
|
* 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: 'TextToLatentsInvocation',
|
||||||
}, {
|
}, {
|
||||||
type: 'LatentsToImageInvocation',
|
type: 'LatentsToImageInvocation',
|
||||||
|
}, {
|
||||||
|
type: 'ResizeLatentsInvocation',
|
||||||
|
}, {
|
||||||
|
type: 'ScaleLatentsInvocation',
|
||||||
}, {
|
}, {
|
||||||
type: 'AddInvocation',
|
type: 'AddInvocation',
|
||||||
}, {
|
}, {
|
||||||
|
@ -16,12 +16,6 @@ export const $LatentsToLatentsInvocation = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: `The prompt to generate an image from`,
|
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: {
|
noise: {
|
||||||
type: 'all-of',
|
type: 'all-of',
|
||||||
description: `The noise to use`,
|
description: `The noise to use`,
|
||||||
@ -33,16 +27,6 @@ export const $LatentsToLatentsInvocation = {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
description: `The number of steps to use to generate the image`,
|
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: {
|
cfg_scale: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: `The Classifier-Free Guidance, higher values may result in a result closer to the prompt`,
|
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',
|
type: 'string',
|
||||||
description: `The prompt to generate an image from`,
|
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: {
|
noise: {
|
||||||
type: 'all-of',
|
type: 'all-of',
|
||||||
description: `The noise to use`,
|
description: `The noise to use`,
|
||||||
@ -33,16 +27,6 @@ export const $TextToLatentsInvocation = {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
description: `The number of steps to use to generate the image`,
|
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: {
|
cfg_scale: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
description: `The Classifier-Free Guidance, higher values may result in a result closer to the prompt`,
|
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 { PasteImageInvocation } from '../models/PasteImageInvocation';
|
||||||
import type { RandomRangeInvocation } from '../models/RandomRangeInvocation';
|
import type { RandomRangeInvocation } from '../models/RandomRangeInvocation';
|
||||||
import type { RangeInvocation } from '../models/RangeInvocation';
|
import type { RangeInvocation } from '../models/RangeInvocation';
|
||||||
|
import type { ResizeLatentsInvocation } from '../models/ResizeLatentsInvocation';
|
||||||
import type { RestoreFaceInvocation } from '../models/RestoreFaceInvocation';
|
import type { RestoreFaceInvocation } from '../models/RestoreFaceInvocation';
|
||||||
|
import type { ScaleLatentsInvocation } from '../models/ScaleLatentsInvocation';
|
||||||
import type { ShowImageInvocation } from '../models/ShowImageInvocation';
|
import type { ShowImageInvocation } from '../models/ShowImageInvocation';
|
||||||
import type { SubtractInvocation } from '../models/SubtractInvocation';
|
import type { SubtractInvocation } from '../models/SubtractInvocation';
|
||||||
import type { TextToImageInvocation } from '../models/TextToImageInvocation';
|
import type { TextToImageInvocation } from '../models/TextToImageInvocation';
|
||||||
@ -142,7 +144,7 @@ export class SessionsService {
|
|||||||
* The id of the session
|
* The id of the session
|
||||||
*/
|
*/
|
||||||
sessionId: string,
|
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> {
|
}): CancelablePromise<string> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -179,7 +181,7 @@ export class SessionsService {
|
|||||||
* The path to the node in the graph
|
* The path to the node in the graph
|
||||||
*/
|
*/
|
||||||
nodePath: string,
|
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> {
|
}): CancelablePromise<GraphExecutionState> {
|
||||||
return __request(OpenAPI, {
|
return __request(OpenAPI, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
|
@ -7,15 +7,9 @@ import {
|
|||||||
} from 'services/events/types';
|
} from 'services/events/types';
|
||||||
import {
|
import {
|
||||||
invocationComplete,
|
invocationComplete,
|
||||||
socketConnected,
|
|
||||||
socketDisconnected,
|
|
||||||
socketSubscribed,
|
socketSubscribed,
|
||||||
socketUnsubscribed,
|
socketUnsubscribed,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
|
||||||
receivedResultImagesPage,
|
|
||||||
receivedUploadImagesPage,
|
|
||||||
} from 'services/thunks/gallery';
|
|
||||||
import { AppDispatch, RootState } from 'app/store/store';
|
import { AppDispatch, RootState } from 'app/store/store';
|
||||||
import { getTimestamp } from 'common/util/getTimestamp';
|
import { getTimestamp } from 'common/util/getTimestamp';
|
||||||
import {
|
import {
|
||||||
@ -23,14 +17,12 @@ import {
|
|||||||
isFulfilledSessionCreatedAction,
|
isFulfilledSessionCreatedAction,
|
||||||
} from 'services/thunks/session';
|
} from 'services/thunks/session';
|
||||||
import { OpenAPI } from 'services/api';
|
import { OpenAPI } from 'services/api';
|
||||||
import { receivedModels } from 'services/thunks/model';
|
|
||||||
import { receivedOpenAPISchema } from 'services/thunks/schema';
|
|
||||||
import { isImageOutput } from 'services/types/guards';
|
import { isImageOutput } from 'services/types/guards';
|
||||||
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
import { imageReceived, thumbnailReceived } from 'services/thunks/image';
|
||||||
import { setEventListeners } from 'services/events/util/setEventListeners';
|
import { setEventListeners } from 'services/events/util/setEventListeners';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
|
|
||||||
const moduleLog = log.child({ namespace: 'socketio' });
|
const socketioLog = log.child({ namespace: 'socketio' });
|
||||||
|
|
||||||
export const socketMiddleware = () => {
|
export const socketMiddleware = () => {
|
||||||
let areListenersSet = false;
|
let areListenersSet = false;
|
||||||
@ -65,106 +57,27 @@ export const socketMiddleware = () => {
|
|||||||
(store: MiddlewareAPI<AppDispatch, RootState>) => (next) => (action) => {
|
(store: MiddlewareAPI<AppDispatch, RootState>) => (next) => (action) => {
|
||||||
const { dispatch, getState } = store;
|
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
|
// Set listeners for `connect` and `disconnect` events once
|
||||||
// Must happen in middleware to get access to `dispatch`
|
// Must happen in middleware to get access to `dispatch`
|
||||||
if (!areListenersSet) {
|
if (!areListenersSet) {
|
||||||
socket.on('connect', () => {
|
setEventListeners({ store, socket, log: socketioLog });
|
||||||
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() }));
|
|
||||||
});
|
|
||||||
|
|
||||||
areListenersSet = true;
|
areListenersSet = true;
|
||||||
|
|
||||||
// must manually connect
|
|
||||||
socket.connect();
|
socket.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything else only happens once we have created a session
|
|
||||||
if (isFulfilledSessionCreatedAction(action)) {
|
if (isFulfilledSessionCreatedAction(action)) {
|
||||||
const sessionId = action.payload.id;
|
const sessionId = action.payload.id;
|
||||||
const sessionLog = moduleLog.child({ sessionId });
|
const sessionLog = socketioLog.child({ sessionId });
|
||||||
const oldSessionId = getState().system.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) {
|
if (oldSessionId) {
|
||||||
sessionLog.debug(
|
sessionLog.debug(
|
||||||
{ oldSessionId },
|
{ oldSessionId },
|
||||||
`Unsubscribed from old session (${oldSessionId})`
|
`Unsubscribed from old session (${oldSessionId})`
|
||||||
);
|
);
|
||||||
// Unsubscribe when invocations complete
|
|
||||||
socket.emit('unsubscribe', {
|
socket.emit('unsubscribe', {
|
||||||
session: oldSessionId,
|
session: oldSessionId,
|
||||||
});
|
});
|
||||||
@ -175,28 +88,18 @@ export const socketMiddleware = () => {
|
|||||||
timestamp: getTimestamp(),
|
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})`);
|
sessionLog.debug(`Subscribe to new session (${sessionId})`);
|
||||||
|
|
||||||
socket.emit('subscribe', { session: sessionId });
|
socket.emit('subscribe', { session: sessionId });
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
socketSubscribed({
|
socketSubscribed({
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
timestamp: getTimestamp(),
|
timestamp: getTimestamp(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
setEventListeners({ socket, store, sessionLog });
|
|
||||||
|
|
||||||
// Finally we actually invoke the session, starting processing
|
// Finally we actually invoke the session, starting processing
|
||||||
dispatch(sessionInvoked({ sessionId }));
|
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);
|
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
|
* 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 AnyResult = GraphExecutionState['results'][string];
|
||||||
|
|
||||||
|
export type BaseNode = {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
[key: string]: NonNullable<InvokeAIMetadata['node']>[string];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `generator_progress` socket.io event.
|
* A `generator_progress` socket.io event.
|
||||||
*
|
*
|
||||||
@ -24,7 +30,7 @@ export type AnyResult = GraphExecutionState['results'][string];
|
|||||||
*/
|
*/
|
||||||
export type GeneratorProgressEvent = {
|
export type GeneratorProgressEvent = {
|
||||||
graph_execution_state_id: string;
|
graph_execution_state_id: string;
|
||||||
node: AnyInvocation;
|
node: BaseNode;
|
||||||
source_node_id: string;
|
source_node_id: string;
|
||||||
progress_image?: ProgressImage;
|
progress_image?: ProgressImage;
|
||||||
step: number;
|
step: number;
|
||||||
@ -40,7 +46,7 @@ export type GeneratorProgressEvent = {
|
|||||||
*/
|
*/
|
||||||
export type InvocationCompleteEvent = {
|
export type InvocationCompleteEvent = {
|
||||||
graph_execution_state_id: string;
|
graph_execution_state_id: string;
|
||||||
node: AnyInvocation;
|
node: BaseNode;
|
||||||
source_node_id: string;
|
source_node_id: string;
|
||||||
result: AnyResult;
|
result: AnyResult;
|
||||||
};
|
};
|
||||||
@ -52,7 +58,7 @@ export type InvocationCompleteEvent = {
|
|||||||
*/
|
*/
|
||||||
export type InvocationErrorEvent = {
|
export type InvocationErrorEvent = {
|
||||||
graph_execution_state_id: string;
|
graph_execution_state_id: string;
|
||||||
node: AnyInvocation;
|
node: BaseNode;
|
||||||
source_node_id: string;
|
source_node_id: string;
|
||||||
error: string;
|
error: string;
|
||||||
};
|
};
|
||||||
@ -64,7 +70,7 @@ export type InvocationErrorEvent = {
|
|||||||
*/
|
*/
|
||||||
export type InvocationStartedEvent = {
|
export type InvocationStartedEvent = {
|
||||||
graph_execution_state_id: string;
|
graph_execution_state_id: string;
|
||||||
node: AnyInvocation;
|
node: BaseNode;
|
||||||
source_node_id: string;
|
source_node_id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,38 +9,140 @@ import {
|
|||||||
invocationComplete,
|
invocationComplete,
|
||||||
invocationError,
|
invocationError,
|
||||||
invocationStarted,
|
invocationStarted,
|
||||||
|
socketConnected,
|
||||||
|
socketDisconnected,
|
||||||
|
socketSubscribed,
|
||||||
} from '../actions';
|
} from '../actions';
|
||||||
import { ClientToServerEvents, ServerToClientEvents } from '../types';
|
import { ClientToServerEvents, ServerToClientEvents } from '../types';
|
||||||
import { Logger } from 'roarr';
|
import { Logger } from 'roarr';
|
||||||
import { JsonObject } from 'roarr/dist/types';
|
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 = {
|
type SetEventListenersArg = {
|
||||||
socket: Socket<ServerToClientEvents, ClientToServerEvents>;
|
socket: Socket<ServerToClientEvents, ClientToServerEvents>;
|
||||||
store: MiddlewareAPI<AppDispatch, RootState>;
|
store: MiddlewareAPI<AppDispatch, RootState>;
|
||||||
sessionLog: Logger<JsonObject>;
|
log: Logger<JsonObject>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setEventListeners = (arg: SetEventListenersArg) => {
|
export const setEventListeners = (arg: SetEventListenersArg) => {
|
||||||
const { socket, store, sessionLog } = arg;
|
const { socket, store, log } = arg;
|
||||||
const { dispatch, getState } = store;
|
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) => {
|
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() }));
|
dispatch(invocationStarted({ data, timestamp: getTimestamp() }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generator progress
|
||||||
|
*/
|
||||||
socket.on('generator_progress', (data) => {
|
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() }));
|
dispatch(generatorProgress({ data, timestamp: getTimestamp() }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invocation error
|
||||||
|
*/
|
||||||
socket.on('invocation_error', (data) => {
|
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() }));
|
dispatch(invocationError({ data, timestamp: getTimestamp() }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invocation complete
|
||||||
|
*/
|
||||||
socket.on('invocation_complete', (data) => {
|
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 sessionId = data.graph_execution_state_id;
|
||||||
|
|
||||||
const { cancelType, isCancelScheduled } = getState().system;
|
const { cancelType, isCancelScheduled } = getState().system;
|
||||||
@ -60,10 +162,12 @@ export const setEventListeners = (arg: SetEventListenersArg) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Graph complete
|
||||||
|
*/
|
||||||
socket.on('graph_execution_state_complete', (data) => {
|
socket.on('graph_execution_state_complete', (data) => {
|
||||||
sessionLog
|
log.info(
|
||||||
.child({ data })
|
{ data, sessionId: data.graph_execution_state_id },
|
||||||
.info(
|
|
||||||
`Graph execution state complete (${data.graph_execution_state_id})`
|
`Graph execution state complete (${data.graph_execution_state_id})`
|
||||||
);
|
);
|
||||||
dispatch(graphExecutionStateComplete({ data, timestamp: getTimestamp() }));
|
dispatch(graphExecutionStateComplete({ data, timestamp: getTimestamp() }));
|
||||||
|
@ -2,7 +2,7 @@ import { isFulfilled, isRejected } from '@reduxjs/toolkit';
|
|||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import { createAppAsyncThunk } from 'app/store/storeUtils';
|
import { createAppAsyncThunk } from 'app/store/storeUtils';
|
||||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
import { clamp } from 'lodash-es';
|
import { clamp, isString } from 'lodash-es';
|
||||||
import { ImagesService } from 'services/api';
|
import { ImagesService } from 'services/api';
|
||||||
import { getHeaders } from 'services/util/getHeaders';
|
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.
|
// 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
|
// Unfortunately, we have to do this here, because the resultsSlice and uploadsSlice cannot change
|
||||||
// the selected image.
|
// the selected image.
|
||||||
const selectedImageName = getState().gallery.selectedImageName;
|
const selectedImageName = getState().gallery.selectedImage?.name;
|
||||||
|
|
||||||
if (selectedImageName === imageName) {
|
if (selectedImageName === imageName) {
|
||||||
const allIds = getState()[imageType].ids;
|
const allIds = getState()[imageType].ids;
|
||||||
@ -104,9 +104,13 @@ export const imageDeleted = createAppAsyncThunk(
|
|||||||
|
|
||||||
const newSelectedImageId = filteredIds[newSelectedImageIndex];
|
const newSelectedImageId = filteredIds[newSelectedImageIndex];
|
||||||
|
|
||||||
|
if (newSelectedImageId) {
|
||||||
dispatch(
|
dispatch(
|
||||||
imageSelected(newSelectedImageId ? newSelectedImageId.toString() : '')
|
imageSelected({ name: newSelectedImageId as string, type: imageType })
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
dispatch(imageSelected());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await ImagesService.deleteImage(arg);
|
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 { isAnyOf, isFulfilled } from '@reduxjs/toolkit';
|
||||||
import { buildNodesGraph } from 'features/nodes/util/nodesGraphBuilder/buildNodesGraph';
|
import { buildNodesGraph } from 'features/nodes/util/nodesGraphBuilder/buildNodesGraph';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
|
import { serializeError } from 'serialize-error';
|
||||||
|
|
||||||
const sessionLog = log.child({ namespace: 'session' });
|
const sessionLog = log.child({ namespace: 'session' });
|
||||||
|
|
||||||
export const generateGraphBuilt = createAppAsyncThunk(
|
export const generateGraphBuilt = createAppAsyncThunk(
|
||||||
'api/generateGraphBuilt',
|
'api/generateGraphBuilt',
|
||||||
async (_, { dispatch, getState }) => {
|
async (_, { dispatch, getState, rejectWithValue }) => {
|
||||||
|
try {
|
||||||
const graph = buildGenerateGraph(getState());
|
const graph = buildGenerateGraph(getState());
|
||||||
|
|
||||||
dispatch(sessionCreated({ graph }));
|
dispatch(sessionCreated({ graph }));
|
||||||
|
|
||||||
return graph;
|
return graph;
|
||||||
|
} catch (err: any) {
|
||||||
|
sessionLog.error(
|
||||||
|
{ error: serializeError(err) },
|
||||||
|
'Problem building graph'
|
||||||
|
);
|
||||||
|
return rejectWithValue(err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const nodesGraphBuilt = createAppAsyncThunk(
|
export const nodesGraphBuilt = createAppAsyncThunk(
|
||||||
'api/nodesGraphBuilt',
|
'api/nodesGraphBuilt',
|
||||||
async (_, { dispatch, getState }) => {
|
async (_, { dispatch, getState, rejectWithValue }) => {
|
||||||
|
try {
|
||||||
const graph = buildNodesGraph(getState());
|
const graph = buildNodesGraph(getState());
|
||||||
|
|
||||||
dispatch(sessionCreated({ graph }));
|
dispatch(sessionCreated({ graph }));
|
||||||
|
|
||||||
return 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 {
|
import {
|
||||||
GraphExecutionState,
|
GraphExecutionState,
|
||||||
GraphInvocationOutput,
|
GraphInvocationOutput,
|
||||||
@ -6,6 +8,8 @@ import {
|
|||||||
PromptOutput,
|
PromptOutput,
|
||||||
IterateInvocationOutput,
|
IterateInvocationOutput,
|
||||||
CollectInvocationOutput,
|
CollectInvocationOutput,
|
||||||
|
ImageType,
|
||||||
|
ImageField,
|
||||||
} from 'services/api';
|
} from 'services/api';
|
||||||
|
|
||||||
export const isImageOutput = (
|
export const isImageOutput = (
|
||||||
@ -31,3 +35,16 @@ export const isIterateOutput = (
|
|||||||
export const isCollectOutput = (
|
export const isCollectOutput = (
|
||||||
output: GraphExecutionState['results'][string]
|
output: GraphExecutionState['results'][string]
|
||||||
): output is CollectInvocationOutput => output.type === 'collect_output';
|
): 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:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.11"
|
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":
|
"@babel/types@^7.21.4", "@babel/types@^7.4":
|
||||||
version "7.21.4"
|
version "7.21.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4"
|
||||||
@ -1837,6 +1844,11 @@
|
|||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
hoist-non-react-statics "^3.3.0"
|
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":
|
"@types/json-schema@*", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.9":
|
||||||
version "7.0.11"
|
version "7.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||||
@ -2048,6 +2060,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@swc/core" "^1.3.42"
|
"@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":
|
"@yarnpkg/lockfile@^1.1.0":
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
|
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"
|
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||||
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
|
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:
|
code-block-writer@^12.0.0:
|
||||||
version "12.0.0"
|
version "12.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770"
|
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"
|
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
|
||||||
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
|
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"
|
version "3.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
|
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
|
||||||
integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
|
integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
|
||||||
@ -2736,7 +2758,22 @@ css-box-model@1.2.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tiny-invariant "^1.0.6"
|
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"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
||||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
||||||
@ -3142,6 +3179,13 @@ error-ex@^1.3.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish "^0.2.1"
|
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:
|
es-abstract@^1.19.0, es-abstract@^1.20.4:
|
||||||
version "1.21.2"
|
version "1.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff"
|
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"
|
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||||
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
|
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:
|
fast-printf@^1.6.9:
|
||||||
version "1.6.9"
|
version "1.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.9.tgz#212f56570d2dc8ccdd057ee93d50dd414d07d676"
|
resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.9.tgz#212f56570d2dc8ccdd057ee93d50dd414d07d676"
|
||||||
@ -3488,6 +3542,16 @@ fast-printf@^1.6.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
boolean "^3.1.4"
|
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:
|
fastq@^1.6.0:
|
||||||
version "1.15.0"
|
version "1.15.0"
|
||||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
|
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"
|
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
|
||||||
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
|
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:
|
i18next-browser-languagedetector@^7.0.1:
|
||||||
version "7.0.1"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz#ead34592edc96c6c3a618a51cb57ad027c5b5d87"
|
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"
|
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
|
||||||
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
|
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:
|
internal-slot@^1.0.3, internal-slot@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
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"
|
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
|
||||||
integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==
|
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:
|
js-sdsl@^4.1.4:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430"
|
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"
|
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
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:
|
merge-stream@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
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"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
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:
|
nanoid@^3.3.6:
|
||||||
version "3.3.6"
|
version "3.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
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"
|
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||||
integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
|
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:
|
p-cancelable@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
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"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
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:
|
re-resizable@^6.9.9:
|
||||||
version "6.9.9"
|
version "6.9.9"
|
||||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.9.tgz#99e8b31c67a62115dc9c5394b7e55892265be216"
|
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"
|
loose-envify "^1.1.0"
|
||||||
scheduler "^0.23.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:
|
react-dropzone@^14.2.3:
|
||||||
version "14.2.3"
|
version "14.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.2.3.tgz#0acab68308fda2d54d1273a1e626264e13d4e84b"
|
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-callback-ref "^1.3.0"
|
||||||
use-sidecar "^1.1.2"
|
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:
|
react-style-singleton@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
|
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"
|
loose-envify "^1.4.0"
|
||||||
prop-types "^15.6.2"
|
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:
|
react-zoom-pan-pinch@^3.0.7:
|
||||||
version "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"
|
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"
|
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
|
||||||
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
|
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:
|
resolve-dependency-path@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-2.0.0.tgz#11700e340717b865d216c66cabeb4a2a3c696736"
|
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:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
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:
|
run-parallel@^1.1.9:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||||
@ -5743,6 +5920,11 @@ scheduler@^0.23.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.1.0"
|
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:
|
semver-compare@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||||
@ -5779,6 +5961,18 @@ semver@~7.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lru-cache "^6.0.0"
|
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:
|
shebang-command@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
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"
|
buffer-from "^1.0.0"
|
||||||
source-map "^0.6.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:
|
source-map@^0.5.7:
|
||||||
version "0.5.7"
|
version "0.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
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"
|
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||||
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
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:
|
spawn-command@0.0.2-1:
|
||||||
version "0.0.2-1"
|
version "0.0.2-1"
|
||||||
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
|
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"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
|
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:
|
stream-to-array@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353"
|
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"
|
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||||
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
||||||
|
|
||||||
stylis@4.1.4:
|
stylis@4.1.4, stylis@^4.0.6:
|
||||||
version "4.1.4"
|
version "4.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b"
|
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b"
|
||||||
integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ==
|
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"
|
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
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:
|
through@^2.3.8:
|
||||||
version "2.3.8"
|
version "2.3.8"
|
||||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
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"
|
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
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:
|
ts-error@^1.0.6:
|
||||||
version "1.0.6"
|
version "1.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/ts-error/-/ts-error-1.0.6.tgz#277496f2a28de6c184cfce8dfd5cdd03a4e6b0fc"
|
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"
|
minimist "^1.2.6"
|
||||||
strip-bom "^3.0.0"
|
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:
|
tslib@2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
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"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
|
||||||
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
|
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:
|
typed-array-length@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user