Compare commits

..

1 Commits

Author SHA1 Message Date
Millun Atluri
7487f96da2 Update diffuses to 0.25 2023-12-29 11:34:02 +11:00
1050 changed files with 23717 additions and 28599 deletions

View File

@@ -1,10 +1,10 @@
<div align="center">
![project hero](https://github.com/invoke-ai/InvokeAI/assets/31807370/6e3728c7-e90e-4711-905c-3b55844ff5be)
![project hero](https://github.com/invoke-ai/InvokeAI/assets/31807370/1a917d94-e099-4fa1-a70f-7dd8d0691018)
# Invoke - Professional Creative AI Tools for Visual Media
## To learn more about Invoke, or implement our Business solutions, visit [invoke.com](https://www.invoke.com/about)
# Invoke AI - Generative AI for Professional Creatives
## Professional Creative Tools for Stable Diffusion, Custom-Trained Models, and more.
To learn more about Invoke AI, get started instantly, or implement our Business solutions, visit [invoke.ai](https://invoke.ai)
[![discord badge]][discord link]
@@ -56,9 +56,7 @@ the foundation for multiple commercial products.
<div align="center">
![Highlighted Features - Canvas and Workflows](https://github.com/invoke-ai/InvokeAI/assets/31807370/708f7a82-084f-4860-bfbe-e2588c53548d)
![canvas preview](https://github.com/invoke-ai/InvokeAI/raw/main/docs/assets/canvas_preview.png)
</div>

View File

@@ -2,13 +2,10 @@
## Any environment variables supported by InvokeAI can be specified here,
## in addition to the examples below.
# HOST_INVOKEAI_ROOT is the path on the docker host's filesystem where InvokeAI will store data.
# INVOKEAI_ROOT is the path to a path on the local filesystem where InvokeAI will store data.
# Outputs will also be stored here by default.
# If relative, it will be relative to the docker directory in which the docker-compose.yml file is located
#HOST_INVOKEAI_ROOT=../../invokeai-data
# INVOKEAI_ROOT is the path to the root of the InvokeAI repository within the container.
# INVOKEAI_ROOT=~/invokeai
# This **must** be an absolute path.
INVOKEAI_ROOT=
# Get this value from your HuggingFace account settings page.
# HUGGING_FACE_HUB_TOKEN=

View File

@@ -21,9 +21,7 @@ x-invokeai: &invokeai
ports:
- "${INVOKEAI_PORT:-9090}:9090"
volumes:
- type: bind
source: ${HOST_INVOKEAI_ROOT:-${INVOKEAI_ROOT:-~/invokeai}}
target: ${INVOKEAI_ROOT:-/invokeai}
- ${INVOKEAI_ROOT:-~/invokeai}:${INVOKEAI_ROOT:-/invokeai}
- ${HF_HOME:-~/.cache/huggingface}:${HF_HOME:-/invokeai/.cache/huggingface}
# - ${INVOKEAI_MODELS_DIR:-${INVOKEAI_ROOT:-/invokeai/models}}
# - ${INVOKEAI_MODELS_CONFIG_PATH:-${INVOKEAI_ROOT:-/invokeai/configs/models.yaml}}

View File

@@ -36,8 +36,7 @@ To use a community workflow, download the the `.json` node graph file and load i
+ [Mask Operations](#mask-operations)
+ [Match Histogram](#match-histogram)
+ [Metadata-Linked](#metadata-linked-nodes)
+ [Negative Image](#negative-image)
+ [Nightmare Promptgen](#nightmare-promptgen)
+ [Negative Image](#negative-image)
+ [Oobabooga](#oobabooga)
+ [Prompt Tools](#prompt-tools)
+ [Remote Image](#remote-image)
@@ -347,13 +346,6 @@ Node Link: https://github.com/VeyDlin/negative-image-node
View:
</br><img src="https://raw.githubusercontent.com/VeyDlin/negative-image-node/master/.readme/node.png" width="500" />
--------------------------------
### Nightmare Promptgen
**Description:** Nightmare Prompt Generator - Uses a local text generation model to create unique imaginative (but usually nightmarish) prompts for InvokeAI. By default, it allows you to choose from some gpt-neo models I finetuned on over 2500 of my own InvokeAI prompts in Compel format, but you're able to add your own, as well. Offers support for replacing any troublesome words with a random choice from list you can also define.
**Node Link:** [https://github.com/gogurtenjoyer/nightmare-promptgen](https://github.com/gogurtenjoyer/nightmare-promptgen)
--------------------------------
### Oobabooga

View File

@@ -23,11 +23,10 @@ class DynamicPromptsResponse(BaseModel):
)
async def parse_dynamicprompts(
prompt: str = Body(description="The prompt to parse with dynamicprompts"),
max_prompts: int = Body(ge=1, le=10000, default=1000, description="The max number of prompts to generate"),
max_prompts: int = Body(default=1000, description="The max number of prompts to generate"),
combinatorial: bool = Body(default=True, description="Whether to use the combinatorial generator"),
) -> DynamicPromptsResponse:
"""Creates a batch process"""
max_prompts = min(max_prompts, 10000)
generator: Union[RandomPromptGenerator, CombinatorialPromptGenerator]
try:
error: Optional[str] = None

View File

@@ -24,10 +24,9 @@ from controlnet_aux import (
)
from controlnet_aux.util import HWC3, ade_palette
from PIL import Image
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator
from invokeai.app.invocations.primitives import ImageField, ImageOutput
from invokeai.app.invocations.util import validate_begin_end_step, validate_weights
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
from invokeai.app.shared.fields import FieldDescriptions
@@ -76,16 +75,17 @@ class ControlField(BaseModel):
resize_mode: CONTROLNET_RESIZE_VALUES = Field(default="just_resize", description="The resize mode to use")
@field_validator("control_weight")
@classmethod
def validate_control_weight(cls, v):
validate_weights(v)
"""Validate that all control weights in the valid range"""
if isinstance(v, list):
for i in v:
if i < -1 or i > 2:
raise ValueError("Control weights must be within -1 to 2 range")
else:
if v < -1 or v > 2:
raise ValueError("Control weights must be within -1 to 2 range")
return v
@model_validator(mode="after")
def validate_begin_end_step_percent(self):
validate_begin_end_step(self.begin_step_percent, self.end_step_percent)
return self
@invocation_output("control_output")
class ControlOutput(BaseInvocationOutput):
@@ -95,17 +95,17 @@ class ControlOutput(BaseInvocationOutput):
control: ControlField = OutputField(description=FieldDescriptions.control)
@invocation("controlnet", title="ControlNet", tags=["controlnet"], category="controlnet", version="1.1.1")
@invocation("controlnet", title="ControlNet", tags=["controlnet"], category="controlnet", version="1.1.0")
class ControlNetInvocation(BaseInvocation):
"""Collects ControlNet info to pass to other nodes"""
image: ImageField = InputField(description="The control image")
control_model: ControlNetModelField = InputField(description=FieldDescriptions.controlnet_model, input=Input.Direct)
control_weight: Union[float, List[float]] = InputField(
default=1.0, ge=-1, le=2, description="The weight given to the ControlNet"
default=1.0, description="The weight given to the ControlNet"
)
begin_step_percent: float = InputField(
default=0, ge=0, le=1, description="When the ControlNet is first applied (% of total steps)"
default=0, ge=-1, le=2, description="When the ControlNet is first applied (% of total steps)"
)
end_step_percent: float = InputField(
default=1, ge=0, le=1, description="When the ControlNet is last applied (% of total steps)"
@@ -113,17 +113,6 @@ class ControlNetInvocation(BaseInvocation):
control_mode: CONTROLNET_MODE_VALUES = InputField(default="balanced", description="The control mode used")
resize_mode: CONTROLNET_RESIZE_VALUES = InputField(default="just_resize", description="The resize mode used")
@field_validator("control_weight")
@classmethod
def validate_control_weight(cls, v):
validate_weights(v)
return v
@model_validator(mode="after")
def validate_begin_end_step_percent(self) -> "ControlNetInvocation":
validate_begin_end_step(self.begin_step_percent, self.end_step_percent)
return self
def invoke(self, context: InvocationContext) -> ControlOutput:
return ControlOutput(
control=ControlField(

View File

@@ -2,7 +2,7 @@ import os
from builtins import float
from typing import List, Union
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic import BaseModel, ConfigDict, Field
from invokeai.app.invocations.baseinvocation import (
BaseInvocation,
@@ -15,7 +15,6 @@ from invokeai.app.invocations.baseinvocation import (
invocation_output,
)
from invokeai.app.invocations.primitives import ImageField
from invokeai.app.invocations.util import validate_begin_end_step, validate_weights
from invokeai.app.shared.fields import FieldDescriptions
from invokeai.backend.model_management.models.base import BaseModelType, ModelType
from invokeai.backend.model_management.models.ip_adapter import get_ip_adapter_image_encoder_model_id
@@ -40,6 +39,7 @@ class IPAdapterField(BaseModel):
ip_adapter_model: IPAdapterModelField = Field(description="The IP-Adapter model to use.")
image_encoder_model: CLIPVisionModelField = Field(description="The name of the CLIP image encoder model.")
weight: Union[float, List[float]] = Field(default=1, description="The weight given to the ControlNet")
# weight: float = Field(default=1.0, ge=0, description="The weight of the IP-Adapter.")
begin_step_percent: float = Field(
default=0, ge=0, le=1, description="When the IP-Adapter is first applied (% of total steps)"
)
@@ -47,17 +47,6 @@ class IPAdapterField(BaseModel):
default=1, ge=0, le=1, description="When the IP-Adapter is last applied (% of total steps)"
)
@field_validator("weight")
@classmethod
def validate_ip_adapter_weight(cls, v):
validate_weights(v)
return v
@model_validator(mode="after")
def validate_begin_end_step_percent(self):
validate_begin_end_step(self.begin_step_percent, self.end_step_percent)
return self
@invocation_output("ip_adapter_output")
class IPAdapterOutput(BaseInvocationOutput):
@@ -65,7 +54,7 @@ class IPAdapterOutput(BaseInvocationOutput):
ip_adapter: IPAdapterField = OutputField(description=FieldDescriptions.ip_adapter, title="IP-Adapter")
@invocation("ip_adapter", title="IP-Adapter", tags=["ip_adapter", "control"], category="ip_adapter", version="1.1.1")
@invocation("ip_adapter", title="IP-Adapter", tags=["ip_adapter", "control"], category="ip_adapter", version="1.1.0")
class IPAdapterInvocation(BaseInvocation):
"""Collects IP-Adapter info to pass to other nodes."""
@@ -75,27 +64,18 @@ class IPAdapterInvocation(BaseInvocation):
description="The IP-Adapter model.", title="IP-Adapter Model", input=Input.Direct, ui_order=-1
)
# weight: float = InputField(default=1.0, description="The weight of the IP-Adapter.", ui_type=UIType.Float)
weight: Union[float, List[float]] = InputField(
default=1, description="The weight given to the IP-Adapter", title="Weight"
default=1, ge=-1, description="The weight given to the IP-Adapter", title="Weight"
)
begin_step_percent: float = InputField(
default=0, ge=0, le=1, description="When the IP-Adapter is first applied (% of total steps)"
default=0, ge=-1, le=2, description="When the IP-Adapter is first applied (% of total steps)"
)
end_step_percent: float = InputField(
default=1, ge=0, le=1, description="When the IP-Adapter is last applied (% of total steps)"
)
@field_validator("weight")
@classmethod
def validate_ip_adapter_weight(cls, v):
validate_weights(v)
return v
@model_validator(mode="after")
def validate_begin_end_step_percent(self):
validate_begin_end_step(self.begin_step_percent, self.end_step_percent)
return self
def invoke(self, context: InvocationContext) -> IPAdapterOutput:
# Lookup the CLIP Vision encoder that is intended to be used with the IP-Adapter model.
ip_adapter_info = context.services.model_manager.model_info(

View File

@@ -220,7 +220,7 @@ def get_scheduler(
title="Denoise Latents",
tags=["latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
category="latents",
version="1.5.1",
version="1.5.0",
)
class DenoiseLatentsInvocation(BaseInvocation):
"""Denoises noisy latents to decodable images"""
@@ -279,7 +279,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
ui_order=7,
)
cfg_rescale_multiplier: float = InputField(
title="CFG Rescale Multiplier", default=0, ge=0, lt=1, description=FieldDescriptions.cfg_rescale_multiplier
default=0, ge=0, lt=1, description=FieldDescriptions.cfg_rescale_multiplier
)
latents: Optional[LatentsField] = InputField(
default=None,

View File

@@ -1,6 +1,6 @@
from typing import Union
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from pydantic import BaseModel, ConfigDict, Field
from invokeai.app.invocations.baseinvocation import (
BaseInvocation,
@@ -14,7 +14,6 @@ from invokeai.app.invocations.baseinvocation import (
)
from invokeai.app.invocations.controlnet_image_processors import CONTROLNET_RESIZE_VALUES
from invokeai.app.invocations.primitives import ImageField
from invokeai.app.invocations.util import validate_begin_end_step, validate_weights
from invokeai.app.shared.fields import FieldDescriptions
from invokeai.backend.model_management.models.base import BaseModelType
@@ -38,17 +37,6 @@ class T2IAdapterField(BaseModel):
)
resize_mode: CONTROLNET_RESIZE_VALUES = Field(default="just_resize", description="The resize mode to use")
@field_validator("weight")
@classmethod
def validate_ip_adapter_weight(cls, v):
validate_weights(v)
return v
@model_validator(mode="after")
def validate_begin_end_step_percent(self):
validate_begin_end_step(self.begin_step_percent, self.end_step_percent)
return self
@invocation_output("t2i_adapter_output")
class T2IAdapterOutput(BaseInvocationOutput):
@@ -56,7 +44,7 @@ class T2IAdapterOutput(BaseInvocationOutput):
@invocation(
"t2i_adapter", title="T2I-Adapter", tags=["t2i_adapter", "control"], category="t2i_adapter", version="1.0.1"
"t2i_adapter", title="T2I-Adapter", tags=["t2i_adapter", "control"], category="t2i_adapter", version="1.0.0"
)
class T2IAdapterInvocation(BaseInvocation):
"""Collects T2I-Adapter info to pass to other nodes."""
@@ -73,7 +61,7 @@ class T2IAdapterInvocation(BaseInvocation):
default=1, ge=0, description="The weight given to the T2I-Adapter", title="Weight"
)
begin_step_percent: float = InputField(
default=0, ge=0, le=1, description="When the T2I-Adapter is first applied (% of total steps)"
default=0, ge=-1, le=2, description="When the T2I-Adapter is first applied (% of total steps)"
)
end_step_percent: float = InputField(
default=1, ge=0, le=1, description="When the T2I-Adapter is last applied (% of total steps)"
@@ -83,17 +71,6 @@ class T2IAdapterInvocation(BaseInvocation):
description="The resize mode applied to the T2I-Adapter input image so that it matches the target output size.",
)
@field_validator("weight")
@classmethod
def validate_ip_adapter_weight(cls, v):
validate_weights(v)
return v
@model_validator(mode="after")
def validate_begin_end_step_percent(self):
validate_begin_end_step(self.begin_step_percent, self.end_step_percent)
return self
def invoke(self, context: InvocationContext) -> T2IAdapterOutput:
return T2IAdapterOutput(
t2i_adapter=T2IAdapterField(

View File

@@ -1,14 +0,0 @@
from typing import Union
def validate_weights(weights: Union[float, list[float]]) -> None:
"""Validate that all control weights in the valid range"""
to_validate = weights if isinstance(weights, list) else [weights]
if any(i < -1 or i > 2 for i in to_validate):
raise ValueError("Control weights must be within -1 to 2 range")
def validate_begin_end_step(begin_step_percent: float, end_step_percent: float) -> None:
"""Validate that begin_step_percent is less than end_step_percent"""
if begin_step_percent >= end_step_percent:
raise ValueError("Begin step percent must be less than or equal to end step percent")

View File

@@ -13,7 +13,6 @@ from safetensors.torch import load_file
from transformers import CLIPTextModel, CLIPTokenizer
from invokeai.app.shared.models import FreeUConfig
from invokeai.backend.model_management.model_load_optimizations import skip_torch_weight_init
from .models.lora import LoRAModel
@@ -212,12 +211,8 @@ class ModelPatcher:
for i in range(ti_embedding.shape[0]):
new_tokens_added += ti_tokenizer.add_tokens(_get_trigger(ti_name, i))
# Modify text_encoder.
# resize_token_embeddings(...) constructs a new torch.nn.Embedding internally. Initializing the weights of
# this embedding is slow and unnecessary, so we wrap this step in skip_torch_weight_init() to save some
# time.
with skip_torch_weight_init():
text_encoder.resize_token_embeddings(init_tokens_count + new_tokens_added, pad_to_multiple_of)
# modify text_encoder
text_encoder.resize_token_embeddings(init_tokens_count + new_tokens_added, pad_to_multiple_of)
model_embeddings = text_encoder.get_input_embeddings()
for ti_name, ti in ti_list:

View File

@@ -44,7 +44,7 @@ def torch_dtype(device: torch.device) -> torch.dtype:
if config.full_precision:
return torch.float32
if choose_precision(device) == "float16":
return torch.bfloat16 if device.type == "cuda" else torch.float16
return torch.float16
else:
return torch.float32

View File

@@ -68,9 +68,12 @@ def welcome(latest_release: str, latest_prerelease: str):
yield ""
yield "This script will update InvokeAI to the latest release, or to the development version of your choice."
yield ""
yield "When updating to an arbitrary tag or branch, be aware that the front end may be mismatched to the backend,"
yield "making the web frontend unusable. Please downgrade to the latest release if this happens."
yield ""
yield "[bold yellow]Options:"
yield f"""[1] Update to the latest [bold]official release[/bold] ([italic]{latest_release}[/italic])
[2] Update to the latest [bold]pre-release[/bold] (may be buggy, database backups are recommended before installation; caveat emptor!) ([italic]{latest_prerelease}[/italic])
[2] Update to the latest [bold]pre-release[/bold] (may be buggy; caveat emptor!) ([italic]{latest_prerelease}[/italic])
[3] Manually enter the [bold]version[/bold] you wish to update to"""
console.rule()

View File

@@ -7,4 +7,4 @@ stats.html
index.html
.yarn/
*.scss
src/services/api/schema.ts
src/services/api/schema.d.ts

View File

@@ -28,16 +28,12 @@ module.exports = {
'i18next',
'path',
'unused-imports',
'simple-import-sort',
'eslint-plugin-import',
// These rules are too strict for normal usage, but are useful for optimizing rerenders
// '@arthurgeron/react-usememo',
],
root: true,
rules: {
'path/no-relative-imports': ['error', { maxDepth: 0 }],
curly: 'error',
'i18next/no-literal-string': 'warn',
'i18next/no-literal-string': 2,
'react/jsx-no-bind': ['error', { allowBind: true }],
'react/jsx-curly-brace-presence': [
'error',
@@ -47,7 +43,6 @@ module.exports = {
'no-var': 'error',
'brace-style': 'error',
'prefer-template': 'error',
'import/no-duplicates': 'error',
radix: 'error',
'space-before-blocks': 'error',
'import/prefer-default-export': 'off',
@@ -62,18 +57,6 @@ module.exports = {
argsIgnorePattern: '^_',
},
],
// These rules are too strict for normal usage, but are useful for optimizing rerenders
// '@arthurgeron/react-usememo/require-usememo': [
// 'warn',
// {
// strict: false,
// checkHookReturnObject: false,
// fix: { addImports: true },
// checkHookCalls: false,
// },
// ],
// '@arthurgeron/react-usememo/require-memo': 'warn',
'@typescript-eslint/ban-ts-comment': 'warn',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-empty-interface': [
@@ -82,26 +65,7 @@ module.exports = {
allowSingleExtends: true,
},
],
'@typescript-eslint/consistent-type-imports': [
'error',
{
prefer: 'type-imports',
fixStyle: 'separate-type-imports',
disallowTypeAnnotations: true,
},
],
'@typescript-eslint/no-import-type-side-effects': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
},
overrides: [
{
files: ['*.stories.tsx'],
rules: {
'i18next/no-literal-string': 'off',
},
},
],
settings: {
react: {
version: 'detect',

View File

@@ -9,8 +9,7 @@ index.html
.yarn/
.yalc/
*.scss
src/services/api/schema.ts
src/services/api/schema.d.ts
static/
src/theme/css/overlayscrollbars.css
src/theme_/css/overlayscrollbars.css
pnpm-lock.yaml

View File

@@ -1,25 +0,0 @@
import { PropsWithChildren, memo, useEffect } from 'react';
import { modelChanged } from '../src/features/parameters/store/generationSlice';
import { useAppDispatch } from '../src/app/store/storeHooks';
import { useGlobalModifiersInit } from '../src/common/hooks/useGlobalModifiers';
/**
* Initializes some state for storybook. Must be in a different component
* so that it is run inside the redux context.
*/
export const ReduxInit = memo((props: PropsWithChildren) => {
const dispatch = useAppDispatch();
useGlobalModifiersInit();
useEffect(() => {
dispatch(
modelChanged({
model_name: 'test_model',
base_model: 'sd-1',
model_type: 'main',
})
);
}, []);
return props.children;
});
ReduxInit.displayName = 'ReduxInit';

View File

@@ -6,7 +6,6 @@ const config: StorybookConfig = {
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-storysource',
],
framework: {
name: '@storybook/react-vite',

View File

@@ -1,17 +1,16 @@
import { Preview } from '@storybook/react';
import { themes } from '@storybook/theming';
import i18n from 'i18next';
import React from 'react';
import { initReactI18next } from 'react-i18next';
import { Provider } from 'react-redux';
import GlobalHotkeys from '../src/app/components/GlobalHotkeys';
import ThemeLocaleProvider from '../src/app/components/ThemeLocaleProvider';
import { $baseUrl } from '../src/app/store/nanostores/baseUrl';
import { createStore } from '../src/app/store/store';
import { Container } from '@chakra-ui/react';
// TODO: Disabled for IDE performance issues with our translation JSON
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import translationEN from '../public/locales/en.json';
import { ReduxInit } from './ReduxInit';
i18n.use(initReactI18next).init({
lng: 'en',
@@ -26,21 +25,17 @@ i18n.use(initReactI18next).init({
});
const store = createStore(undefined, false);
$baseUrl.set('http://localhost:9090');
const preview: Preview = {
decorators: [
(Story) => {
return (
<Provider store={store}>
<ThemeLocaleProvider>
<ReduxInit>
<Story />
</ReduxInit>
</ThemeLocaleProvider>
</Provider>
);
},
(Story) => (
<Provider store={store}>
<ThemeLocaleProvider>
<GlobalHotkeys />
<Story />
</ThemeLocaleProvider>
</Provider>
),
],
parameters: {
docs: {

View File

@@ -1,15 +0,0 @@
{
"entry": ["src/main.tsx"],
"extensions": [".ts", ".tsx"],
"ignorePatterns": [
"**/node_modules/**",
"dist/**",
"public/**",
"**/*.stories.tsx",
"config/**"
],
"ignoreUnresolved": [],
"ignoreUnimported": ["src/i18.d.ts", "vite.config.ts", "src/vite-env.d.ts"],
"respectGitignore": true,
"ignoreUnused": []
}

View File

@@ -1,6 +1,6 @@
import react from '@vitejs/plugin-react-swc';
import { visualizer } from 'rollup-plugin-visualizer';
import type { PluginOption, UserConfig } from 'vite';
import { PluginOption, UserConfig } from 'vite';
import eslint from 'vite-plugin-eslint';
import tsconfigPaths from 'vite-tsconfig-paths';

View File

@@ -1,6 +1,5 @@
import type { UserConfig } from 'vite';
import { commonPlugins } from './common.mjs';
import { UserConfig } from 'vite';
import { commonPlugins } from './common';
export const appConfig: UserConfig = {
base: './',

View File

@@ -1,9 +1,8 @@
import path from 'path';
import type { UserConfig } from 'vite';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import { UserConfig } from 'vite';
import dts from 'vite-plugin-dts';
import { commonPlugins } from './common.mjs';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import { commonPlugins } from './common';
export const packageConfig: UserConfig = {
base: './',

View File

@@ -1,4 +0,0 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="6" fill="#E6FD13"/>
<path d="M19.2378 10.9H25V7H7V10.9H12.7622L19.2378 21.1H25V25H7V21.1H12.7622" stroke="#181818" stroke-width="1.5"/>
</svg>

Before

Width:  |  Height:  |  Size: 272 B

View File

@@ -1,3 +0,0 @@
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M29.1951 10.6667H42V2H2V10.6667H14.8049L29.1951 33.3333H42V42H2V33.3333H14.8049" stroke="#E6FD13" stroke-width="2.8"/>
</svg>

Before

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -1,27 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="shortcut icon" type="icon" href="favicon.ico" />
<style>
html,
body {
padding: 0;
margin: 0;
}
</style>
</head>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="mask-icon" type="icon" href="favicon-outline.svg" color="#E6FD13" sizes="any" />
<link rel="icon" type="icon" href="favicon-key.svg" />
<style>
html,
body {
padding: 0;
margin: 0;
}
</style>
</head>
<body dir="ltr">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
<body dir="ltr">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -31,17 +31,13 @@
"lint": "concurrently -g -n eslint,prettier,tsc,madge -c cyan,green,magenta,yellow \"pnpm run lint:eslint\" \"pnpm run lint:prettier\" \"pnpm run lint:tsc\" \"pnpm run lint:madge\"",
"fix": "eslint --fix . && prettier --log-level warn --write .",
"preinstall": "npx only-allow pnpm",
"postinstall": "pnpm run theme",
"postinstall": "patch-package && pnpm run theme",
"theme": "chakra-cli tokens src/theme/theme.ts",
"theme:watch": "chakra-cli tokens src/theme/theme.ts --watch",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"unimported": "npx unimported"
"build-storybook": "storybook build"
},
"madge": {
"excludeRegExp": [
"^index.ts$"
],
"detectiveOptions": {
"ts": {
"skipTypeImports": true
@@ -57,56 +53,56 @@
"@chakra-ui/layout": "^2.3.1",
"@chakra-ui/portal": "^2.1.0",
"@chakra-ui/react": "^2.8.2",
"@chakra-ui/react-use-size": "^2.1.0",
"@chakra-ui/styled-system": "^2.9.2",
"@chakra-ui/theme-tools": "^2.1.2",
"@dagrejs/graphlib": "^2.1.13",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.11.3",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource-variable/inter": "^5.0.16",
"@mantine/form": "6.0.21",
"@mantine/core": "^6.0.19",
"@mantine/form": "^6.0.19",
"@mantine/hooks": "^6.0.19",
"@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "2.0.1",
"@reduxjs/toolkit": "^2.0.1",
"@roarr/browser-log-writer": "^1.3.0",
"chakra-react-select": "^4.7.6",
"@storybook/manager-api": "^7.6.4",
"@storybook/theming": "^7.6.4",
"compare-versions": "^6.1.0",
"dateformat": "^5.0.3",
"framer-motion": "^10.16.16",
"i18next": "^23.7.13",
"framer-motion": "^10.16.15",
"i18next": "^23.7.8",
"i18next-http-backend": "^2.4.2",
"idb-keyval": "^6.2.1",
"konva": "^9.3.0",
"konva": "^9.2.3",
"lodash-es": "^4.17.21",
"nanostores": "^0.9.5",
"new-github-issue-url": "^1.0.0",
"overlayscrollbars": "^2.4.6",
"overlayscrollbars": "^2.4.5",
"overlayscrollbars-react": "^0.5.3",
"patch-package": "^8.0.0",
"query-string": "^8.1.0",
"react": "^18.2.0",
"react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-error-boundary": "^4.0.12",
"react-hook-form": "^7.49.2",
"react-error-boundary": "^4.0.11",
"react-hotkeys-hook": "4.4.1",
"react-i18next": "^14.0.0",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
"react-konva": "^18.2.10",
"react-redux": "9.0.4",
"react-resizable-panels": "^1.0.7",
"react-select": "5.8.0",
"react-textarea-autosize": "^8.5.3",
"react-redux": "^9.0.2",
"react-resizable-panels": "^0.0.55",
"react-use": "^17.4.2",
"react-virtuoso": "^4.6.2",
"reactflow": "^11.10.1",
"redux-dynamic-middlewares": "^2.2.0",
"redux-remember": "^5.0.1",
"redux-remember": "^5.0.0",
"roarr": "^7.21.0",
"serialize-error": "^11.0.3",
"socket.io-client": "^4.7.2",
"type-fest": "^4.9.0",
"type-fest": "^4.8.3",
"use-debounce": "^10.0.0",
"use-image": "^1.1.1",
"uuid": "^9.0.1",
@@ -121,56 +117,44 @@
"ts-toolbelt": "^9.6.0"
},
"devDependencies": {
"@arthurgeron/eslint-plugin-react-usememo": "^2.2.2",
"@chakra-ui/cli": "^2.4.1",
"@storybook/addon-docs": "^7.6.6",
"@storybook/addon-essentials": "^7.6.6",
"@storybook/addon-interactions": "^7.6.6",
"@storybook/addon-links": "^7.6.6",
"@storybook/addon-storysource": "^7.6.6",
"@storybook/blocks": "^7.6.6",
"@storybook/manager-api": "^7.6.6",
"@storybook/react": "^7.6.6",
"@storybook/react-vite": "^7.6.6",
"@storybook/test": "^7.6.6",
"@storybook/theming": "^7.6.6",
"@storybook/addon-essentials": "^7.6.4",
"@storybook/addon-interactions": "^7.6.4",
"@storybook/addon-links": "^7.6.4",
"@storybook/blocks": "^7.6.4",
"@storybook/react": "^7.6.4",
"@storybook/react-vite": "^7.6.4",
"@storybook/test": "^7.6.4",
"@types/dateformat": "^5.0.2",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.10.6",
"@types/react": "^18.2.46",
"@types/react-dom": "^18.2.18",
"@types/node": "^20.9.0",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.17",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"@typescript-eslint/eslint-plugin": "^6.13.2",
"@typescript-eslint/parser": "^6.13.2",
"@vitejs/plugin-react-swc": "^3.5.0",
"concurrently": "^8.2.2",
"eslint": "^8.56.0",
"eslint": "^8.55.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-i18next": "^6.0.3",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-path": "^1.2.3",
"eslint-plugin-path": "^1.2.2",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-storybook": "^0.6.15",
"eslint-plugin-unused-imports": "^3.0.0",
"madge": "^6.1.0",
"openapi-types": "^12.1.3",
"openapi-typescript": "^6.7.3",
"prettier": "^3.1.1",
"rollup-plugin-visualizer": "^5.12.0",
"storybook": "^7.6.6",
"openapi-typescript": "^6.7.2",
"prettier": "^3.1.0",
"rollup-plugin-visualizer": "^5.10.0",
"storybook": "^7.6.4",
"ts-toolbelt": "^9.6.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-css-injected-by-js": "^3.3.1",
"vite-plugin-dts": "^3.7.0",
"vite": "^4.5.1",
"vite-plugin-css-injected-by-js": "^3.3.0",
"vite-plugin-dts": "^3.6.4",
"vite-plugin-eslint": "^1.8.1",
"vite-tsconfig-paths": "^4.2.3"
},
"pnpm": {
"patchedDependencies": {
"reselect@5.0.1": "patches/reselect@5.0.1.patch"
}
"vite-tsconfig-paths": "^4.2.2"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -83,6 +83,10 @@
"title": "خيارات التثبيت",
"desc": "ثبت لوحة الخيارات"
},
"toggleViewer": {
"title": "تبديل العارض",
"desc": "فتح وإغلاق مشاهد الصور"
},
"toggleGallery": {
"title": "تبديل المعرض",
"desc": "فتح وإغلاق درابزين المعرض"
@@ -143,6 +147,10 @@
"title": "الصورة التالية",
"desc": "عرض الصورة التالية في الصالة"
},
"toggleGalleryPin": {
"title": "تبديل تثبيت الصالة",
"desc": "يثبت ويفتح تثبيت الصالة على الواجهة الرسومية"
},
"increaseGalleryThumbSize": {
"title": "زيادة حجم صورة الصالة",
"desc": "يزيد حجم الصور المصغرة في الصالة"

View File

@@ -168,6 +168,10 @@
"title": "Optionen anheften",
"desc": "Anheften des Optionsfeldes"
},
"toggleViewer": {
"title": "Bildbetrachter umschalten",
"desc": "Bildbetrachter öffnen und schließen"
},
"toggleGallery": {
"title": "Galerie umschalten",
"desc": "Öffnen und Schließen des Galerie-Schubfachs"
@@ -228,6 +232,10 @@
"title": "Nächstes Bild",
"desc": "Nächstes Bild in Galerie anzeigen"
},
"toggleGalleryPin": {
"title": "Galerie anheften umschalten",
"desc": "Heftet die Galerie an die Benutzeroberfläche bzw. löst die sie"
},
"increaseGalleryThumbSize": {
"title": "Größe der Galeriebilder erhöhen",
"desc": "Vergrößert die Galerie-Miniaturansichten"
@@ -752,6 +760,7 @@
"w": "W",
"addControlNet": "$t(common.controlNet) hinzufügen",
"none": "Kein",
"incompatibleBaseModel": "Inkompatibles Basismodell:",
"enableControlnet": "Aktiviere ControlNet",
"detectResolution": "Auflösung erkennen",
"controlNetT2IMutexDesc": "$t(common.controlNet) und $t(common.t2iAdapter) zur gleichen Zeit wird nicht unterstützt.",

View File

@@ -50,33 +50,9 @@
"uncategorized": "Uncategorized",
"downloadBoard": "Download Board"
},
"accordions": {
"generation": {
"title": "Generation",
"modelTab": "Model",
"conceptsTab": "Concepts"
},
"image": {
"title": "Image"
},
"advanced": {
"title": "Advanced"
},
"control": {
"title": "Control",
"controlAdaptersTab": "Control Adapters",
"ipTab": "Image Prompts"
},
"compositing": {
"title": "Compositing",
"coherenceTab": "Coherence Pass",
"infillTab": "Infill"
}
},
"common": {
"accept": "Accept",
"advanced": "Advanced",
"advancedOptions": "Advanced Options",
"ai": "ai",
"areYouSure": "Are you sure?",
"auto": "Auto",
@@ -86,7 +62,6 @@
"copyError": "$t(gallery.copy) Error",
"close": "Close",
"on": "On",
"or": "or",
"checkpoint": "Checkpoint",
"communityLabel": "Community",
"controlNet": "ControlNet",
@@ -104,7 +79,6 @@
"file": "File",
"folder": "Folder",
"format": "format",
"free": "Free",
"generate": "Generate",
"githubLabel": "Github",
"hotkeysLabel": "Hotkeys",
@@ -157,7 +131,6 @@
"save": "Save",
"saveAs": "Save As",
"settingsLabel": "Settings",
"preferencesLabel": "Preferences",
"simple": "Simple",
"somethingWentWrong": "Something went wrong",
"statusConnected": "Connected",
@@ -248,6 +221,7 @@
"colorMapTileSize": "Tile Size",
"importImageFromCanvas": "Import Image From Canvas",
"importMaskFromCanvas": "Import Mask From Canvas",
"incompatibleBaseModel": "Incompatible base model:",
"lineart": "Lineart",
"lineartAnime": "Lineart Anime",
"lineartAnimeDescription": "Anime-style lineart processing",
@@ -272,7 +246,6 @@
"prompt": "Prompt",
"resetControlImage": "Reset Control Image",
"resize": "Resize",
"resizeSimple": "Resize (Simple)",
"resizeMode": "Resize Mode",
"safe": "Safe",
"saveControlImage": "Save Control Image",
@@ -311,7 +284,7 @@
"queue": "Queue",
"queueFront": "Add to Front of Queue",
"queueBack": "Add to Queue",
"queueCountPrediction": "{{promptsCount}} prompts × {{iterations}} iterations -> {{count}} generations",
"queueCountPrediction": "Add {{predicted}} to Queue",
"queueMaxExceeded": "Max of {{max_queue_size}} exceeded, would skip {{skip}}",
"queuedCount": "{{pending}} Pending",
"queueTotal": "{{total}} Total",
@@ -367,8 +340,7 @@
"back": "back",
"batchFailedToQueue": "Failed to Queue Batch",
"graphQueued": "Graph queued",
"graphFailedToQueue": "Failed to queue graph",
"openQueue": "Open Queue"
"graphFailedToQueue": "Failed to queue graph"
},
"invocationCache": {
"invocationCache": "Invocation Cache",
@@ -428,9 +400,6 @@
"problemDeletingImagesDesc": "One or more images could not be deleted"
},
"hotkeys": {
"searchHotkeys": "Search Hotkeys",
"clearSearch": "Clear Search",
"noHotkeysFound": "No Hotkeys Found",
"acceptStagingImage": {
"desc": "Accept Current Staging Area Image",
"title": "Accept Staging Image"
@@ -439,15 +408,11 @@
"desc": "Opens the add node menu",
"title": "Add Nodes"
},
"appHotkeys": "App",
"appHotkeys": "App Hotkeys",
"cancel": {
"desc": "Cancel current queue item",
"desc": "Cancel image generation",
"title": "Cancel"
},
"cancelAndClear": {
"desc": "Cancel current queue item and clear all pending items",
"title": "Cancel and Clear"
},
"changeTabs": {
"desc": "Switch to another workspace",
"title": "Change Tabs"
@@ -504,8 +469,8 @@
"desc": "Focus the prompt input area",
"title": "Focus Prompt"
},
"galleryHotkeys": "Gallery",
"generalHotkeys": "General",
"galleryHotkeys": "Gallery Hotkeys",
"generalHotkeys": "General Hotkeys",
"hideMask": {
"desc": "Hide and unhide mask",
"title": "Hide Mask"
@@ -526,7 +491,7 @@
"desc": "Generate an image",
"title": "Invoke"
},
"keyboardShortcuts": "Hotkeys",
"keyboardShortcuts": "Keyboard Shortcuts",
"maximizeWorkSpace": {
"desc": "Close panels and maximize work area",
"title": "Maximize Workspace"
@@ -547,7 +512,7 @@
"desc": "Next Staging Area Image",
"title": "Next Staging Image"
},
"nodesHotkeys": "Nodes",
"nodesHotkeys": "Nodes Hotkeys",
"pinOptions": {
"desc": "Pin the options panel",
"title": "Pin Options"
@@ -616,31 +581,31 @@
"desc": "Open and close the gallery drawer",
"title": "Toggle Gallery"
},
"toggleOptions": {
"desc": "Open and close the options panel",
"title": "Toggle Options"
},
"toggleOptionsAndGallery": {
"desc": "Open and close the options and gallery panels",
"title": "Toggle Options and Gallery"
},
"resetOptionsAndGallery": {
"desc": "Resets the options and gallery panels",
"title": "Reset Options and Gallery"
"toggleGalleryPin": {
"desc": "Pins and unpins the gallery to the UI",
"title": "Toggle Gallery Pin"
},
"toggleLayer": {
"desc": "Toggles mask/base layer selection",
"title": "Toggle Layer"
},
"toggleOptions": {
"desc": "Open and close the options panel",
"title": "Toggle Options"
},
"toggleSnap": {
"desc": "Toggles Snap to Grid",
"title": "Toggle Snap"
},
"toggleViewer": {
"desc": "Open and close Image Viewer",
"title": "Toggle Viewer"
},
"undoStroke": {
"desc": "Undo a brush stroke",
"title": "Undo Stroke"
},
"unifiedCanvasHotkeys": "Unified Canvas",
"unifiedCanvasHotkeys": "Unified Canvas Hotkeys",
"upscale": {
"desc": "Upscale the current image",
"title": "Upscale"
@@ -823,23 +788,17 @@
},
"models": {
"addLora": "Add LoRA",
"allLoRAsAdded": "All LoRAs added",
"loraAlreadyAdded": "LoRA already added",
"esrganModel": "ESRGAN Model",
"loading": "loading",
"incompatibleBaseModel": "Incompatible base model",
"noMainModelSelected": "No main model selected",
"noLoRAsAvailable": "No LoRAs available",
"noLoRAsLoaded": "No LoRAs Loaded",
"noMatchingLoRAs": "No matching LoRAs",
"noMatchingModels": "No matching Models",
"noModelsAvailable": "No models available",
"lora": "LoRA",
"selectLoRA": "Select a LoRA",
"selectModel": "Select a Model",
"noLoRAsInstalled": "No LoRAs installed",
"noRefinerModelsInstalled": "No SDXL Refiner models installed",
"defaultVAE": "Default VAE"
"noRefinerModelsInstalled": "No SDXL Refiner models installed"
},
"nodes": {
"addNode": "Add Node",
@@ -1078,14 +1037,8 @@
"prototypeDesc": "This invocation is a prototype. It may have breaking changes during app updates and may be removed at any time."
},
"parameters": {
"aspect": "Aspect",
"aspectRatio": "Aspect Ratio",
"aspectRatioFree": "Free",
"lockAspectRatio": "Lock Aspect Ratio",
"swapDimensions": "Swap Dimensions",
"setToOptimalSize": "Optimize size for model",
"setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (may be too small)",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (may be too large)",
"boundingBoxHeader": "Bounding Box",
"boundingBoxHeight": "Bounding Box Height",
"boundingBoxWidth": "Bounding Box Width",
@@ -1124,7 +1077,6 @@
"imageFit": "Fit Initial Image To Output Size",
"images": "Images",
"imageToImage": "Image to Image",
"imageSize": "Image Size",
"img2imgStrength": "Image To Image Strength",
"infillMethod": "Infill Method",
"infillScalingHeader": "Infill and Scaling",
@@ -1139,7 +1091,7 @@
"noControlImageForControlAdapter": "Control Adapter #{{number}} has no control image",
"noInitialImageSelected": "No initial image selected",
"noModelForControlAdapter": "Control Adapter #{{number}} has no model selected.",
"incompatibleBaseModelForControlAdapter": "Control Adapter #{{number}} model is incompatible with main model.",
"incompatibleBaseModelForControlAdapter": "Control Adapter #{{number}} model is invalid with main model.",
"noModelSelected": "No model selected",
"noPrompts": "No prompts generated",
"noNodesInGraph": "No nodes in graph",
@@ -1175,8 +1127,8 @@
"seamCorrectionHeader": "Seam Correction",
"seamHighThreshold": "High",
"seamlessTiling": "Seamless Tiling",
"seamlessXAxis": "Seamless Tiling X Axis",
"seamlessYAxis": "Seamless Tiling Y Axis",
"seamlessXAxis": "X Axis",
"seamlessYAxis": "Y Axis",
"seamlessX": "Seamless X",
"seamlessY": "Seamless Y",
"seamlessX&Y": "Seamless X & Y",
@@ -1219,7 +1171,6 @@
},
"dynamicPrompts": {
"combinatorial": "Combinatorial Generation",
"showDynamicPrompts": "Show Dynamic Prompts",
"dynamicPrompts": "Dynamic Prompts",
"enableDynamicPrompts": "Enable Dynamic Prompts",
"maxPrompts": "Max Prompts",
@@ -1232,13 +1183,11 @@
"perIterationDesc": "Use a different seed for each iteration",
"perPromptLabel": "Seed per Image",
"perPromptDesc": "Use a different seed for each image"
},
"loading": "Generating Dynamic Prompts..."
}
},
"sdxl": {
"cfgScale": "CFG Scale",
"concatPromptStyle": "Linking Prompt & Style",
"freePromptStyle": "Manual Style Prompting",
"concatPromptStyle": "Concatenate Prompt & Style",
"denoisingStrength": "Denoising Strength",
"loading": "Loading...",
"negAestheticScore": "Negative Aesthetic Score",

View File

@@ -127,6 +127,10 @@
"title": "Fijar opciones",
"desc": "Fijar el panel de opciones"
},
"toggleViewer": {
"title": "Alternar visor",
"desc": "Mostar y ocultar el visor de imágenes"
},
"toggleGallery": {
"title": "Alternar galería",
"desc": "Mostar y ocultar la galería de imágenes"
@@ -187,6 +191,10 @@
"title": "Imagen siguiente",
"desc": "Muetra la imagen siguiente en la galería"
},
"toggleGalleryPin": {
"title": "Alternar fijado de galería",
"desc": "Fijar o desfijar la galería en la interfaz"
},
"increaseGalleryThumbSize": {
"title": "Aumentar imagen en galería",
"desc": "Aumenta el tamaño de las miniaturas de la galería"

View File

@@ -96,6 +96,10 @@
"title": "Epinglage des options",
"desc": "Epingler le panneau d'options"
},
"toggleViewer": {
"title": "Affichage de la visionneuse",
"desc": "Afficher et masquer la visionneuse d'image"
},
"toggleGallery": {
"title": "Affichage de la galerie",
"desc": "Afficher et masquer la galerie"
@@ -156,6 +160,10 @@
"title": "Image suivante",
"desc": "Afficher l'image suivante dans la galerie"
},
"toggleGalleryPin": {
"title": "Activer/désactiver l'épinglage de la galerie",
"desc": "Épingle ou dépingle la galerie à l'interface"
},
"increaseGalleryThumbSize": {
"title": "Augmenter la taille des miniatures de la galerie",
"desc": "Augmente la taille des miniatures de la galerie"

View File

@@ -192,6 +192,10 @@
"title": "הצמד הגדרות",
"desc": "הצמד את פאנל ההגדרות"
},
"toggleViewer": {
"title": "הצג את חלון ההצגה",
"desc": "פתח וסגור את מציג התמונות"
},
"changeTabs": {
"title": "החלף לשוניות",
"desc": "החלף לאיזור עבודה אחר"
@@ -232,6 +236,10 @@
"title": "תמונה קודמת",
"desc": "הצג את התמונה הקודמת בגלריה"
},
"toggleGalleryPin": {
"title": "הצג את מצמיד הגלריה",
"desc": "הצמדה וביטול הצמדה של הגלריה לממשק המשתמש"
},
"decreaseGalleryThumbSize": {
"title": "הקטנת גודל תמונת גלריה",
"desc": "מקטין את גודל התמונות הממוזערות של הגלריה"

View File

@@ -114,9 +114,7 @@
"nextPage": "Pagina successiva",
"saveAs": "Salva come",
"unsaved": "Non salvato",
"direction": "Direzione",
"advancedOptions": "Opzioni avanzate",
"free": "Libero"
"direction": "Direzione"
},
"gallery": {
"generations": "Generazioni",
@@ -134,7 +132,7 @@
"noImagesInGallery": "Nessuna immagine da visualizzare",
"deleteImage": "Elimina l'immagine",
"deleteImagePermanent": "Le immagini eliminate non possono essere ripristinate.",
"deleteImageBin": "Le immagini eliminate verranno spostate nel cestino del tuo sistema operativo.",
"deleteImageBin": "Le immagini eliminate verranno spostate nel Cestino del tuo sistema operativo.",
"assets": "Risorse",
"autoAssignBoardOnClick": "Assegna automaticamente la bacheca al clic",
"featuresWillReset": "Se elimini questa immagine, quelle funzionalità verranno immediatamente ripristinate.",
@@ -159,18 +157,18 @@
"problemDeletingImages": "Problema durante l'eliminazione delle immagini"
},
"hotkeys": {
"keyboardShortcuts": "Tasti di scelta rapida",
"appHotkeys": "Applicazione",
"generalHotkeys": "Generale",
"galleryHotkeys": "Galleria",
"unifiedCanvasHotkeys": "Tela Unificata",
"keyboardShortcuts": "Tasti rapidi",
"appHotkeys": "Tasti di scelta rapida dell'applicazione",
"generalHotkeys": "Tasti di scelta rapida generali",
"galleryHotkeys": "Tasti di scelta rapida della galleria",
"unifiedCanvasHotkeys": "Tasti di scelta rapida Tela Unificata",
"invoke": {
"title": "Invoke",
"desc": "Genera un'immagine"
},
"cancel": {
"title": "Annulla",
"desc": "Annulla l'elemento della coda corrente"
"desc": "Annulla la generazione dell'immagine"
},
"focusPrompt": {
"title": "Metti a fuoco il Prompt",
@@ -184,8 +182,12 @@
"title": "Appunta le opzioni",
"desc": "Blocca il pannello delle opzioni"
},
"toggleViewer": {
"title": "Attiva/disattiva visualizzatore",
"desc": "Apre e chiude il visualizzatore immagini"
},
"toggleGallery": {
"title": "Attiva/disattiva galleria",
"title": "Attiva/disattiva Galleria",
"desc": "Apre e chiude il pannello della galleria"
},
"maximizeWorkSpace": {
@@ -244,6 +246,10 @@
"title": "Immagine successiva",
"desc": "Visualizza l'immagine successiva nella galleria"
},
"toggleGalleryPin": {
"title": "Attiva/disattiva il blocco della galleria",
"desc": "Blocca/sblocca la galleria dall'interfaccia utente"
},
"increaseGalleryThumbSize": {
"title": "Aumenta dimensione immagini nella galleria",
"desc": "Aumenta la dimensione delle miniature della galleria"
@@ -356,26 +362,11 @@
"title": "Accetta l'immagine della sessione",
"desc": "Accetta l'immagine dell'area della sessione corrente"
},
"nodesHotkeys": "Nodi",
"nodesHotkeys": "Tasti di scelta rapida dei Nodi",
"addNodes": {
"title": "Aggiungi Nodi",
"desc": "Apre il menu Aggiungi Nodi"
},
"cancelAndClear": {
"desc": "Annulla l'elemento della coda corrente e cancella tutti gli elementi in sospeso",
"title": "Annulla e cancella"
},
"resetOptionsAndGallery": {
"title": "Ripristina Opzioni e Galleria",
"desc": "Reimposta le opzioni e i pannelli della galleria"
},
"searchHotkeys": "Cerca tasti di scelta rapida",
"noHotkeysFound": "Nessun tasto di scelta rapida trovato",
"toggleOptionsAndGallery": {
"desc": "Apre e chiude le opzioni e i pannelli della galleria",
"title": "Attiva/disattiva le Opzioni e la Galleria"
},
"clearSearch": "Cancella ricerca"
}
},
"modelManager": {
"modelManager": "Gestione Modelli",
@@ -590,8 +581,8 @@
"hidePreview": "Nascondi l'anteprima",
"showPreview": "Mostra l'anteprima",
"noiseSettings": "Rumore",
"seamlessXAxis": "Piastrella senza cucitura Asse X",
"seamlessYAxis": "Piastrella senza cucitura Asse Y",
"seamlessXAxis": "Asse X",
"seamlessYAxis": "Asse Y",
"scheduler": "Campionatore",
"boundingBoxWidth": "Larghezza riquadro di delimitazione",
"boundingBoxHeight": "Altezza riquadro di delimitazione",
@@ -651,14 +642,7 @@
"unmasked": "No maschera",
"cfgRescaleMultiplier": "Moltiplicatore riscala CFG",
"cfgRescale": "Riscala CFG",
"useSize": "Usa Dimensioni",
"setToOptimalSize": "Ottimizza le dimensioni per il modello",
"setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (potrebbe essere troppo piccolo)",
"imageSize": "Dimensione dell'immagine",
"lockAspectRatio": "Blocca proporzioni",
"swapDimensions": "Scambia dimensioni",
"aspect": "Aspetto",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (potrebbe essere troppo grande)"
"useSize": "Usa Dimensioni"
},
"settings": {
"models": "Modelli",
@@ -1194,6 +1178,7 @@
"w": "W",
"processor": "Processore",
"none": "Nessuno",
"incompatibleBaseModel": "Modello base incompatibile:",
"pidiDescription": "Elaborazione immagini PIDI",
"fill": "Riempie",
"colorMapDescription": "Genera una mappa dei colori dall'immagine",
@@ -1229,13 +1214,12 @@
"minConfidence": "Confidenza minima",
"scribble": "Scribble",
"amult": "Angolo di illuminazione",
"coarse": "Approssimativo",
"resizeSimple": "Ridimensiona (semplice)"
"coarse": "Approssimativo"
},
"queue": {
"queueFront": "Aggiungi all'inizio della coda",
"queueBack": "Aggiungi alla coda",
"queueCountPrediction": "{{promptsCount}} prompt × {{iterations}} iterazioni -> {{count}} generazioni",
"queueCountPrediction": "Aggiungi {{predicted}} alla coda",
"queue": "Coda",
"status": "Stato",
"pruneSucceeded": "Rimossi {{item_count}} elementi completati dalla coda",
@@ -1293,8 +1277,7 @@
"graphFailedToQueue": "Impossibile mettere in coda il grafico",
"queueMaxExceeded": "È stato superato il limite massimo di {{max_queue_size}} e {{skip}} elementi verrebbero saltati",
"batchFieldValues": "Valori Campi Lotto",
"time": "Tempo",
"openQueue": "Apri coda"
"time": "Tempo"
},
"embedding": {
"noMatchingEmbedding": "Nessun Incorporamento corrispondente",
@@ -1314,12 +1297,7 @@
"noLoRAsInstalled": "Nessun LoRA installato",
"esrganModel": "Modello ESRGAN",
"addLora": "Aggiungi LoRA",
"noLoRAsLoaded": "Nessun LoRA caricato",
"noMainModelSelected": "Nessun modello principale selezionato",
"allLoRAsAdded": "Tutti i LoRA aggiunti",
"defaultVAE": "VAE predefinito",
"incompatibleBaseModel": "Modello base incompatibile",
"loraAlreadyAdded": "LoRA già aggiunto"
"noLoRAsLoaded": "Nessuna LoRA caricata"
},
"invocationCache": {
"disable": "Disabilita",
@@ -1353,9 +1331,7 @@
"promptsWithCount_many": "{{count}} Prompt",
"promptsWithCount_other": "{{count}} Prompt",
"dynamicPrompts": "Prompt dinamici",
"promptsPreview": "Anteprima dei prompt",
"showDynamicPrompts": "Mostra prompt dinamici",
"loading": "Generazione prompt dinamici..."
"promptsPreview": "Anteprima dei prompt"
},
"popovers": {
"paramScheduler": {
@@ -1581,7 +1557,7 @@
"scheduler": "Campionatore",
"noModelsAvailable": "Nessun modello disponibile",
"denoisingStrength": "Forza di riduzione del rumore",
"concatPromptStyle": "Collega Prompt & Stile",
"concatPromptStyle": "Concatena Prompt & Stile",
"loading": "Caricamento...",
"steps": "Passi",
"refinerStart": "Inizio Affinamento",
@@ -1592,8 +1568,7 @@
"useRefiner": "Utilizza l'affinatore",
"refinermodel": "Modello Affinatore",
"posAestheticScore": "Punteggio estetico positivo",
"posStylePrompt": "Prompt Stile positivo",
"freePromptStyle": "Prompt di stile manuale"
"posStylePrompt": "Prompt Stile positivo"
},
"metadata": {
"initImage": "Immagine iniziale",
@@ -1666,28 +1641,5 @@
},
"app": {
"storeNotInitialized": "Il negozio non è inizializzato"
},
"accordions": {
"compositing": {
"infillTab": "Riempimento",
"coherenceTab": "Passaggio di coerenza",
"title": "Composizione"
},
"control": {
"controlAdaptersTab": "Adattatori di Controllo",
"ipTab": "Prompt immagine",
"title": "Controllo"
},
"generation": {
"title": "Generazione",
"conceptsTab": "Concetti",
"modelTab": "Modello"
},
"advanced": {
"title": "Avanzate"
},
"image": {
"title": "Immagine"
}
}
}

View File

@@ -133,6 +133,10 @@
"title": "ピン",
"desc": "オプションパネルを固定"
},
"toggleViewer": {
"title": "ビュワーのトグル",
"desc": "ビュワーを開閉"
},
"toggleGallery": {
"title": "ギャラリーのトグル",
"desc": "ギャラリードロワーの開閉"
@@ -193,6 +197,10 @@
"title": "次の画像",
"desc": "ギャラリー内の1つ後の画像を表示"
},
"toggleGalleryPin": {
"title": "ギャラリードロワーの固定",
"desc": "ギャラリーをUIにピン留め/解除"
},
"increaseGalleryThumbSize": {
"title": "ギャラリーの画像を拡大",
"desc": "ギャラリーのサムネイル画像を拡大"
@@ -582,6 +590,7 @@
"processor": "プロセッサー",
"addControlNet": "$t(common.controlNet)を追加",
"none": "なし",
"incompatibleBaseModel": "互換性のないベースモデル:",
"enableControlnet": "コントロールネットを有効化",
"detectResolution": "検出解像度",
"controlNetT2IMutexDesc": "$t(common.controlNet)と$t(common.t2iAdapter)の同時使用は現在サポートされていません。",

View File

@@ -363,6 +363,7 @@
"processor": "프로세서",
"addControlNet": "$t(common.controlNet) 추가",
"none": "해당없음",
"incompatibleBaseModel": "호환되지 않는 기본 모델:",
"enableControlnet": "사용 가능한 ControlNet",
"detectResolution": "해상도 탐지",
"controlNetT2IMutexDesc": "$t(common.controlNet)와 $t(common.t2iAdapter)는 현재 동시에 지원되지 않습니다.",
@@ -407,6 +408,10 @@
"maxFaces": "Max Faces"
},
"hotkeys": {
"toggleGalleryPin": {
"title": "Gallery Pin 전환",
"desc": "갤러리를 UI에 고정했다가 풉니다"
},
"toggleSnap": {
"desc": "Snap을 Grid로 전환",
"title": "Snap 전환"
@@ -596,6 +601,10 @@
"desc": "노드 추가 메뉴 열기",
"title": "노드 추가"
},
"toggleViewer": {
"desc": "이미지 뷰어 열기 및 닫기",
"title": "Viewer 전환"
},
"undoStroke": {
"title": "Stroke 실행 취소",
"desc": "brush stroke 실행 취소"

View File

@@ -148,6 +148,10 @@
"title": "Zet Opties vast",
"desc": "Zet het deelscherm Opties vast"
},
"toggleViewer": {
"title": "Zet Viewer vast",
"desc": "Opent of sluit Afbeeldingsviewer"
},
"toggleGallery": {
"title": "Zet Galerij vast",
"desc": "Opent of sluit het deelscherm Galerij"
@@ -208,6 +212,10 @@
"title": "Volgende afbeelding",
"desc": "Toont de volgende afbeelding in de galerij"
},
"toggleGalleryPin": {
"title": "Zet galerij vast/los",
"desc": "Zet de galerij vast of los aan de gebruikersinterface"
},
"increaseGalleryThumbSize": {
"title": "Vergroot afbeeldingsgrootte galerij",
"desc": "Vergroot de grootte van de galerijminiaturen"
@@ -1063,6 +1071,7 @@
"processor": "Verwerker",
"addControlNet": "Voeg $t(common.controlNet) toe",
"none": "Geen",
"incompatibleBaseModel": "Niet-compatibel basismodel:",
"enableControlnet": "Schakel ControlNet in",
"detectResolution": "Herken resolutie",
"controlNetT2IMutexDesc": "Gelijktijdig gebruik van $t(common.controlNet) en $t(common.t2iAdapter) wordt op dit moment niet ondersteund.",

View File

@@ -86,6 +86,10 @@
"title": "Przypnij opcje",
"desc": "Przypina panel opcji"
},
"toggleViewer": {
"title": "Przełącz podgląd",
"desc": "Otwiera lub zamyka widok podglądu"
},
"toggleGallery": {
"title": "Przełącz galerię",
"desc": "Wysuwa lub chowa galerię"
@@ -146,6 +150,10 @@
"title": "Następny obraz",
"desc": "Aktywuje następny obraz z galerii"
},
"toggleGalleryPin": {
"title": "Przypnij galerię",
"desc": "Przypina lub odpina widok galerii"
},
"increaseGalleryThumbSize": {
"title": "Powiększ obrazy",
"desc": "Powiększa rozmiar obrazów w galerii"

View File

@@ -81,6 +81,10 @@
"hotkeys": {
"generalHotkeys": "Atalhos Gerais",
"galleryHotkeys": "Atalhos da Galeria",
"toggleViewer": {
"title": "Ativar Visualizador",
"desc": "Abrir e fechar o Visualizador de Imagens"
},
"maximizeWorkSpace": {
"desc": "Fechar painéis e maximixar área de trabalho",
"title": "Maximizar a Área de Trabalho"
@@ -228,6 +232,10 @@
"title": "Apagar Imagem",
"desc": "Apaga a imagem atual"
},
"toggleGalleryPin": {
"title": "Ativar Fixar Galeria",
"desc": "Fixa e desafixa a galeria na interface"
},
"increaseGalleryThumbSize": {
"title": "Aumentar Tamanho da Galeria de Imagem",
"desc": "Aumenta o tamanho das thumbs na galeria"

View File

@@ -103,6 +103,10 @@
"title": "Fixar Opções",
"desc": "Fixar o painel de opções"
},
"toggleViewer": {
"title": "Ativar Visualizador",
"desc": "Abrir e fechar o Visualizador de Imagens"
},
"toggleGallery": {
"title": "Ativar Galeria",
"desc": "Abrir e fechar a gaveta da galeria"
@@ -163,6 +167,10 @@
"title": "Próxima Imagem",
"desc": "Mostra a próxima imagem na galeria"
},
"toggleGalleryPin": {
"title": "Ativar Fixar Galeria",
"desc": "Fixa e desafixa a galeria na interface"
},
"increaseGalleryThumbSize": {
"title": "Aumentar Tamanho da Galeria de Imagem",
"desc": "Aumenta o tamanho das thumbs na galeria"

View File

@@ -189,6 +189,10 @@
"title": "Закрепить параметры",
"desc": "Закрепить панель параметров"
},
"toggleViewer": {
"title": "Показать просмотр",
"desc": "Открывать и закрывать просмотрщик изображений"
},
"toggleGallery": {
"title": "Показать галерею",
"desc": "Открывать и закрывать ящик галереи"
@@ -249,6 +253,10 @@
"title": "Следующее изображение",
"desc": "Отображение следующего изображения в галерее"
},
"toggleGalleryPin": {
"title": "Закрепить галерею",
"desc": "Закрепляет и открепляет галерею"
},
"increaseGalleryThumbSize": {
"title": "Увеличить размер миниатюр галереи",
"desc": "Увеличивает размер миниатюр галереи"
@@ -515,7 +523,7 @@
"parameters": {
"images": "Изображения",
"steps": "Шаги",
"cfgScale": "Шкала точности (CFG)",
"cfgScale": "Точность следования запросу (CFG)",
"width": "Ширина",
"height": "Высота",
"seed": "Сид",
@@ -1154,6 +1162,7 @@
"processor": "Процессор",
"addControlNet": "Добавить $t(common.controlNet)",
"none": "ничего",
"incompatibleBaseModel": "Несовместимая базовая модель:",
"controlNetT2IMutexDesc": "$t(common.controlNet) и $t(common.t2iAdapter) одновременно в настоящее время не поддерживаются.",
"ip_adapter": "$t(controlnet.controlAdapter_one) №{{number}} $t(common.ipAdapter)",
"pidiDescription": "PIDI-обработка изображений",

View File

@@ -129,6 +129,10 @@
"title": "Växla inställningar",
"desc": "Öppna och stäng alternativpanelen"
},
"toggleViewer": {
"title": "Växla visaren",
"desc": "Öppna och stäng bildvisaren"
},
"toggleGallery": {
"title": "Växla galleri",
"desc": "Öppna eller stäng galleribyrån"
@@ -189,6 +193,10 @@
"title": "Nästa bild",
"desc": "Visa nästa bild"
},
"toggleGalleryPin": {
"title": "Växla gallerinål",
"desc": "Nålar fast eller nålar av galleriet i gränssnittet"
},
"increaseGalleryThumbSize": {
"title": "Förstora galleriets bildstorlek",
"desc": "Förstora miniatyrbildernas storlek"

View File

@@ -111,6 +111,10 @@
"title": "Закріпити параметри",
"desc": "Закріпити панель параметрів"
},
"toggleViewer": {
"title": "Показати перегляд",
"desc": "Відкривати і закривати переглядач зображень"
},
"toggleGallery": {
"title": "Показати галерею",
"desc": "Відкривати і закривати скриньку галереї"
@@ -171,6 +175,10 @@
"title": "Наступне зображення",
"desc": "Відображення наступного зображення в галереї"
},
"toggleGalleryPin": {
"title": "Закріпити галерею",
"desc": "Закріплює і відкріплює галерею"
},
"increaseGalleryThumbSize": {
"title": "Збільшити розмір мініатюр галереї",
"desc": "Збільшує розмір мініатюр галереї"

View File

@@ -189,6 +189,10 @@
"title": "常开选项卡",
"desc": "保持选项浮窗常开"
},
"toggleViewer": {
"title": "切换图像查看器",
"desc": "打开或关闭图像查看器"
},
"toggleGallery": {
"title": "切换图库",
"desc": "打开或关闭图库"
@@ -249,6 +253,10 @@
"title": "下一张图像",
"desc": "显示图库中的下一张图像"
},
"toggleGalleryPin": {
"title": "切换图库常开",
"desc": "开关图库在界面中的常开模式"
},
"increaseGalleryThumbSize": {
"title": "增大预览尺寸",
"desc": "增大图库中预览的尺寸"
@@ -1141,6 +1149,7 @@
"crop": "裁剪",
"processor": "处理器",
"none": "无",
"incompatibleBaseModel": "不兼容的基础模型:",
"enableControlnet": "启用 ControlNet",
"detectResolution": "检测分辨率",
"pidiDescription": "像素差分 (PIDI) 图像处理",

View File

@@ -1,9 +1,8 @@
import fs from 'node:fs';
import openapiTS from 'openapi-typescript';
const OPENAPI_URL = 'http://127.0.0.1:9090/openapi.json';
const OUTPUT_FILE = 'src/services/api/schema.ts';
const OUTPUT_FILE = 'src/services/api/schema.d.ts';
async function main() {
process.stdout.write(

View File

@@ -1,29 +1,27 @@
import { Box } from '@chakra-ui/react';
import { useSocketIO } from 'app/hooks/useSocketIO';
import { Flex, Grid } from '@chakra-ui/react';
import { useStore } from '@nanostores/react';
import { useLogger } from 'app/logging/useLogger';
import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted';
import { $headerComponent } from 'app/store/nanostores/headerComponent';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import type { PartialAppConfig } from 'app/types/invokeai';
import ImageUploadOverlay from 'common/components/ImageUploadOverlay';
import { useClearStorage } from 'common/hooks/useClearStorage';
import { useFullscreenDropzone } from 'common/hooks/useFullscreenDropzone';
import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys';
import { useGlobalModifiersInit } from 'common/hooks/useGlobalModifiers';
import { PartialAppConfig } from 'app/types/invokeai';
import ImageUploader from 'common/components/ImageUploader';
import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardModal';
import DeleteImageModal from 'features/deleteImageModal/components/DeleteImageModal';
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
import SiteHeader from 'features/system/components/SiteHeader';
import { configChanged } from 'features/system/store/configSlice';
import { languageSelector } from 'features/system/store/systemSelectors';
import InvokeTabs from 'features/ui/components/InvokeTabs';
import { AnimatePresence } from 'framer-motion';
import i18n from 'i18n';
import { size } from 'lodash-es';
import { memo, useCallback, useEffect } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import AppErrorBoundaryFallback from './AppErrorBoundaryFallback';
import GlobalHotkeys from './GlobalHotkeys';
import PreselectedImage from './PreselectedImage';
import Toaster from './Toaster';
import { useSocketIO } from 'app/hooks/useSocketIO';
import { useClearStorage } from 'common/hooks/useClearStorage';
const DEFAULT_CONFIG = {};
@@ -43,11 +41,6 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
// singleton!
useSocketIO();
useGlobalModifiersInit();
useGlobalHotkeys();
const { dropzone, isHandlingUpload, setIsHandlingUpload } =
useFullscreenDropzone();
const handleReset = useCallback(() => {
clearStorage();
@@ -70,34 +63,41 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
dispatch(appStarted());
}, [dispatch]);
const headerComponent = useStore($headerComponent);
return (
<ErrorBoundary
onReset={handleReset}
FallbackComponent={AppErrorBoundaryFallback}
>
<Box
id="invoke-app-wrapper"
w="100vw"
h="100vh"
position="relative"
overflow="hidden"
{...dropzone.getRootProps()}
>
<input {...dropzone.getInputProps()} />
<InvokeTabs />
<AnimatePresence>
{dropzone.isDragActive && isHandlingUpload && (
<ImageUploadOverlay
dropzone={dropzone}
setIsHandlingUpload={setIsHandlingUpload}
/>
)}
</AnimatePresence>
</Box>
<Grid w="100vw" h="100vh" position="relative" overflow="hidden">
<ImageUploader>
<Grid
sx={{
gap: 4,
p: 4,
gridAutoRows: 'min-content auto',
w: 'full',
h: 'full',
}}
>
{headerComponent || <SiteHeader />}
<Flex
sx={{
gap: 4,
w: 'full',
h: 'full',
}}
>
<InvokeTabs />
</Flex>
</Grid>
</ImageUploader>
</Grid>
<DeleteImageModal />
<ChangeBoardModal />
<DynamicPromptsModal />
<Toaster />
<GlobalHotkeys />
<PreselectedImage selectedImage={selectedImage} />
</ErrorBoundary>
);

View File

@@ -1,6 +1,5 @@
import { Flex, Heading, Link, useToast } from '@chakra-ui/react';
import { InvButton } from 'common/components/InvButton/InvButton';
import { InvText } from 'common/components/InvText/wrapper';
import { Flex, Heading, Link, Text, useToast } from '@chakra-ui/react';
import IAIButton from 'common/components/IAIButton';
import newGithubIssueUrl from 'new-github-issue-url';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -38,48 +37,60 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
return (
<Flex
layerStyle="body"
w="100vw"
h="100vh"
alignItems="center"
justifyContent="center"
p={4}
sx={{
w: '100vw',
h: '100vh',
alignItems: 'center',
justifyContent: 'center',
p: 4,
}}
>
<Flex
layerStyle="first"
flexDir="column"
borderRadius="base"
justifyContent="center"
gap={8}
p={16}
sx={{
flexDir: 'column',
borderRadius: 'base',
justifyContent: 'center',
gap: 8,
p: 16,
}}
>
<Heading>{t('common.somethingWentWrong')}</Heading>
<Flex
layerStyle="second"
px={8}
py={4}
gap={4}
borderRadius="base"
justifyContent="space-between"
alignItems="center"
sx={{
px: 8,
py: 4,
borderRadius: 'base',
gap: 4,
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<InvText fontWeight="semibold" color="error.400">
<Text
sx={{
fontWeight: 600,
color: 'error.500',
_dark: { color: 'error.400' },
}}
>
{error.name}: {error.message}
</InvText>
</Text>
</Flex>
<Flex gap={4}>
<InvButton
<Flex sx={{ gap: 4 }}>
<IAIButton
leftIcon={<FaArrowRotateLeft />}
onClick={resetErrorBoundary}
>
{t('accessibility.resetUI')}
</InvButton>
<InvButton leftIcon={<FaCopy />} onClick={handleCopy}>
</IAIButton>
<IAIButton leftIcon={<FaCopy />} onClick={handleCopy}>
{t('common.copyError')}
</InvButton>
</IAIButton>
<Link href={url} isExternal>
<InvButton leftIcon={<FaExternalLinkAlt />}>
<IAIButton leftIcon={<FaExternalLinkAlt />}>
{t('accessibility.createIssue')}
</InvButton>
</IAIButton>
</Link>
</Flex>
</Flex>

View File

@@ -0,0 +1,112 @@
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useQueueBack } from 'features/queue/hooks/useQueueBack';
import { useQueueFront } from 'features/queue/hooks/useQueueFront';
import {
ctrlKeyPressed,
metaKeyPressed,
shiftKeyPressed,
} from 'features/ui/store/hotkeysSlice';
import { setActiveTab } from 'features/ui/store/uiSlice';
import React, { memo } from 'react';
import { isHotkeyPressed, useHotkeys } from 'react-hotkeys-hook';
const globalHotkeysSelector = createMemoizedSelector(
[stateSelector],
({ hotkeys }) => {
const { shift, ctrl, meta } = hotkeys;
return { shift, ctrl, meta };
}
);
// TODO: Does not catch keypresses while focused in an input. Maybe there is a way?
/**
* Logical component. Handles app-level global hotkeys.
* @returns null
*/
const GlobalHotkeys: React.FC = () => {
const dispatch = useAppDispatch();
const { shift, ctrl, meta } = useAppSelector(globalHotkeysSelector);
const {
queueBack,
isDisabled: isDisabledQueueBack,
isLoading: isLoadingQueueBack,
} = useQueueBack();
useHotkeys(
['ctrl+enter', 'meta+enter'],
queueBack,
{
enabled: () => !isDisabledQueueBack && !isLoadingQueueBack,
preventDefault: true,
enableOnFormTags: ['input', 'textarea', 'select'],
},
[queueBack, isDisabledQueueBack, isLoadingQueueBack]
);
const {
queueFront,
isDisabled: isDisabledQueueFront,
isLoading: isLoadingQueueFront,
} = useQueueFront();
useHotkeys(
['ctrl+shift+enter', 'meta+shift+enter'],
queueFront,
{
enabled: () => !isDisabledQueueFront && !isLoadingQueueFront,
preventDefault: true,
enableOnFormTags: ['input', 'textarea', 'select'],
},
[queueFront, isDisabledQueueFront, isLoadingQueueFront]
);
useHotkeys(
'*',
() => {
if (isHotkeyPressed('shift')) {
!shift && dispatch(shiftKeyPressed(true));
} else {
shift && dispatch(shiftKeyPressed(false));
}
if (isHotkeyPressed('ctrl')) {
!ctrl && dispatch(ctrlKeyPressed(true));
} else {
ctrl && dispatch(ctrlKeyPressed(false));
}
if (isHotkeyPressed('meta')) {
!meta && dispatch(metaKeyPressed(true));
} else {
meta && dispatch(metaKeyPressed(false));
}
},
{ keyup: true, keydown: true },
[shift, ctrl, meta]
);
useHotkeys('1', () => {
dispatch(setActiveTab('txt2img'));
});
useHotkeys('2', () => {
dispatch(setActiveTab('img2img'));
});
useHotkeys('3', () => {
dispatch(setActiveTab('unifiedCanvas'));
});
useHotkeys('4', () => {
dispatch(setActiveTab('nodes'));
});
useHotkeys('5', () => {
dispatch(setActiveTab('modelManager'));
});
return null;
};
export default memo(GlobalHotkeys);

View File

@@ -1,27 +1,29 @@
import 'i18n';
import type { Middleware } from '@reduxjs/toolkit';
import { Middleware } from '@reduxjs/toolkit';
import { $socketOptions } from 'app/hooks/useSocketIO';
import { $authToken } from 'app/store/nanostores/authToken';
import { $baseUrl } from 'app/store/nanostores/baseUrl';
import { $customNavComponent } from 'app/store/nanostores/customNavComponent';
import type { CustomStarUi } from 'app/store/nanostores/customStarUI';
import { $customStarUI } from 'app/store/nanostores/customStarUI';
import { $galleryHeader } from 'app/store/nanostores/galleryHeader';
import { $customStarUI, CustomStarUi } from 'app/store/nanostores/customStarUI';
import { $headerComponent } from 'app/store/nanostores/headerComponent';
import { $isDebugging } from 'app/store/nanostores/isDebugging';
import { $logo } from 'app/store/nanostores/logo';
import { $projectId } from 'app/store/nanostores/projectId';
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
import { $store } from 'app/store/nanostores/store';
import { createStore } from 'app/store/store';
import type { PartialAppConfig } from 'app/types/invokeai';
import { PartialAppConfig } from 'app/types/invokeai';
import Loading from 'common/components/Loading/Loading';
import AppDndContext from 'features/dnd/components/AppDndContext';
import type { PropsWithChildren, ReactNode } from 'react';
import React, { lazy, memo, useEffect, useMemo } from 'react';
import 'i18n';
import React, {
PropsWithChildren,
ReactNode,
lazy,
memo,
useEffect,
useMemo,
} from 'react';
import { Provider } from 'react-redux';
import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
import type { ManagerOptions, SocketOptions } from 'socket.io-client';
import { ManagerOptions, SocketOptions } from 'socket.io-client';
const App = lazy(() => import('./App'));
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
@@ -30,10 +32,9 @@ interface Props extends PropsWithChildren {
apiUrl?: string;
token?: string;
config?: PartialAppConfig;
customNavComponent?: ReactNode;
headerComponent?: ReactNode;
middleware?: Middleware[];
projectId?: string;
galleryHeader?: ReactNode;
queueId?: string;
selectedImage?: {
imageName: string;
@@ -42,23 +43,20 @@ interface Props extends PropsWithChildren {
customStarUi?: CustomStarUi;
socketOptions?: Partial<ManagerOptions & SocketOptions>;
isDebugging?: boolean;
logo?: ReactNode;
}
const InvokeAIUI = ({
apiUrl,
token,
config,
customNavComponent,
headerComponent,
middleware,
projectId,
galleryHeader,
queueId,
selectedImage,
customStarUi,
socketOptions,
isDebugging = false,
logo,
}: Props) => {
useEffect(() => {
// configure API client token
@@ -114,34 +112,14 @@ const InvokeAIUI = ({
}, [customStarUi]);
useEffect(() => {
if (customNavComponent) {
$customNavComponent.set(customNavComponent);
if (headerComponent) {
$headerComponent.set(headerComponent);
}
return () => {
$customNavComponent.set(undefined);
$headerComponent.set(undefined);
};
}, [customNavComponent]);
useEffect(() => {
if (galleryHeader) {
$galleryHeader.set(galleryHeader);
}
return () => {
$galleryHeader.set(undefined);
};
}, [galleryHeader]);
useEffect(() => {
if (logo) {
$logo.set(logo);
}
return () => {
$logo.set(undefined);
};
}, [logo]);
}, [headerComponent]);
useEffect(() => {
if (socketOptions) {
@@ -167,15 +145,6 @@ const InvokeAIUI = ({
useEffect(() => {
$store.set(store);
if (import.meta.env.MODE === 'development') {
window.$store = $store;
}
() => {
$store.set(undefined);
if (import.meta.env.MODE === 'development') {
window.$store = undefined;
}
};
}, [store]);
return (

View File

@@ -1,17 +1,24 @@
import '@fontsource-variable/inter';
import 'overlayscrollbars/overlayscrollbars.css';
import 'common/components/OverlayScrollbars/overlayscrollbars.css';
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
import type { ReactNode } from 'react';
import { memo, useEffect, useMemo } from 'react';
import {
ChakraProvider,
createLocalStorageManager,
extendTheme,
} from '@chakra-ui/react';
import { ReactNode, memo, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { theme as invokeAITheme, TOAST_OPTIONS } from 'theme/theme';
import { TOAST_OPTIONS, theme as invokeAITheme } from 'theme/theme';
import '@fontsource-variable/inter';
import { MantineProvider } from '@mantine/core';
import { useMantineTheme } from 'mantine-theme/theme';
import 'overlayscrollbars/overlayscrollbars.css';
import 'theme/css/overlayscrollbars.css';
type ThemeLocaleProviderProps = {
children: ReactNode;
};
const manager = createLocalStorageManager('@@invokeai-color-mode');
function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
const { i18n } = useTranslation();
@@ -28,10 +35,18 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
document.body.dir = direction;
}, [direction]);
const mantineTheme = useMantineTheme();
return (
<ChakraProvider theme={theme} toastOptions={TOAST_OPTIONS}>
{children}
</ChakraProvider>
<MantineProvider theme={mantineTheme}>
<ChakraProvider
theme={theme}
colorModeManager={manager}
toastOptions={TOAST_OPTIONS}
>
{children}
</ChakraProvider>
</MantineProvider>
);
}

View File

@@ -1,8 +1,7 @@
import { useToast } from '@chakra-ui/react';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
import type { MakeToastArg } from 'features/system/util/makeToast';
import { makeToast } from 'features/system/util/makeToast';
import { MakeToastArg, makeToast } from 'features/system/util/makeToast';
import { memo, useCallback, useEffect } from 'react';
/**
@@ -11,7 +10,7 @@ import { memo, useCallback, useEffect } from 'react';
*/
const Toaster = () => {
const dispatch = useAppDispatch();
const toastQueue = useAppSelector((s) => s.system.toastQueue);
const toastQueue = useAppSelector((state) => state.system.toastQueue);
const toast = useToast();
useEffect(() => {
toastQueue.forEach((t) => {

View File

@@ -0,0 +1,94 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
type FeatureHelpInfo = {
text: string;
href: string;
guideImage: string;
};
export enum Feature {
PROMPT,
GALLERY,
OTHER,
SEED,
VARIATIONS,
UPSCALE,
FACE_CORRECTION,
IMAGE_TO_IMAGE,
BOUNDING_BOX,
SEAM_CORRECTION,
INFILL_AND_SCALING,
}
/** For each tooltip in the UI, the below feature definitions & props will pull relevant information into the tooltip.
*
* To-do: href & GuideImages are placeholders, and are not currently utilized, but will be updated (along with the tooltip UI) as feature and UI develop and we get a better idea on where things "forever homes" will be .
*/
const useFeatures = (): Record<Feature, FeatureHelpInfo> => {
const { t } = useTranslation();
return useMemo(
() => ({
[Feature.PROMPT]: {
text: t('tooltip.feature.prompt'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.GALLERY]: {
text: t('tooltip.feature.gallery'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.OTHER]: {
text: t('tooltip.feature.other'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.SEED]: {
text: t('tooltip.feature.seed'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.VARIATIONS]: {
text: t('tooltip.feature.variations'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.UPSCALE]: {
text: t('tooltip.feature.upscale'),
href: 'link/to/docs/feature1.html',
guideImage: 'asset/path.gif',
},
[Feature.FACE_CORRECTION]: {
text: t('tooltip.feature.faceCorrection'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.IMAGE_TO_IMAGE]: {
text: t('tooltip.feature.imageToImage'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.BOUNDING_BOX]: {
text: t('tooltip.feature.boundingBox'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.SEAM_CORRECTION]: {
text: t('tooltip.feature.seamCorrection'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
[Feature.INFILL_AND_SCALING]: {
text: t('tooltip.feature.infillAndScaling'),
href: 'link/to/docs/feature3.html',
guideImage: 'asset/path.gif',
},
}),
[t]
);
};
export const useFeatureHelpInfo = (feature: Feature): FeatureHelpInfo => {
const features = useFeatures();
return features[feature];
};

View File

@@ -3,16 +3,14 @@ import { $authToken } from 'app/store/nanostores/authToken';
import { $baseUrl } from 'app/store/nanostores/baseUrl';
import { $isDebugging } from 'app/store/nanostores/isDebugging';
import { useAppDispatch } from 'app/store/storeHooks';
import type { MapStore } from 'nanostores';
import { atom, map } from 'nanostores';
import { MapStore, atom, map } from 'nanostores';
import { useEffect, useMemo } from 'react';
import type {
import {
ClientToServerEvents,
ServerToClientEvents,
} from 'services/events/types';
import { setEventListeners } from 'services/events/util/setEventListeners';
import type { ManagerOptions, Socket, SocketOptions } from 'socket.io-client';
import { io } from 'socket.io-client';
import { ManagerOptions, Socket, SocketOptions, io } from 'socket.io-client';
// Inject socket options and url into window for debugging
declare global {

View File

@@ -1,8 +1,6 @@
import { createLogWriter } from '@roarr/browser-log-writer';
import { atom } from 'nanostores';
import type { Logger } from 'roarr';
import { ROARR, Roarr } from 'roarr';
import { z } from 'zod';
import { Logger, ROARR, Roarr } from 'roarr';
ROARR.write = createLogWriter();
@@ -28,20 +26,19 @@ export type LoggerNamespace =
export const logger = (namespace: LoggerNamespace) =>
$logger.get().child({ namespace });
export const zLogLevel = z.enum([
export const VALID_LOG_LEVELS = [
'trace',
'debug',
'info',
'warn',
'error',
'fatal',
]);
export type LogLevel = z.infer<typeof zLogLevel>;
export const isLogLevel = (v: unknown): v is LogLevel =>
zLogLevel.safeParse(v).success;
] as const;
export type InvokeLogLevel = (typeof VALID_LOG_LEVELS)[number];
// Translate human-readable log levels to numbers, used for log filtering
export const LOG_LEVEL_MAP: Record<LogLevel, number> = {
export const LOG_LEVEL_MAP: Record<InvokeLogLevel, number> = {
trace: 10,
debug: 20,
info: 30,

View File

@@ -1,14 +1,28 @@
import { createLogWriter } from '@roarr/browser-log-writer';
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
import { stateSelector } from 'app/store/store';
import { useAppSelector } from 'app/store/storeHooks';
import { useEffect, useMemo } from 'react';
import { ROARR, Roarr } from 'roarr';
import {
$logger,
BASE_CONTEXT,
LOG_LEVEL_MAP,
LoggerNamespace,
logger,
} from './logger';
import type { LoggerNamespace } from './logger';
import { $logger, BASE_CONTEXT, LOG_LEVEL_MAP, logger } from './logger';
const selector = createMemoizedSelector(stateSelector, ({ system }) => {
const { consoleLogLevel, shouldLogToConsole } = system;
return {
consoleLogLevel,
shouldLogToConsole,
};
});
export const useLogger = (namespace: LoggerNamespace) => {
const consoleLogLevel = useAppSelector((s) => s.system.consoleLogLevel);
const shouldLogToConsole = useAppSelector((s) => s.system.shouldLogToConsole);
const { consoleLogLevel, shouldLogToConsole } = useAppSelector(selector);
// The provided Roarr browser log writer uses localStorage to config logging to console
useEffect(() => {

View File

@@ -1,6 +1,6 @@
import { createAction } from '@reduxjs/toolkit';
import type { InvokeTabName } from 'features/ui/store/tabMap';
import type { BatchConfig } from 'services/api/types';
import { InvokeTabName } from 'features/ui/store/tabMap';
import { BatchConfig } from 'services/api/types';
export const enqueueRequested = createAction<{
tabName: InvokeTabName;

View File

@@ -1,9 +1,4 @@
import {
createDraftSafeSelectorCreator,
createSelectorCreator,
lruMemoize,
} from '@reduxjs/toolkit';
import type { GetSelectorsOptions } from '@reduxjs/toolkit/dist/entities/state_selectors';
import { createSelectorCreator, lruMemoize } from '@reduxjs/toolkit';
import { isEqual } from 'lodash-es';
/**
@@ -14,22 +9,4 @@ export const createMemoizedSelector = createSelectorCreator({
memoizeOptions: {
resultEqualityCheck: isEqual,
},
argsMemoize: lruMemoize,
});
/**
* A memoized selector creator that uses LRU cache default shallow equality check.
*/
export const createLruSelector = createSelectorCreator({
memoize: lruMemoize,
argsMemoize: lruMemoize,
});
export const createLruDraftSafeSelector = createDraftSafeSelectorCreator({
memoize: lruMemoize,
argsMemoize: lruMemoize,
});
export const getSelectorsOptions: GetSelectorsOptions = {
createSelector: createLruDraftSafeSelector,
};

View File

@@ -8,7 +8,7 @@ import { postprocessingPersistDenylist } from 'features/parameters/store/postpro
import { systemPersistDenylist } from 'features/system/store/systemPersistDenylist';
import { uiPersistDenylist } from 'features/ui/store/uiPersistDenylist';
import { omit } from 'lodash-es';
import type { SerializeFunction } from 'redux-remember';
import { SerializeFunction } from 'redux-remember';
const serializationDenylist: {
[key: string]: string[];

View File

@@ -8,9 +8,10 @@ import { initialPostprocessingState } from 'features/parameters/store/postproces
import { initialSDXLState } from 'features/sdxl/store/sdxlSlice';
import { initialConfigState } from 'features/system/store/configSlice';
import { initialSystemState } from 'features/system/store/systemSlice';
import { initialHotkeysState } from 'features/ui/store/hotkeysSlice';
import { initialUIState } from 'features/ui/store/uiSlice';
import { defaultsDeep } from 'lodash-es';
import type { UnserializeFunction } from 'redux-remember';
import { UnserializeFunction } from 'redux-remember';
const initialStates: {
[key: string]: object; // TODO: type this properly
@@ -23,6 +24,7 @@ const initialStates: {
system: initialSystemState,
config: initialConfigState,
ui: initialUIState,
hotkeys: initialHotkeysState,
controlAdapters: initialControlAdapterState,
dynamicPrompts: initialDynamicPromptsState,
sdxl: initialSDXLState,

View File

@@ -1,8 +1,8 @@
import type { UnknownAction } from '@reduxjs/toolkit';
import { UnknownAction } from '@reduxjs/toolkit';
import { isAnyGraphBuilt } from 'features/nodes/store/actions';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodeTemplatesSlice';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
import type { Graph } from 'services/api/types';
import { Graph } from 'services/api/types';
export const actionSanitizer = <A extends UnknownAction>(action: A): A => {
if (isAnyGraphBuilt(action)) {

View File

@@ -3,14 +3,19 @@
*/
export const actionsDenylist = [
// very spammy canvas actions
'canvas/setCursorPosition',
'canvas/setStageCoordinates',
'canvas/setStageScale',
'canvas/setIsDrawing',
'canvas/setBoundingBoxCoordinates',
'canvas/setBoundingBoxDimensions',
'canvas/setIsDrawing',
'canvas/addPointToCurrentLine',
// bazillions during generation
'socket/socketGeneratorProgress',
'socket/appSocketGeneratorProgress',
// every time user presses shift
// 'hotkeys/shiftKeyPressed',
// this happens after every state change
'@@REMEMBER_PERSISTED',
];

View File

@@ -1,12 +1,11 @@
import type {
ListenerEffect,
TypedAddListener,
TypedStartListening,
import type { TypedAddListener, TypedStartListening } from '@reduxjs/toolkit';
import {
UnknownAction,
ListenerEffect,
addListener,
createListenerMiddleware,
} from '@reduxjs/toolkit';
import { addListener, createListenerMiddleware } from '@reduxjs/toolkit';
import type { AppDispatch, RootState } from 'app/store/store';
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
import { addFirstListImagesListener } from './listeners/addFirstListImagesListener.ts';
import { addAnyEnqueuedListener } from './listeners/anyEnqueued';
@@ -43,13 +42,13 @@ import {
addImageRemovedFromBoardFulfilledListener,
addImageRemovedFromBoardRejectedListener,
} from './listeners/imageRemovedFromBoard';
import { addImagesStarredListener } from './listeners/imagesStarred';
import { addImagesUnstarredListener } from './listeners/imagesUnstarred';
import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected';
import {
addImageUploadedFulfilledListener,
addImageUploadedRejectedListener,
} from './listeners/imageUploaded';
import { addImagesStarredListener } from './listeners/imagesStarred';
import { addImagesUnstarredListener } from './listeners/imagesUnstarred';
import { addInitialImageSelectedListener } from './listeners/initialImageSelected';
import { addModelSelectedListener } from './listeners/modelSelected';
import { addModelsLoadedListener } from './listeners/modelsLoaded';
@@ -70,9 +69,9 @@ import { addSocketSubscribedEventListener as addSocketSubscribedListener } from
import { addSocketUnsubscribedEventListener as addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed';
import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved';
import { addTabChangedListener } from './listeners/tabChanged';
import { addUpdateAllNodesRequestedListener } from './listeners/updateAllNodesRequested';
import { addUpscaleRequestedListener } from './listeners/upscaleRequested';
import { addWorkflowLoadRequestedListener } from './listeners/workflowLoadRequested';
import { addUpdateAllNodesRequestedListener } from './listeners/updateAllNodesRequested';
export const listenerMiddleware = createListenerMiddleware();

View File

@@ -8,7 +8,6 @@ import {
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { queueApi } from 'services/api/endpoints/queue';
import { startAppListening } from '..';
const matcher = isAnyOf(commitStagingAreaImage, discardStagedImages);

View File

@@ -2,10 +2,9 @@ import { createAction } from '@reduxjs/toolkit';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
import { imagesApi } from 'services/api/endpoints/images';
import type { ImageCache } from 'services/api/types';
import { getListImagesUrl, imagesSelectors } from 'services/api/util';
import { startAppListening } from '..';
import { getListImagesUrl, imagesAdapter } from 'services/api/util';
import { ImageCache } from 'services/api/types';
export const appStarted = createAction('app/appStarted');
@@ -33,7 +32,7 @@ export const addFirstListImagesListener = () => {
if (data.ids.length > 0) {
// Select the first image
const firstImage = imagesSelectors.selectAll(data)[0];
const firstImage = imagesAdapter.getSelectors().selectAll(data)[0];
dispatch(imageSelected(firstImage ?? null));
}
},

View File

@@ -1,5 +1,4 @@
import { queueApi } from 'services/api/endpoints/queue';
import { startAppListening } from '..';
export const addAnyEnqueuedListener = () => {

View File

@@ -4,7 +4,6 @@ import {
shouldUseWatermarkerChanged,
} from 'features/system/store/systemSlice';
import { appInfoApi } from 'services/api/endpoints/appInfo';
import { startAppListening } from '..';
export const addAppConfigReceivedListener = () => {

View File

@@ -1,5 +1,4 @@
import { createAction } from '@reduxjs/toolkit';
import { startAppListening } from '..';
export const appStarted = createAction('app/appStarted');

View File

@@ -5,8 +5,7 @@ import { zPydanticValidationError } from 'features/system/store/zodSchemas';
import { t } from 'i18next';
import { truncate, upperFirst } from 'lodash-es';
import { queueApi } from 'services/api/endpoints/queue';
import { theme, TOAST_OPTIONS } from 'theme/theme';
import { TOAST_OPTIONS, theme } from 'theme/theme';
import { startAppListening } from '..';
const { toast } = createStandaloneToast({

View File

@@ -4,7 +4,6 @@ import { getImageUsage } from 'features/deleteImageModal/store/selectors';
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
export const addDeleteBoardAndImagesFulfilledListener = () => {
@@ -20,15 +19,9 @@ export const addDeleteBoardAndImagesFulfilledListener = () => {
let wasNodeEditorReset = false;
let wereControlAdaptersReset = false;
const { generation, canvas, nodes, controlAdapters } = getState();
const state = getState();
deleted_images.forEach((image_name) => {
const imageUsage = getImageUsage(
generation,
canvas,
nodes,
controlAdapters,
image_name
);
const imageUsage = getImageUsage(state, image_name);
if (imageUsage.isInitialImage && !wasInitialImageReset) {
dispatch(clearInitialImage());

View File

@@ -9,9 +9,8 @@ import {
IMAGE_CATEGORIES,
} from 'features/gallery/store/types';
import { imagesApi } from 'services/api/endpoints/images';
import { imagesSelectors } from 'services/api/util';
import { startAppListening } from '..';
import { imagesSelectors } from 'services/api/util';
export const addBoardIdSelectedListener = () => {
startAppListening({

View File

@@ -1,12 +1,11 @@
import { $logger } from 'app/logging/logger';
import { canvasCopiedToClipboard } from 'features/canvas/store/actions';
import { startAppListening } from '..';
import { $logger } from 'app/logging/logger';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { addToast } from 'features/system/store/systemSlice';
import { copyBlobToClipboard } from 'features/system/util/copyBlobToClipboard';
import { t } from 'i18next';
import { startAppListening } from '..';
export const addCanvasCopiedToClipboardListener = () => {
startAppListening({
actionCreator: canvasCopiedToClipboard,

View File

@@ -1,12 +1,11 @@
import { $logger } from 'app/logging/logger';
import { canvasDownloadedAsImage } from 'features/canvas/store/actions';
import { startAppListening } from '..';
import { $logger } from 'app/logging/logger';
import { downloadBlob } from 'features/canvas/util/downloadBlob';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { startAppListening } from '..';
export const addCanvasDownloadedAsImageListener = () => {
startAppListening({
actionCreator: canvasDownloadedAsImage,

View File

@@ -1,12 +1,11 @@
import { logger } from 'app/logging/logger';
import { canvasImageToControlAdapter } from 'features/canvas/store/actions';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { controlAdapterImageChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
import { canvasImageToControlAdapter } from 'features/canvas/store/actions';
export const addCanvasImageToControlNetListener = () => {
startAppListening({

View File

@@ -2,10 +2,9 @@ import { logger } from 'app/logging/logger';
import { canvasMaskSavedToGallery } from 'features/canvas/store/actions';
import { getCanvasData } from 'features/canvas/util/getCanvasData';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
import { t } from 'i18next';
export const addCanvasMaskSavedToGalleryListener = () => {
startAppListening({

View File

@@ -5,7 +5,6 @@ import { controlAdapterImageChanged } from 'features/controlAdapters/store/contr
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
export const addCanvasMaskToControlNetListener = () => {

View File

@@ -4,10 +4,9 @@ import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob';
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
import { t } from 'i18next';
export const addCanvasMergedListener = () => {
startAppListening({

View File

@@ -2,10 +2,9 @@ import { logger } from 'app/logging/logger';
import { canvasSavedToGallery } from 'features/canvas/store/actions';
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
import { t } from 'i18next';
export const addCanvasSavedToGalleryListener = () => {
startAppListening({

View File

@@ -1,6 +1,6 @@
import type { AnyListenerPredicate } from '@reduxjs/toolkit';
import { AnyListenerPredicate } from '@reduxjs/toolkit';
import { logger } from 'app/logging/logger';
import type { RootState } from 'app/store/store';
import { RootState } from 'app/store/store';
import { controlAdapterImageProcessed } from 'features/controlAdapters/store/actions';
import {
controlAdapterAutoConfigToggled,
@@ -10,9 +10,8 @@ import {
controlAdapterProcessortTypeChanged,
selectControlAdapterById,
} from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { startAppListening } from '..';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
type AnyControlAdapterParamChangeAction =
| ReturnType<typeof controlAdapterProcessorParamsChanged>
@@ -69,12 +68,10 @@ const predicate: AnyListenerPredicate<RootState> = (
return isProcessorSelected && hasControlImage;
};
const DEBOUNCE_MS = 300;
/**
* Listener that automatically processes a ControlNet image when its processor parameters are changed.
*
* The network request is debounced.
* The network request is debounced by 1 second.
*/
export const addControlNetAutoProcessListener = () => {
startAppListening({
@@ -87,7 +84,7 @@ export const addControlNetAutoProcessListener = () => {
cancelActiveListeners();
log.trace('ControlNet auto-process triggered');
// Delay before starting actual work
await delay(DEBOUNCE_MS);
await delay(300);
dispatch(controlAdapterImageProcessed({ id }));
},

View File

@@ -8,15 +8,14 @@ import {
selectControlAdapterById,
} from 'features/controlAdapters/store/controlAdaptersSlice';
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
import { isImageOutput } from 'features/nodes/types/common';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { imagesApi } from 'services/api/endpoints/images';
import { queueApi } from 'services/api/endpoints/queue';
import type { BatchConfig, ImageDTO } from 'services/api/types';
import { BatchConfig, ImageDTO } from 'services/api/types';
import { socketInvocationComplete } from 'services/events/actions';
import { startAppListening } from '..';
import { isImageOutput } from 'features/nodes/types/common';
export const addControlNetImageProcessedListener = () => {
startAppListening({

View File

@@ -14,8 +14,7 @@ import { buildCanvasGraph } from 'features/nodes/util/graph/buildCanvasGraph';
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
import { imagesApi } from 'services/api/endpoints/images';
import { queueApi } from 'services/api/endpoints/queue';
import type { ImageDTO } from 'services/api/types';
import { ImageDTO } from 'services/api/types';
import { startAppListening } from '..';
/**

View File

@@ -5,7 +5,6 @@ import { buildLinearSDXLImageToImageGraph } from 'features/nodes/util/graph/buil
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLTextToImageGraph';
import { buildLinearTextToImageGraph } from 'features/nodes/util/graph/buildLinearTextToImageGraph';
import { queueApi } from 'services/api/endpoints/queue';
import { startAppListening } from '..';
export const addEnqueueRequestedLinear = () => {

View File

@@ -1,9 +1,8 @@
import { enqueueRequested } from 'app/store/actions';
import { buildNodesGraph } from 'features/nodes/util/graph/buildNodesGraph';
import { buildWorkflowRight } from 'features/nodes/util/workflow/buildWorkflow';
import { buildWorkflow } from 'features/nodes/util/workflow/buildWorkflow';
import { queueApi } from 'services/api/endpoints/queue';
import type { BatchConfig } from 'services/api/types';
import { BatchConfig } from 'services/api/types';
import { startAppListening } from '..';
export const addEnqueueRequestedNodes = () => {
@@ -15,16 +14,14 @@ export const addEnqueueRequestedNodes = () => {
const { nodes, edges } = state.nodes;
const workflow = state.workflow;
const graph = buildNodesGraph(state.nodes);
const builtWorkflow = buildWorkflowRight({
const builtWorkflow = buildWorkflow({
nodes,
edges,
workflow,
});
if (builtWorkflow) {
// embedded workflows don't have an id
delete builtWorkflow.id;
}
// embedded workflows don't have an id
delete builtWorkflow.id;
const batchConfig: BatchConfig = {
batch: {

View File

@@ -1,6 +1,5 @@
import { logger } from 'app/logging/logger';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
export const addImageAddedToBoardFulfilledListener = () => {

View File

@@ -17,8 +17,7 @@ import { clearInitialImage } from 'features/parameters/store/generationSlice';
import { clamp, forEach } from 'lodash-es';
import { api } from 'services/api';
import { imagesApi } from 'services/api/endpoints/images';
import { imagesSelectors } from 'services/api/util';
import { imagesAdapter } from 'services/api/util';
import { startAppListening } from '..';
export const addRequestedSingleImageDeletionListener = () => {
@@ -53,7 +52,9 @@ export const addRequestedSingleImageDeletionListener = () => {
const { data } =
imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
const cachedImageDTOs = data ? imagesSelectors.selectAll(data) : [];
const cachedImageDTOs = data
? imagesAdapter.getSelectors().selectAll(data)
: [];
const deletedImageIndex = cachedImageDTOs.findIndex(
(i) => i.image_name === image_name
@@ -185,7 +186,7 @@ export const addRequestedMultipleImageDeletionListener = () => {
imagesApi.endpoints.listImages.select(baseQueryArgs)(state);
const newSelectedImageDTO = data
? imagesSelectors.selectAll(data)[0]
? imagesAdapter.getSelectors().selectAll(data)[0]
: undefined;
if (newSelectedImageDTO) {

View File

@@ -6,20 +6,16 @@ import {
controlAdapterImageChanged,
controlAdapterIsEnabledChanged,
} from 'features/controlAdapters/store/controlAdaptersSlice';
import type {
import {
TypesafeDraggableData,
TypesafeDroppableData,
} from 'features/dnd/types';
import { imageSelected } from 'features/gallery/store/gallerySlice';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import { workflowExposedFieldAdded } from 'features/nodes/store/workflowSlice';
import {
initialImageChanged,
selectOptimalDimension,
} from 'features/parameters/store/generationSlice';
import { initialImageChanged } from 'features/parameters/store/generationSlice';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '../';
import { workflowExposedFieldAdded } from 'features/nodes/store/workflowSlice';
export const dndDropped = createAction<{
overData: TypesafeDroppableData;
@@ -29,7 +25,7 @@ export const dndDropped = createAction<{
export const addImageDroppedListener = () => {
startAppListening({
actionCreator: dndDropped,
effect: async (action, { dispatch, getState }) => {
effect: async (action, { dispatch }) => {
const log = logger('dnd');
const { activeData, overData } = action.payload;
@@ -118,12 +114,7 @@ export const addImageDroppedListener = () => {
activeData.payloadType === 'IMAGE_DTO' &&
activeData.payload.imageDTO
) {
dispatch(
setInitialCanvasImage(
activeData.payload.imageDTO,
selectOptimalDimension(getState())
)
);
dispatch(setInitialCanvasImage(activeData.payload.imageDTO));
return;
}

View File

@@ -1,6 +1,5 @@
import { logger } from 'app/logging/logger';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
export const addImageRemovedFromBoardFulfilledListener = () => {

View File

@@ -4,7 +4,6 @@ import {
imagesToDeleteSelected,
isModalOpenChanged,
} from 'features/deleteImageModal/store/slice';
import { startAppListening } from '..';
export const addImageToDeleteSelectedListener = () => {

View File

@@ -1,4 +1,4 @@
import type { UseToastOptions } from '@chakra-ui/react';
import { UseToastOptions } from '@chakra-ui/react';
import { logger } from 'app/logging/logger';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import {
@@ -6,17 +6,13 @@ import {
controlAdapterIsEnabledChanged,
} from 'features/controlAdapters/store/controlAdaptersSlice';
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
import {
initialImageChanged,
selectOptimalDimension,
} from 'features/parameters/store/generationSlice';
import { initialImageChanged } from 'features/parameters/store/generationSlice';
import { addToast } from 'features/system/store/systemSlice';
import { t } from 'i18next';
import { omit } from 'lodash-es';
import { boardsApi } from 'services/api/endpoints/boards';
import { imagesApi } from 'services/api/endpoints/images';
import { startAppListening } from '..';
import { imagesApi } from 'services/api/endpoints/images';
export const addImageUploadedFulfilledListener = () => {
startAppListening({
@@ -79,9 +75,7 @@ export const addImageUploadedFulfilledListener = () => {
}
if (postUploadAction?.type === 'SET_CANVAS_INITIAL_IMAGE') {
dispatch(
setInitialCanvasImage(imageDTO, selectOptimalDimension(state))
);
dispatch(setInitialCanvasImage(imageDTO));
dispatch(
addToast({
...DEFAULT_UPLOADED_TOAST,

View File

@@ -1,8 +1,7 @@
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { imagesApi } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
import { startAppListening } from '..';
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { ImageDTO } from 'services/api/types';
export const addImagesStarredListener = () => {
startAppListening({

View File

@@ -1,8 +1,7 @@
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { imagesApi } from 'services/api/endpoints/images';
import type { ImageDTO } from 'services/api/types';
import { startAppListening } from '..';
import { selectionChanged } from 'features/gallery/store/gallerySlice';
import { ImageDTO } from 'services/api/types';
export const addImagesUnstarredListener = () => {
startAppListening({

View File

@@ -3,7 +3,6 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice';
import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast';
import { t } from 'i18next';
import { startAppListening } from '..';
export const addInitialImageSelectedListener = () => {

View File

@@ -1,4 +1,5 @@
import { logger } from 'app/logging/logger';
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
import {
controlAdapterIsEnabledChanged,
selectControlAdapterAll,
@@ -7,15 +8,16 @@ import { loraRemoved } from 'features/lora/store/loraSlice';
import { modelSelected } from 'features/parameters/store/actions';
import {
modelChanged,
setHeight,
setWidth,
vaeSelected,
} from 'features/parameters/store/generationSlice';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast';
import { t } from 'i18next';
import { forEach } from 'lodash-es';
import { startAppListening } from '..';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
export const addModelSelectedListener = () => {
startAppListening({
@@ -81,6 +83,22 @@ export const addModelSelectedListener = () => {
}
}
// Update Width / Height / Bounding Box Dimensions on Model Change
if (
state.generation.model?.base_model !== newModel.base_model &&
state.ui.shouldAutoChangeDimensions
) {
if (['sdxl', 'sdxl-refiner'].includes(newModel.base_model)) {
dispatch(setWidth(1024));
dispatch(setHeight(1024));
dispatch(setBoundingBoxDimensions({ width: 1024, height: 1024 }));
} else {
dispatch(setWidth(512));
dispatch(setHeight(512));
dispatch(setBoundingBoxDimensions({ width: 512, height: 512 }));
}
}
dispatch(modelChanged(newModel));
},
});

View File

@@ -12,17 +12,20 @@ import {
} from 'features/parameters/store/generationSlice';
import {
zParameterModel,
zParameterSDXLRefinerModel,
zParameterVAEModel,
} from 'features/parameters/types/parameterSchemas';
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
import {
refinerModelChanged,
setShouldUseSDXLRefiner,
} from 'features/sdxl/store/sdxlSlice';
import { forEach, some } from 'lodash-es';
import {
mainModelsAdapterSelectors,
mainModelsAdapter,
modelsApi,
vaeModelsAdapterSelectors,
vaeModelsAdapter,
} from 'services/api/endpoints/models';
import type { TypeGuardFor } from 'services/api/types';
import { TypeGuardFor } from 'services/api/types';
import { startAppListening } from '..';
export const addModelsLoadedListener = () => {
@@ -43,7 +46,7 @@ export const addModelsLoadedListener = () => {
);
const currentModel = getState().generation.model;
const models = mainModelsAdapterSelectors.selectAll(action.payload);
const models = mainModelsAdapter.getSelectors().selectAll(action.payload);
if (models.length === 0) {
// No models loaded at all
@@ -94,11 +97,12 @@ export const addModelsLoadedListener = () => {
);
const currentModel = getState().sdxl.refinerModel;
const models = mainModelsAdapterSelectors.selectAll(action.payload);
const models = mainModelsAdapter.getSelectors().selectAll(action.payload);
if (models.length === 0) {
// No models loaded at all
dispatch(refinerModelChanged(null));
dispatch(setShouldUseSDXLRefiner(false));
return;
}
@@ -111,10 +115,21 @@ export const addModelsLoadedListener = () => {
)
: false;
if (!isCurrentModelAvailable) {
dispatch(refinerModelChanged(null));
if (isCurrentModelAvailable) {
return;
}
const result = zParameterSDXLRefinerModel.safeParse(models[0]);
if (!result.success) {
log.error(
{ error: result.error.format() },
'Failed to parse SDXL Refiner Model'
);
return;
}
dispatch(refinerModelChanged(result.data));
},
});
startAppListening({
@@ -145,7 +160,9 @@ export const addModelsLoadedListener = () => {
return;
}
const firstModel = vaeModelsAdapterSelectors.selectAll(action.payload)[0];
const firstModel = vaeModelsAdapter
.getSelectors()
.selectAll(action.payload)[0];
if (!firstModel) {
// No custom VAEs loaded at all; use the default

View File

@@ -8,11 +8,9 @@ import {
parsingErrorChanged,
promptsChanged,
} from 'features/dynamicPrompts/store/dynamicPromptsSlice';
import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt';
import { setPositivePrompt } from 'features/parameters/store/generationSlice';
import { utilitiesApi } from 'services/api/endpoints/utilities';
import { appSocketConnected } from 'services/events/actions';
import { startAppListening } from '..';
const matcher = isAnyOf(
@@ -30,39 +28,20 @@ export const addDynamicPromptsListener = () => {
action,
{ dispatch, getState, cancelActiveListeners, delay }
) => {
// debounce request
cancelActiveListeners();
await delay(1000);
const state = getState();
const { positivePrompt } = state.generation;
const { maxPrompts } = state.dynamicPrompts;
if (state.config.disabledFeatures.includes('dynamicPrompting')) {
return;
}
const cachedPrompts = utilitiesApi.endpoints.dynamicPrompts.select({
prompt: positivePrompt,
max_prompts: maxPrompts,
})(getState()).data;
const { positivePrompt } = state.generation;
const { maxPrompts } = state.dynamicPrompts;
if (cachedPrompts) {
dispatch(promptsChanged(cachedPrompts.prompts));
return;
}
if (!getShouldProcessPrompt(state.generation.positivePrompt)) {
if (state.dynamicPrompts.isLoading) {
dispatch(isLoadingChanged(false));
}
dispatch(promptsChanged([state.generation.positivePrompt]));
return;
}
if (!state.dynamicPrompts.isLoading) {
dispatch(isLoadingChanged(true));
}
// debounce request
await delay(1000);
dispatch(isLoadingChanged(true));
try {
const req = dispatch(

View File

@@ -1,10 +1,9 @@
import { logger } from 'app/logging/logger';
import { parseify } from 'common/util/serialize';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodeTemplatesSlice';
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
import { parseSchema } from 'features/nodes/util/schema/parseSchema';
import { size } from 'lodash-es';
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
import { startAppListening } from '..';
export const addReceivedOpenAPISchemaListener = () => {

View File

@@ -1,11 +1,10 @@
import { logger } from 'app/logging/logger';
import { isInitializedChanged } from 'features/system/store/systemSlice';
import { size } from 'lodash-es';
import { api } from 'services/api';
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
import { appSocketConnected, socketConnected } from 'services/events/actions';
import { startAppListening } from '../..';
import { isInitializedChanged } from 'features/system/store/systemSlice';
export const addSocketConnectedEventListener = () => {
startAppListening({
@@ -15,11 +14,11 @@ export const addSocketConnectedEventListener = () => {
log.debug('Connected');
const { nodeTemplates, config, system } = getState();
const { nodes, config, system } = getState();
const { disabledTabs } = config;
if (!size(nodeTemplates.templates) && !disabledTabs.includes('nodes')) {
if (!size(nodes.nodeTemplates) && !disabledTabs.includes('nodes')) {
dispatch(receivedOpenAPISchema());
}

Some files were not shown because too many files have changed in this diff Show More