mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into lstein/new-model-manager
This commit is contained in:
commit
8a0ec0fa0f
@ -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()
|
||||||
|
40
invokeai/frontend/web/config/vite.app.config.ts
Normal file
40
invokeai/frontend/web/config/vite.app.config.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import react from '@vitejs/plugin-react-swc';
|
||||||
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
|
import { PluginOption, UserConfig } from 'vite';
|
||||||
|
import eslint from 'vite-plugin-eslint';
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
|
export const appConfig: UserConfig = {
|
||||||
|
base: './',
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
eslint(),
|
||||||
|
tsconfigPaths(),
|
||||||
|
visualizer() as unknown as PluginOption,
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
chunkSizeWarningLimit: 1500,
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
// Proxy HTTP requests to the flask server
|
||||||
|
proxy: {
|
||||||
|
// Proxy socket.io to the nodes socketio server
|
||||||
|
'/ws/socket.io': {
|
||||||
|
target: 'ws://127.0.0.1:9090',
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
|
// Proxy openapi schema definiton
|
||||||
|
'/openapi.json': {
|
||||||
|
target: 'http://127.0.0.1:9090/openapi.json',
|
||||||
|
rewrite: (path) => path.replace(/^\/openapi.json/, ''),
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
// proxy nodes api
|
||||||
|
'/api/v1': {
|
||||||
|
target: 'http://127.0.0.1:9090/api/v1',
|
||||||
|
rewrite: (path) => path.replace(/^\/api\/v1/, ''),
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
47
invokeai/frontend/web/config/vite.package.config.ts
Normal file
47
invokeai/frontend/web/config/vite.package.config.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import react from '@vitejs/plugin-react-swc';
|
||||||
|
import path from 'path';
|
||||||
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
|
import { PluginOption, UserConfig } from 'vite';
|
||||||
|
import dts from 'vite-plugin-dts';
|
||||||
|
import eslint from 'vite-plugin-eslint';
|
||||||
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
|
export const packageConfig: UserConfig = {
|
||||||
|
base: './',
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
eslint(),
|
||||||
|
tsconfigPaths(),
|
||||||
|
visualizer() as unknown as PluginOption,
|
||||||
|
dts({
|
||||||
|
insertTypesEntry: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
chunkSizeWarningLimit: 1500,
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, '../src/index.ts'),
|
||||||
|
name: 'InvokeAIUI',
|
||||||
|
fileName: (format) => `invoke-ai-ui.${format}.js`,
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['react', 'react-dom', '@emotion/react'],
|
||||||
|
output: {
|
||||||
|
globals: {
|
||||||
|
react: 'React',
|
||||||
|
'react-dom': 'ReactDOM',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
app: path.resolve(__dirname, '../src/app'),
|
||||||
|
assets: path.resolve(__dirname, '../src/assets'),
|
||||||
|
common: path.resolve(__dirname, '../src/common'),
|
||||||
|
features: path.resolve(__dirname, '../src/features'),
|
||||||
|
services: path.resolve(__dirname, '../src/services'),
|
||||||
|
theme: path.resolve(__dirname, '../src/theme'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
96
invokeai/frontend/web/index.d.ts
vendored
96
invokeai/frontend/web/index.d.ts
vendored
@ -1,96 +0,0 @@
|
|||||||
import React, { PropsWithChildren } from 'react';
|
|
||||||
import { IAIPopoverProps } from '../web/src/common/components/IAIPopover';
|
|
||||||
import { IAIIconButtonProps } from '../web/src/common/components/IAIIconButton';
|
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
|
||||||
import { PartialAppConfig } from 'app/invokeai';
|
|
||||||
|
|
||||||
export {};
|
|
||||||
|
|
||||||
declare module 'redux-socket.io-middleware';
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
interface Array<T> {
|
|
||||||
/**
|
|
||||||
* Returns the value of the last element in the array where predicate is true, and undefined
|
|
||||||
* otherwise.
|
|
||||||
* @param predicate findLast calls predicate once for each element of the array, in descending
|
|
||||||
* order, until it finds one where predicate returns true. If such an element is found, findLast
|
|
||||||
* immediately returns that element value. Otherwise, findLast returns undefined.
|
|
||||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
|
||||||
* predicate. If it is not provided, undefined is used instead.
|
|
||||||
*/
|
|
||||||
findLast<S extends T>(
|
|
||||||
predicate: (value: T, index: number, array: T[]) => value is S,
|
|
||||||
thisArg?: any
|
|
||||||
): S | undefined;
|
|
||||||
findLast(
|
|
||||||
predicate: (value: T, index: number, array: T[]) => unknown,
|
|
||||||
thisArg?: any
|
|
||||||
): T | undefined;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the index of the last element in the array where predicate is true, and -1
|
|
||||||
* otherwise.
|
|
||||||
* @param predicate findLastIndex calls predicate once for each element of the array, in descending
|
|
||||||
* order, until it finds one where predicate returns true. If such an element is found,
|
|
||||||
* findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.
|
|
||||||
* @param thisArg If provided, it will be used as the this value for each invocation of
|
|
||||||
* predicate. If it is not provided, undefined is used instead.
|
|
||||||
*/
|
|
||||||
findLastIndex(
|
|
||||||
predicate: (value: T, index: number, array: T[]) => unknown,
|
|
||||||
thisArg?: any
|
|
||||||
): number;
|
|
||||||
}
|
|
||||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '@invoke-ai/invoke-ai-ui' {
|
|
||||||
declare class ThemeChanger extends React.Component<ThemeChangerProps> {
|
|
||||||
public constructor(props: ThemeChangerProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class InvokeAiLogoComponent extends React.Component<InvokeAILogoComponentProps> {
|
|
||||||
public constructor(props: InvokeAILogoComponentProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class IAIPopover extends React.Component<IAIPopoverProps> {
|
|
||||||
public constructor(props: IAIPopoverProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class IAIIconButton extends React.Component<IAIIconButtonProps> {
|
|
||||||
public constructor(props: IAIIconButtonProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class SettingsModal extends React.Component<SettingsModalProps> {
|
|
||||||
public constructor(props: SettingsModalProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class StatusIndicator extends React.Component<StatusIndicatorProps> {
|
|
||||||
public constructor(props: StatusIndicatorProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class ModelSelect extends React.Component<ModelSelectProps> {
|
|
||||||
public constructor(props: ModelSelectProps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InvokeProps extends PropsWithChildren {
|
|
||||||
apiUrl?: string;
|
|
||||||
token?: string;
|
|
||||||
config?: PartialAppConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare function Invoke(props: InvokeProps): JSX.Element;
|
|
||||||
|
|
||||||
export {
|
|
||||||
ThemeChanger,
|
|
||||||
InvokeAiLogoComponent,
|
|
||||||
IAIPopover,
|
|
||||||
IAIIconButton,
|
|
||||||
SettingsModal,
|
|
||||||
StatusIndicator,
|
|
||||||
ModelSelect,
|
|
||||||
};
|
|
||||||
export = Invoke;
|
|
@ -1,7 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "invoke-ai-ui",
|
"name": "@invoke-ai/invoke-ai-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "restricted",
|
||||||
|
"registry": "https://npm.pkg.github.com"
|
||||||
|
},
|
||||||
|
"main": "./dist/invoke-ai-ui.umd.js",
|
||||||
|
"module": "./dist/invoke-ai-ui.es.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./dist/invoke-ai-ui.es.js",
|
||||||
|
"require": "./dist/invoke-ai-ui.umd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "cd ../../../ && husky install invokeai/frontend/web/.husky",
|
"prepare": "cd ../../../ && husky install invokeai/frontend/web/.husky",
|
||||||
"dev": "concurrently \"vite dev\" \"yarn run theme:watch\"",
|
"dev": "concurrently \"vite dev\" \"yarn run theme:watch\"",
|
||||||
@ -40,81 +56,96 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/anatomy": "^2.1.1",
|
"@chakra-ui/anatomy": "^2.1.1",
|
||||||
"@chakra-ui/cli": "^2.3.0",
|
"@chakra-ui/icons": "^2.0.19",
|
||||||
"@chakra-ui/icons": "^2.0.17",
|
"@chakra-ui/react": "^2.6.0",
|
||||||
"@chakra-ui/react": "^2.5.1",
|
"@chakra-ui/styled-system": "^2.9.0",
|
||||||
"@chakra-ui/styled-system": "^2.6.1",
|
|
||||||
"@chakra-ui/theme-tools": "^2.0.16",
|
"@chakra-ui/theme-tools": "^2.0.16",
|
||||||
"@dagrejs/graphlib": "^2.1.12",
|
"@dagrejs/graphlib": "^2.1.12",
|
||||||
"@emotion/react": "^11.10.6",
|
"@emotion/react": "^11.10.6",
|
||||||
"@emotion/styled": "^11.10.6",
|
"@emotion/styled": "^11.10.6",
|
||||||
"@fontsource/inter": "^4.5.15",
|
"@fontsource/inter": "^4.5.15",
|
||||||
"@reduxjs/toolkit": "^1.9.3",
|
"@reduxjs/toolkit": "^1.9.5",
|
||||||
|
"@roarr/browser-log-writer": "^1.1.5",
|
||||||
"chakra-ui-contextmenu": "^1.0.5",
|
"chakra-ui-contextmenu": "^1.0.5",
|
||||||
"dateformat": "^5.0.3",
|
"dateformat": "^5.0.3",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"framer-motion": "^9.0.4",
|
"framer-motion": "^10.12.4",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"i18next": "^22.4.10",
|
"i18next": "^22.4.15",
|
||||||
"i18next-browser-languagedetector": "^7.0.1",
|
"i18next-browser-languagedetector": "^7.0.1",
|
||||||
"i18next-http-backend": "^2.1.1",
|
"i18next-http-backend": "^2.2.0",
|
||||||
"konva": "^8.4.2",
|
"konva": "^9.0.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"patch-package": "^6.5.1",
|
"overlayscrollbars": "^2.1.1",
|
||||||
|
"overlayscrollbars-react": "^0.5.0",
|
||||||
|
"patch-package": "^7.0.0",
|
||||||
"re-resizable": "^6.9.9",
|
"re-resizable": "^6.9.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-colorful": "^5.6.1",
|
"react-colorful": "^5.6.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-hotkeys-hook": "4.3.5",
|
"react-hotkeys-hook": "4.4.0",
|
||||||
"react-i18next": "^12.1.5",
|
"react-i18next": "^12.2.2",
|
||||||
"react-icons": "^4.7.1",
|
"react-icons": "^4.7.1",
|
||||||
"react-konva": "^18.2.4",
|
"react-konva": "^18.2.7",
|
||||||
"react-konva-utils": "^0.3.2",
|
"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-zoom-pan-pinch": "^2.6.1",
|
"react-use": "^17.4.0",
|
||||||
|
"react-virtuoso": "^4.3.5",
|
||||||
|
"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",
|
||||||
|
"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"
|
||||||
},
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@chakra-ui/cli": "^2.4.0",
|
||||||
|
"react": "^18.2.0",
|
||||||
|
"react-dom": "^18.2.0",
|
||||||
|
"ts-toolbelt": "^9.6.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@chakra-ui/cli": "^2.4.0",
|
||||||
"@types/dateformat": "^5.0.0",
|
"@types/dateformat": "^5.0.0",
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/lodash-es": "^4.14.194",
|
||||||
"@types/react": "^18.0.28",
|
"@types/node": "^18.16.2",
|
||||||
"@types/react-dom": "^18.0.11",
|
"@types/react": "^18.2.0",
|
||||||
|
"@types/react-dom": "^18.2.1",
|
||||||
"@types/react-transition-group": "^4.4.5",
|
"@types/react-transition-group": "^4.4.5",
|
||||||
"@types/uuid": "^9.0.0",
|
"@types/uuid": "^9.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
"@typescript-eslint/eslint-plugin": "^5.59.1",
|
||||||
"@typescript-eslint/parser": "^5.52.0",
|
"@typescript-eslint/parser": "^5.59.1",
|
||||||
"@vitejs/plugin-react-swc": "^3.2.0",
|
"@vitejs/plugin-react-swc": "^3.3.0",
|
||||||
"axios": "^1.3.4",
|
"axios": "^1.4.0",
|
||||||
"babel-plugin-transform-imports": "^2.0.0",
|
"babel-plugin-transform-imports": "^2.0.0",
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^8.0.1",
|
||||||
"eslint": "^8.34.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^13.1.2",
|
"lint-staged": "^13.2.2",
|
||||||
"madge": "^6.0.0",
|
"madge": "^6.0.0",
|
||||||
"openapi-types": "^12.1.0",
|
"openapi-types": "^12.1.0",
|
||||||
"openapi-typescript-codegen": "^0.23.0",
|
"openapi-typescript-codegen": "^0.24.0",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.8.4",
|
"prettier": "^2.8.8",
|
||||||
"rollup-plugin-visualizer": "^5.9.0",
|
"rollup-plugin-visualizer": "^5.9.0",
|
||||||
"terser": "^5.16.4",
|
"terser": "^5.17.1",
|
||||||
"ts-toolbelt": "^9.6.0",
|
"ts-toolbelt": "^9.6.0",
|
||||||
"typescript": "4.9.5",
|
"vite": "^4.3.3",
|
||||||
"vite": "^4.1.2",
|
"vite-plugin-dts": "^2.3.0",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-tsconfig-paths": "^4.0.5",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"yarn": "^1.22.19"
|
"yarn": "^1.22.19"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -527,10 +527,15 @@
|
|||||||
"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.",
|
||||||
"resetComplete": "Web UI has been reset. Refresh the page to reload."
|
"resetComplete": "Web UI has been reset. Refresh the page to reload.",
|
||||||
|
"consoleLogLevel": "Log Level",
|
||||||
|
"shouldLogToConsole": "Console Logging",
|
||||||
|
"developer": "Developer",
|
||||||
|
"general": "General"
|
||||||
},
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"serverError": "Server Error",
|
"serverError": "Server Error",
|
||||||
@ -641,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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import ImageUploader from 'common/components/ImageUploader';
|
import ImageUploader from 'common/components/ImageUploader';
|
||||||
import Console from 'features/system/components/Console';
|
|
||||||
import ProgressBar from 'features/system/components/ProgressBar';
|
import ProgressBar from 'features/system/components/ProgressBar';
|
||||||
import SiteHeader from 'features/system/components/SiteHeader';
|
import SiteHeader from 'features/system/components/SiteHeader';
|
||||||
import InvokeTabs from 'features/ui/components/InvokeTabs';
|
import InvokeTabs from 'features/ui/components/InvokeTabs';
|
||||||
import { keepGUIAlive } from './utils';
|
|
||||||
|
|
||||||
import useToastWatcher from 'features/system/hooks/useToastWatcher';
|
import useToastWatcher from 'features/system/hooks/useToastWatcher';
|
||||||
|
|
||||||
@ -13,25 +11,34 @@ import { Box, Flex, Grid, Portal, useColorMode } from '@chakra-ui/react';
|
|||||||
import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants';
|
import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants';
|
||||||
import ImageGalleryPanel from 'features/gallery/components/ImageGalleryPanel';
|
import ImageGalleryPanel from 'features/gallery/components/ImageGalleryPanel';
|
||||||
import Lightbox from 'features/lightbox/components/Lightbox';
|
import Lightbox from 'features/lightbox/components/Lightbox';
|
||||||
import { useAppDispatch, useAppSelector } from './storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { PropsWithChildren, useCallback, useEffect, useState } from 'react';
|
import {
|
||||||
|
memo,
|
||||||
|
PropsWithChildren,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import Loading from 'common/components/Loading/Loading';
|
import Loading from 'common/components/Loading/Loading';
|
||||||
import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady';
|
import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady';
|
||||||
import { PartialAppConfig } from './invokeai';
|
import { PartialAppConfig } from 'app/types/invokeai';
|
||||||
import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys';
|
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 ProgressImagePreview from 'features/parameters/components/ProgressImagePreview';
|
||||||
|
|
||||||
keepGUIAlive();
|
const DEFAULT_CONFIG = {};
|
||||||
|
|
||||||
interface Props extends PropsWithChildren {
|
interface Props extends PropsWithChildren {
|
||||||
config?: PartialAppConfig;
|
config?: PartialAppConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
const App = ({ config = {}, children }: Props) => {
|
const App = ({ config = DEFAULT_CONFIG, children }: Props) => {
|
||||||
useToastWatcher();
|
useToastWatcher();
|
||||||
useGlobalHotkeys();
|
useGlobalHotkeys();
|
||||||
|
const log = useLogger();
|
||||||
|
|
||||||
const currentTheme = useAppSelector((state) => state.ui.currentTheme);
|
const currentTheme = useAppSelector((state) => state.ui.currentTheme);
|
||||||
|
|
||||||
@ -45,9 +52,9 @@ const App = ({ config = {}, children }: Props) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('Received config: ', config);
|
log.info({ namespace: 'App', data: config }, 'Received config');
|
||||||
dispatch(configChanged(config));
|
dispatch(configChanged(config));
|
||||||
}, [dispatch, config]);
|
}, [dispatch, config, log]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setColorMode(['light'].includes(currentTheme) ? 'light' : 'dark');
|
setColorMode(['light'].includes(currentTheme) ? 'light' : 'dark');
|
||||||
@ -58,7 +65,7 @@ const App = ({ 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 />
|
||||||
@ -114,11 +121,9 @@ const App = ({ config = {}, children }: Props) => {
|
|||||||
<Portal>
|
<Portal>
|
||||||
<FloatingGalleryButton />
|
<FloatingGalleryButton />
|
||||||
</Portal>
|
</Portal>
|
||||||
<Portal>
|
<ProgressImagePreview />
|
||||||
<Console />
|
|
||||||
</Portal>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export default memo(App);
|
@ -1,8 +1,8 @@
|
|||||||
import React, { lazy, 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';
|
import { store } from 'app/store/store';
|
||||||
import { persistor } from './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';
|
||||||
import '@fontsource/inter/200.css';
|
import '@fontsource/inter/200.css';
|
||||||
@ -14,14 +14,15 @@ 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 Loading from './common/components/Loading/Loading';
|
import Loading from '../../common/components/Loading/Loading';
|
||||||
import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
|
import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
|
||||||
import { PartialAppConfig } from 'app/invokeai';
|
import { PartialAppConfig } from 'app/types/invokeai';
|
||||||
|
|
||||||
import './i18n';
|
import '../../i18n';
|
||||||
|
import { socketMiddleware } from 'services/events/middleware';
|
||||||
|
|
||||||
const App = lazy(() => import('./app/App'));
|
const App = lazy(() => import('./App'));
|
||||||
const ThemeLocaleProvider = lazy(() => import('./app/ThemeLocaleProvider'));
|
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||||
|
|
||||||
interface Props extends PropsWithChildren {
|
interface Props extends PropsWithChildren {
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
@ -29,7 +30,7 @@ interface Props extends PropsWithChildren {
|
|||||||
config?: PartialAppConfig;
|
config?: PartialAppConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Component({ apiUrl, token, config, children }: Props) {
|
const InvokeAIUI = ({ apiUrl, token, config, children }: Props) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// configure API client token
|
// configure API client token
|
||||||
if (token) {
|
if (token) {
|
||||||
@ -50,7 +51,7 @@ export default function Component({ 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 (
|
||||||
@ -66,4 +67,6 @@ export default function Component({ apiUrl, token, config, children }: Props) {
|
|||||||
</Provider>
|
</Provider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default memo(InvokeAIUI);
|
@ -2,8 +2,8 @@ import { ChakraProvider, extendTheme } from '@chakra-ui/react';
|
|||||||
import { ReactNode, useEffect } from 'react';
|
import { ReactNode, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { theme as invokeAITheme } from 'theme/theme';
|
import { theme as invokeAITheme } from 'theme/theme';
|
||||||
import { RootState } from './store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppSelector } from './storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
|
||||||
import { greenTeaThemeColors } from 'theme/colors/greenTea';
|
import { greenTeaThemeColors } from 'theme/colors/greenTea';
|
||||||
import { invokeAIThemeColors } from 'theme/colors/invokeAI';
|
import { invokeAIThemeColors } from 'theme/colors/invokeAI';
|
||||||
@ -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,23 +1,6 @@
|
|||||||
// TODO: use Enums?
|
// TODO: use Enums?
|
||||||
|
|
||||||
import { InProgressImageType } from 'features/system/store/systemSlice';
|
export const DIFFUSERS_SCHEDULERS: Array<string> = [
|
||||||
|
|
||||||
// Valid samplers
|
|
||||||
export const SAMPLERS: Array<string> = [
|
|
||||||
'ddim',
|
|
||||||
'plms',
|
|
||||||
'k_lms',
|
|
||||||
'k_dpm_2',
|
|
||||||
'k_dpm_2_a',
|
|
||||||
'k_dpmpp_2',
|
|
||||||
'k_dpmpp_2_a',
|
|
||||||
'k_euler',
|
|
||||||
'k_euler_a',
|
|
||||||
'k_heun',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Valid Diffusers Samplers
|
|
||||||
export const DIFFUSERS_SAMPLERS: Array<string> = [
|
|
||||||
'ddim',
|
'ddim',
|
||||||
'plms',
|
'plms',
|
||||||
'k_lms',
|
'k_lms',
|
||||||
@ -48,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;
|
||||||
|
94
invokeai/frontend/web/src/app/logging/useLogger.ts
Normal file
94
invokeai/frontend/web/src/app/logging/useLogger.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { LogLevelName, ROARR, Roarr } from 'roarr';
|
||||||
|
import { createLogWriter } from '@roarr/browser-log-writer';
|
||||||
|
|
||||||
|
// Base logging context includes only the package name
|
||||||
|
const baseContext = { package: '@invoke-ai/invoke-ai-ui' };
|
||||||
|
|
||||||
|
// Create browser log writer
|
||||||
|
ROARR.write = createLogWriter();
|
||||||
|
|
||||||
|
// Module-scoped logger - can be imported and used anywhere
|
||||||
|
export let log = Roarr.child(baseContext);
|
||||||
|
|
||||||
|
// Translate human-readable log levels to numbers, used for log filtering
|
||||||
|
export const LOG_LEVEL_MAP: Record<LogLevelName, number> = {
|
||||||
|
trace: 10,
|
||||||
|
debug: 20,
|
||||||
|
info: 30,
|
||||||
|
warn: 40,
|
||||||
|
error: 50,
|
||||||
|
fatal: 60,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VALID_LOG_LEVELS = [
|
||||||
|
'trace',
|
||||||
|
'debug',
|
||||||
|
'info',
|
||||||
|
'warn',
|
||||||
|
'error',
|
||||||
|
'fatal',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type InvokeLogLevel = (typeof VALID_LOG_LEVELS)[number];
|
||||||
|
|
||||||
|
const selector = createSelector(
|
||||||
|
systemSelector,
|
||||||
|
(system) => {
|
||||||
|
const { app_version, consoleLogLevel, shouldLogToConsole } = system;
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: app_version,
|
||||||
|
consoleLogLevel,
|
||||||
|
shouldLogToConsole,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{
|
||||||
|
memoizeOptions: {
|
||||||
|
resultEqualityCheck: isEqual,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const useLogger = () => {
|
||||||
|
const { version, consoleLogLevel, shouldLogToConsole } =
|
||||||
|
useAppSelector(selector);
|
||||||
|
|
||||||
|
// The provided Roarr browser log writer uses localStorage to config logging to console
|
||||||
|
useEffect(() => {
|
||||||
|
if (shouldLogToConsole) {
|
||||||
|
// Enable console log output
|
||||||
|
localStorage.setItem('ROARR_LOG', 'true');
|
||||||
|
|
||||||
|
// Use a filter to show only logs of the given level
|
||||||
|
localStorage.setItem(
|
||||||
|
'ROARR_FILTER',
|
||||||
|
`context.logLevel:>=${LOG_LEVEL_MAP[consoleLogLevel]}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Disable console log output
|
||||||
|
localStorage.setItem('ROARR_LOG', 'false');
|
||||||
|
}
|
||||||
|
ROARR.write = createLogWriter();
|
||||||
|
}, [consoleLogLevel, shouldLogToConsole]);
|
||||||
|
|
||||||
|
// Update the module-scoped logger context as needed
|
||||||
|
useEffect(() => {
|
||||||
|
const newContext: Record<string, any> = {
|
||||||
|
...baseContext,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
newContext.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
log = Roarr.child(newContext);
|
||||||
|
}, [version]);
|
||||||
|
|
||||||
|
// Use the logger within components - no different than just importing it directly
|
||||||
|
return log;
|
||||||
|
};
|
@ -4,7 +4,7 @@ import { initialCanvasImageSelector } from 'features/canvas/store/canvasSelector
|
|||||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
export const readinessSelector = createSelector(
|
export const readinessSelector = createSelector(
|
||||||
[
|
[
|
||||||
|
@ -1,65 +1,67 @@
|
|||||||
import { createAction } from '@reduxjs/toolkit';
|
// import { createAction } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/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,208 +1,209 @@
|
|||||||
import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
// import { AnyAction, Dispatch, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/invokeai';
|
// import * as InvokeAI from 'app/types/invokeai';
|
||||||
import type { RootState } from 'app/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 {
|
||||||
addLogEntry,
|
// 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,501 +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/invokeai';
|
// import * as InvokeAI from 'app/types/invokeai';
|
||||||
|
|
||||||
import {
|
// import {
|
||||||
addLogEntry,
|
// 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';
|
// 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/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 {};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { store } from 'app/store';
|
import { store } from 'app/store/store';
|
||||||
import { persistStore } from 'redux-persist';
|
import { persistStore } from 'redux-persist';
|
||||||
|
|
||||||
export const persistor = persistStore(store);
|
export const persistor = persistStore(store);
|
@ -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';
|
||||||
@ -28,8 +26,10 @@ import { lightboxDenylist } from 'features/lightbox/store/lightboxPersistDenylis
|
|||||||
import { modelsDenylist } from 'features/system/store/modelsPersistDenylist';
|
import { modelsDenylist } from 'features/system/store/modelsPersistDenylist';
|
||||||
import { nodesDenylist } from 'features/nodes/store/nodesPersistDenylist';
|
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/systemPersistsDenylist';
|
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.
|
||||||
@ -82,19 +82,18 @@ const rootPersistConfig = getPersistConfig({
|
|||||||
'hotkeys',
|
'hotkeys',
|
||||||
'config',
|
'config',
|
||||||
],
|
],
|
||||||
debounce: 300,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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,
|
||||||
@ -114,6 +113,7 @@ export const store = configureStore({
|
|||||||
'canvas/setBoundingBoxDimensions',
|
'canvas/setBoundingBoxDimensions',
|
||||||
'canvas/setIsDrawing',
|
'canvas/setIsDrawing',
|
||||||
'canvas/addPointToCurrentLine',
|
'canvas/addPointToCurrentLine',
|
||||||
|
'socket/generatorProgress',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
@ -1,5 +1,5 @@
|
|||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppDispatch, RootState } from './store';
|
import { AppDispatch, RootState } from 'app/store/store';
|
||||||
|
|
||||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
export const useAppDispatch: () => AppDispatch = useDispatch;
|
@ -1,5 +1,5 @@
|
|||||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import { AppDispatch, RootState } from './store';
|
import { AppDispatch, RootState } from 'app/store/store';
|
||||||
|
|
||||||
// https://redux-toolkit.js.org/usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk
|
// https://redux-toolkit.js.org/usage/usage-with-typescript#defining-a-pre-typed-createasyncthunk
|
||||||
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
|
export const createAppAsyncThunk = createAsyncThunk.withTypes<{
|
@ -12,10 +12,11 @@
|
|||||||
* 'gfpgan'.
|
* 'gfpgan'.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { GalleryCategory } from 'features/gallery/store/gallerySlice';
|
||||||
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
|
import { FacetoolType } from 'features/parameters/store/postprocessingSlice';
|
||||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
import { IRect } from 'konva/lib/types';
|
import { IRect } from 'konva/lib/types';
|
||||||
import { ImageMetadata, ImageType } from 'services/api';
|
import { ImageResponseMetadata, ImageType } from 'services/api';
|
||||||
import { AnyInvocation } from 'services/events/types';
|
import { AnyInvocation } from 'services/events/types';
|
||||||
import { O } from 'ts-toolbelt';
|
import { O } from 'ts-toolbelt';
|
||||||
|
|
||||||
@ -28,24 +29,24 @@ import { O } from 'ts-toolbelt';
|
|||||||
* TODO: Better documentation of types.
|
* TODO: Better documentation of types.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export declare type PromptItem = {
|
export type PromptItem = {
|
||||||
prompt: string;
|
prompt: string;
|
||||||
weight: number;
|
weight: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TECHDEBT: We need to retain compatibility with plain prompt strings and the structure Prompt type
|
// TECHDEBT: We need to retain compatibility with plain prompt strings and the structure Prompt type
|
||||||
export declare type Prompt = Array<PromptItem> | string;
|
export type Prompt = Array<PromptItem> | string;
|
||||||
|
|
||||||
export declare type SeedWeightPair = {
|
export type SeedWeightPair = {
|
||||||
seed: number;
|
seed: number;
|
||||||
weight: number;
|
weight: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type SeedWeights = Array<SeedWeightPair>;
|
export type SeedWeights = Array<SeedWeightPair>;
|
||||||
|
|
||||||
// All generated images contain these metadata.
|
// All generated images contain these metadata.
|
||||||
export declare type CommonGeneratedImageMetadata = {
|
export type CommonGeneratedImageMetadata = {
|
||||||
postprocessing: null | Array<ESRGANMetadata | GFPGANMetadata>;
|
postprocessing: null | Array<ESRGANMetadata | FacetoolMetadata>;
|
||||||
sampler:
|
sampler:
|
||||||
| 'ddim'
|
| 'ddim'
|
||||||
| 'k_dpm_2_a'
|
| 'k_dpm_2_a'
|
||||||
@ -70,11 +71,11 @@ export declare type CommonGeneratedImageMetadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// txt2img and img2img images have some unique attributes.
|
// txt2img and img2img images have some unique attributes.
|
||||||
export declare type Txt2ImgMetadata = GeneratedImageMetadata & {
|
export type Txt2ImgMetadata = CommonGeneratedImageMetadata & {
|
||||||
type: 'txt2img';
|
type: 'txt2img';
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type Img2ImgMetadata = GeneratedImageMetadata & {
|
export type Img2ImgMetadata = CommonGeneratedImageMetadata & {
|
||||||
type: 'img2img';
|
type: 'img2img';
|
||||||
orig_hash: string;
|
orig_hash: string;
|
||||||
strength: number;
|
strength: number;
|
||||||
@ -84,102 +85,80 @@ export declare type Img2ImgMetadata = GeneratedImageMetadata & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Superset of generated image metadata types.
|
// Superset of generated image metadata types.
|
||||||
export declare type GeneratedImageMetadata = Txt2ImgMetadata | Img2ImgMetadata;
|
export type GeneratedImageMetadata = Txt2ImgMetadata | Img2ImgMetadata;
|
||||||
|
|
||||||
// All post processed images contain these metadata.
|
// All post processed images contain these metadata.
|
||||||
export declare type CommonPostProcessedImageMetadata = {
|
export type CommonPostProcessedImageMetadata = {
|
||||||
orig_path: string;
|
orig_path: string;
|
||||||
orig_hash: string;
|
orig_hash: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
// esrgan and gfpgan images have some unique attributes.
|
// esrgan and gfpgan images have some unique attributes.
|
||||||
export declare type ESRGANMetadata = CommonPostProcessedImageMetadata & {
|
export type ESRGANMetadata = CommonPostProcessedImageMetadata & {
|
||||||
type: 'esrgan';
|
type: 'esrgan';
|
||||||
scale: 2 | 4;
|
scale: 2 | 4;
|
||||||
strength: number;
|
strength: number;
|
||||||
denoise_str: number;
|
denoise_str: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type FacetoolMetadata = CommonPostProcessedImageMetadata & {
|
export type FacetoolMetadata = CommonPostProcessedImageMetadata & {
|
||||||
type: 'gfpgan' | 'codeformer';
|
type: 'gfpgan' | 'codeformer';
|
||||||
strength: number;
|
strength: number;
|
||||||
fidelity?: number;
|
fidelity?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Superset of all postprocessed image metadata types..
|
// Superset of all postprocessed image metadata types..
|
||||||
export declare type PostProcessedImageMetadata =
|
export type PostProcessedImageMetadata = ESRGANMetadata | FacetoolMetadata;
|
||||||
| ESRGANMetadata
|
|
||||||
| FacetoolMetadata;
|
|
||||||
|
|
||||||
// Metadata includes the system config and image metadata.
|
// Metadata includes the system config and image metadata.
|
||||||
export declare 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 declare 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
|
||||||
*/
|
*/
|
||||||
export declare type Image = {
|
export type Image = {
|
||||||
name: string;
|
name: string;
|
||||||
type: ImageType;
|
type: ImageType;
|
||||||
url: string;
|
url: string;
|
||||||
thumbnail: string;
|
thumbnail: string;
|
||||||
metadata: ImageMetadata;
|
metadata: ImageResponseMetadata;
|
||||||
};
|
|
||||||
|
|
||||||
// GalleryImages is an array of Image.
|
|
||||||
export declare 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 declare 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 declare 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 declare type SystemConfig = SystemGenerationMetadata & {
|
// export type SystemConfig = SystemGenerationMetadata & {
|
||||||
model_list: ModelList;
|
// model_list: ModelList;
|
||||||
infill_methods: string[];
|
// infill_methods: string[];
|
||||||
};
|
// };
|
||||||
|
|
||||||
export declare type ModelStatus = 'active' | 'cached' | 'not loaded';
|
export type ModelStatus = 'active' | 'cached' | 'not loaded';
|
||||||
|
|
||||||
export declare type Model = {
|
export type Model = {
|
||||||
status: ModelStatus;
|
status: ModelStatus;
|
||||||
description: string;
|
description: string;
|
||||||
weights: string;
|
weights: string;
|
||||||
@ -191,7 +170,7 @@ export declare type Model = {
|
|||||||
format?: string;
|
format?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type DiffusersModel = {
|
export type DiffusersModel = {
|
||||||
status: ModelStatus;
|
status: ModelStatus;
|
||||||
description: string;
|
description: string;
|
||||||
repo_id?: string;
|
repo_id?: string;
|
||||||
@ -204,14 +183,14 @@ export declare type DiffusersModel = {
|
|||||||
default?: boolean;
|
default?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ModelList = Record<string, Model & DiffusersModel>;
|
export type ModelList = Record<string, Model & DiffusersModel>;
|
||||||
|
|
||||||
export declare type FoundModel = {
|
export type FoundModel = {
|
||||||
name: string;
|
name: string;
|
||||||
location: string;
|
location: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type InvokeModelConfigProps = {
|
export type InvokeModelConfigProps = {
|
||||||
name: string | undefined;
|
name: string | undefined;
|
||||||
description: string | undefined;
|
description: string | undefined;
|
||||||
config: string | undefined;
|
config: string | undefined;
|
||||||
@ -223,7 +202,7 @@ export declare type InvokeModelConfigProps = {
|
|||||||
format: string | undefined;
|
format: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type InvokeDiffusersModelConfigProps = {
|
export type InvokeDiffusersModelConfigProps = {
|
||||||
name: string | undefined;
|
name: string | undefined;
|
||||||
description: string | undefined;
|
description: string | undefined;
|
||||||
repo_id: string | undefined;
|
repo_id: string | undefined;
|
||||||
@ -236,13 +215,13 @@ export declare type InvokeDiffusersModelConfigProps = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type InvokeModelConversionProps = {
|
export type InvokeModelConversionProps = {
|
||||||
model_name: string;
|
model_name: string;
|
||||||
save_location: string;
|
save_location: string;
|
||||||
custom_location: string | null;
|
custom_location: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type InvokeModelMergingProps = {
|
export type InvokeModelMergingProps = {
|
||||||
models_to_merge: string[];
|
models_to_merge: string[];
|
||||||
alpha: number;
|
alpha: number;
|
||||||
interp: 'weighted_sum' | 'sigmoid' | 'inv_sigmoid' | 'add_difference';
|
interp: 'weighted_sum' | 'sigmoid' | 'inv_sigmoid' | 'add_difference';
|
||||||
@ -255,48 +234,48 @@ export declare type InvokeModelMergingProps = {
|
|||||||
* These types type data received from the server via socketio.
|
* These types type data received from the server via socketio.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export declare type ModelChangeResponse = {
|
export type ModelChangeResponse = {
|
||||||
model_name: string;
|
model_name: string;
|
||||||
model_list: ModelList;
|
model_list: ModelList;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ModelConvertedResponse = {
|
export type ModelConvertedResponse = {
|
||||||
converted_model_name: string;
|
converted_model_name: string;
|
||||||
model_list: ModelList;
|
model_list: ModelList;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ModelsMergedResponse = {
|
export type ModelsMergedResponse = {
|
||||||
merged_models: string[];
|
merged_models: string[];
|
||||||
merged_model_name: string;
|
merged_model_name: string;
|
||||||
model_list: ModelList;
|
model_list: ModelList;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ModelAddedResponse = {
|
export type ModelAddedResponse = {
|
||||||
new_model_name: string;
|
new_model_name: string;
|
||||||
model_list: ModelList;
|
model_list: ModelList;
|
||||||
update: boolean;
|
update: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ModelDeletedResponse = {
|
export type ModelDeletedResponse = {
|
||||||
deleted_model_name: string;
|
deleted_model_name: string;
|
||||||
model_list: ModelList;
|
model_list: ModelList;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type FoundModelResponse = {
|
export type FoundModelResponse = {
|
||||||
search_folder: string;
|
search_folder: string;
|
||||||
found_models: FoundModel[];
|
found_models: FoundModel[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type SystemStatusResponse = SystemStatus;
|
// export type SystemStatusResponse = SystemStatus;
|
||||||
|
|
||||||
export declare type SystemConfigResponse = SystemConfig;
|
// export type SystemConfigResponse = SystemConfig;
|
||||||
|
|
||||||
export declare type ImageResultResponse = Omit<_Image, 'uuid'> & {
|
export type ImageResultResponse = Omit<_Image, 'uuid'> & {
|
||||||
boundingBox?: IRect;
|
boundingBox?: IRect;
|
||||||
generationMode: InvokeTabName;
|
generationMode: InvokeTabName;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ImageUploadResponse = {
|
export type ImageUploadResponse = {
|
||||||
// image: Omit<Image, 'uuid' | 'metadata' | 'category'>;
|
// image: Omit<Image, 'uuid' | 'metadata' | 'category'>;
|
||||||
url: string;
|
url: string;
|
||||||
mtime: number;
|
mtime: number;
|
||||||
@ -306,33 +285,16 @@ export declare type ImageUploadResponse = {
|
|||||||
// bbox: [number, number, number, number];
|
// bbox: [number, number, number, number];
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type ErrorResponse = {
|
export type ErrorResponse = {
|
||||||
message: string;
|
message: string;
|
||||||
additionalData?: string;
|
additionalData?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type GalleryImagesResponse = {
|
export type ImageUrlResponse = {
|
||||||
images: Array<Omit<_Image, 'uuid'>>;
|
|
||||||
areMoreImagesAvailable: boolean;
|
|
||||||
category: GalleryCategory;
|
|
||||||
};
|
|
||||||
|
|
||||||
export declare type ImageDeletedResponse = {
|
|
||||||
uuid: string;
|
|
||||||
url: string;
|
|
||||||
category: GalleryCategory;
|
|
||||||
};
|
|
||||||
|
|
||||||
export declare type ImageUrlResponse = {
|
|
||||||
url: string;
|
url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type UploadImagePayload = {
|
export type UploadOutpaintingMergeImagePayload = {
|
||||||
file: File;
|
|
||||||
destination?: ImageUploadDestination;
|
|
||||||
};
|
|
||||||
|
|
||||||
export declare type UploadOutpaintingMergeImagePayload = {
|
|
||||||
dataURL: string;
|
dataURL: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
@ -340,7 +302,7 @@ export declare type UploadOutpaintingMergeImagePayload = {
|
|||||||
/**
|
/**
|
||||||
* A disable-able application feature
|
* A disable-able application feature
|
||||||
*/
|
*/
|
||||||
export declare type AppFeature =
|
export type AppFeature =
|
||||||
| 'faceRestore'
|
| 'faceRestore'
|
||||||
| 'upscaling'
|
| 'upscaling'
|
||||||
| 'lightbox'
|
| 'lightbox'
|
||||||
@ -353,7 +315,7 @@ export declare type AppFeature =
|
|||||||
/**
|
/**
|
||||||
* A disable-able Stable Diffusion feature
|
* A disable-able Stable Diffusion feature
|
||||||
*/
|
*/
|
||||||
export declare type StableDiffusionFeature =
|
export type StableDiffusionFeature =
|
||||||
| 'noiseConfig'
|
| 'noiseConfig'
|
||||||
| 'variations'
|
| 'variations'
|
||||||
| 'symmetry'
|
| 'symmetry'
|
||||||
@ -364,7 +326,7 @@ export declare type StableDiffusionFeature =
|
|||||||
* Configuration options for the InvokeAI UI.
|
* Configuration options for the InvokeAI UI.
|
||||||
* Distinct from system settings which may be changed inside the app.
|
* Distinct from system settings which may be changed inside the app.
|
||||||
*/
|
*/
|
||||||
export declare type AppConfig = {
|
export type AppConfig = {
|
||||||
/**
|
/**
|
||||||
* Whether or not URLs should be transformed to use a different host
|
* Whether or not URLs should be transformed to use a different host
|
||||||
*/
|
*/
|
||||||
@ -428,4 +390,4 @@ export declare type AppConfig = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare type PartialAppConfig = O.Partial<AppConfig, 'deep'>;
|
export type PartialAppConfig = O.Partial<AppConfig, 'deep'>;
|
@ -1,25 +0,0 @@
|
|||||||
export function keepGUIAlive() {
|
|
||||||
async function getRequest(url = '') {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'GET',
|
|
||||||
cache: 'no-cache',
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keepAliveServer = () => {
|
|
||||||
const url = document.location;
|
|
||||||
const route = '/flaskwebgui-keep-server-alive';
|
|
||||||
getRequest(url + route).then((data) => {
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!import.meta.env.NODE_ENV || import.meta.env.NODE_ENV === 'production') {
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const intervalRequest = 3 * 1000;
|
|
||||||
keepAliveServer();
|
|
||||||
setInterval(keepAliveServer, intervalRequest);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { Feature, useFeatureHelpInfo } from 'app/features';
|
import { Feature, useFeatureHelpInfo } from 'app/features';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { SystemState } from 'features/system/store/systemSlice';
|
import { SystemState } from 'features/system/store/systemSlice';
|
||||||
import { memo, ReactElement } from 'react';
|
import { memo, ReactElement } from 'react';
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipProps,
|
TooltipProps,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { clamp } from 'lodash';
|
import { clamp } from 'lodash-es';
|
||||||
|
|
||||||
import { FocusEvent, memo, useEffect, useState } from 'react';
|
import { FocusEvent, memo, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
@ -16,13 +16,23 @@ type IAISelectProps = SelectProps & {
|
|||||||
validValues:
|
validValues:
|
||||||
| Array<number | string>
|
| Array<number | string>
|
||||||
| Array<{ key: string; value: string | number }>;
|
| Array<{ key: string; value: string | number }>;
|
||||||
|
horizontal?: boolean;
|
||||||
|
spaceEvenly?: boolean;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Customized Chakra FormControl + Select multi-part component.
|
* Customized Chakra FormControl + Select multi-part component.
|
||||||
*/
|
*/
|
||||||
const IAISelect = (props: IAISelectProps) => {
|
const IAISelect = (props: IAISelectProps) => {
|
||||||
const { label, isDisabled, validValues, tooltip, tooltipProps, ...rest } =
|
const {
|
||||||
props;
|
label,
|
||||||
|
isDisabled,
|
||||||
|
validValues,
|
||||||
|
tooltip,
|
||||||
|
tooltipProps,
|
||||||
|
horizontal,
|
||||||
|
spaceEvenly,
|
||||||
|
...rest
|
||||||
|
} = props;
|
||||||
return (
|
return (
|
||||||
<FormControl
|
<FormControl
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
@ -32,10 +42,28 @@ const IAISelect = (props: IAISelectProps) => {
|
|||||||
e.nativeEvent.stopPropagation();
|
e.nativeEvent.stopPropagation();
|
||||||
e.nativeEvent.cancelBubble = true;
|
e.nativeEvent.cancelBubble = true;
|
||||||
}}
|
}}
|
||||||
|
sx={
|
||||||
|
horizontal
|
||||||
|
? {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: 4,
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{label && <FormLabel>{label}</FormLabel>}
|
{label && (
|
||||||
|
<FormLabel sx={spaceEvenly ? { flexBasis: 0, flexGrow: 1 } : {}}>
|
||||||
|
{label}
|
||||||
|
</FormLabel>
|
||||||
|
)}
|
||||||
<Tooltip label={tooltip} {...tooltipProps}>
|
<Tooltip label={tooltip} {...tooltipProps}>
|
||||||
<Select {...rest}>
|
<Select
|
||||||
|
{...rest}
|
||||||
|
rootProps={{ sx: spaceEvenly ? { flexBasis: 0, flexGrow: 1 } : {} }}
|
||||||
|
>
|
||||||
{validValues.map((opt) => {
|
{validValues.map((opt) => {
|
||||||
return typeof opt === 'string' || typeof opt === 'number' ? (
|
return typeof opt === 'string' || typeof opt === 'number' ? (
|
||||||
<IAIOption key={opt} value={opt}>
|
<IAIOption key={opt} value={opt}>
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipProps,
|
TooltipProps,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { clamp } from 'lodash';
|
import { clamp } from 'lodash-es';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -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';
|
import { Image } from 'app/types/invokeai';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/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/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/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { RootState } from 'app/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 { Box, useToast } from '@chakra-ui/react';
|
import { Box, useToast } from '@chakra-ui/react';
|
||||||
import { ImageUploaderTriggerContext } from 'app/contexts/ImageUploaderTriggerContext';
|
import { ImageUploaderTriggerContext } from 'app/contexts/ImageUploaderTriggerContext';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import useImageUploader from 'common/hooks/useImageUploader';
|
import useImageUploader from 'common/hooks/useImageUploader';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { ResourceKey } from 'i18next';
|
import { ResourceKey } from 'i18next';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Flex, Image, Spinner } from '@chakra-ui/react';
|
import { Flex, Image, Spinner } from '@chakra-ui/react';
|
||||||
import InvokeAILogoImage from 'assets/images/logo.png';
|
import InvokeAILogoImage from 'assets/images/logo.png';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
// This component loads before the theme so we cannot use theme tokens here
|
// This component loads before the theme so we cannot use theme tokens here
|
||||||
|
|
||||||
@ -29,4 +30,4 @@ const Loading = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Loading;
|
export default memo(Loading);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
import { shiftKeyPressed } from 'features/ui/store/hotkeysSlice';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
import { isHotkeyPressed, useHotkeys } from 'react-hotkeys-hook';
|
import { isHotkeyPressed, useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
|
||||||
const globalHotkeysSelector = createSelector(
|
const globalHotkeysSelector = createSelector(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import promptToString from './promptToString';
|
import promptToString from './promptToString';
|
||||||
|
|
||||||
export function getPromptAndNegative(inputPrompt: InvokeAI.Prompt) {
|
export function getPromptAndNegative(inputPrompt: InvokeAI.Prompt) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useCallback } from 'react';
|
||||||
import { OpenAPI } from 'services/api';
|
import { OpenAPI } from 'services/api';
|
||||||
|
|
||||||
export const getUrlAlt = (url: string, shouldTransformUrls: boolean) => {
|
export const getUrlAlt = (url: string, shouldTransformUrls: boolean) => {
|
||||||
@ -15,14 +16,19 @@ export const useGetUrl = () => {
|
|||||||
(state: RootState) => state.config.shouldTransformUrls
|
(state: RootState) => state.config.shouldTransformUrls
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
const getUrl = useCallback(
|
||||||
shouldTransformUrls,
|
(url?: string) => {
|
||||||
getUrl: (url?: string) => {
|
|
||||||
if (OpenAPI.BASE && shouldTransformUrls) {
|
if (OpenAPI.BASE && shouldTransformUrls) {
|
||||||
return [OpenAPI.BASE, url].join('/');
|
return [OpenAPI.BASE, url].join('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
|
[shouldTransformUrls]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
shouldTransformUrls,
|
||||||
|
getUrl,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { forEach, size } from 'lodash';
|
import { forEach, size } from 'lodash-es';
|
||||||
import { ImageField, LatentsField } from 'services/api';
|
import { ImageField, LatentsField } from 'services/api';
|
||||||
|
|
||||||
const OBJECT_TYPESTRING = '[object Object]';
|
const OBJECT_TYPESTRING = '[object Object]';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
|
|
||||||
const promptToString = (prompt: InvokeAI.Prompt): string => {
|
const promptToString = (prompt: InvokeAI.Prompt): string => {
|
||||||
if (typeof prompt === 'string') {
|
if (typeof prompt === 'string') {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
|
|
||||||
export const stringToSeedWeights = (
|
export const stringToSeedWeights = (
|
||||||
string: string
|
string: string
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import Component from './component';
|
|
||||||
|
|
||||||
import InvokeAiLogoComponent from './features/system/components/InvokeAILogoComponent';
|
|
||||||
import ThemeChanger from './features/system/components/ThemeChanger';
|
|
||||||
import IAIPopover from './common/components/IAIPopover';
|
|
||||||
import IAIIconButton from './common/components/IAIIconButton';
|
|
||||||
import SettingsModal from './features/system/components/SettingsModal/SettingsModal';
|
|
||||||
import StatusIndicator from './features/system/components/StatusIndicator';
|
|
||||||
import ModelSelect from 'features/system/components/ModelSelect';
|
|
||||||
|
|
||||||
export default Component;
|
|
||||||
export {
|
|
||||||
InvokeAiLogoComponent,
|
|
||||||
ThemeChanger,
|
|
||||||
IAIPopover,
|
|
||||||
IAIIconButton,
|
|
||||||
SettingsModal,
|
|
||||||
StatusIndicator,
|
|
||||||
ModelSelect,
|
|
||||||
};
|
|
@ -1,4 +1,4 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/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';
|
||||||
import { clearCanvasHistory } from 'features/canvas/store/canvasSlice';
|
import { clearCanvasHistory } from 'features/canvas/store/canvasSlice';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Box, chakra, Flex } from '@chakra-ui/react';
|
import { Box, chakra, Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
@ -8,7 +8,7 @@ import {
|
|||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import { Vector2d } from 'konva/lib/types';
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useCallback, useRef } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import { Layer, Stage } from 'react-konva';
|
import { Layer, Stage } from 'react-konva';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { Group, Rect } from 'react-konva';
|
import { Group, Rect } from 'react-konva';
|
||||||
import { canvasSelector } from '../store/canvasSelectors';
|
import { canvasSelector } from '../store/canvasSelectors';
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
import { useToken } from '@chakra-ui/react';
|
import { useToken } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { isEqual, range } from 'lodash';
|
import { isEqual, range } from 'lodash-es';
|
||||||
|
|
||||||
import { ReactNode, useCallback, useLayoutEffect, useState } from 'react';
|
import { ReactNode, useCallback, useLayoutEffect, useState } from 'react';
|
||||||
import { Group, Line as KonvaLine } from 'react-konva';
|
import { Group, Line as KonvaLine } from 'react-konva';
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { GalleryState } from 'features/gallery/store/gallerySlice';
|
import { GalleryState } from 'features/gallery/store/gallerySlice';
|
||||||
import { ImageConfig } from 'konva/lib/shapes/Image';
|
import { ImageConfig } from 'konva/lib/shapes/Image';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Image as KonvaImage } from 'react-konva';
|
import { Image as KonvaImage } from 'react-konva';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { RectConfig } from 'konva/lib/shapes/Rect';
|
import { RectConfig } from 'konva/lib/shapes/Rect';
|
||||||
import { Rect } from 'react-konva';
|
import { Rect } from 'react-konva';
|
||||||
|
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash-es';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
export const canvasMaskCompositerSelector = createSelector(
|
export const canvasMaskCompositerSelector = createSelector(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { GroupConfig } from 'konva/lib/Group';
|
import { GroupConfig } from 'konva/lib/Group';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { Group, Line } from 'react-konva';
|
import { Group, Line } from 'react-konva';
|
||||||
import { isCanvasMaskLine } from '../store/canvasTypes';
|
import { isCanvasMaskLine } from '../store/canvasTypes';
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { Group, Line, Rect } from 'react-konva';
|
import { Group, Line, Rect } from 'react-konva';
|
||||||
import {
|
import {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Flex, Spinner } from '@chakra-ui/react';
|
import { Flex, Spinner } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
initialCanvasImageSelector,
|
initialCanvasImageSelector,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { GroupConfig } from 'konva/lib/Group';
|
import { GroupConfig } from 'konva/lib/Group';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { Group, Rect } from 'react-konva';
|
import { Group, Rect } from 'react-konva';
|
||||||
import IAICanvasImage from './IAICanvasImage';
|
import IAICanvasImage from './IAICanvasImage';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
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/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';
|
||||||
import {
|
import {
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
setShouldShowStagingImage,
|
setShouldShowStagingImage,
|
||||||
setShouldShowStagingOutline,
|
setShouldShowStagingOutline,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import roundToHundreth from '../util/roundToHundreth';
|
import roundToHundreth from '../util/roundToHundreth';
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import roundToHundreth from 'features/canvas/util/roundToHundreth';
|
import roundToHundreth from 'features/canvas/util/roundToHundreth';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { GroupConfig } from 'konva/lib/Group';
|
import { GroupConfig } from 'konva/lib/Group';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { Circle, Group } from 'react-konva';
|
import { Circle, Group } from 'react-konva';
|
||||||
import {
|
import {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
roundDownToMultiple,
|
roundDownToMultiple,
|
||||||
roundToMultiple,
|
roundToMultiple,
|
||||||
@ -16,7 +16,7 @@ import Konva from 'konva';
|
|||||||
import { GroupConfig } from 'konva/lib/Group';
|
import { GroupConfig } from 'konva/lib/Group';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import { Vector2d } from 'konva/lib/types';
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { Group, Rect, Transformer } from 'react-konva';
|
import { Group, Rect, Transformer } from 'react-konva';
|
||||||
|
@ -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 { useAppDispatch, useAppSelector } from 'app/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';
|
||||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
@ -18,7 +18,7 @@ import {
|
|||||||
setShouldPreserveMaskedArea,
|
setShouldPreserveMaskedArea,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
import { rgbaColorToString } from 'features/canvas/util/colorToString';
|
||||||
import { isEqual } from 'lodash';
|
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';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/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';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
@ -9,7 +9,7 @@ import { FaRedo } from 'react-icons/fa';
|
|||||||
import { redo } from 'features/canvas/store/canvasSlice';
|
import { redo } from 'features/canvas/store/canvasSlice';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
|
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const canvasRedoSelector = createSelector(
|
const canvasRedoSelector = createSelector(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAICheckbox from 'common/components/IAICheckbox';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
@ -16,7 +16,7 @@ import {
|
|||||||
setShouldSnapToGrid,
|
setShouldSnapToGrid,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import EmptyTempFolderButtonModal from 'features/system/components/ClearTempFolderButtonModal';
|
import EmptyTempFolderButtonModal from 'features/system/components/ClearTempFolderButtonModal';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
@ -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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIColorPicker from 'common/components/IAIColorPicker';
|
import IAIColorPicker from 'common/components/IAIColorPicker';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
setTool,
|
setTool,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { clamp, isEqual } from 'lodash';
|
import { clamp, 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';
|
||||||
|
@ -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 { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import IAISelect from 'common/components/IAISelect';
|
import IAISelect from 'common/components/IAISelect';
|
||||||
import useImageUploader from 'common/hooks/useImageUploader';
|
import useImageUploader from 'common/hooks/useImageUploader';
|
||||||
@ -24,7 +24,7 @@ import {
|
|||||||
import { mergeAndUploadCanvas } from 'features/canvas/store/thunks/mergeAndUploadCanvas';
|
import { mergeAndUploadCanvas } from 'features/canvas/store/thunks/mergeAndUploadCanvas';
|
||||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/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';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
@ -9,7 +9,7 @@ import { undo } from 'features/canvas/store/canvasSlice';
|
|||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const canvasUndoSelector = createSelector(
|
const canvasUndoSelector = createSelector(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
@ -9,7 +9,7 @@ import {
|
|||||||
setStageCoordinates,
|
setStageCoordinates,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
@ -13,7 +13,7 @@ import {
|
|||||||
setTool,
|
setTool,
|
||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { MutableRefObject, useCallback } from 'react';
|
import { MutableRefObject, useCallback } from 'react';
|
||||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { Vector2d } from 'konva/lib/types';
|
import { Vector2d } from 'konva/lib/types';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { MutableRefObject, useCallback } from 'react';
|
import { MutableRefObject, useCallback } from 'react';
|
||||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useAppDispatch } from 'app/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { mouseLeftCanvas } from 'features/canvas/store/canvasSlice';
|
import { mouseLeftCanvas } from 'features/canvas/store/canvasSlice';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
canvasSelector,
|
canvasSelector,
|
||||||
isStagingSelector,
|
isStagingSelector,
|
||||||
@ -12,7 +12,7 @@ import {
|
|||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { MutableRefObject, useCallback } from 'react';
|
import { MutableRefObject, useCallback } from 'react';
|
||||||
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
import getScaledCursorPosition from '../util/getScaledCursorPosition';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import {
|
import {
|
||||||
setStageCoordinates,
|
setStageCoordinates,
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import { KonvaEventObject } from 'konva/lib/Node';
|
import { KonvaEventObject } from 'konva/lib/Node';
|
||||||
import { clamp, isEqual } from 'lodash';
|
import { clamp, isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { MutableRefObject, useCallback } from 'react';
|
import { MutableRefObject, useCallback } from 'react';
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useAppDispatch } from 'app/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import Konva from 'konva';
|
import Konva from 'konva';
|
||||||
import {
|
import {
|
||||||
commitColorPickerColor,
|
commitColorPickerColor,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes';
|
import { CanvasImage, CanvasState, isCanvasBaseImage } from './canvasTypes';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
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/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import {
|
import {
|
||||||
roundDownToMultiple,
|
roundDownToMultiple,
|
||||||
roundToMultiple,
|
roundToMultiple,
|
||||||
} from 'common/util/roundDownToMultiple';
|
} from 'common/util/roundDownToMultiple';
|
||||||
import { IRect, Vector2d } from 'konva/lib/types';
|
import { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { clamp, cloneDeep } from 'lodash';
|
import { clamp, cloneDeep } from 'lodash-es';
|
||||||
//
|
//
|
||||||
import { RgbaColor } from 'react-colorful';
|
import { RgbaColor } from 'react-colorful';
|
||||||
import calculateCoordinates from '../util/calculateCoordinates';
|
import calculateCoordinates from '../util/calculateCoordinates';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { IRect, Vector2d } from 'konva/lib/types';
|
import { IRect, Vector2d } from 'konva/lib/types';
|
||||||
import { RgbaColor } from 'react-colorful';
|
import { RgbaColor } from 'react-colorful';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
|
import { AnyAction, ThunkAction } from '@reduxjs/toolkit';
|
||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { RootState } from 'app/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,6 +1,6 @@
|
|||||||
import { AppDispatch, AppGetState } from 'app/store';
|
import { AppDispatch, AppGetState } from 'app/store/store';
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash-es';
|
||||||
import { setDoesCanvasNeedScaling } from '../canvasSlice';
|
import { setDoesCanvasNeedScaling } from '../canvasSlice';
|
||||||
|
|
||||||
const debouncedCanvasScale = debounce((dispatch: AppDispatch) => {
|
const debouncedCanvasScale = debounce((dispatch: AppDispatch) => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { isEqual } from 'lodash';
|
import { get, isEqual, isNumber, isString } from 'lodash-es';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
@ -10,8 +10,8 @@ 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/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';
|
||||||
import IAIPopover from 'common/components/IAIPopover';
|
import IAIPopover from 'common/components/IAIPopover';
|
||||||
@ -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,18 +163,10 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
|
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const setBothPrompts = useSetBothPrompts();
|
|
||||||
|
|
||||||
const handleClickUseAsInitialImage = () => {
|
const { recallPrompt, recallSeed, sendToImageToImage } = useParameters();
|
||||||
if (!image) return;
|
|
||||||
if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
|
|
||||||
dispatch(initialImageSelected(image.name));
|
|
||||||
// dispatch(setInitialImage(currentImage));
|
|
||||||
|
|
||||||
// dispatch(setActiveTab('img2img'));
|
const handleCopyImage = useCallback(async () => {
|
||||||
};
|
|
||||||
|
|
||||||
const handleCopyImage = async () => {
|
|
||||||
if (!image?.url) {
|
if (!image?.url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -194,9 +188,9 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
duration: 2500,
|
duration: 2500,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
};
|
}, [getUrl, t, image?.url, toast]);
|
||||||
|
|
||||||
const handleCopyImageLink = () => {
|
const handleCopyImageLink = useCallback(() => {
|
||||||
const url = image
|
const url = image
|
||||||
? shouldTransformUrls
|
? shouldTransformUrls
|
||||||
? getUrl(image.url)
|
? getUrl(image.url)
|
||||||
@ -215,37 +209,13 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}, [toast, shouldTransformUrls, getUrl, t, image]);
|
||||||
|
|
||||||
useHotkeys(
|
const handlePreviewVisibility = useCallback(() => {
|
||||||
'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 = () => {
|
|
||||||
dispatch(setShouldHidePreview(!shouldHidePreview));
|
dispatch(setShouldHidePreview(!shouldHidePreview));
|
||||||
};
|
}, [dispatch, shouldHidePreview]);
|
||||||
|
|
||||||
const handleClickUseAllParameters = () => {
|
const handleClickUseAllParameters = useCallback(() => {
|
||||||
if (!image) return;
|
if (!image) return;
|
||||||
// selectedImage.metadata &&
|
// selectedImage.metadata &&
|
||||||
// dispatch(setAllParameters(selectedImage.metadata));
|
// dispatch(setAllParameters(selectedImage.metadata));
|
||||||
@ -254,12 +224,13 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
// } else if (selectedImage.metadata?.image.type === 'txt2img') {
|
// } else if (selectedImage.metadata?.image.type === 'txt2img') {
|
||||||
// dispatch(setActiveTab('txt2img'));
|
// dispatch(setActiveTab('txt2img'));
|
||||||
// }
|
// }
|
||||||
};
|
}, [image]);
|
||||||
|
|
||||||
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,67 +251,27 @@ 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',
|
|
||||||
() => {
|
|
||||||
if (image?.metadata?.sd_metadata?.prompt) {
|
|
||||||
handleClickUsePrompt();
|
|
||||||
toast({
|
|
||||||
title: t('toast.promptSet'),
|
|
||||||
status: 'success',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
toast({
|
|
||||||
title: t('toast.promptNotSet'),
|
|
||||||
description: t('toast.promptNotSetDesc'),
|
|
||||||
status: 'error',
|
|
||||||
duration: 2500,
|
|
||||||
isClosable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[image]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleClickUpscale = () => {
|
const handleSendToImageToImage = useCallback(() => {
|
||||||
|
sendToImageToImage(image);
|
||||||
|
}, [image, sendToImageToImage]);
|
||||||
|
|
||||||
|
useHotkeys('shift+i', handleSendToImageToImage, [image]);
|
||||||
|
|
||||||
|
const handleClickUpscale = useCallback(() => {
|
||||||
// selectedImage && dispatch(runESRGAN(selectedImage));
|
// selectedImage && dispatch(runESRGAN(selectedImage));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'Shift+U',
|
'Shift+U',
|
||||||
@ -369,9 +300,9 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickFixFaces = () => {
|
const handleClickFixFaces = useCallback(() => {
|
||||||
// selectedImage && dispatch(runFacetool(selectedImage));
|
// selectedImage && dispatch(runFacetool(selectedImage));
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'Shift+R',
|
'Shift+R',
|
||||||
@ -401,10 +332,12 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleClickShowImageDetails = () =>
|
const handleClickShowImageDetails = useCallback(
|
||||||
dispatch(setShouldShowImageDetails(!shouldShowImageDetails));
|
() => dispatch(setShouldShowImageDetails(!shouldShowImageDetails)),
|
||||||
|
[dispatch, shouldShowImageDetails]
|
||||||
|
);
|
||||||
|
|
||||||
const handleSendToCanvas = () => {
|
const handleSendToCanvas = useCallback(() => {
|
||||||
if (!image) return;
|
if (!image) return;
|
||||||
if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
|
if (isLightboxOpen) dispatch(setIsLightboxOpen(false));
|
||||||
|
|
||||||
@ -421,7 +354,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
duration: 2500,
|
duration: 2500,
|
||||||
isClosable: true,
|
isClosable: true,
|
||||||
});
|
});
|
||||||
};
|
}, [image, isLightboxOpen, dispatch, activeTabName, toast, t]);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'i',
|
'i',
|
||||||
@ -440,19 +373,19 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
[image, shouldShowImageDetails]
|
[image, shouldShowImageDetails]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleInitiateDelete = () => {
|
const handleDelete = useCallback(() => {
|
||||||
|
if (canDeleteImage && image) {
|
||||||
|
dispatch(imageDeleted({ imageType: image.type, imageName: image.name }));
|
||||||
|
}
|
||||||
|
}, [image, canDeleteImage, dispatch]);
|
||||||
|
|
||||||
|
const handleInitiateDelete = useCallback(() => {
|
||||||
if (shouldConfirmOnDelete) {
|
if (shouldConfirmOnDelete) {
|
||||||
onDeleteDialogOpen();
|
onDeleteDialogOpen();
|
||||||
} else {
|
} else {
|
||||||
handleDelete();
|
handleDelete();
|
||||||
}
|
}
|
||||||
};
|
}, [shouldConfirmOnDelete, onDeleteDialogOpen, handleDelete]);
|
||||||
|
|
||||||
const handleDelete = () => {
|
|
||||||
if (canDeleteImage && image) {
|
|
||||||
dispatch(imageDeleted({ imageType: image.type, imageName: image.name }));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useHotkeys('delete', handleInitiateDelete, [
|
useHotkeys('delete', handleInitiateDelete, [
|
||||||
image,
|
image,
|
||||||
@ -461,9 +394,9 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => {
|
|||||||
isProcessing,
|
isProcessing,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleLightBox = () => {
|
const handleLightBox = useCallback(() => {
|
||||||
dispatch(setIsLightboxOpen(!isLightboxOpen));
|
dispatch(setIsLightboxOpen(!isLightboxOpen));
|
||||||
};
|
}, [dispatch, isLightboxOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -494,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')}
|
||||||
@ -568,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
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Flex, Icon } from '@chakra-ui/react';
|
import { Flex, Icon } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { MdPhoto } from 'react-icons/md';
|
import { MdPhoto } from 'react-icons/md';
|
||||||
import { selectedImageSelector } from '../store/gallerySelectors';
|
import { selectedImageSelector } from '../store/gallerySelectors';
|
||||||
|
@ -1,46 +1,27 @@
|
|||||||
import { Box, Flex, Image } from '@chakra-ui/react';
|
import { Box, Flex, Image } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { selectedImageSelector } from '../store/gallerySelectors';
|
import { selectedImageSelector } from '../store/gallerySelectors';
|
||||||
import CurrentImageFallback from './CurrentImageFallback';
|
import CurrentImageFallback from './CurrentImageFallback';
|
||||||
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
|
import ImageMetadataViewer from './ImageMetaDataViewer/ImageMetadataViewer';
|
||||||
import NextPrevImageButtons from './NextPrevImageButtons';
|
import NextPrevImageButtons from './NextPrevImageButtons';
|
||||||
import CurrentImageHidden from './CurrentImageHidden';
|
import CurrentImageHidden from './CurrentImageHidden';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
export const imagesSelector = createSelector(
|
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,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -50,8 +31,8 @@ export const imagesSelector = createSelector(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function CurrentImagePreview() {
|
const CurrentImagePreview = () => {
|
||||||
const { shouldShowImageDetails, imageToDisplay, shouldHidePreview } =
|
const { shouldShowImageDetails, image, shouldHidePreview } =
|
||||||
useAppSelector(imagesSelector);
|
useAppSelector(imagesSelector);
|
||||||
const { getUrl } = useGetUrl();
|
const { getUrl } = useGetUrl();
|
||||||
|
|
||||||
@ -65,41 +46,23 @@ export default function 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',
|
||||||
@ -110,9 +73,12 @@ export default function CurrentImagePreview() {
|
|||||||
overflow: 'scroll',
|
overflow: 'scroll',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ImageMetadataViewer image={imageToDisplay.image} />
|
<ImageMetadataViewer image={image} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
{!shouldShowImageDetails && <NextPrevImageButtons />}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default memo(CurrentImagePreview);
|
||||||
|
@ -9,13 +9,13 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { configSelector } from 'features/system/store/configSelectors';
|
import { configSelector } from 'features/system/store/configSelectors';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
|
import { setShouldConfirmOnDelete } from 'features/system/store/systemSlice';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { ChangeEvent, memo, useCallback, useRef } from 'react';
|
import { ChangeEvent, memo, useCallback, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
@ -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/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/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';
|
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 (image.metadata?.sd_metadata?.prompt) {
|
(e: DragEvent<HTMLDivElement>) => {
|
||||||
setBothPrompts(image.metadata?.sd_metadata?.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
|
||||||
image.metadata.sd_metadata &&
|
const handleRecallPrompt = useCallback(() => {
|
||||||
dispatch(setSeed(image.metadata.sd_metadata.image.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));
|
||||||
|
|
||||||
@ -195,48 +176,14 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleUseAllParameters = () => {
|
const handleUseAllParameters = () => {
|
||||||
metadata.sd_metadata && dispatch(setAllParameters(metadata.sd_metadata));
|
// metadata.invokeai?.node &&
|
||||||
toast({
|
// dispatch(setAllParameters(metadata.invokeai?.node));
|
||||||
title: t('toast.parametersSet'),
|
// toast({
|
||||||
status: 'success',
|
// title: t('toast.parametersSet'),
|
||||||
duration: 2500,
|
// status: 'success',
|
||||||
isClosable: true,
|
// duration: 2500,
|
||||||
});
|
// isClosable: true,
|
||||||
};
|
// });
|
||||||
|
|
||||||
const handleUseInitialImage = async () => {
|
|
||||||
if (metadata.sd_metadata?.image?.init_image_path) {
|
|
||||||
const response = await fetch(
|
|
||||||
metadata.sd_metadata?.image?.init_image_path
|
|
||||||
);
|
|
||||||
if (response.ok) {
|
|
||||||
dispatch(setAllImageToImageParameters(metadata?.sd_metadata));
|
|
||||||
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 = () => {
|
||||||
@ -253,37 +200,37 @@ 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?.sd_metadata?.prompt === undefined}
|
isDisabled={image?.metadata?.invokeai?.node?.prompt === undefined}
|
||||||
>
|
>
|
||||||
{t('parameters.usePrompt')}
|
{t('parameters.usePrompt')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<IoArrowUndoCircleOutline />}
|
icon={<IoArrowUndoCircleOutline />}
|
||||||
onClickCapture={handleUseSeed}
|
onClickCapture={handleRecallSeed}
|
||||||
isDisabled={image?.metadata?.sd_metadata?.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?.sd_metadata?.type !== 'img2img'}
|
isDisabled={image?.metadata?.invokeai?.node?.type !== 'img2img'}
|
||||||
>
|
>
|
||||||
{t('parameters.useInitImg')}
|
{t('parameters.useInitImg')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@ -292,7 +239,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
|
|||||||
onClickCapture={handleUseAllParameters}
|
onClickCapture={handleUseAllParameters}
|
||||||
isDisabled={
|
isDisabled={
|
||||||
!['txt2img', 'img2img'].includes(
|
!['txt2img', 'img2img'].includes(
|
||||||
image?.metadata?.sd_metadata?.type
|
String(image?.metadata?.invokeai?.node?.type)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -322,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',
|
||||||
@ -370,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%',
|
||||||
@ -381,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',
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
// useTheme,
|
// useTheme,
|
||||||
// } from '@chakra-ui/react';
|
// } from '@chakra-ui/react';
|
||||||
// import { requestImages } from 'app/socketio/actions';
|
// import { requestImages } from 'app/socketio/actions';
|
||||||
// import { useAppDispatch, useAppSelector } from 'app/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';
|
||||||
// import IAIIconButton from 'common/components/IAIIconButton';
|
// import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
@ -35,7 +35,7 @@
|
|||||||
// } from 'features/ui/store/uiSlice';
|
// } from 'features/ui/store/uiSlice';
|
||||||
// import { InvokeTabName } from 'features/ui/store/tabMap';
|
// import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||||
|
|
||||||
// import { clamp } from 'lodash';
|
// import { clamp } from 'lodash-es';
|
||||||
// import { Direction } from 're-resizable/lib/resizer';
|
// import { Direction } from 're-resizable/lib/resizer';
|
||||||
// import React, {
|
// import React, {
|
||||||
// ChangeEvent,
|
// ChangeEvent,
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
import { ButtonGroup, Flex, Grid, Icon, Image, Text } from '@chakra-ui/react';
|
import {
|
||||||
import { requestImages } from 'app/socketio/actions';
|
Box,
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
ButtonGroup,
|
||||||
|
Flex,
|
||||||
|
FlexProps,
|
||||||
|
Grid,
|
||||||
|
Icon,
|
||||||
|
Text,
|
||||||
|
forwardRef,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
import IAICheckbox from 'common/components/IAICheckbox';
|
import IAICheckbox from 'common/components/IAICheckbox';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
@ -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';
|
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,13 +1,13 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/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';
|
||||||
|
|
||||||
import { clamp, isEqual } from 'lodash';
|
import { clamp, isEqual } from 'lodash-es';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
|
|
||||||
import './ImageGallery.css';
|
import './ImageGallery.css';
|
||||||
@ -28,6 +28,7 @@ import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvas
|
|||||||
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
||||||
import useResolution from 'common/hooks/useResolution';
|
import useResolution from 'common/hooks/useResolution';
|
||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
const GALLERY_TAB_WIDTHS: Record<
|
const GALLERY_TAB_WIDTHS: Record<
|
||||||
InvokeTabName,
|
InvokeTabName,
|
||||||
@ -72,7 +73,7 @@ const galleryPanelSelector = createSelector(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function ImageGalleryPanel() {
|
export const ImageGalleryPanel = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {
|
const {
|
||||||
shouldPinGallery,
|
shouldPinGallery,
|
||||||
@ -109,28 +110,6 @@ export default function 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',
|
||||||
() => {
|
() => {
|
||||||
@ -232,4 +211,6 @@ export default function ImageGalleryPanel() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return renderImageGallery();
|
return renderImageGallery();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default memo(ImageGalleryPanel);
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { useAppDispatch } from 'app/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import promptToString from 'common/util/promptToString';
|
import promptToString from 'common/util/promptToString';
|
||||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||||
@ -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}>
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { useAppDispatch } from 'app/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
import promptToString from 'common/util/promptToString';
|
import promptToString from 'common/util/promptToString';
|
||||||
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
import { seedWeightsToString } from 'common/util/seedWeightPairs';
|
||||||
|
@ -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/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { isEqual } from 'lodash';
|
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}
|
||||||
/>
|
/>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { ImageType } from 'services/api';
|
import { ImageType } from 'services/api';
|
||||||
import { selectResultsEntities } from '../store/resultsSlice';
|
import { selectResultsEntities } from '../store/resultsSlice';
|
||||||
import { selectUploadsEntities } from '../store/uploadsSlice';
|
import { selectUploadsEntities } from '../store/uploadsSlice';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppSelector } from 'app/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { gallerySelector } from '../store/gallerySelectors';
|
import { gallerySelector } from '../store/gallerySelectors';
|
||||||
|
|
||||||
const selector = createSelector(gallerySelector, (gallery) => ({
|
const selector = createSelector(gallerySelector, (gallery) => ({
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
import { lightboxSelector } from 'features/lightbox/store/lightboxSelectors';
|
||||||
import { configSelector } from 'features/system/store/configSelectors';
|
import { configSelector } from 'features/system/store/configSelectors';
|
||||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
activeTabNameSelector,
|
activeTabNameSelector,
|
||||||
uiSelector,
|
uiSelector,
|
||||||
} from 'features/ui/store/uiSelectors';
|
} from 'features/ui/store/uiSelectors';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
selectResultsAll,
|
selectResultsAll,
|
||||||
selectResultsById,
|
selectResultsById,
|
||||||
@ -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/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';
|
|
||||||
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}`
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
||||||
import { Image } from 'app/invokeai';
|
import { Image } from 'app/types/invokeai';
|
||||||
import { invocationComplete } from 'services/events/actions';
|
import { invocationComplete } from 'services/events/actions';
|
||||||
|
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import {
|
import {
|
||||||
receivedResultImagesPage,
|
receivedResultImagesPage,
|
||||||
IMAGES_PER_PAGE,
|
IMAGES_PER_PAGE,
|
||||||
@ -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}`
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
||||||
import { Image } from 'app/invokeai';
|
import { Image } from 'app/types/invokeai';
|
||||||
|
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import {
|
import {
|
||||||
receivedUploadImagesPage,
|
receivedUploadImagesPage,
|
||||||
IMAGES_PER_PAGE,
|
IMAGES_PER_PAGE,
|
||||||
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, Flex } from '@chakra-ui/react';
|
import { Box, Flex } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
import CurrentImageButtons from 'features/gallery/components/CurrentImageButtons';
|
import CurrentImageButtons from 'features/gallery/components/CurrentImageButtons';
|
||||||
import ImageMetadataViewer from 'features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer';
|
import ImageMetadataViewer from 'features/gallery/components/ImageMetaDataViewer/ImageMetadataViewer';
|
||||||
@ -10,7 +10,7 @@ import { gallerySelector } from 'features/gallery/store/gallerySelectors';
|
|||||||
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
|
import { setIsLightboxOpen } from 'features/lightbox/store/lightboxSlice';
|
||||||
import { uiSelector } from 'features/ui/store/uiSelectors';
|
import { uiSelector } from 'features/ui/store/uiSelectors';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { BiExit } from 'react-icons/bi';
|
import { BiExit } from 'react-icons/bi';
|
||||||
import { TransformWrapper } from 'react-zoom-pan-pinch';
|
import { TransformWrapper } from 'react-zoom-pan-pinch';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { TransformComponent, useTransformContext } from 'react-zoom-pan-pinch';
|
import { TransformComponent, useTransformContext } from 'react-zoom-pan-pinch';
|
||||||
import * as InvokeAI from 'app/invokeai';
|
import * as InvokeAI from 'app/types/invokeai';
|
||||||
import { useGetUrl } from 'common/util/getUrl';
|
import { useGetUrl } from 'common/util/getUrl';
|
||||||
|
|
||||||
type ReactPanZoomProps = {
|
type ReactPanZoomProps = {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
export const lightboxSelector = createSelector(
|
export const lightboxSelector = createSelector(
|
||||||
(state: RootState) => state.lightbox,
|
(state: RootState) => state.lightbox,
|
||||||
|
@ -11,15 +11,15 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FaEllipsisV, FaPlus } from 'react-icons/fa';
|
import { FaEllipsisV, FaPlus } from 'react-icons/fa';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { nodeAdded } from '../store/nodesSlice';
|
import { nodeAdded } from '../store/nodesSlice';
|
||||||
import { cloneDeep, map } from 'lodash';
|
import { cloneDeep, map } from 'lodash-es';
|
||||||
import { RootState } from 'app/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useBuildInvocation } from '../hooks/useBuildInvocation';
|
import { useBuildInvocation } from '../hooks/useBuildInvocation';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { makeToast } from 'features/system/hooks/useToastWatcher';
|
import { makeToast } from 'features/system/hooks/useToastWatcher';
|
||||||
import { IAIIconButton } from 'exports';
|
|
||||||
import { AnyInvocationType } from 'services/events/types';
|
import { AnyInvocationType } from 'services/events/types';
|
||||||
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
|
||||||
const AddNodeMenu = () => {
|
const AddNodeMenu = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'reactflow/dist/style.css';
|
import 'reactflow/dist/style.css';
|
||||||
import { Tooltip, Badge, Flex } from '@chakra-ui/react';
|
import { Tooltip, Badge, Flex } from '@chakra-ui/react';
|
||||||
import { map } from 'lodash';
|
import { map } from 'lodash-es';
|
||||||
import { FIELDS } from '../types/constants';
|
import { FIELDS } from '../types/constants';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user