From 884ec0b5dfadf12a630674b67f75385b0b593ab0 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:06:21 +1100 Subject: [PATCH 01/24] feat: replace isort, flake8 & black with ruff --- pyproject.toml | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fc9c150e3e..b74d4e7367 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,10 +35,10 @@ dependencies = [ "accelerate~=0.23.0", "albumentations", "click", - "clip_anytorch", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip", + "clip_anytorch", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip", "compel~=2.0.2", "controlnet-aux>=0.0.6", - "timm==0.6.13", # needed to override timm latest in controlnet_aux, see https://github.com/isl-org/ZoeDepth/issues/26 + "timm==0.6.13", # needed to override timm latest in controlnet_aux, see https://github.com/isl-org/ZoeDepth/issues/26 "datasets", "diffusers[torch]~=0.23.0", "dnspython~=2.4.0", @@ -96,10 +96,7 @@ dependencies = [ ] "dev" = ["jurigged", "pudb"] "test" = [ - "black", - "flake8", - "Flake8-pyproject", - "isort", + "ruff", "mypy", "pre-commit", "pytest>6.0.0", @@ -108,7 +105,7 @@ dependencies = [ ] "xformers" = [ "xformers==0.0.22post7; sys_platform!='darwin'", - "triton; sys_platform=='linux'", + "triton; sys_platform=='linux'", ] "onnx" = ["onnxruntime"] "onnx-cuda" = ["onnxruntime-gpu"] @@ -194,10 +191,16 @@ directory = "coverage/html" output = "coverage/index.xml" #=== End: PyTest and Coverage -[tool.flake8] -max-line-length = 120 -ignore = ["E203", "E266", "E501", "W503"] -select = ["B", "C", "E", "F", "W", "T4"] +#=== Begin: Ruff +[tool.ruff] +line-length = 120 +ignore = [ + "E501", # https://docs.astral.sh/ruff/rules/line-too-long/ + "C901", # https://docs.astral.sh/ruff/rules/complex-structure/ + "B008", # https://docs.astral.sh/ruff/rules/function-call-in-default-argument/ + "B904", # https://docs.astral.sh/ruff/rules/raise-without-from-inside-except/ +] +select = ["B", "C", "E", "F", "W"] exclude = [ ".git", "__pycache__", @@ -206,14 +209,9 @@ exclude = [ "invokeai/frontend/web/node_modules/", ".venv*", ] +#=== End: Ruff -[tool.black] -line-length = 120 - -[tool.isort] -profile = "black" -line_length = 120 - +#=== Begin: MyPy [tool.mypy] ignore_missing_imports = true # ignores missing types in third-party libraries @@ -263,3 +261,4 @@ module = [ "invokeai.backend.util.util", "invokeai.frontend.install.model_install", ] +#=== End: MyPy From 99e4b87fae6cd564bcb49d931d3a4b288103e7ea Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:14:21 +1100 Subject: [PATCH 02/24] feat: use ruff in GH style-checks action --- .github/workflows/style-checks.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index 08ff8ba402..fc86af522e 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -18,8 +18,7 @@ jobs: - name: Install dependencies with pip run: | - pip install black flake8 Flake8-pyproject isort + pip install ruff - - run: isort --check-only . - - run: black --check . - - run: flake8 + - run: ruff check . + - run: ruff format --check . From 8111dd6cc5ce6a132452fe003aa2020819e054af Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:17:03 +1100 Subject: [PATCH 03/24] feat: remove pyflakes gh action ruff supersedes it --- .github/workflows/pyflakes.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/workflows/pyflakes.yml diff --git a/.github/workflows/pyflakes.yml b/.github/workflows/pyflakes.yml deleted file mode 100644 index 4bda2dd103..0000000000 --- a/.github/workflows/pyflakes.yml +++ /dev/null @@ -1,20 +0,0 @@ -on: - pull_request: - push: - branches: - - main - - development - - 'release-candidate-*' - -jobs: - pyflakes: - name: runner / pyflakes - if: github.event.pull_request.draft == false - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: pyflakes - uses: reviewdog/action-pyflakes@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - reporter: github-pr-review From d0cf98d7f6f8a74fa056c0ad63e1bfbb97ae106e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:24:53 +1100 Subject: [PATCH 04/24] feat: add ruff-lsp to support most editors --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index b74d4e7367..bdf23c4bf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,6 +97,7 @@ dependencies = [ "dev" = ["jurigged", "pudb"] "test" = [ "ruff", + "ruff-lsp", "mypy", "pre-commit", "pytest>6.0.0", From 43f2398e14f42683695472a3d674a0e85dc7721c Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:28:26 +1100 Subject: [PATCH 05/24] feat: use ruff's github output format for action --- .github/workflows/style-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/style-checks.yml b/.github/workflows/style-checks.yml index fc86af522e..121af62e1b 100644 --- a/.github/workflows/style-checks.yml +++ b/.github/workflows/style-checks.yml @@ -20,5 +20,5 @@ jobs: run: | pip install ruff - - run: ruff check . + - run: ruff check --output-format=github . - run: ruff format --check . From 3a136420d53c9c27500cacb596d16b302ef2ea35 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:44:43 +1100 Subject: [PATCH 06/24] chore: ruff check - fix flake8-comprensions --- invokeai/app/api/events.py | 2 +- invokeai/app/api/routers/models.py | 2 +- invokeai/app/api_app.py | 14 +- invokeai/app/invocations/__init__.py | 2 +- invokeai/app/invocations/baseinvocation.py | 117 +++++---- invokeai/app/invocations/facetools.py | 2 +- invokeai/app/invocations/latent.py | 2 +- invokeai/app/invocations/math.py | 44 ++-- invokeai/app/invocations/onnx.py | 4 +- invokeai/app/invocations/param_easing.py | 6 +- .../board_image_records_sqlite.py | 4 +- .../board_records/board_records_sqlite.py | 4 +- invokeai/app/services/config/config_base.py | 6 +- .../app/services/config/config_default.py | 26 +- invokeai/app/services/events/events_base.py | 230 +++++++++--------- .../services/image_files/image_files_disk.py | 2 +- .../image_records/image_records_common.py | 9 +- .../image_records/image_records_sqlite.py | 6 +- invokeai/app/services/images/images_base.py | 4 +- .../app/services/images/images_default.py | 9 +- .../invocation_processor_default.py | 2 +- .../invocation_queue_memory.py | 2 +- .../invocation_stats_default.py | 2 +- .../item_storage/item_storage_base.py | 4 +- .../item_storage/item_storage_sqlite.py | 4 +- .../latents_storage/latents_storage_base.py | 4 +- .../latents_storage_forward_cache.py | 2 +- .../session_processor_default.py | 6 +- .../session_queue/session_queue_common.py | 18 +- .../app/services/shared/default_graphs.py | 2 +- invokeai/app/services/shared/graph.py | 54 ++-- invokeai/backend/image_util/pngwriter.py | 2 +- invokeai/backend/image_util/util.py | 2 +- .../backend/install/invokeai_configure.py | 44 ++-- invokeai/backend/install/migrate_to_3.py | 14 +- .../backend/install/model_install_backend.py | 32 ++- invokeai/backend/model_management/lora.py | 10 +- .../backend/model_management/model_cache.py | 6 +- .../backend/model_management/model_manager.py | 10 +- .../backend/model_management/model_merge.py | 16 +- .../backend/model_management/model_search.py | 6 +- .../model_management/models/__init__.py | 6 +- .../backend/model_management/models/base.py | 18 +- .../model_management/models/controlnet.py | 2 +- .../backend/model_management/models/lora.py | 10 +- .../models/stable_diffusion.py | 4 +- .../models/textual_inversion.py | 2 +- .../backend/model_management/models/vae.py | 2 +- .../stable_diffusion/schedulers/schedulers.py | 50 ++-- .../training/textual_inversion_training.py | 2 +- invokeai/backend/util/logging.py | 58 ++--- invokeai/backend/util/util.py | 4 +- invokeai/frontend/install/import_images.py | 26 +- invokeai/frontend/install/model_install.py | 18 +- invokeai/frontend/merge/merge_diffusers.py | 18 +- .../frontend/training/textual_inversion.py | 4 +- scripts/scan_models_directory.py | 24 +- tests/nodes/test_graph_execution_state.py | 6 +- tests/nodes/test_node_graph.py | 8 +- tests/nodes/test_nodes.py | 2 +- 60 files changed, 489 insertions(+), 512 deletions(-) diff --git a/invokeai/app/api/events.py b/invokeai/app/api/events.py index 40dfdb2c71..2ac07e6dfe 100644 --- a/invokeai/app/api/events.py +++ b/invokeai/app/api/events.py @@ -28,7 +28,7 @@ class FastAPIEventService(EventServiceBase): self.__queue.put(None) def dispatch(self, event_name: str, payload: Any) -> None: - self.__queue.put(dict(event_name=event_name, payload=payload)) + self.__queue.put({"event_name": event_name, "payload": payload}) async def __dispatch_from_queue(self, stop_event: threading.Event): """Get events on from the queue and dispatch them, from the correct thread""" diff --git a/invokeai/app/api/routers/models.py b/invokeai/app/api/routers/models.py index afa7d8df82..cf3d31cc38 100644 --- a/invokeai/app/api/routers/models.py +++ b/invokeai/app/api/routers/models.py @@ -55,7 +55,7 @@ async def list_models( ) -> ModelsList: """Gets a list of models""" if base_models and len(base_models) > 0: - models_raw = list() + models_raw = [] for base_model in base_models: models_raw.extend(ApiDependencies.invoker.services.model_manager.list_models(base_model, model_type)) else: diff --git a/invokeai/app/api_app.py b/invokeai/app/api_app.py index 51aa14c75b..19bdd084e2 100644 --- a/invokeai/app/api_app.py +++ b/invokeai/app/api_app.py @@ -130,7 +130,7 @@ def custom_openapi() -> dict[str, Any]: # Add all outputs all_invocations = BaseInvocation.get_invocations() output_types = set() - output_type_titles = dict() + output_type_titles = {} for invoker in all_invocations: output_type = signature(invoker.invoke).return_annotation output_types.add(output_type) @@ -171,12 +171,12 @@ def custom_openapi() -> dict[str, Any]: # print(f"Config with name {name} already defined") continue - openapi_schema["components"]["schemas"][name] = dict( - title=name, - description="An enumeration.", - type="string", - enum=list(v.value for v in model_config_format_enum), - ) + openapi_schema["components"]["schemas"][name] = { + "title": name, + "description": "An enumeration.", + "type": "string", + "enum": [v.value for v in model_config_format_enum], + } app.openapi_schema = openapi_schema return app.openapi_schema diff --git a/invokeai/app/invocations/__init__.py b/invokeai/app/invocations/__init__.py index 32cf73d215..718c4a7c38 100644 --- a/invokeai/app/invocations/__init__.py +++ b/invokeai/app/invocations/__init__.py @@ -25,4 +25,4 @@ spec.loader.exec_module(module) # add core nodes to __all__ python_files = filter(lambda f: not f.name.startswith("_"), Path(__file__).parent.glob("*.py")) -__all__ = list(f.stem for f in python_files) # type: ignore +__all__ = [f.stem for f in python_files] # type: ignore diff --git a/invokeai/app/invocations/baseinvocation.py b/invokeai/app/invocations/baseinvocation.py index ea79e0cceb..a984d67dfa 100644 --- a/invokeai/app/invocations/baseinvocation.py +++ b/invokeai/app/invocations/baseinvocation.py @@ -236,35 +236,35 @@ def InputField( Ignored for non-collection fields. """ - json_schema_extra_: dict[str, Any] = dict( - input=input, - ui_type=ui_type, - ui_component=ui_component, - ui_hidden=ui_hidden, - ui_order=ui_order, - item_default=item_default, - ui_choice_labels=ui_choice_labels, - _field_kind="input", - ) + json_schema_extra_: dict[str, Any] = { + "input": input, + "ui_type": ui_type, + "ui_component": ui_component, + "ui_hidden": ui_hidden, + "ui_order": ui_order, + "item_default": item_default, + "ui_choice_labels": ui_choice_labels, + "_field_kind": "input", + } - field_args = dict( - default=default, - default_factory=default_factory, - title=title, - description=description, - pattern=pattern, - strict=strict, - gt=gt, - ge=ge, - lt=lt, - le=le, - multiple_of=multiple_of, - allow_inf_nan=allow_inf_nan, - max_digits=max_digits, - decimal_places=decimal_places, - min_length=min_length, - max_length=max_length, - ) + field_args = { + "default": default, + "default_factory": default_factory, + "title": title, + "description": description, + "pattern": pattern, + "strict": strict, + "gt": gt, + "ge": ge, + "lt": lt, + "le": le, + "multiple_of": multiple_of, + "allow_inf_nan": allow_inf_nan, + "max_digits": max_digits, + "decimal_places": decimal_places, + "min_length": min_length, + "max_length": max_length, + } """ Invocation definitions have their fields typed correctly for their `invoke()` functions. @@ -299,24 +299,24 @@ def InputField( # because we are manually making fields optional, we need to store the original required bool for reference later if default is PydanticUndefined and default_factory is PydanticUndefined: - json_schema_extra_.update(dict(orig_required=True)) + json_schema_extra_.update({"orig_required": True}) else: - json_schema_extra_.update(dict(orig_required=False)) + json_schema_extra_.update({"orig_required": False}) # make Input.Any and Input.Connection fields optional, providing None as a default if the field doesn't already have one if (input is Input.Any or input is Input.Connection) and default_factory is PydanticUndefined: default_ = None if default is PydanticUndefined else default - provided_args.update(dict(default=default_)) + provided_args.update({"default": default_}) if default is not PydanticUndefined: # before invoking, we'll grab the original default value and set it on the field if the field wasn't provided a value - json_schema_extra_.update(dict(default=default)) - json_schema_extra_.update(dict(orig_default=default)) + json_schema_extra_.update({"default": default}) + json_schema_extra_.update({"orig_default": default}) elif default is not PydanticUndefined and default_factory is PydanticUndefined: default_ = default - provided_args.update(dict(default=default_)) - json_schema_extra_.update(dict(orig_default=default_)) + provided_args.update({"default": default_}) + json_schema_extra_.update({"orig_default": default_}) elif default_factory is not PydanticUndefined: - provided_args.update(dict(default_factory=default_factory)) + provided_args.update({"default_factory": default_factory}) # TODO: cannot serialize default_factory... # json_schema_extra_.update(dict(orig_default_factory=default_factory)) @@ -383,12 +383,12 @@ def OutputField( decimal_places=decimal_places, min_length=min_length, max_length=max_length, - json_schema_extra=dict( - ui_type=ui_type, - ui_hidden=ui_hidden, - ui_order=ui_order, - _field_kind="output", - ), + json_schema_extra={ + "ui_type": ui_type, + "ui_hidden": ui_hidden, + "ui_order": ui_order, + "_field_kind": "output", + }, ) @@ -460,14 +460,14 @@ class BaseInvocationOutput(BaseModel): @classmethod def get_output_types(cls) -> Iterable[str]: - return map(lambda i: get_type(i), BaseInvocationOutput.get_outputs()) + return (get_type(i) for i in BaseInvocationOutput.get_outputs()) @staticmethod def json_schema_extra(schema: dict[str, Any], model_class: Type[BaseModel]) -> None: # Because we use a pydantic Literal field with default value for the invocation type, # it will be typed as optional in the OpenAPI schema. Make it required manually. if "required" not in schema or not isinstance(schema["required"], list): - schema["required"] = list() + schema["required"] = [] schema["required"].extend(["type"]) model_config = ConfigDict( @@ -527,16 +527,11 @@ class BaseInvocation(ABC, BaseModel): @classmethod def get_invocations_map(cls) -> dict[str, BaseInvocation]: # Get the type strings out of the literals and into a dictionary - return dict( - map( - lambda i: (get_type(i), i), - BaseInvocation.get_invocations(), - ) - ) + return {get_type(i): i for i in BaseInvocation.get_invocations()} @classmethod def get_invocation_types(cls) -> Iterable[str]: - return map(lambda i: get_type(i), BaseInvocation.get_invocations()) + return (get_type(i) for i in BaseInvocation.get_invocations()) @classmethod def get_output_type(cls) -> BaseInvocationOutput: @@ -555,7 +550,7 @@ class BaseInvocation(ABC, BaseModel): if uiconfig and hasattr(uiconfig, "version"): schema["version"] = uiconfig.version if "required" not in schema or not isinstance(schema["required"], list): - schema["required"] = list() + schema["required"] = [] schema["required"].extend(["type", "id"]) @abstractmethod @@ -609,15 +604,15 @@ class BaseInvocation(ABC, BaseModel): id: str = Field( default_factory=uuid_string, description="The id of this instance of an invocation. Must be unique among all instances of invocations.", - json_schema_extra=dict(_field_kind="internal"), + json_schema_extra={"_field_kind": "internal"}, ) is_intermediate: bool = Field( default=False, description="Whether or not this is an intermediate invocation.", - json_schema_extra=dict(ui_type=UIType.IsIntermediate, _field_kind="internal"), + json_schema_extra={"ui_type": UIType.IsIntermediate, "_field_kind": "internal"}, ) use_cache: bool = Field( - default=True, description="Whether or not to use the cache", json_schema_extra=dict(_field_kind="internal") + default=True, description="Whether or not to use the cache", json_schema_extra={"_field_kind": "internal"} ) UIConfig: ClassVar[Type[UIConfigBase]] @@ -651,7 +646,7 @@ class _Model(BaseModel): # Get all pydantic model attrs, methods, etc -RESERVED_PYDANTIC_FIELD_NAMES = set(map(lambda m: m[0], inspect.getmembers(_Model()))) +RESERVED_PYDANTIC_FIELD_NAMES = {m[0] for m in inspect.getmembers(_Model())} def validate_fields(model_fields: dict[str, FieldInfo], model_type: str) -> None: @@ -729,7 +724,7 @@ def invocation( # Add OpenAPI schema extras uiconf_name = cls.__qualname__ + ".UIConfig" if not hasattr(cls, "UIConfig") or cls.UIConfig.__qualname__ != uiconf_name: - cls.UIConfig = type(uiconf_name, (UIConfigBase,), dict()) + cls.UIConfig = type(uiconf_name, (UIConfigBase,), {}) if title is not None: cls.UIConfig.title = title if tags is not None: @@ -756,7 +751,7 @@ def invocation( invocation_type_annotation = Literal[invocation_type] # type: ignore invocation_type_field = Field( - title="type", default=invocation_type, json_schema_extra=dict(_field_kind="internal") + title="type", default=invocation_type, json_schema_extra={"_field_kind": "internal"} ) docstring = cls.__doc__ @@ -802,7 +797,7 @@ def invocation_output( # Add the output type to the model. output_type_annotation = Literal[output_type] # type: ignore - output_type_field = Field(title="type", default=output_type, json_schema_extra=dict(_field_kind="internal")) + output_type_field = Field(title="type", default=output_type, json_schema_extra={"_field_kind": "internal"}) docstring = cls.__doc__ cls = create_model( @@ -834,7 +829,7 @@ WorkflowFieldValidator = TypeAdapter(WorkflowField) class WithWorkflow(BaseModel): workflow: Optional[WorkflowField] = Field( - default=None, description=FieldDescriptions.workflow, json_schema_extra=dict(_field_kind="internal") + default=None, description=FieldDescriptions.workflow, json_schema_extra={"_field_kind": "internal"} ) @@ -852,5 +847,5 @@ MetadataFieldValidator = TypeAdapter(MetadataField) class WithMetadata(BaseModel): metadata: Optional[MetadataField] = Field( - default=None, description=FieldDescriptions.metadata, json_schema_extra=dict(_field_kind="internal") + default=None, description=FieldDescriptions.metadata, json_schema_extra={"_field_kind": "internal"} ) diff --git a/invokeai/app/invocations/facetools.py b/invokeai/app/invocations/facetools.py index 0bb24ef69d..2fe4565d78 100644 --- a/invokeai/app/invocations/facetools.py +++ b/invokeai/app/invocations/facetools.py @@ -131,7 +131,7 @@ def prepare_faces_list( deduped_faces: list[FaceResultData] = [] if len(face_result_list) == 0: - return list() + return [] for candidate in face_result_list: should_add = True diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index c9a0ca4423..d86dbc7166 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -77,7 +77,7 @@ if choose_torch_device() == torch.device("mps"): DEFAULT_PRECISION = choose_precision(choose_torch_device()) -SAMPLER_NAME_VALUES = Literal[tuple(list(SCHEDULER_MAP.keys()))] +SAMPLER_NAME_VALUES = Literal[tuple(SCHEDULER_MAP.keys())] @invocation_output("scheduler_output") diff --git a/invokeai/app/invocations/math.py b/invokeai/app/invocations/math.py index 585122d091..c50da4c16f 100644 --- a/invokeai/app/invocations/math.py +++ b/invokeai/app/invocations/math.py @@ -145,17 +145,17 @@ INTEGER_OPERATIONS = Literal[ ] -INTEGER_OPERATIONS_LABELS = dict( - ADD="Add A+B", - SUB="Subtract A-B", - MUL="Multiply A*B", - DIV="Divide A/B", - EXP="Exponentiate A^B", - MOD="Modulus A%B", - ABS="Absolute Value of A", - MIN="Minimum(A,B)", - MAX="Maximum(A,B)", -) +INTEGER_OPERATIONS_LABELS = { + "ADD": "Add A+B", + "SUB": "Subtract A-B", + "MUL": "Multiply A*B", + "DIV": "Divide A/B", + "EXP": "Exponentiate A^B", + "MOD": "Modulus A%B", + "ABS": "Absolute Value of A", + "MIN": "Minimum(A,B)", + "MAX": "Maximum(A,B)", +} @invocation( @@ -231,17 +231,17 @@ FLOAT_OPERATIONS = Literal[ ] -FLOAT_OPERATIONS_LABELS = dict( - ADD="Add A+B", - SUB="Subtract A-B", - MUL="Multiply A*B", - DIV="Divide A/B", - EXP="Exponentiate A^B", - ABS="Absolute Value of A", - SQRT="Square Root of A", - MIN="Minimum(A,B)", - MAX="Maximum(A,B)", -) +FLOAT_OPERATIONS_LABELS = { + "ADD": "Add A+B", + "SUB": "Subtract A-B", + "MUL": "Multiply A*B", + "DIV": "Divide A/B", + "EXP": "Exponentiate A^B", + "ABS": "Absolute Value of A", + "SQRT": "Square Root of A", + "MIN": "Minimum(A,B)", + "MAX": "Maximum(A,B)", +} @invocation( diff --git a/invokeai/app/invocations/onnx.py b/invokeai/app/invocations/onnx.py index 699930fc06..45b5ed61b1 100644 --- a/invokeai/app/invocations/onnx.py +++ b/invokeai/app/invocations/onnx.py @@ -54,7 +54,7 @@ ORT_TO_NP_TYPE = { "tensor(double)": np.float64, } -PRECISION_VALUES = Literal[tuple(list(ORT_TO_NP_TYPE.keys()))] +PRECISION_VALUES = Literal[tuple(ORT_TO_NP_TYPE.keys())] @invocation("prompt_onnx", title="ONNX Prompt (Raw)", tags=["prompt", "onnx"], category="conditioning", version="1.0.0") @@ -252,7 +252,7 @@ class ONNXTextToLatentsInvocation(BaseInvocation): scheduler.set_timesteps(self.steps) latents = latents * np.float64(scheduler.init_noise_sigma) - extra_step_kwargs = dict() + extra_step_kwargs = {} if "eta" in set(inspect.signature(scheduler.step).parameters.keys()): extra_step_kwargs.update( eta=0.0, diff --git a/invokeai/app/invocations/param_easing.py b/invokeai/app/invocations/param_easing.py index 0e86fb978b..dccd18f754 100644 --- a/invokeai/app/invocations/param_easing.py +++ b/invokeai/app/invocations/param_easing.py @@ -100,7 +100,7 @@ EASING_FUNCTIONS_MAP = { "BounceInOut": BounceEaseInOut, } -EASING_FUNCTION_KEYS = Literal[tuple(list(EASING_FUNCTIONS_MAP.keys()))] +EASING_FUNCTION_KEYS = Literal[tuple(EASING_FUNCTIONS_MAP.keys())] # actually I think for now could just use CollectionOutput (which is list[Any] @@ -161,7 +161,7 @@ class StepParamEasingInvocation(BaseInvocation): easing_class = EASING_FUNCTIONS_MAP[self.easing] if log_diagnostics: context.services.logger.debug("easing class: " + str(easing_class)) - easing_list = list() + easing_list = [] if self.mirror: # "expected" mirroring # if number of steps is even, squeeze duration down to (number_of_steps)/2 # and create reverse copy of list to append @@ -178,7 +178,7 @@ class StepParamEasingInvocation(BaseInvocation): end=self.end_value, duration=base_easing_duration - 1, ) - base_easing_vals = list() + base_easing_vals = [] for step_index in range(base_easing_duration): easing_val = easing_function.ease(step_index) base_easing_vals.append(easing_val) diff --git a/invokeai/app/services/board_image_records/board_image_records_sqlite.py b/invokeai/app/services/board_image_records/board_image_records_sqlite.py index 9f4e4379bc..02bafd00ec 100644 --- a/invokeai/app/services/board_image_records/board_image_records_sqlite.py +++ b/invokeai/app/services/board_image_records/board_image_records_sqlite.py @@ -139,7 +139,7 @@ class SqliteBoardImageRecordStorage(BoardImageRecordStorageBase): (board_id,), ) result = cast(list[sqlite3.Row], self._cursor.fetchall()) - images = list(map(lambda r: deserialize_image_record(dict(r)), result)) + images = [deserialize_image_record(dict(r)) for r in result] self._cursor.execute( """--sql @@ -167,7 +167,7 @@ class SqliteBoardImageRecordStorage(BoardImageRecordStorageBase): (board_id,), ) result = cast(list[sqlite3.Row], self._cursor.fetchall()) - image_names = list(map(lambda r: r[0], result)) + image_names = [r[0] for r in result] return image_names except sqlite3.Error as e: self._conn.rollback() diff --git a/invokeai/app/services/board_records/board_records_sqlite.py b/invokeai/app/services/board_records/board_records_sqlite.py index 9e3423ab19..ef507def2a 100644 --- a/invokeai/app/services/board_records/board_records_sqlite.py +++ b/invokeai/app/services/board_records/board_records_sqlite.py @@ -199,7 +199,7 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase): ) result = cast(list[sqlite3.Row], self._cursor.fetchall()) - boards = list(map(lambda r: deserialize_board_record(dict(r)), result)) + boards = [deserialize_board_record(dict(r)) for r in result] # Get the total number of boards self._cursor.execute( @@ -236,7 +236,7 @@ class SqliteBoardRecordStorage(BoardRecordStorageBase): ) result = cast(list[sqlite3.Row], self._cursor.fetchall()) - boards = list(map(lambda r: deserialize_board_record(dict(r)), result)) + boards = [deserialize_board_record(dict(r)) for r in result] return boards diff --git a/invokeai/app/services/config/config_base.py b/invokeai/app/services/config/config_base.py index 9405c1dfae..66c456d311 100644 --- a/invokeai/app/services/config/config_base.py +++ b/invokeai/app/services/config/config_base.py @@ -55,7 +55,7 @@ class InvokeAISettings(BaseSettings): """ cls = self.__class__ type = get_args(get_type_hints(cls)["type"])[0] - field_dict = dict({type: dict()}) + field_dict = {type: {}} for name, field in self.model_fields.items(): if name in cls._excluded_from_yaml(): continue @@ -64,7 +64,7 @@ class InvokeAISettings(BaseSettings): ) value = getattr(self, name) if category not in field_dict[type]: - field_dict[type][category] = dict() + field_dict[type][category] = {} # keep paths as strings to make it easier to read field_dict[type][category][name] = str(value) if isinstance(value, Path) else value conf = OmegaConf.create(field_dict) @@ -89,7 +89,7 @@ class InvokeAISettings(BaseSettings): # create an upcase version of the environment in # order to achieve case-insensitive environment # variables (the way Windows does) - upcase_environ = dict() + upcase_environ = {} for key, value in os.environ.items(): upcase_environ[key.upper()] = value diff --git a/invokeai/app/services/config/config_default.py b/invokeai/app/services/config/config_default.py index f0e9dbcda4..30c6694ddb 100644 --- a/invokeai/app/services/config/config_default.py +++ b/invokeai/app/services/config/config_default.py @@ -188,18 +188,18 @@ DEFAULT_MAX_VRAM = 0.5 class Categories(object): - WebServer = dict(category="Web Server") - Features = dict(category="Features") - Paths = dict(category="Paths") - Logging = dict(category="Logging") - Development = dict(category="Development") - Other = dict(category="Other") - ModelCache = dict(category="Model Cache") - Device = dict(category="Device") - Generation = dict(category="Generation") - Queue = dict(category="Queue") - Nodes = dict(category="Nodes") - MemoryPerformance = dict(category="Memory/Performance") + WebServer = {"category": "Web Server"} + Features = {"category": "Features"} + Paths = {"category": "Paths"} + Logging = {"category": "Logging"} + Development = {"category": "Development"} + Other = {"category": "Other"} + ModelCache = {"category": "Model Cache"} + Device = {"category": "Device"} + Generation = {"category": "Generation"} + Queue = {"category": "Queue"} + Nodes = {"category": "Nodes"} + MemoryPerformance = {"category": "Memory/Performance"} class InvokeAIAppConfig(InvokeAISettings): @@ -482,7 +482,7 @@ def _find_root() -> Path: venv = Path(os.environ.get("VIRTUAL_ENV") or ".") if os.environ.get("INVOKEAI_ROOT"): root = Path(os.environ["INVOKEAI_ROOT"]) - elif any([(venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]]): + elif any((venv.parent / x).exists() for x in [INIT_FILE, LEGACY_INIT_FILE]): root = (venv.parent).resolve() else: root = Path("~/invokeai").expanduser().resolve() diff --git a/invokeai/app/services/events/events_base.py b/invokeai/app/services/events/events_base.py index ad00815151..dd4152e609 100644 --- a/invokeai/app/services/events/events_base.py +++ b/invokeai/app/services/events/events_base.py @@ -27,7 +27,7 @@ class EventServiceBase: payload["timestamp"] = get_timestamp() self.dispatch( event_name=EventServiceBase.queue_event, - payload=dict(event=event_name, data=payload), + payload={"event": event_name, "data": payload}, ) # Define events here for every event in the system. @@ -48,18 +48,18 @@ class EventServiceBase: """Emitted when there is generation progress""" self.__emit_queue_event( event_name="generator_progress", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - node_id=node.get("id"), - source_node_id=source_node_id, - progress_image=progress_image.model_dump() if progress_image is not None else None, - step=step, - order=order, - total_steps=total_steps, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "node_id": node.get("id"), + "source_node_id": source_node_id, + "progress_image": progress_image.model_dump() if progress_image is not None else None, + "step": step, + "order": order, + "total_steps": total_steps, + }, ) def emit_invocation_complete( @@ -75,15 +75,15 @@ class EventServiceBase: """Emitted when an invocation has completed""" self.__emit_queue_event( event_name="invocation_complete", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - node=node, - source_node_id=source_node_id, - result=result, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "node": node, + "source_node_id": source_node_id, + "result": result, + }, ) def emit_invocation_error( @@ -100,16 +100,16 @@ class EventServiceBase: """Emitted when an invocation has completed""" self.__emit_queue_event( event_name="invocation_error", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - node=node, - source_node_id=source_node_id, - error_type=error_type, - error=error, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "node": node, + "source_node_id": source_node_id, + "error_type": error_type, + "error": error, + }, ) def emit_invocation_started( @@ -124,14 +124,14 @@ class EventServiceBase: """Emitted when an invocation has started""" self.__emit_queue_event( event_name="invocation_started", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - node=node, - source_node_id=source_node_id, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "node": node, + "source_node_id": source_node_id, + }, ) def emit_graph_execution_complete( @@ -140,12 +140,12 @@ class EventServiceBase: """Emitted when a session has completed all invocations""" self.__emit_queue_event( event_name="graph_execution_state_complete", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + }, ) def emit_model_load_started( @@ -162,16 +162,16 @@ class EventServiceBase: """Emitted when a model is requested""" self.__emit_queue_event( event_name="model_load_started", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - model_name=model_name, - base_model=base_model, - model_type=model_type, - submodel=submodel, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "model_name": model_name, + "base_model": base_model, + "model_type": model_type, + "submodel": submodel, + }, ) def emit_model_load_completed( @@ -189,19 +189,19 @@ class EventServiceBase: """Emitted when a model is correctly loaded (returns model info)""" self.__emit_queue_event( event_name="model_load_completed", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - model_name=model_name, - base_model=base_model, - model_type=model_type, - submodel=submodel, - hash=model_info.hash, - location=str(model_info.location), - precision=str(model_info.precision), - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "model_name": model_name, + "base_model": base_model, + "model_type": model_type, + "submodel": submodel, + "hash": model_info.hash, + "location": str(model_info.location), + "precision": str(model_info.precision), + }, ) def emit_session_retrieval_error( @@ -216,14 +216,14 @@ class EventServiceBase: """Emitted when session retrieval fails""" self.__emit_queue_event( event_name="session_retrieval_error", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - error_type=error_type, - error=error, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "error_type": error_type, + "error": error, + }, ) def emit_invocation_retrieval_error( @@ -239,15 +239,15 @@ class EventServiceBase: """Emitted when invocation retrieval fails""" self.__emit_queue_event( event_name="invocation_retrieval_error", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - node_id=node_id, - error_type=error_type, - error=error, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + "node_id": node_id, + "error_type": error_type, + "error": error, + }, ) def emit_session_canceled( @@ -260,12 +260,12 @@ class EventServiceBase: """Emitted when a session is canceled""" self.__emit_queue_event( event_name="session_canceled", - payload=dict( - queue_id=queue_id, - queue_item_id=queue_item_id, - queue_batch_id=queue_batch_id, - graph_execution_state_id=graph_execution_state_id, - ), + payload={ + "queue_id": queue_id, + "queue_item_id": queue_item_id, + "queue_batch_id": queue_batch_id, + "graph_execution_state_id": graph_execution_state_id, + }, ) def emit_queue_item_status_changed( @@ -277,39 +277,39 @@ class EventServiceBase: """Emitted when a queue item's status changes""" self.__emit_queue_event( event_name="queue_item_status_changed", - payload=dict( - queue_id=queue_status.queue_id, - queue_item=dict( - queue_id=session_queue_item.queue_id, - item_id=session_queue_item.item_id, - status=session_queue_item.status, - batch_id=session_queue_item.batch_id, - session_id=session_queue_item.session_id, - error=session_queue_item.error, - created_at=str(session_queue_item.created_at) if session_queue_item.created_at else None, - updated_at=str(session_queue_item.updated_at) if session_queue_item.updated_at else None, - started_at=str(session_queue_item.started_at) if session_queue_item.started_at else None, - completed_at=str(session_queue_item.completed_at) if session_queue_item.completed_at else None, - ), - batch_status=batch_status.model_dump(), - queue_status=queue_status.model_dump(), - ), + payload={ + "queue_id": queue_status.queue_id, + "queue_item": { + "queue_id": session_queue_item.queue_id, + "item_id": session_queue_item.item_id, + "status": session_queue_item.status, + "batch_id": session_queue_item.batch_id, + "session_id": session_queue_item.session_id, + "error": session_queue_item.error, + "created_at": str(session_queue_item.created_at) if session_queue_item.created_at else None, + "updated_at": str(session_queue_item.updated_at) if session_queue_item.updated_at else None, + "started_at": str(session_queue_item.started_at) if session_queue_item.started_at else None, + "completed_at": str(session_queue_item.completed_at) if session_queue_item.completed_at else None, + }, + "batch_status": batch_status.model_dump(), + "queue_status": queue_status.model_dump(), + }, ) def emit_batch_enqueued(self, enqueue_result: EnqueueBatchResult) -> None: """Emitted when a batch is enqueued""" self.__emit_queue_event( event_name="batch_enqueued", - payload=dict( - queue_id=enqueue_result.queue_id, - batch_id=enqueue_result.batch.batch_id, - enqueued=enqueue_result.enqueued, - ), + payload={ + "queue_id": enqueue_result.queue_id, + "batch_id": enqueue_result.batch.batch_id, + "enqueued": enqueue_result.enqueued, + }, ) def emit_queue_cleared(self, queue_id: str) -> None: """Emitted when the queue is cleared""" self.__emit_queue_event( event_name="queue_cleared", - payload=dict(queue_id=queue_id), + payload={"queue_id": queue_id}, ) diff --git a/invokeai/app/services/image_files/image_files_disk.py b/invokeai/app/services/image_files/image_files_disk.py index 91c1e14789..cffcb702c9 100644 --- a/invokeai/app/services/image_files/image_files_disk.py +++ b/invokeai/app/services/image_files/image_files_disk.py @@ -25,7 +25,7 @@ class DiskImageFileStorage(ImageFileStorageBase): __invoker: Invoker def __init__(self, output_folder: Union[str, Path]): - self.__cache = dict() + self.__cache = {} self.__cache_ids = Queue() self.__max_cache_size = 10 # TODO: get this from config diff --git a/invokeai/app/services/image_records/image_records_common.py b/invokeai/app/services/image_records/image_records_common.py index 5a6e5652c9..a2738fbeb9 100644 --- a/invokeai/app/services/image_records/image_records_common.py +++ b/invokeai/app/services/image_records/image_records_common.py @@ -90,10 +90,7 @@ class ImageRecordDeleteException(Exception): IMAGE_DTO_COLS = ", ".join( - list( - map( - lambda c: "images." + c, - [ + ["images." + c for c in [ "image_name", "image_origin", "image_category", @@ -106,9 +103,7 @@ IMAGE_DTO_COLS = ", ".join( "updated_at", "deleted_at", "starred", - ], - ) - ) + ]] ) diff --git a/invokeai/app/services/image_records/image_records_sqlite.py b/invokeai/app/services/image_records/image_records_sqlite.py index 239917b728..e0dabf1657 100644 --- a/invokeai/app/services/image_records/image_records_sqlite.py +++ b/invokeai/app/services/image_records/image_records_sqlite.py @@ -263,7 +263,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): if categories is not None: # Convert the enum values to unique list of strings - category_strings = list(map(lambda c: c.value, set(categories))) + category_strings = [c.value for c in set(categories)] # Create the correct length of placeholders placeholders = ",".join("?" * len(category_strings)) @@ -307,7 +307,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): # Build the list of images, deserializing each row self._cursor.execute(images_query, images_params) result = cast(list[sqlite3.Row], self._cursor.fetchall()) - images = list(map(lambda r: deserialize_image_record(dict(r)), result)) + images = [deserialize_image_record(dict(r)) for r in result] # Set up and execute the count query, without pagination count_query += query_conditions + ";" @@ -386,7 +386,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): """ ) result = cast(list[sqlite3.Row], self._cursor.fetchall()) - image_names = list(map(lambda r: r[0], result)) + image_names = [r[0] for r in result] self._cursor.execute( """--sql DELETE FROM images diff --git a/invokeai/app/services/images/images_base.py b/invokeai/app/services/images/images_base.py index 50a3a5fb82..b3990d08f5 100644 --- a/invokeai/app/services/images/images_base.py +++ b/invokeai/app/services/images/images_base.py @@ -21,8 +21,8 @@ class ImageServiceABC(ABC): _on_deleted_callbacks: list[Callable[[str], None]] def __init__(self) -> None: - self._on_changed_callbacks = list() - self._on_deleted_callbacks = list() + self._on_changed_callbacks = [] + self._on_deleted_callbacks = [] def on_changed(self, on_changed: Callable[[ImageDTO], None]) -> None: """Register a callback for when an image is changed""" diff --git a/invokeai/app/services/images/images_default.py b/invokeai/app/services/images/images_default.py index 8eb768a1b9..2cb157f852 100644 --- a/invokeai/app/services/images/images_default.py +++ b/invokeai/app/services/images/images_default.py @@ -217,18 +217,13 @@ class ImageService(ImageServiceABC): board_id, ) - image_dtos = list( - map( - lambda r: image_record_to_dto( + image_dtos = [image_record_to_dto( image_record=r, image_url=self.__invoker.services.urls.get_image_url(r.image_name), thumbnail_url=self.__invoker.services.urls.get_image_url(r.image_name, True), board_id=self.__invoker.services.board_image_records.get_board_for_image(r.image_name), workflow_id=self.__invoker.services.workflow_image_records.get_workflow_for_image(r.image_name), - ), - results.items, - ) - ) + ) for r in results.items] return OffsetPaginatedResults[ImageDTO]( items=image_dtos, diff --git a/invokeai/app/services/invocation_processor/invocation_processor_default.py b/invokeai/app/services/invocation_processor/invocation_processor_default.py index c59fb678ef..6e0d3075ea 100644 --- a/invokeai/app/services/invocation_processor/invocation_processor_default.py +++ b/invokeai/app/services/invocation_processor/invocation_processor_default.py @@ -26,7 +26,7 @@ class DefaultInvocationProcessor(InvocationProcessorABC): self.__invoker_thread = Thread( name="invoker_processor", target=self.__process, - kwargs=dict(stop_event=self.__stop_event), + kwargs={"stop_event": self.__stop_event}, ) self.__invoker_thread.daemon = True # TODO: make async and do not use threads self.__invoker_thread.start() diff --git a/invokeai/app/services/invocation_queue/invocation_queue_memory.py b/invokeai/app/services/invocation_queue/invocation_queue_memory.py index 33e82fae18..8d6fff7052 100644 --- a/invokeai/app/services/invocation_queue/invocation_queue_memory.py +++ b/invokeai/app/services/invocation_queue/invocation_queue_memory.py @@ -14,7 +14,7 @@ class MemoryInvocationQueue(InvocationQueueABC): def __init__(self): self.__queue = Queue() - self.__cancellations = dict() + self.__cancellations = {} def get(self) -> InvocationQueueItem: item = self.__queue.get() diff --git a/invokeai/app/services/invocation_stats/invocation_stats_default.py b/invokeai/app/services/invocation_stats/invocation_stats_default.py index be019b6820..e7392c64be 100644 --- a/invokeai/app/services/invocation_stats/invocation_stats_default.py +++ b/invokeai/app/services/invocation_stats/invocation_stats_default.py @@ -142,7 +142,7 @@ class InvocationStatsService(InvocationStatsServiceBase): cache_stats = self._cache_stats[graph_id] hwm = cache_stats.high_watermark / GIG tot = cache_stats.cache_size / GIG - loaded = sum([v for v in cache_stats.loaded_model_sizes.values()]) / GIG + loaded = sum(list(cache_stats.loaded_model_sizes.values())) / GIG logger.info(f"TOTAL GRAPH EXECUTION TIME: {total_time:7.3f}s") logger.info("RAM used by InvokeAI process: " + "%4.2fG" % self.ram_used + f" ({self.ram_changed:+5.3f}G)") diff --git a/invokeai/app/services/item_storage/item_storage_base.py b/invokeai/app/services/item_storage/item_storage_base.py index 1446c0cd08..e94c049ee4 100644 --- a/invokeai/app/services/item_storage/item_storage_base.py +++ b/invokeai/app/services/item_storage/item_storage_base.py @@ -15,8 +15,8 @@ class ItemStorageABC(ABC, Generic[T]): _on_deleted_callbacks: list[Callable[[str], None]] def __init__(self) -> None: - self._on_changed_callbacks = list() - self._on_deleted_callbacks = list() + self._on_changed_callbacks = [] + self._on_deleted_callbacks = [] """Base item storage class""" diff --git a/invokeai/app/services/item_storage/item_storage_sqlite.py b/invokeai/app/services/item_storage/item_storage_sqlite.py index d0249ebfa6..d5a1b7f730 100644 --- a/invokeai/app/services/item_storage/item_storage_sqlite.py +++ b/invokeai/app/services/item_storage/item_storage_sqlite.py @@ -112,7 +112,7 @@ class SqliteItemStorage(ItemStorageABC, Generic[T]): ) result = self._cursor.fetchall() - items = list(map(lambda r: self._parse_item(r[0]), result)) + items = [self._parse_item(r[0]) for r in result] self._cursor.execute(f"""SELECT count(*) FROM {self._table_name};""") count = self._cursor.fetchone()[0] @@ -132,7 +132,7 @@ class SqliteItemStorage(ItemStorageABC, Generic[T]): ) result = self._cursor.fetchall() - items = list(map(lambda r: self._parse_item(r[0]), result)) + items = [self._parse_item(r[0]) for r in result] self._cursor.execute( f"""SELECT count(*) FROM {self._table_name} WHERE item LIKE ?;""", diff --git a/invokeai/app/services/latents_storage/latents_storage_base.py b/invokeai/app/services/latents_storage/latents_storage_base.py index 4850a477d3..9fa42b0ae6 100644 --- a/invokeai/app/services/latents_storage/latents_storage_base.py +++ b/invokeai/app/services/latents_storage/latents_storage_base.py @@ -13,8 +13,8 @@ class LatentsStorageBase(ABC): _on_deleted_callbacks: list[Callable[[str], None]] def __init__(self) -> None: - self._on_changed_callbacks = list() - self._on_deleted_callbacks = list() + self._on_changed_callbacks = [] + self._on_deleted_callbacks = [] @abstractmethod def get(self, name: str) -> torch.Tensor: diff --git a/invokeai/app/services/latents_storage/latents_storage_forward_cache.py b/invokeai/app/services/latents_storage/latents_storage_forward_cache.py index 5248362ff5..da82b5904d 100644 --- a/invokeai/app/services/latents_storage/latents_storage_forward_cache.py +++ b/invokeai/app/services/latents_storage/latents_storage_forward_cache.py @@ -19,7 +19,7 @@ class ForwardCacheLatentsStorage(LatentsStorageBase): def __init__(self, underlying_storage: LatentsStorageBase, max_cache_size: int = 20): super().__init__() self.__underlying_storage = underlying_storage - self.__cache = dict() + self.__cache = {} self.__cache_ids = Queue() self.__max_cache_size = max_cache_size diff --git a/invokeai/app/services/session_processor/session_processor_default.py b/invokeai/app/services/session_processor/session_processor_default.py index 5b59dee254..a8a6b7bf3c 100644 --- a/invokeai/app/services/session_processor/session_processor_default.py +++ b/invokeai/app/services/session_processor/session_processor_default.py @@ -33,9 +33,9 @@ class DefaultSessionProcessor(SessionProcessorBase): self.__thread = Thread( name="session_processor", target=self.__process, - kwargs=dict( - stop_event=self.__stop_event, poll_now_event=self.__poll_now_event, resume_event=self.__resume_event - ), + kwargs={ + "stop_event": self.__stop_event, "poll_now_event": self.__poll_now_event, "resume_event": self.__resume_event + }, ) self.__thread.start() diff --git a/invokeai/app/services/session_queue/session_queue_common.py b/invokeai/app/services/session_queue/session_queue_common.py index 69e6a3ab87..67952c40a0 100644 --- a/invokeai/app/services/session_queue/session_queue_common.py +++ b/invokeai/app/services/session_queue/session_queue_common.py @@ -129,12 +129,12 @@ class Batch(BaseModel): return v model_config = ConfigDict( - json_schema_extra=dict( - required=[ + json_schema_extra={ + "required": [ "graph", "runs", ] - ) + } ) @@ -191,8 +191,8 @@ class SessionQueueItemWithoutGraph(BaseModel): return SessionQueueItemDTO(**queue_item_dict) model_config = ConfigDict( - json_schema_extra=dict( - required=[ + json_schema_extra={ + "required": [ "item_id", "status", "batch_id", @@ -203,7 +203,7 @@ class SessionQueueItemWithoutGraph(BaseModel): "created_at", "updated_at", ] - ) + } ) @@ -222,8 +222,8 @@ class SessionQueueItem(SessionQueueItemWithoutGraph): return SessionQueueItem(**queue_item_dict) model_config = ConfigDict( - json_schema_extra=dict( - required=[ + json_schema_extra={ + "required": [ "item_id", "status", "batch_id", @@ -235,7 +235,7 @@ class SessionQueueItem(SessionQueueItemWithoutGraph): "created_at", "updated_at", ] - ) + } ) diff --git a/invokeai/app/services/shared/default_graphs.py b/invokeai/app/services/shared/default_graphs.py index 9a6e2456cb..7e62c6d0a1 100644 --- a/invokeai/app/services/shared/default_graphs.py +++ b/invokeai/app/services/shared/default_graphs.py @@ -78,7 +78,7 @@ def create_system_graphs(graph_library: ItemStorageABC[LibraryGraph]) -> list[Li """Creates the default system graphs, or adds new versions if the old ones don't match""" # TODO: Uncomment this when we are ready to fix this up to prevent breaking changes - graphs: list[LibraryGraph] = list() + graphs: list[LibraryGraph] = [] text_to_image = graph_library.get(default_text_to_image_graph_id) diff --git a/invokeai/app/services/shared/graph.py b/invokeai/app/services/shared/graph.py index b84d456071..70444ee204 100644 --- a/invokeai/app/services/shared/graph.py +++ b/invokeai/app/services/shared/graph.py @@ -352,7 +352,7 @@ class Graph(BaseModel): # Validate that all node ids are unique node_ids = [n.id for n in self.nodes.values()] - duplicate_node_ids = set([node_id for node_id in node_ids if node_ids.count(node_id) >= 2]) + duplicate_node_ids = {node_id for node_id in node_ids if node_ids.count(node_id) >= 2} if duplicate_node_ids: raise DuplicateNodeIdError(f"Node ids must be unique, found duplicates {duplicate_node_ids}") @@ -616,7 +616,7 @@ class Graph(BaseModel): self, node_path: str, prefix: Optional[str] = None ) -> list[tuple["Graph", Union[str, None], Edge]]: """Gets all input edges for a node along with the graph they are in and the graph's path""" - edges = list() + edges = [] # Return any input edges that appear in this graph edges.extend([(self, prefix, e) for e in self.edges if e.destination.node_id == node_path]) @@ -658,7 +658,7 @@ class Graph(BaseModel): self, node_path: str, prefix: Optional[str] = None ) -> list[tuple["Graph", Union[str, None], Edge]]: """Gets all output edges for a node along with the graph they are in and the graph's path""" - edges = list() + edges = [] # Return any input edges that appear in this graph edges.extend([(self, prefix, e) for e in self.edges if e.source.node_id == node_path]) @@ -680,8 +680,8 @@ class Graph(BaseModel): new_input: Optional[EdgeConnection] = None, new_output: Optional[EdgeConnection] = None, ) -> bool: - inputs = list([e.source for e in self._get_input_edges(node_path, "collection")]) - outputs = list([e.destination for e in self._get_output_edges(node_path, "item")]) + inputs = [e.source for e in self._get_input_edges(node_path, "collection")] + outputs = [e.destination for e in self._get_output_edges(node_path, "item")] if new_input is not None: inputs.append(new_input) @@ -694,7 +694,7 @@ class Graph(BaseModel): # Get input and output fields (the fields linked to the iterator's input/output) input_field = get_output_field(self.get_node(inputs[0].node_id), inputs[0].field) - output_fields = list([get_input_field(self.get_node(e.node_id), e.field) for e in outputs]) + output_fields = [get_input_field(self.get_node(e.node_id), e.field) for e in outputs] # Input type must be a list if get_origin(input_field) != list: @@ -713,8 +713,8 @@ class Graph(BaseModel): new_input: Optional[EdgeConnection] = None, new_output: Optional[EdgeConnection] = None, ) -> bool: - inputs = list([e.source for e in self._get_input_edges(node_path, "item")]) - outputs = list([e.destination for e in self._get_output_edges(node_path, "collection")]) + inputs = [e.source for e in self._get_input_edges(node_path, "item")] + outputs = [e.destination for e in self._get_output_edges(node_path, "collection")] if new_input is not None: inputs.append(new_input) @@ -722,18 +722,16 @@ class Graph(BaseModel): outputs.append(new_output) # Get input and output fields (the fields linked to the iterator's input/output) - input_fields = list([get_output_field(self.get_node(e.node_id), e.field) for e in inputs]) - output_fields = list([get_input_field(self.get_node(e.node_id), e.field) for e in outputs]) + input_fields = [get_output_field(self.get_node(e.node_id), e.field) for e in inputs] + output_fields = [get_input_field(self.get_node(e.node_id), e.field) for e in outputs] # Validate that all inputs are derived from or match a single type - input_field_types = set( - [ - t + input_field_types = { + t for input_field in input_fields for t in ([input_field] if get_origin(input_field) is None else get_args(input_field)) if t != NoneType - ] - ) # Get unique types + } # Get unique types type_tree = nx.DiGraph() type_tree.add_nodes_from(input_field_types) type_tree.add_edges_from([e for e in itertools.permutations(input_field_types, 2) if issubclass(e[1], e[0])]) @@ -761,15 +759,15 @@ class Graph(BaseModel): """Returns a NetworkX DiGraph representing the layout of this graph""" # TODO: Cache this? g = nx.DiGraph() - g.add_nodes_from([n for n in self.nodes.keys()]) - g.add_edges_from(set([(e.source.node_id, e.destination.node_id) for e in self.edges])) + g.add_nodes_from(list(self.nodes.keys())) + g.add_edges_from({(e.source.node_id, e.destination.node_id) for e in self.edges}) return g def nx_graph_with_data(self) -> nx.DiGraph: """Returns a NetworkX DiGraph representing the data and layout of this graph""" g = nx.DiGraph() - g.add_nodes_from([n for n in self.nodes.items()]) - g.add_edges_from(set([(e.source.node_id, e.destination.node_id) for e in self.edges])) + g.add_nodes_from(list(self.nodes.items())) + g.add_edges_from({(e.source.node_id, e.destination.node_id) for e in self.edges}) return g def nx_graph_flat(self, nx_graph: Optional[nx.DiGraph] = None, prefix: Optional[str] = None) -> nx.DiGraph: @@ -791,7 +789,7 @@ class Graph(BaseModel): # TODO: figure out if iteration nodes need to be expanded - unique_edges = set([(e.source.node_id, e.destination.node_id) for e in self.edges]) + unique_edges = {(e.source.node_id, e.destination.node_id) for e in self.edges} g.add_edges_from([(self._get_node_path(e[0], prefix), self._get_node_path(e[1], prefix)) for e in unique_edges]) return g @@ -843,8 +841,8 @@ class GraphExecutionState(BaseModel): return v model_config = ConfigDict( - json_schema_extra=dict( - required=[ + json_schema_extra={ + "required": [ "id", "graph", "execution_graph", @@ -855,7 +853,7 @@ class GraphExecutionState(BaseModel): "prepared_source_mapping", "source_prepared_mapping", ] - ) + } ) def next(self) -> Optional[BaseInvocation]: @@ -895,7 +893,7 @@ class GraphExecutionState(BaseModel): source_node = self.prepared_source_mapping[node_id] prepared_nodes = self.source_prepared_mapping[source_node] - if all([n in self.executed for n in prepared_nodes]): + if all(n in self.executed for n in prepared_nodes): self.executed.add(source_node) self.executed_history.append(source_node) @@ -930,7 +928,7 @@ class GraphExecutionState(BaseModel): input_collection = getattr(input_collection_prepared_node_output, input_collection_edge.source.field) self_iteration_count = len(input_collection) - new_nodes: list[str] = list() + new_nodes: list[str] = [] if self_iteration_count == 0: # TODO: should this raise a warning? It might just happen if an empty collection is input, and should be valid. return new_nodes @@ -940,7 +938,7 @@ class GraphExecutionState(BaseModel): # Create new edges for this iteration # For collect nodes, this may contain multiple inputs to the same field - new_edges: list[Edge] = list() + new_edges: list[Edge] = [] for edge in input_edges: for input_node_id in (n[1] for n in iteration_node_map if n[0] == edge.source.node_id): new_edge = Edge( @@ -1034,7 +1032,7 @@ class GraphExecutionState(BaseModel): # Create execution nodes next_node = self.graph.get_node(next_node_id) - new_node_ids = list() + new_node_ids = [] if isinstance(next_node, CollectInvocation): # Collapse all iterator input mappings and create a single execution node for the collect invocation all_iteration_mappings = list( @@ -1201,7 +1199,7 @@ class LibraryGraph(BaseModel): @field_validator("exposed_inputs", "exposed_outputs") def validate_exposed_aliases(cls, v: list[Union[ExposedNodeInput, ExposedNodeOutput]]): - if len(v) != len(set(i.alias for i in v)): + if len(v) != len({i.alias for i in v}): raise ValueError("Duplicate exposed alias") return v diff --git a/invokeai/backend/image_util/pngwriter.py b/invokeai/backend/image_util/pngwriter.py index 47f6a44c28..c9c58264c2 100644 --- a/invokeai/backend/image_util/pngwriter.py +++ b/invokeai/backend/image_util/pngwriter.py @@ -88,7 +88,7 @@ class PromptFormatter: t2i = self.t2i opt = self.opt - switches = list() + switches = [] switches.append(f'"{opt.prompt}"') switches.append(f"-s{opt.steps or t2i.steps}") switches.append(f"-W{opt.width or t2i.width}") diff --git a/invokeai/backend/image_util/util.py b/invokeai/backend/image_util/util.py index 7eceb9be82..5b8be7f118 100644 --- a/invokeai/backend/image_util/util.py +++ b/invokeai/backend/image_util/util.py @@ -40,7 +40,7 @@ class InitImageResizer: (rw, rh) = (int(scale * im.width), int(scale * im.height)) # round everything to multiples of 64 - width, height, rw, rh = map(lambda x: x - x % 64, (width, height, rw, rh)) + width, height, rw, rh = (x - x % 64 for x in (width, height, rw, rh)) # no resize necessary, but return a copy if im.width == width and im.height == height: diff --git a/invokeai/backend/install/invokeai_configure.py b/invokeai/backend/install/invokeai_configure.py index 28910e8942..2621b811ac 100755 --- a/invokeai/backend/install/invokeai_configure.py +++ b/invokeai/backend/install/invokeai_configure.py @@ -197,7 +197,7 @@ def download_with_progress_bar(model_url: str, model_dest: str, label: str = "th def download_conversion_models(): target_dir = config.models_path / "core/convert" - kwargs = dict() # for future use + kwargs = {} # for future use try: logger.info("Downloading core tokenizers and text encoders") @@ -252,26 +252,26 @@ def download_conversion_models(): def download_realesrgan(): logger.info("Installing ESRGAN Upscaling models...") URLs = [ - dict( - url="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth", - dest="core/upscaling/realesrgan/RealESRGAN_x4plus.pth", - description="RealESRGAN_x4plus.pth", - ), - dict( - url="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth", - dest="core/upscaling/realesrgan/RealESRGAN_x4plus_anime_6B.pth", - description="RealESRGAN_x4plus_anime_6B.pth", - ), - dict( - url="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.1/ESRGAN_SRx4_DF2KOST_official-ff704c30.pth", - dest="core/upscaling/realesrgan/ESRGAN_SRx4_DF2KOST_official-ff704c30.pth", - description="ESRGAN_SRx4_DF2KOST_official.pth", - ), - dict( - url="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", - dest="core/upscaling/realesrgan/RealESRGAN_x2plus.pth", - description="RealESRGAN_x2plus.pth", - ), + { + "url": "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth", + "dest": "core/upscaling/realesrgan/RealESRGAN_x4plus.pth", + "description": "RealESRGAN_x4plus.pth", + }, + { + "url": "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth", + "dest": "core/upscaling/realesrgan/RealESRGAN_x4plus_anime_6B.pth", + "description": "RealESRGAN_x4plus_anime_6B.pth", + }, + { + "url": "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.1/ESRGAN_SRx4_DF2KOST_official-ff704c30.pth", + "dest": "core/upscaling/realesrgan/ESRGAN_SRx4_DF2KOST_official-ff704c30.pth", + "description": "ESRGAN_SRx4_DF2KOST_official.pth", + }, + { + "url": "https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", + "dest": "core/upscaling/realesrgan/RealESRGAN_x2plus.pth", + "description": "RealESRGAN_x2plus.pth", + }, ] for model in URLs: download_with_progress_bar(model["url"], config.models_path / model["dest"], model["description"]) @@ -680,7 +680,7 @@ def default_user_selections(program_opts: Namespace) -> InstallSelections: if program_opts.default_only else [models[x].path or models[x].repo_id for x in installer.recommended_models()] if program_opts.yes_to_all - else list(), + else [], ) diff --git a/invokeai/backend/install/migrate_to_3.py b/invokeai/backend/install/migrate_to_3.py index ea5bee8058..62b23fb808 100644 --- a/invokeai/backend/install/migrate_to_3.py +++ b/invokeai/backend/install/migrate_to_3.py @@ -182,10 +182,10 @@ class MigrateTo3(object): """ dest_directory = self.dest_models - kwargs = dict( - cache_dir=self.root_directory / "models/hub", + kwargs = { + "cache_dir": self.root_directory / "models/hub", # local_files_only = True - ) + } try: logger.info("Migrating core tokenizers and text encoders") target_dir = dest_directory / "core" / "convert" @@ -316,11 +316,11 @@ class MigrateTo3(object): dest_dir = self.dest_models cache = self.root_directory / "models/hub" - kwargs = dict( - cache_dir=cache, - safety_checker=None, + kwargs = { + "cache_dir": cache, + "safety_checker": None, # local_files_only = True, - ) + } owner, repo_name = repo_id.split("/") model_name = model_name or repo_name diff --git a/invokeai/backend/install/model_install_backend.py b/invokeai/backend/install/model_install_backend.py index 34526cfaf3..56e512a73a 100644 --- a/invokeai/backend/install/model_install_backend.py +++ b/invokeai/backend/install/model_install_backend.py @@ -120,7 +120,7 @@ class ModelInstall(object): be treated uniformly. It also sorts the models alphabetically by their name, to improve the display somewhat. """ - model_dict = dict() + model_dict = {} # first populate with the entries in INITIAL_MODELS.yaml for key, value in self.datasets.items(): @@ -134,7 +134,7 @@ class ModelInstall(object): model_dict[key] = model_info # supplement with entries in models.yaml - installed_models = [x for x in self.mgr.list_models()] + installed_models = list(self.mgr.list_models()) for md in installed_models: base = md["base_model"] @@ -184,7 +184,7 @@ class ModelInstall(object): def recommended_models(self) -> Set[str]: starters = self.starter_models(all_models=True) - return set([x for x in starters if self.datasets[x].get("recommended", False)]) + return {x for x in starters if self.datasets[x].get("recommended", False)} def default_model(self) -> str: starters = self.starter_models() @@ -234,7 +234,7 @@ class ModelInstall(object): """ if not models_installed: - models_installed = dict() + models_installed = {} model_path_id_or_url = str(model_path_id_or_url).strip("\"' ") @@ -252,8 +252,7 @@ class ModelInstall(object): # folders style or similar elif path.is_dir() and any( - [ - (path / x).exists() + (path / x).exists() for x in { "config.json", "model_index.json", @@ -261,7 +260,6 @@ class ModelInstall(object): "pytorch_lora_weights.bin", "pytorch_lora_weights.safetensors", } - ] ): models_installed.update({str(model_path_id_or_url): self._install_path(path)}) @@ -433,17 +431,17 @@ class ModelInstall(object): rel_path = self.relative_to_root(path, self.config.models_path) - attributes = dict( - path=str(rel_path), - description=str(description), - model_format=info.format, - ) + attributes = { + "path": str(rel_path), + "description": str(description), + "model_format": info.format, + } legacy_conf = None if info.model_type == ModelType.Main or info.model_type == ModelType.ONNX: attributes.update( - dict( - variant=info.variant_type, - ) + { + "variant": info.variant_type, + } ) if info.format == "checkpoint": try: @@ -474,7 +472,7 @@ class ModelInstall(object): ) if legacy_conf: - attributes.update(dict(config=str(legacy_conf))) + attributes.update({"config": str(legacy_conf)}) return attributes def relative_to_root(self, path: Path, root: Optional[Path] = None) -> Path: @@ -519,7 +517,7 @@ class ModelInstall(object): def _download_hf_model(self, repo_id: str, files: List[str], staging: Path, subfolder: None) -> Path: _, name = repo_id.split("/") location = staging / name - paths = list() + paths = [] for filename in files: filePath = Path(filename) p = hf_download_with_resume( diff --git a/invokeai/backend/model_management/lora.py b/invokeai/backend/model_management/lora.py index e4caf60aac..411ec4a400 100644 --- a/invokeai/backend/model_management/lora.py +++ b/invokeai/backend/model_management/lora.py @@ -104,7 +104,7 @@ class ModelPatcher: loras: List[Tuple[LoRAModel, float]], prefix: str, ): - original_weights = dict() + original_weights = {} try: with torch.no_grad(): for lora, lora_weight in loras: @@ -324,7 +324,7 @@ class TextualInversionManager(BaseTextualInversionManager): tokenizer: CLIPTokenizer def __init__(self, tokenizer: CLIPTokenizer): - self.pad_tokens = dict() + self.pad_tokens = {} self.tokenizer = tokenizer def expand_textual_inversion_token_ids_if_necessary(self, token_ids: list[int]) -> list[int]: @@ -385,10 +385,10 @@ class ONNXModelPatcher: if not isinstance(model, IAIOnnxRuntimeModel): raise Exception("Only IAIOnnxRuntimeModel models supported") - orig_weights = dict() + orig_weights = {} try: - blended_loras = dict() + blended_loras = {} for lora, lora_weight in loras: for layer_key, layer in lora.layers.items(): @@ -404,7 +404,7 @@ class ONNXModelPatcher: else: blended_loras[layer_key] = layer_weight - node_names = dict() + node_names = {} for node in model.nodes.values(): node_names[node.name.replace("/", "_").replace(".", "_").lstrip("_")] = node.name diff --git a/invokeai/backend/model_management/model_cache.py b/invokeai/backend/model_management/model_cache.py index 83af789219..2055691a06 100644 --- a/invokeai/backend/model_management/model_cache.py +++ b/invokeai/backend/model_management/model_cache.py @@ -132,7 +132,7 @@ class ModelCache(object): snapshots, so it is recommended to disable this feature unless you are actively inspecting the model cache's behaviour. """ - self.model_infos: Dict[str, ModelBase] = dict() + self.model_infos: Dict[str, ModelBase] = {} # allow lazy offloading only when vram cache enabled self.lazy_offloading = lazy_offloading and max_vram_cache_size > 0 self.precision: torch.dtype = precision @@ -147,8 +147,8 @@ class ModelCache(object): # used for stats collection self.stats = None - self._cached_models = dict() - self._cache_stack = list() + self._cached_models = {} + self._cache_stack = [] def _capture_memory_snapshot(self) -> Optional[MemorySnapshot]: if self._log_memory_usage: diff --git a/invokeai/backend/model_management/model_manager.py b/invokeai/backend/model_management/model_manager.py index da4239fa07..f38b3cd325 100644 --- a/invokeai/backend/model_management/model_manager.py +++ b/invokeai/backend/model_management/model_manager.py @@ -363,7 +363,7 @@ class ModelManager(object): else: return - self.models = dict() + self.models = {} for model_key, model_config in config.items(): if model_key.startswith("_"): continue @@ -374,7 +374,7 @@ class ModelManager(object): self.models[model_key] = model_class.create_config(**model_config) # check config version number and update on disk/RAM if necessary - self.cache_keys = dict() + self.cache_keys = {} # add controlnet, lora and textual_inversion models from disk self.scan_models_directory() @@ -902,7 +902,7 @@ class ModelManager(object): """ Write current configuration out to the indicated file. """ - data_to_save = dict() + data_to_save = {} data_to_save["__metadata__"] = self.config_meta.model_dump() for model_key, model_config in self.models.items(): @@ -1034,7 +1034,7 @@ class ModelManager(object): self.ignore = ignore def on_search_started(self): - self.new_models_found = dict() + self.new_models_found = {} def on_model_found(self, model: Path): if model not in self.ignore: @@ -1106,7 +1106,7 @@ class ModelManager(object): # avoid circular import here from invokeai.backend.install.model_install_backend import ModelInstall - successfully_installed = dict() + successfully_installed = {} installer = ModelInstall( config=self.app_config, prediction_type_helper=prediction_type_helper, model_manager=self diff --git a/invokeai/backend/model_management/model_merge.py b/invokeai/backend/model_management/model_merge.py index 59201d64d9..a9f0a23618 100644 --- a/invokeai/backend/model_management/model_merge.py +++ b/invokeai/backend/model_management/model_merge.py @@ -92,7 +92,7 @@ class ModelMerger(object): **kwargs - the default DiffusionPipeline.get_config_dict kwargs: cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map """ - model_paths = list() + model_paths = [] config = self.manager.app_config base_model = BaseModelType(base_model) vae = None @@ -124,13 +124,13 @@ class ModelMerger(object): dump_path = (dump_path / merged_model_name).as_posix() merged_pipe.save_pretrained(dump_path, safe_serialization=True) - attributes = dict( - path=dump_path, - description=f"Merge of models {', '.join(model_names)}", - model_format="diffusers", - variant=ModelVariantType.Normal.value, - vae=vae, - ) + attributes = { + "path": dump_path, + "description": f"Merge of models {', '.join(model_names)}", + "model_format": "diffusers", + "variant": ModelVariantType.Normal.value, + "vae": vae, + } return self.manager.add_model( merged_model_name, base_model=base_model, diff --git a/invokeai/backend/model_management/model_search.py b/invokeai/backend/model_management/model_search.py index 7e6b37c832..3d56d9d8b9 100644 --- a/invokeai/backend/model_management/model_search.py +++ b/invokeai/backend/model_management/model_search.py @@ -59,7 +59,7 @@ class ModelSearch(ABC): for root, dirs, files in os.walk(path, followlinks=True): if str(Path(root).name).startswith("."): self._pruned_paths.add(root) - if any([Path(root).is_relative_to(x) for x in self._pruned_paths]): + if any(Path(root).is_relative_to(x) for x in self._pruned_paths): continue self._items_scanned += len(dirs) + len(files) @@ -69,8 +69,7 @@ class ModelSearch(ABC): self._scanned_dirs.add(path) continue if any( - [ - (path / x).exists() + (path / x).exists() for x in { "config.json", "model_index.json", @@ -78,7 +77,6 @@ class ModelSearch(ABC): "pytorch_lora_weights.bin", "image_encoder.txt", } - ] ): try: self.on_model_found(path) diff --git a/invokeai/backend/model_management/models/__init__.py b/invokeai/backend/model_management/models/__init__.py index 0afd731032..150ed7b371 100644 --- a/invokeai/backend/model_management/models/__init__.py +++ b/invokeai/backend/model_management/models/__init__.py @@ -97,8 +97,8 @@ MODEL_CLASSES = { # }, } -MODEL_CONFIGS = list() -OPENAPI_MODEL_CONFIGS = list() +MODEL_CONFIGS = [] +OPENAPI_MODEL_CONFIGS = [] class OpenAPIModelInfoBase(BaseModel): @@ -133,7 +133,7 @@ for base_model, models in MODEL_CLASSES.items(): def get_model_config_enums(): - enums = list() + enums = [] for model_config in MODEL_CONFIGS: if hasattr(inspect, "get_annotations"): diff --git a/invokeai/backend/model_management/models/base.py b/invokeai/backend/model_management/models/base.py index f735e37189..0038f9d9b0 100644 --- a/invokeai/backend/model_management/models/base.py +++ b/invokeai/backend/model_management/models/base.py @@ -164,7 +164,7 @@ class ModelBase(metaclass=ABCMeta): with suppress(Exception): return cls.__configs - configs = dict() + configs = {} for name in dir(cls): if name.startswith("__"): continue @@ -246,8 +246,8 @@ class DiffusersModel(ModelBase): def __init__(self, model_path: str, base_model: BaseModelType, model_type: ModelType): super().__init__(model_path, base_model, model_type) - self.child_types: Dict[str, Type] = dict() - self.child_sizes: Dict[str, int] = dict() + self.child_types: Dict[str, Type] = {} + self.child_sizes: Dict[str, int] = {} try: config_data = DiffusionPipeline.load_config(self.model_path) @@ -326,8 +326,8 @@ def calc_model_size_by_fs(model_path: str, subfolder: Optional[str] = None, vari all_files = os.listdir(model_path) all_files = [f for f in all_files if os.path.isfile(os.path.join(model_path, f))] - fp16_files = set([f for f in all_files if ".fp16." in f or ".fp16-" in f]) - bit8_files = set([f for f in all_files if ".8bit." in f or ".8bit-" in f]) + fp16_files = {f for f in all_files if ".fp16." in f or ".fp16-" in f} + bit8_files = {f for f in all_files if ".8bit." in f or ".8bit-" in f} other_files = set(all_files) - fp16_files - bit8_files if variant is None: @@ -413,7 +413,7 @@ def _calc_onnx_model_by_data(model) -> int: def _fast_safetensors_reader(path: str): - checkpoint = dict() + checkpoint = {} device = torch.device("meta") with open(path, "rb") as f: definition_len = int.from_bytes(f.read(8), "little") @@ -483,7 +483,7 @@ class IAIOnnxRuntimeModel: class _tensor_access: def __init__(self, model): self.model = model - self.indexes = dict() + self.indexes = {} for idx, obj in enumerate(self.model.proto.graph.initializer): self.indexes[obj.name] = idx @@ -524,7 +524,7 @@ class IAIOnnxRuntimeModel: class _access_helper: def __init__(self, raw_proto): - self.indexes = dict() + self.indexes = {} self.raw_proto = raw_proto for idx, obj in enumerate(raw_proto): self.indexes[obj.name] = idx @@ -549,7 +549,7 @@ class IAIOnnxRuntimeModel: return self.indexes.keys() def values(self): - return [obj for obj in self.raw_proto] + return list(self.raw_proto) def __init__(self, model_path: str, provider: Optional[str]): self.path = model_path diff --git a/invokeai/backend/model_management/models/controlnet.py b/invokeai/backend/model_management/models/controlnet.py index 6a42b59fe1..da269eba4b 100644 --- a/invokeai/backend/model_management/models/controlnet.py +++ b/invokeai/backend/model_management/models/controlnet.py @@ -104,7 +104,7 @@ class ControlNetModel(ModelBase): return ControlNetModelFormat.Diffusers if os.path.isfile(path): - if any([path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt", "pth"]]): + if any(path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt", "pth"]): return ControlNetModelFormat.Checkpoint raise InvalidModelException(f"Not a valid model: {path}") diff --git a/invokeai/backend/model_management/models/lora.py b/invokeai/backend/model_management/models/lora.py index 43a24275d1..472815adbb 100644 --- a/invokeai/backend/model_management/models/lora.py +++ b/invokeai/backend/model_management/models/lora.py @@ -73,7 +73,7 @@ class LoRAModel(ModelBase): return LoRAModelFormat.Diffusers if os.path.isfile(path): - if any([path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]]): + if any(path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]): return LoRAModelFormat.LyCORIS raise InvalidModelException(f"Not a valid model: {path}") @@ -499,7 +499,7 @@ class LoRAModelRaw: # (torch.nn.Module): stability_unet_keys = list(SDXL_UNET_STABILITY_TO_DIFFUSERS_MAP) stability_unet_keys.sort() - new_state_dict = dict() + new_state_dict = {} for full_key, value in state_dict.items(): if full_key.startswith("lora_unet_"): search_key = full_key.replace("lora_unet_", "") @@ -545,7 +545,7 @@ class LoRAModelRaw: # (torch.nn.Module): model = cls( name=file_path.stem, # TODO: - layers=dict(), + layers={}, ) if file_path.suffix == ".safetensors": @@ -593,12 +593,12 @@ class LoRAModelRaw: # (torch.nn.Module): @staticmethod def _group_state(state_dict: dict): - state_dict_groupped = dict() + state_dict_groupped = {} for key, value in state_dict.items(): stem, leaf = key.split(".", 1) if stem not in state_dict_groupped: - state_dict_groupped[stem] = dict() + state_dict_groupped[stem] = {} state_dict_groupped[stem][leaf] = value return state_dict_groupped diff --git a/invokeai/backend/model_management/models/stable_diffusion.py b/invokeai/backend/model_management/models/stable_diffusion.py index ffce42d9e9..a38a44fccf 100644 --- a/invokeai/backend/model_management/models/stable_diffusion.py +++ b/invokeai/backend/model_management/models/stable_diffusion.py @@ -110,7 +110,7 @@ class StableDiffusion1Model(DiffusersModel): return StableDiffusion1ModelFormat.Diffusers if os.path.isfile(model_path): - if any([model_path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]]): + if any(model_path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]): return StableDiffusion1ModelFormat.Checkpoint raise InvalidModelException(f"Not a valid model: {model_path}") @@ -221,7 +221,7 @@ class StableDiffusion2Model(DiffusersModel): return StableDiffusion2ModelFormat.Diffusers if os.path.isfile(model_path): - if any([model_path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]]): + if any(model_path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]): return StableDiffusion2ModelFormat.Checkpoint raise InvalidModelException(f"Not a valid model: {model_path}") diff --git a/invokeai/backend/model_management/models/textual_inversion.py b/invokeai/backend/model_management/models/textual_inversion.py index b59e635045..99358704b8 100644 --- a/invokeai/backend/model_management/models/textual_inversion.py +++ b/invokeai/backend/model_management/models/textual_inversion.py @@ -71,7 +71,7 @@ class TextualInversionModel(ModelBase): return None # diffusers-ti if os.path.isfile(path): - if any([path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt", "bin"]]): + if any(path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt", "bin"]): return None raise InvalidModelException(f"Not a valid model: {path}") diff --git a/invokeai/backend/model_management/models/vae.py b/invokeai/backend/model_management/models/vae.py index 637160c69b..8cc37e67a7 100644 --- a/invokeai/backend/model_management/models/vae.py +++ b/invokeai/backend/model_management/models/vae.py @@ -89,7 +89,7 @@ class VaeModel(ModelBase): return VaeModelFormat.Diffusers if os.path.isfile(path): - if any([path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]]): + if any(path.endswith(f".{ext}") for ext in ["safetensors", "ckpt", "pt"]): return VaeModelFormat.Checkpoint raise InvalidModelException(f"Not a valid model: {path}") diff --git a/invokeai/backend/stable_diffusion/schedulers/schedulers.py b/invokeai/backend/stable_diffusion/schedulers/schedulers.py index 65bb05b582..c824d94dca 100644 --- a/invokeai/backend/stable_diffusion/schedulers/schedulers.py +++ b/invokeai/backend/stable_diffusion/schedulers/schedulers.py @@ -16,28 +16,28 @@ from diffusers import ( UniPCMultistepScheduler, ) -SCHEDULER_MAP = dict( - ddim=(DDIMScheduler, dict()), - ddpm=(DDPMScheduler, dict()), - deis=(DEISMultistepScheduler, dict()), - lms=(LMSDiscreteScheduler, dict(use_karras_sigmas=False)), - lms_k=(LMSDiscreteScheduler, dict(use_karras_sigmas=True)), - pndm=(PNDMScheduler, dict()), - heun=(HeunDiscreteScheduler, dict(use_karras_sigmas=False)), - heun_k=(HeunDiscreteScheduler, dict(use_karras_sigmas=True)), - euler=(EulerDiscreteScheduler, dict(use_karras_sigmas=False)), - euler_k=(EulerDiscreteScheduler, dict(use_karras_sigmas=True)), - euler_a=(EulerAncestralDiscreteScheduler, dict()), - kdpm_2=(KDPM2DiscreteScheduler, dict()), - kdpm_2_a=(KDPM2AncestralDiscreteScheduler, dict()), - dpmpp_2s=(DPMSolverSinglestepScheduler, dict(use_karras_sigmas=False)), - dpmpp_2s_k=(DPMSolverSinglestepScheduler, dict(use_karras_sigmas=True)), - dpmpp_2m=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=False)), - dpmpp_2m_k=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=True)), - dpmpp_2m_sde=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=False, algorithm_type="sde-dpmsolver++")), - dpmpp_2m_sde_k=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=True, algorithm_type="sde-dpmsolver++")), - dpmpp_sde=(DPMSolverSDEScheduler, dict(use_karras_sigmas=False, noise_sampler_seed=0)), - dpmpp_sde_k=(DPMSolverSDEScheduler, dict(use_karras_sigmas=True, noise_sampler_seed=0)), - unipc=(UniPCMultistepScheduler, dict(cpu_only=True)), - lcm=(LCMScheduler, dict()), -) +SCHEDULER_MAP = { + "ddim": (DDIMScheduler, {}), + "ddpm": (DDPMScheduler, {}), + "deis": (DEISMultistepScheduler, {}), + "lms": (LMSDiscreteScheduler, {"use_karras_sigmas": False}), + "lms_k": (LMSDiscreteScheduler, {"use_karras_sigmas": True}), + "pndm": (PNDMScheduler, {}), + "heun": (HeunDiscreteScheduler, {"use_karras_sigmas": False}), + "heun_k": (HeunDiscreteScheduler, {"use_karras_sigmas": True}), + "euler": (EulerDiscreteScheduler, {"use_karras_sigmas": False}), + "euler_k": (EulerDiscreteScheduler, {"use_karras_sigmas": True}), + "euler_a": (EulerAncestralDiscreteScheduler, {}), + "kdpm_2": (KDPM2DiscreteScheduler, {}), + "kdpm_2_a": (KDPM2AncestralDiscreteScheduler, {}), + "dpmpp_2s": (DPMSolverSinglestepScheduler, {"use_karras_sigmas": False}), + "dpmpp_2s_k": (DPMSolverSinglestepScheduler, {"use_karras_sigmas": True}), + "dpmpp_2m": (DPMSolverMultistepScheduler, {"use_karras_sigmas": False}), + "dpmpp_2m_k": (DPMSolverMultistepScheduler, {"use_karras_sigmas": True}), + "dpmpp_2m_sde": (DPMSolverMultistepScheduler, {"use_karras_sigmas": False, "algorithm_type": "sde-dpmsolver++"}), + "dpmpp_2m_sde_k": (DPMSolverMultistepScheduler, {"use_karras_sigmas": True, "algorithm_type": "sde-dpmsolver++"}), + "dpmpp_sde": (DPMSolverSDEScheduler, {"use_karras_sigmas": False, "noise_sampler_seed": 0}), + "dpmpp_sde_k": (DPMSolverSDEScheduler, {"use_karras_sigmas": True, "noise_sampler_seed": 0}), + "unipc": (UniPCMultistepScheduler, {"cpu_only": True}), + "lcm": (LCMScheduler, {}), +} diff --git a/invokeai/backend/training/textual_inversion_training.py b/invokeai/backend/training/textual_inversion_training.py index 9bc1d188bc..0d1b9a2d8c 100644 --- a/invokeai/backend/training/textual_inversion_training.py +++ b/invokeai/backend/training/textual_inversion_training.py @@ -615,7 +615,7 @@ def do_textual_inversion_training( vae_info = model_manager.get_model(*model_meta, submodel=SubModelType.Vae) unet_info = model_manager.get_model(*model_meta, submodel=SubModelType.UNet) - pipeline_args = dict(local_files_only=True) + pipeline_args = {"local_files_only": True} if tokenizer_name: tokenizer = CLIPTokenizer.from_pretrained(tokenizer_name, **pipeline_args) else: diff --git a/invokeai/backend/util/logging.py b/invokeai/backend/util/logging.py index 3c829a1a02..cb9f362d90 100644 --- a/invokeai/backend/util/logging.py +++ b/invokeai/backend/util/logging.py @@ -225,34 +225,34 @@ def basicConfig(**kwargs): _FACILITY_MAP = ( - dict( - LOG_KERN=syslog.LOG_KERN, - LOG_USER=syslog.LOG_USER, - LOG_MAIL=syslog.LOG_MAIL, - LOG_DAEMON=syslog.LOG_DAEMON, - LOG_AUTH=syslog.LOG_AUTH, - LOG_LPR=syslog.LOG_LPR, - LOG_NEWS=syslog.LOG_NEWS, - LOG_UUCP=syslog.LOG_UUCP, - LOG_CRON=syslog.LOG_CRON, - LOG_SYSLOG=syslog.LOG_SYSLOG, - LOG_LOCAL0=syslog.LOG_LOCAL0, - LOG_LOCAL1=syslog.LOG_LOCAL1, - LOG_LOCAL2=syslog.LOG_LOCAL2, - LOG_LOCAL3=syslog.LOG_LOCAL3, - LOG_LOCAL4=syslog.LOG_LOCAL4, - LOG_LOCAL5=syslog.LOG_LOCAL5, - LOG_LOCAL6=syslog.LOG_LOCAL6, - LOG_LOCAL7=syslog.LOG_LOCAL7, - ) + { + "LOG_KERN": syslog.LOG_KERN, + "LOG_USER": syslog.LOG_USER, + "LOG_MAIL": syslog.LOG_MAIL, + "LOG_DAEMON": syslog.LOG_DAEMON, + "LOG_AUTH": syslog.LOG_AUTH, + "LOG_LPR": syslog.LOG_LPR, + "LOG_NEWS": syslog.LOG_NEWS, + "LOG_UUCP": syslog.LOG_UUCP, + "LOG_CRON": syslog.LOG_CRON, + "LOG_SYSLOG": syslog.LOG_SYSLOG, + "LOG_LOCAL0": syslog.LOG_LOCAL0, + "LOG_LOCAL1": syslog.LOG_LOCAL1, + "LOG_LOCAL2": syslog.LOG_LOCAL2, + "LOG_LOCAL3": syslog.LOG_LOCAL3, + "LOG_LOCAL4": syslog.LOG_LOCAL4, + "LOG_LOCAL5": syslog.LOG_LOCAL5, + "LOG_LOCAL6": syslog.LOG_LOCAL6, + "LOG_LOCAL7": syslog.LOG_LOCAL7, + } if SYSLOG_AVAILABLE - else dict() + else {} ) -_SOCK_MAP = dict( - SOCK_STREAM=socket.SOCK_STREAM, - SOCK_DGRAM=socket.SOCK_DGRAM, -) +_SOCK_MAP = { + "SOCK_STREAM": socket.SOCK_STREAM, + "SOCK_DGRAM": socket.SOCK_DGRAM, +} class InvokeAIFormatter(logging.Formatter): @@ -344,7 +344,7 @@ LOG_FORMATTERS = { class InvokeAILogger(object): - loggers = dict() + loggers = {} @classmethod def get_logger( @@ -364,7 +364,7 @@ class InvokeAILogger(object): @classmethod def get_loggers(cls, config: InvokeAIAppConfig) -> list[logging.Handler]: handler_strs = config.log_handlers - handlers = list() + handlers = [] for handler in handler_strs: handler_name, *args = handler.split("=", 2) args = args[0] if len(args) > 0 else None @@ -398,7 +398,7 @@ class InvokeAILogger(object): raise ValueError("syslog is not available on this system") if not args: args = "/dev/log" if Path("/dev/log").exists() else "address:localhost:514" - syslog_args = dict() + syslog_args = {} try: for a in args.split(","): arg_name, *arg_value = a.split(":", 2) @@ -434,7 +434,7 @@ class InvokeAILogger(object): path = url.path port = url.port or 80 - syslog_args = dict() + syslog_args = {} for a in arg_list: arg_name, *arg_value = a.split(":", 2) if arg_name == "method": diff --git a/invokeai/backend/util/util.py b/invokeai/backend/util/util.py index 0796f1a8cd..de34034bf5 100644 --- a/invokeai/backend/util/util.py +++ b/invokeai/backend/util/util.py @@ -26,7 +26,7 @@ def log_txt_as_img(wh, xc, size=10): # wh a tuple of (width, height) # xc a list of captions to plot b = len(xc) - txts = list() + txts = [] for bi in range(b): txt = Image.new("RGB", wh, color="white") draw = ImageDraw.Draw(txt) @@ -90,7 +90,7 @@ def instantiate_from_config(config, **kwargs): elif config == "__is_unconditional__": return None raise KeyError("Expected key `target` to instantiate.") - return get_obj_from_str(config["target"])(**config.get("params", dict()), **kwargs) + return get_obj_from_str(config["target"])(**config.get("params", {}), **kwargs) def get_obj_from_str(string, reload=False): diff --git a/invokeai/frontend/install/import_images.py b/invokeai/frontend/install/import_images.py index ec90700bd0..61faa48f9d 100644 --- a/invokeai/frontend/install/import_images.py +++ b/invokeai/frontend/install/import_images.py @@ -341,19 +341,19 @@ class InvokeAIMetadataParser: # this was more elegant as a case statement, but that's not available in python 3.9 if old_scheduler is None: return None - scheduler_map = dict( - ddim="ddim", - plms="pnmd", - k_lms="lms", - k_dpm_2="kdpm_2", - k_dpm_2_a="kdpm_2_a", - dpmpp_2="dpmpp_2s", - k_dpmpp_2="dpmpp_2m", - k_dpmpp_2_a=None, # invalid, in 2.3.x, selecting this sample would just fallback to last run or plms if new session - k_euler="euler", - k_euler_a="euler_a", - k_heun="heun", - ) + scheduler_map = { + "ddim": "ddim", + "plms": "pnmd", + "k_lms": "lms", + "k_dpm_2": "kdpm_2", + "k_dpm_2_a": "kdpm_2_a", + "dpmpp_2": "dpmpp_2s", + "k_dpmpp_2": "dpmpp_2m", + "k_dpmpp_2_a": None, # invalid, in 2.3.x, selecting this sample would just fallback to last run or plms if new session + "k_euler": "euler", + "k_euler_a": "euler_a", + "k_heun": "heun", + } return scheduler_map.get(old_scheduler) def split_prompt(self, raw_prompt: str): diff --git a/invokeai/frontend/install/model_install.py b/invokeai/frontend/install/model_install.py index 0ea2570b2b..08afd0d52a 100644 --- a/invokeai/frontend/install/model_install.py +++ b/invokeai/frontend/install/model_install.py @@ -210,7 +210,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): ############# diffusers tab ########## def add_starter_pipelines(self) -> dict[str, npyscreen.widget]: """Add widgets responsible for selecting diffusers models""" - widgets = dict() + widgets = {} models = self.all_models starters = self.starter_models starter_model_labels = self.model_labels @@ -261,7 +261,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): exclude: set = set(), ) -> dict[str, npyscreen.widget]: """Generic code to create model selection widgets""" - widgets = dict() + widgets = {} model_list = [x for x in self.all_models if self.all_models[x].model_type == model_type and x not in exclude] model_labels = [self.model_labels[x] for x in model_list] @@ -391,7 +391,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): label_width = max([len(models[x].name) for x in models]) description_width = window_width - label_width - checkbox_width - spacing_width - result = dict() + result = {} for x in models.keys(): description = models[x].description description = ( @@ -433,11 +433,11 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): parent_conn, child_conn = Pipe() p = Process( target=process_and_execute, - kwargs=dict( - opt=app.program_opts, - selections=app.install_selections, - conn_out=child_conn, - ), + kwargs={ + "opt": app.program_opts, + "selections": app.install_selections, + "conn_out": child_conn, + }, ) p.start() child_conn.close() @@ -558,7 +558,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): for section in ui_sections: if "models_selected" not in section: continue - selected = set([section["models"][x] for x in section["models_selected"].value]) + selected = {section["models"][x] for x in section["models_selected"].value} models_to_install = [x for x in selected if not self.all_models[x].installed] models_to_remove = [x for x in section["models"] if x not in selected and self.all_models[x].installed] selections.remove_models.extend(models_to_remove) diff --git a/invokeai/frontend/merge/merge_diffusers.py b/invokeai/frontend/merge/merge_diffusers.py index d515c5b4ee..92b98b52f9 100644 --- a/invokeai/frontend/merge/merge_diffusers.py +++ b/invokeai/frontend/merge/merge_diffusers.py @@ -275,14 +275,14 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): interp = self.interpolations[self.merge_method.value[0]] bases = ["sd-1", "sd-2", "sdxl"] - args = dict( - model_names=models, - base_model=BaseModelType(bases[self.base_select.value[0]]), - alpha=self.alpha.value, - interp=interp, - force=self.force.value, - merged_model_name=self.merged_model_name.value, - ) + args = { + "model_names": models, + "base_model": BaseModelType(bases[self.base_select.value[0]]), + "alpha": self.alpha.value, + "interp": interp, + "force": self.force.value, + "merged_model_name": self.merged_model_name.value, + } return args def check_for_overwrite(self) -> bool: @@ -297,7 +297,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction): def validate_field_values(self) -> bool: bad_fields = [] model_names = self.model_names - selected_models = set((model_names[self.model1.value[0]], model_names[self.model2.value[0]])) + selected_models = {model_names[self.model1.value[0]], model_names[self.model2.value[0]]} if self.model3.value[0] > 0: selected_models.add(model_names[self.model3.value[0] - 1]) if len(selected_models) < 2: diff --git a/invokeai/frontend/training/textual_inversion.py b/invokeai/frontend/training/textual_inversion.py index f3911f7e0e..556f216e97 100755 --- a/invokeai/frontend/training/textual_inversion.py +++ b/invokeai/frontend/training/textual_inversion.py @@ -276,13 +276,13 @@ class textualInversionForm(npyscreen.FormMultiPageAction): def get_model_names(self) -> Tuple[List[str], int]: conf = OmegaConf.load(config.root_dir / "configs/models.yaml") - model_names = [idx for idx in sorted(list(conf.keys())) if conf[idx].get("format", None) == "diffusers"] + model_names = [idx for idx in sorted(conf.keys()) if conf[idx].get("format", None) == "diffusers"] defaults = [idx for idx in range(len(model_names)) if "default" in conf[model_names[idx]]] default = defaults[0] if len(defaults) > 0 else 0 return (model_names, default) def marshall_arguments(self) -> dict: - args = dict() + args = {} # the choices args.update( diff --git a/scripts/scan_models_directory.py b/scripts/scan_models_directory.py index 0038023c06..a85fb793dd 100755 --- a/scripts/scan_models_directory.py +++ b/scripts/scan_models_directory.py @@ -37,22 +37,22 @@ def main(): if args.all_models or model_type == "diffusers": for d in dirs: - conf[f"{base}/{model_type}/{d}"] = dict( - path=os.path.join(root, d), - description=f"{model_type} model {d}", - format="folder", - base=base, - ) + conf[f"{base}/{model_type}/{d}"] = { + "path": os.path.join(root, d), + "description": f"{model_type} model {d}", + "format": "folder", + "base": base, + } for f in files: basename = Path(f).stem format = Path(f).suffix[1:] - conf[f"{base}/{model_type}/{basename}"] = dict( - path=os.path.join(root, f), - description=f"{model_type} model {basename}", - format=format, - base=base, - ) + conf[f"{base}/{model_type}/{basename}"] = { + "path": os.path.join(root, f), + "description": f"{model_type} model {basename}", + "format": format, + "base": base, + } OmegaConf.save(config=dict(sorted(conf.items())), f=sys.stdout) diff --git a/tests/nodes/test_graph_execution_state.py b/tests/nodes/test_graph_execution_state.py index 171cdfdb6f..f518460612 100644 --- a/tests/nodes/test_graph_execution_state.py +++ b/tests/nodes/test_graph_execution_state.py @@ -149,8 +149,8 @@ def test_graph_state_expands_iterator(mock_services): invoke_next(g, mock_services) prepared_add_nodes = g.source_prepared_mapping["3"] - results = set([g.results[n].value for n in prepared_add_nodes]) - expected = set([1, 11, 21]) + results = {g.results[n].value for n in prepared_add_nodes} + expected = {1, 11, 21} assert results == expected @@ -229,7 +229,7 @@ def test_graph_executes_depth_first(mock_services): # Because ordering is not guaranteed, we cannot compare results directly. # Instead, we must count the number of results. def get_completed_count(g, id): - ids = [i for i in g.source_prepared_mapping[id]] + ids = list(g.source_prepared_mapping[id]) completed_ids = [i for i in g.executed if i in ids] return len(completed_ids) diff --git a/tests/nodes/test_node_graph.py b/tests/nodes/test_node_graph.py index e2a50e61e5..4c75ff3610 100644 --- a/tests/nodes/test_node_graph.py +++ b/tests/nodes/test_node_graph.py @@ -503,8 +503,8 @@ def test_graph_expands_subgraph(): g.add_edge(create_edge("1.2", "value", "2", "a")) dg = g.nx_graph_flat() - assert set(dg.nodes) == set(["1.1", "1.2", "2"]) - assert set(dg.edges) == set([("1.1", "1.2"), ("1.2", "2")]) + assert set(dg.nodes) == {"1.1", "1.2", "2"} + assert set(dg.edges) == {("1.1", "1.2"), ("1.2", "2")} def test_graph_subgraph_t2i(): @@ -532,9 +532,7 @@ def test_graph_subgraph_t2i(): # Validate dg = g.nx_graph_flat() - assert set(dg.nodes) == set( - ["1.width", "1.height", "1.seed", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "2", "3", "4"] - ) + assert set(dg.nodes) == {"1.width", "1.height", "1.seed", "1.3", "1.4", "1.5", "1.6", "1.7", "1.8", "2", "3", "4"} expected_edges = [(f"1.{e.source.node_id}", f"1.{e.destination.node_id}") for e in lg.graph.edges] expected_edges.extend([("2", "1.width"), ("3", "1.height"), ("1.8", "4")]) print(expected_edges) diff --git a/tests/nodes/test_nodes.py b/tests/nodes/test_nodes.py index 51b33dd4c7..caafa33591 100644 --- a/tests/nodes/test_nodes.py +++ b/tests/nodes/test_nodes.py @@ -130,7 +130,7 @@ class TestEventService(EventServiceBase): def __init__(self): super().__init__() - self.events = list() + self.events = [] def dispatch(self, event_name: str, payload: Any) -> None: pass From 99a8ebe3a035ee3f34aeea1e38e7ecd06d3b2ae5 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:51:21 +1100 Subject: [PATCH 07/24] chore: ruff check - fix flake8-bugbear --- installer/lib/messages.py | 2 +- invokeai/app/invocations/facetools.py | 2 +- invokeai/app/invocations/latent.py | 2 +- .../invocation_processor_base.py | 2 +- .../invocation_stats/invocation_stats_default.py | 2 +- .../services/session_queue/session_queue_common.py | 4 ++-- invokeai/app/services/shared/graph.py | 2 +- invokeai/app/util/controlnet_utils.py | 2 +- invokeai/backend/install/migrate_to_3.py | 4 ---- invokeai/backend/install/model_install_backend.py | 2 +- invokeai/backend/ip_adapter/attention_processor.py | 2 +- .../model_management/convert_ckpt_to_diffusers.py | 6 +++--- invokeai/backend/model_management/lora.py | 2 +- .../model_management/model_load_optimizations.py | 2 +- invokeai/backend/model_management/model_manager.py | 2 +- invokeai/backend/model_management/model_probe.py | 2 +- .../backend/model_management/models/__init__.py | 2 +- invokeai/backend/model_management/models/base.py | 2 +- invokeai/backend/model_management/models/lora.py | 2 +- .../diffusion/cross_attention_control.py | 14 +++++++------- .../diffusion/cross_attention_map_saving.py | 2 +- .../diffusion/shared_invokeai_diffusion.py | 4 ++-- invokeai/backend/util/hotfixes.py | 4 ++-- invokeai/frontend/install/model_install.py | 12 +++++++----- invokeai/frontend/install/widgets.py | 9 +++++++-- scripts/configure_invokeai.py | 2 +- tests/nodes/test_node_graph.py | 3 --- 27 files changed, 48 insertions(+), 48 deletions(-) diff --git a/installer/lib/messages.py b/installer/lib/messages.py index e4c03bbfd2..b1ffc256c1 100644 --- a/installer/lib/messages.py +++ b/installer/lib/messages.py @@ -137,7 +137,7 @@ def dest_path(dest=None) -> Path: path_completer = PathCompleter( only_directories=True, expanduser=True, - get_paths=lambda: [browse_start], + get_paths=lambda: [browse_start], # noqa: B023 # get_paths=lambda: [".."].extend(list(browse_start.iterdir())) ) diff --git a/invokeai/app/invocations/facetools.py b/invokeai/app/invocations/facetools.py index 2fe4565d78..41d1ef1e4b 100644 --- a/invokeai/app/invocations/facetools.py +++ b/invokeai/app/invocations/facetools.py @@ -210,7 +210,7 @@ def generate_face_box_mask( # Check if any face is detected. if results.multi_face_landmarks: # type: ignore # this are via protobuf and not typed # Search for the face_id in the detected faces. - for face_id, face_landmarks in enumerate(results.multi_face_landmarks): # type: ignore #this are via protobuf and not typed + for _face_id, face_landmarks in enumerate(results.multi_face_landmarks): # type: ignore #this are via protobuf and not typed # Get the bounding box of the face mesh. x_coordinates = [landmark.x for landmark in face_landmarks.landmark] y_coordinates = [landmark.y for landmark in face_landmarks.landmark] diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index d86dbc7166..9412aec39b 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -1105,7 +1105,7 @@ class BlendLatentsInvocation(BaseInvocation): latents_b = context.services.latents.get(self.latents_b.latents_name) if latents_a.shape != latents_b.shape: - raise "Latents to blend must be the same size." + raise Exception("Latents to blend must be the same size.") # TODO: device = choose_torch_device() diff --git a/invokeai/app/services/invocation_processor/invocation_processor_base.py b/invokeai/app/services/invocation_processor/invocation_processor_base.py index 04774accc2..700a15e643 100644 --- a/invokeai/app/services/invocation_processor/invocation_processor_base.py +++ b/invokeai/app/services/invocation_processor/invocation_processor_base.py @@ -1,5 +1,5 @@ from abc import ABC -class InvocationProcessorABC(ABC): +class InvocationProcessorABC(ABC): # noqa: B024 pass diff --git a/invokeai/app/services/invocation_stats/invocation_stats_default.py b/invokeai/app/services/invocation_stats/invocation_stats_default.py index e7392c64be..34d2cd8354 100644 --- a/invokeai/app/services/invocation_stats/invocation_stats_default.py +++ b/invokeai/app/services/invocation_stats/invocation_stats_default.py @@ -122,7 +122,7 @@ class InvocationStatsService(InvocationStatsServiceBase): def log_stats(self): completed = set() errored = set() - for graph_id, node_log in self._stats.items(): + for graph_id, _node_log in self._stats.items(): try: current_graph_state = self._invoker.services.graph_execution_manager.get(graph_id) except Exception: diff --git a/invokeai/app/services/session_queue/session_queue_common.py b/invokeai/app/services/session_queue/session_queue_common.py index 67952c40a0..e7d7cdda46 100644 --- a/invokeai/app/services/session_queue/session_queue_common.py +++ b/invokeai/app/services/session_queue/session_queue_common.py @@ -355,7 +355,7 @@ def create_session_nfv_tuples( for item in batch_datum.items ] node_field_values_to_zip.append(node_field_values) - data.append(list(zip(*node_field_values_to_zip))) # type: ignore [arg-type] + data.append(list(zip(*node_field_values_to_zip, strict=True))) # type: ignore [arg-type] # create generator to yield session,nfv tuples count = 0 @@ -383,7 +383,7 @@ def calc_session_count(batch: Batch) -> int: for batch_datum in batch_datum_list: batch_data_items = range(len(batch_datum.items)) to_zip.append(batch_data_items) - data.append(list(zip(*to_zip))) + data.append(list(zip(*to_zip, strict=True))) data_product = list(product(*data)) return len(data_product) * batch.runs diff --git a/invokeai/app/services/shared/graph.py b/invokeai/app/services/shared/graph.py index 70444ee204..63fca9d89e 100644 --- a/invokeai/app/services/shared/graph.py +++ b/invokeai/app/services/shared/graph.py @@ -1119,7 +1119,7 @@ class GraphExecutionState(BaseModel): for edge in input_edges if edge.destination.field == "item" ] - setattr(node, "collection", output_collection) + node.collection = output_collection else: for edge in input_edges: output_value = getattr(self.results[edge.source.node_id], edge.source.field) diff --git a/invokeai/app/util/controlnet_utils.py b/invokeai/app/util/controlnet_utils.py index 51ceec2edd..b3e2560211 100644 --- a/invokeai/app/util/controlnet_utils.py +++ b/invokeai/app/util/controlnet_utils.py @@ -59,7 +59,7 @@ def thin_one_time(x, kernels): def lvmin_thin(x, prunings=True): y = x - for i in range(32): + for _i in range(32): y, is_done = thin_one_time(y, lvmin_kernels) if is_done: break diff --git a/invokeai/backend/install/migrate_to_3.py b/invokeai/backend/install/migrate_to_3.py index 62b23fb808..e15eb23f5b 100644 --- a/invokeai/backend/install/migrate_to_3.py +++ b/invokeai/backend/install/migrate_to_3.py @@ -123,8 +123,6 @@ class MigrateTo3(object): logger.error(str(e)) except KeyboardInterrupt: raise - except Exception as e: - logger.error(str(e)) for f in files: # don't copy raw learned_embeds.bin or pytorch_lora_weights.bin # let them be copied as part of a tree copy operation @@ -143,8 +141,6 @@ class MigrateTo3(object): logger.error(str(e)) except KeyboardInterrupt: raise - except Exception as e: - logger.error(str(e)) def migrate_support_models(self): """ diff --git a/invokeai/backend/install/model_install_backend.py b/invokeai/backend/install/model_install_backend.py index 56e512a73a..0962fe9740 100644 --- a/invokeai/backend/install/model_install_backend.py +++ b/invokeai/backend/install/model_install_backend.py @@ -176,7 +176,7 @@ class ModelInstall(object): # logic here a little reversed to maintain backward compatibility def starter_models(self, all_models: bool = False) -> Set[str]: models = set() - for key, value in self.datasets.items(): + for key, _value in self.datasets.items(): name, base, model_type = ModelManager.parse_key(key) if all_models or model_type in [ModelType.Main, ModelType.Vae]: models.add(key) diff --git a/invokeai/backend/ip_adapter/attention_processor.py b/invokeai/backend/ip_adapter/attention_processor.py index c06d7d113c..1ae731290e 100644 --- a/invokeai/backend/ip_adapter/attention_processor.py +++ b/invokeai/backend/ip_adapter/attention_processor.py @@ -130,7 +130,7 @@ class IPAttnProcessor2_0(torch.nn.Module): assert ip_adapter_image_prompt_embeds is not None assert len(ip_adapter_image_prompt_embeds) == len(self._weights) - for ipa_embed, ipa_weights, scale in zip(ip_adapter_image_prompt_embeds, self._weights, self._scales): + for ipa_embed, ipa_weights, scale in zip(ip_adapter_image_prompt_embeds, self._weights, self._scales, strict=True): # The batch dimensions should match. assert ipa_embed.shape[0] == encoder_hidden_states.shape[0] # The token_len dimensions should match. diff --git a/invokeai/backend/model_management/convert_ckpt_to_diffusers.py b/invokeai/backend/model_management/convert_ckpt_to_diffusers.py index 0a3a63dad6..1cecfb1a72 100644 --- a/invokeai/backend/model_management/convert_ckpt_to_diffusers.py +++ b/invokeai/backend/model_management/convert_ckpt_to_diffusers.py @@ -269,7 +269,7 @@ def create_unet_diffusers_config(original_config, image_size: int, controlnet=Fa resolution *= 2 up_block_types = [] - for i in range(len(block_out_channels)): + for _i in range(len(block_out_channels)): block_type = "CrossAttnUpBlock2D" if resolution in unet_params.attention_resolutions else "UpBlock2D" up_block_types.append(block_type) resolution //= 2 @@ -1223,7 +1223,7 @@ def download_from_original_stable_diffusion_ckpt( # scan model scan_result = scan_file_path(checkpoint_path) if scan_result.infected_files != 0: - raise "The model {checkpoint_path} is potentially infected by malware. Aborting import." + raise Exception("The model {checkpoint_path} is potentially infected by malware. Aborting import.") if device is None: device = "cuda" if torch.cuda.is_available() else "cpu" checkpoint = torch.load(checkpoint_path, map_location=device) @@ -1664,7 +1664,7 @@ def download_controlnet_from_original_ckpt( # scan model scan_result = scan_file_path(checkpoint_path) if scan_result.infected_files != 0: - raise "The model {checkpoint_path} is potentially infected by malware. Aborting import." + raise Exception("The model {checkpoint_path} is potentially infected by malware. Aborting import.") if device is None: device = "cuda" if torch.cuda.is_available() else "cpu" checkpoint = torch.load(checkpoint_path, map_location=device) diff --git a/invokeai/backend/model_management/lora.py b/invokeai/backend/model_management/lora.py index 411ec4a400..4389cacacc 100644 --- a/invokeai/backend/model_management/lora.py +++ b/invokeai/backend/model_management/lora.py @@ -242,7 +242,7 @@ class ModelPatcher: ): skipped_layers = [] try: - for i in range(clip_skip): + for _i in range(clip_skip): skipped_layers.append(text_encoder.text_model.encoder.layers.pop(-1)) yield diff --git a/invokeai/backend/model_management/model_load_optimizations.py b/invokeai/backend/model_management/model_load_optimizations.py index 8dc8a8793e..a46d262175 100644 --- a/invokeai/backend/model_management/model_load_optimizations.py +++ b/invokeai/backend/model_management/model_load_optimizations.py @@ -26,5 +26,5 @@ def skip_torch_weight_init(): yield None finally: - for torch_module, saved_function in zip(torch_modules, saved_functions): + for torch_module, saved_function in zip(torch_modules, saved_functions, strict=True): torch_module.reset_parameters = saved_function diff --git a/invokeai/backend/model_management/model_manager.py b/invokeai/backend/model_management/model_manager.py index f38b3cd325..e9f498a438 100644 --- a/invokeai/backend/model_management/model_manager.py +++ b/invokeai/backend/model_management/model_manager.py @@ -655,7 +655,7 @@ class ModelManager(object): """ # TODO: redo for model_dict in self.list_models(): - for model_name, model_info in model_dict.items(): + for _model_name, model_info in model_dict.items(): line = f'{model_info["name"]:25s} {model_info["type"]:10s} {model_info["description"]}' print(line) diff --git a/invokeai/backend/model_management/model_probe.py b/invokeai/backend/model_management/model_probe.py index e82be8d069..83d3d610c3 100644 --- a/invokeai/backend/model_management/model_probe.py +++ b/invokeai/backend/model_management/model_probe.py @@ -237,7 +237,7 @@ class ModelProbe(object): # scan model scan_result = scan_file_path(checkpoint) if scan_result.infected_files != 0: - raise "The model {model_name} is potentially infected by malware. Aborting import." + raise Exception("The model {model_name} is potentially infected by malware. Aborting import.") # ##################################################3 diff --git a/invokeai/backend/model_management/models/__init__.py b/invokeai/backend/model_management/models/__init__.py index 150ed7b371..5f9b13b96f 100644 --- a/invokeai/backend/model_management/models/__init__.py +++ b/invokeai/backend/model_management/models/__init__.py @@ -109,7 +109,7 @@ class OpenAPIModelInfoBase(BaseModel): model_config = ConfigDict(protected_namespaces=()) -for base_model, models in MODEL_CLASSES.items(): +for _base_model, models in MODEL_CLASSES.items(): for model_type, model_class in models.items(): model_configs = set(model_class._get_configs().values()) model_configs.discard(None) diff --git a/invokeai/backend/model_management/models/base.py b/invokeai/backend/model_management/models/base.py index 0038f9d9b0..7807cb9a54 100644 --- a/invokeai/backend/model_management/models/base.py +++ b/invokeai/backend/model_management/models/base.py @@ -153,7 +153,7 @@ class ModelBase(metaclass=ABCMeta): else: res_type = sys.modules["diffusers"] - res_type = getattr(res_type, "pipelines") + res_type = res_type.pipelines for subtype in subtypes: res_type = getattr(res_type, subtype) diff --git a/invokeai/backend/model_management/models/lora.py b/invokeai/backend/model_management/models/lora.py index 472815adbb..b110d75d22 100644 --- a/invokeai/backend/model_management/models/lora.py +++ b/invokeai/backend/model_management/models/lora.py @@ -462,7 +462,7 @@ class LoRAModelRaw: # (torch.nn.Module): dtype: Optional[torch.dtype] = None, ): # TODO: try revert if exception? - for key, layer in self.layers.items(): + for _key, layer in self.layers.items(): layer.to(device=device, dtype=dtype) def calc_size(self) -> int: diff --git a/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py b/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py index 3cb1862004..b45bb8f338 100644 --- a/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py +++ b/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py @@ -54,13 +54,13 @@ class Context: self.clear_requests(cleanup=True) def register_cross_attention_modules(self, model): - for name, module in get_cross_attention_modules(model, CrossAttentionType.SELF): + for name, _module in get_cross_attention_modules(model, CrossAttentionType.SELF): if name in self.self_cross_attention_module_identifiers: - assert False, f"name {name} cannot appear more than once" + raise AssertionError(f"name {name} cannot appear more than once") self.self_cross_attention_module_identifiers.append(name) - for name, module in get_cross_attention_modules(model, CrossAttentionType.TOKENS): + for name, _module in get_cross_attention_modules(model, CrossAttentionType.TOKENS): if name in self.tokens_cross_attention_module_identifiers: - assert False, f"name {name} cannot appear more than once" + raise AssertionError(f"name {name} cannot appear more than once") self.tokens_cross_attention_module_identifiers.append(name) def request_save_attention_maps(self, cross_attention_type: CrossAttentionType): @@ -170,7 +170,7 @@ class Context: self.saved_cross_attention_maps = {} def offload_saved_attention_slices_to_cpu(self): - for key, map_dict in self.saved_cross_attention_maps.items(): + for _key, map_dict in self.saved_cross_attention_maps.items(): for offset, slice in map_dict["slices"].items(): map_dict[offset] = slice.to("cpu") @@ -433,7 +433,7 @@ def inject_attention_function(unet, context: Context): module.identifier = identifier try: module.set_attention_slice_wrangler(attention_slice_wrangler) - module.set_slicing_strategy_getter(lambda module: context.get_slicing_strategy(identifier)) + module.set_slicing_strategy_getter(lambda module: context.get_slicing_strategy(identifier)) # noqa: B023 except AttributeError as e: if is_attribute_error_about(e, "set_attention_slice_wrangler"): print(f"TODO: implement set_attention_slice_wrangler for {type(module)}") # TODO @@ -445,7 +445,7 @@ def remove_attention_function(unet): cross_attention_modules = get_cross_attention_modules( unet, CrossAttentionType.TOKENS ) + get_cross_attention_modules(unet, CrossAttentionType.SELF) - for identifier, module in cross_attention_modules: + for _identifier, module in cross_attention_modules: try: # clear wrangler callback module.set_attention_slice_wrangler(None) diff --git a/invokeai/backend/stable_diffusion/diffusion/cross_attention_map_saving.py b/invokeai/backend/stable_diffusion/diffusion/cross_attention_map_saving.py index b5ea40185a..82c9f1dcea 100644 --- a/invokeai/backend/stable_diffusion/diffusion/cross_attention_map_saving.py +++ b/invokeai/backend/stable_diffusion/diffusion/cross_attention_map_saving.py @@ -56,7 +56,7 @@ class AttentionMapSaver: merged = None - for key, maps in self.collated_maps.items(): + for _key, maps in self.collated_maps.items(): # maps has shape [(H*W), N] for N tokens # but we want [N, H, W] this_scale_factor = math.sqrt(maps.shape[0] / (latents_width * latents_height)) diff --git a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py index b7c0058fe9..5cfe846d50 100644 --- a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py +++ b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py @@ -123,7 +123,7 @@ class InvokeAIDiffuserComponent: # control_data should be type List[ControlNetData] # this loop covers both ControlNet (one ControlNetData in list) # and MultiControlNet (multiple ControlNetData in list) - for i, control_datum in enumerate(control_data): + for _i, control_datum in enumerate(control_data): control_mode = control_datum.control_mode # soft_injection and cfg_injection are the two ControlNet control_mode booleans # that are combined at higher level to make control_mode enum @@ -214,7 +214,7 @@ class InvokeAIDiffuserComponent: # add controlnet outputs together if have multiple controlnets down_block_res_samples = [ samples_prev + samples_curr - for samples_prev, samples_curr in zip(down_block_res_samples, down_samples) + for samples_prev, samples_curr in zip(down_block_res_samples, down_samples, strict=True) ] mid_block_res_sample += mid_sample diff --git a/invokeai/backend/util/hotfixes.py b/invokeai/backend/util/hotfixes.py index fb1297996c..32bc52c738 100644 --- a/invokeai/backend/util/hotfixes.py +++ b/invokeai/backend/util/hotfixes.py @@ -732,7 +732,7 @@ class ControlNetModel(ModelMixin, ConfigMixin, FromOriginalControlnetMixin): controlnet_down_block_res_samples = () - for down_block_res_sample, controlnet_block in zip(down_block_res_samples, self.controlnet_down_blocks): + for down_block_res_sample, controlnet_block in zip(down_block_res_samples, self.controlnet_down_blocks, strict=True): down_block_res_sample = controlnet_block(down_block_res_sample) controlnet_down_block_res_samples = controlnet_down_block_res_samples + (down_block_res_sample,) @@ -745,7 +745,7 @@ class ControlNetModel(ModelMixin, ConfigMixin, FromOriginalControlnetMixin): scales = torch.logspace(-1, 0, len(down_block_res_samples) + 1, device=sample.device) # 0.1 to 1.0 scales = scales * conditioning_scale - down_block_res_samples = [sample * scale for sample, scale in zip(down_block_res_samples, scales)] + down_block_res_samples = [sample * scale for sample, scale in zip(down_block_res_samples, scales, strict=True)] mid_block_res_sample = mid_block_res_sample * scales[-1] # last one else: down_block_res_samples = [sample * conditioning_scale for sample in down_block_res_samples] diff --git a/invokeai/frontend/install/model_install.py b/invokeai/frontend/install/model_install.py index 08afd0d52a..a47bd4c35c 100644 --- a/invokeai/frontend/install/model_install.py +++ b/invokeai/frontend/install/model_install.py @@ -72,7 +72,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): def __init__(self, parentApp, name, multipage=False, *args, **keywords): self.multipage = multipage self.subprocess = None - super().__init__(parentApp=parentApp, name=name, *args, **keywords) + super().__init__(parentApp=parentApp, name=name, *args, **keywords) # noqa: B026 # TODO: maybe this is bad? def create(self): self.keypress_timeout = 10 @@ -203,7 +203,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): ) # This restores the selected page on return from an installation - for i in range(1, self.current_tab + 1): + for _i in range(1, self.current_tab + 1): self.tabs.h_cursor_line_down(1) self._toggle_tables([self.current_tab]) @@ -258,9 +258,11 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): model_type: ModelType, window_width: int = 120, install_prompt: str = None, - exclude: set = set(), + exclude: set = None, ) -> dict[str, npyscreen.widget]: """Generic code to create model selection widgets""" + if exclude is None: + exclude = set() widgets = {} model_list = [x for x in self.all_models if self.all_models[x].model_type == model_type and x not in exclude] model_labels = [self.model_labels[x] for x in model_list] @@ -366,13 +368,13 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): ] for group in widgets: - for k, v in group.items(): + for _k, v in group.items(): try: v.hidden = True v.editable = False except Exception: pass - for k, v in widgets[selected_tab].items(): + for _k, v in widgets[selected_tab].items(): try: v.hidden = False if not isinstance(v, (npyscreen.FixedText, npyscreen.TitleFixedText, CenteredTitleText)): diff --git a/invokeai/frontend/install/widgets.py b/invokeai/frontend/install/widgets.py index 4a37aba9b8..5905ae29da 100644 --- a/invokeai/frontend/install/widgets.py +++ b/invokeai/frontend/install/widgets.py @@ -11,6 +11,7 @@ import sys import textwrap from curses import BUTTON2_CLICKED, BUTTON3_CLICKED from shutil import get_terminal_size +from typing import Optional import npyscreen import npyscreen.wgmultiline as wgmultiline @@ -243,7 +244,9 @@ class SelectColumnBase: class MultiSelectColumns(SelectColumnBase, npyscreen.MultiSelect): - def __init__(self, screen, columns: int = 1, values: list = [], **keywords): + def __init__(self, screen, columns: int = 1, values: Optional[list] = None, **keywords): + if values is None: + values = [] self.columns = columns self.value_cnt = len(values) self.rows = math.ceil(self.value_cnt / self.columns) @@ -267,7 +270,9 @@ class SingleSelectWithChanged(npyscreen.SelectOne): class SingleSelectColumnsSimple(SelectColumnBase, SingleSelectWithChanged): """Row of radio buttons. Spacebar to select.""" - def __init__(self, screen, columns: int = 1, values: list = [], **keywords): + def __init__(self, screen, columns: int = 1, values: list = None, **keywords): + if values is None: + values = [] self.columns = columns self.value_cnt = len(values) self.rows = math.ceil(self.value_cnt / self.columns) diff --git a/scripts/configure_invokeai.py b/scripts/configure_invokeai.py index 98e5044511..dff658c2b0 100755 --- a/scripts/configure_invokeai.py +++ b/scripts/configure_invokeai.py @@ -6,5 +6,5 @@ import warnings from invokeai.frontend.install.invokeai_configure import invokeai_configure as configure if __name__ == "__main__": - warnings.warn("configure_invokeai.py is deprecated, running 'invokeai-configure'...", DeprecationWarning) + warnings.warn("configure_invokeai.py is deprecated, running 'invokeai-configure'...", DeprecationWarning, stacklevel=2) configure() diff --git a/tests/nodes/test_node_graph.py b/tests/nodes/test_node_graph.py index 4c75ff3610..12a181f392 100644 --- a/tests/nodes/test_node_graph.py +++ b/tests/nodes/test_node_graph.py @@ -471,7 +471,6 @@ def test_graph_gets_subgraph_node(): g = Graph() n1 = GraphInvocation(id="1") n1.graph = Graph() - n1.graph.add_node n1_1 = TextToImageTestInvocation(id="1", prompt="Banana sushi") n1.graph.add_node(n1_1) @@ -544,7 +543,6 @@ def test_graph_fails_to_get_missing_subgraph_node(): g = Graph() n1 = GraphInvocation(id="1") n1.graph = Graph() - n1.graph.add_node n1_1 = TextToImageTestInvocation(id="1", prompt="Banana sushi") n1.graph.add_node(n1_1) @@ -559,7 +557,6 @@ def test_graph_fails_to_enumerate_non_subgraph_node(): g = Graph() n1 = GraphInvocation(id="1") n1.graph = Graph() - n1.graph.add_node n1_1 = TextToImageTestInvocation(id="1", prompt="Banana sushi") n1.graph.add_node(n1_1) From 513fceac82ac0a2ac792e2edbb4493653f5e9672 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:54:52 +1100 Subject: [PATCH 08/24] chore: ruff check - fix pycodestyle --- invokeai/app/invocations/math.py | 2 +- invokeai/app/util/metadata.py | 6 +++--- invokeai/backend/image_util/txt2mask.py | 2 +- invokeai/backend/ip_adapter/resampler.py | 4 ++-- .../diffusion/shared_invokeai_diffusion.py | 2 +- invokeai/backend/util/util.py | 7 ++----- tests/nodes/test_session_queue.py | 2 +- 7 files changed, 11 insertions(+), 14 deletions(-) diff --git a/invokeai/app/invocations/math.py b/invokeai/app/invocations/math.py index c50da4c16f..defc61275f 100644 --- a/invokeai/app/invocations/math.py +++ b/invokeai/app/invocations/math.py @@ -266,7 +266,7 @@ class FloatMathInvocation(BaseInvocation): raise ValueError("Cannot divide by zero") elif info.data["operation"] == "EXP" and info.data["a"] == 0 and v < 0: raise ValueError("Cannot raise zero to a negative power") - elif info.data["operation"] == "EXP" and type(info.data["a"] ** v) is complex: + elif info.data["operation"] == "EXP" and isinstance(info.data["a"] ** v, complex): raise ValueError("Root operation resulted in a complex number") return v diff --git a/invokeai/app/util/metadata.py b/invokeai/app/util/metadata.py index 15951cb009..52f9750e4f 100644 --- a/invokeai/app/util/metadata.py +++ b/invokeai/app/util/metadata.py @@ -21,11 +21,11 @@ def get_metadata_graph_from_raw_session(session_raw: str) -> Optional[dict]: # sanity check make sure the graph is at least reasonably shaped if ( - type(graph) is not dict + not isinstance(graph, dict) or "nodes" not in graph - or type(graph["nodes"]) is not dict + or not isinstance(graph["nodes"], dict) or "edges" not in graph - or type(graph["edges"]) is not list + or not isinstance(graph["edges"], list) ): # something has gone terribly awry, return an empty dict return None diff --git a/invokeai/backend/image_util/txt2mask.py b/invokeai/backend/image_util/txt2mask.py index de0c6a1652..5cbc7c1e38 100644 --- a/invokeai/backend/image_util/txt2mask.py +++ b/invokeai/backend/image_util/txt2mask.py @@ -88,7 +88,7 @@ class Txt2Mask(object): provided image and returns a SegmentedGrayscale object in which the brighter pixels indicate where the object is inferred to be. """ - if type(image) is str: + if isinstance(image, str): image = Image.open(image).convert("RGB") image = ImageOps.exif_transpose(image) diff --git a/invokeai/backend/ip_adapter/resampler.py b/invokeai/backend/ip_adapter/resampler.py index 84224fd359..a8db22c0fd 100644 --- a/invokeai/backend/ip_adapter/resampler.py +++ b/invokeai/backend/ip_adapter/resampler.py @@ -56,7 +56,7 @@ class PerceiverAttention(nn.Module): x = self.norm1(x) latents = self.norm2(latents) - b, l, _ = latents.shape + b, L, _ = latents.shape q = self.to_q(latents) kv_input = torch.cat((x, latents), dim=-2) @@ -72,7 +72,7 @@ class PerceiverAttention(nn.Module): weight = torch.softmax(weight.float(), dim=-1).type(weight.dtype) out = weight @ v - out = out.permute(0, 2, 1, 3).reshape(b, l, -1) + out = out.permute(0, 2, 1, 3).reshape(b, L, -1) return self.to_out(out) diff --git a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py index 5cfe846d50..4123399cf4 100644 --- a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py +++ b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py @@ -642,7 +642,7 @@ class InvokeAIDiffuserComponent: deltas = None uncond_latents = None - weighted_cond_list = c_or_weighted_c_list if type(c_or_weighted_c_list) is list else [(c_or_weighted_c_list, 1)] + weighted_cond_list = c_or_weighted_c_list if isinstance(c_or_weighted_c_list, list) else [(c_or_weighted_c_list, 1)] # below is fugly omg conditionings = [uc] + [c for c, weight in weighted_cond_list] diff --git a/invokeai/backend/util/util.py b/invokeai/backend/util/util.py index de34034bf5..7043355219 100644 --- a/invokeai/backend/util/util.py +++ b/invokeai/backend/util/util.py @@ -228,11 +228,8 @@ def rand_perlin_2d(shape, res, device, fade=lambda t: 6 * t**5 - 15 * t**4 + 10 angles = 2 * math.pi * rand_val gradients = torch.stack((torch.cos(angles), torch.sin(angles)), dim=-1).to(device) - tile_grads = ( - lambda slice1, slice2: gradients[slice1[0] : slice1[1], slice2[0] : slice2[1]] - .repeat_interleave(d[0], 0) - .repeat_interleave(d[1], 1) - ) + def tile_grads(slice1, slice2): + return gradients[slice1[0]:slice1[1], slice2[0]:slice2[1]].repeat_interleave(d[0], 0).repeat_interleave(d[1], 1) def dot(grad, shift): return ( diff --git a/tests/nodes/test_session_queue.py b/tests/nodes/test_session_queue.py index cdab5729f8..768b09898d 100644 --- a/tests/nodes/test_session_queue.py +++ b/tests/nodes/test_session_queue.py @@ -169,7 +169,7 @@ def test_prepare_values_to_insert(batch_data_collection, batch_graph): NodeFieldValueValidator = TypeAdapter(list[NodeFieldValue]) # should have 3 node field values - assert type(values[0].field_values) is str + assert isinstance(values[0].field_values, str) assert len(NodeFieldValueValidator.validate_json(values[0].field_values)) == 3 # should have batch id and priority From 6494e8e551578722788f2af5f88bcd9161b06e5d Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Sat, 11 Nov 2023 10:55:06 +1100 Subject: [PATCH 09/24] chore: ruff format --- installer/lib/messages.py | 4 +-- invokeai/app/invocations/baseinvocation.py | 4 +-- .../image_records/image_records_common.py | 31 ++++++++++--------- .../app/services/images/images_default.py | 17 +++++----- .../invocation_processor_base.py | 2 +- .../session_processor_default.py | 4 ++- invokeai/app/services/shared/graph.py | 11 ++++--- .../backend/install/model_install_backend.py | 14 ++++----- .../backend/ip_adapter/attention_processor.py | 4 ++- .../backend/model_management/model_cache.py | 2 ++ .../backend/model_management/model_search.py | 14 ++++----- .../stable_diffusion/diffusers_pipeline.py | 1 + .../diffusion/cross_attention_control.py | 2 +- .../diffusion/shared_invokeai_diffusion.py | 4 ++- invokeai/backend/util/hotfixes.py | 8 +++-- invokeai/backend/util/util.py | 6 +++- invokeai/frontend/install/model_install.py | 2 +- scripts/configure_invokeai.py | 4 ++- 18 files changed, 80 insertions(+), 54 deletions(-) diff --git a/installer/lib/messages.py b/installer/lib/messages.py index b1ffc256c1..6d95eaff59 100644 --- a/installer/lib/messages.py +++ b/installer/lib/messages.py @@ -137,7 +137,7 @@ def dest_path(dest=None) -> Path: path_completer = PathCompleter( only_directories=True, expanduser=True, - get_paths=lambda: [browse_start], # noqa: B023 + get_paths=lambda: [browse_start], # noqa: B023 # get_paths=lambda: [".."].extend(list(browse_start.iterdir())) ) @@ -149,7 +149,7 @@ def dest_path(dest=None) -> Path: completer=path_completer, default=str(browse_start) + os.sep, vi_mode=True, - complete_while_typing=True + complete_while_typing=True, # Test that this is not needed on Windows # complete_style=CompleteStyle.READLINE_LIKE, ) diff --git a/invokeai/app/invocations/baseinvocation.py b/invokeai/app/invocations/baseinvocation.py index a984d67dfa..1b3e535d34 100644 --- a/invokeai/app/invocations/baseinvocation.py +++ b/invokeai/app/invocations/baseinvocation.py @@ -661,9 +661,7 @@ def validate_fields(model_fields: dict[str, FieldInfo], model_type: str) -> None field_kind = ( # _field_kind is defined via InputField(), OutputField() or by one of the internal fields defined in this file - field.json_schema_extra.get("_field_kind", None) - if field.json_schema_extra - else None + field.json_schema_extra.get("_field_kind", None) if field.json_schema_extra else None ) # must have a field_kind diff --git a/invokeai/app/services/image_records/image_records_common.py b/invokeai/app/services/image_records/image_records_common.py index a2738fbeb9..61b97c6032 100644 --- a/invokeai/app/services/image_records/image_records_common.py +++ b/invokeai/app/services/image_records/image_records_common.py @@ -90,20 +90,23 @@ class ImageRecordDeleteException(Exception): IMAGE_DTO_COLS = ", ".join( - ["images." + c for c in [ - "image_name", - "image_origin", - "image_category", - "width", - "height", - "session_id", - "node_id", - "is_intermediate", - "created_at", - "updated_at", - "deleted_at", - "starred", - ]] + [ + "images." + c + for c in [ + "image_name", + "image_origin", + "image_category", + "width", + "height", + "session_id", + "node_id", + "is_intermediate", + "created_at", + "updated_at", + "deleted_at", + "starred", + ] + ] ) diff --git a/invokeai/app/services/images/images_default.py b/invokeai/app/services/images/images_default.py index 2cb157f852..63fa78d6c8 100644 --- a/invokeai/app/services/images/images_default.py +++ b/invokeai/app/services/images/images_default.py @@ -217,13 +217,16 @@ class ImageService(ImageServiceABC): board_id, ) - image_dtos = [image_record_to_dto( - image_record=r, - image_url=self.__invoker.services.urls.get_image_url(r.image_name), - thumbnail_url=self.__invoker.services.urls.get_image_url(r.image_name, True), - board_id=self.__invoker.services.board_image_records.get_board_for_image(r.image_name), - workflow_id=self.__invoker.services.workflow_image_records.get_workflow_for_image(r.image_name), - ) for r in results.items] + image_dtos = [ + image_record_to_dto( + image_record=r, + image_url=self.__invoker.services.urls.get_image_url(r.image_name), + thumbnail_url=self.__invoker.services.urls.get_image_url(r.image_name, True), + board_id=self.__invoker.services.board_image_records.get_board_for_image(r.image_name), + workflow_id=self.__invoker.services.workflow_image_records.get_workflow_for_image(r.image_name), + ) + for r in results.items + ] return OffsetPaginatedResults[ImageDTO]( items=image_dtos, diff --git a/invokeai/app/services/invocation_processor/invocation_processor_base.py b/invokeai/app/services/invocation_processor/invocation_processor_base.py index 700a15e643..7947a201dd 100644 --- a/invokeai/app/services/invocation_processor/invocation_processor_base.py +++ b/invokeai/app/services/invocation_processor/invocation_processor_base.py @@ -1,5 +1,5 @@ from abc import ABC -class InvocationProcessorABC(ABC): # noqa: B024 +class InvocationProcessorABC(ABC): # noqa: B024 pass diff --git a/invokeai/app/services/session_processor/session_processor_default.py b/invokeai/app/services/session_processor/session_processor_default.py index a8a6b7bf3c..28591fd7df 100644 --- a/invokeai/app/services/session_processor/session_processor_default.py +++ b/invokeai/app/services/session_processor/session_processor_default.py @@ -34,7 +34,9 @@ class DefaultSessionProcessor(SessionProcessorBase): name="session_processor", target=self.__process, kwargs={ - "stop_event": self.__stop_event, "poll_now_event": self.__poll_now_event, "resume_event": self.__resume_event + "stop_event": self.__stop_event, + "poll_now_event": self.__poll_now_event, + "resume_event": self.__resume_event, }, ) self.__thread.start() diff --git a/invokeai/app/services/shared/graph.py b/invokeai/app/services/shared/graph.py index 63fca9d89e..29af1e2333 100644 --- a/invokeai/app/services/shared/graph.py +++ b/invokeai/app/services/shared/graph.py @@ -728,9 +728,9 @@ class Graph(BaseModel): # Validate that all inputs are derived from or match a single type input_field_types = { t - for input_field in input_fields - for t in ([input_field] if get_origin(input_field) is None else get_args(input_field)) - if t != NoneType + for input_field in input_fields + for t in ([input_field] if get_origin(input_field) is None else get_args(input_field)) + if t != NoneType } # Get unique types type_tree = nx.DiGraph() type_tree.add_nodes_from(input_field_types) @@ -1053,7 +1053,10 @@ class GraphExecutionState(BaseModel): # For every iterator, the parent must either not be a child of that iterator, or must match the prepared iteration for that iterator # TODO: Handle a node mapping to none eg = self.execution_graph.nx_graph_flat() - prepared_parent_mappings = [[(n, self._get_iteration_node(n, g, eg, it)) for n in next_node_parents] for it in iterator_node_prepared_combinations] # type: ignore + prepared_parent_mappings = [ + [(n, self._get_iteration_node(n, g, eg, it)) for n in next_node_parents] + for it in iterator_node_prepared_combinations + ] # type: ignore # Create execution node for each iteration for iteration_mappings in prepared_parent_mappings: diff --git a/invokeai/backend/install/model_install_backend.py b/invokeai/backend/install/model_install_backend.py index 0962fe9740..afbcc848d8 100644 --- a/invokeai/backend/install/model_install_backend.py +++ b/invokeai/backend/install/model_install_backend.py @@ -253,13 +253,13 @@ class ModelInstall(object): # folders style or similar elif path.is_dir() and any( (path / x).exists() - for x in { - "config.json", - "model_index.json", - "learned_embeds.bin", - "pytorch_lora_weights.bin", - "pytorch_lora_weights.safetensors", - } + for x in { + "config.json", + "model_index.json", + "learned_embeds.bin", + "pytorch_lora_weights.bin", + "pytorch_lora_weights.safetensors", + } ): models_installed.update({str(model_path_id_or_url): self._install_path(path)}) diff --git a/invokeai/backend/ip_adapter/attention_processor.py b/invokeai/backend/ip_adapter/attention_processor.py index 1ae731290e..195cb12d1b 100644 --- a/invokeai/backend/ip_adapter/attention_processor.py +++ b/invokeai/backend/ip_adapter/attention_processor.py @@ -130,7 +130,9 @@ class IPAttnProcessor2_0(torch.nn.Module): assert ip_adapter_image_prompt_embeds is not None assert len(ip_adapter_image_prompt_embeds) == len(self._weights) - for ipa_embed, ipa_weights, scale in zip(ip_adapter_image_prompt_embeds, self._weights, self._scales, strict=True): + for ipa_embed, ipa_weights, scale in zip( + ip_adapter_image_prompt_embeds, self._weights, self._scales, strict=True + ): # The batch dimensions should match. assert ipa_embed.shape[0] == encoder_hidden_states.shape[0] # The token_len dimensions should match. diff --git a/invokeai/backend/model_management/model_cache.py b/invokeai/backend/model_management/model_cache.py index 2055691a06..2a7f4b5a95 100644 --- a/invokeai/backend/model_management/model_cache.py +++ b/invokeai/backend/model_management/model_cache.py @@ -66,11 +66,13 @@ class CacheStats(object): class ModelLocker(object): "Forward declaration" + pass class ModelCache(object): "Forward declaration" + pass diff --git a/invokeai/backend/model_management/model_search.py b/invokeai/backend/model_management/model_search.py index 3d56d9d8b9..e125c3ced7 100644 --- a/invokeai/backend/model_management/model_search.py +++ b/invokeai/backend/model_management/model_search.py @@ -70,13 +70,13 @@ class ModelSearch(ABC): continue if any( (path / x).exists() - for x in { - "config.json", - "model_index.json", - "learned_embeds.bin", - "pytorch_lora_weights.bin", - "image_encoder.txt", - } + for x in { + "config.json", + "model_index.json", + "learned_embeds.bin", + "pytorch_lora_weights.bin", + "image_encoder.txt", + } ): try: self.on_model_found(path) diff --git a/invokeai/backend/stable_diffusion/diffusers_pipeline.py b/invokeai/backend/stable_diffusion/diffusers_pipeline.py index 1b65326f6e..1353e804a7 100644 --- a/invokeai/backend/stable_diffusion/diffusers_pipeline.py +++ b/invokeai/backend/stable_diffusion/diffusers_pipeline.py @@ -193,6 +193,7 @@ class InvokeAIStableDiffusionPipelineOutput(StableDiffusionPipelineOutput): attention_map_saver (`AttentionMapSaver`): Object containing attention maps that can be displayed to the user after generation completes. Optional. """ + attention_map_saver: Optional[AttentionMapSaver] diff --git a/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py b/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py index b45bb8f338..92a538ff70 100644 --- a/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py +++ b/invokeai/backend/stable_diffusion/diffusion/cross_attention_control.py @@ -433,7 +433,7 @@ def inject_attention_function(unet, context: Context): module.identifier = identifier try: module.set_attention_slice_wrangler(attention_slice_wrangler) - module.set_slicing_strategy_getter(lambda module: context.get_slicing_strategy(identifier)) # noqa: B023 + module.set_slicing_strategy_getter(lambda module: context.get_slicing_strategy(identifier)) # noqa: B023 except AttributeError as e: if is_attribute_error_about(e, "set_attention_slice_wrangler"): print(f"TODO: implement set_attention_slice_wrangler for {type(module)}") # TODO diff --git a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py index 4123399cf4..455e5e1096 100644 --- a/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py +++ b/invokeai/backend/stable_diffusion/diffusion/shared_invokeai_diffusion.py @@ -642,7 +642,9 @@ class InvokeAIDiffuserComponent: deltas = None uncond_latents = None - weighted_cond_list = c_or_weighted_c_list if isinstance(c_or_weighted_c_list, list) else [(c_or_weighted_c_list, 1)] + weighted_cond_list = ( + c_or_weighted_c_list if isinstance(c_or_weighted_c_list, list) else [(c_or_weighted_c_list, 1)] + ) # below is fugly omg conditionings = [uc] + [c for c, weight in weighted_cond_list] diff --git a/invokeai/backend/util/hotfixes.py b/invokeai/backend/util/hotfixes.py index 32bc52c738..835575c7a1 100644 --- a/invokeai/backend/util/hotfixes.py +++ b/invokeai/backend/util/hotfixes.py @@ -732,7 +732,9 @@ class ControlNetModel(ModelMixin, ConfigMixin, FromOriginalControlnetMixin): controlnet_down_block_res_samples = () - for down_block_res_sample, controlnet_block in zip(down_block_res_samples, self.controlnet_down_blocks, strict=True): + for down_block_res_sample, controlnet_block in zip( + down_block_res_samples, self.controlnet_down_blocks, strict=True + ): down_block_res_sample = controlnet_block(down_block_res_sample) controlnet_down_block_res_samples = controlnet_down_block_res_samples + (down_block_res_sample,) @@ -745,7 +747,9 @@ class ControlNetModel(ModelMixin, ConfigMixin, FromOriginalControlnetMixin): scales = torch.logspace(-1, 0, len(down_block_res_samples) + 1, device=sample.device) # 0.1 to 1.0 scales = scales * conditioning_scale - down_block_res_samples = [sample * scale for sample, scale in zip(down_block_res_samples, scales, strict=True)] + down_block_res_samples = [ + sample * scale for sample, scale in zip(down_block_res_samples, scales, strict=True) + ] mid_block_res_sample = mid_block_res_sample * scales[-1] # last one else: down_block_res_samples = [sample * conditioning_scale for sample in down_block_res_samples] diff --git a/invokeai/backend/util/util.py b/invokeai/backend/util/util.py index 7043355219..12ba3701cf 100644 --- a/invokeai/backend/util/util.py +++ b/invokeai/backend/util/util.py @@ -229,7 +229,11 @@ def rand_perlin_2d(shape, res, device, fade=lambda t: 6 * t**5 - 15 * t**4 + 10 gradients = torch.stack((torch.cos(angles), torch.sin(angles)), dim=-1).to(device) def tile_grads(slice1, slice2): - return gradients[slice1[0]:slice1[1], slice2[0]:slice2[1]].repeat_interleave(d[0], 0).repeat_interleave(d[1], 1) + return ( + gradients[slice1[0] : slice1[1], slice2[0] : slice2[1]] + .repeat_interleave(d[0], 0) + .repeat_interleave(d[1], 1) + ) def dot(grad, shift): return ( diff --git a/invokeai/frontend/install/model_install.py b/invokeai/frontend/install/model_install.py index a47bd4c35c..e23538ffd6 100644 --- a/invokeai/frontend/install/model_install.py +++ b/invokeai/frontend/install/model_install.py @@ -72,7 +72,7 @@ class addModelsForm(CyclingForm, npyscreen.FormMultiPage): def __init__(self, parentApp, name, multipage=False, *args, **keywords): self.multipage = multipage self.subprocess = None - super().__init__(parentApp=parentApp, name=name, *args, **keywords) # noqa: B026 # TODO: maybe this is bad? + super().__init__(parentApp=parentApp, name=name, *args, **keywords) # noqa: B026 # TODO: maybe this is bad? def create(self): self.keypress_timeout = 10 diff --git a/scripts/configure_invokeai.py b/scripts/configure_invokeai.py index dff658c2b0..c994668ea6 100755 --- a/scripts/configure_invokeai.py +++ b/scripts/configure_invokeai.py @@ -6,5 +6,7 @@ import warnings from invokeai.frontend.install.invokeai_configure import invokeai_configure as configure if __name__ == "__main__": - warnings.warn("configure_invokeai.py is deprecated, running 'invokeai-configure'...", DeprecationWarning, stacklevel=2) + warnings.warn( + "configure_invokeai.py is deprecated, running 'invokeai-configure'...", DeprecationWarning, stacklevel=2 + ) configure() From 41bf9ec4a38c36e656bb6137142ec5acbce7f58d Mon Sep 17 00:00:00 2001 From: Wubbbi Date: Sat, 11 Nov 2023 09:46:23 +0100 Subject: [PATCH 10/24] Update Accelerate to 0.24.X --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fc9c150e3e..481677a226 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ classifiers = [ 'Topic :: Scientific/Engineering :: Image Processing', ] dependencies = [ - "accelerate~=0.23.0", + "accelerate~=0.24.0", "albumentations", "click", "clip_anytorch", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip", From 90a038c6853861a7df2cd44990d56b70a29c8a90 Mon Sep 17 00:00:00 2001 From: Riccardo Giovanetti Date: Sun, 12 Nov 2023 10:02:57 +0100 Subject: [PATCH 11/24] translationBot(ui): update translation (Italian) Currently translated at 97.7% (1200 of 1228 strings) Co-authored-by: Riccardo Giovanetti Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/ Translation: InvokeAI/Web UI --- invokeai/frontend/web/public/locales/it.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/invokeai/frontend/web/public/locales/it.json b/invokeai/frontend/web/public/locales/it.json index c358f415b2..53b0339ae4 100644 --- a/invokeai/frontend/web/public/locales/it.json +++ b/invokeai/frontend/web/public/locales/it.json @@ -1487,5 +1487,18 @@ "scheduler": "Campionatore", "recallParameters": "Richiama i parametri", "noRecallParameters": "Nessun parametro da richiamare trovato" + }, + "hrf": { + "enableHrf": "Abilita Correzione Alta Risoluzione", + "upscaleMethod": "Metodo di ampliamento", + "enableHrfTooltip": "Genera con una risoluzione iniziale inferiore, esegue l'ampliamento alla risoluzione di base, quindi esegue Immagine a Immagine.", + "metadata": { + "strength": "Forza della Correzione Alta Risoluzione", + "enabled": "Correzione Alta Risoluzione Abilitata", + "method": "Metodo della Correzione Alta Risoluzione" + }, + "hrf": "Correzione Alta Risoluzione", + "hrfStrength": "Forza della Correzione Alta Risoluzione", + "strengthTooltip": "Valori più bassi comportano meno dettagli, il che può ridurre potenziali artefatti." } } From 89a039460da33bd17441e9f1f74ca9f93aa60a74 Mon Sep 17 00:00:00 2001 From: Rohinish <92542124+rohinish404@users.noreply.github.com> Date: Mon, 13 Nov 2023 02:37:26 +0530 Subject: [PATCH 12/24] feat(ui): add number inputs for canvas brush color picker (#5067) * drop-down for the color picker * fixed the bug in alpha value * designing done --------- Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com> --- .../src/common/components/IAIColorPicker.tsx | 84 +++++++++++++++++-- .../IAICanvasToolChooserOptions.tsx | 1 + 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/invokeai/frontend/web/src/common/components/IAIColorPicker.tsx b/invokeai/frontend/web/src/common/components/IAIColorPicker.tsx index 5854f7503f..b61693c86a 100644 --- a/invokeai/frontend/web/src/common/components/IAIColorPicker.tsx +++ b/invokeai/frontend/web/src/common/components/IAIColorPicker.tsx @@ -1,9 +1,12 @@ -import { Box, ChakraProps } from '@chakra-ui/react'; -import { memo } from 'react'; +import { ChakraProps, Flex } from '@chakra-ui/react'; +import { memo, useCallback } from 'react'; import { RgbaColorPicker } from 'react-colorful'; import { ColorPickerBaseProps, RgbaColor } from 'react-colorful/dist/types'; +import IAINumberInput from './IAINumberInput'; -type IAIColorPickerProps = ColorPickerBaseProps; +type IAIColorPickerProps = ColorPickerBaseProps & { + withNumberInput?: boolean; +}; const colorPickerStyles: NonNullable = { width: 6, @@ -11,17 +14,84 @@ const colorPickerStyles: NonNullable = { borderColor: 'base.100', }; -const sx = { +const sx: ChakraProps['sx'] = { '.react-colorful__hue-pointer': colorPickerStyles, '.react-colorful__saturation-pointer': colorPickerStyles, '.react-colorful__alpha-pointer': colorPickerStyles, + gap: 2, + flexDir: 'column', }; +const numberInputWidth: ChakraProps['w'] = '4.2rem'; + const IAIColorPicker = (props: IAIColorPickerProps) => { + const { color, onChange, withNumberInput, ...rest } = props; + const handleChangeR = useCallback( + (r: number) => onChange({ ...color, r }), + [color, onChange] + ); + const handleChangeG = useCallback( + (g: number) => onChange({ ...color, g }), + [color, onChange] + ); + const handleChangeB = useCallback( + (b: number) => onChange({ ...color, b }), + [color, onChange] + ); + const handleChangeA = useCallback( + (a: number) => onChange({ ...color, a }), + [color, onChange] + ); return ( - - - + + + {withNumberInput && ( + + + + + + + )} + ); }; diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx index b5770fdda6..0030f1f06e 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx @@ -245,6 +245,7 @@ const IAICanvasToolChooserOptions = () => { }} > dispatch(setBrushColor(newColor))} /> From 71e298b722b34efbc379df9bced1bf481ddebec6 Mon Sep 17 00:00:00 2001 From: Stefan Tobler Date: Sun, 12 Nov 2023 16:19:12 -0500 Subject: [PATCH 13/24] Feat (ui): Add VAE Model to Recall Parameters (#5073) * adding VAE recall when using all parameters * adding VAE to the RecallParameters tab in ImageMetadataActions * checking for nil vae and casting to null if undefined * adding default VAE to recall actions list if VAE is nullish * fix(ui): use `lodash-es` for tree-shakeable imports --------- Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com> --- invokeai/frontend/web/public/locales/en.json | 1 + .../ImageMetadataActions.tsx | 10 ++++++ .../parameters/hooks/useRecallParameters.ts | 33 ++++++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index e06804772b..94e05f791c 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -583,6 +583,7 @@ "strength": "Image to image strength", "Threshold": "Noise Threshold", "variations": "Seed-weight pairs", + "vae": "VAE", "width": "Width", "workflow": "Workflow" }, diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx index 4e4531d268..3ca554cb84 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -31,6 +31,7 @@ const ImageMetadataActions = (props: Props) => { recallCfgScale, recallModel, recallScheduler, + recallVaeModel, recallSteps, recallWidth, recallHeight, @@ -72,6 +73,10 @@ const ImageMetadataActions = (props: Props) => { recallScheduler(metadata?.scheduler); }, [metadata?.scheduler, recallScheduler]); + const handleRecallVaeModel = useCallback(() => { + recallVaeModel(metadata?.vae); + }, [metadata?.vae, recallVaeModel]); + const handleRecallSteps = useCallback(() => { recallSteps(metadata?.steps); }, [metadata?.steps, recallSteps]); @@ -219,6 +224,11 @@ const ImageMetadataActions = (props: Props) => { onClick={handleRecallScheduler} /> )} + {metadata.steps && ( { [dispatch, parameterSetToast, parameterNotSetToast] ); + /** + * Recall vae model + */ + const recallVaeModel = useCallback( + (vae: unknown) => { + if (!isValidVaeModel(vae) && !isNil(vae)) { + parameterNotSetToast(); + return; + } + if (isNil(vae)) { + dispatch(vaeSelected(null)); + } else { + dispatch(vaeSelected(vae)); + } + parameterSetToast(); + }, + [dispatch, parameterSetToast, parameterNotSetToast] + ); + /** * Recall steps with toast */ @@ -757,6 +779,7 @@ export const useRecallParameters = () => { positive_prompt, negative_prompt, scheduler, + vae, seed, steps, width, @@ -798,6 +821,13 @@ export const useRecallParameters = () => { if (isValidScheduler(scheduler)) { dispatch(setScheduler(scheduler)); } + if (isValidVaeModel(vae) || isNil(vae)) { + if (isNil(vae)) { + dispatch(vaeSelected(null)); + } else { + dispatch(vaeSelected(vae)); + } + } if (isValidSeed(seed)) { dispatch(setSeed(seed)); @@ -932,6 +962,7 @@ export const useRecallParameters = () => { recallCfgScale, recallModel, recallScheduler, + recallVaeModel, recallSteps, recallWidth, recallHeight, From 5eaea9dd64ff24dd817f1257e508da7a7aa27663 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 13 Nov 2023 08:43:27 +1100 Subject: [PATCH 14/24] chore(ui): delete unused files --- .../IAIErrorLoadingImageFallback.tsx | 43 ----- .../web/src/common/components/IAIForm.tsx | 8 - .../IAIForms/IAIFormErrorMessage.tsx | 15 -- .../components/IAIForms/IAIFormHelperText.tsx | 15 -- .../IAIForms/IAIFormItemWrapper.tsx | 25 --- .../src/common/components/IAIFullCheckbox.tsx | 25 --- .../components/SelectImagePlaceholder.tsx | 23 --- .../web/src/common/hooks/useResolution.ts | 24 --- .../web/src/common/util/getTimestamp.ts | 7 - .../web/src/common/util/seedWeightPairs.ts | 71 -------- .../canvas/util/konvaNodeToDataURL.ts | 16 -- .../ControlAdapterPreprocessButton.tsx | 36 ---- .../dynamicPrompts/store/selectors.ts | 1 - .../embedding/store/embeddingSlice.ts | 0 .../Boards/BoardsList/GenericBoard.tsx | 108 ----------- .../Boards/BoardsList/SystemBoardButton.tsx | 53 ------ .../CurrentImage/CurrentImageHidden.tsx | 22 --- .../components/ImageFallbackSpinner.tsx | 27 --- .../fields/inputs/ClipInputField.tsx | 14 -- .../fields/inputs/CollectionInputField.tsx | 17 -- .../inputs/CollectionItemInputField.tsx | 17 -- .../fields/inputs/ConditioningInputField.tsx | 17 -- .../fields/inputs/ControlInputField.tsx | 19 -- .../fields/inputs/DenoiseMaskInputField.tsx | 17 -- .../fields/inputs/IPAdapterInputField.tsx | 19 -- .../inputs/ImageCollectionInputField.tsx | 94 ---------- .../fields/inputs/LatentsInputField.tsx | 19 -- .../fields/inputs/T2IAdapterInputField.tsx | 19 -- .../fields/inputs/UnetInputField.tsx | 14 -- .../fields/inputs/VaeInputField.tsx | 14 -- .../flow/nodes/common/NodeResizer.tsx | 27 --- .../inspector/InspectorDetailsTab.tsx | 78 -------- .../sidePanel/workflow/WorkflowNotesTab.tsx | 51 ------ .../nodes/store/util/makeTemplateSelector.ts | 11 -- .../BoundingBox/ParamBoundingBoxCollapse.tsx | 21 --- .../Parameters/Noise/ParamNoiseThreshold.tsx | 39 ---- .../Parameters/Noise/ParamPerlinNoise.tsx | 39 ---- .../Variations/ParamVariationAmount.tsx | 35 ---- .../Variations/ParamVariationCollapse.tsx | 51 ------ .../Variations/ParamVariationToggle.tsx | 24 --- .../Variations/ParamVariationWeights.tsx | 41 ----- .../store/postprocessingSelectors.ts | 4 - .../queue/components/CurrentQueueItemCard.tsx | 18 -- .../queue/components/NextQueueItemCard.tsx | 18 -- .../QueueList/QueueItemSkeleton.tsx | 41 ----- .../queue/components/QueueStatusCard.tsx | 54 ------ .../queue/components/common/QueueItemCard.tsx | 52 ------ .../components/common/QueueStatusDot.tsx | 28 --- .../features/queue/util/formatNumberShort.ts | 4 - .../SettingsModal/ResetWebUIButton.tsx | 82 --------- .../web/src/features/system/store/actions.ts | 3 - .../src/features/system/store/sessionSlice.ts | 62 ------- .../components/common/OverlayScrollable.tsx | 22 --- .../store/modelmanagerSelectors.tsx | 3 - .../UnifiedCanvasBaseBrushSettings.tsx | 12 -- .../UnifiedCanvasBrushSettings.tsx | 12 -- .../UnifiedCanvasBrushSize.tsx | 54 ------ .../UnifiedCanvasClearMask.tsx | 25 --- .../UnifiedCanvasColorPicker.tsx | 128 ------------- .../UnifiedCanvasDarkenOutsideSelection.tsx | 25 --- .../UnifiedCanvasEnableMask.tsx | 25 --- .../UnifiedCanvasLimitStrokesToBox.tsx | 25 --- .../UnifiedCanvasMaskBrushSettings.tsx | 16 -- .../UnifiedCanvasMoveSettings.tsx | 14 -- .../UnifiedCanvasPreserveMask.tsx | 22 --- .../UnifiedCanvasSettings.tsx | 113 ------------ .../UnifiedCanvasShowGrid.tsx | 22 --- .../UnifiedCanvasSnapToGrid.tsx | 26 --- .../UnifiedCanvasToolSettingsBeta.tsx | 41 ----- .../UnifiedCanvasCopyToClipboard.tsx | 50 ----- .../UnifiedCanvasDownloadImage.tsx | 43 ----- .../UnifiedCanvasFileUploader.tsx | 28 --- .../UnifiedCanvasLayerSelect.tsx | 71 -------- .../UnifiedCanvasMergeVisible.tsx | 37 ---- .../UnifiedCanvasMoveTool.tsx | 39 ---- .../UnifiedCanvasResetCanvas.tsx | 26 --- .../UnifiedCanvasResetView.tsx | 55 ------ .../UnifiedCanvasSaveToGallery.tsx | 39 ---- .../UnifiedCanvasToolSelect.tsx | 172 ------------------ .../UnifiedCanvasToolbarBeta.tsx | 53 ------ .../web/src/theme/components/table.ts | 0 81 files changed, 2780 deletions(-) delete mode 100644 invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx delete mode 100644 invokeai/frontend/web/src/common/components/IAIForm.tsx delete mode 100644 invokeai/frontend/web/src/common/components/IAIForms/IAIFormErrorMessage.tsx delete mode 100644 invokeai/frontend/web/src/common/components/IAIForms/IAIFormHelperText.tsx delete mode 100644 invokeai/frontend/web/src/common/components/IAIForms/IAIFormItemWrapper.tsx delete mode 100644 invokeai/frontend/web/src/common/components/IAIFullCheckbox.tsx delete mode 100644 invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx delete mode 100644 invokeai/frontend/web/src/common/hooks/useResolution.ts delete mode 100644 invokeai/frontend/web/src/common/util/getTimestamp.ts delete mode 100644 invokeai/frontend/web/src/common/util/seedWeightPairs.ts delete mode 100644 invokeai/frontend/web/src/features/canvas/util/konvaNodeToDataURL.ts delete mode 100644 invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterPreprocessButton.tsx delete mode 100644 invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts delete mode 100644 invokeai/frontend/web/src/features/embedding/store/embeddingSlice.ts delete mode 100644 invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx delete mode 100644 invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/SystemBoardButton.tsx delete mode 100644 invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageHidden.tsx delete mode 100644 invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ClipInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionItemInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ConditioningInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ControlInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/DenoiseMaskInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IPAdapterInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageCollectionInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LatentsInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/T2IAdapterInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/UnetInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VaeInputField.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeResizer.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowNotesTab.tsx delete mode 100644 invokeai/frontend/web/src/features/nodes/store/util/makeTemplateSelector.ts delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxCollapse.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamNoiseThreshold.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamPerlinNoise.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationAmount.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationCollapse.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationToggle.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationWeights.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/store/postprocessingSelectors.ts delete mode 100644 invokeai/frontend/web/src/features/queue/components/CurrentQueueItemCard.tsx delete mode 100644 invokeai/frontend/web/src/features/queue/components/NextQueueItemCard.tsx delete mode 100644 invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemSkeleton.tsx delete mode 100644 invokeai/frontend/web/src/features/queue/components/QueueStatusCard.tsx delete mode 100644 invokeai/frontend/web/src/features/queue/components/common/QueueItemCard.tsx delete mode 100644 invokeai/frontend/web/src/features/queue/components/common/QueueStatusDot.tsx delete mode 100644 invokeai/frontend/web/src/features/queue/util/formatNumberShort.ts delete mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/ResetWebUIButton.tsx delete mode 100644 invokeai/frontend/web/src/features/system/store/actions.ts delete mode 100644 invokeai/frontend/web/src/features/system/store/sessionSlice.ts delete mode 100644 invokeai/frontend/web/src/features/ui/components/common/OverlayScrollable.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/store/modelmanagerSelectors.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSettings.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSize.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasClearMask.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasColorPicker.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasDarkenOutsideSelection.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasEnableMask.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasLimitStrokesToBox.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasPreserveMask.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSettings.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasShowGrid.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSnapToGrid.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettingsBeta.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasDownloadImage.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasFileUploader.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasLayerSelect.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMergeVisible.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMoveTool.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetCanvas.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetView.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasToolSelect.tsx delete mode 100644 invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbarBeta.tsx delete mode 100644 invokeai/frontend/web/src/theme/components/table.ts diff --git a/invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx b/invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx deleted file mode 100644 index 0a5d4fb12f..0000000000 --- a/invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Box, Flex, Icon } from '@chakra-ui/react'; -import { memo } from 'react'; -import { FaExclamation } from 'react-icons/fa'; - -const IAIErrorLoadingImageFallback = () => { - return ( - - - - - - ); -}; - -export default memo(IAIErrorLoadingImageFallback); diff --git a/invokeai/frontend/web/src/common/components/IAIForm.tsx b/invokeai/frontend/web/src/common/components/IAIForm.tsx deleted file mode 100644 index c94d177a21..0000000000 --- a/invokeai/frontend/web/src/common/components/IAIForm.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { chakra } from '@chakra-ui/react'; - -/** - * Chakra-enabled
- */ -const IAIForm = chakra.form; - -export default IAIForm; diff --git a/invokeai/frontend/web/src/common/components/IAIForms/IAIFormErrorMessage.tsx b/invokeai/frontend/web/src/common/components/IAIForms/IAIFormErrorMessage.tsx deleted file mode 100644 index 22aa10cb06..0000000000 --- a/invokeai/frontend/web/src/common/components/IAIForms/IAIFormErrorMessage.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { FormErrorMessage, FormErrorMessageProps } from '@chakra-ui/react'; -import { ReactNode } from 'react'; - -type IAIFormErrorMessageProps = FormErrorMessageProps & { - children: ReactNode | string; -}; - -export default function IAIFormErrorMessage(props: IAIFormErrorMessageProps) { - const { children, ...rest } = props; - return ( - - {children} - - ); -} diff --git a/invokeai/frontend/web/src/common/components/IAIForms/IAIFormHelperText.tsx b/invokeai/frontend/web/src/common/components/IAIForms/IAIFormHelperText.tsx deleted file mode 100644 index 4d48ac991c..0000000000 --- a/invokeai/frontend/web/src/common/components/IAIForms/IAIFormHelperText.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { FormHelperText, FormHelperTextProps } from '@chakra-ui/react'; -import { ReactNode } from 'react'; - -type IAIFormHelperTextProps = FormHelperTextProps & { - children: ReactNode | string; -}; - -export default function IAIFormHelperText(props: IAIFormHelperTextProps) { - const { children, ...rest } = props; - return ( - - {children} - - ); -} diff --git a/invokeai/frontend/web/src/common/components/IAIForms/IAIFormItemWrapper.tsx b/invokeai/frontend/web/src/common/components/IAIForms/IAIFormItemWrapper.tsx deleted file mode 100644 index 83e91366c2..0000000000 --- a/invokeai/frontend/web/src/common/components/IAIForms/IAIFormItemWrapper.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Flex, useColorMode } from '@chakra-ui/react'; -import { ReactElement } from 'react'; -import { mode } from 'theme/util/mode'; - -export function IAIFormItemWrapper({ - children, -}: { - children: ReactElement | ReactElement[]; -}) { - const { colorMode } = useColorMode(); - return ( - - {children} - - ); -} diff --git a/invokeai/frontend/web/src/common/components/IAIFullCheckbox.tsx b/invokeai/frontend/web/src/common/components/IAIFullCheckbox.tsx deleted file mode 100644 index 97ff24689c..0000000000 --- a/invokeai/frontend/web/src/common/components/IAIFullCheckbox.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { - Checkbox, - CheckboxProps, - FormControl, - FormControlProps, - FormLabel, -} from '@chakra-ui/react'; -import { memo, ReactNode } from 'react'; - -type IAIFullCheckboxProps = CheckboxProps & { - label: string | ReactNode; - formControlProps?: FormControlProps; -}; - -const IAIFullCheckbox = (props: IAIFullCheckboxProps) => { - const { label, formControlProps, ...rest } = props; - return ( - - {label} - - - ); -}; - -export default memo(IAIFullCheckbox); diff --git a/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx b/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx deleted file mode 100644 index 2db202ddc0..0000000000 --- a/invokeai/frontend/web/src/common/components/SelectImagePlaceholder.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Flex, Icon } from '@chakra-ui/react'; -import { memo } from 'react'; -import { FaImage } from 'react-icons/fa'; - -const SelectImagePlaceholder = () => { - return ( - - - - ); -}; - -export default memo(SelectImagePlaceholder); diff --git a/invokeai/frontend/web/src/common/hooks/useResolution.ts b/invokeai/frontend/web/src/common/hooks/useResolution.ts deleted file mode 100644 index fb52555be8..0000000000 --- a/invokeai/frontend/web/src/common/hooks/useResolution.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useBreakpoint } from '@chakra-ui/react'; - -export default function useResolution(): - | 'mobile' - | 'tablet' - | 'desktop' - | 'unknown' { - const breakpointValue = useBreakpoint(); - - const mobileResolutions = ['base', 'sm']; - const tabletResolutions = ['md', 'lg']; - const desktopResolutions = ['xl', '2xl']; - - if (mobileResolutions.includes(breakpointValue)) { - return 'mobile'; - } - if (tabletResolutions.includes(breakpointValue)) { - return 'tablet'; - } - if (desktopResolutions.includes(breakpointValue)) { - return 'desktop'; - } - return 'unknown'; -} diff --git a/invokeai/frontend/web/src/common/util/getTimestamp.ts b/invokeai/frontend/web/src/common/util/getTimestamp.ts deleted file mode 100644 index daa9f8dc33..0000000000 --- a/invokeai/frontend/web/src/common/util/getTimestamp.ts +++ /dev/null @@ -1,7 +0,0 @@ -import dateFormat from 'dateformat'; - -/** - * Get a `now` timestamp with 1s precision, formatted as ISO datetime. - */ -export const getTimestamp = () => - dateFormat(new Date(), `yyyy-mm-dd'T'HH:MM:ss:lo`); diff --git a/invokeai/frontend/web/src/common/util/seedWeightPairs.ts b/invokeai/frontend/web/src/common/util/seedWeightPairs.ts deleted file mode 100644 index 564f66c1e3..0000000000 --- a/invokeai/frontend/web/src/common/util/seedWeightPairs.ts +++ /dev/null @@ -1,71 +0,0 @@ -// TODO: Restore variations -// Support code from v2.3 in here. - -// export const stringToSeedWeights = ( -// string: string -// ): InvokeAI.SeedWeights | boolean => { -// const stringPairs = string.split(','); -// const arrPairs = stringPairs.map((p) => p.split(':')); -// const pairs = arrPairs.map((p: Array): InvokeAI.SeedWeightPair => { -// return { seed: Number(p[0]), weight: Number(p[1]) }; -// }); - -// if (!validateSeedWeights(pairs)) { -// return false; -// } - -// return pairs; -// }; - -// export const validateSeedWeights = ( -// seedWeights: InvokeAI.SeedWeights | string -// ): boolean => { -// return typeof seedWeights === 'string' -// ? Boolean(stringToSeedWeights(seedWeights)) -// : Boolean( -// seedWeights.length && -// !seedWeights.some((pair: InvokeAI.SeedWeightPair) => { -// const { seed, weight } = pair; -// const isSeedValid = !isNaN(parseInt(seed.toString(), 10)); -// const isWeightValid = -// !isNaN(parseInt(weight.toString(), 10)) && -// weight >= 0 && -// weight <= 1; -// return !(isSeedValid && isWeightValid); -// }) -// ); -// }; - -// export const seedWeightsToString = ( -// seedWeights: InvokeAI.SeedWeights -// ): string => { -// return seedWeights.reduce((acc, pair, i, arr) => { -// const { seed, weight } = pair; -// acc += `${seed}:${weight}`; -// if (i !== arr.length - 1) { -// acc += ','; -// } -// return acc; -// }, ''); -// }; - -// export const seedWeightsToArray = ( -// seedWeights: InvokeAI.SeedWeights -// ): Array> => { -// return seedWeights.map((pair: InvokeAI.SeedWeightPair) => [ -// pair.seed, -// pair.weight, -// ]); -// }; - -// export const stringToSeedWeightsArray = ( -// string: string -// ): Array> => { -// const stringPairs = string.split(','); -// const arrPairs = stringPairs.map((p) => p.split(':')); -// return arrPairs.map( -// (p: Array): Array => [parseInt(p[0], 10), parseFloat(p[1])] -// ); -// }; - -export default {}; diff --git a/invokeai/frontend/web/src/features/canvas/util/konvaNodeToDataURL.ts b/invokeai/frontend/web/src/features/canvas/util/konvaNodeToDataURL.ts deleted file mode 100644 index 5d0aaf5443..0000000000 --- a/invokeai/frontend/web/src/features/canvas/util/konvaNodeToDataURL.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Konva from 'konva'; -import { IRect } from 'konva/lib/types'; - -/** - * Converts a Konva node to a dataURL - * @param node - The Konva node to convert to a dataURL - * @param boundingBox - The bounding box to crop to - * @returns A dataURL of the node cropped to the bounding box - */ -export const konvaNodeToDataURL = ( - node: Konva.Node, - boundingBox: IRect -): string => { - // get a dataURL of the bbox'd region - return node.toDataURL(boundingBox); -}; diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterPreprocessButton.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterPreprocessButton.tsx deleted file mode 100644 index 44a34dcd46..0000000000 --- a/invokeai/frontend/web/src/features/controlAdapters/components/ControlAdapterPreprocessButton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useAppDispatch } from 'app/store/storeHooks'; -import IAIButton from 'common/components/IAIButton'; -import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; -import { memo, useCallback } from 'react'; -import { useControlAdapterControlImage } from '../hooks/useControlAdapterControlImage'; -import { controlAdapterImageProcessed } from '../store/actions'; - -type Props = { - id: string; -}; - -const ControlAdapterPreprocessButton = ({ id }: Props) => { - const controlImage = useControlAdapterControlImage(id); - const dispatch = useAppDispatch(); - const isReady = useIsReadyToEnqueue(); - - const handleProcess = useCallback(() => { - dispatch( - controlAdapterImageProcessed({ - id, - }) - ); - }, [id, dispatch]); - - return ( - - Preprocess - - ); -}; - -export default memo(ControlAdapterPreprocessButton); diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts b/invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts deleted file mode 100644 index 8337712ea5..0000000000 --- a/invokeai/frontend/web/src/features/dynamicPrompts/store/selectors.ts +++ /dev/null @@ -1 +0,0 @@ -// diff --git a/invokeai/frontend/web/src/features/embedding/store/embeddingSlice.ts b/invokeai/frontend/web/src/features/embedding/store/embeddingSlice.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx deleted file mode 100644 index 7a95e7fcd9..0000000000 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { As, Badge, Flex } from '@chakra-ui/react'; -import IAIDroppable from 'common/components/IAIDroppable'; -import { IAINoContentFallback } from 'common/components/IAIImageFallback'; -import { TypesafeDroppableData } from 'features/dnd/types'; -import { BoardId } from 'features/gallery/store/types'; -import { ReactNode, memo } from 'react'; -import BoardContextMenu from '../BoardContextMenu'; - -type GenericBoardProps = { - board_id: BoardId; - droppableData?: TypesafeDroppableData; - onClick: () => void; - isSelected: boolean; - icon: As; - label: string; - dropLabel?: ReactNode; - badgeCount?: number; -}; - -export const formatBadgeCount = (count: number) => - Intl.NumberFormat('en-US', { - notation: 'compact', - maximumFractionDigits: 1, - }).format(count); - -const GenericBoard = (props: GenericBoardProps) => { - const { - board_id, - droppableData, - onClick, - isSelected, - icon, - label, - badgeCount, - dropLabel, - } = props; - - return ( - - {(ref) => ( - - - - - {badgeCount !== undefined && ( - {formatBadgeCount(badgeCount)} - )} - - - - - {label} - - - )} - - ); -}; - -export default memo(GenericBoard); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/SystemBoardButton.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/SystemBoardButton.tsx deleted file mode 100644 index 462aa4b5e6..0000000000 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/SystemBoardButton.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import IAIButton from 'common/components/IAIButton'; -import { boardIdSelected } from 'features/gallery/store/gallerySlice'; -import { memo, useCallback, useMemo } from 'react'; -import { useBoardName } from 'services/api/hooks/useBoardName'; - -type Props = { - board_id: 'images' | 'assets' | 'no_board'; -}; - -const SystemBoardButton = ({ board_id }: Props) => { - const dispatch = useAppDispatch(); - - const selector = useMemo( - () => - createSelector( - [stateSelector], - ({ gallery }) => { - const { selectedBoardId } = gallery; - return { isSelected: selectedBoardId === board_id }; - }, - defaultSelectorOptions - ), - [board_id] - ); - - const { isSelected } = useAppSelector(selector); - - const boardName = useBoardName(board_id); - - const handleClick = useCallback(() => { - dispatch(boardIdSelected({ boardId: board_id })); - }, [board_id, dispatch]); - - return ( - - {boardName} - - ); -}; - -export default memo(SystemBoardButton); diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageHidden.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageHidden.tsx deleted file mode 100644 index af2a7c5f98..0000000000 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImage/CurrentImageHidden.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Flex } from '@chakra-ui/react'; -import { memo } from 'react'; -import { FaEyeSlash } from 'react-icons/fa'; - -const CurrentImageHidden = () => { - return ( - - - - ); -}; - -export default memo(CurrentImageHidden); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx deleted file mode 100644 index 95577efc13..0000000000 --- a/invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Flex, Spinner, SpinnerProps } from '@chakra-ui/react'; -import { memo } from 'react'; - -type ImageFallbackSpinnerProps = SpinnerProps; - -const ImageFallbackSpinner = (props: ImageFallbackSpinnerProps) => { - const { size = 'xl', ...rest } = props; - - return ( - - - - ); -}; - -export default memo(ImageFallbackSpinner); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ClipInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ClipInputField.tsx deleted file mode 100644 index cf5d7fae95..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ClipInputField.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { - ClipInputFieldTemplate, - ClipInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const ClipInputFieldComponent = ( - _props: FieldComponentProps -) => { - return null; -}; - -export default memo(ClipInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionInputField.tsx deleted file mode 100644 index 7cbc46f28c..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionInputField.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { - CollectionInputFieldTemplate, - CollectionInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const CollectionInputFieldComponent = ( - _props: FieldComponentProps< - CollectionInputFieldValue, - CollectionInputFieldTemplate - > -) => { - return null; -}; - -export default memo(CollectionInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionItemInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionItemInputField.tsx deleted file mode 100644 index e67a20bdfb..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/CollectionItemInputField.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { - CollectionItemInputFieldTemplate, - CollectionItemInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const CollectionItemInputFieldComponent = ( - _props: FieldComponentProps< - CollectionItemInputFieldValue, - CollectionItemInputFieldTemplate - > -) => { - return null; -}; - -export default memo(CollectionItemInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ConditioningInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ConditioningInputField.tsx deleted file mode 100644 index 9d174f40c5..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ConditioningInputField.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { - ConditioningInputFieldTemplate, - ConditioningInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const ConditioningInputFieldComponent = ( - _props: FieldComponentProps< - ConditioningInputFieldValue, - ConditioningInputFieldTemplate - > -) => { - return null; -}; - -export default memo(ConditioningInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ControlInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ControlInputField.tsx deleted file mode 100644 index 2aaac52615..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ControlInputField.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - ControlInputFieldTemplate, - ControlInputFieldValue, - ControlPolymorphicInputFieldTemplate, - ControlPolymorphicInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const ControlInputFieldComponent = ( - _props: FieldComponentProps< - ControlInputFieldValue | ControlPolymorphicInputFieldValue, - ControlInputFieldTemplate | ControlPolymorphicInputFieldTemplate - > -) => { - return null; -}; - -export default memo(ControlInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/DenoiseMaskInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/DenoiseMaskInputField.tsx deleted file mode 100644 index 79b2668887..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/DenoiseMaskInputField.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { - DenoiseMaskInputFieldTemplate, - DenoiseMaskInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const DenoiseMaskInputFieldComponent = ( - _props: FieldComponentProps< - DenoiseMaskInputFieldValue, - DenoiseMaskInputFieldTemplate - > -) => { - return null; -}; - -export default memo(DenoiseMaskInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IPAdapterInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IPAdapterInputField.tsx deleted file mode 100644 index efeffe5723..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/IPAdapterInputField.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - IPAdapterInputFieldTemplate, - IPAdapterInputFieldValue, - FieldComponentProps, - IPAdapterPolymorphicInputFieldValue, - IPAdapterPolymorphicInputFieldTemplate, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const IPAdapterInputFieldComponent = ( - _props: FieldComponentProps< - IPAdapterInputFieldValue | IPAdapterPolymorphicInputFieldValue, - IPAdapterInputFieldTemplate | IPAdapterPolymorphicInputFieldTemplate - > -) => { - return null; -}; - -export default memo(IPAdapterInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageCollectionInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageCollectionInputField.tsx deleted file mode 100644 index 4a19f08614..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ImageCollectionInputField.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { - ImageCollectionInputFieldTemplate, - ImageCollectionInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -import { Flex } from '@chakra-ui/react'; -import IAIDndImage from 'common/components/IAIDndImage'; -import IAIDropOverlay from 'common/components/IAIDropOverlay'; -import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks'; -import { NodesMultiImageDropData } from 'features/dnd/types'; -import { isValidDrop } from 'features/dnd/util/isValidDrop'; -import { useGetImageDTOQuery } from 'services/api/endpoints/images'; - -const ImageCollectionInputFieldComponent = ( - props: FieldComponentProps< - ImageCollectionInputFieldValue, - ImageCollectionInputFieldTemplate - > -) => { - const { nodeId, field } = props; - - // const dispatch = useAppDispatch(); - - // const handleDrop = useCallback( - // ({ image_name }: ImageDTO) => { - // dispatch( - // fieldValueChanged({ - // nodeId, - // fieldName: field.name, - // value: uniqBy([...(field.value ?? []), { image_name }], 'image_name'), - // }) - // ); - // }, - // [dispatch, field.name, field.value, nodeId] - // ); - - const droppableData: NodesMultiImageDropData = { - id: `node-${nodeId}-${field.name}`, - actionType: 'SET_MULTI_NODES_IMAGE', - context: { nodeId: nodeId, fieldName: field.name }, - }; - - const { - isOver, - setNodeRef: setDroppableRef, - active, - } = useDroppableTypesafe({ - id: `node_${nodeId}`, - data: droppableData, - }); - - // const handleReset = useCallback(() => { - // dispatch( - // fieldValueChanged({ - // nodeId, - // fieldName: field.name, - // value: undefined, - // }) - // ); - // }, [dispatch, field.name, nodeId]); - - return ( - - {field.value?.map(({ image_name }) => ( - - ))} - {isValidDrop(droppableData, active) && } - - ); -}; - -export default memo(ImageCollectionInputFieldComponent); - -type ImageSubFieldProps = { imageName: string }; - -const ImageSubField = (props: ImageSubFieldProps) => { - const { currentData: image } = useGetImageDTOQuery(props.imageName); - - return ( - - ); -}; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LatentsInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LatentsInputField.tsx deleted file mode 100644 index a5065be0ee..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LatentsInputField.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - LatentsInputFieldTemplate, - LatentsInputFieldValue, - FieldComponentProps, - LatentsPolymorphicInputFieldValue, - LatentsPolymorphicInputFieldTemplate, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const LatentsInputFieldComponent = ( - _props: FieldComponentProps< - LatentsInputFieldValue | LatentsPolymorphicInputFieldValue, - LatentsInputFieldTemplate | LatentsPolymorphicInputFieldTemplate - > -) => { - return null; -}; - -export default memo(LatentsInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/T2IAdapterInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/T2IAdapterInputField.tsx deleted file mode 100644 index 210adb6eff..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/T2IAdapterInputField.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { - T2IAdapterInputFieldTemplate, - T2IAdapterInputFieldValue, - T2IAdapterPolymorphicInputFieldTemplate, - T2IAdapterPolymorphicInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const T2IAdapterInputFieldComponent = ( - _props: FieldComponentProps< - T2IAdapterInputFieldValue | T2IAdapterPolymorphicInputFieldValue, - T2IAdapterInputFieldTemplate | T2IAdapterPolymorphicInputFieldTemplate - > -) => { - return null; -}; - -export default memo(T2IAdapterInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/UnetInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/UnetInputField.tsx deleted file mode 100644 index 2beefc7034..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/UnetInputField.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { - UNetInputFieldTemplate, - UNetInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const UNetInputFieldComponent = ( - _props: FieldComponentProps -) => { - return null; -}; - -export default memo(UNetInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VaeInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VaeInputField.tsx deleted file mode 100644 index 738267faab..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/VaeInputField.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { - VaeInputFieldTemplate, - VaeInputFieldValue, - FieldComponentProps, -} from 'features/nodes/types/types'; -import { memo } from 'react'; - -const VaeInputFieldComponent = ( - _props: FieldComponentProps -) => { - return null; -}; - -export default memo(VaeInputFieldComponent); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeResizer.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeResizer.tsx deleted file mode 100644 index 6391e86471..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/common/NodeResizer.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { NODE_MIN_WIDTH } from 'features/nodes/types/constants'; -import { memo } from 'react'; -import { NodeResizeControl, NodeResizerProps } from 'reactflow'; - -// this causes https://github.com/invoke-ai/InvokeAI/issues/4140 -// not using it for now - -const NodeResizer = (props: NodeResizerProps) => { - const { ...rest } = props; - return ( - - ); -}; - -export default memo(NodeResizer); diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx deleted file mode 100644 index ffc260b95a..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { Box, Flex } from '@chakra-ui/react'; -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppSelector } from 'app/store/storeHooks'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import { IAINoContentFallback } from 'common/components/IAIImageFallback'; -import { InvocationTemplate, NodeData } from 'features/nodes/types/types'; -import { memo } from 'react'; -import NotesTextarea from '../../flow/nodes/Invocation/NotesTextarea'; -import NodeTitle from '../../flow/nodes/common/NodeTitle'; -import ScrollableContent from '../ScrollableContent'; -import { useTranslation } from 'react-i18next'; - -const selector = createSelector( - stateSelector, - ({ nodes }) => { - const lastSelectedNodeId = - nodes.selectedNodes[nodes.selectedNodes.length - 1]; - - const lastSelectedNode = nodes.nodes.find( - (node) => node.id === lastSelectedNodeId - ); - - const lastSelectedNodeTemplate = lastSelectedNode - ? nodes.nodeTemplates[lastSelectedNode.data.type] - : undefined; - - return { - data: lastSelectedNode?.data, - template: lastSelectedNodeTemplate, - }; - }, - defaultSelectorOptions -); - -const InspectorDetailsTab = () => { - const { data, template } = useAppSelector(selector); - const { t } = useTranslation(); - - if (!template || !data) { - return ( - - ); - } - - return ; -}; - -export default memo(InspectorDetailsTab); - -const Content = (props: { data: NodeData; template: InvocationTemplate }) => { - const { data } = props; - - return ( - - - - - - - - - ); -}; diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowNotesTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowNotesTab.tsx deleted file mode 100644 index d1ea0bcea1..0000000000 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/workflow/WorkflowNotesTab.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Box, Text } from '@chakra-ui/react'; -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAITextarea from 'common/components/IAITextarea'; -import { workflowNotesChanged } from 'features/nodes/store/nodesSlice'; -import { ChangeEvent, memo, useCallback } from 'react'; - -const selector = createSelector(stateSelector, ({ nodes }) => { - const { notes } = nodes.workflow; - - return { - notes, - }; -}); - -const WorkflowNotesTab = () => { - const { notes } = useAppSelector(selector); - const dispatch = useAppDispatch(); - - const handleChangeNotes = useCallback( - (e: ChangeEvent) => { - dispatch(workflowNotesChanged(e.target.value)); - }, - [dispatch] - ); - - return ( - - - - - {notes.length} - - - - ); -}; - -export default memo(WorkflowNotesTab); diff --git a/invokeai/frontend/web/src/features/nodes/store/util/makeTemplateSelector.ts b/invokeai/frontend/web/src/features/nodes/store/util/makeTemplateSelector.ts deleted file mode 100644 index 2c4ec37f0b..0000000000 --- a/invokeai/frontend/web/src/features/nodes/store/util/makeTemplateSelector.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import { AnyInvocationType } from 'services/events/types'; - -export const makeTemplateSelector = (type: AnyInvocationType) => - createSelector( - stateSelector, - ({ nodes }) => nodes.nodeTemplates[type], - defaultSelectorOptions - ); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxCollapse.tsx deleted file mode 100644 index b9cc8511aa..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxCollapse.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Flex } from '@chakra-ui/react'; -import IAICollapse from 'common/components/IAICollapse'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import ParamBoundingBoxHeight from './ParamBoundingBoxHeight'; -import ParamBoundingBoxWidth from './ParamBoundingBoxWidth'; - -const ParamBoundingBoxCollapse = () => { - const { t } = useTranslation(); - - return ( - - - - - - - ); -}; - -export default memo(ParamBoundingBoxCollapse); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamNoiseThreshold.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamNoiseThreshold.tsx deleted file mode 100644 index 7244800c41..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamNoiseThreshold.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import IAISlider from 'common/components/IAISlider'; -import { setThreshold } from 'features/parameters/store/generationSlice'; -import { useTranslation } from 'react-i18next'; - -const selector = createSelector( - stateSelector, - (state) => { - const { threshold } = state.generation; - return { - threshold, - }; - }, - defaultSelectorOptions -); - -export default function ParamNoiseThreshold() { - const dispatch = useAppDispatch(); - const { threshold } = useAppSelector(selector); - const { t } = useTranslation(); - - return ( - dispatch(setThreshold(v))} - handleReset={() => dispatch(setThreshold(0))} - value={threshold} - withInput - withReset - withSliderMarks - /> - ); -} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamPerlinNoise.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamPerlinNoise.tsx deleted file mode 100644 index b5429dc292..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Noise/ParamPerlinNoise.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -import IAISlider from 'common/components/IAISlider'; -import { setPerlin } from 'features/parameters/store/generationSlice'; -import { useTranslation } from 'react-i18next'; - -const selector = createSelector( - stateSelector, - (state) => { - const { perlin } = state.generation; - return { - perlin, - }; - }, - defaultSelectorOptions -); - -export default function ParamPerlinNoise() { - const dispatch = useAppDispatch(); - const { perlin } = useAppSelector(selector); - const { t } = useTranslation(); - - return ( - dispatch(setPerlin(v))} - handleReset={() => dispatch(setPerlin(0))} - value={perlin} - withInput - withReset - withSliderMarks - /> - ); -} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationAmount.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationAmount.tsx deleted file mode 100644 index 9bc84a0bf4..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationAmount.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISlider from 'common/components/IAISlider'; -import { setVariationAmount } from 'features/parameters/store/generationSlice'; -import { useTranslation } from 'react-i18next'; - -export default function ParamVariationAmount() { - const variationAmount = useAppSelector( - (state: RootState) => state.generation.variationAmount - ); - - const shouldGenerateVariations = useAppSelector( - (state: RootState) => state.generation.shouldGenerateVariations - ); - - const { t } = useTranslation(); - - const dispatch = useAppDispatch(); - - return ( - dispatch(setVariationAmount(v))} - handleReset={() => dispatch(setVariationAmount(0.1))} - withInput - withReset - withSliderMarks - /> - ); -} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationCollapse.tsx deleted file mode 100644 index 7ea248498d..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationCollapse.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// TODO: variations - -// import { Flex } from '@chakra-ui/react'; -// import { createSelector } from '@reduxjs/toolkit'; -// import { stateSelector } from 'app/store/store'; -// import { useAppSelector } from 'app/store/storeHooks'; -// import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; -// import IAICollapse from 'common/components/IAICollapse'; -// import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -// import { memo } from 'react'; -// import { useTranslation } from 'react-i18next'; -// import ParamVariationAmount from './ParamVariationAmount'; -// import { ParamVariationToggle } from './ParamVariationToggle'; -// import ParamVariationWeights from './ParamVariationWeights'; - -// const selector = createSelector( -// stateSelector, -// (state) => { -// const activeLabel = state.generation.shouldGenerateVariations -// ? 'Enabled' -// : undefined; - -// return { activeLabel }; -// }, -// defaultSelectorOptions -// ); - -// const ParamVariationCollapse = () => { -// const { t } = useTranslation(); -// const { activeLabel } = useAppSelector(selector); - -// const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled; - -// if (!isVariationEnabled) { -// return null; -// } - -// return ( -// -// -// -// -// -// -// -// ); -// }; - -// export default memo(ParamVariationCollapse); - -export default {}; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationToggle.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationToggle.tsx deleted file mode 100644 index 96929ea00a..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationToggle.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISwitch from 'common/components/IAISwitch'; -import { setShouldGenerateVariations } from 'features/parameters/store/generationSlice'; -import { ChangeEvent } from 'react'; - -export const ParamVariationToggle = () => { - const dispatch = useAppDispatch(); - - const shouldGenerateVariations = useAppSelector( - (state: RootState) => state.generation.shouldGenerateVariations - ); - - const handleChange = (e: ChangeEvent) => - dispatch(setShouldGenerateVariations(e.target.checked)); - - return ( - - ); -}; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationWeights.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationWeights.tsx deleted file mode 100644 index 45029e8536..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Variations/ParamVariationWeights.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// TODO: variations - -// import { RootState } from 'app/store/store'; -// import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -// import IAIInput from 'common/components/IAIInput'; -// import { validateSeedWeights } from 'common/util/seedWeightPairs'; -// import { setSeedWeights } from 'features/parameters/store/generationSlice'; -// import { ChangeEvent } from 'react'; -// import { useTranslation } from 'react-i18next'; - -// export default function ParamVariationWeights() { -// const seedWeights = useAppSelector( -// (state: RootState) => state.generation.seedWeights -// ); - -// const shouldGenerateVariations = useAppSelector( -// (state: RootState) => state.generation.shouldGenerateVariations -// ); - -// const { t } = useTranslation(); - -// const dispatch = useAppDispatch(); - -// const handleChangeSeedWeights = (e: ChangeEvent) => -// dispatch(setSeedWeights(e.target.value)); - -// return ( -// -// ); -// } - -export default {}; diff --git a/invokeai/frontend/web/src/features/parameters/store/postprocessingSelectors.ts b/invokeai/frontend/web/src/features/parameters/store/postprocessingSelectors.ts deleted file mode 100644 index 2908d16c54..0000000000 --- a/invokeai/frontend/web/src/features/parameters/store/postprocessingSelectors.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { RootState } from 'app/store/store'; - -export const postprocessingSelector = (state: RootState) => - state.postprocessing; diff --git a/invokeai/frontend/web/src/features/queue/components/CurrentQueueItemCard.tsx b/invokeai/frontend/web/src/features/queue/components/CurrentQueueItemCard.tsx deleted file mode 100644 index 2f7fc6fd8b..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/CurrentQueueItemCard.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { memo } from 'react'; -import QueueItemCard from './common/QueueItemCard'; -import { useGetCurrentQueueItemQuery } from 'services/api/endpoints/queue'; -import { useTranslation } from 'react-i18next'; - -const CurrentQueueItemCard = () => { - const { t } = useTranslation(); - const { data: currentQueueItemData } = useGetCurrentQueueItemQuery(); - - return ( - - ); -}; - -export default memo(CurrentQueueItemCard); diff --git a/invokeai/frontend/web/src/features/queue/components/NextQueueItemCard.tsx b/invokeai/frontend/web/src/features/queue/components/NextQueueItemCard.tsx deleted file mode 100644 index f9b9e874ab..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/NextQueueItemCard.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useGetNextQueueItemQuery } from 'services/api/endpoints/queue'; -import QueueItemCard from './common/QueueItemCard'; - -const NextQueueItemCard = () => { - const { t } = useTranslation(); - const { data: nextQueueItemData } = useGetNextQueueItemQuery(); - - return ( - - ); -}; - -export default memo(NextQueueItemCard); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemSkeleton.tsx b/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemSkeleton.tsx deleted file mode 100644 index 529c46af74..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/QueueList/QueueItemSkeleton.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Flex, Skeleton } from '@chakra-ui/react'; -import { memo } from 'react'; -import { COLUMN_WIDTHS } from './constants'; - -const QueueItemSkeleton = () => { - return ( - - - -   - - - - -   - - - - -   - - - - -   - - - - -   - - - - ); -}; - -export default memo(QueueItemSkeleton); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueStatusCard.tsx b/invokeai/frontend/web/src/features/queue/components/QueueStatusCard.tsx deleted file mode 100644 index fb57500461..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/QueueStatusCard.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Flex, Heading, Text } from '@chakra-ui/react'; -import { memo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useGetQueueStatusQuery } from 'services/api/endpoints/queue'; - -const QueueStatusCard = () => { - const { t } = useTranslation(); - const { data: queueStatus } = useGetQueueStatusQuery(); - - return ( - - {t('queue.status')} - - - {t('queue.pending')}:{' '} - - {queueStatus?.queue.pending} - - - - {t('queue.in_progress')}:{' '} - - {queueStatus?.queue.in_progress} - - - - {t('queue.completed')}:{' '} - - {queueStatus?.queue.completed} - - - - {t('queue.failed')}:{' '} - - {queueStatus?.queue.failed} - - - - {t('queue.canceled')}:{' '} - - {queueStatus?.queue.canceled} - - - ); -}; - -export default memo(QueueStatusCard); diff --git a/invokeai/frontend/web/src/features/queue/components/common/QueueItemCard.tsx b/invokeai/frontend/web/src/features/queue/components/common/QueueItemCard.tsx deleted file mode 100644 index d441be4ecb..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/common/QueueItemCard.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Flex, Heading, Text, Tooltip } from '@chakra-ui/react'; -import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent'; -import { memo } from 'react'; -import { components } from 'services/api/schema'; - -const QueueItemCard = ({ - session_queue_item, - label, -}: { - session_queue_item?: components['schemas']['SessionQueueItem'] | null; - label: string; -}) => { - return ( - - - {label} - {session_queue_item && ( - - {session_queue_item.batch_id} - - )} - - {session_queue_item && ( - - Batch Values: - {session_queue_item.field_values && - session_queue_item.field_values - .filter((v) => v.node_path !== 'metadata_accumulator') - .map(({ node_path, field_name, value }) => ( - - - {node_path}.{field_name} - - : {value} - - ))} - - )} - - ); -}; - -export default memo(QueueItemCard); diff --git a/invokeai/frontend/web/src/features/queue/components/common/QueueStatusDot.tsx b/invokeai/frontend/web/src/features/queue/components/common/QueueStatusDot.tsx deleted file mode 100644 index 6a500cada2..0000000000 --- a/invokeai/frontend/web/src/features/queue/components/common/QueueStatusDot.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Box } from '@chakra-ui/react'; -import { memo, useMemo } from 'react'; -import { SessionQueueItemStatus } from 'services/api/endpoints/queue'; - -const STATUSES = { - pending: { colorScheme: 'cyan', translationKey: 'queue.pending' }, - in_progress: { colorScheme: 'yellow', translationKey: 'queue.in_progress' }, - completed: { colorScheme: 'green', translationKey: 'queue.completed' }, - failed: { colorScheme: 'red', translationKey: 'queue.failed' }, - canceled: { colorScheme: 'orange', translationKey: 'queue.canceled' }, -}; - -const QueueStatusDot = ({ status }: { status: SessionQueueItemStatus }) => { - const sx = useMemo( - () => ({ - w: 2, - h: 2, - bg: `${STATUSES[status].colorScheme}.${500}`, - _dark: { - bg: `${STATUSES[status].colorScheme}.${400}`, - }, - borderRadius: '100%', - }), - [status] - ); - return ; -}; -export default memo(QueueStatusDot); diff --git a/invokeai/frontend/web/src/features/queue/util/formatNumberShort.ts b/invokeai/frontend/web/src/features/queue/util/formatNumberShort.ts deleted file mode 100644 index 9cc300c960..0000000000 --- a/invokeai/frontend/web/src/features/queue/util/formatNumberShort.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const formatNumberShort = (num: number) => - Intl.NumberFormat('en-US', { - notation: 'standard', - }).format(num); diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/ResetWebUIButton.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/ResetWebUIButton.tsx deleted file mode 100644 index 9617d0cd34..0000000000 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/ResetWebUIButton.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { - Flex, - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, - Text, - useDisclosure, -} from '@chakra-ui/react'; -import { LOCALSTORAGE_KEYS, LOCALSTORAGE_PREFIX } from 'app/store/constants'; -import IAIButton from 'common/components/IAIButton'; -import { memo, useCallback, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; - -type Props = { - onSettingsModalClose: () => void; -}; - -const ResetWebUIButton = ({ onSettingsModalClose }: Props) => { - const { t } = useTranslation(); - const [countdown, setCountdown] = useState(5); - - const { - isOpen: isRefreshModalOpen, - onOpen: onRefreshModalOpen, - onClose: onRefreshModalClose, - } = useDisclosure(); - - const handleClickResetWebUI = useCallback(() => { - // Only remove our keys - Object.keys(window.localStorage).forEach((key) => { - if ( - LOCALSTORAGE_KEYS.includes(key) || - key.startsWith(LOCALSTORAGE_PREFIX) - ) { - localStorage.removeItem(key); - } - }); - onSettingsModalClose(); - onRefreshModalOpen(); - setInterval(() => setCountdown((prev) => prev - 1), 1000); - }, [onSettingsModalClose, onRefreshModalOpen]); - - useEffect(() => { - if (countdown <= 0) { - window.location.reload(); - } - }, [countdown]); - - return ( - <> - - {t('settings.resetWebUI')} - - - - - - - - - {t('settings.resetComplete')} - Reloading in {countdown}... - - - - - - - - ); -}; - -export default memo(ResetWebUIButton); diff --git a/invokeai/frontend/web/src/features/system/store/actions.ts b/invokeai/frontend/web/src/features/system/store/actions.ts deleted file mode 100644 index 66181bc803..0000000000 --- a/invokeai/frontend/web/src/features/system/store/actions.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createAction } from '@reduxjs/toolkit'; - -export const sessionReadyToInvoke = createAction('system/sessionReadyToInvoke'); diff --git a/invokeai/frontend/web/src/features/system/store/sessionSlice.ts b/invokeai/frontend/web/src/features/system/store/sessionSlice.ts deleted file mode 100644 index 40d59c7baa..0000000000 --- a/invokeai/frontend/web/src/features/system/store/sessionSlice.ts +++ /dev/null @@ -1,62 +0,0 @@ -// TODO: split system slice inot this - -// import type { PayloadAction } from '@reduxjs/toolkit'; -// import { createSlice } from '@reduxjs/toolkit'; -// import { socketSubscribed, socketUnsubscribed } from 'services/events/actions'; - -// export type SessionState = { -// /** -// * The current socket session id -// */ -// sessionId: string; -// /** -// * Whether the current session is a canvas session. Needed to manage the staging area. -// */ -// isCanvasSession: boolean; -// /** -// * When a session is canceled, its ID is stored here until a new session is created. -// */ -// canceledSessionId: string; -// }; - -// export const initialSessionState: SessionState = { -// sessionId: '', -// isCanvasSession: false, -// canceledSessionId: '', -// }; - -// export const sessionSlice = createSlice({ -// name: 'session', -// initialState: initialSessionState, -// reducers: { -// sessionIdChanged: (state, action: PayloadAction) => { -// state.sessionId = action.payload; -// }, -// isCanvasSessionChanged: (state, action: PayloadAction) => { -// state.isCanvasSession = action.payload; -// }, -// }, -// extraReducers: (builder) => { -// /** -// * Socket Subscribed -// */ -// builder.addCase(socketSubscribed, (state, action) => { -// state.sessionId = action.payload.sessionId; -// state.canceledSessionId = ''; -// }); - -// /** -// * Socket Unsubscribed -// */ -// builder.addCase(socketUnsubscribed, (state) => { -// state.sessionId = ''; -// }); -// }, -// }); - -// export const { sessionIdChanged, isCanvasSessionChanged } = -// sessionSlice.actions; - -// export default sessionSlice.reducer; - -export default {}; diff --git a/invokeai/frontend/web/src/features/ui/components/common/OverlayScrollable.tsx b/invokeai/frontend/web/src/features/ui/components/common/OverlayScrollable.tsx deleted file mode 100644 index 722ee46fd5..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/common/OverlayScrollable.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; -import { PropsWithChildren, memo } from 'react'; -const OverlayScrollable = (props: PropsWithChildren) => { - return ( - - {props.children} - - ); -}; -export default memo(OverlayScrollable); diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/store/modelmanagerSelectors.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/store/modelmanagerSelectors.tsx deleted file mode 100644 index 593282760a..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/store/modelmanagerSelectors.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { RootState } from 'app/store/store'; - -export const modelmanagerSelector = (state: RootState) => state.modelmanager; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings.tsx deleted file mode 100644 index 9b9310b197..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Flex } from '@chakra-ui/react'; -import UnifiedCanvasBrushSettings from './UnifiedCanvasBrushSettings'; -import UnifiedCanvasLimitStrokesToBox from './UnifiedCanvasLimitStrokesToBox'; - -export default function UnifiedCanvasBaseBrushSettings() { - return ( - - - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSettings.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSettings.tsx deleted file mode 100644 index 1a5e42461b..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSettings.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Flex } from '@chakra-ui/react'; -import UnifiedCanvasBrushSize from './UnifiedCanvasBrushSize'; -import UnifiedCanvasColorPicker from './UnifiedCanvasColorPicker'; - -export default function UnifiedCanvasBrushSettings() { - return ( - - - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSize.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSize.tsx deleted file mode 100644 index 0792b787cd..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasBrushSize.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISlider from 'common/components/IAISlider'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { setBrushSize } from 'features/canvas/store/canvasSlice'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasBrushSize() { - const dispatch = useAppDispatch(); - - const brushSize = useAppSelector( - (state: RootState) => state.canvas.brushSize - ); - - const { t } = useTranslation(); - - const isStaging = useAppSelector(isStagingSelector); - - useHotkeys( - ['BracketLeft'], - () => { - dispatch(setBrushSize(Math.max(brushSize - 5, 5))); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [brushSize] - ); - - useHotkeys( - ['BracketRight'], - () => { - dispatch(setBrushSize(Math.min(brushSize + 5, 500))); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [brushSize] - ); - - return ( - dispatch(setBrushSize(newSize))} - sliderNumberInputProps={{ max: 500 }} - isCompact - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasClearMask.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasClearMask.tsx deleted file mode 100644 index 325afa7327..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasClearMask.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useAppDispatch } from 'app/store/storeHooks'; -import IAIButton from 'common/components/IAIButton'; - -import { clearMask } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; - -import { FaTrash } from 'react-icons/fa'; - -export default function UnifiedCanvasClearMask() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const handleClearMask = () => dispatch(clearMask()); - - return ( - } - onClick={handleClearMask} - tooltip={`${t('unifiedCanvas.clearMask')} (Shift+C)`} - > - {t('unifiedCanvas.betaClear')} - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasColorPicker.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasColorPicker.tsx deleted file mode 100644 index e57d87915f..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasColorPicker.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { Box, Flex } from '@chakra-ui/react'; -import { createSelector } from '@reduxjs/toolkit'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIColorPicker from 'common/components/IAIColorPicker'; -import IAIPopover from 'common/components/IAIPopover'; -import { - canvasSelector, - isStagingSelector, -} from 'features/canvas/store/canvasSelectors'; -import { setBrushColor, setMaskColor } from 'features/canvas/store/canvasSlice'; -import { clamp, isEqual } from 'lodash-es'; - -import { useHotkeys } from 'react-hotkeys-hook'; - -const selector = createSelector( - [canvasSelector, isStagingSelector], - (canvas, isStaging) => { - const { brushColor, maskColor, layer } = canvas; - return { - brushColor, - maskColor, - layer, - isStaging, - }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -export default function UnifiedCanvasColorPicker() { - const dispatch = useAppDispatch(); - const { brushColor, maskColor, layer, isStaging } = useAppSelector(selector); - - const currentColorDisplay = () => { - if (layer === 'base') { - return `rgba(${brushColor.r},${brushColor.g},${brushColor.b},${brushColor.a})`; - } - if (layer === 'mask') { - return `rgba(${maskColor.r},${maskColor.g},${maskColor.b},${maskColor.a})`; - } - }; - - useHotkeys( - ['shift+BracketLeft'], - () => { - dispatch( - setBrushColor({ - ...brushColor, - a: clamp(brushColor.a - 0.05, 0.05, 1), - }) - ); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [brushColor] - ); - - useHotkeys( - ['shift+BracketRight'], - () => { - dispatch( - setBrushColor({ - ...brushColor, - a: clamp(brushColor.a + 0.05, 0.05, 1), - }) - ); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [brushColor] - ); - - return ( - - } - > - - {layer === 'base' && ( - - dispatch(setBrushColor(newColor))} - /> - - )} - {layer === 'mask' && ( - - dispatch(setMaskColor(newColor))} - /> - - )} - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasDarkenOutsideSelection.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasDarkenOutsideSelection.tsx deleted file mode 100644 index 53e36f62b6..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasDarkenOutsideSelection.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import { setShouldDarkenOutsideBoundingBox } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasDarkenOutsideSelection() { - const shouldDarkenOutsideBoundingBox = useAppSelector( - (state: RootState) => state.canvas.shouldDarkenOutsideBoundingBox - ); - - const dispatch = useAppDispatch(); - - const { t } = useTranslation(); - - return ( - - dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked)) - } - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasEnableMask.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasEnableMask.tsx deleted file mode 100644 index ceb58cb5ca..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasEnableMask.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import { setIsMaskEnabled } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasEnableMask() { - const isMaskEnabled = useAppSelector( - (state: RootState) => state.canvas.isMaskEnabled - ); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const handleToggleEnableMask = () => - dispatch(setIsMaskEnabled(!isMaskEnabled)); - - return ( - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasLimitStrokesToBox.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasLimitStrokesToBox.tsx deleted file mode 100644 index 65f77c582f..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasLimitStrokesToBox.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import { setShouldRestrictStrokesToBox } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasLimitStrokesToBox() { - const dispatch = useAppDispatch(); - - const shouldRestrictStrokesToBox = useAppSelector( - (state: RootState) => state.canvas.shouldRestrictStrokesToBox - ); - - const { t } = useTranslation(); - - return ( - - dispatch(setShouldRestrictStrokesToBox(e.target.checked)) - } - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings.tsx deleted file mode 100644 index 71f41496ed..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Flex } from '@chakra-ui/react'; -import UnifiedCanvasBrushSettings from './UnifiedCanvasBrushSettings'; -import UnifiedCanvasClearMask from './UnifiedCanvasClearMask'; -import UnifiedCanvasEnableMask from './UnifiedCanvasEnableMask'; -import UnifiedCanvasPreserveMask from './UnifiedCanvasPreserveMask'; - -export default function UnifiedCanvasMaskBrushSettings() { - return ( - - - - - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings.tsx deleted file mode 100644 index 8881c93bb1..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Flex } from '@chakra-ui/layout'; -import UnifiedCanvasDarkenOutsideSelection from './UnifiedCanvasDarkenOutsideSelection'; -import UnifiedCanvasShowGrid from './UnifiedCanvasShowGrid'; -import UnifiedCanvasSnapToGrid from './UnifiedCanvasSnapToGrid'; - -export default function UnifiedCanvasMoveSettings() { - return ( - - - - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasPreserveMask.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasPreserveMask.tsx deleted file mode 100644 index fd3396533c..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasPreserveMask.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import { setShouldPreserveMaskedArea } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasPreserveMask() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const shouldPreserveMaskedArea = useAppSelector( - (state: RootState) => state.canvas.shouldPreserveMaskedArea - ); - - return ( - dispatch(setShouldPreserveMaskedArea(e.target.checked))} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSettings.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSettings.tsx deleted file mode 100644 index 22737ce0bb..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSettings.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { Flex } from '@chakra-ui/react'; -import { createSelector } from '@reduxjs/toolkit'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import IAIIconButton from 'common/components/IAIIconButton'; -import IAIPopover from 'common/components/IAIPopover'; -import { canvasSelector } from 'features/canvas/store/canvasSelectors'; -import { - setShouldAntialias, - setShouldAutoSave, - setShouldCropToBoundingBoxOnSave, - setShouldShowCanvasDebugInfo, - setShouldShowIntermediates, -} from 'features/canvas/store/canvasSlice'; - -import { FaWrench } from 'react-icons/fa'; - -import ClearCanvasHistoryButtonModal from 'features/canvas/components/ClearCanvasHistoryButtonModal'; -import { isEqual } from 'lodash-es'; -import { useTranslation } from 'react-i18next'; -import { memo } from 'react'; - -export const canvasControlsSelector = createSelector( - [canvasSelector], - (canvas) => { - const { - shouldAutoSave, - shouldCropToBoundingBoxOnSave, - shouldShowCanvasDebugInfo, - shouldShowIntermediates, - shouldAntialias, - } = canvas; - - return { - shouldAutoSave, - shouldCropToBoundingBoxOnSave, - shouldShowCanvasDebugInfo, - shouldShowIntermediates, - shouldAntialias, - }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -const UnifiedCanvasSettings = () => { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const { - shouldAutoSave, - shouldCropToBoundingBoxOnSave, - shouldShowCanvasDebugInfo, - shouldShowIntermediates, - shouldAntialias, - } = useAppSelector(canvasControlsSelector); - - return ( - } - /> - } - > - - - dispatch(setShouldShowIntermediates(e.target.checked)) - } - /> - dispatch(setShouldAutoSave(e.target.checked))} - /> - - dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked)) - } - /> - - dispatch(setShouldShowCanvasDebugInfo(e.target.checked)) - } - /> - dispatch(setShouldAntialias(e.target.checked))} - /> - - - - ); -}; - -export default memo(UnifiedCanvasSettings); diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasShowGrid.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasShowGrid.tsx deleted file mode 100644 index e17f74ce41..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasShowGrid.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import { setShouldShowGrid } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasShowGrid() { - const shouldShowGrid = useAppSelector( - (state: RootState) => state.canvas.shouldShowGrid - ); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - return ( - dispatch(setShouldShowGrid(e.target.checked))} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSnapToGrid.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSnapToGrid.tsx deleted file mode 100644 index 69e9a4e78b..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettings/UnifiedCanvasSnapToGrid.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; -import { setShouldSnapToGrid } from 'features/canvas/store/canvasSlice'; -import { ChangeEvent } from 'react'; -import { useTranslation } from 'react-i18next'; - -export default function UnifiedCanvasSnapToGrid() { - const shouldSnapToGrid = useAppSelector( - (state: RootState) => state.canvas.shouldSnapToGrid - ); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const handleChangeShouldSnapToGrid = (e: ChangeEvent) => - dispatch(setShouldSnapToGrid(e.target.checked)); - - return ( - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettingsBeta.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettingsBeta.tsx deleted file mode 100644 index e5503ac203..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolSettingsBeta.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppSelector } from 'app/store/storeHooks'; -import { canvasSelector } from 'features/canvas/store/canvasSelectors'; - -import { Flex } from '@chakra-ui/react'; -import { isEqual } from 'lodash-es'; -import UnifiedCanvasBaseBrushSettings from './UnifiedCanvasToolSettings/UnifiedCanvasBaseBrushSettings'; -import UnifiedCanvasMaskBrushSettings from './UnifiedCanvasToolSettings/UnifiedCanvasMaskBrushSettings'; -import UnifiedCanvasMoveSettings from './UnifiedCanvasToolSettings/UnifiedCanvasMoveSettings'; - -const selector = createSelector( - [canvasSelector], - (canvas) => { - const { tool, layer } = canvas; - return { - tool, - layer, - }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -export default function UnifiedCanvasToolSettingsBeta() { - const { tool, layer } = useAppSelector(selector); - - return ( - - {layer == 'base' && ['brush', 'eraser', 'colorPicker'].includes(tool) && ( - - )} - {layer == 'mask' && ['brush', 'eraser', 'colorPicker'].includes(tool) && ( - - )} - {tool == 'move' && } - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard.tsx deleted file mode 100644 index a865bcd0de..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { canvasCopiedToClipboard } from 'features/canvas/store/actions'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard'; -import { useCallback } from 'react'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { FaCopy } from 'react-icons/fa'; - -export default function UnifiedCanvasCopyToClipboard() { - const isStaging = useAppSelector(isStagingSelector); - const { isClipboardAPIAvailable } = useCopyImageToClipboard(); - - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - useHotkeys( - ['meta+c', 'ctrl+c'], - () => { - handleCopyImageToClipboard(); - }, - { - enabled: () => !isStaging && isClipboardAPIAvailable, - preventDefault: true, - }, - [isClipboardAPIAvailable] - ); - - const handleCopyImageToClipboard = useCallback(() => { - if (!isClipboardAPIAvailable) { - return; - } - dispatch(canvasCopiedToClipboard()); - }, [dispatch, isClipboardAPIAvailable]); - - if (!isClipboardAPIAvailable) { - return null; - } - - return ( - } - onClick={handleCopyImageToClipboard} - isDisabled={isStaging} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasDownloadImage.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasDownloadImage.tsx deleted file mode 100644 index 1039c5f364..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasDownloadImage.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { canvasDownloadedAsImage } from 'features/canvas/store/actions'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { FaDownload } from 'react-icons/fa'; - -export default function UnifiedCanvasDownloadImage() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const canvasBaseLayer = getCanvasBaseLayer(); - - const isStaging = useAppSelector(isStagingSelector); - - useHotkeys( - ['shift+d'], - () => { - handleDownloadAsImage(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [canvasBaseLayer] - ); - - const handleDownloadAsImage = () => { - dispatch(canvasDownloadedAsImage()); - }; - - return ( - } - onClick={handleDownloadAsImage} - isDisabled={isStaging} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasFileUploader.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasFileUploader.tsx deleted file mode 100644 index ac83e7f20b..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasFileUploader.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { useImageUploadButton } from 'common/hooks/useImageUploadButton'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { useTranslation } from 'react-i18next'; -import { FaUpload } from 'react-icons/fa'; - -export default function UnifiedCanvasFileUploader() { - const isStaging = useAppSelector(isStagingSelector); - - const { getUploadButtonProps, getUploadInputProps } = useImageUploadButton({ - postUploadAction: { type: 'SET_CANVAS_INITIAL_IMAGE' }, - }); - const { t } = useTranslation(); - - return ( - <> - } - isDisabled={isStaging} - {...getUploadButtonProps()} - /> - - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasLayerSelect.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasLayerSelect.tsx deleted file mode 100644 index d7fb9d8201..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasLayerSelect.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIMantineSelect from 'common/components/IAIMantineSelect'; -import { - canvasSelector, - isStagingSelector, -} from 'features/canvas/store/canvasSelectors'; -import { setIsMaskEnabled, setLayer } from 'features/canvas/store/canvasSlice'; -import { - CanvasLayer, - LAYER_NAMES_DICT, -} from 'features/canvas/store/canvasTypes'; -import { isEqual } from 'lodash-es'; - -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; - -const selector = createSelector( - [canvasSelector, isStagingSelector], - (canvas, isStaging) => { - const { layer, isMaskEnabled } = canvas; - return { layer, isMaskEnabled, isStaging }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -export default function UnifiedCanvasLayerSelect() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - const { layer, isMaskEnabled, isStaging } = useAppSelector(selector); - - const handleToggleMaskLayer = () => { - dispatch(setLayer(layer === 'mask' ? 'base' : 'mask')); - }; - - useHotkeys( - ['q'], - () => { - handleToggleMaskLayer(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [layer] - ); - - const handleChangeLayer = (v: string) => { - const newLayer = v as CanvasLayer; - dispatch(setLayer(newLayer)); - if (newLayer === 'mask' && !isMaskEnabled) { - dispatch(setIsMaskEnabled(true)); - } - }; - return ( - - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMergeVisible.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMergeVisible.tsx deleted file mode 100644 index 66c378e068..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMergeVisible.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { canvasMerged } from 'features/canvas/store/actions'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { FaLayerGroup } from 'react-icons/fa'; -export default function UnifiedCanvasMergeVisible() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const isStaging = useAppSelector(isStagingSelector); - - useHotkeys( - ['shift+m'], - () => { - handleMergeVisible(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [] - ); - - const handleMergeVisible = () => { - dispatch(canvasMerged()); - }; - return ( - } - onClick={handleMergeVisible} - isDisabled={isStaging} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMoveTool.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMoveTool.tsx deleted file mode 100644 index 5362657def..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasMoveTool.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { RootState } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { setTool } from 'features/canvas/store/canvasSlice'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { FaArrowsAlt } from 'react-icons/fa'; - -export default function UnifiedCanvasMoveTool() { - const tool = useAppSelector((state: RootState) => state.canvas.tool); - const isStaging = useAppSelector(isStagingSelector); - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - useHotkeys( - ['v'], - () => { - handleSelectMoveTool(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [] - ); - - const handleSelectMoveTool = () => dispatch(setTool('move')); - - return ( - } - isChecked={tool === 'move' || isStaging} - onClick={handleSelectMoveTool} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetCanvas.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetCanvas.tsx deleted file mode 100644 index 85396c87be..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetCanvas.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { resetCanvas } from 'features/canvas/store/canvasSlice'; -import { useTranslation } from 'react-i18next'; -import { FaTrash } from 'react-icons/fa'; - -export default function UnifiedCanvasResetCanvas() { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const isStaging = useAppSelector(isStagingSelector); - - const handleResetCanvas = () => { - dispatch(resetCanvas()); - }; - return ( - } - onClick={handleResetCanvas} - isDisabled={isStaging} - colorScheme="error" - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetView.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetView.tsx deleted file mode 100644 index 7d0478476e..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasResetView.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useAppDispatch } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { useSingleAndDoubleClick } from 'common/hooks/useSingleAndDoubleClick'; -import { resetCanvasView } from 'features/canvas/store/canvasSlice'; -import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { FaCrosshairs } from 'react-icons/fa'; - -export default function UnifiedCanvasResetView() { - const canvasBaseLayer = getCanvasBaseLayer(); - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - useHotkeys( - ['r'], - () => { - handleResetCanvasView(); - }, - { - enabled: () => true, - preventDefault: true, - }, - [canvasBaseLayer] - ); - - const handleClickResetCanvasView = useSingleAndDoubleClick( - () => handleResetCanvasView(false), - () => handleResetCanvasView(true) - ); - - const handleResetCanvasView = (shouldScaleTo1 = false) => { - const canvasBaseLayer = getCanvasBaseLayer(); - if (!canvasBaseLayer) { - return; - } - const clientRect = canvasBaseLayer.getClientRect({ - skipTransform: true, - }); - dispatch( - resetCanvasView({ - contentRect: clientRect, - shouldScaleTo1, - }) - ); - }; - return ( - } - onClick={handleClickResetCanvasView} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery.tsx deleted file mode 100644 index 31617a4dbe..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { canvasSavedToGallery } from 'features/canvas/store/actions'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { FaSave } from 'react-icons/fa'; - -export default function UnifiedCanvasSaveToGallery() { - const isStaging = useAppSelector(isStagingSelector); - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - - useHotkeys( - ['shift+s'], - () => { - handleSaveToGallery(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [] - ); - - const handleSaveToGallery = () => { - dispatch(canvasSavedToGallery()); - }; - - return ( - } - onClick={handleSaveToGallery} - isDisabled={isStaging} - /> - ); -} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasToolSelect.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasToolSelect.tsx deleted file mode 100644 index 59a0890960..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbar/UnifiedCanvasToolSelect.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { ButtonGroup, Flex } from '@chakra-ui/react'; -import { createSelector } from '@reduxjs/toolkit'; -import { stateSelector } from 'app/store/store'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; -import { - addEraseRect, - addFillRect, - setTool, -} from 'features/canvas/store/canvasSlice'; -import { isEqual } from 'lodash-es'; -import { memo, useCallback } from 'react'; - -import { useHotkeys } from 'react-hotkeys-hook'; -import { useTranslation } from 'react-i18next'; -import { - FaEraser, - FaEyeDropper, - FaFillDrip, - FaPaintBrush, - FaPlus, -} from 'react-icons/fa'; - -export const selector = createSelector( - [stateSelector, isStagingSelector], - ({ canvas }, isStaging) => { - const { tool } = canvas; - - return { - tool, - isStaging, - }; - }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } -); - -const UnifiedCanvasToolSelect = () => { - const dispatch = useAppDispatch(); - const { t } = useTranslation(); - const { tool, isStaging } = useAppSelector(selector); - - useHotkeys( - ['b'], - () => { - handleSelectBrushTool(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [] - ); - - useHotkeys( - ['e'], - () => { - handleSelectEraserTool(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [tool] - ); - - useHotkeys( - ['c'], - () => { - handleSelectColorPickerTool(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - }, - [tool] - ); - - useHotkeys( - ['shift+f'], - () => { - handleFillRect(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - } - ); - - useHotkeys( - ['delete', 'backspace'], - () => { - handleEraseBoundingBox(); - }, - { - enabled: () => !isStaging, - preventDefault: true, - } - ); - - const handleSelectBrushTool = useCallback( - () => dispatch(setTool('brush')), - [dispatch] - ); - const handleSelectEraserTool = useCallback( - () => dispatch(setTool('eraser')), - [dispatch] - ); - const handleSelectColorPickerTool = useCallback( - () => dispatch(setTool('colorPicker')), - [dispatch] - ); - const handleFillRect = useCallback(() => dispatch(addFillRect()), [dispatch]); - const handleEraseBoundingBox = useCallback( - () => dispatch(addEraseRect()), - [dispatch] - ); - - return ( - - - } - isChecked={tool === 'brush' && !isStaging} - onClick={handleSelectBrushTool} - isDisabled={isStaging} - /> - } - isChecked={tool === 'eraser' && !isStaging} - isDisabled={isStaging} - onClick={handleSelectEraserTool} - /> - - - } - isDisabled={isStaging} - onClick={handleFillRect} - /> - } - isDisabled={isStaging} - onClick={handleEraseBoundingBox} - /> - - } - isChecked={tool === 'colorPicker' && !isStaging} - isDisabled={isStaging} - onClick={handleSelectColorPickerTool} - width="max-content" - /> - - ); -}; - -export default memo(UnifiedCanvasToolSelect); diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbarBeta.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbarBeta.tsx deleted file mode 100644 index 7ef5af6949..0000000000 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasBeta/UnifiedCanvasToolbarBeta.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Flex } from '@chakra-ui/react'; - -import IAICanvasRedoButton from 'features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton'; -import IAICanvasUndoButton from 'features/canvas/components/IAICanvasToolbar/IAICanvasUndoButton'; -import { memo } from 'react'; -import UnifiedCanvasSettings from './UnifiedCanvasToolSettings/UnifiedCanvasSettings'; -import UnifiedCanvasCopyToClipboard from './UnifiedCanvasToolbar/UnifiedCanvasCopyToClipboard'; -import UnifiedCanvasDownloadImage from './UnifiedCanvasToolbar/UnifiedCanvasDownloadImage'; -import UnifiedCanvasFileUploader from './UnifiedCanvasToolbar/UnifiedCanvasFileUploader'; -import UnifiedCanvasLayerSelect from './UnifiedCanvasToolbar/UnifiedCanvasLayerSelect'; -import UnifiedCanvasMergeVisible from './UnifiedCanvasToolbar/UnifiedCanvasMergeVisible'; -import UnifiedCanvasMoveTool from './UnifiedCanvasToolbar/UnifiedCanvasMoveTool'; -import UnifiedCanvasResetCanvas from './UnifiedCanvasToolbar/UnifiedCanvasResetCanvas'; -import UnifiedCanvasResetView from './UnifiedCanvasToolbar/UnifiedCanvasResetView'; -import UnifiedCanvasSaveToGallery from './UnifiedCanvasToolbar/UnifiedCanvasSaveToGallery'; -import UnifiedCanvasToolSelect from './UnifiedCanvasToolbar/UnifiedCanvasToolSelect'; - -const UnifiedCanvasToolbarBeta = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default memo(UnifiedCanvasToolbarBeta); diff --git a/invokeai/frontend/web/src/theme/components/table.ts b/invokeai/frontend/web/src/theme/components/table.ts deleted file mode 100644 index e69de29bb2..0000000000 From 3a0ec635c9d52e10c757a103e2a271b70b872f71 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:01:14 +1100 Subject: [PATCH 15/24] feat(ui): add eslint rule `react/jsx-no-bind` This rule enforces no arrow functions in component props. In practice, it means all functions passed as component props must be wrapped in `useCallback()`. This is a performance optimization to prevent unnecessary rerenders. The rule is added and all violations have been fixed, whew! --- invokeai/frontend/web/.eslintrc.js | 1 + .../src/common/components/IAIAlertDialog.tsx | 17 ++- .../src/common/components/IAIMantineInput.tsx | 52 +++++---- .../src/common/components/IAINumberInput.tsx | 40 ++++--- .../web/src/common/components/IAISelect.tsx | 15 +-- .../web/src/common/components/IAISlider.tsx | 19 ++-- .../src/common/components/ImageUploader.tsx | 17 ++- .../ClearCanvasHistoryButtonModal.tsx | 9 +- .../IAICanvasToolbar/IAICanvasMaskOptions.tsx | 41 ++++--- .../IAICanvasToolbar/IAICanvasRedoButton.tsx | 5 +- .../IAICanvasSettingsButtonPopover.tsx | 76 +++++++++---- .../IAICanvasToolChooserOptions.tsx | 39 +++++-- .../IAICanvasToolbar/IAICanvasToolbar.tsx | 43 ++++---- .../IAICanvasToolbar/IAICanvasUndoButton.tsx | 5 +- .../components/ChangeBoardModal.tsx | 7 +- .../components/ParamEmbeddingPopover.tsx | 16 +-- .../components/Boards/BoardAutoAddSelect.tsx | 12 ++- .../components/Boards/BoardContextMenu.tsx | 77 ++++++++------ .../components/GallerySettingsPopover.tsx | 10 +- .../ImageContextMenu/ImageContextMenu.tsx | 56 +++++----- .../components/ImageGrid/GalleryImageGrid.tsx | 12 ++- .../ImageMetadataActions.tsx | 8 +- .../ImageMetadataViewer/ImageMetadataItem.tsx | 9 +- .../lora/components/ParamLoraSelect.tsx | 12 ++- .../nodes/CurrentImage/CurrentImageNode.tsx | 10 +- .../Invocation/fields/FieldContextMenu.tsx | 34 +++--- .../fields/inputs/LoRAModelInputField.tsx | 12 ++- .../fields/inputs/NumberInputField.tsx | 33 +++--- .../BoundingBox/ParamBoundingBoxHeight.tsx | 33 +++--- .../BoundingBox/ParamBoundingBoxWidth.tsx | 33 +++--- .../ParamCanvasCoherenceMode.tsx | 17 +-- .../ParamCanvasCoherenceSteps.tsx | 21 ++-- .../ParamCanvasCoherenceStrength.tsx | 20 ++-- .../MaskAdjustment/ParamMaskBlur.tsx | 19 ++-- .../MaskAdjustment/ParamMaskBlurMethod.tsx | 16 +-- .../ParamScaleBeforeProcessing.tsx | 11 +- .../InfillAndScaling/ParamScaledHeight.tsx | 35 +++--- .../InfillAndScaling/ParamScaledWidth.tsx | 35 +++--- .../Parameters/Core/ParamAspectRatio.tsx | 14 ++- .../ImageToImage/ImageToImageFit.tsx | 10 +- .../components/Parameters/Seed/ParamSeed.tsx | 6 +- .../Parameters/Seed/ParamSeedRandomize.tsx | 9 +- .../Parameters/Seed/ParamSeedShuffle.tsx | 7 +- .../Symmetry/ParamSymmetryHorizontal.tsx | 15 ++- .../Symmetry/ParamSymmetryToggle.tsx | 9 +- .../Symmetry/ParamSymmetryVertical.tsx | 15 ++- .../Upscale/ParamRealESRGANModel.tsx | 7 +- .../sdxl/components/ParamSDXLConcatButton.tsx | 5 +- .../SDXLRefiner/ParamUseSDXLRefiner.tsx | 11 +- .../SettingsModal/SettingsModal.tsx | 95 +++++++++++------ .../ui/components/FloatingGalleryButton.tsx | 6 +- .../FloatingParametersPanelButtons.tsx | 6 +- .../subpanels/AddModelsPanel/AddModels.tsx | 11 +- .../AddModelsPanel/AdvancedAddCheckpoint.tsx | 37 ++++--- .../AddModelsPanel/AdvancedAddDiffusers.tsx | 29 +++-- .../AddModelsPanel/AdvancedAddModels.tsx | 15 +-- .../AddModelsPanel/FoundModelsList.tsx | 7 +- .../AddModelsPanel/ScanAdvancedAddModels.tsx | 33 +++--- .../AddModelsPanel/SearchFolderForm.tsx | 14 +-- .../subpanels/ImportModelsPanel.tsx | 9 +- .../subpanels/MergeModelsPanel.tsx | 100 +++++++++++++----- .../ModelManagerPanel/CheckpointModelEdit.tsx | 7 +- .../ModelManagerPanel/ModelConvert.tsx | 37 ++++--- .../subpanels/ModelManagerPanel/ModelList.tsx | 12 +-- .../SyncModelsButton.tsx | 5 +- 65 files changed, 932 insertions(+), 526 deletions(-) diff --git a/invokeai/frontend/web/.eslintrc.js b/invokeai/frontend/web/.eslintrc.js index c2b1433a9a..976aea9739 100644 --- a/invokeai/frontend/web/.eslintrc.js +++ b/invokeai/frontend/web/.eslintrc.js @@ -24,6 +24,7 @@ module.exports = { root: true, rules: { curly: 'error', + 'react/jsx-no-bind': ['error', { allowBind: true }], 'react/jsx-curly-brace-presence': [ 'error', { props: 'never', children: 'never' }, diff --git a/invokeai/frontend/web/src/common/components/IAIAlertDialog.tsx b/invokeai/frontend/web/src/common/components/IAIAlertDialog.tsx index bd919c162a..a82095daa3 100644 --- a/invokeai/frontend/web/src/common/components/IAIAlertDialog.tsx +++ b/invokeai/frontend/web/src/common/components/IAIAlertDialog.tsx @@ -8,7 +8,14 @@ import { forwardRef, useDisclosure, } from '@chakra-ui/react'; -import { cloneElement, memo, ReactElement, ReactNode, useRef } from 'react'; +import { + cloneElement, + memo, + ReactElement, + ReactNode, + useCallback, + useRef, +} from 'react'; import { useTranslation } from 'react-i18next'; import IAIButton from './IAIButton'; @@ -38,15 +45,15 @@ const IAIAlertDialog = forwardRef((props: Props, ref) => { const { isOpen, onOpen, onClose } = useDisclosure(); const cancelRef = useRef(null); - const handleAccept = () => { + const handleAccept = useCallback(() => { acceptCallback(); onClose(); - }; + }, [acceptCallback, onClose]); - const handleCancel = () => { + const handleCancel = useCallback(() => { cancelCallback && cancelCallback(); onClose(); - }; + }, [cancelCallback, onClose]); return ( <> diff --git a/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx b/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx index afe8b9442b..a324f80770 100644 --- a/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx +++ b/invokeai/frontend/web/src/common/components/IAIMantineInput.tsx @@ -1,6 +1,7 @@ import { useColorMode } from '@chakra-ui/react'; import { TextInput, TextInputProps } from '@mantine/core'; import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; +import { useCallback } from 'react'; import { mode } from 'theme/util/mode'; type IAIMantineTextInputProps = TextInputProps; @@ -20,26 +21,37 @@ export default function IAIMantineTextInput(props: IAIMantineTextInputProps) { } = useChakraThemeTokens(); const { colorMode } = useColorMode(); - return ( - ({ - input: { - color: mode(base900, base100)(colorMode), - backgroundColor: mode(base50, base900)(colorMode), - borderColor: mode(base200, base800)(colorMode), - borderWidth: 2, - outline: 'none', - ':focus': { - borderColor: mode(accent300, accent500)(colorMode), - }, + const stylesFunc = useCallback( + () => ({ + input: { + color: mode(base900, base100)(colorMode), + backgroundColor: mode(base50, base900)(colorMode), + borderColor: mode(base200, base800)(colorMode), + borderWidth: 2, + outline: 'none', + ':focus': { + borderColor: mode(accent300, accent500)(colorMode), }, - label: { - color: mode(base700, base300)(colorMode), - fontWeight: 'normal', - marginBottom: 4, - }, - })} - {...rest} - /> + }, + label: { + color: mode(base700, base300)(colorMode), + fontWeight: 'normal' as const, + marginBottom: 4, + }, + }), + [ + accent300, + accent500, + base100, + base200, + base300, + base50, + base700, + base800, + base900, + colorMode, + ] ); + + return ; } diff --git a/invokeai/frontend/web/src/common/components/IAINumberInput.tsx b/invokeai/frontend/web/src/common/components/IAINumberInput.tsx index 243dac9d63..36098ec097 100644 --- a/invokeai/frontend/web/src/common/components/IAINumberInput.tsx +++ b/invokeai/frontend/web/src/common/components/IAINumberInput.tsx @@ -98,28 +98,34 @@ const IAINumberInput = forwardRef((props: Props, ref) => { } }, [value, valueAsString]); - const handleOnChange = (v: string) => { - setValueAsString(v); - // This allows negatives and decimals e.g. '-123', `.5`, `-0.2`, etc. - if (!v.match(numberStringRegex)) { - // Cast the value to number. Floor it if it should be an integer. - onChange(isInteger ? Math.floor(Number(v)) : Number(v)); - } - }; + const handleOnChange = useCallback( + (v: string) => { + setValueAsString(v); + // This allows negatives and decimals e.g. '-123', `.5`, `-0.2`, etc. + if (!v.match(numberStringRegex)) { + // Cast the value to number. Floor it if it should be an integer. + onChange(isInteger ? Math.floor(Number(v)) : Number(v)); + } + }, + [isInteger, onChange] + ); /** * Clicking the steppers allows the value to go outside bounds; we need to * clamp it on blur and floor it if needed. */ - const handleBlur = (e: FocusEvent) => { - const clamped = clamp( - isInteger ? Math.floor(Number(e.target.value)) : Number(e.target.value), - min, - max - ); - setValueAsString(String(clamped)); - onChange(clamped); - }; + const handleBlur = useCallback( + (e: FocusEvent) => { + const clamped = clamp( + isInteger ? Math.floor(Number(e.target.value)) : Number(e.target.value), + min, + max + ); + setValueAsString(String(clamped)); + onChange(clamped); + }, + [isInteger, max, min, onChange] + ); const handleKeyDown = useCallback( (e: KeyboardEvent) => { diff --git a/invokeai/frontend/web/src/common/components/IAISelect.tsx b/invokeai/frontend/web/src/common/components/IAISelect.tsx index 7bc8952d94..faa5732017 100644 --- a/invokeai/frontend/web/src/common/components/IAISelect.tsx +++ b/invokeai/frontend/web/src/common/components/IAISelect.tsx @@ -6,7 +6,7 @@ import { Tooltip, TooltipProps, } from '@chakra-ui/react'; -import { memo, MouseEvent } from 'react'; +import { memo, MouseEvent, useCallback } from 'react'; import IAIOption from './IAIOption'; type IAISelectProps = SelectProps & { @@ -33,15 +33,16 @@ const IAISelect = (props: IAISelectProps) => { spaceEvenly, ...rest } = props; + const handleClick = useCallback((e: MouseEvent) => { + e.stopPropagation(); + e.nativeEvent.stopImmediatePropagation(); + e.nativeEvent.stopPropagation(); + e.nativeEvent.cancelBubble = true; + }, []); return ( ) => { - e.stopPropagation(); - e.nativeEvent.stopImmediatePropagation(); - e.nativeEvent.stopPropagation(); - e.nativeEvent.cancelBubble = true; - }} + onClick={handleClick} sx={ horizontal ? { diff --git a/invokeai/frontend/web/src/common/components/IAISlider.tsx b/invokeai/frontend/web/src/common/components/IAISlider.tsx index e879c00977..3ed3ee1920 100644 --- a/invokeai/frontend/web/src/common/components/IAISlider.tsx +++ b/invokeai/frontend/web/src/common/components/IAISlider.tsx @@ -186,6 +186,13 @@ const IAISlider = forwardRef((props: IAIFullSliderProps, ref) => { [dispatch] ); + const handleMouseEnter = useCallback(() => setShowTooltip(true), []); + const handleMouseLeave = useCallback(() => setShowTooltip(false), []); + const handleStepperClick = useCallback( + () => onChange(Number(localInputValue)), + [localInputValue, onChange] + ); + return ( { max={max} step={step} onChange={handleSliderChange} - onMouseEnter={() => setShowTooltip(true)} - onMouseLeave={() => setShowTooltip(false)} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} focusThumbOnChange={false} isDisabled={isDisabled} {...rest} @@ -332,12 +339,8 @@ const IAISlider = forwardRef((props: IAIFullSliderProps, ref) => { {...sliderNumberInputFieldProps} /> - onChange(Number(localInputValue))} - /> - onChange(Number(localInputValue))} - /> + + )} diff --git a/invokeai/frontend/web/src/common/components/ImageUploader.tsx b/invokeai/frontend/web/src/common/components/ImageUploader.tsx index 59349e615d..913d3b4414 100644 --- a/invokeai/frontend/web/src/common/components/ImageUploader.tsx +++ b/invokeai/frontend/web/src/common/components/ImageUploader.tsx @@ -146,16 +146,15 @@ const ImageUploader = (props: ImageUploaderProps) => { }; }, [inputRef]); + const handleKeyDown = useCallback((e: KeyboardEvent) => { + // Bail out if user hits spacebar - do not open the uploader + if (e.key === ' ') { + return; + } + }, []); + return ( - { - // Bail out if user hits spacebar - do not open the uploader - if (e.key === ' ') { - return; - } - }} - > + {children} diff --git a/invokeai/frontend/web/src/features/canvas/components/ClearCanvasHistoryButtonModal.tsx b/invokeai/frontend/web/src/features/canvas/components/ClearCanvasHistoryButtonModal.tsx index a86497aade..3f5264fe92 100644 --- a/invokeai/frontend/web/src/features/canvas/components/ClearCanvasHistoryButtonModal.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/ClearCanvasHistoryButtonModal.tsx @@ -5,17 +5,22 @@ import { clearCanvasHistory } from 'features/canvas/store/canvasSlice'; import { useTranslation } from 'react-i18next'; import { FaTrash } from 'react-icons/fa'; import { isStagingSelector } from '../store/canvasSelectors'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; const ClearCanvasHistoryButtonModal = () => { const isStaging = useAppSelector(isStagingSelector); const dispatch = useAppDispatch(); const { t } = useTranslation(); + const acceptCallback = useCallback( + () => dispatch(clearCanvasHistory()), + [dispatch] + ); + return ( dispatch(clearCanvasHistory())} + acceptCallback={acceptCallback} acceptButtonText={t('unifiedCanvas.clearHistory')} triggerComponent={ } isDisabled={isStaging}> diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx index 43e8febd66..e1b2105bbc 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasMaskOptions.tsx @@ -20,7 +20,8 @@ import { } from 'features/canvas/store/canvasSlice'; import { rgbaColorToString } from 'features/canvas/util/colorToString'; import { isEqual } from 'lodash-es'; -import { memo } from 'react'; +import { ChangeEvent, memo, useCallback } from 'react'; +import { RgbaColor } from 'react-colorful'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; @@ -95,18 +96,35 @@ const IAICanvasMaskOptions = () => { [isMaskEnabled] ); - const handleToggleMaskLayer = () => { + const handleToggleMaskLayer = useCallback(() => { dispatch(setLayer(layer === 'mask' ? 'base' : 'mask')); - }; + }, [dispatch, layer]); - const handleClearMask = () => dispatch(clearMask()); + const handleClearMask = useCallback(() => { + dispatch(clearMask()); + }, [dispatch]); - const handleToggleEnableMask = () => + const handleToggleEnableMask = useCallback(() => { dispatch(setIsMaskEnabled(!isMaskEnabled)); + }, [dispatch, isMaskEnabled]); - const handleSaveMask = async () => { + const handleSaveMask = useCallback(async () => { dispatch(canvasMaskSavedToGallery()); - }; + }, [dispatch]); + + const handleChangePreserveMaskedArea = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldPreserveMaskedArea(e.target.checked)); + }, + [dispatch] + ); + + const handleChangeMaskColor = useCallback( + (newColor: RgbaColor) => { + dispatch(setMaskColor(newColor)); + }, + [dispatch] + ); return ( { - dispatch(setShouldPreserveMaskedArea(e.target.checked)) - } + onChange={handleChangePreserveMaskedArea} /> - dispatch(setMaskColor(newColor))} - /> + } onClick={handleSaveMask}> Save Mask diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx index 18ded1c9c7..e20980f318 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasRedoButton.tsx @@ -10,6 +10,7 @@ import { redo } from 'features/canvas/store/canvasSlice'; import { stateSelector } from 'app/store/store'; import { isEqual } from 'lodash-es'; import { useTranslation } from 'react-i18next'; +import { useCallback } from 'react'; const canvasRedoSelector = createSelector( [stateSelector, activeTabNameSelector], @@ -34,9 +35,9 @@ export default function IAICanvasRedoButton() { const { t } = useTranslation(); - const handleRedo = () => { + const handleRedo = useCallback(() => { dispatch(redo()); - }; + }, [dispatch]); useHotkeys( ['meta+shift+z', 'ctrl+shift+z', 'control+y', 'meta+y'], diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx index aae2da5632..c653c4ccd2 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasSettingsButtonPopover.tsx @@ -18,7 +18,7 @@ import { } from 'features/canvas/store/canvasSlice'; import { isEqual } from 'lodash-es'; -import { ChangeEvent, memo } from 'react'; +import { ChangeEvent, memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; import { FaWrench } from 'react-icons/fa'; @@ -86,8 +86,52 @@ const IAICanvasSettingsButtonPopover = () => { [shouldSnapToGrid] ); - const handleChangeShouldSnapToGrid = (e: ChangeEvent) => - dispatch(setShouldSnapToGrid(e.target.checked)); + const handleChangeShouldSnapToGrid = useCallback( + (e: ChangeEvent) => + dispatch(setShouldSnapToGrid(e.target.checked)), + [dispatch] + ); + + const handleChangeShouldShowIntermediates = useCallback( + (e: ChangeEvent) => + dispatch(setShouldShowIntermediates(e.target.checked)), + [dispatch] + ); + const handleChangeShouldShowGrid = useCallback( + (e: ChangeEvent) => + dispatch(setShouldShowGrid(e.target.checked)), + [dispatch] + ); + const handleChangeShouldDarkenOutsideBoundingBox = useCallback( + (e: ChangeEvent) => + dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked)), + [dispatch] + ); + const handleChangeShouldAutoSave = useCallback( + (e: ChangeEvent) => + dispatch(setShouldAutoSave(e.target.checked)), + [dispatch] + ); + const handleChangeShouldCropToBoundingBoxOnSave = useCallback( + (e: ChangeEvent) => + dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked)), + [dispatch] + ); + const handleChangeShouldRestrictStrokesToBox = useCallback( + (e: ChangeEvent) => + dispatch(setShouldRestrictStrokesToBox(e.target.checked)), + [dispatch] + ); + const handleChangeShouldShowCanvasDebugInfo = useCallback( + (e: ChangeEvent) => + dispatch(setShouldShowCanvasDebugInfo(e.target.checked)), + [dispatch] + ); + const handleChangeShouldAntialias = useCallback( + (e: ChangeEvent) => + dispatch(setShouldAntialias(e.target.checked)), + [dispatch] + ); return ( { - dispatch(setShouldShowIntermediates(e.target.checked)) - } + onChange={handleChangeShouldShowIntermediates} /> dispatch(setShouldShowGrid(e.target.checked))} + onChange={handleChangeShouldShowGrid} /> { - dispatch(setShouldDarkenOutsideBoundingBox(e.target.checked)) - } + onChange={handleChangeShouldDarkenOutsideBoundingBox} /> dispatch(setShouldAutoSave(e.target.checked))} + onChange={handleChangeShouldAutoSave} /> - dispatch(setShouldCropToBoundingBoxOnSave(e.target.checked)) - } + onChange={handleChangeShouldCropToBoundingBoxOnSave} /> - dispatch(setShouldRestrictStrokesToBox(e.target.checked)) - } + onChange={handleChangeShouldRestrictStrokesToBox} /> - dispatch(setShouldShowCanvasDebugInfo(e.target.checked)) - } + onChange={handleChangeShouldShowCanvasDebugInfo} /> dispatch(setShouldAntialias(e.target.checked))} + onChange={handleChangeShouldAntialias} /> diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx index 0030f1f06e..40b3186fb1 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolChooserOptions.tsx @@ -15,7 +15,8 @@ import { setTool, } from 'features/canvas/store/canvasSlice'; import { clamp, isEqual } from 'lodash-es'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; +import { RgbaColor } from 'react-colorful'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; @@ -172,11 +173,33 @@ const IAICanvasToolChooserOptions = () => { [brushColor] ); - const handleSelectBrushTool = () => dispatch(setTool('brush')); - const handleSelectEraserTool = () => dispatch(setTool('eraser')); - const handleSelectColorPickerTool = () => dispatch(setTool('colorPicker')); - const handleFillRect = () => dispatch(addFillRect()); - const handleEraseBoundingBox = () => dispatch(addEraseRect()); + const handleSelectBrushTool = useCallback(() => { + dispatch(setTool('brush')); + }, [dispatch]); + const handleSelectEraserTool = useCallback(() => { + dispatch(setTool('eraser')); + }, [dispatch]); + const handleSelectColorPickerTool = useCallback(() => { + dispatch(setTool('colorPicker')); + }, [dispatch]); + const handleFillRect = useCallback(() => { + dispatch(addFillRect()); + }, [dispatch]); + const handleEraseBoundingBox = useCallback(() => { + dispatch(addEraseRect()); + }, [dispatch]); + const handleChangeBrushSize = useCallback( + (newSize: number) => { + dispatch(setBrushSize(newSize)); + }, + [dispatch] + ); + const handleChangeBrushColor = useCallback( + (newColor: RgbaColor) => { + dispatch(setBrushColor(newColor)); + }, + [dispatch] + ); return ( @@ -233,7 +256,7 @@ const IAICanvasToolChooserOptions = () => { label={t('unifiedCanvas.brushSize')} value={brushSize} withInput - onChange={(newSize) => dispatch(setBrushSize(newSize))} + onChange={handleChangeBrushSize} sliderNumberInputProps={{ max: 500 }} /> @@ -247,7 +270,7 @@ const IAICanvasToolChooserOptions = () => { dispatch(setBrushColor(newColor))} + onChange={handleChangeBrushColor} /> diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx index 617fedf0f0..7c04208349 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx @@ -27,7 +27,7 @@ import { import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider'; import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard'; import { isEqual } from 'lodash-es'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; import { @@ -151,7 +151,9 @@ const IAICanvasToolbar = () => { [canvasBaseLayer] ); - const handleSelectMoveTool = () => dispatch(setTool('move')); + const handleSelectMoveTool = useCallback(() => { + dispatch(setTool('move')); + }, [dispatch]); const handleClickResetCanvasView = useSingleAndDoubleClick( () => handleResetCanvasView(false), @@ -174,36 +176,39 @@ const IAICanvasToolbar = () => { ); }; - const handleResetCanvas = () => { + const handleResetCanvas = useCallback(() => { dispatch(resetCanvas()); - }; + }, [dispatch]); - const handleMergeVisible = () => { + const handleMergeVisible = useCallback(() => { dispatch(canvasMerged()); - }; + }, [dispatch]); - const handleSaveToGallery = () => { + const handleSaveToGallery = useCallback(() => { dispatch(canvasSavedToGallery()); - }; + }, [dispatch]); - const handleCopyImageToClipboard = () => { + const handleCopyImageToClipboard = useCallback(() => { if (!isClipboardAPIAvailable) { return; } dispatch(canvasCopiedToClipboard()); - }; + }, [dispatch, isClipboardAPIAvailable]); - const handleDownloadAsImage = () => { + const handleDownloadAsImage = useCallback(() => { dispatch(canvasDownloadedAsImage()); - }; + }, [dispatch]); - const handleChangeLayer = (v: string) => { - const newLayer = v as CanvasLayer; - dispatch(setLayer(newLayer)); - if (newLayer === 'mask' && !isMaskEnabled) { - dispatch(setIsMaskEnabled(true)); - } - }; + const handleChangeLayer = useCallback( + (v: string) => { + const newLayer = v as CanvasLayer; + dispatch(setLayer(newLayer)); + if (newLayer === 'mask' && !isMaskEnabled) { + dispatch(setIsMaskEnabled(true)); + } + }, + [dispatch, isMaskEnabled] + ); return ( { + const handleUndo = useCallback(() => { dispatch(undo()); - }; + }, [dispatch]); useHotkeys( ['meta+z', 'ctrl+z'], diff --git a/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx b/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx index 6bdf434d52..122b2b7d38 100644 --- a/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx +++ b/invokeai/frontend/web/src/features/changeBoardModal/components/ChangeBoardModal.tsx @@ -87,6 +87,11 @@ const ChangeBoardModal = () => { selectedBoard, ]); + const handleSetSelectedBoard = useCallback( + (v: string | null) => setSelectedBoard(v), + [] + ); + const cancelRef = useRef(null); return ( @@ -113,7 +118,7 @@ const ChangeBoardModal = () => { isFetching ? t('boards.loading') : t('boards.selectBoard') } disabled={isFetching} - onChange={(v) => setSelectedBoard(v)} + onChange={handleSetSelectedBoard} value={selectedBoard} data={data} /> diff --git a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx index 164fd01a1f..32334a9f88 100644 --- a/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx +++ b/invokeai/frontend/web/src/features/embedding/components/ParamEmbeddingPopover.tsx @@ -14,9 +14,9 @@ import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectI import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; import { forEach } from 'lodash-es'; import { PropsWithChildren, memo, useCallback, useMemo, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; import { useGetTextualInversionModelsQuery } from 'services/api/endpoints/models'; import { PARAMETERS_PANEL_WIDTH } from 'theme/util/constants'; -import { useTranslation } from 'react-i18next'; type Props = PropsWithChildren & { onSelect: (v: string) => void; @@ -78,6 +78,13 @@ const ParamEmbeddingPopover = (props: Props) => { [onSelect] ); + const filterFunc = useCallback( + (value: string, item: SelectItem) => + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || + item.value.toLowerCase().includes(value.toLowerCase().trim()), + [] + ); + return ( { itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} onDropdownClose={onClose} - filter={(value, item: SelectItem) => - item.label - ?.toLowerCase() - .includes(value.toLowerCase().trim()) || - item.value.toLowerCase().includes(value.toLowerCase().trim()) - } + filter={filterFunc} onChange={handleChange} /> )} diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx index 29211a7558..9c77500ef3 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx @@ -60,6 +60,13 @@ const BoardAutoAddSelect = () => { [dispatch] ); + const filterFunc = useCallback( + (value: string, item: SelectItem) => + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || + item.value.toLowerCase().includes(value.toLowerCase().trim()), + [] + ); + return ( { nothingFound={t('boards.noMatching')} itemComponent={IAIMantineSelectItemWithTooltip} disabled={!hasBoards || autoAssignBoardOnClick} - filter={(value, item: SelectItem) => - item.label?.toLowerCase().includes(value.toLowerCase().trim()) || - item.value.toLowerCase().includes(value.toLowerCase().trim()) - } + filter={filterFunc} onChange={handleChange} /> ); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx index 5dd6b94753..97e49b493c 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx @@ -90,6 +90,50 @@ const BoardContextMenu = ({ e.preventDefault(); }, []); + const renderMenuFunc = useCallback( + () => ( + + + } + isDisabled={isAutoAdd || autoAssignBoardOnClick} + onClick={handleSetAutoAdd} + > + {t('boards.menuItemAutoAdd')} + + {isBulkDownloadEnabled && ( + } onClickCapture={handleBulkDownload}> + {t('boards.downloadBoard')} + + )} + {!board && } + {board && ( + + )} + + + ), + [ + autoAssignBoardOnClick, + board, + boardName, + handleBulkDownload, + handleSetAutoAdd, + isAutoAdd, + isBulkDownloadEnabled, + setBoardToDelete, + skipEvent, + t, + ] + ); + return ( menuProps={{ size: 'sm', isLazy: true }} @@ -97,38 +141,7 @@ const BoardContextMenu = ({ bg: 'transparent', _hover: { bg: 'transparent' }, }} - renderMenu={() => ( - - - } - isDisabled={isAutoAdd || autoAssignBoardOnClick} - onClick={handleSetAutoAdd} - > - {t('boards.menuItemAutoAdd')} - - {isBulkDownloadEnabled && ( - } - onClickCapture={handleBulkDownload} - > - {t('boards.downloadBoard')} - - )} - {!board && } - {board && ( - - )} - - - )} + renderMenu={renderMenuFunc} > {children} diff --git a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx index 2eab78d118..015a206fa2 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GallerySettingsPopover.tsx @@ -61,6 +61,12 @@ const GallerySettingsPopover = () => { [dispatch] ); + const handleChangeAutoAssignBoardOnClick = useCallback( + (e: ChangeEvent) => + dispatch(autoAssignBoardOnClickChanged(e.target.checked)), + [dispatch] + ); + return ( { ) => - dispatch(autoAssignBoardOnClickChanged(e.target.checked)) - } + onChange={handleChangeAutoAssignBoardOnClick} /> diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx index 0c13b37f0c..6fc5765785 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/ImageContextMenu.tsx @@ -35,6 +35,34 @@ const ImageContextMenu = ({ imageDTO, children }: Props) => { e.preventDefault(); }, []); + const renderMenuFunc = useCallback(() => { + if (!imageDTO) { + return null; + } + + if (selectionCount > 1) { + return ( + + + + ); + } + + return ( + + + + ); + }, [imageDTO, selectionCount, skipEvent]); + return ( menuProps={{ size: 'sm', isLazy: true }} @@ -42,33 +70,7 @@ const ImageContextMenu = ({ imageDTO, children }: Props) => { bg: 'transparent', _hover: { bg: 'transparent' }, }} - renderMenu={() => { - if (!imageDTO) { - return null; - } - - if (selectionCount > 1) { - return ( - - - - ); - } - - return ( - - - - ); - }} + renderMenu={renderMenuFunc} > {children} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx index dc41a2ef2a..4b7997f2ef 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImageGrid.tsx @@ -20,6 +20,7 @@ import { useBoardTotal } from 'services/api/hooks/useBoardTotal'; import GalleryImage from './GalleryImage'; import ImageGridItemContainer from './ImageGridItemContainer'; import ImageGridListContainer from './ImageGridListContainer'; +import { EntityId } from '@reduxjs/toolkit'; const overlayScrollbarsConfig: UseOverlayScrollbarsParams = { defer: true, @@ -71,6 +72,13 @@ const GalleryImageGrid = () => { }); }, [areMoreAvailable, listImages, queryArgs, currentData?.ids.length]); + const itemContentFunc = useCallback( + (index: number, imageName: EntityId) => ( + + ), + [] + ); + useEffect(() => { // Initialize the gallery's custom scrollbar const { current: root } = rootRef; @@ -131,9 +139,7 @@ const GalleryImageGrid = () => { List: ImageGridListContainer, }} scrollerRef={setScroller} - itemContent={(index, imageName) => ( - - )} + itemContent={itemContentFunc} /> { key={index} label="LoRA" value={`${lora.lora.model_name} - ${lora.weight}`} - onClick={() => handleRecallLoRA(lora)} + onClick={handleRecallLoRA.bind(null, lora)} /> ); } @@ -289,7 +289,7 @@ const ImageMetadataActions = (props: Props) => { key={index} label="ControlNet" value={`${controlnet.control_model?.model_name} - ${controlnet.control_weight}`} - onClick={() => handleRecallControlNet(controlnet)} + onClick={handleRecallControlNet.bind(null, controlnet)} /> ))} {validIPAdapters.map((ipAdapter, index) => ( @@ -297,7 +297,7 @@ const ImageMetadataActions = (props: Props) => { key={index} label="IP Adapter" value={`${ipAdapter.ip_adapter_model?.model_name} - ${ipAdapter.weight}`} - onClick={() => handleRecallIPAdapter(ipAdapter)} + onClick={handleRecallIPAdapter.bind(null, ipAdapter)} /> ))} {validT2IAdapters.map((t2iAdapter, index) => ( @@ -305,7 +305,7 @@ const ImageMetadataActions = (props: Props) => { key={index} label="T2I Adapter" value={`${t2iAdapter.t2i_adapter_model?.model_name} - ${t2iAdapter.weight}`} - onClick={() => handleRecallT2IAdapter(t2iAdapter)} + onClick={handleRecallT2IAdapter.bind(null, t2iAdapter)} /> ))} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataItem.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataItem.tsx index c03fd26ba1..135159562b 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataItem.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataItem.tsx @@ -1,6 +1,6 @@ import { ExternalLinkIcon } from '@chakra-ui/icons'; import { Flex, IconButton, Link, Text, Tooltip } from '@chakra-ui/react'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { FaCopy } from 'react-icons/fa'; import { IoArrowUndoCircleOutline } from 'react-icons/io5'; @@ -27,6 +27,11 @@ const ImageMetadataItem = ({ }: MetadataItemProps) => { const { t } = useTranslation(); + const handleCopy = useCallback( + () => navigator.clipboard.writeText(value.toString()), + [value] + ); + if (!value) { return null; } @@ -53,7 +58,7 @@ const ImageMetadataItem = ({ size="xs" variant="ghost" fontSize={14} - onClick={() => navigator.clipboard.writeText(value.toString())} + onClick={handleCopy} /> )} diff --git a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx index 6048e4a159..e2fc841f15 100644 --- a/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx +++ b/invokeai/frontend/web/src/features/lora/components/ParamLoraSelect.tsx @@ -76,6 +76,13 @@ const ParamLoRASelect = () => { [dispatch, loraModels?.entities] ); + const filterFunc = useCallback( + (value: string, item: SelectItem) => + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || + item.value.toLowerCase().includes(value.toLowerCase().trim()), + [] + ); + if (loraModels?.ids.length === 0) { return ( @@ -94,10 +101,7 @@ const ParamLoRASelect = () => { nothingFound="No matching LoRAs" itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} - filter={(value, item: SelectItem) => - item.label?.toLowerCase().includes(value.toLowerCase().trim()) || - item.value.toLowerCase().includes(value.toLowerCase().trim()) - } + filter={filterFunc} onChange={handleChange} data-testid="add-lora" /> diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx index 96ef7c56a3..57cdc6a8db 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/CurrentImage/CurrentImageNode.tsx @@ -1,4 +1,4 @@ -import { useState, PropsWithChildren, memo } from 'react'; +import { useState, PropsWithChildren, memo, useCallback } from 'react'; import { useSelector } from 'react-redux'; import { createSelector } from '@reduxjs/toolkit'; import { Flex, Image, Text } from '@chakra-ui/react'; @@ -59,13 +59,13 @@ export default memo(CurrentImageNode); const Wrapper = (props: PropsWithChildren<{ nodeProps: NodeProps }>) => { const [isHovering, setIsHovering] = useState(false); - const handleMouseEnter = () => { + const handleMouseEnter = useCallback(() => { setIsHovering(true); - }; + }, []); - const handleMouseLeave = () => { + const handleMouseLeave = useCallback(() => { setIsHovering(false); - }; + }, []); return ( { nodeId, ]); + const renderMenuFunc = useCallback( + () => + !menuItems.length ? null : ( + + + {menuItems} + + + ), + [fieldTemplateTitle, label, menuItems, skipEvent, t] + ); + return ( menuProps={{ @@ -114,21 +132,7 @@ const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => { bg: 'transparent', _hover: { bg: 'transparent' }, }} - renderMenu={() => - !menuItems.length ? null : ( - - - {menuItems} - - - ) - } + renderMenu={renderMenuFunc} > {children} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAModelInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAModelInputField.tsx index 3ca87b3e85..712b1ff5a0 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAModelInputField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/LoRAModelInputField.tsx @@ -80,6 +80,13 @@ const LoRAModelInputFieldComponent = ( [dispatch, field.name, nodeId] ); + const filterFunc = useCallback( + (value: string, item: SelectItem) => + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || + item.value.toLowerCase().includes(value.toLowerCase().trim()), + [] + ); + if (loraModels?.ids.length === 0) { return ( @@ -101,10 +108,7 @@ const LoRAModelInputFieldComponent = ( nothingFound={t('models.noMatchingLoRAs')} itemComponent={IAIMantineSelectItemWithTooltip} disabled={data.length === 0} - filter={(value, item: SelectItem) => - item.label?.toLowerCase().includes(value.toLowerCase().trim()) || - item.value.toLowerCase().includes(value.toLowerCase().trim()) - } + filter={filterFunc} error={!selectedLoRAModel} onChange={handleChange} sx={{ diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/NumberInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/NumberInputField.tsx index 2afbec1df8..2b2763ca3e 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/NumberInputField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/NumberInputField.tsx @@ -19,7 +19,7 @@ import { IntegerPolymorphicInputFieldTemplate, IntegerPolymorphicInputFieldValue, } from 'features/nodes/types/types'; -import { memo, useEffect, useMemo, useState } from 'react'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; const NumberInputFieldComponent = ( props: FieldComponentProps< @@ -43,20 +43,23 @@ const NumberInputFieldComponent = ( [fieldTemplate.type] ); - const handleValueChanged = (v: string) => { - setValueAsString(v); - // This allows negatives and decimals e.g. '-123', `.5`, `-0.2`, etc. - if (!v.match(numberStringRegex)) { - // Cast the value to number. Floor it if it should be an integer. - dispatch( - fieldNumberValueChanged({ - nodeId, - fieldName: field.name, - value: isIntegerField ? Math.floor(Number(v)) : Number(v), - }) - ); - } - }; + const handleValueChanged = useCallback( + (v: string) => { + setValueAsString(v); + // This allows negatives and decimals e.g. '-123', `.5`, `-0.2`, etc. + if (!v.match(numberStringRegex)) { + // Cast the value to number. Floor it if it should be an integer. + dispatch( + fieldNumberValueChanged({ + nodeId, + fieldName: field.name, + value: isIntegerField ? Math.floor(Number(v)) : Number(v), + }) + ); + } + }, + [dispatch, field.name, isIntegerField, nodeId] + ); useEffect(() => { if ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight.tsx index 1711c9344a..18a5b52e99 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/BoundingBox/ParamBoundingBoxHeight.tsx @@ -6,7 +6,7 @@ import IAISlider from 'common/components/IAISlider'; import { roundToMultiple } from 'common/util/roundDownToMultiple'; import { isStagingSelector } from 'features/canvas/store/canvasSelectors'; import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -36,25 +36,28 @@ const ParamBoundingBoxWidth = () => { ? 1024 : 512; - const handleChangeHeight = (v: number) => { - dispatch( - setBoundingBoxDimensions({ - ...boundingBoxDimensions, - height: Math.floor(v), - }) - ); - if (aspectRatio) { - const newWidth = roundToMultiple(v * aspectRatio, 64); + const handleChangeHeight = useCallback( + (v: number) => { dispatch( setBoundingBoxDimensions({ - width: newWidth, + ...boundingBoxDimensions, height: Math.floor(v), }) ); - } - }; + if (aspectRatio) { + const newWidth = roundToMultiple(v * aspectRatio, 64); + dispatch( + setBoundingBoxDimensions({ + width: newWidth, + height: Math.floor(v), + }) + ); + } + }, + [aspectRatio, boundingBoxDimensions, dispatch] + ); - const handleResetHeight = () => { + const handleResetHeight = useCallback(() => { dispatch( setBoundingBoxDimensions({ ...boundingBoxDimensions, @@ -70,7 +73,7 @@ const ParamBoundingBoxWidth = () => { }) ); } - }; + }, [aspectRatio, boundingBoxDimensions, dispatch, initial]); return ( { const { t } = useTranslation(); - const handleChangeWidth = (v: number) => { - dispatch( - setBoundingBoxDimensions({ - ...boundingBoxDimensions, - width: Math.floor(v), - }) - ); - if (aspectRatio) { - const newHeight = roundToMultiple(v / aspectRatio, 64); + const handleChangeWidth = useCallback( + (v: number) => { dispatch( setBoundingBoxDimensions({ + ...boundingBoxDimensions, width: Math.floor(v), - height: newHeight, }) ); - } - }; + if (aspectRatio) { + const newHeight = roundToMultiple(v / aspectRatio, 64); + dispatch( + setBoundingBoxDimensions({ + width: Math.floor(v), + height: newHeight, + }) + ); + } + }, + [aspectRatio, boundingBoxDimensions, dispatch] + ); - const handleResetWidth = () => { + const handleResetWidth = useCallback(() => { dispatch( setBoundingBoxDimensions({ ...boundingBoxDimensions, @@ -70,7 +73,7 @@ const ParamBoundingBoxWidth = () => { }) ); } - }; + }, [aspectRatio, boundingBoxDimensions, dispatch, initial]); return ( { ); const { t } = useTranslation(); - const handleCoherenceModeChange = (v: string | null) => { - if (!v) { - return; - } + const handleCoherenceModeChange = useCallback( + (v: string | null) => { + if (!v) { + return; + } - dispatch(setCanvasCoherenceMode(v as CanvasCoherenceModeParam)); - }; + dispatch(setCanvasCoherenceMode(v as CanvasCoherenceModeParam)); + }, + [dispatch] + ); return ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx index 463d596a5c..3c3bc59c20 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx @@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import IAISlider from 'common/components/IAISlider'; import { setCanvasCoherenceSteps } from 'features/parameters/store/generationSlice'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const ParamCanvasCoherenceSteps = () => { @@ -13,6 +13,17 @@ const ParamCanvasCoherenceSteps = () => { ); const { t } = useTranslation(); + const handleChange = useCallback( + (v: number) => { + dispatch(setCanvasCoherenceSteps(v)); + }, + [dispatch] + ); + + const handleReset = useCallback(() => { + dispatch(setCanvasCoherenceSteps(20)); + }, [dispatch]); + return ( { step={1} sliderNumberInputProps={{ max: 999 }} value={canvasCoherenceSteps} - onChange={(v) => { - dispatch(setCanvasCoherenceSteps(v)); - }} + onChange={handleChange} withInput withSliderMarks withReset - handleReset={() => { - dispatch(setCanvasCoherenceSteps(20)); - }} + handleReset={handleReset} /> ); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx index e5311db3a6..7fc3096062 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx @@ -3,7 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import IAISlider from 'common/components/IAISlider'; import { setCanvasCoherenceStrength } from 'features/parameters/store/generationSlice'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const ParamCanvasCoherenceStrength = () => { @@ -13,6 +13,16 @@ const ParamCanvasCoherenceStrength = () => { ); const { t } = useTranslation(); + const handleChange = useCallback( + (v: number) => { + dispatch(setCanvasCoherenceStrength(v)); + }, + [dispatch] + ); + const handleReset = useCallback(() => { + dispatch(setCanvasCoherenceStrength(0.3)); + }, [dispatch]); + return ( { step={0.01} sliderNumberInputProps={{ max: 999 }} value={canvasCoherenceStrength} - onChange={(v) => { - dispatch(setCanvasCoherenceStrength(v)); - }} + onChange={handleChange} withInput withSliderMarks withReset - handleReset={() => { - dispatch(setCanvasCoherenceStrength(0.3)); - }} + handleReset={handleReset} /> ); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx index bbadbbabf0..013d4ad4a3 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx @@ -3,6 +3,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import IAISlider from 'common/components/IAISlider'; import { setMaskBlur } from 'features/parameters/store/generationSlice'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; export default function ParamMaskBlur() { @@ -12,6 +13,16 @@ export default function ParamMaskBlur() { ); const { t } = useTranslation(); + const handleChange = useCallback( + (v: number) => { + dispatch(setMaskBlur(v)); + }, + [dispatch] + ); + const handleReset = useCallback(() => { + dispatch(setMaskBlur(16)); + }, [dispatch]); + return ( { - dispatch(setMaskBlur(v)); - }} + onChange={handleChange} withInput withSliderMarks withReset - handleReset={() => { - dispatch(setMaskBlur(16)); - }} + handleReset={handleReset} /> ); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx index df0b2ebc90..b7490461b5 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx @@ -5,6 +5,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover'; import IAIMantineSelect from 'common/components/IAIMantineSelect'; import { setMaskBlurMethod } from 'features/parameters/store/generationSlice'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; type MaskBlurMethods = 'box' | 'gaussian'; @@ -21,12 +22,15 @@ export default function ParamMaskBlurMethod() { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const handleMaskBlurMethodChange = (v: string | null) => { - if (!v) { - return; - } - dispatch(setMaskBlurMethod(v as MaskBlurMethods)); - }; + const handleMaskBlurMethodChange = useCallback( + (v: string | null) => { + if (!v) { + return; + } + dispatch(setMaskBlurMethod(v as MaskBlurMethods)); + }, + [dispatch] + ); return ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx index 1a0e497739..06594b7b1b 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx @@ -10,7 +10,7 @@ import { BoundingBoxScale, } from 'features/canvas/store/canvasTypes'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createSelector( @@ -31,9 +31,12 @@ const ParamScaleBeforeProcessing = () => { const { t } = useTranslation(); - const handleChangeBoundingBoxScaleMethod = (v: string) => { - dispatch(setBoundingBoxScaleMethod(v as BoundingBoxScale)); - }; + const handleChangeBoundingBoxScaleMethod = useCallback( + (v: string) => { + dispatch(setBoundingBoxScaleMethod(v as BoundingBoxScale)); + }, + [dispatch] + ); return ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaledHeight.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaledHeight.tsx index 6d0ac52b2f..62b7c899f2 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaledHeight.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Canvas/InfillAndScaling/ParamScaledHeight.tsx @@ -6,7 +6,7 @@ import { roundToMultiple } from 'common/util/roundDownToMultiple'; import { canvasSelector } from 'features/canvas/store/canvasSelectors'; import { setScaledBoundingBoxDimensions } from 'features/canvas/store/canvasSlice'; import { generationSelector } from 'features/parameters/store/generationSelectors'; -import { memo } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const selector = createSelector( @@ -36,23 +36,26 @@ const ParamScaledHeight = () => { const { t } = useTranslation(); - const handleChangeScaledHeight = (v: number) => { - let newWidth = scaledBoundingBoxDimensions.width; - const newHeight = Math.floor(v); + const handleChangeScaledHeight = useCallback( + (v: number) => { + let newWidth = scaledBoundingBoxDimensions.width; + const newHeight = Math.floor(v); - if (aspectRatio) { - newWidth = roundToMultiple(newHeight * aspectRatio, 64); - } + if (aspectRatio) { + newWidth = roundToMultiple(newHeight * aspectRatio, 64); + } - dispatch( - setScaledBoundingBoxDimensions({ - width: newWidth, - height: newHeight, - }) - ); - }; + dispatch( + setScaledBoundingBoxDimensions({ + width: newWidth, + height: newHeight, + }) + ); + }, + [aspectRatio, dispatch, scaledBoundingBoxDimensions.width] + ); - const handleResetScaledHeight = () => { + const handleResetScaledHeight = useCallback(() => { let resetWidth = scaledBoundingBoxDimensions.width; const resetHeight = Math.floor(initial); @@ -66,7 +69,7 @@ const ParamScaledHeight = () => { height: resetHeight, }) ); - }; + }, [aspectRatio, dispatch, initial, scaledBoundingBoxDimensions.width]); return ( { const { t } = useTranslation(); - const handleChangeScaledWidth = (v: number) => { - const newWidth = Math.floor(v); - let newHeight = scaledBoundingBoxDimensions.height; + const handleChangeScaledWidth = useCallback( + (v: number) => { + const newWidth = Math.floor(v); + let newHeight = scaledBoundingBoxDimensions.height; - if (aspectRatio) { - newHeight = roundToMultiple(newWidth / aspectRatio, 64); - } + if (aspectRatio) { + newHeight = roundToMultiple(newWidth / aspectRatio, 64); + } - dispatch( - setScaledBoundingBoxDimensions({ - width: newWidth, - height: newHeight, - }) - ); - }; + dispatch( + setScaledBoundingBoxDimensions({ + width: newWidth, + height: newHeight, + }) + ); + }, + [aspectRatio, dispatch, scaledBoundingBoxDimensions.height] + ); - const handleResetScaledWidth = () => { + const handleResetScaledWidth = useCallback(() => { const resetWidth = Math.floor(initial); let resetHeight = scaledBoundingBoxDimensions.height; @@ -66,7 +69,7 @@ const ParamScaledWidth = () => { height: resetHeight, }) ); - }; + }, [aspectRatio, dispatch, initial, scaledBoundingBoxDimensions.height]); return ( { + dispatch(setAspectRatio(ratio.value)); + dispatch(setShouldLockAspectRatio(false)); + }, + [dispatch] + ); + return ( {aspectRatios.map((ratio) => ( @@ -39,10 +48,7 @@ export default function ParamAspectRatio() { isDisabled={ activeTabName === 'img2img' ? !shouldFitToWidthHeight : false } - onClick={() => { - dispatch(setAspectRatio(ratio.value)); - dispatch(setShouldLockAspectRatio(false)); - }} + onClick={handleClick.bind(null, ratio)} > {ratio.name} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageFit.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageFit.tsx index 03f502846c..0305424ef0 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageFit.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageFit.tsx @@ -2,7 +2,7 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAISwitch from 'common/components/IAISwitch'; import { setShouldFitToWidthHeight } from 'features/parameters/store/generationSlice'; -import { ChangeEvent } from 'react'; +import { ChangeEvent, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; export default function ImageToImageFit() { @@ -12,8 +12,12 @@ export default function ImageToImageFit() { (state: RootState) => state.generation.shouldFitToWidthHeight ); - const handleChangeFit = (e: ChangeEvent) => - dispatch(setShouldFitToWidthHeight(e.target.checked)); + const handleChangeFit = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldFitToWidthHeight(e.target.checked)); + }, + [dispatch] + ); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Seed/ParamSeed.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Seed/ParamSeed.tsx index 481fe27964..4dc86fb8ee 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Seed/ParamSeed.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Seed/ParamSeed.tsx @@ -3,6 +3,7 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAINumberInput from 'common/components/IAINumberInput'; import { setSeed } from 'features/parameters/store/generationSlice'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; export default function ParamSeed() { @@ -18,7 +19,10 @@ export default function ParamSeed() { const dispatch = useAppDispatch(); - const handleChangeSeed = (v: number) => dispatch(setSeed(v)); + const handleChangeSeed = useCallback( + (v: number) => dispatch(setSeed(v)), + [dispatch] + ); return ( { (state: RootState) => state.generation.shouldRandomizeSeed ); - const handleChangeShouldRandomizeSeed = (e: ChangeEvent) => - dispatch(setShouldRandomizeSeed(e.target.checked)); + const handleChangeShouldRandomizeSeed = useCallback( + (e: ChangeEvent) => + dispatch(setShouldRandomizeSeed(e.target.checked)), + [dispatch] + ); return ( - dispatch(setSeed(randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX))); + const handleClickRandomizeSeed = useCallback( + () => dispatch(setSeed(randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX))), + [dispatch] + ); return ( { + dispatch(setHorizontalSymmetrySteps(v)); + }, + [dispatch] + ); + const handleReset = useCallback(() => { + dispatch(setHorizontalSymmetrySteps(0)); + }, [dispatch]); + return ( dispatch(setHorizontalSymmetrySteps(v))} + onChange={handleChange} min={0} max={steps} step={1} withInput withSliderMarks withReset - handleReset={() => dispatch(setHorizontalSymmetrySteps(0))} + handleReset={handleReset} /> ); } diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryToggle.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryToggle.tsx index 59386ff526..5de62fd9b2 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryToggle.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryToggle.tsx @@ -2,6 +2,7 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAISwitch from 'common/components/IAISwitch'; import { setShouldUseSymmetry } from 'features/parameters/store/generationSlice'; +import { ChangeEvent, useCallback } from 'react'; export default function ParamSymmetryToggle() { const shouldUseSymmetry = useAppSelector( @@ -9,12 +10,18 @@ export default function ParamSymmetryToggle() { ); const dispatch = useAppDispatch(); + const handleChange = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldUseSymmetry(e.target.checked)); + }, + [dispatch] + ); return ( dispatch(setShouldUseSymmetry(e.target.checked))} + onChange={handleChange} /> ); } diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryVertical.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryVertical.tsx index c8ddb46a3a..90656272ec 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryVertical.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Symmetry/ParamSymmetryVertical.tsx @@ -2,6 +2,7 @@ import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAISlider from 'common/components/IAISlider'; import { setVerticalSymmetrySteps } from 'features/parameters/store/generationSlice'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; export default function ParamSymmetryVertical() { @@ -15,18 +16,28 @@ export default function ParamSymmetryVertical() { const { t } = useTranslation(); + const handleChange = useCallback( + (v: number) => { + dispatch(setVerticalSymmetrySteps(v)); + }, + [dispatch] + ); + const handleReset = useCallback(() => { + dispatch(setVerticalSymmetrySteps(0)); + }, [dispatch]); + return ( dispatch(setVerticalSymmetrySteps(v))} + onChange={handleChange} min={0} max={steps} step={1} withInput withSliderMarks withReset - handleReset={() => dispatch(setVerticalSymmetrySteps(0))} + handleReset={handleReset} /> ); } diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Upscale/ParamRealESRGANModel.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Upscale/ParamRealESRGANModel.tsx index 5c1bc09717..8a5ac94fac 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Upscale/ParamRealESRGANModel.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Upscale/ParamRealESRGANModel.tsx @@ -7,6 +7,7 @@ import { ESRGANModelName, esrganModelNameChanged, } from 'features/parameters/store/postprocessingSlice'; +import { useCallback } from 'react'; export const ESRGAN_MODEL_NAMES: SelectItem[] = [ { @@ -42,8 +43,10 @@ export default function ParamESRGANModel() { const dispatch = useAppDispatch(); - const handleChange = (v: string) => - dispatch(esrganModelNameChanged(v as ESRGANModelName)); + const handleChange = useCallback( + (v: string) => dispatch(esrganModelNameChanged(v as ESRGANModelName)), + [dispatch] + ); return ( { + const handleShouldConcatPromptChange = useCallback(() => { dispatch(setShouldConcatSDXLStylePrompt(!shouldConcatSDXLStylePrompt)); - }; + }, [dispatch, shouldConcatSDXLStylePrompt]); return ( ) => { - dispatch(setShouldUseSDXLRefiner(e.target.checked)); - }; + const handleUseSDXLRefinerChange = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldUseSDXLRefiner(e.target.checked)); + }, + [dispatch] + ); return ( { useFeatureStatus('localization').isFeatureEnabled; const language = useAppSelector(languageSelector); + const handleChangeShouldConfirmOnDelete = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldConfirmOnDelete(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldUseNSFWChecker = useCallback( + (e: ChangeEvent) => { + dispatch(shouldUseNSFWCheckerChanged(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldUseWatermarker = useCallback( + (e: ChangeEvent) => { + dispatch(shouldUseWatermarkerChanged(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldUseSliders = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldUseSliders(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldShowProgressInViewer = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldShowProgressInViewer(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldAntialiasProgressImage = useCallback( + (e: ChangeEvent) => { + dispatch(shouldAntialiasProgressImageChanged(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldAutoChangeDimensions = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldAutoChangeDimensions(e.target.checked)); + }, + [dispatch] + ); + const handleChangeShouldEnableInformationalPopovers = useCallback( + (e: ChangeEvent) => { + dispatch(setShouldEnableInformationalPopovers(e.target.checked)); + }, + [dispatch] + ); + const handleChangeEnableImageDebugging = useCallback( + (e: ChangeEvent) => { + dispatch(setEnableImageDebugging(e.target.checked)); + }, + [dispatch] + ); + return ( <> {cloneElement(children, { @@ -235,9 +290,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { ) => - dispatch(setShouldConfirmOnDelete(e.target.checked)) - } + onChange={handleChangeShouldConfirmOnDelete} /> @@ -248,17 +301,13 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { label="Enable NSFW Checker" isDisabled={!isNSFWCheckerAvailable} isChecked={shouldUseNSFWChecker} - onChange={(e: ChangeEvent) => - dispatch(shouldUseNSFWCheckerChanged(e.target.checked)) - } + onChange={handleChangeShouldUseNSFWChecker} /> ) => - dispatch(shouldUseWatermarkerChanged(e.target.checked)) - } + onChange={handleChangeShouldUseWatermarker} /> @@ -272,32 +321,22 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { ) => - dispatch(setShouldUseSliders(e.target.checked)) - } + onChange={handleChangeShouldUseSliders} /> ) => - dispatch(setShouldShowProgressInViewer(e.target.checked)) - } + onChange={handleChangeShouldShowProgressInViewer} /> ) => - dispatch( - shouldAntialiasProgressImageChanged(e.target.checked) - ) - } + onChange={handleChangeShouldAntialiasProgressImage} /> ) => - dispatch(setShouldAutoChangeDimensions(e.target.checked)) - } + onChange={handleChangeShouldAutoChangeDimensions} /> {shouldShowLocalizationToggle && ( { ) => - dispatch( - setShouldEnableInformationalPopovers(e.target.checked) - ) - } + onChange={handleChangeShouldEnableInformationalPopovers} /> @@ -340,9 +375,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { ) => - dispatch(setEnableImageDebugging(e.target.checked)) - } + onChange={handleChangeEnableImageDebugging} /> )} diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx index b18a069cfa..c999214841 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx @@ -1,7 +1,7 @@ import { Flex } from '@chakra-ui/layout'; import { Portal } from '@chakra-ui/portal'; import IAIIconButton from 'common/components/IAIIconButton'; -import { RefObject, memo } from 'react'; +import { RefObject, memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { MdPhotoLibrary } from 'react-icons/md'; import { ImperativePanelHandle } from 'react-resizable-panels'; @@ -17,9 +17,9 @@ const FloatingGalleryButton = ({ }: Props) => { const { t } = useTranslation(); - const handleShowGallery = () => { + const handleShowGallery = useCallback(() => { galleryPanelRef.current?.expand(); - }; + }, [galleryPanelRef]); if (!isGalleryCollapsed) { return null; diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx index da4d409943..49a6e120ab 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx @@ -3,7 +3,7 @@ import IAIIconButton from 'common/components/IAIIconButton'; import CancelCurrentQueueItemButton from 'features/queue/components/CancelCurrentQueueItemButton'; import ClearQueueButton from 'features/queue/components/ClearQueueButton'; import QueueBackButton from 'features/queue/components/QueueBackButton'; -import { RefObject, memo } from 'react'; +import { RefObject, memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { FaSlidersH } from 'react-icons/fa'; @@ -25,9 +25,9 @@ const FloatingSidePanelButtons = ({ }: Props) => { const { t } = useTranslation(); - const handleShowSidePanel = () => { + const handleShowSidePanel = useCallback(() => { sidePanelRef.current?.expand(); - }; + }, [sidePanelRef]); if (!isSidePanelCollapsed) { return null; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AddModels.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AddModels.tsx index 0fa46ec57f..98ab96dea9 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AddModels.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AddModels.tsx @@ -1,6 +1,6 @@ import { ButtonGroup, Flex } from '@chakra-ui/react'; import IAIButton from 'common/components/IAIButton'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import AdvancedAddModels from './AdvancedAddModels'; import SimpleAddModels from './SimpleAddModels'; @@ -8,6 +8,11 @@ export default function AddModels() { const [addModelMode, setAddModelMode] = useState<'simple' | 'advanced'>( 'simple' ); + const handleAddModelSimple = useCallback(() => setAddModelMode('simple'), []); + const handleAddModelAdvanced = useCallback( + () => setAddModelMode('advanced'), + [] + ); return ( setAddModelMode('simple')} + onClick={handleAddModelSimple} > Simple setAddModelMode('advanced')} + onClick={handleAddModelAdvanced} > Advanced diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx index 94a65a9122..f2d5efb565 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx @@ -6,7 +6,7 @@ import IAIMantineTextInput from 'common/components/IAIMantineInput'; import IAISimpleCheckbox from 'common/components/IAISimpleCheckbox'; import { addToast } from 'features/system/store/systemSlice'; import { makeToast } from 'features/system/util/makeToast'; -import { useState } from 'react'; +import { FocusEventHandler, useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAddMainModelsMutation } from 'services/api/endpoints/models'; import { CheckpointModelConfig } from 'services/api/types'; @@ -83,6 +83,27 @@ export default function AdvancedAddCheckpoint( }); }; + const handleBlurModelLocation: FocusEventHandler = + useCallback( + (e) => { + if (advancedAddCheckpointForm.values['model_name'] === '') { + const modelName = getModelName(e.currentTarget.value); + if (modelName) { + advancedAddCheckpointForm.setFieldValue( + 'model_name', + modelName as string + ); + } + } + }, + [advancedAddCheckpointForm] + ); + + const handleChangeUseCustomConfig = useCallback( + () => setUseCustomConfig((prev) => !prev), + [] + ); + return ( @@ -104,17 +125,7 @@ export default function AdvancedAddCheckpoint( label={t('modelManager.modelLocation')} required {...advancedAddCheckpointForm.getInputProps('path')} - onBlur={(e) => { - if (advancedAddCheckpointForm.values['model_name'] === '') { - const modelName = getModelName(e.currentTarget.value); - if (modelName) { - advancedAddCheckpointForm.setFieldValue( - 'model_name', - modelName as string - ); - } - } - }} + onBlur={handleBlurModelLocation} /> setUseCustomConfig(!useCustomConfig)} + onChange={handleChangeUseCustomConfig} label={t('modelManager.useCustomConfig')} /> diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx index 7c22b4b2ac..2a087681c2 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx @@ -12,6 +12,7 @@ import { setAdvancedAddScanModel } from '../../store/modelManagerSlice'; import BaseModelSelect from '../shared/BaseModelSelect'; import ModelVariantSelect from '../shared/ModelVariantSelect'; import { getModelName } from './util'; +import { FocusEventHandler, useCallback } from 'react'; type AdvancedAddDiffusersProps = { model_path?: string; @@ -74,6 +75,22 @@ export default function AdvancedAddDiffusers(props: AdvancedAddDiffusersProps) { }); }; + const handleBlurModelLocation: FocusEventHandler = + useCallback( + (e) => { + if (advancedAddDiffusersForm.values['model_name'] === '') { + const modelName = getModelName(e.currentTarget.value, false); + if (modelName) { + advancedAddDiffusersForm.setFieldValue( + 'model_name', + modelName as string + ); + } + } + }, + [advancedAddDiffusersForm] + ); + return ( @@ -96,17 +113,7 @@ export default function AdvancedAddDiffusers(props: AdvancedAddDiffusersProps) { label={t('modelManager.modelLocation')} placeholder={t('modelManager.modelLocationValidationMsg')} {...advancedAddDiffusersForm.getInputProps('path')} - onBlur={(e) => { - if (advancedAddDiffusersForm.values['model_name'] === '') { - const modelName = getModelName(e.currentTarget.value, false); - if (modelName) { - advancedAddDiffusersForm.setFieldValue( - 'model_name', - modelName as string - ); - } - } - }} + onBlur={handleBlurModelLocation} /> ('diffusers'); const { t } = useTranslation(); + const handleChange = useCallback((v: string | null) => { + if (!v) { + return; + } + setAdvancedAddMode(v as ManualAddMode); + }, []); return ( @@ -25,12 +31,7 @@ export default function AdvancedAddModels() { label={t('modelManager.modelType')} value={advancedAddMode} data={advancedAddModeData} - onChange={(v) => { - if (!v) { - return; - } - setAdvancedAddMode(v as ManualAddMode); - }} + onChange={handleChange} /> dispatch(setAdvancedAddScanModel(model)), + [dispatch] + ); + const renderModels = ({ models, showActions = true, @@ -140,7 +145,7 @@ export default function FoundModelsList() { {t('modelManager.quickAdd')} dispatch(setAdvancedAddScanModel(model))} + onClick={handleClickSetAdvanced.bind(null, model)} isLoading={isLoading} > {t('modelManager.advanced')} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx index 615c76b71e..17f4467af5 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx @@ -4,7 +4,7 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIIconButton from 'common/components/IAIIconButton'; import IAIMantineSelect from 'common/components/IAIMantineSelect'; import { motion } from 'framer-motion'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { FaTimes } from 'react-icons/fa'; import { setAdvancedAddScanModel } from '../../store/modelManagerSlice'; import AdvancedAddCheckpoint from './AdvancedAddCheckpoint'; @@ -35,6 +35,23 @@ export default function ScanAdvancedAddModels() { const dispatch = useAppDispatch(); + const handleClickSetAdvanced = useCallback( + () => dispatch(setAdvancedAddScanModel(null)), + [dispatch] + ); + + const handleChangeAddMode = useCallback((v: string | null) => { + if (!v) { + return; + } + setAdvancedAddMode(v as ManualAddMode); + if (v === 'checkpoint') { + setIsCheckpoint(true); + } else { + setIsCheckpoint(false); + } + }, []); + if (!advancedAddScanModel) { return null; } @@ -68,7 +85,7 @@ export default function ScanAdvancedAddModels() { } aria-label={t('modelManager.closeAdvanced')} - onClick={() => dispatch(setAdvancedAddScanModel(null))} + onClick={handleClickSetAdvanced} size="sm" /> @@ -76,17 +93,7 @@ export default function ScanAdvancedAddModels() { label={t('modelManager.modelType')} value={advancedAddMode} data={advancedAddModeData} - onChange={(v) => { - if (!v) { - return; - } - setAdvancedAddMode(v as ManualAddMode); - if (v === 'checkpoint') { - setIsCheckpoint(true); - } else { - setIsCheckpoint(false); - } - }} + onChange={handleChangeAddMode} /> {isCheckpoint ? ( { + const scanAgainHandler = useCallback(() => { refetchFoundModels(); - }; + }, [refetchFoundModels]); + + const handleClickClearCheckpointFolder = useCallback(() => { + dispatch(setSearchFolder(null)); + dispatch(setAdvancedAddScanModel(null)); + }, [dispatch]); return ( } size="sm" - onClick={() => { - dispatch(setSearchFolder(null)); - dispatch(setAdvancedAddScanModel(null)); - }} + onClick={handleClickClearCheckpointFolder} isDisabled={!searchFolder} colorScheme="red" /> diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ImportModelsPanel.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ImportModelsPanel.tsx index b0a8c124eb..acf6b705db 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ImportModelsPanel.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ImportModelsPanel.tsx @@ -1,6 +1,6 @@ import { ButtonGroup, Flex } from '@chakra-ui/react'; import IAIButton from 'common/components/IAIButton'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import AddModels from './AddModelsPanel/AddModels'; import ScanModels from './AddModelsPanel/ScanModels'; @@ -11,11 +11,14 @@ export default function ImportModelsPanel() { const [addModelTab, setAddModelTab] = useState('add'); const { t } = useTranslation(); + const handleClickAddTab = useCallback(() => setAddModelTab('add'), []); + const handleClickScanTab = useCallback(() => setAddModelTab('scan'), []); + return ( setAddModelTab('add')} + onClick={handleClickAddTab} isChecked={addModelTab == 'add'} size="sm" width="100%" @@ -23,7 +26,7 @@ export default function ImportModelsPanel() { {t('modelManager.addModel')} setAddModelTab('scan')} + onClick={handleClickScanTab} isChecked={addModelTab == 'scan'} size="sm" width="100%" diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/MergeModelsPanel.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/MergeModelsPanel.tsx index e5c68ba6cf..f9132517c2 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/MergeModelsPanel.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/MergeModelsPanel.tsx @@ -9,7 +9,7 @@ import IAISlider from 'common/components/IAISlider'; import { addToast } from 'features/system/store/systemSlice'; import { makeToast } from 'features/system/util/makeToast'; import { pickBy } from 'lodash-es'; -import { useMemo, useState } from 'react'; +import { ChangeEvent, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ALL_BASE_MODELS } from 'services/api/constants'; import { @@ -94,13 +94,58 @@ export default function MergeModelsPanel() { modelsMap[baseModel as keyof typeof modelsMap] ).filter((model) => model !== modelOne && model !== modelTwo); - const handleBaseModelChange = (v: string) => { + const handleBaseModelChange = useCallback((v: string) => { setBaseModel(v as BaseModelType); setModelOne(null); setModelTwo(null); - }; + }, []); - const mergeModelsHandler = () => { + const handleChangeModelOne = useCallback((v: string) => { + setModelOne(v); + }, []); + const handleChangeModelTwo = useCallback((v: string) => { + setModelTwo(v); + }, []); + const handleChangeModelThree = useCallback((v: string) => { + if (!v) { + setModelThree(null); + setModelMergeInterp('add_difference'); + } else { + setModelThree(v); + setModelMergeInterp('weighted_sum'); + } + }, []); + const handleChangeMergedModelName = useCallback( + (e: ChangeEvent) => setMergedModelName(e.target.value), + [] + ); + const handleChangeModelMergeAlpha = useCallback( + (v: number) => setModelMergeAlpha(v), + [] + ); + const handleResetModelMergeAlpha = useCallback( + () => setModelMergeAlpha(0.5), + [] + ); + const handleChangeMergeInterp = useCallback( + (v: MergeInterpolationMethods) => setModelMergeInterp(v), + [] + ); + const handleChangeMergeSaveLocType = useCallback( + (v: 'root' | 'custom') => setModelMergeSaveLocType(v), + [] + ); + const handleChangeMergeCustomSaveLoc = useCallback( + (e: ChangeEvent) => + setModelMergeCustomSaveLoc(e.target.value), + [] + ); + const handleChangeModelMergeForce = useCallback( + (e: ChangeEvent) => setModelMergeForce(e.target.checked), + [] + ); + + const mergeModelsHandler = useCallback(() => { const models_names: string[] = []; let modelsToMerge: (string | null)[] = [modelOne, modelTwo, modelThree]; @@ -150,7 +195,21 @@ export default function MergeModelsPanel() { ); } }); - }; + }, [ + baseModel, + dispatch, + mergeModels, + mergedModelName, + modelMergeAlpha, + modelMergeCustomSaveLoc, + modelMergeForce, + modelMergeInterp, + modelMergeSaveLocType, + modelOne, + modelThree, + modelTwo, + t, + ]); return ( @@ -180,7 +239,7 @@ export default function MergeModelsPanel() { value={modelOne} placeholder={t('modelManager.selectModel')} data={modelOneList} - onChange={(v) => setModelOne(v)} + onChange={handleChangeModelOne} /> setModelTwo(v)} + onChange={handleChangeModelTwo} /> { - if (!v) { - setModelThree(null); - setModelMergeInterp('add_difference'); - } else { - setModelThree(v); - setModelMergeInterp('weighted_sum'); - } - }} + onChange={handleChangeModelThree} /> setMergedModelName(e.target.value)} + onChange={handleChangeMergedModelName} /> setModelMergeAlpha(v)} + onChange={handleChangeModelMergeAlpha} withInput withReset - handleReset={() => setModelMergeAlpha(0.5)} + handleReset={handleResetModelMergeAlpha} withSliderMarks /> @@ -257,10 +308,7 @@ export default function MergeModelsPanel() { {t('modelManager.interpolationType')} - setModelMergeInterp(v)} - > + {modelThree === null ? ( <> @@ -305,7 +353,7 @@ export default function MergeModelsPanel() { setModelMergeSaveLocType(v)} + onChange={handleChangeMergeSaveLocType} > @@ -323,7 +371,7 @@ export default function MergeModelsPanel() { setModelMergeCustomSaveLoc(e.target.value)} + onChange={handleChangeMergeCustomSaveLoc} /> )} @@ -331,7 +379,7 @@ export default function MergeModelsPanel() { setModelMergeForce(e.target.checked)} + onChange={handleChangeModelMergeForce} fontWeight="500" /> diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx index f4943d3ce1..e6a8abbaaf 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx @@ -59,6 +59,11 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) { }, }); + const handleChangeUseCustomConfig = useCallback( + () => setUseCustomConfig((prev) => !prev), + [] + ); + const editModelFormSubmitHandler = useCallback( (values: CheckpointModelConfig) => { const responseBody = { @@ -181,7 +186,7 @@ export default function CheckpointModelEdit(props: CheckpointModelEditProps) { )} setUseCustomConfig(!useCustomConfig)} + onChange={handleChangeUseCustomConfig} label="Use Custom Config" /> diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelConvert.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelConvert.tsx index 56dcdfd52f..1b92981bda 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelConvert.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelConvert.tsx @@ -14,7 +14,7 @@ import IAIAlertDialog from 'common/components/IAIAlertDialog'; import IAIButton from 'common/components/IAIButton'; import IAIInput from 'common/components/IAIInput'; import { addToast } from 'features/system/store/systemSlice'; -import { useEffect, useState } from 'react'; +import { ChangeEvent, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useConvertMainModelsMutation } from 'services/api/endpoints/models'; @@ -42,11 +42,21 @@ export default function ModelConvert(props: ModelConvertProps) { setSaveLocation('InvokeAIRoot'); }, [model]); - const modelConvertCancelHandler = () => { + const modelConvertCancelHandler = useCallback(() => { setSaveLocation('InvokeAIRoot'); - }; + }, []); - const modelConvertHandler = () => { + const handleChangeSaveLocation = useCallback((v: string) => { + setSaveLocation(v as SaveLocation); + }, []); + const handleChangeCustomSaveLocation = useCallback( + (e: ChangeEvent) => { + setCustomSaveLocation(e.target.value); + }, + [] + ); + + const modelConvertHandler = useCallback(() => { const queryArg = { base_model: model.base_model, model_name: model.model_name, @@ -101,7 +111,15 @@ export default function ModelConvert(props: ModelConvertProps) { ) ); }); - }; + }, [ + convertModel, + customSaveLocation, + dispatch, + model.base_model, + model.model_name, + saveLocation, + t, + ]); return ( {t('modelManager.convertToDiffusersSaveLocation')} - setSaveLocation(v as SaveLocation)} - > + @@ -162,9 +177,7 @@ export default function ModelConvert(props: ModelConvertProps) { { - setCustomSaveLocation(e.target.value); - }} + onChange={handleChangeCustomSaveLocation} width="full" /> diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx index a77d58d07f..879aea0f6c 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx @@ -100,7 +100,7 @@ const ModelList = (props: ModelListProps) => { setModelFormatFilter('all')} + onClick={setModelFormatFilter.bind(null, 'all')} isChecked={modelFormatFilter === 'all'} size="sm" > @@ -108,35 +108,35 @@ const ModelList = (props: ModelListProps) => { setModelFormatFilter('diffusers')} + onClick={setModelFormatFilter.bind(null, 'diffusers')} isChecked={modelFormatFilter === 'diffusers'} > {t('modelManager.diffusersModels')} setModelFormatFilter('checkpoint')} + onClick={setModelFormatFilter.bind(null, 'checkpoint')} isChecked={modelFormatFilter === 'checkpoint'} > {t('modelManager.checkpointModels')} setModelFormatFilter('onnx')} + onClick={setModelFormatFilter.bind(null, 'onnx')} isChecked={modelFormatFilter === 'onnx'} > {t('modelManager.onnxModels')} setModelFormatFilter('olive')} + onClick={setModelFormatFilter.bind(null, 'olive')} isChecked={modelFormatFilter === 'olive'} > {t('modelManager.oliveModels')} setModelFormatFilter('lora')} + onClick={setModelFormatFilter.bind(null, 'lora')} isChecked={modelFormatFilter === 'lora'} > {t('modelManager.loraModels')} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx index 33ee345ef7..c58abaaae5 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx @@ -4,6 +4,7 @@ import IAIButton from 'common/components/IAIButton'; import IAIIconButton from 'common/components/IAIIconButton'; import { addToast } from 'features/system/store/systemSlice'; import { makeToast } from 'features/system/util/makeToast'; +import { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { FaSync } from 'react-icons/fa'; import { useSyncModelsMutation } from 'services/api/endpoints/models'; @@ -19,7 +20,7 @@ export default function SyncModelsButton(props: SyncModelsButtonProps) { const [syncModels, { isLoading }] = useSyncModelsMutation(); - const syncModelsHandler = () => { + const syncModelsHandler = useCallback(() => { syncModels() .unwrap() .then((_) => { @@ -44,7 +45,7 @@ export default function SyncModelsButton(props: SyncModelsButtonProps) { ); } }); - }; + }, [dispatch, syncModels, t]); return !iconMode ? ( Date: Sun, 12 Nov 2023 19:03:39 -0500 Subject: [PATCH 16/24] Update config.py Co-authored-by: psychedelicious <4822129+psychedelicious@users.noreply.github.com> --- invokeai/backend/model_manager/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/backend/model_manager/config.py b/invokeai/backend/model_manager/config.py index ff835b1f3f..a4ca0b0de2 100644 --- a/invokeai/backend/model_manager/config.py +++ b/invokeai/backend/model_manager/config.py @@ -114,7 +114,7 @@ class ModelConfigBase(BaseModel): current_hash: Optional[str] = Field( description="current fasthash of model contents", default=None ) # if model is converted or otherwise modified, this will hold updated hash - description: Optional[str] = Field(None) + description: Optional[str] = Field(default=None) source: Optional[str] = Field(description="Model download source (URL or repo_id)", default=None) model_config = ConfigDict( From 04d8f2dfea201cf47766e35bfdc5ba84070d02a0 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:06:04 +1100 Subject: [PATCH 17/24] fix(backend): fix controlnet zip len Do not use `strict=True` when scaling controlnet conditioning. When using `guess_mode` (e.g. `more_control` or `more_prompt`), `down_block_res_samples` and `scales` are zipped. These two objects are of different lengths, so using zip's strict mode raises an error. In testing, `len(scales) === len(down_block_res_samples) + 1`. It appears this behaviour is intentional, as the final "extra" item in `scales` is used immediately afterwards. --- invokeai/backend/util/hotfixes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/backend/util/hotfixes.py b/invokeai/backend/util/hotfixes.py index 835575c7a1..b0e31796d5 100644 --- a/invokeai/backend/util/hotfixes.py +++ b/invokeai/backend/util/hotfixes.py @@ -748,7 +748,7 @@ class ControlNetModel(ModelMixin, ConfigMixin, FromOriginalControlnetMixin): scales = scales * conditioning_scale down_block_res_samples = [ - sample * scale for sample, scale in zip(down_block_res_samples, scales, strict=True) + sample * scale for sample, scale in zip(down_block_res_samples, scales, strict=False) ] mid_block_res_sample = mid_block_res_sample * scales[-1] # last one else: From f2d26a3a3c264ecba48f5c6c40cce5378b92bf90 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:23:46 +1100 Subject: [PATCH 18/24] chore(ui): move useCopyImageToClipboard to `common/hooks/` --- .../{features/ui => common}/hooks/useCopyImageToClipboard.ts | 0 .../canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx | 2 +- .../components/ImageContextMenu/SingleSelectionMenuItems.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename invokeai/frontend/web/src/{features/ui => common}/hooks/useCopyImageToClipboard.ts (100%) diff --git a/invokeai/frontend/web/src/features/ui/hooks/useCopyImageToClipboard.ts b/invokeai/frontend/web/src/common/hooks/useCopyImageToClipboard.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/hooks/useCopyImageToClipboard.ts rename to invokeai/frontend/web/src/common/hooks/useCopyImageToClipboard.ts diff --git a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx index 7c04208349..89a710c14d 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx @@ -25,7 +25,7 @@ import { LAYER_NAMES_DICT, } from 'features/canvas/store/canvasTypes'; import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider'; -import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard'; +import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard'; import { isEqual } from 'lodash-es'; import { memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx index ed12eb5ff4..5a88ef320f 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx @@ -13,7 +13,7 @@ import { workflowLoadRequested } from 'features/nodes/store/actions'; import { useRecallParameters } from 'features/parameters/hooks/useRecallParameters'; import { initialImageSelected } from 'features/parameters/store/actions'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import { useCopyImageToClipboard } from 'features/ui/hooks/useCopyImageToClipboard'; +import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard'; import { setActiveTab } from 'features/ui/store/uiSlice'; import { memo, useCallback } from 'react'; import { flushSync } from 'react-dom'; From bb52861896f74680d965deade9903610a71dc828 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:32:03 +1100 Subject: [PATCH 19/24] chore(ui): move MM components & store to `features/` Somehow they had ended up in `features/ui/tabs` which isn't right --- invokeai/frontend/web/src/app/store/store.ts | 2 +- .../store/modelManagerSlice.ts | 0 .../subpanels/AddModelsPanel/AddModels.tsx | 0 .../subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx | 0 .../subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx | 0 .../subpanels/AddModelsPanel/AdvancedAddModels.tsx | 0 .../subpanels/AddModelsPanel/FoundModelsList.tsx | 0 .../subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx | 0 .../subpanels/AddModelsPanel/ScanModels.tsx | 0 .../subpanels/AddModelsPanel/SearchFolderForm.tsx | 0 .../subpanels/AddModelsPanel/SimpleAddModels.tsx | 0 .../subpanels/AddModelsPanel/util.ts | 0 .../subpanels/ImportModelsPanel.tsx | 0 .../subpanels/MergeModelsPanel.tsx | 0 .../subpanels/ModelManagerPanel.tsx | 0 .../subpanels/ModelManagerPanel/CheckpointModelEdit.tsx | 0 .../subpanels/ModelManagerPanel/DiffusersModelEdit.tsx | 0 .../subpanels/ModelManagerPanel/LoRAModelEdit.tsx | 0 .../subpanels/ModelManagerPanel/ModelConvert.tsx | 0 .../subpanels/ModelManagerPanel/ModelList.tsx | 0 .../subpanels/ModelManagerPanel/ModelListItem.tsx | 0 .../subpanels/ModelManagerSettingsPanel.tsx | 0 .../subpanels/ModelManagerSettingsPanel/SyncModels.tsx | 0 .../ModelManagerSettingsPanel/SyncModelsButton.tsx | 0 .../subpanels/shared/BaseModelSelect.tsx | 0 .../subpanels/shared/CheckpointConfigsSelect.tsx | 0 .../subpanels/shared/ModelVariantSelect.tsx | 0 .../Invocation/fields/inputs/MainModelInputField.tsx | 2 +- .../Invocation/fields/inputs/RefinerModelInputField.tsx | 2 +- .../Invocation/fields/inputs/SDXLMainModelInputField.tsx | 2 +- .../Parameters/MainModel/ParamMainModelSelect.tsx | 2 +- .../SDXLRefiner/ParamSDXLRefinerModelSelect.tsx | 2 +- .../ui/components/tabs/ModelManager/ModelManagerTab.tsx | 8 ++++---- 33 files changed, 10 insertions(+), 10 deletions(-) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/store/modelManagerSlice.ts (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/AddModels.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/AdvancedAddModels.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/FoundModelsList.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/ScanModels.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/SearchFolderForm.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/SimpleAddModels.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/AddModelsPanel/util.ts (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ImportModelsPanel.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/MergeModelsPanel.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel/LoRAModelEdit.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel/ModelConvert.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel/ModelList.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerPanel/ModelListItem.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerSettingsPanel.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerSettingsPanel/SyncModels.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/shared/BaseModelSelect.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/shared/CheckpointConfigsSelect.tsx (100%) rename invokeai/frontend/web/src/features/{ui/components/tabs/ModelManager => modelManager}/subpanels/shared/ModelVariantSelect.tsx (100%) diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index 00cbdcccd1..49315b80dd 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -19,7 +19,7 @@ import sdxlReducer from 'features/sdxl/store/sdxlSlice'; import configReducer from 'features/system/store/configSlice'; import systemReducer from 'features/system/store/systemSlice'; import queueReducer from 'features/queue/store/queueSlice'; -import modelmanagerReducer from 'features/ui/components/tabs/ModelManager/store/modelManagerSlice'; +import modelmanagerReducer from 'features/modelManager/store/modelManagerSlice'; import hotkeysReducer from 'features/ui/store/hotkeysSlice'; import uiReducer from 'features/ui/store/uiSlice'; import dynamicMiddlewares from 'redux-dynamic-middlewares'; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/store/modelManagerSlice.ts b/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/store/modelManagerSlice.ts rename to invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AddModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AddModels.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AddModels.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddModels.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/FoundModelsList.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/FoundModelsList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/FoundModelsList.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/FoundModelsList.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanAdvancedAddModels.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/ScanModels.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/ScanModels.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SearchFolderForm.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/SimpleAddModels.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/util.ts b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/util.ts similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/AddModelsPanel/util.ts rename to invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/util.ts diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ImportModelsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ImportModelsPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ImportModelsPanel.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ImportModelsPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/MergeModelsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/MergeModelsPanel.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelConvert.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelConvert.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelConvert.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelConvert.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelList.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelList.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelListItem.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelListItem.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/ModelListItem.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModels.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/shared/BaseModelSelect.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/shared/BaseModelSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/shared/BaseModelSelect.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/shared/BaseModelSelect.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/shared/CheckpointConfigsSelect.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/shared/CheckpointConfigsSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/shared/CheckpointConfigsSelect.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/shared/CheckpointConfigsSelect.tsx diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/shared/ModelVariantSelect.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/shared/ModelVariantSelect.tsx similarity index 100% rename from invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/shared/ModelVariantSelect.tsx rename to invokeai/frontend/web/src/features/modelManager/subpanels/shared/ModelVariantSelect.tsx diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelInputField.tsx index 08483986e3..af68b4291c 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelInputField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/MainModelInputField.tsx @@ -11,7 +11,7 @@ import { import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; +import SyncModelsButton from 'features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; import { forEach } from 'lodash-es'; import { memo, useCallback, useMemo } from 'react'; import { NON_SDXL_MAIN_MODELS } from 'services/api/constants'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelInputField.tsx index 19f2c5ac8e..e6db6031b8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelInputField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/RefinerModelInputField.tsx @@ -11,7 +11,7 @@ import { import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; +import SyncModelsButton from 'features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; import { forEach } from 'lodash-es'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelInputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelInputField.tsx index 89cb8d5150..c6ef5c6bb4 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelInputField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/SDXLMainModelInputField.tsx @@ -11,7 +11,7 @@ import { import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; +import SyncModelsButton from 'features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; import { forEach } from 'lodash-es'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/MainModel/ParamMainModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/MainModel/ParamMainModelSelect.tsx index f4d7421eed..74b6e7e0f3 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/MainModel/ParamMainModelSelect.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/MainModel/ParamMainModelSelect.tsx @@ -12,7 +12,7 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { modelSelected } from 'features/parameters/store/actions'; import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; import { modelIdToMainModelParam } from 'features/parameters/util/modelIdToMainModelParam'; -import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; +import SyncModelsButton from 'features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { forEach } from 'lodash-es'; import { NON_REFINER_BASE_MODELS } from 'services/api/constants'; diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx index cb337740ed..292f20f1fc 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLRefiner/ParamSDXLRefinerModelSelect.tsx @@ -9,7 +9,7 @@ import { MODEL_TYPE_MAP } from 'features/parameters/types/constants'; import { modelIdToSDXLRefinerModelParam } from 'features/parameters/util/modelIdToSDXLRefinerModelParam'; import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; -import SyncModelsButton from 'features/ui/components/tabs/ModelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; +import SyncModelsButton from 'features/modelManager/subpanels/ModelManagerSettingsPanel/SyncModelsButton'; import { forEach } from 'lodash-es'; import { memo, useCallback, useMemo } from 'react'; import { REFINER_BASE_MODELS } from 'services/api/constants'; diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/ModelManagerTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/ModelManagerTab.tsx index 1c8ea3a735..50a9b3b581 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/ModelManagerTab.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/ModelManagerTab.tsx @@ -1,10 +1,10 @@ import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react'; import i18n from 'i18n'; import { ReactNode, memo } from 'react'; -import ImportModelsPanel from './subpanels/ImportModelsPanel'; -import MergeModelsPanel from './subpanels/MergeModelsPanel'; -import ModelManagerPanel from './subpanels/ModelManagerPanel'; -import ModelManagerSettingsPanel from './subpanels/ModelManagerSettingsPanel'; +import ImportModelsPanel from '../../../../modelManager/subpanels/ImportModelsPanel'; +import MergeModelsPanel from '../../../../modelManager/subpanels/MergeModelsPanel'; +import ModelManagerPanel from '../../../../modelManager/subpanels/ModelManagerPanel'; +import ModelManagerSettingsPanel from '../../../../modelManager/subpanels/ModelManagerSettingsPanel'; type ModelManagerTabName = | 'modelManager' From bc64cde6f9ca02d68627a54b46ff37ab6e0fe0b5 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 14 Nov 2023 07:57:07 +1100 Subject: [PATCH 20/24] chore: ruff lint --- invokeai/app/api/routers/model_records.py | 2 +- invokeai/backend/model_manager/hash.py | 4 ++-- .../model_records/test_model_records_sql.py | 20 +++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/invokeai/app/api/routers/model_records.py b/invokeai/app/api/routers/model_records.py index 298b30735a..51a4481acb 100644 --- a/invokeai/app/api/routers/model_records.py +++ b/invokeai/app/api/routers/model_records.py @@ -42,7 +42,7 @@ async def list_model_records( """Get a list of models.""" record_store = ApiDependencies.invoker.services.model_records if base_models and len(base_models) > 0: - models_raw = list() + models_raw = [] for base_model in base_models: models_raw.extend( [x.model_dump() for x in record_store.search_by_attr(base_model=base_model, model_type=model_type)] diff --git a/invokeai/backend/model_manager/hash.py b/invokeai/backend/model_manager/hash.py index f26f3f4394..fb563a8cda 100644 --- a/invokeai/backend/model_manager/hash.py +++ b/invokeai/backend/model_manager/hash.py @@ -49,7 +49,7 @@ class FastModelHash(object): def _hash_dir(cls, model_location: Union[str, Path]) -> str: components: Dict[str, str] = {} - for root, dirs, files in os.walk(model_location): + for root, _dirs, files in os.walk(model_location): for file in files: # only tally tensor files because diffusers config files change slightly # depending on how the model was downloaded/converted. @@ -61,6 +61,6 @@ class FastModelHash(object): # hash all the model hashes together, using alphabetic file order md5 = hashlib.md5() - for path, fast_hash in sorted(components.items()): + for _path, fast_hash in sorted(components.items()): md5.update(fast_hash.encode("utf-8")) return md5.hexdigest() diff --git a/tests/app/services/model_records/test_model_records_sql.py b/tests/app/services/model_records/test_model_records_sql.py index 52a7c40dfd..02a55c89ac 100644 --- a/tests/app/services/model_records/test_model_records_sql.py +++ b/tests/app/services/model_records/test_model_records_sql.py @@ -52,16 +52,16 @@ def test_type(store: ModelRecordServiceBase): def test_add(store: ModelRecordServiceBase): - raw = dict( - path="/tmp/foo.ckpt", - name="model1", - base=BaseModelType("sd-1"), - type="main", - config="/tmp/foo.yaml", - variant="normal", - format="checkpoint", - original_hash="111222333444", - ) + raw = { + "path": "/tmp/foo.ckpt", + "name": "model1", + "base": BaseModelType("sd-1"), + "type": "main", + "config": "/tmp/foo.yaml", + "variant": "normal", + "format": "checkpoint", + "original_hash": "111222333444", + } store.add_model("key1", raw) config1 = store.get_model("key1") assert config1 is not None From 7cdd7b6ad76b588b976452cbb4d1a8f780b737d6 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 14 Nov 2023 08:00:21 +1100 Subject: [PATCH 21/24] feat(api): simplifiy `list_model_records` handler --- invokeai/app/api/routers/model_records.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/invokeai/app/api/routers/model_records.py b/invokeai/app/api/routers/model_records.py index 51a4481acb..1eb31ab772 100644 --- a/invokeai/app/api/routers/model_records.py +++ b/invokeai/app/api/routers/model_records.py @@ -42,15 +42,12 @@ async def list_model_records( """Get a list of models.""" record_store = ApiDependencies.invoker.services.model_records if base_models and len(base_models) > 0: - models_raw = [] + found_models: list[AnyModelConfig] = [] for base_model in base_models: - models_raw.extend( - [x.model_dump() for x in record_store.search_by_attr(base_model=base_model, model_type=model_type)] - ) + found_models.extend(record_store.search_by_attr(base_model=base_model, model_type=model_type)) else: - models_raw = [x.model_dump() for x in record_store.search_by_attr(model_type=model_type)] - models = ModelsListValidator.validate_python({"models": models_raw}) - return models + found_models = record_store.search_by_attr(model_type=model_type) + return ModelsList(models=found_models) @model_records_router.get( From 7daee41ad28c64a1b39e514219cfd3a5cb1017df Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 14 Nov 2023 08:01:44 +1100 Subject: [PATCH 22/24] fix(api): remove unused `ModelsListValidator` --- invokeai/app/api/routers/model_records.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/invokeai/app/api/routers/model_records.py b/invokeai/app/api/routers/model_records.py index 1eb31ab772..27a4bd869b 100644 --- a/invokeai/app/api/routers/model_records.py +++ b/invokeai/app/api/routers/model_records.py @@ -8,7 +8,7 @@ from typing import List, Optional from fastapi import Body, Path, Query, Response from fastapi.routing import APIRouter -from pydantic import BaseModel, ConfigDict, TypeAdapter +from pydantic import BaseModel, ConfigDict from starlette.exceptions import HTTPException from typing_extensions import Annotated @@ -28,9 +28,6 @@ class ModelsList(BaseModel): model_config = ConfigDict(use_enum_values=True) -ModelsListValidator = TypeAdapter(ModelsList) - - @model_records_router.get( "/", operation_id="list_model_records", From 428f0b265fad97aac116afa91241049ebb5d74fc Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 14 Nov 2023 08:06:35 +1100 Subject: [PATCH 23/24] feat(api): add log stmt to update_model_record route --- invokeai/app/api/routers/model_records.py | 1 + 1 file changed, 1 insertion(+) diff --git a/invokeai/app/api/routers/model_records.py b/invokeai/app/api/routers/model_records.py index 27a4bd869b..4ed23d55fa 100644 --- a/invokeai/app/api/routers/model_records.py +++ b/invokeai/app/api/routers/model_records.py @@ -88,6 +88,7 @@ async def update_model_record( record_store = ApiDependencies.invoker.services.model_records try: model_response = record_store.update_model(key, config=info) + logger.info(f"Updated model: {key}") except UnknownModelException as e: raise HTTPException(status_code=404, detail=str(e)) except ValueError as e: From 8929495aeb36e827d8424365a502c7f2c9b4a2d6 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Tue, 14 Nov 2023 08:08:23 +1100 Subject: [PATCH 24/24] fix(test): remove unused assignment to value --- tests/app/services/model_records/test_model_records_sql.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/app/services/model_records/test_model_records_sql.py b/tests/app/services/model_records/test_model_records_sql.py index 02a55c89ac..5f3d949d8a 100644 --- a/tests/app/services/model_records/test_model_records_sql.py +++ b/tests/app/services/model_records/test_model_records_sql.py @@ -161,8 +161,6 @@ def test_filter(store: ModelRecordServiceBase): assert matches[0].key == sha256("config3".encode("utf-8")).hexdigest() assert isinstance(matches[0].type, ModelType) # This tests that we get proper enums back - matches = store.search_by_attr(model_type=BaseModelType("sd-2")) - matches = store.search_by_hash("CONFIG1HASH") assert len(matches) == 1 assert matches[0].original_hash == "CONFIG1HASH"