diff --git a/docs/installation/010_INSTALL_AUTOMATED.md b/docs/installation/010_INSTALL_AUTOMATED.md index 9eb8620321..3c6c90afdc 100644 --- a/docs/installation/010_INSTALL_AUTOMATED.md +++ b/docs/installation/010_INSTALL_AUTOMATED.md @@ -98,7 +98,7 @@ Updating is exactly the same as installing - download the latest installer, choo If you have installation issues, please review the [FAQ]. You can also [create an issue] or ask for help on [discord]. -[installation requirements]: INSTALLATION.md#installation-requirements +[installation requirements]: INSTALL_REQUIREMENTS.md [FAQ]: ../help/FAQ.md [install some models]: 050_INSTALLING_MODELS.md [configuration docs]: ../features/CONFIGURATION.md diff --git a/docs/installation/INSTALL_REQUIREMENTS.md b/docs/installation/INSTALL_REQUIREMENTS.md index 31f60b86eb..2279e7efb8 100644 --- a/docs/installation/INSTALL_REQUIREMENTS.md +++ b/docs/installation/INSTALL_REQUIREMENTS.md @@ -37,13 +37,13 @@ Invoke runs best with a dedicated GPU, but will fall back to running on CPU, alb === "Nvidia" ``` - Any GPU with at least 8GB VRAM. Linux only. + Any GPU with at least 8GB VRAM. ``` === "AMD" ``` - Any GPU with at least 16GB VRAM. + Any GPU with at least 16GB VRAM. Linux only. ``` === "Mac" diff --git a/invokeai/app/api/routers/app_info.py b/invokeai/app/api/routers/app_info.py index 21286ac2b0..c3bc98a038 100644 --- a/invokeai/app/api/routers/app_info.py +++ b/invokeai/app/api/routers/app_info.py @@ -13,7 +13,6 @@ from pydantic import BaseModel, Field from invokeai.app.invocations.upscale import ESRGAN_MODELS from invokeai.app.services.invocation_cache.invocation_cache_common import InvocationCacheStatus from invokeai.backend.image_util.infill_methods.patchmatch import PatchMatch -from invokeai.backend.image_util.safety_checker import SafetyChecker from invokeai.backend.util.logging import logging from invokeai.version import __version__ @@ -109,9 +108,7 @@ async def get_config() -> AppConfig: upscaling_models.append(str(Path(model).stem)) upscaler = Upscaler(upscaling_method="esrgan", upscaling_models=upscaling_models) - nsfw_methods = [] - if SafetyChecker.safety_checker_available(): - nsfw_methods.append("nsfw_checker") + nsfw_methods = ["nsfw_checker"] watermarking_methods = ["invisible_watermark"] diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py index dc8a04b711..9c55ff6531 100644 --- a/invokeai/app/api/routers/images.py +++ b/invokeai/app/api/routers/images.py @@ -6,13 +6,12 @@ from fastapi import BackgroundTasks, Body, HTTPException, Path, Query, Request, from fastapi.responses import FileResponse from fastapi.routing import APIRouter from PIL import Image -from pydantic import BaseModel, Field, ValidationError +from pydantic import BaseModel, Field -from invokeai.app.invocations.fields import MetadataField, MetadataFieldValidator +from invokeai.app.invocations.fields import MetadataField from invokeai.app.services.image_records.image_records_common import ImageCategory, ImageRecordChanges, ResourceOrigin from invokeai.app.services.images.images_common import ImageDTO, ImageUrlsDTO from invokeai.app.services.shared.pagination import OffsetPaginatedResults -from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID, WorkflowWithoutIDValidator from ..dependencies import ApiDependencies @@ -49,6 +48,7 @@ async def upload_image( metadata = None workflow = None + graph = None contents = await file.read() try: @@ -63,21 +63,27 @@ async def upload_image( # TODO: retain non-invokeai metadata on upload? # attempt to parse metadata from image metadata_raw = pil_image.info.get("invokeai_metadata", None) - if metadata_raw: - try: - metadata = MetadataFieldValidator.validate_json(metadata_raw) - except ValidationError: - ApiDependencies.invoker.services.logger.warn("Failed to parse metadata for uploaded image") - pass + if isinstance(metadata_raw, str): + metadata = metadata_raw + else: + ApiDependencies.invoker.services.logger.warn("Failed to parse metadata for uploaded image") + pass # attempt to parse workflow from image workflow_raw = pil_image.info.get("invokeai_workflow", None) - if workflow_raw is not None: - try: - workflow = WorkflowWithoutIDValidator.validate_json(workflow_raw) - except ValidationError: - ApiDependencies.invoker.services.logger.warn("Failed to parse metadata for uploaded image") - pass + if isinstance(workflow_raw, str): + workflow = workflow_raw + else: + ApiDependencies.invoker.services.logger.warn("Failed to parse workflow for uploaded image") + pass + + # attempt to extract graph from image + graph_raw = pil_image.info.get("invokeai_graph", None) + if isinstance(graph_raw, str): + graph = graph_raw + else: + ApiDependencies.invoker.services.logger.warn("Failed to parse graph for uploaded image") + pass try: image_dto = ApiDependencies.invoker.services.images.create( @@ -88,6 +94,7 @@ async def upload_image( board_id=board_id, metadata=metadata, workflow=workflow, + graph=graph, is_intermediate=is_intermediate, ) @@ -185,14 +192,21 @@ async def get_image_metadata( raise HTTPException(status_code=404) +class WorkflowAndGraphResponse(BaseModel): + workflow: Optional[str] = Field(description="The workflow used to generate the image, as stringified JSON") + graph: Optional[str] = Field(description="The graph used to generate the image, as stringified JSON") + + @images_router.get( - "/i/{image_name}/workflow", operation_id="get_image_workflow", response_model=Optional[WorkflowWithoutID] + "/i/{image_name}/workflow", operation_id="get_image_workflow", response_model=WorkflowAndGraphResponse ) async def get_image_workflow( image_name: str = Path(description="The name of image whose workflow to get"), -) -> Optional[WorkflowWithoutID]: +) -> WorkflowAndGraphResponse: try: - return ApiDependencies.invoker.services.images.get_workflow(image_name) + workflow = ApiDependencies.invoker.services.images.get_workflow(image_name) + graph = ApiDependencies.invoker.services.images.get_graph(image_name) + return WorkflowAndGraphResponse(workflow=workflow, graph=graph) except Exception: raise HTTPException(status_code=404) diff --git a/invokeai/app/api/routers/model_manager.py b/invokeai/app/api/routers/model_manager.py index 7bb0f23dc8..1ba3e30e07 100644 --- a/invokeai/app/api/routers/model_manager.py +++ b/invokeai/app/api/routers/model_manager.py @@ -6,7 +6,7 @@ import pathlib import shutil import traceback from copy import deepcopy -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Type from fastapi import Body, Path, Query, Response, UploadFile from fastapi.responses import FileResponse @@ -16,6 +16,7 @@ from pydantic import AnyHttpUrl, BaseModel, ConfigDict, Field from starlette.exceptions import HTTPException from typing_extensions import Annotated +from invokeai.app.services.model_images.model_images_common import ModelImageFileNotFoundException from invokeai.app.services.model_install import ModelInstallJob from invokeai.app.services.model_records import ( DuplicateModelException, @@ -52,6 +53,13 @@ class ModelsList(BaseModel): model_config = ConfigDict(use_enum_values=True) +def add_cover_image_to_model_config(config: AnyModelConfig, dependencies: Type[ApiDependencies]) -> AnyModelConfig: + """Add a cover image URL to a model configuration.""" + cover_image = dependencies.invoker.services.model_images.get_url(config.key) + config.cover_image = cover_image + return config + + ############################################################################## # These are example inputs and outputs that are used in places where Swagger # is unable to generate a correct example. @@ -118,8 +126,7 @@ async def list_model_records( record_store.search_by_attr(model_type=model_type, model_name=model_name, model_format=model_format) ) for model in found_models: - cover_image = ApiDependencies.invoker.services.model_images.get_url(model.key) - model.cover_image = cover_image + model = add_cover_image_to_model_config(model, ApiDependencies) return ModelsList(models=found_models) @@ -160,12 +167,9 @@ async def get_model_record( key: str = Path(description="Key of the model record to fetch."), ) -> AnyModelConfig: """Get a model record""" - record_store = ApiDependencies.invoker.services.model_manager.store try: - config: AnyModelConfig = record_store.get_model(key) - cover_image = ApiDependencies.invoker.services.model_images.get_url(key) - config.cover_image = cover_image - return config + config = ApiDependencies.invoker.services.model_manager.store.get_model(key) + return add_cover_image_to_model_config(config, ApiDependencies) except UnknownModelException as e: raise HTTPException(status_code=404, detail=str(e)) @@ -294,14 +298,15 @@ async def update_model_record( installer = ApiDependencies.invoker.services.model_manager.install try: record_store.update_model(key, changes=changes) - model_response: AnyModelConfig = installer.sync_model_path(key) + config = installer.sync_model_path(key) + config = add_cover_image_to_model_config(config, ApiDependencies) logger.info(f"Updated model: {key}") except UnknownModelException as e: raise HTTPException(status_code=404, detail=str(e)) except ValueError as e: logger.error(str(e)) raise HTTPException(status_code=409, detail=str(e)) - return model_response + return config @model_manager_router.get( @@ -648,6 +653,14 @@ async def convert_model( logger.error(str(e)) raise HTTPException(status_code=409, detail=str(e)) + # Update the model image if the model had one + try: + model_image = ApiDependencies.invoker.services.model_images.get(key) + ApiDependencies.invoker.services.model_images.save(model_image, new_key) + ApiDependencies.invoker.services.model_images.delete(key) + except ModelImageFileNotFoundException: + pass + # delete the original safetensors file installer.delete(key) @@ -655,7 +668,8 @@ async def convert_model( shutil.rmtree(cache_path) # return the config record for the new diffusers directory - new_config: AnyModelConfig = store.get_model(new_key) + new_config = store.get_model(new_key) + new_config = add_cover_image_to_model_config(new_config, ApiDependencies) return new_config diff --git a/invokeai/app/api_app.py b/invokeai/app/api_app.py index ceaeb95147..062682f7d0 100644 --- a/invokeai/app/api_app.py +++ b/invokeai/app/api_app.py @@ -164,6 +164,12 @@ def custom_openapi() -> dict[str, Any]: for schema_key, schema_json in additional_schemas[1]["$defs"].items(): openapi_schema["components"]["schemas"][schema_key] = schema_json + openapi_schema["components"]["schemas"]["InvocationOutputMap"] = { + "type": "object", + "properties": {}, + "required": [], + } + # Add a reference to the output type to additionalProperties of the invoker schema for invoker in all_invocations: invoker_name = invoker.__name__ # type: ignore [attr-defined] # this is a valid attribute @@ -172,6 +178,8 @@ def custom_openapi() -> dict[str, Any]: invoker_schema = openapi_schema["components"]["schemas"][f"{invoker_name}"] outputs_ref = {"$ref": f"#/components/schemas/{output_type_title}"} invoker_schema["output"] = outputs_ref + openapi_schema["components"]["schemas"]["InvocationOutputMap"]["properties"][invoker.get_type()] = outputs_ref + openapi_schema["components"]["schemas"]["InvocationOutputMap"]["required"].append(invoker.get_type()) invoker_schema["class"] = "invocation" # This code no longer seems to be necessary? diff --git a/invokeai/app/invocations/image.py b/invokeai/app/invocations/image.py index 05ffc0d67b..65e7ce5e06 100644 --- a/invokeai/app/invocations/image.py +++ b/invokeai/app/invocations/image.py @@ -1,6 +1,5 @@ # Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654) -from pathlib import Path from typing import Literal, Optional import cv2 @@ -504,7 +503,7 @@ class ImageInverseLerpInvocation(BaseInvocation, WithMetadata, WithBoard): title="Blur NSFW Image", tags=["image", "nsfw"], category="image", - version="1.2.2", + version="1.2.3", ) class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithBoard): """Add blur to NSFW-flagged images""" @@ -516,23 +515,12 @@ class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithBoard): logger = context.logger logger.debug("Running NSFW checker") - if SafetyChecker.has_nsfw_concept(image): - logger.info("A potentially NSFW image has been detected. Image will be blurred.") - blurry_image = image.filter(filter=ImageFilter.GaussianBlur(radius=32)) - caution = self._get_caution_img() - blurry_image.paste(caution, (0, 0), caution) - image = blurry_image + image = SafetyChecker.blur_if_nsfw(image) image_dto = context.images.save(image=image) return ImageOutput.build(image_dto) - def _get_caution_img(self) -> Image.Image: - import invokeai.app.assets.images as image_assets - - caution = Image.open(Path(image_assets.__path__[0]) / "caution.png") - return caution.resize((caution.width // 2, caution.height // 2)) - @invocation( "img_watermark", diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 3d1439f7db..b3ac3973bf 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -586,13 +586,6 @@ class DenoiseLatentsInvocation(BaseInvocation): unet: UNet2DConditionModel, scheduler: Scheduler, ) -> StableDiffusionGeneratorPipeline: - # TODO: - # configure_model_padding( - # unet, - # self.seamless, - # self.seamless_axes, - # ) - class FakeVae: class FakeVaeConfig: def __init__(self) -> None: diff --git a/invokeai/app/invocations/model.py b/invokeai/app/invocations/model.py index caa8a53540..245034c481 100644 --- a/invokeai/app/invocations/model.py +++ b/invokeai/app/invocations/model.py @@ -190,6 +190,75 @@ class LoRALoaderInvocation(BaseInvocation): return output +@invocation_output("lora_selector_output") +class LoRASelectorOutput(BaseInvocationOutput): + """Model loader output""" + + lora: LoRAField = OutputField(description="LoRA model and weight", title="LoRA") + + +@invocation("lora_selector", title="LoRA Selector", tags=["model"], category="model", version="1.0.0") +class LoRASelectorInvocation(BaseInvocation): + """Selects a LoRA model and weight.""" + + lora: ModelIdentifierField = InputField( + description=FieldDescriptions.lora_model, input=Input.Direct, title="LoRA", ui_type=UIType.LoRAModel + ) + weight: float = InputField(default=0.75, description=FieldDescriptions.lora_weight) + + def invoke(self, context: InvocationContext) -> LoRASelectorOutput: + return LoRASelectorOutput(lora=LoRAField(lora=self.lora, weight=self.weight)) + + +@invocation("lora_collection_loader", title="LoRA Collection Loader", tags=["model"], category="model", version="1.0.0") +class LoRACollectionLoader(BaseInvocation): + """Applies a collection of LoRAs to the provided UNet and CLIP models.""" + + loras: LoRAField | list[LoRAField] = InputField( + description="LoRA models and weights. May be a single LoRA or collection.", title="LoRAs" + ) + unet: Optional[UNetField] = InputField( + default=None, + description=FieldDescriptions.unet, + input=Input.Connection, + title="UNet", + ) + clip: Optional[CLIPField] = InputField( + default=None, + description=FieldDescriptions.clip, + input=Input.Connection, + title="CLIP", + ) + + def invoke(self, context: InvocationContext) -> LoRALoaderOutput: + output = LoRALoaderOutput() + loras = self.loras if isinstance(self.loras, list) else [self.loras] + added_loras: list[str] = [] + + for lora in loras: + if lora.lora.key in added_loras: + continue + + if not context.models.exists(lora.lora.key): + raise Exception(f"Unknown lora: {lora.lora.key}!") + + assert lora.lora.base in (BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2) + + added_loras.append(lora.lora.key) + + if self.unet is not None: + if output.unet is None: + output.unet = self.unet.model_copy(deep=True) + output.unet.loras.append(lora) + + if self.clip is not None: + if output.clip is None: + output.clip = self.clip.model_copy(deep=True) + output.clip.loras.append(lora) + + return output + + @invocation_output("sdxl_lora_loader_output") class SDXLLoRALoaderOutput(BaseInvocationOutput): """SDXL LoRA Loader Output""" @@ -279,6 +348,72 @@ class SDXLLoRALoaderInvocation(BaseInvocation): return output +@invocation( + "sdxl_lora_collection_loader", + title="SDXL LoRA Collection Loader", + tags=["model"], + category="model", + version="1.0.0", +) +class SDXLLoRACollectionLoader(BaseInvocation): + """Applies a collection of SDXL LoRAs to the provided UNet and CLIP models.""" + + loras: LoRAField | list[LoRAField] = InputField( + description="LoRA models and weights. May be a single LoRA or collection.", title="LoRAs" + ) + unet: Optional[UNetField] = InputField( + default=None, + description=FieldDescriptions.unet, + input=Input.Connection, + title="UNet", + ) + clip: Optional[CLIPField] = InputField( + default=None, + description=FieldDescriptions.clip, + input=Input.Connection, + title="CLIP", + ) + clip2: Optional[CLIPField] = InputField( + default=None, + description=FieldDescriptions.clip, + input=Input.Connection, + title="CLIP 2", + ) + + def invoke(self, context: InvocationContext) -> SDXLLoRALoaderOutput: + output = SDXLLoRALoaderOutput() + loras = self.loras if isinstance(self.loras, list) else [self.loras] + added_loras: list[str] = [] + + for lora in loras: + if lora.lora.key in added_loras: + continue + + if not context.models.exists(lora.lora.key): + raise Exception(f"Unknown lora: {lora.lora.key}!") + + assert lora.lora.base is BaseModelType.StableDiffusionXL + + added_loras.append(lora.lora.key) + + if self.unet is not None: + if output.unet is None: + output.unet = self.unet.model_copy(deep=True) + output.unet.loras.append(lora) + + if self.clip is not None: + if output.clip is None: + output.clip = self.clip.model_copy(deep=True) + output.clip.loras.append(lora) + + if self.clip2 is not None: + if output.clip2 is None: + output.clip2 = self.clip2.model_copy(deep=True) + output.clip2.loras.append(lora) + + return output + + @invocation("vae_loader", title="VAE", tags=["vae", "model"], category="model", version="1.0.2") class VAELoaderInvocation(BaseInvocation): """Loads a VAE model, outputting a VaeLoaderOutput""" diff --git a/invokeai/app/services/events/events_base.py b/invokeai/app/services/events/events_base.py index 6373ec1f78..aa91cdaec8 100644 --- a/invokeai/app/services/events/events_base.py +++ b/invokeai/app/services/events/events_base.py @@ -122,6 +122,8 @@ class EventServiceBase: source_node_id: str, error_type: str, error: str, + user_id: str | None, + project_id: str | None, ) -> None: """Emitted when an invocation has completed""" self.__emit_queue_event( @@ -135,6 +137,8 @@ class EventServiceBase: "source_node_id": source_node_id, "error_type": error_type, "error": error, + "user_id": user_id, + "project_id": project_id, }, ) diff --git a/invokeai/app/services/image_files/image_files_base.py b/invokeai/app/services/image_files/image_files_base.py index f4036277b7..dc6609aa48 100644 --- a/invokeai/app/services/image_files/image_files_base.py +++ b/invokeai/app/services/image_files/image_files_base.py @@ -4,9 +4,6 @@ from typing import Optional from PIL.Image import Image as PILImageType -from invokeai.app.invocations.fields import MetadataField -from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID - class ImageFileStorageBase(ABC): """Low-level service responsible for storing and retrieving image files.""" @@ -33,8 +30,9 @@ class ImageFileStorageBase(ABC): self, image: PILImageType, image_name: str, - metadata: Optional[MetadataField] = None, - workflow: Optional[WorkflowWithoutID] = None, + metadata: Optional[str] = None, + workflow: Optional[str] = None, + graph: Optional[str] = None, thumbnail_size: int = 256, ) -> None: """Saves an image and a 256x256 WEBP thumbnail. Returns a tuple of the image name, thumbnail name, and created timestamp.""" @@ -46,6 +44,11 @@ class ImageFileStorageBase(ABC): pass @abstractmethod - def get_workflow(self, image_name: str) -> Optional[WorkflowWithoutID]: + def get_workflow(self, image_name: str) -> Optional[str]: """Gets the workflow of an image.""" pass + + @abstractmethod + def get_graph(self, image_name: str) -> Optional[str]: + """Gets the graph of an image.""" + pass diff --git a/invokeai/app/services/image_files/image_files_disk.py b/invokeai/app/services/image_files/image_files_disk.py index 35fa93f81c..15d0be31f8 100644 --- a/invokeai/app/services/image_files/image_files_disk.py +++ b/invokeai/app/services/image_files/image_files_disk.py @@ -7,9 +7,7 @@ from PIL import Image, PngImagePlugin from PIL.Image import Image as PILImageType from send2trash import send2trash -from invokeai.app.invocations.fields import MetadataField from invokeai.app.services.invoker import Invoker -from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID from invokeai.app.util.thumbnails import get_thumbnail_name, make_thumbnail from .image_files_base import ImageFileStorageBase @@ -56,8 +54,9 @@ class DiskImageFileStorage(ImageFileStorageBase): self, image: PILImageType, image_name: str, - metadata: Optional[MetadataField] = None, - workflow: Optional[WorkflowWithoutID] = None, + metadata: Optional[str] = None, + workflow: Optional[str] = None, + graph: Optional[str] = None, thumbnail_size: int = 256, ) -> None: try: @@ -68,13 +67,14 @@ class DiskImageFileStorage(ImageFileStorageBase): info_dict = {} if metadata is not None: - metadata_json = metadata.model_dump_json() - info_dict["invokeai_metadata"] = metadata_json - pnginfo.add_text("invokeai_metadata", metadata_json) + info_dict["invokeai_metadata"] = metadata + pnginfo.add_text("invokeai_metadata", metadata) if workflow is not None: - workflow_json = workflow.model_dump_json() - info_dict["invokeai_workflow"] = workflow_json - pnginfo.add_text("invokeai_workflow", workflow_json) + info_dict["invokeai_workflow"] = workflow + pnginfo.add_text("invokeai_workflow", workflow) + if graph is not None: + info_dict["invokeai_graph"] = graph + pnginfo.add_text("invokeai_graph", graph) # When saving the image, the image object's info field is not populated. We need to set it image.info = info_dict @@ -129,11 +129,18 @@ class DiskImageFileStorage(ImageFileStorageBase): path = path if isinstance(path, Path) else Path(path) return path.exists() - def get_workflow(self, image_name: str) -> WorkflowWithoutID | None: + def get_workflow(self, image_name: str) -> str | None: image = self.get(image_name) workflow = image.info.get("invokeai_workflow", None) - if workflow is not None: - return WorkflowWithoutID.model_validate_json(workflow) + if isinstance(workflow, str): + return workflow + return None + + def get_graph(self, image_name: str) -> str | None: + image = self.get(image_name) + graph = image.info.get("invokeai_graph", None) + if isinstance(graph, str): + return graph return None def __validate_storage_folders(self) -> None: diff --git a/invokeai/app/services/image_records/image_records_base.py b/invokeai/app/services/image_records/image_records_base.py index 7b7b261eca..45c0705090 100644 --- a/invokeai/app/services/image_records/image_records_base.py +++ b/invokeai/app/services/image_records/image_records_base.py @@ -80,7 +80,7 @@ class ImageRecordStorageBase(ABC): starred: Optional[bool] = False, session_id: Optional[str] = None, node_id: Optional[str] = None, - metadata: Optional[MetadataField] = None, + metadata: Optional[str] = None, ) -> datetime: """Saves an image record.""" pass diff --git a/invokeai/app/services/image_records/image_records_sqlite.py b/invokeai/app/services/image_records/image_records_sqlite.py index 5b37913c8f..ef73e79fa1 100644 --- a/invokeai/app/services/image_records/image_records_sqlite.py +++ b/invokeai/app/services/image_records/image_records_sqlite.py @@ -328,10 +328,9 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): starred: Optional[bool] = False, session_id: Optional[str] = None, node_id: Optional[str] = None, - metadata: Optional[MetadataField] = None, + metadata: Optional[str] = None, ) -> datetime: try: - metadata_json = metadata.model_dump_json() if metadata is not None else None self._lock.acquire() self._cursor.execute( """--sql @@ -358,7 +357,7 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): height, node_id, session_id, - metadata_json, + metadata, is_intermediate, starred, has_workflow, diff --git a/invokeai/app/services/images/images_base.py b/invokeai/app/services/images/images_base.py index 42c4266774..9175fc4809 100644 --- a/invokeai/app/services/images/images_base.py +++ b/invokeai/app/services/images/images_base.py @@ -12,7 +12,6 @@ from invokeai.app.services.image_records.image_records_common import ( ) from invokeai.app.services.images.images_common import ImageDTO from invokeai.app.services.shared.pagination import OffsetPaginatedResults -from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID class ImageServiceABC(ABC): @@ -51,8 +50,9 @@ class ImageServiceABC(ABC): session_id: Optional[str] = None, board_id: Optional[str] = None, is_intermediate: Optional[bool] = False, - metadata: Optional[MetadataField] = None, - workflow: Optional[WorkflowWithoutID] = None, + metadata: Optional[str] = None, + workflow: Optional[str] = None, + graph: Optional[str] = None, ) -> ImageDTO: """Creates an image, storing the file and its metadata.""" pass @@ -87,7 +87,12 @@ class ImageServiceABC(ABC): pass @abstractmethod - def get_workflow(self, image_name: str) -> Optional[WorkflowWithoutID]: + def get_workflow(self, image_name: str) -> Optional[str]: + """Gets an image's workflow.""" + pass + + @abstractmethod + def get_graph(self, image_name: str) -> Optional[str]: """Gets an image's workflow.""" pass diff --git a/invokeai/app/services/images/images_default.py b/invokeai/app/services/images/images_default.py index adeed73811..1206526bd5 100644 --- a/invokeai/app/services/images/images_default.py +++ b/invokeai/app/services/images/images_default.py @@ -5,7 +5,6 @@ from PIL.Image import Image as PILImageType from invokeai.app.invocations.fields import MetadataField from invokeai.app.services.invoker import Invoker from invokeai.app.services.shared.pagination import OffsetPaginatedResults -from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID from ..image_files.image_files_common import ( ImageFileDeleteException, @@ -42,8 +41,9 @@ class ImageService(ImageServiceABC): session_id: Optional[str] = None, board_id: Optional[str] = None, is_intermediate: Optional[bool] = False, - metadata: Optional[MetadataField] = None, - workflow: Optional[WorkflowWithoutID] = None, + metadata: Optional[str] = None, + workflow: Optional[str] = None, + graph: Optional[str] = None, ) -> ImageDTO: if image_origin not in ResourceOrigin: raise InvalidOriginException @@ -64,7 +64,7 @@ class ImageService(ImageServiceABC): image_category=image_category, width=width, height=height, - has_workflow=workflow is not None, + has_workflow=workflow is not None or graph is not None, # Meta fields is_intermediate=is_intermediate, # Nullable fields @@ -75,7 +75,7 @@ class ImageService(ImageServiceABC): if board_id is not None: self.__invoker.services.board_image_records.add_image_to_board(board_id=board_id, image_name=image_name) self.__invoker.services.image_files.save( - image_name=image_name, image=image, metadata=metadata, workflow=workflow + image_name=image_name, image=image, metadata=metadata, workflow=workflow, graph=graph ) image_dto = self.get_dto(image_name) @@ -157,7 +157,7 @@ class ImageService(ImageServiceABC): self.__invoker.services.logger.error("Problem getting image metadata") raise e - def get_workflow(self, image_name: str) -> Optional[WorkflowWithoutID]: + def get_workflow(self, image_name: str) -> Optional[str]: try: return self.__invoker.services.image_files.get_workflow(image_name) except ImageFileNotFoundException: @@ -167,6 +167,16 @@ class ImageService(ImageServiceABC): self.__invoker.services.logger.error("Problem getting image workflow") raise + def get_graph(self, image_name: str) -> Optional[str]: + try: + return self.__invoker.services.image_files.get_graph(image_name) + except ImageFileNotFoundException: + self.__invoker.services.logger.error("Image file not found") + raise + except Exception: + self.__invoker.services.logger.error("Problem getting image graph") + raise + def get_path(self, image_name: str, thumbnail: bool = False) -> str: try: return str(self.__invoker.services.image_files.get_path(image_name, thumbnail)) diff --git a/invokeai/app/services/session_processor/session_processor_default.py b/invokeai/app/services/session_processor/session_processor_default.py index 61270e0879..894996b1e6 100644 --- a/invokeai/app/services/session_processor/session_processor_default.py +++ b/invokeai/app/services/session_processor/session_processor_default.py @@ -237,6 +237,8 @@ class DefaultSessionProcessor(SessionProcessorBase): source_node_id=source_invocation_id, error_type=e.__class__.__name__, error=error, + user_id=None, + project_id=None, ) pass diff --git a/invokeai/app/services/shared/invocation_context.py b/invokeai/app/services/shared/invocation_context.py index 32d32e227b..1acc5c725d 100644 --- a/invokeai/app/services/shared/invocation_context.py +++ b/invokeai/app/services/shared/invocation_context.py @@ -181,9 +181,9 @@ class ImagesInterface(InvocationContextInterface): # If `metadata` is provided directly, use that. Else, use the metadata provided by `WithMetadata`, falling back to None. metadata_ = None if metadata: - metadata_ = metadata - elif isinstance(self._data.invocation, WithMetadata): - metadata_ = self._data.invocation.metadata + metadata_ = metadata.model_dump_json() + elif isinstance(self._data.invocation, WithMetadata) and self._data.invocation.metadata: + metadata_ = self._data.invocation.metadata.model_dump_json() # If `board_id` is provided directly, use that. Else, use the board provided by `WithBoard`, falling back to None. board_id_ = None @@ -192,6 +192,14 @@ class ImagesInterface(InvocationContextInterface): elif isinstance(self._data.invocation, WithBoard) and self._data.invocation.board: board_id_ = self._data.invocation.board.board_id + workflow_ = None + if self._data.queue_item.workflow: + workflow_ = self._data.queue_item.workflow.model_dump_json() + + graph_ = None + if self._data.queue_item.session.graph: + graph_ = self._data.queue_item.session.graph.model_dump_json() + return self._services.images.create( image=image, is_intermediate=self._data.invocation.is_intermediate, @@ -199,7 +207,8 @@ class ImagesInterface(InvocationContextInterface): board_id=board_id_, metadata=metadata_, image_origin=ResourceOrigin.INTERNAL, - workflow=self._data.queue_item.workflow, + workflow=workflow_, + graph=graph_, session_id=self._data.queue_item.session_id, node_id=self._data.invocation.id, ) diff --git a/invokeai/backend/image_util/__init__.py b/invokeai/backend/image_util/__init__.py index dec2a92150..f45af9feb4 100644 --- a/invokeai/backend/image_util/__init__.py +++ b/invokeai/backend/image_util/__init__.py @@ -4,5 +4,4 @@ Initialization file for invokeai.backend.image_util methods. from .infill_methods.patchmatch import PatchMatch # noqa: F401 from .pngwriter import PngWriter, PromptFormatter, retrieve_metadata, write_metadata # noqa: F401 -from .seamless import configure_model_padding # noqa: F401 from .util import InitImageResizer, make_grid # noqa: F401 diff --git a/invokeai/backend/image_util/safety_checker.py b/invokeai/backend/image_util/safety_checker.py index 60dcd93fcc..ab09a29619 100644 --- a/invokeai/backend/image_util/safety_checker.py +++ b/invokeai/backend/image_util/safety_checker.py @@ -8,7 +8,7 @@ from pathlib import Path import numpy as np from diffusers.pipelines.stable_diffusion.safety_checker import StableDiffusionSafetyChecker -from PIL import Image +from PIL import Image, ImageFilter from transformers import AutoFeatureExtractor import invokeai.backend.util.logging as logger @@ -16,6 +16,7 @@ from invokeai.app.services.config.config_default import get_config from invokeai.backend.util.devices import TorchDevice from invokeai.backend.util.silence_warnings import SilenceWarnings +repo_id = "CompVis/stable-diffusion-safety-checker" CHECKER_PATH = "core/convert/stable-diffusion-safety-checker" @@ -24,30 +25,30 @@ class SafetyChecker: Wrapper around SafetyChecker model. """ - safety_checker = None feature_extractor = None - tried_load: bool = False + safety_checker = None @classmethod def _load_safety_checker(cls): - if cls.tried_load: + if cls.safety_checker is not None and cls.feature_extractor is not None: return try: - cls.safety_checker = StableDiffusionSafetyChecker.from_pretrained(get_config().models_path / CHECKER_PATH) - cls.feature_extractor = AutoFeatureExtractor.from_pretrained(get_config().models_path / CHECKER_PATH) + model_path = get_config().models_path / CHECKER_PATH + if model_path.exists(): + cls.feature_extractor = AutoFeatureExtractor.from_pretrained(model_path) + cls.safety_checker = StableDiffusionSafetyChecker.from_pretrained(model_path) + else: + model_path.mkdir(parents=True, exist_ok=True) + cls.feature_extractor = AutoFeatureExtractor.from_pretrained(repo_id) + cls.feature_extractor.save_pretrained(model_path, safe_serialization=True) + cls.safety_checker = StableDiffusionSafetyChecker.from_pretrained(repo_id) + cls.safety_checker.save_pretrained(model_path, safe_serialization=True) except Exception as e: logger.warning(f"Could not load NSFW checker: {str(e)}") - cls.tried_load = True - - @classmethod - def safety_checker_available(cls) -> bool: - return Path(get_config().models_path, CHECKER_PATH).exists() @classmethod def has_nsfw_concept(cls, image: Image.Image) -> bool: - if not cls.safety_checker_available() and cls.tried_load: - return False cls._load_safety_checker() if cls.safety_checker is None or cls.feature_extractor is None: return False @@ -60,3 +61,24 @@ class SafetyChecker: with SilenceWarnings(): checked_image, has_nsfw_concept = cls.safety_checker(images=x_image, clip_input=features.pixel_values) return has_nsfw_concept[0] + + @classmethod + def blur_if_nsfw(cls, image: Image.Image) -> Image.Image: + if cls.has_nsfw_concept(image): + logger.warning("A potentially NSFW image has been detected. Image will be blurred.") + blurry_image = image.filter(filter=ImageFilter.GaussianBlur(radius=32)) + caution = cls._get_caution_img() + # Center the caution image on the blurred image + x = (blurry_image.width - caution.width) // 2 + y = (blurry_image.height - caution.height) // 2 + blurry_image.paste(caution, (x, y), caution) + image = blurry_image + + return image + + @classmethod + def _get_caution_img(cls) -> Image.Image: + import invokeai.app.assets.images as image_assets + + caution = Image.open(Path(image_assets.__path__[0]) / "caution.png") + return caution.resize((caution.width // 2, caution.height // 2)) diff --git a/invokeai/backend/image_util/seamless.py b/invokeai/backend/image_util/seamless.py deleted file mode 100644 index 8a2580bfcc..0000000000 --- a/invokeai/backend/image_util/seamless.py +++ /dev/null @@ -1,52 +0,0 @@ -import torch.nn as nn - - -def _conv_forward_asymmetric(self, input, weight, bias): - """ - Patch for Conv2d._conv_forward that supports asymmetric padding - """ - working = nn.functional.pad(input, self.asymmetric_padding["x"], mode=self.asymmetric_padding_mode["x"]) - working = nn.functional.pad(working, self.asymmetric_padding["y"], mode=self.asymmetric_padding_mode["y"]) - return nn.functional.conv2d( - working, - weight, - bias, - self.stride, - nn.modules.utils._pair(0), - self.dilation, - self.groups, - ) - - -def configure_model_padding(model, seamless, seamless_axes): - """ - Modifies the 2D convolution layers to use a circular padding mode based on - the `seamless` and `seamless_axes` options. - """ - # TODO: get an explicit interface for this in diffusers: https://github.com/huggingface/diffusers/issues/556 - for m in model.modules(): - if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): - if seamless: - m.asymmetric_padding_mode = {} - m.asymmetric_padding = {} - m.asymmetric_padding_mode["x"] = "circular" if ("x" in seamless_axes) else "constant" - m.asymmetric_padding["x"] = ( - m._reversed_padding_repeated_twice[0], - m._reversed_padding_repeated_twice[1], - 0, - 0, - ) - m.asymmetric_padding_mode["y"] = "circular" if ("y" in seamless_axes) else "constant" - m.asymmetric_padding["y"] = ( - 0, - 0, - m._reversed_padding_repeated_twice[2], - m._reversed_padding_repeated_twice[3], - ) - m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d) - else: - m._conv_forward = nn.Conv2d._conv_forward.__get__(m, nn.Conv2d) - if hasattr(m, "asymmetric_padding_mode"): - del m.asymmetric_padding_mode - if hasattr(m, "asymmetric_padding"): - del m.asymmetric_padding diff --git a/invokeai/backend/stable_diffusion/seamless.py b/invokeai/backend/stable_diffusion/seamless.py index 2e22c19d0e..23ed978c6d 100644 --- a/invokeai/backend/stable_diffusion/seamless.py +++ b/invokeai/backend/stable_diffusion/seamless.py @@ -1,89 +1,51 @@ -from __future__ import annotations - from contextlib import contextmanager -from typing import Callable, List, Union +from typing import Callable, List, Optional, Tuple, Union +import torch import torch.nn as nn from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL from diffusers.models.autoencoders.autoencoder_tiny import AutoencoderTiny +from diffusers.models.lora import LoRACompatibleConv from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel -def _conv_forward_asymmetric(self, input, weight, bias): - """ - Patch for Conv2d._conv_forward that supports asymmetric padding - """ - working = nn.functional.pad(input, self.asymmetric_padding["x"], mode=self.asymmetric_padding_mode["x"]) - working = nn.functional.pad(working, self.asymmetric_padding["y"], mode=self.asymmetric_padding_mode["y"]) - return nn.functional.conv2d( - working, - weight, - bias, - self.stride, - nn.modules.utils._pair(0), - self.dilation, - self.groups, - ) - - @contextmanager def set_seamless(model: Union[UNet2DConditionModel, AutoencoderKL, AutoencoderTiny], seamless_axes: List[str]): if not seamless_axes: yield return - # Callable: (input: Tensor, weight: Tensor, bias: Optional[Tensor]) -> Tensor - to_restore: list[tuple[nn.Conv2d | nn.ConvTranspose2d, Callable]] = [] + # override conv_forward + # https://github.com/huggingface/diffusers/issues/556#issuecomment-1993287019 + def _conv_forward_asymmetric(self, input: torch.Tensor, weight: torch.Tensor, bias: Optional[torch.Tensor] = None): + self.paddingX = (self._reversed_padding_repeated_twice[0], self._reversed_padding_repeated_twice[1], 0, 0) + self.paddingY = (0, 0, self._reversed_padding_repeated_twice[2], self._reversed_padding_repeated_twice[3]) + working = torch.nn.functional.pad(input, self.paddingX, mode=x_mode) + working = torch.nn.functional.pad(working, self.paddingY, mode=y_mode) + return torch.nn.functional.conv2d( + working, weight, bias, self.stride, torch.nn.modules.utils._pair(0), self.dilation, self.groups + ) + + original_layers: List[Tuple[nn.Conv2d, Callable]] = [] + try: - # Hard coded to skip down block layers, allowing for seamless tiling at the expense of prompt adherence - skipped_layers = 1 - for m_name, m in model.named_modules(): - if not isinstance(m, (nn.Conv2d, nn.ConvTranspose2d)): - continue + x_mode = "circular" if "x" in seamless_axes else "constant" + y_mode = "circular" if "y" in seamless_axes else "constant" - if isinstance(model, UNet2DConditionModel) and m_name.startswith("down_blocks.") and ".resnets." in m_name: - # down_blocks.1.resnets.1.conv1 - _, block_num, _, resnet_num, submodule_name = m_name.split(".") - block_num = int(block_num) - resnet_num = int(resnet_num) + conv_layers: List[torch.nn.Conv2d] = [] - if block_num >= len(model.down_blocks) - skipped_layers: - continue + for module in model.modules(): + if isinstance(module, torch.nn.Conv2d): + conv_layers.append(module) - # Skip the second resnet (could be configurable) - if resnet_num > 0: - continue - - # Skip Conv2d layers (could be configurable) - if submodule_name == "conv2": - continue - - m.asymmetric_padding_mode = {} - m.asymmetric_padding = {} - m.asymmetric_padding_mode["x"] = "circular" if ("x" in seamless_axes) else "constant" - m.asymmetric_padding["x"] = ( - m._reversed_padding_repeated_twice[0], - m._reversed_padding_repeated_twice[1], - 0, - 0, - ) - m.asymmetric_padding_mode["y"] = "circular" if ("y" in seamless_axes) else "constant" - m.asymmetric_padding["y"] = ( - 0, - 0, - m._reversed_padding_repeated_twice[2], - m._reversed_padding_repeated_twice[3], - ) - - to_restore.append((m, m._conv_forward)) - m._conv_forward = _conv_forward_asymmetric.__get__(m, nn.Conv2d) + for layer in conv_layers: + if isinstance(layer, LoRACompatibleConv) and layer.lora_layer is None: + layer.lora_layer = lambda *x: 0 + original_layers.append((layer, layer._conv_forward)) + layer._conv_forward = _conv_forward_asymmetric.__get__(layer, torch.nn.Conv2d) yield finally: - for module, orig_conv_forward in to_restore: - module._conv_forward = orig_conv_forward - if hasattr(module, "asymmetric_padding_mode"): - del module.asymmetric_padding_mode - if hasattr(module, "asymmetric_padding"): - del module.asymmetric_padding + for layer, orig_conv_forward in original_layers: + layer._conv_forward = orig_conv_forward diff --git a/invokeai/frontend/web/.eslintrc.js b/invokeai/frontend/web/.eslintrc.js index 18a6e3a9b9..519e725fb4 100644 --- a/invokeai/frontend/web/.eslintrc.js +++ b/invokeai/frontend/web/.eslintrc.js @@ -10,6 +10,8 @@ module.exports = { 'path/no-relative-imports': ['error', { maxDepth: 0 }], // https://github.com/edvardchen/eslint-plugin-i18next/blob/HEAD/docs/rules/no-literal-string.md 'i18next/no-literal-string': 'error', + // https://eslint.org/docs/latest/rules/no-console + 'no-console': 'error', }, overrides: [ /** diff --git a/invokeai/frontend/web/.gitignore b/invokeai/frontend/web/.gitignore index 3e8a372bc7..757d6ebcc8 100644 --- a/invokeai/frontend/web/.gitignore +++ b/invokeai/frontend/web/.gitignore @@ -43,4 +43,5 @@ stats.html yalc.lock # vitest -tsconfig.vitest-temp.json \ No newline at end of file +tsconfig.vitest-temp.json +coverage/ \ No newline at end of file diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index 96db090386..f2210e4c68 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -35,6 +35,7 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "test": "vitest", + "test:ui": "vitest --coverage --ui", "test:no-watch": "vitest --no-watch" }, "madge": { @@ -52,47 +53,48 @@ }, "dependencies": { "@chakra-ui/react-use-size": "^2.1.0", - "@dagrejs/dagre": "^1.1.1", - "@dagrejs/graphlib": "^2.2.1", + "@dagrejs/dagre": "^1.1.2", + "@dagrejs/graphlib": "^2.2.2", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@dnd-kit/utilities": "^3.2.2", - "@fontsource-variable/inter": "^5.0.17", + "@fontsource-variable/inter": "^5.0.18", "@invoke-ai/ui-library": "^0.0.25", "@nanostores/react": "^0.7.2", - "@reduxjs/toolkit": "2.2.2", + "@reduxjs/toolkit": "2.2.3", "@roarr/browser-log-writer": "^1.3.0", "chakra-react-select": "^4.7.6", "compare-versions": "^6.1.0", "dateformat": "^5.0.3", - "framer-motion": "^11.0.22", - "i18next": "^23.10.1", - "i18next-http-backend": "^2.5.0", + "fracturedjsonjs": "^4.0.1", + "framer-motion": "^11.1.8", + "i18next": "^23.11.3", + "i18next-http-backend": "^2.5.1", "idb-keyval": "^6.2.1", "jsondiffpatch": "^0.6.0", "konva": "^9.3.6", "lodash-es": "^4.17.21", - "nanostores": "^0.10.0", + "nanostores": "^0.10.3", "new-github-issue-url": "^1.0.0", - "overlayscrollbars": "^2.6.1", - "overlayscrollbars-react": "^0.5.5", + "overlayscrollbars": "^2.7.3", + "overlayscrollbars-react": "^0.5.6", "query-string": "^9.0.0", - "react": "^18.2.0", + "react": "^18.3.1", "react-colorful": "^5.6.1", - "react-dom": "^18.2.0", + "react-dom": "^18.3.1", "react-dropzone": "^14.2.3", "react-error-boundary": "^4.0.13", - "react-hook-form": "^7.51.2", + "react-hook-form": "^7.51.4", "react-hotkeys-hook": "4.5.0", - "react-i18next": "^14.1.0", - "react-icons": "^5.0.1", + "react-i18next": "^14.1.1", + "react-icons": "^5.2.0", "react-konva": "^18.2.10", - "react-redux": "9.1.0", - "react-resizable-panels": "^2.0.16", + "react-redux": "9.1.2", + "react-resizable-panels": "^2.0.19", "react-select": "5.8.0", "react-use": "^17.5.0", - "react-virtuoso": "^4.7.5", - "reactflow": "^11.10.4", + "react-virtuoso": "^4.7.10", + "reactflow": "^11.11.3", "redux-dynamic-middlewares": "^2.2.0", "redux-remember": "^5.1.0", "redux-undo": "^1.1.0", @@ -104,8 +106,8 @@ "use-device-pixel-ratio": "^1.1.2", "use-image": "^1.1.1", "uuid": "^9.0.1", - "zod": "^3.22.4", - "zod-validation-error": "^3.0.3" + "zod": "^3.23.6", + "zod-validation-error": "^3.2.0" }, "peerDependencies": { "@chakra-ui/react": "^2.8.2", @@ -116,40 +118,42 @@ "devDependencies": { "@invoke-ai/eslint-config-react": "^0.0.14", "@invoke-ai/prettier-config-react": "^0.0.7", - "@storybook/addon-essentials": "^8.0.4", - "@storybook/addon-interactions": "^8.0.4", - "@storybook/addon-links": "^8.0.4", - "@storybook/addon-storysource": "^8.0.4", - "@storybook/manager-api": "^8.0.4", - "@storybook/react": "^8.0.4", - "@storybook/react-vite": "^8.0.4", - "@storybook/theming": "^8.0.4", + "@storybook/addon-essentials": "^8.0.10", + "@storybook/addon-interactions": "^8.0.10", + "@storybook/addon-links": "^8.0.10", + "@storybook/addon-storysource": "^8.0.10", + "@storybook/manager-api": "^8.0.10", + "@storybook/react": "^8.0.10", + "@storybook/react-vite": "^8.0.10", + "@storybook/theming": "^8.0.10", "@types/dateformat": "^5.0.2", "@types/lodash-es": "^4.17.12", - "@types/node": "^20.11.30", - "@types/react": "^18.2.73", - "@types/react-dom": "^18.2.22", + "@types/node": "^20.12.10", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.0", "@types/uuid": "^9.0.8", "@vitejs/plugin-react-swc": "^3.6.0", + "@vitest/coverage-v8": "^1.5.0", + "@vitest/ui": "^1.5.0", "concurrently": "^8.2.2", "dpdm": "^3.14.0", "eslint": "^8.57.0", "eslint-plugin-i18next": "^6.0.3", "eslint-plugin-path": "^1.3.0", - "knip": "^5.6.1", + "knip": "^5.12.3", "openapi-types": "^12.1.3", "openapi-typescript": "^6.7.5", "prettier": "^3.2.5", "rollup-plugin-visualizer": "^5.12.0", - "storybook": "^8.0.4", + "storybook": "^8.0.10", "ts-toolbelt": "^9.6.0", "tsafe": "^1.6.6", - "typescript": "^5.4.3", - "vite": "^5.2.6", - "vite-plugin-css-injected-by-js": "^3.5.0", - "vite-plugin-dts": "^3.8.0", + "typescript": "^5.4.5", + "vite": "^5.2.11", + "vite-plugin-css-injected-by-js": "^3.5.1", + "vite-plugin-dts": "^3.9.1", "vite-plugin-eslint": "^1.8.1", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.4.0" + "vitest": "^1.6.0" } } diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml index 2e5442479f..64189f0d82 100644 --- a/invokeai/frontend/web/pnpm-lock.yaml +++ b/invokeai/frontend/web/pnpm-lock.yaml @@ -7,58 +7,61 @@ settings: dependencies: '@chakra-ui/react': specifier: ^2.8.2 - version: 2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0) + version: 2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.3.1)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1) '@chakra-ui/react-use-size': specifier: ^2.1.0 - version: 2.1.0(react@18.2.0) + version: 2.1.0(react@18.3.1) '@dagrejs/dagre': - specifier: ^1.1.1 - version: 1.1.1 + specifier: ^1.1.2 + version: 1.1.2 '@dagrejs/graphlib': - specifier: ^2.2.1 - version: 2.2.1 + specifier: ^2.2.2 + version: 2.2.2 '@dnd-kit/core': specifier: ^6.1.0 - version: 6.1.0(react-dom@18.2.0)(react@18.2.0) + version: 6.1.0(react-dom@18.3.1)(react@18.3.1) '@dnd-kit/sortable': specifier: ^8.0.0 - version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0) + version: 8.0.0(@dnd-kit/core@6.1.0)(react@18.3.1) '@dnd-kit/utilities': specifier: ^3.2.2 - version: 3.2.2(react@18.2.0) + version: 3.2.2(react@18.3.1) '@fontsource-variable/inter': - specifier: ^5.0.17 - version: 5.0.17 + specifier: ^5.0.18 + version: 5.0.18 '@invoke-ai/ui-library': specifier: ^0.0.25 - version: 0.0.25(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.17)(@internationalized/date@3.5.3)(@types/react@18.2.73)(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0) + version: 0.0.25(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.18)(@internationalized/date@3.5.3)(@types/react@18.3.1)(i18next@23.11.3)(react-dom@18.3.1)(react@18.3.1) '@nanostores/react': specifier: ^0.7.2 - version: 0.7.2(nanostores@0.10.0)(react@18.2.0) + version: 0.7.2(nanostores@0.10.3)(react@18.3.1) '@reduxjs/toolkit': - specifier: 2.2.2 - version: 2.2.2(react-redux@9.1.0)(react@18.2.0) + specifier: 2.2.3 + version: 2.2.3(react-redux@9.1.2)(react@18.3.1) '@roarr/browser-log-writer': specifier: ^1.3.0 version: 1.3.0 chakra-react-select: specifier: ^4.7.6 - version: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.4)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + version: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.4)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) compare-versions: specifier: ^6.1.0 version: 6.1.0 dateformat: specifier: ^5.0.3 version: 5.0.3 + fracturedjsonjs: + specifier: ^4.0.1 + version: 4.0.1 framer-motion: - specifier: ^11.0.22 - version: 11.0.22(react-dom@18.2.0)(react@18.2.0) + specifier: ^11.1.8 + version: 11.1.8(react-dom@18.3.1)(react@18.3.1) i18next: - specifier: ^23.10.1 - version: 23.10.1 + specifier: ^23.11.3 + version: 23.11.3 i18next-http-backend: - specifier: ^2.5.0 - version: 2.5.0 + specifier: ^2.5.1 + version: 2.5.1 idb-keyval: specifier: ^6.2.1 version: 6.2.1 @@ -72,68 +75,68 @@ dependencies: specifier: ^4.17.21 version: 4.17.21 nanostores: - specifier: ^0.10.0 - version: 0.10.0 + specifier: ^0.10.3 + version: 0.10.3 new-github-issue-url: specifier: ^1.0.0 version: 1.0.0 overlayscrollbars: - specifier: ^2.6.1 - version: 2.6.1 + specifier: ^2.7.3 + version: 2.7.3 overlayscrollbars-react: - specifier: ^0.5.5 - version: 0.5.5(overlayscrollbars@2.6.1)(react@18.2.0) + specifier: ^0.5.6 + version: 0.5.6(overlayscrollbars@2.7.3)(react@18.3.1) query-string: specifier: ^9.0.0 version: 9.0.0 react: - specifier: ^18.2.0 - version: 18.2.0 + specifier: ^18.3.1 + version: 18.3.1 react-colorful: specifier: ^5.6.1 - version: 5.6.1(react-dom@18.2.0)(react@18.2.0) + version: 5.6.1(react-dom@18.3.1)(react@18.3.1) react-dom: - specifier: ^18.2.0 - version: 18.2.0(react@18.2.0) + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) react-dropzone: specifier: ^14.2.3 - version: 14.2.3(react@18.2.0) + version: 14.2.3(react@18.3.1) react-error-boundary: specifier: ^4.0.13 - version: 4.0.13(react@18.2.0) + version: 4.0.13(react@18.3.1) react-hook-form: - specifier: ^7.51.2 - version: 7.51.2(react@18.2.0) + specifier: ^7.51.4 + version: 7.51.4(react@18.3.1) react-hotkeys-hook: specifier: 4.5.0 - version: 4.5.0(react-dom@18.2.0)(react@18.2.0) + version: 4.5.0(react-dom@18.3.1)(react@18.3.1) react-i18next: - specifier: ^14.1.0 - version: 14.1.0(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0) + specifier: ^14.1.1 + version: 14.1.1(i18next@23.11.3)(react-dom@18.3.1)(react@18.3.1) react-icons: - specifier: ^5.0.1 - version: 5.0.1(react@18.2.0) + specifier: ^5.2.0 + version: 5.2.0(react@18.3.1) react-konva: specifier: ^18.2.10 - version: 18.2.10(konva@9.3.6)(react-dom@18.2.0)(react@18.2.0) + version: 18.2.10(konva@9.3.6)(react-dom@18.3.1)(react@18.3.1) react-redux: - specifier: 9.1.0 - version: 9.1.0(@types/react@18.2.73)(react@18.2.0)(redux@5.0.1) + specifier: 9.1.2 + version: 9.1.2(@types/react@18.3.1)(react@18.3.1)(redux@5.0.1) react-resizable-panels: - specifier: ^2.0.16 - version: 2.0.16(react-dom@18.2.0)(react@18.2.0) + specifier: ^2.0.19 + version: 2.0.19(react-dom@18.3.1)(react@18.3.1) react-select: specifier: 5.8.0 - version: 5.8.0(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + version: 5.8.0(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) react-use: specifier: ^17.5.0 - version: 17.5.0(react-dom@18.2.0)(react@18.2.0) + version: 17.5.0(react-dom@18.3.1)(react@18.3.1) react-virtuoso: - specifier: ^4.7.5 - version: 4.7.5(react-dom@18.2.0)(react@18.2.0) + specifier: ^4.7.10 + version: 4.7.10(react-dom@18.3.1)(react@18.3.1) reactflow: - specifier: ^11.10.4 - version: 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + specifier: ^11.11.3 + version: 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) redux-dynamic-middlewares: specifier: ^2.2.0 version: 2.2.0 @@ -157,54 +160,54 @@ dependencies: version: 4.7.5 use-debounce: specifier: ^10.0.0 - version: 10.0.0(react@18.2.0) + version: 10.0.0(react@18.3.1) use-device-pixel-ratio: specifier: ^1.1.2 - version: 1.1.2(react@18.2.0) + version: 1.1.2(react@18.3.1) use-image: specifier: ^1.1.1 - version: 1.1.1(react-dom@18.2.0)(react@18.2.0) + version: 1.1.1(react-dom@18.3.1)(react@18.3.1) uuid: specifier: ^9.0.1 version: 9.0.1 zod: - specifier: ^3.22.4 - version: 3.22.4 + specifier: ^3.23.6 + version: 3.23.6 zod-validation-error: - specifier: ^3.0.3 - version: 3.0.3(zod@3.22.4) + specifier: ^3.2.0 + version: 3.2.0(zod@3.23.6) devDependencies: '@invoke-ai/eslint-config-react': specifier: ^0.0.14 - version: 0.0.14(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.3) + version: 0.0.14(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.5) '@invoke-ai/prettier-config-react': specifier: ^0.0.7 version: 0.0.7(prettier@3.2.5) '@storybook/addon-essentials': - specifier: ^8.0.4 - version: 8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + specifier: ^8.0.10 + version: 8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) '@storybook/addon-interactions': - specifier: ^8.0.4 - version: 8.0.4(vitest@1.4.0) + specifier: ^8.0.10 + version: 8.0.10(vitest@1.6.0) '@storybook/addon-links': - specifier: ^8.0.4 - version: 8.0.4(react@18.2.0) + specifier: ^8.0.10 + version: 8.0.10(react@18.3.1) '@storybook/addon-storysource': - specifier: ^8.0.4 - version: 8.0.4 + specifier: ^8.0.10 + version: 8.0.10 '@storybook/manager-api': - specifier: ^8.0.4 - version: 8.0.4(react-dom@18.2.0)(react@18.2.0) + specifier: ^8.0.10 + version: 8.0.10(react-dom@18.3.1)(react@18.3.1) '@storybook/react': - specifier: ^8.0.4 - version: 8.0.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3) + specifier: ^8.0.10 + version: 8.0.10(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5) '@storybook/react-vite': - specifier: ^8.0.4 - version: 8.0.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)(vite@5.2.6) + specifier: ^8.0.10 + version: 8.0.10(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5)(vite@5.2.11) '@storybook/theming': - specifier: ^8.0.4 - version: 8.0.4(react-dom@18.2.0)(react@18.2.0) + specifier: ^8.0.10 + version: 8.0.10(react-dom@18.3.1)(react@18.3.1) '@types/dateformat': specifier: ^5.0.2 version: 5.0.2 @@ -212,20 +215,26 @@ devDependencies: specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^20.11.30 - version: 20.11.30 + specifier: ^20.12.10 + version: 20.12.10 '@types/react': - specifier: ^18.2.73 - version: 18.2.73 + specifier: ^18.3.1 + version: 18.3.1 '@types/react-dom': - specifier: ^18.2.22 - version: 18.2.22 + specifier: ^18.3.0 + version: 18.3.0 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 '@vitejs/plugin-react-swc': specifier: ^3.6.0 - version: 3.6.0(vite@5.2.6) + version: 3.6.0(vite@5.2.11) + '@vitest/coverage-v8': + specifier: ^1.5.0 + version: 1.6.0(vitest@1.6.0) + '@vitest/ui': + specifier: ^1.5.0 + version: 1.6.0(vitest@1.6.0) concurrently: specifier: ^8.2.2 version: 8.2.2 @@ -242,8 +251,8 @@ devDependencies: specifier: ^1.3.0 version: 1.3.0(eslint@8.57.0) knip: - specifier: ^5.6.1 - version: 5.6.1(@types/node@20.11.30)(typescript@5.4.3) + specifier: ^5.12.3 + version: 5.12.3(@types/node@20.12.10)(typescript@5.4.5) openapi-types: specifier: ^12.1.3 version: 12.1.3 @@ -257,8 +266,8 @@ devDependencies: specifier: ^5.12.0 version: 5.12.0 storybook: - specifier: ^8.0.4 - version: 8.0.4(react-dom@18.2.0)(react@18.2.0) + specifier: ^8.0.10 + version: 8.0.10(react-dom@18.3.1)(react@18.3.1) ts-toolbelt: specifier: ^9.6.0 version: 9.6.0 @@ -266,34 +275,29 @@ devDependencies: specifier: ^1.6.6 version: 1.6.6 typescript: - specifier: ^5.4.3 - version: 5.4.3 + specifier: ^5.4.5 + version: 5.4.5 vite: - specifier: ^5.2.6 - version: 5.2.6(@types/node@20.11.30) + specifier: ^5.2.11 + version: 5.2.11(@types/node@20.12.10) vite-plugin-css-injected-by-js: - specifier: ^3.5.0 - version: 3.5.0(vite@5.2.6) + specifier: ^3.5.1 + version: 3.5.1(vite@5.2.11) vite-plugin-dts: - specifier: ^3.8.0 - version: 3.8.0(@types/node@20.11.30)(typescript@5.4.3)(vite@5.2.6) + specifier: ^3.9.1 + version: 3.9.1(@types/node@20.12.10)(typescript@5.4.5)(vite@5.2.11) vite-plugin-eslint: specifier: ^1.8.1 - version: 1.8.1(eslint@8.57.0)(vite@5.2.6) + version: 1.8.1(eslint@8.57.0)(vite@5.2.11) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.4.3)(vite@5.2.6) + version: 4.3.2(typescript@5.4.5)(vite@5.2.11) vitest: - specifier: ^1.4.0 - version: 1.4.0(@types/node@20.11.30) + specifier: ^1.6.0 + version: 1.6.0(@types/node@20.12.10)(@vitest/ui@1.6.0) packages: - /@aashutoshrathi/word-wrap@1.2.6: - resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} - engines: {node: '>=0.10.0'} - dev: true - /@adobe/css-tools@4.3.3: resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} dev: true @@ -345,7 +349,7 @@ packages: - '@internationalized/date' dev: false - /@ark-ui/react@1.3.0(@internationalized/date@3.5.3)(react-dom@18.2.0)(react@18.2.0): + /@ark-ui/react@1.3.0(@internationalized/date@3.5.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-JHjNoIX50+mUCTaEGMjfGQWGGi31pKsV646jZJlR/1xohpYJigzg8BvO97cTsVk8fwtur+cm11gz3Nf7f5QUnA==} peerDependencies: react: '>=18.0.0' @@ -375,7 +379,7 @@ packages: '@zag-js/progress': 0.32.1 '@zag-js/radio-group': 0.32.1 '@zag-js/rating-group': 0.32.1 - '@zag-js/react': 0.32.1(react-dom@18.2.0)(react@18.2.0) + '@zag-js/react': 0.32.1(react-dom@18.3.1)(react@18.3.1) '@zag-js/select': 0.32.1 '@zag-js/slider': 0.32.1 '@zag-js/splitter': 0.32.1 @@ -386,8 +390,8 @@ packages: '@zag-js/toggle-group': 0.32.1 '@zag-js/tooltip': 0.32.1 '@zag-js/types': 0.32.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@internationalized/date' dev: false @@ -403,28 +407,28 @@ packages: resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/highlight': 7.24.2 + '@babel/highlight': 7.24.5 picocolors: 1.0.0 - /@babel/compat-data@7.24.1: - resolution: {integrity: sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==} + /@babel/compat-data@7.24.4: + resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} engines: {node: '>=6.9.0'} dev: true - /@babel/core@7.24.3: - resolution: {integrity: sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==} + /@babel/core@7.24.5: + resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} engines: {node: '>=6.9.0'} dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.1 + '@babel/generator': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) - '@babel/helpers': 7.24.1 - '@babel/parser': 7.24.1 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helpers': 7.24.5 + '@babel/parser': 7.24.5 '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 convert-source-map: 2.0.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -434,11 +438,11 @@ packages: - supports-color dev: true - /@babel/generator@7.24.1: - resolution: {integrity: sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==} + /@babel/generator@7.24.5: + resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -448,65 +452,65 @@ packages: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/helper-compilation-targets@7.23.6: resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/compat-data': 7.24.1 + '@babel/compat-data': 7.24.4 '@babel/helper-validator-option': 7.23.5 browserslist: 4.23.0 lru-cache: 5.1.1 semver: 6.3.1 dev: true - /@babel/helper-create-class-features-plugin@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==} + /@babel/helper-create-class-features-plugin@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 - '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.24.5 '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.3) + '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.5) '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-split-export-declaration': 7.24.5 semver: 6.3.1 dev: true - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.3): + /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.5): resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-annotate-as-pure': 7.22.5 regexpu-core: 5.3.2 semver: 6.3.1 dev: true - /@babel/helper-define-polyfill-provider@0.6.1(@babel/core@7.24.3): - resolution: {integrity: sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==} + /@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.5): + resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.5 debug: 4.3.4 lodash.debounce: 4.0.8 resolve: 1.22.8 @@ -524,106 +528,106 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true - /@babel/helper-member-expression-to-functions@7.23.0: - resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} + /@babel/helper-member-expression-to-functions@7.24.5: + resolution: {integrity: sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/helper-module-imports@7.24.3: resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 - /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.3): - resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} + /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-simple-access': 7.24.5 + '@babel/helper-split-export-declaration': 7.24.5 + '@babel/helper-validator-identifier': 7.24.5 dev: true /@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true - /@babel/helper-plugin-utils@7.24.0: - resolution: {integrity: sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==} + /@babel/helper-plugin-utils@7.24.5: + resolution: {integrity: sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.3): + /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.24.5): resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-wrap-function': 7.22.20 + '@babel/helper-wrap-function': 7.24.5 dev: true - /@babel/helper-replace-supers@7.24.1(@babel/core@7.24.3): + /@babel/helper-replace-supers@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-member-expression-to-functions': 7.23.0 + '@babel/helper-member-expression-to-functions': 7.24.5 '@babel/helper-optimise-call-expression': 7.22.5 dev: true - /@babel/helper-simple-access@7.22.5: - resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} + /@babel/helper-simple-access@7.24.5: + resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true - /@babel/helper-split-export-declaration@7.22.6: - resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} + /@babel/helper-split-export-declaration@7.24.5: + resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@babel/helper-string-parser@7.24.1: resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.20: - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + /@babel/helper-validator-identifier@7.24.5: + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} engines: {node: '>=6.9.0'} /@babel/helper-validator-option@7.23.5: @@ -631,974 +635,986 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-wrap-function@7.22.20: - resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==} + /@babel/helper-wrap-function@7.24.5: + resolution: {integrity: sha512-/xxzuNvgRl4/HLNKvnFwdhdgN3cpLxgLROeLDl83Yx0AJ1SGvq1ak0OszTOjDfiB8Vx03eJbeDWh9r+jCCWttw==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-function-name': 7.23.0 '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true - /@babel/helpers@7.24.1: - resolution: {integrity: sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==} + /@babel/helpers@7.24.5: + resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.24.0 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 transitivePeerDependencies: - supports-color dev: true - /@babel/highlight@7.24.2: - resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} + /@babel/highlight@7.24.5: + resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-validator-identifier': 7.24.5 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.0 - /@babel/parser@7.24.1: - resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} + /@babel/parser@7.24.5: + resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.1(@babel/core@7.24.3): + /@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-LdXRi1wEMTrHVR4Zc9F8OewC3vdm5h4QB6L71zy6StmYeqGi1b3ttIO8UC+BfZKcH9jdr4aI249rBkm+3+YvHw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.24.5 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-plugin-utils': 7.24.5 + dev: true + + /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.1(@babel/core@7.24.3): + /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.24.1(@babel/core@7.24.3) + '@babel/plugin-transform-optional-chaining': 7.24.5(@babel/core@7.24.5) dev: true - /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.1(@babel/core@7.24.3): + /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.3): + /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.5): resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.3): + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.5): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.3): + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.5): resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.3): + /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.5): resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-flow@7.24.1(@babel/core@7.24.3): + /@babel/plugin-syntax-flow@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-import-assertions@7.24.1(@babel/core@7.24.3): + /@babel/plugin-syntax-import-assertions@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-import-attributes@7.24.1(@babel/core@7.24.3): + /@babel/plugin-syntax-import-attributes@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.3): + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.3): + /@babel/plugin-syntax-jsx@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.3): + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.3): + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.3): + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.5): resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.3): + /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.5): resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.3): + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.5): resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.3): + /@babel/plugin-syntax-typescript@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.3): + /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.5): resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-arrow-functions@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-arrow-functions@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-async-generator-functions@7.24.3(@babel/core@7.24.3): + /@babel/plugin-transform-async-generator-functions@7.24.3(@babel/core@7.24.5): resolution: {integrity: sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.5) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-async-to-generator@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-async-to-generator@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-module-imports': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-block-scoped-functions@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-block-scoped-functions@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-block-scoping@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==} + /@babel/plugin-transform-block-scoping@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-class-properties@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-class-properties@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-class-static-block@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==} + /@babel/plugin-transform-class-static-block@7.24.4(@babel/core@7.24.5): + resolution: {integrity: sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-classes@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==} + /@babel/plugin-transform-classes@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.3) - '@babel/helper-split-export-declaration': 7.22.6 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.5) + '@babel/helper-split-export-declaration': 7.24.5 globals: 11.12.0 dev: true - /@babel/plugin-transform-computed-properties@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-computed-properties@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/template': 7.24.0 dev: true - /@babel/plugin-transform-destructuring@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==} + /@babel/plugin-transform-destructuring@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-dotall-regex@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-dotall-regex@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-duplicate-keys@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-duplicate-keys@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-dynamic-import@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-dynamic-import@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-exponentiation-operator@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-exponentiation-operator@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-export-namespace-from@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-export-namespace-from@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-flow-strip-types@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-flow-strip-types@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-flow': 7.24.1(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-flow': 7.24.1(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-for-of@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-for-of@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-function-name@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-function-name@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-function-name': 7.23.0 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-json-strings@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-json-strings@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-literals@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-literals@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-logical-assignment-operators@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-logical-assignment-operators@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-member-expression-literals@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-member-expression-literals@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-modules-amd@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-modules-amd@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-modules-commonjs@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-modules-commonjs@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-simple-access': 7.22.5 + '@babel/core': 7.24.5 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-simple-access': 7.24.5 dev: true - /@babel/plugin-transform-modules-systemjs@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-modules-systemjs@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-validator-identifier': 7.24.5 dev: true - /@babel/plugin-transform-modules-umd@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-modules-umd@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.3): + /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.5): resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-new-target@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-new-target@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-nullish-coalescing-operator@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-nullish-coalescing-operator@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-numeric-separator@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-numeric-separator@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-object-rest-spread@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==} + /@babel/plugin-transform-object-rest-spread@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-7EauQHszLGM3ay7a161tTQH7fj+3vVM/gThlz5HpFtnygTxjrlvoeq7MPVA1Vy9Q555OB8SnAOsMkLShNkkrHA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-transform-parameters': 7.24.1(@babel/core@7.24.3) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-transform-parameters': 7.24.5(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-object-super@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-object-super@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-optional-catch-binding@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-optional-catch-binding@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-optional-chaining@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==} + /@babel/plugin-transform-optional-chaining@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-parameters@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==} + /@babel/plugin-transform-parameters@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-private-methods@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-private-methods@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-private-property-in-object@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==} + /@babel/plugin-transform-private-property-in-object@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-JM4MHZqnWR04jPMujQDTBVRnqxpLLpx2tkn7iPn+Hmsc0Gnb79yvRWOkvqFOx3Z7P7VxiRIR22c4eGSNj87OBQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.3) + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-property-literals@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-property-literals@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-regenerator@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-regenerator@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 regenerator-transform: 0.15.2 dev: true - /@babel/plugin-transform-reserved-words@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-reserved-words@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-shorthand-properties@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-shorthand-properties@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-spread@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-spread@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 dev: true - /@babel/plugin-transform-sticky-regex@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-sticky-regex@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-template-literals@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-template-literals@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-typeof-symbol@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==} + /@babel/plugin-transform-typeof-symbol@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-typescript@7.24.1(@babel/core@7.24.3): - resolution: {integrity: sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==} + /@babel/plugin-transform-typescript@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-E0VWu/hk83BIFUWnsKZ4D81KXjN5L3MobvevOHErASk9IPwKHOkTgvqzvNo1yP/ePJWqqK2SpUR5z+KQbl6NVw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.24.1(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.3) + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 + '@babel/plugin-syntax-typescript': 7.24.1(@babel/core@7.24.5) dev: true - /@babel/plugin-transform-unicode-escapes@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-unicode-escapes@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-unicode-property-regex@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-unicode-property-regex@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-unicode-regex@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-unicode-regex@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/plugin-transform-unicode-sets-regex@7.24.1(@babel/core@7.24.3): + /@babel/plugin-transform-unicode-sets-regex@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.3) - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.5) + '@babel/helper-plugin-utils': 7.24.5 dev: true - /@babel/preset-env@7.24.3(@babel/core@7.24.3): - resolution: {integrity: sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==} + /@babel/preset-env@7.24.5(@babel/core@7.24.5): + resolution: {integrity: sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/compat-data': 7.24.1 - '@babel/core': 7.24.3 + '@babel/compat-data': 7.24.4 + '@babel/core': 7.24.5 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.3) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.3) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.3) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.3) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-import-assertions': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-syntax-import-attributes': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.3) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.3) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.3) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.3) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.3) - '@babel/plugin-transform-arrow-functions': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-async-generator-functions': 7.24.3(@babel/core@7.24.3) - '@babel/plugin-transform-async-to-generator': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-block-scoped-functions': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-block-scoping': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-class-properties': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-class-static-block': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-classes': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-computed-properties': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-destructuring': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-dotall-regex': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-duplicate-keys': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-dynamic-import': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-exponentiation-operator': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-export-namespace-from': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-for-of': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-function-name': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-json-strings': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-literals': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-logical-assignment-operators': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-member-expression-literals': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-modules-amd': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-modules-systemjs': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-modules-umd': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.24.3) - '@babel/plugin-transform-new-target': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-numeric-separator': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-object-rest-spread': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-object-super': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-optional-catch-binding': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-optional-chaining': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-parameters': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-private-methods': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-private-property-in-object': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-property-literals': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-regenerator': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-reserved-words': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-shorthand-properties': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-spread': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-sticky-regex': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-template-literals': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-typeof-symbol': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-escapes': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-property-regex': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-regex': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-unicode-sets-regex': 7.24.1(@babel/core@7.24.3) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.3) - babel-plugin-polyfill-corejs2: 0.4.10(@babel/core@7.24.3) - babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.3) - babel-plugin-polyfill-regenerator: 0.6.1(@babel/core@7.24.3) - core-js-compat: 3.36.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.5) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.5) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.5) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-import-assertions': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-syntax-import-attributes': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.5) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.5) + '@babel/plugin-transform-arrow-functions': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-async-generator-functions': 7.24.3(@babel/core@7.24.5) + '@babel/plugin-transform-async-to-generator': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-block-scoped-functions': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-block-scoping': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-class-properties': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-class-static-block': 7.24.4(@babel/core@7.24.5) + '@babel/plugin-transform-classes': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-computed-properties': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-destructuring': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-dotall-regex': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-duplicate-keys': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-dynamic-import': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-exponentiation-operator': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-export-namespace-from': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-for-of': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-function-name': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-json-strings': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-literals': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-logical-assignment-operators': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-member-expression-literals': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-modules-amd': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-modules-systemjs': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-modules-umd': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.24.5) + '@babel/plugin-transform-new-target': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-numeric-separator': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-object-rest-spread': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-object-super': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-optional-catch-binding': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-optional-chaining': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-parameters': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-private-methods': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-private-property-in-object': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-property-literals': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-regenerator': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-reserved-words': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-shorthand-properties': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-spread': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-sticky-regex': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-template-literals': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-typeof-symbol': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-unicode-escapes': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-unicode-property-regex': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-unicode-regex': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-unicode-sets-regex': 7.24.1(@babel/core@7.24.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.5) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.5) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.5) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.5) + core-js-compat: 3.37.0 semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /@babel/preset-flow@7.24.1(@babel/core@7.24.3): + /@babel/preset-flow@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-sWCV2G9pcqZf+JHyv/RyqEIpFypxdCSxWIxQjpdaQxenNog7cN1pr76hg8u0Fz8Qgg0H4ETkGcJnXL8d4j0PPA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-transform-flow-strip-types': 7.24.1(@babel/core@7.24.3) + '@babel/plugin-transform-flow-strip-types': 7.24.1(@babel/core@7.24.5) dev: true - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.3): + /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.5): resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 - '@babel/types': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 + '@babel/types': 7.24.5 esutils: 2.0.3 dev: true - /@babel/preset-typescript@7.24.1(@babel/core@7.24.3): + /@babel/preset-typescript@7.24.1(@babel/core@7.24.5): resolution: {integrity: sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-plugin-utils': 7.24.0 + '@babel/core': 7.24.5 + '@babel/helper-plugin-utils': 7.24.5 '@babel/helper-validator-option': 7.23.5 - '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-typescript': 7.24.1(@babel/core@7.24.3) + '@babel/plugin-syntax-jsx': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-typescript': 7.24.5(@babel/core@7.24.5) dev: true - /@babel/register@7.23.7(@babel/core@7.24.3): + /@babel/register@7.23.7(@babel/core@7.24.5): resolution: {integrity: sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 clone-deep: 4.0.1 find-cache-dir: 2.1.0 make-dir: 2.1.0 @@ -1622,127 +1638,138 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 + dev: false + + /@babel/runtime@7.24.5: + resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 /@babel/template@7.24.0: resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.24.2 - '@babel/parser': 7.24.1 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 dev: true - /@babel/traverse@7.24.1: - resolution: {integrity: sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==} + /@babel/traverse@7.24.5: + resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.1 + '@babel/generator': 7.24.5 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.1 - '@babel/types': 7.24.0 + '@babel/helper-split-export-declaration': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color dev: true - /@babel/types@7.24.0: - resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==} + /@babel/types@7.24.5: + resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.24.1 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-validator-identifier': 7.24.5 to-fast-properties: 2.0.0 /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} dev: true - /@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0): + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1): resolution: {integrity: sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/descendant': 3.1.0(react@18.2.0) - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/descendant': 3.1.0(react@18.3.1) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0): + /@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1): resolution: {integrity: sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/descendant': 3.1.0(react@18.2.0) - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/descendant': 3.1.0(react@18.3.1) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.1.8)(react@18.3.1) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/alert@2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/alert@2.2.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-jHg4LYMRNOJH830ViLuicjb3F+v6iriE/2G5T+Sd0Hna04nukNJ1MxUmBPE+vI22me2dIflfelu2v9wdB6Pojw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/anatomy@2.2.2: resolution: {integrity: sha512-MV6D4VLRIHr4PkW4zMyqfrNS1mPlCTiCXwvYGtDFQYr+xHFfonhAuf9WjsSc0nyp2m0OdkSLnzmVKkZFLo25Tg==} dev: false - /@chakra-ui/avatar@2.3.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/avatar@2.3.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-8gKSyLfygnaotbJbDMHDiJoF38OHXUYVme4gGxZ1fLnQEdPVEaIWfH+NndIjOM0z8S+YEFnT9KyGMUtvPrBk3g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/breadcrumb@2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/breadcrumb@2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-4cWCG24flYBxjruRi4RJREWTGF74L/KzI2CognAW/d/zWR0CjiScuJhf37Am3LFbCySP6WSoyBOtTIoTA4yLEA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/breakpoint-utils@2.0.8: @@ -1751,334 +1778,334 @@ packages: '@chakra-ui/shared-utils': 2.0.5 dev: false - /@chakra-ui/button@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/button@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-95CplwlRKmmUXkdEp/21VkEWgnwcx2TOBG6NfYlsuLBDHSLlo5FKIiE2oSi4zXc4TLcopGcWPNcm/NDaSC5pvA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/card@2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/card@2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-xUB/k5MURj4CtPAhdSoXZidUbm8j3hci9vnc+eZJVDqhDOShNlD6QeniQNRPRys4lWAQLCbFcrwL29C8naDi6g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/checkbox@2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/checkbox@2.3.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-85g38JIXMEv6M+AcyIGLh7igNtfpAN6KGQFYxY9tBj0eWvWk4NKQxvqqyVta0bSAyIl1rixNIIezNpNWk2iO4g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) '@zag-js/focus-visible': 0.16.0 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/clickable@2.1.0(react@18.2.0): + /@chakra-ui/clickable@2.1.0(react@18.3.1): resolution: {integrity: sha512-flRA/ClPUGPYabu+/GLREZVZr9j2uyyazCAUHAdrTUEdDYCr31SVGhgh7dgKdtq23bOvAQJpIJjw/0Bs0WvbXw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/close-button@2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/close-button@2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-gnpENKOanKexswSVpVz7ojZEALl2x5qjLYNqSQGbxz+aP9sOXPfUS56ebyBrre7T7exuWGiFeRwnM0oVeGPaiw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/color-mode@2.2.0(react@18.2.0): + /@chakra-ui/color-mode@2.2.0(react@18.3.1): resolution: {integrity: sha512-niTEA8PALtMWRI9wJ4LL0CSBDo8NBfLNp4GD6/0hstcm3IlbBHTVKxN6HwSaoNYfphDQLxCjT4yG+0BJA5tFpg==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/control-box@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/control-box@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-gVrRDyXFdMd8E7rulL0SKeoljkLQiPITFnsyMO8EFHNZ+AHt5wK4LIguYVEq88APqAGZGfHFWXr79RYrNiE3Mg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/counter@2.1.0(react@18.2.0): + /@chakra-ui/counter@2.1.0(react@18.3.1): resolution: {integrity: sha512-s6hZAEcWT5zzjNz2JIWUBzRubo9la/oof1W7EKZVVfPYHERnl5e16FmBC79Yfq8p09LQ+aqFKm/etYoJMMgghw==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/number-utils': 2.0.7 - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/css-reset@2.3.0(@emotion/react@11.11.4)(react@18.2.0): + /@chakra-ui/css-reset@2.3.0(@emotion/react@11.11.4)(react@18.3.1): resolution: {integrity: sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==} peerDependencies: '@emotion/react': '>=10.0.35' react: '>=18' dependencies: - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - react: 18.2.0 + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/descendant@3.1.0(react@18.2.0): + /@chakra-ui/descendant@3.1.0(react@18.3.1): resolution: {integrity: sha512-VxCIAir08g5w27klLyi7PVo8BxhW4tgU/lxQyujkmi4zx7hT9ZdrcQLAted/dAa+aSIZ14S1oV0Q9lGjsAdxUQ==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/dom-utils@2.1.0: resolution: {integrity: sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ==} dev: false - /@chakra-ui/editable@3.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/editable@3.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-j2JLrUL9wgg4YA6jLlbU88370eCRyor7DZQD9lzpY95tSOXpTljeg3uF9eOmDnCs6fxp3zDWIfkgMm/ExhcGTg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/event-utils@2.0.8: resolution: {integrity: sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==} dev: false - /@chakra-ui/focus-lock@2.1.0(@types/react@18.2.73)(react@18.2.0): + /@chakra-ui/focus-lock@2.1.0(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/dom-utils': 2.1.0 - react: 18.2.0 - react-focus-lock: 2.11.1(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-focus-lock: 2.11.1(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/form-control@2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/form-control@2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-wehLC1t4fafCVJ2RvJQT2jyqsAwX7KymmiGqBu7nQoQz8ApTkGABWpo/QwDh3F/dBLrouHDoOvGmYTqft3Mirw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/hooks@2.2.1(react@18.2.0): + /@chakra-ui/hooks@2.2.1(react@18.3.1): resolution: {integrity: sha512-RQbTnzl6b1tBjbDPf9zGRo9rf/pQMholsOudTxjy4i9GfTfz6kgp5ValGjQm2z7ng6Z31N1cnjZ1AlSzQ//ZfQ==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-utils': 2.0.12(react@18.2.0) + '@chakra-ui/react-utils': 2.0.12(react@18.3.1) '@chakra-ui/utils': 2.0.15 compute-scroll-into-view: 3.0.3 copy-to-clipboard: 3.3.3 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/icon@3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/icon@3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-xxjGLvlX2Ys4H0iHrI16t74rG9EBcpFvJ3Y3B7KMQTrnW34Kf7Da/UC8J67Gtx85mTHW020ml85SVPKORWNNKQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/icons@2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/icons@2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-3p30hdo4LlRZTT5CwoAJq3G9fHI0wDc0pBaMHj4SUn0yomO+RcDRlzhdXqdr5cVnzax44sqXJVnf3oQG0eI+4g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/image@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/image@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-bskumBYKLiLMySIWDGcz0+D9Th0jPvmX6xnRMs4o92tT3Od/bW26lahmV2a2Op2ItXeCmRMY+XxJH5Gy1i46VA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/input@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/input@2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-GiBbb3EqAA8Ph43yGa6Mc+kUPjh4Spmxp1Pkelr8qtudpc3p2PJOOebLpd90mcqw8UePPa+l6YhhPtp6o0irhw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/object-utils': 2.1.0 - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/layout@2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/layout@2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-nXuZ6WRbq0WdgnRgLw+QuxWAHuhDtVX8ElWqcTK+cSMFg/52eVP47czYBE5F35YhnoW2XBwfNoNgZ7+e8Z01Rg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/breakpoint-utils': 2.0.8 - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/object-utils': 2.1.0 - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/lazy-utils@2.0.5: resolution: {integrity: sha512-UULqw7FBvcckQk2n3iPO56TMJvDsNv0FKZI6PlUNJVaGsPbsYxK/8IQ60vZgaTVPtVcjY6BE+y6zg8u9HOqpyg==} dev: false - /@chakra-ui/live-region@2.1.0(react@18.2.0): + /@chakra-ui/live-region@2.1.0(react@18.3.1): resolution: {integrity: sha512-ZOxFXwtaLIsXjqnszYYrVuswBhnIHHP+XIgK1vC6DePKtyK590Wg+0J0slDwThUAd4MSSIUa/nNX84x1GMphWw==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/media-query@3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/media-query@3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-IsTGgFLoICVoPRp9ykOgqmdMotJG0CnPsKvGQeSFOB/dZfIujdVb14TYxDU4+MURXry1MhJ7LzZhv+Ml7cr8/g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/breakpoint-utils': 2.0.8 - '@chakra-ui/react-env': 3.1.0(react@18.2.0) + '@chakra-ui/react-env': 3.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0): + /@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1): resolution: {integrity: sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/clickable': 2.1.0(react@18.2.0) - '@chakra-ui/descendant': 3.1.0(react@18.2.0) + '@chakra-ui/clickable': 2.1.0(react@18.3.1) + '@chakra-ui/descendant': 3.1.0(react@18.3.1) '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-animation-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-outside-click': 2.2.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-animation-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-disclosure': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-outside-click': 2.2.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0): + /@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1): resolution: {integrity: sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/clickable': 2.1.0(react@18.2.0) - '@chakra-ui/descendant': 3.1.0(react@18.2.0) + '@chakra-ui/clickable': 2.1.0(react@18.3.1) + '@chakra-ui/descendant': 3.1.0(react@18.3.1) '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-animation-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-outside-click': 2.2.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-animation-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-disclosure': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-outside-click': 2.2.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.1.8)(react@18.3.1) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.3.1)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -2086,25 +2113,25 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.73)(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/focus-lock': 2.1.0(@types/react@18.3.1)(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1) aria-hidden: 1.2.3 - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.7(@types/react@18.2.73)(react@18.2.0) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.3.1)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -2112,44 +2139,44 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.73)(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/focus-lock': 2.1.0(@types/react@18.3.1)(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.1.8)(react@18.3.1) aria-hidden: 1.2.3 - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-remove-scroll: 2.5.7(@types/react@18.2.73)(react@18.2.0) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/number-input@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/number-input@2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-pfOdX02sqUN0qC2ysuvgVDiws7xZ20XDIlcNhva55Jgm095xjm8eVdIBfNm3SFbSUNxyXvLTW/YQanX74tKmuA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/counter': 2.1.0(react@18.2.0) - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-interval': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/counter': 2.1.0(react@18.3.1) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-interval': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/number-utils@2.0.7: @@ -2160,103 +2187,103 @@ packages: resolution: {integrity: sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==} dev: false - /@chakra-ui/pin-input@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/pin-input@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-x4vBqLStDxJFMt+jdAHHS8jbh294O53CPQJoL4g228P513rHylV/uPscYUHrVJXRxsHfRztQO9k45jjTYaPRMw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/descendant': 3.1.0(react@18.2.0) - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/descendant': 3.1.0(react@18.3.1) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0): + /@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1): resolution: {integrity: sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-animation-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-animation-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-disclosure': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0): + /@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1): resolution: {integrity: sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-animation-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-animation-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-disclosure': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/popper@3.1.0(react@18.2.0): + /@chakra-ui/popper@3.1.0(react@18.3.1): resolution: {integrity: sha512-ciDdpdYbeFG7og6/6J8lkTFxsSvwTdMLFkpVylAF6VNC22jssiWfquj2eyD4rJnzkRFPvIWJq8hvbfhsm+AjSg==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@popperjs/core': 2.11.8 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/portal@2.1.0(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/portal@2.1.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-9q9KWf6SArEcIq1gGofNcFPSWEyl+MfJjEUg/un1SMlQjaROOh3zYr+6JAwvcORiX7tyHosnmWC3d3wI2aPSQg==} peerDependencies: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@chakra-ui/progress@2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/progress@2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-qUXuKbuhN60EzDD9mHR7B67D7p/ZqNS2Aze4Pbl1qGGZfulPW0PY8Rof32qDtttDQBkzQIzFGE8d9QpAemToIQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/provider@2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/provider@2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==} peerDependencies: '@emotion/react': ^11.0.0 @@ -2264,229 +2291,229 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-env': 3.1.0(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) + '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-env': 3.1.0(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) '@chakra-ui/utils': 2.0.15 - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@chakra-ui/radio@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/radio@2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-n10M46wJrMGbonaghvSRnZ9ToTv/q76Szz284gv4QUWvyljQACcGrXIONUnQ3BIwbOfkRqSk7Xl/JgZtVfll+w==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) '@zag-js/focus-visible': 0.16.0 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-children-utils@2.0.6(react@18.2.0): + /@chakra-ui/react-children-utils@2.0.6(react@18.3.1): resolution: {integrity: sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-context@2.1.0(react@18.2.0): + /@chakra-ui/react-context@2.1.0(react@18.3.1): resolution: {integrity: sha512-iahyStvzQ4AOwKwdPReLGfDesGG+vWJfEsn0X/NoGph/SkN+HXtv2sCfYFFR9k7bb+Kvc6YfpLlSuLvKMHi2+w==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-env@3.1.0(react@18.2.0): + /@chakra-ui/react-env@3.1.0(react@18.3.1): resolution: {integrity: sha512-Vr96GV2LNBth3+IKzr/rq1IcnkXv+MLmwjQH6C8BRtn3sNskgDFD5vLkVXcEhagzZMCh8FR3V/bzZPojBOyNhw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-types@2.0.7(react@18.2.0): + /@chakra-ui/react-types@2.0.7(react@18.3.1): resolution: {integrity: sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-animation-state@2.1.0(react@18.2.0): + /@chakra-ui/react-use-animation-state@2.1.0(react@18.3.1): resolution: {integrity: sha512-CFZkQU3gmDBwhqy0vC1ryf90BVHxVN8cTLpSyCpdmExUEtSEInSCGMydj2fvn7QXsz/za8JNdO2xxgJwxpLMtg==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/dom-utils': 2.1.0 - '@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-callback-ref@2.1.0(react@18.2.0): + /@chakra-ui/react-use-callback-ref@2.1.0(react@18.3.1): resolution: {integrity: sha512-efnJrBtGDa4YaxDzDE90EnKD3Vkh5a1t3w7PhnRQmsphLy3g2UieasoKTlT2Hn118TwDjIv5ZjHJW6HbzXA9wQ==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-controllable-state@2.1.0(react@18.2.0): + /@chakra-ui/react-use-controllable-state@2.1.0(react@18.3.1): resolution: {integrity: sha512-QR/8fKNokxZUs4PfxjXuwl0fj/d71WPrmLJvEpCTkHjnzu7LnYvzoe2wB867IdooQJL0G1zBxl0Dq+6W1P3jpg==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-disclosure@2.1.0(react@18.2.0): + /@chakra-ui/react-use-disclosure@2.1.0(react@18.3.1): resolution: {integrity: sha512-Ax4pmxA9LBGMyEZJhhUZobg9C0t3qFE4jVF1tGBsrLDcdBeLR9fwOogIPY9Hf0/wqSlAryAimICbr5hkpa5GSw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-event-listener@2.1.0(react@18.2.0): + /@chakra-ui/react-use-event-listener@2.1.0(react@18.3.1): resolution: {integrity: sha512-U5greryDLS8ISP69DKDsYcsXRtAdnTQT+jjIlRYZ49K/XhUR/AqVZCK5BkR1spTDmO9H8SPhgeNKI70ODuDU/Q==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-focus-effect@2.1.0(react@18.2.0): + /@chakra-ui/react-use-focus-effect@2.1.0(react@18.3.1): resolution: {integrity: sha512-xzVboNy7J64xveLcxTIJ3jv+lUJKDwRM7Szwn9tNzUIPD94O3qwjV7DDCUzN2490nSYDF4OBMt/wuDBtaR3kUQ==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/dom-utils': 2.1.0 - '@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-focus-on-pointer-down@2.1.0(react@18.2.0): + /@chakra-ui/react-use-focus-on-pointer-down@2.1.0(react@18.3.1): resolution: {integrity: sha512-2jzrUZ+aiCG/cfanrolsnSMDykCAbv9EK/4iUyZno6BYb3vziucmvgKuoXbMPAzWNtwUwtuMhkby8rc61Ue+Lg==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-interval@2.1.0(react@18.2.0): + /@chakra-ui/react-use-interval@2.1.0(react@18.3.1): resolution: {integrity: sha512-8iWj+I/+A0J08pgEXP1J1flcvhLBHkk0ln7ZvGIyXiEyM6XagOTJpwNhiu+Bmk59t3HoV/VyvyJTa+44sEApuw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-latest-ref@2.1.0(react@18.2.0): + /@chakra-ui/react-use-latest-ref@2.1.0(react@18.3.1): resolution: {integrity: sha512-m0kxuIYqoYB0va9Z2aW4xP/5b7BzlDeWwyXCH6QpT2PpW3/281L3hLCm1G0eOUcdVlayqrQqOeD6Mglq+5/xoQ==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-merge-refs@2.1.0(react@18.2.0): + /@chakra-ui/react-use-merge-refs@2.1.0(react@18.3.1): resolution: {integrity: sha512-lERa6AWF1cjEtWSGjxWTaSMvneccnAVH4V4ozh8SYiN9fSPZLlSG3kNxfNzdFvMEhM7dnP60vynF7WjGdTgQbQ==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-outside-click@2.2.0(react@18.2.0): + /@chakra-ui/react-use-outside-click@2.2.0(react@18.3.1): resolution: {integrity: sha512-PNX+s/JEaMneijbgAM4iFL+f3m1ga9+6QK0E5Yh4s8KZJQ/bLwZzdhMz8J/+mL+XEXQ5J0N8ivZN28B82N1kNw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-pan-event@2.1.0(react@18.2.0): + /@chakra-ui/react-use-pan-event@2.1.0(react@18.3.1): resolution: {integrity: sha512-xmL2qOHiXqfcj0q7ZK5s9UjTh4Gz0/gL9jcWPA6GVf+A0Od5imEDa/Vz+533yQKWiNSm1QGrIj0eJAokc7O4fg==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/event-utils': 2.0.8 - '@chakra-ui/react-use-latest-ref': 2.1.0(react@18.2.0) + '@chakra-ui/react-use-latest-ref': 2.1.0(react@18.3.1) framesync: 6.1.2 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-previous@2.1.0(react@18.2.0): + /@chakra-ui/react-use-previous@2.1.0(react@18.3.1): resolution: {integrity: sha512-pjxGwue1hX8AFcmjZ2XfrQtIJgqbTF3Qs1Dy3d1krC77dEsiCUbQ9GzOBfDc8pfd60DrB5N2tg5JyHbypqh0Sg==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-safe-layout-effect@2.1.0(react@18.2.0): + /@chakra-ui/react-use-safe-layout-effect@2.1.0(react@18.3.1): resolution: {integrity: sha512-Knbrrx/bcPwVS1TorFdzrK/zWA8yuU/eaXDkNj24IrKoRlQrSBFarcgAEzlCHtzuhufP3OULPkELTzz91b0tCw==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-size@2.1.0(react@18.2.0): + /@chakra-ui/react-use-size@2.1.0(react@18.3.1): resolution: {integrity: sha512-tbLqrQhbnqOjzTaMlYytp7wY8BW1JpL78iG7Ru1DlV4EWGiAmXFGvtnEt9HftU0NJ0aJyjgymkxfVGI55/1Z4A==} peerDependencies: react: '>=18' dependencies: '@zag-js/element-size': 0.10.5 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-use-timeout@2.1.0(react@18.2.0): + /@chakra-ui/react-use-timeout@2.1.0(react@18.3.1): resolution: {integrity: sha512-cFN0sobKMM9hXUhyCofx3/Mjlzah6ADaEl/AXl5Y+GawB5rgedgAcu2ErAgarEkwvsKdP6c68CKjQ9dmTQlJxQ==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/react-use-update-effect@2.1.0(react@18.2.0): + /@chakra-ui/react-use-update-effect@2.1.0(react@18.3.1): resolution: {integrity: sha512-ND4Q23tETaR2Qd3zwCKYOOS1dfssojPLJMLvUtUbW5M9uW1ejYWgGUobeAiOVfSplownG8QYMmHTP86p/v0lbA==} peerDependencies: react: '>=18' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react-utils@2.0.12(react@18.2.0): + /@chakra-ui/react-utils@2.0.12(react@18.3.1): resolution: {integrity: sha512-GbSfVb283+YA3kA8w8xWmzbjNWk14uhNpntnipHCftBibl0lxtQ9YqMFQLwuFOO0U2gYVocszqqDWX+XNKq9hw==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/utils': 2.0.15 - react: 18.2.0 + react: 18.3.1 dev: false - /@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.3.1)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==} peerDependencies: '@emotion/react': ^11.0.0 @@ -2495,69 +2522,69 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0) - '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/counter': 2.1.0(react@18.2.0) - '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.2.0) - '@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.73)(react@18.2.0) - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/hooks': 2.2.1(react@18.2.0) - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/live-region': 2.1.0(react@18.2.0) - '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0) - '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0) - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/provider': 2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-env': 3.1.0(react@18.2.0) - '@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1) + '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/counter': 2.1.0(react@18.3.1) + '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.3.1) + '@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/focus-lock': 2.1.0(@types/react@18.3.1)(react@18.3.1) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/hooks': 2.2.1(react@18.3.1) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/live-region': 2.1.0(react@18.3.1) + '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1) + '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.3.1)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/provider': 2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-env': 3.1.0(react@18.3.1) + '@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/styled-system': 2.9.2 - '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) '@chakra-ui/theme-utils': 2.0.21 - '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.2.0) + '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.3.1) '@chakra-ui/utils': 2.0.15 - '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.3.1)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==} peerDependencies: '@emotion/react': ^11.0.0 @@ -2566,162 +2593,162 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0) - '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/counter': 2.1.0(react@18.2.0) - '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.2.0) - '@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.73)(react@18.2.0) - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/hooks': 2.2.1(react@18.2.0) - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/live-region': 2.1.0(react@18.2.0) - '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0) - '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0) - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/provider': 2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-env': 3.1.0(react@18.2.0) - '@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1) + '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/counter': 2.1.0(react@18.3.1) + '@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.3.1) + '@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/focus-lock': 2.1.0(@types/react@18.3.1)(react@18.3.1) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/hooks': 2.2.1(react@18.3.1) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/live-region': 2.1.0(react@18.3.1) + '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1) + '@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.3.1)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/provider': 2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-env': 3.1.0(react@18.3.1) + '@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/styled-system': 2.9.2 - '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) '@chakra-ui/theme-utils': 2.0.21 - '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0) + '@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/transition': 2.1.0(framer-motion@11.1.8)(react@18.3.1) '@chakra-ui/utils': 2.0.15 - '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.3.1) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/select@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/select@2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/shared-utils@2.0.5: resolution: {integrity: sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==} dev: false - /@chakra-ui/skeleton@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/skeleton@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-JNRuMPpdZGd6zFVKjVQ0iusu3tXAdI29n4ZENYwAJEMf/fN0l12sVeirOxkJ7oEL0yOx2AgEYFSKdbcAgfUsAQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-use-previous': 2.1.0(react@18.2.0) + '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-use-previous': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/skip-nav@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/skip-nav@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-Hk+FG+vadBSH0/7hwp9LJnLjkO0RPGnx7gBJWI4/SpoJf3e4tZlWYtwGj0toYY4aGKl93jVghuwGbDBEMoHDug==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/slider@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/slider@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-lUOBcLMCnFZiA/s2NONXhELJh6sY5WtbRykPtclGfynqqOo47lwWJx+VP7xaeuhDOPcWSSecWc9Y1BfPOCz9cQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/number-utils': 2.0.7 - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-latest-ref': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-pan-event': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-size': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-callback-ref': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-latest-ref': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-pan-event': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-size': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/spinner@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/spinner@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-hczbnoXt+MMv/d3gE+hjQhmkzLiKuoTo42YhUG7Bs9OSv2lg1fZHW1fGNRFP3wTi6OIbD044U1P9HK+AOgFH3g==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/stat@2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/stat@2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-LDn0d/LXQNbAn2KaR3F1zivsZCewY4Jsy1qShmfBMKwn6rI8yVlbvu6SiA3OpHS0FhxbsZxQI6HefEoIgtqY6Q==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/stepper@2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/stepper@2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-ky77lZbW60zYkSXhYz7kbItUpAQfEdycT0Q4bkHLxfqbuiGMf8OmgZOQkOB9uM4v0zPwy2HXhe0vq4Dd0xa55Q==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/styled-system@2.9.2: @@ -2732,106 +2759,106 @@ packages: lodash.mergewith: 4.6.2 dev: false - /@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.2.0): + /@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react@18.3.1): resolution: {integrity: sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0): + /@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1): resolution: {integrity: sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/system@2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0): + /@chakra-ui/system@2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1): resolution: {integrity: sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==} peerDependencies: '@emotion/react': ^11.0.0 '@emotion/styled': ^11.0.0 react: '>=18' dependencies: - '@chakra-ui/color-mode': 2.2.0(react@18.2.0) + '@chakra-ui/color-mode': 2.2.0(react@18.3.1) '@chakra-ui/object-utils': 2.1.0 - '@chakra-ui/react-utils': 2.0.12(react@18.2.0) + '@chakra-ui/react-utils': 2.0.12(react@18.3.1) '@chakra-ui/styled-system': 2.9.2 '@chakra-ui/theme-utils': 2.0.21 '@chakra-ui/utils': 2.0.15 - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0) - react: 18.2.0 + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.3.1) + react: 18.3.1 react-fast-compare: 3.2.2 dev: false - /@chakra-ui/table@2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/table@2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-o5OrjoHCh5uCLdiUb0Oc0vq9rIAeHSIRScc2ExTC9Qg/uVZl2ygLrjToCaKfaaKl1oQexIeAcZDKvPG8tVkHyQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/react-context': 2.1.0(react@18.2.0) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/tabs@3.0.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/tabs@3.0.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-6Mlclp8L9lqXmsGWF5q5gmemZXOiOYuh0SGT/7PgJVNPz3LXREXlXg2an4MBUD8W5oTkduCX+3KTMCwRrVrDYw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/clickable': 2.1.0(react@18.2.0) - '@chakra-ui/descendant': 3.1.0(react@18.2.0) + '@chakra-ui/clickable': 2.1.0(react@18.3.1) + '@chakra-ui/descendant': 3.1.0(react@18.3.1) '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/react-children-utils': 2.0.6(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.2.0) + '@chakra-ui/react-children-utils': 2.0.6(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-controllable-state': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-safe-layout-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/tag@3.1.1(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/tag@3.1.1(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-Bdel79Dv86Hnge2PKOU+t8H28nm/7Y3cKd4Kfk9k3lOpUh4+nkSGe58dhRzht59lEqa4N9waCgQiBdkydjvBXQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/textarea@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/textarea@2.1.2(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-ip7tvklVCZUb2fOHDb23qPy/Fr2mzDOGdkrpbNi50hDCiV4hFX02jdQJdi3ydHZUyVgZVBKPOJ+lT9i7sKA2wA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/theme-tools@2.1.2(@chakra-ui/styled-system@2.9.2): @@ -2865,7 +2892,7 @@ packages: '@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2) dev: false - /@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==} peerDependencies: '@chakra-ui/system': 2.6.2 @@ -2873,22 +2900,22 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-timeout': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-timeout': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/styled-system': 2.9.2 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==} peerDependencies: '@chakra-ui/system': 2.6.2 @@ -2896,22 +2923,22 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-context': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-timeout': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0) + '@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-context': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-timeout': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-update-effect': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 '@chakra-ui/styled-system': 2.9.2 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) '@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -2920,20 +2947,20 @@ packages: react-dom: '>=18' dependencies: '@chakra-ui/dom-utils': 2.1.0 - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-disclosure': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0): + /@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -2942,39 +2969,39 @@ packages: react-dom: '>=18' dependencies: '@chakra-ui/dom-utils': 2.1.0 - '@chakra-ui/popper': 3.1.0(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react-types': 2.0.7(react@18.2.0) - '@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0) - '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0) + '@chakra-ui/popper': 3.1.0(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react-types': 2.0.7(react@18.3.1) + '@chakra-ui/react-use-disclosure': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-event-listener': 2.1.0(react@18.3.1) + '@chakra-ui/react-use-merge-refs': 2.1.0(react@18.3.1) '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /@chakra-ui/transition@2.1.0(framer-motion@10.18.0)(react@18.2.0): + /@chakra-ui/transition@2.1.0(framer-motion@10.18.0)(react@18.3.1): resolution: {integrity: sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==} peerDependencies: framer-motion: '>=4.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false - /@chakra-ui/transition@2.1.0(framer-motion@11.0.22)(react@18.2.0): + /@chakra-ui/transition@2.1.0(framer-motion@11.1.8)(react@18.3.1): resolution: {integrity: sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==} peerDependencies: framer-motion: '>=4.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 + framer-motion: 11.1.8(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 dev: false /@chakra-ui/utils@2.0.15: @@ -2986,14 +3013,14 @@ packages: lodash.mergewith: 4.6.2 dev: false - /@chakra-ui/visually-hidden@2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0): + /@chakra-ui/visually-hidden@2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1): resolution: {integrity: sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - react: 18.2.0 + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + react: 18.3.1 dev: false /@colors/colors@1.5.0: @@ -3003,14 +3030,14 @@ packages: dev: true optional: true - /@dagrejs/dagre@1.1.1: - resolution: {integrity: sha512-AQfT6pffEuPE32weFzhS/u3UpX+bRXUARIXL7UqLaxz497cN8pjuBlX6axO4IIECE2gBV8eLFQkGCtKX5sDaUA==} + /@dagrejs/dagre@1.1.2: + resolution: {integrity: sha512-F09dphqvHsbe/6C2t2unbmpr5q41BNPEfJCdn8Z7aEBpVSy/zFQ/b4SWsweQjWNsYMDvE2ffNUN8X0CeFsEGNw==} dependencies: - '@dagrejs/graphlib': 2.2.1 + '@dagrejs/graphlib': 2.2.2 dev: false - /@dagrejs/graphlib@2.2.1: - resolution: {integrity: sha512-xJsN1v6OAxXk6jmNdM+OS/bBE8nDCwM0yDNprXR18ZNatL6to9ggod9+l2XtiLhXfLm0NkE7+Er/cpdlM+SkUA==} + /@dagrejs/graphlib@2.2.2: + resolution: {integrity: sha512-CbyGpCDKsiTg/wuk79S7Muoj8mghDGAESWGxcSyhHX5jD35vYMBZochYVFzlHxynpE9unpu6O+4ZuhrLxASsOg==} engines: {node: '>17.0.0'} dev: false @@ -3019,46 +3046,46 @@ packages: engines: {node: '>=10.0.0'} dev: true - /@dnd-kit/accessibility@3.1.0(react@18.2.0): + /@dnd-kit/accessibility@3.1.0(react@18.3.1): resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==} peerDependencies: react: '>=16.8.0' dependencies: - react: 18.2.0 + react: 18.3.1 tslib: 2.6.2 dev: false - /@dnd-kit/core@6.1.0(react-dom@18.2.0)(react@18.2.0): + /@dnd-kit/core@6.1.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - '@dnd-kit/accessibility': 3.1.0(react@18.2.0) - '@dnd-kit/utilities': 3.2.2(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@dnd-kit/accessibility': 3.1.0(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) tslib: 2.6.2 dev: false - /@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.2.0): + /@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0)(react@18.3.1): resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==} peerDependencies: '@dnd-kit/core': ^6.1.0 react: '>=16.8.0' dependencies: - '@dnd-kit/core': 6.1.0(react-dom@18.2.0)(react@18.2.0) - '@dnd-kit/utilities': 3.2.2(react@18.2.0) - react: 18.2.0 + '@dnd-kit/core': 6.1.0(react-dom@18.3.1)(react@18.3.1) + '@dnd-kit/utilities': 3.2.2(react@18.3.1) + react: 18.3.1 tslib: 2.6.2 dev: false - /@dnd-kit/utilities@3.2.2(react@18.2.0): + /@dnd-kit/utilities@3.2.2(react@18.3.1): resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} peerDependencies: react: '>=16.8.0' dependencies: - react: 18.2.0 + react: 18.3.1 tslib: 2.6.2 dev: false @@ -3066,10 +3093,10 @@ packages: resolution: {integrity: sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==} dependencies: '@babel/helper-module-imports': 7.24.3 - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 '@emotion/hash': 0.9.1 '@emotion/memoize': 0.8.1 - '@emotion/serialize': 1.1.3 + '@emotion/serialize': 1.1.4 babel-plugin-macros: 3.1.0 convert-source-map: 1.9.0 escape-string-regexp: 4.0.0 @@ -3116,7 +3143,7 @@ packages: resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==} dev: false - /@emotion/react@11.11.4(@types/react@18.2.73)(react@18.2.0): + /@emotion/react@11.11.4(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==} peerDependencies: '@types/react': '*' @@ -3125,20 +3152,20 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 '@emotion/babel-plugin': 11.11.0 '@emotion/cache': 11.11.0 - '@emotion/serialize': 1.1.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@emotion/serialize': 1.1.4 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1) '@emotion/utils': 1.2.1 '@emotion/weak-memoize': 0.3.1 - '@types/react': 18.2.73 + '@types/react': 18.3.1 hoist-non-react-statics: 3.3.2 - react: 18.2.0 + react: 18.3.1 dev: false - /@emotion/serialize@1.1.3: - resolution: {integrity: sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==} + /@emotion/serialize@1.1.4: + resolution: {integrity: sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==} dependencies: '@emotion/hash': 0.9.1 '@emotion/memoize': 0.8.1 @@ -3151,8 +3178,8 @@ packages: resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==} dev: false - /@emotion/styled@11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0): - resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==} + /@emotion/styled@11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 '@types/react': '*' @@ -3161,27 +3188,27 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 '@emotion/babel-plugin': 11.11.0 '@emotion/is-prop-valid': 1.2.2 - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@emotion/serialize': 1.1.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@emotion/serialize': 1.1.4 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1) '@emotion/utils': 1.2.1 - '@types/react': 18.2.73 - react: 18.2.0 + '@types/react': 18.3.1 + react: 18.3.1 dev: false /@emotion/unitless@0.8.1: resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} dev: false - /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.2.0): + /@emotion/use-insertion-effect-with-fallbacks@1.0.1(react@18.3.1): resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==} peerDependencies: react: '>=16.8.0' dependencies: - react: 18.2.0 + react: 18.3.1 /@emotion/utils@1.2.1: resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==} @@ -3469,39 +3496,39 @@ packages: engines: {node: '>=14'} dev: true - /@floating-ui/core@1.6.0: - resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} + /@floating-ui/core@1.6.1: + resolution: {integrity: sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==} dependencies: - '@floating-ui/utils': 0.2.1 + '@floating-ui/utils': 0.2.2 dev: false /@floating-ui/dom@1.5.4: resolution: {integrity: sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==} dependencies: - '@floating-ui/core': 1.6.0 - '@floating-ui/utils': 0.2.1 + '@floating-ui/core': 1.6.1 + '@floating-ui/utils': 0.2.2 dev: false - /@floating-ui/dom@1.6.3: - resolution: {integrity: sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==} + /@floating-ui/dom@1.6.5: + resolution: {integrity: sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==} dependencies: - '@floating-ui/core': 1.6.0 - '@floating-ui/utils': 0.2.1 + '@floating-ui/core': 1.6.1 + '@floating-ui/utils': 0.2.2 dev: false - /@floating-ui/utils@0.2.1: - resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==} + /@floating-ui/utils@0.2.2: + resolution: {integrity: sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==} dev: false - /@fontsource-variable/inter@5.0.17: - resolution: {integrity: sha512-sa80nNnqF8kzhBvqusWiL9vlPMVpdmOwMmDBup46Jggsr1VBqo+YuzwB36Ls+X6uHJtb8Yv3ALBHL/zGmT862A==} + /@fontsource-variable/inter@5.0.18: + resolution: {integrity: sha512-rJzSrtJ3b7djiGFvRuTe6stDfbYJGhdQSfn2SI2WfXviee7Er0yKAHE5u7FU7OWVQQQ1x3+cxdmx9NdiAkcrcA==} dev: false /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} dependencies: - '@humanwhocodes/object-schema': 2.0.2 + '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: @@ -3513,8 +3540,8 @@ packages: engines: {node: '>=12.22'} dev: true - /@humanwhocodes/object-schema@2.0.2: - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + /@humanwhocodes/object-schema@2.0.3: + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} dev: true /@internationalized/date@3.5.3: @@ -3523,32 +3550,32 @@ packages: '@swc/helpers': 0.5.11 dev: false - /@internationalized/number@3.5.1: - resolution: {integrity: sha512-N0fPU/nz15SwR9IbfJ5xaS9Ss/O5h1sVXMZf43vc9mxEG48ovglvvzBjF53aHlq20uoR6c+88CrIXipU/LSzwg==} + /@internationalized/number@3.5.2: + resolution: {integrity: sha512-4FGHTi0rOEX1giSkt5MH4/te0eHBq3cvAYsfLlpguV6pzJAReXymiYpE5wPCqKqjkUO3PIsyvk+tBiIV1pZtbA==} dependencies: '@swc/helpers': 0.5.11 dev: false - /@invoke-ai/eslint-config-react@0.0.14(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.3): + /@invoke-ai/eslint-config-react@0.0.14(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.5): resolution: {integrity: sha512-6ZUY9zgdDhv2WUoLdDKOQdU9ImnH0CBOFtRlOaNOh34IOsNRfn+JA7wqA0PKnkiNrlfPkIQWhn4GRJp68NT5bw==} peerDependencies: eslint: ^8.56.0 prettier: ^3.2.5 typescript: ^5.3.3 dependencies: - '@typescript-eslint/eslint-plugin': 7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-config-prettier: 9.1.0(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.4.0)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.8.0)(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) eslint-plugin-react-refresh: 0.4.6(eslint@8.57.0) - eslint-plugin-simple-import-sort: 12.0.0(eslint@8.57.0) - eslint-plugin-storybook: 0.8.0(eslint@8.57.0)(typescript@5.4.3) - eslint-plugin-unused-imports: 3.1.0(@typescript-eslint/eslint-plugin@7.4.0)(eslint@8.57.0) + eslint-plugin-simple-import-sort: 12.1.0(eslint@8.57.0) + eslint-plugin-storybook: 0.8.0(eslint@8.57.0)(typescript@5.4.5) + eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.8.0)(eslint@8.57.0) prettier: 3.2.5 - typescript: 5.4.3 + typescript: 5.4.5 transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -3563,36 +3590,36 @@ packages: prettier: 3.2.5 dev: true - /@invoke-ai/ui-library@0.0.25(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.17)(@internationalized/date@3.5.3)(@types/react@18.2.73)(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0): + /@invoke-ai/ui-library@0.0.25(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.18)(@internationalized/date@3.5.3)(@types/react@18.3.1)(i18next@23.11.3)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Fmjdlu62NXHgairYXGjcuCrxPEAl1G6Q6ban8g3excF6pDDdBeS7CmSNCyEDMxnSIOZrQlI04OhaMB17Imi9Uw==} peerDependencies: '@fontsource-variable/inter': ^5.0.16 react: ^18.2.0 react-dom: ^18.2.0 dependencies: - '@ark-ui/react': 1.3.0(@internationalized/date@3.5.3)(react-dom@18.2.0)(react@18.2.0) + '@ark-ui/react': 1.3.0(@internationalized/date@3.5.3)(react-dom@18.3.1)(react@18.3.1) '@chakra-ui/anatomy': 2.2.2 - '@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0) - '@chakra-ui/react': 2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/portal': 2.1.0(react-dom@18.3.1)(react@18.3.1) + '@chakra-ui/react': 2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(@types/react@18.3.1)(framer-motion@10.18.0)(react-dom@18.3.1)(react@18.3.1) '@chakra-ui/styled-system': 2.9.2 '@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2) - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0) - '@fontsource-variable/inter': 5.0.17 - '@nanostores/react': 0.7.2(nanostores@0.9.5)(react@18.2.0) - chakra-react-select: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.4)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0) + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.1)(react@18.3.1) + '@fontsource-variable/inter': 5.0.18 + '@nanostores/react': 0.7.2(nanostores@0.9.5)(react@18.3.1) + chakra-react-select: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.4)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + framer-motion: 10.18.0(react-dom@18.3.1)(react@18.3.1) lodash-es: 4.17.21 nanostores: 0.9.5 - overlayscrollbars: 2.6.1 - overlayscrollbars-react: 0.5.5(overlayscrollbars@2.6.1)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-i18next: 14.1.0(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0) - react-icons: 5.0.1(react@18.2.0) - react-select: 5.8.0(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + overlayscrollbars: 2.7.3 + overlayscrollbars-react: 0.5.6(overlayscrollbars@2.7.3)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-i18next: 14.1.1(i18next@23.11.3)(react-dom@18.3.1)(react@18.3.1) + react-icons: 5.2.0(react@18.3.1) + react-select: 5.8.0(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@chakra-ui/form-control' - '@chakra-ui/icon' @@ -3618,6 +3645,11 @@ packages: wrap-ansi-cjs: /wrap-ansi@7.0.0 dev: true + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3625,7 +3657,7 @@ packages: '@sinclair/typebox': 0.27.8 dev: true - /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.4.3)(vite@5.2.6): + /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.4.5)(vite@5.2.11): resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==} peerDependencies: typescript: '>= 4.3.x' @@ -3637,9 +3669,9 @@ packages: glob: 7.2.3 glob-promise: 4.2.2(glob@7.2.3) magic-string: 0.27.0 - react-docgen-typescript: 2.2.2(typescript@5.4.3) - typescript: 5.4.3 - vite: 5.2.6(@types/node@20.11.30) + react-docgen-typescript: 2.2.2(typescript@5.4.5) + typescript: 5.4.5 + vite: 5.2.11(@types/node@20.12.10) dev: true /@jridgewell/gen-mapping@0.3.5: @@ -3671,38 +3703,38 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@mdx-js/react@3.0.1(@types/react@18.2.73)(react@18.2.0): + /@mdx-js/react@3.0.1(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==} peerDependencies: '@types/react': '>=16' react: '>=16' dependencies: - '@types/mdx': 2.0.12 - '@types/react': 18.2.73 - react: 18.2.0 + '@types/mdx': 2.0.13 + '@types/react': 18.3.1 + react: 18.3.1 dev: true - /@microsoft/api-extractor-model@7.28.13(@types/node@20.11.30): + /@microsoft/api-extractor-model@7.28.13(@types/node@20.12.10): resolution: {integrity: sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==} dependencies: '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.11.30) + '@rushstack/node-core-library': 4.0.2(@types/node@20.12.10) transitivePeerDependencies: - '@types/node' dev: true - /@microsoft/api-extractor@7.43.0(@types/node@20.11.30): + /@microsoft/api-extractor@7.43.0(@types/node@20.12.10): resolution: {integrity: sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==} hasBin: true dependencies: - '@microsoft/api-extractor-model': 7.28.13(@types/node@20.11.30) + '@microsoft/api-extractor-model': 7.28.13(@types/node@20.12.10) '@microsoft/tsdoc': 0.14.2 '@microsoft/tsdoc-config': 0.16.2 - '@rushstack/node-core-library': 4.0.2(@types/node@20.11.30) + '@rushstack/node-core-library': 4.0.2(@types/node@20.12.10) '@rushstack/rig-package': 0.5.2 - '@rushstack/terminal': 0.10.0(@types/node@20.11.30) - '@rushstack/ts-command-line': 4.19.1(@types/node@20.11.30) + '@rushstack/terminal': 0.10.0(@types/node@20.12.10) + '@rushstack/ts-command-line': 4.19.1(@types/node@20.12.10) lodash: 4.17.21 minimatch: 3.0.8 resolve: 1.22.8 @@ -3726,18 +3758,18 @@ packages: resolution: {integrity: sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==} dev: true - /@nanostores/react@0.7.2(nanostores@0.10.0)(react@18.2.0): + /@nanostores/react@0.7.2(nanostores@0.10.3)(react@18.3.1): resolution: {integrity: sha512-e3OhHJFv3NMSFYDgREdlAQqkyBTHJM91s31kOZ4OvZwJKdFk5BLk0MLbh51EOGUz9QGX2aCHfy1RvweSi7fgwA==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: nanostores: ^0.9.0 || ^0.10.0 react: '>=18.0.0' dependencies: - nanostores: 0.10.0 - react: 18.2.0 + nanostores: 0.10.3 + react: 18.3.1 dev: false - /@nanostores/react@0.7.2(nanostores@0.9.5)(react@18.2.0): + /@nanostores/react@0.7.2(nanostores@0.9.5)(react@18.3.1): resolution: {integrity: sha512-e3OhHJFv3NMSFYDgREdlAQqkyBTHJM91s31kOZ4OvZwJKdFk5BLk0MLbh51EOGUz9QGX2aCHfy1RvweSi7fgwA==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: @@ -3745,7 +3777,7 @@ packages: react: '>=18.0.0' dependencies: nanostores: 0.9.5 - react: 18.2.0 + react: 18.3.1 dev: false /@ndelangen/get-tarball@3.0.9: @@ -3798,59 +3830,6 @@ packages: fastq: 1.17.1 dev: true - /@npmcli/git@5.0.4: - resolution: {integrity: sha512-nr6/WezNzuYUppzXRaYu/W4aT5rLxdXqEFupbh6e/ovlYFQ8hpu1UUPV3Ir/YTl+74iXl2ZOMlGzudh9ZPUchQ==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - '@npmcli/promise-spawn': 7.0.1 - lru-cache: 10.2.0 - npm-pick-manifest: 9.0.0 - proc-log: 3.0.0 - promise-inflight: 1.0.1 - promise-retry: 2.0.1 - semver: 7.6.0 - which: 4.0.0 - transitivePeerDependencies: - - bluebird - dev: true - - /@npmcli/map-workspaces@3.0.4: - resolution: {integrity: sha512-Z0TbvXkRbacjFFLpVpV0e2mheCh+WzQpcqL+4xp49uNJOxOnIAPZyXtUxZ5Qn3QBTGKA11Exjd9a5411rBrhDg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - '@npmcli/name-from-folder': 2.0.0 - glob: 10.3.10 - minimatch: 9.0.3 - read-package-json-fast: 3.0.2 - dev: true - - /@npmcli/name-from-folder@2.0.0: - resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true - - /@npmcli/package-json@5.0.0: - resolution: {integrity: sha512-OI2zdYBLhQ7kpNPaJxiflofYIpkNLi+lnGdzqUOfRmCF3r2l1nadcjtCYMJKv/Utm/ZtlffaUuTiAktPHbc17g==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - '@npmcli/git': 5.0.4 - glob: 10.3.10 - hosted-git-info: 7.0.1 - json-parse-even-better-errors: 3.0.1 - normalize-package-data: 6.0.0 - proc-log: 3.0.0 - semver: 7.6.0 - transitivePeerDependencies: - - bluebird - dev: true - - /@npmcli/promise-spawn@7.0.1: - resolution: {integrity: sha512-P4KkF9jX3y+7yFUxgcUdDtLy+t4OlDGuEBLNs57AZsfSfg+uV6MLndqGpnl4831ggaEdXwR50XFoZP4VFtHolg==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - which: 4.0.0 - dev: true - /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -3858,135 +3837,15 @@ packages: dev: true optional: true - /@pnpm/constants@7.1.1: - resolution: {integrity: sha512-31pZqMtjwV+Vaq7MaPrT1EoDFSYwye3dp6BiHIGRJmVThCQwySRKM7hCvqqI94epNkqFAAYoWrNynWoRYosGdw==} - engines: {node: '>=16.14'} - dev: true - - /@pnpm/core-loggers@9.0.6(@pnpm/logger@5.0.0): - resolution: {integrity: sha512-iK67SGbp+06bA/elpg51wygPFjNA7JKHtKkpLxqXXHw+AjFFBC3f2OznJsCIuDK6HdGi5UhHLYqo5QxJ2gMqJQ==} - engines: {node: '>=16.14'} - peerDependencies: - '@pnpm/logger': ^5.0.0 - dependencies: - '@pnpm/logger': 5.0.0 - '@pnpm/types': 9.4.2 - dev: true - - /@pnpm/error@5.0.3: - resolution: {integrity: sha512-ONJU5cUeoeJSy50qOYsMZQHTA/9QKmGgh1ATfEpCLgtbdwqUiwD9MxHNeXUYYI/pocBCz6r1ZCFqiQvO+8SUKA==} - engines: {node: '>=16.14'} - dependencies: - '@pnpm/constants': 7.1.1 - dev: true - - /@pnpm/fetching-types@5.0.0: - resolution: {integrity: sha512-o9gdO1v8Uc5P2fBBuW6GSpfTqIivQmQlqjQJdFiQX0m+tgxlrMRneIg392jZuc6fk7kFqjLheInlslgJfwY+4Q==} - engines: {node: '>=16.14'} - dependencies: - '@zkochan/retry': 0.2.0 - node-fetch: 3.0.0-beta.9 - transitivePeerDependencies: - - domexception - dev: true - - /@pnpm/graceful-fs@3.2.0: - resolution: {integrity: sha512-vRoXJxscDpHak7YE9SqCkzfrayn+Lw+YueOeHIPEqkgokrHeYgYeONoc2kGh0ObHaRtNSsonozVfJ456kxLNvA==} - engines: {node: '>=16.14'} - dependencies: - graceful-fs: 4.2.11 - dev: true - - /@pnpm/logger@5.0.0: - resolution: {integrity: sha512-YfcB2QrX+Wx1o6LD1G2Y2fhDhOix/bAY/oAnMpHoNLsKkWIRbt1oKLkIFvxBMzLwAEPqnYWguJrYC+J6i4ywbw==} - engines: {node: '>=12.17'} - dependencies: - bole: 5.0.11 - ndjson: 2.0.0 - dev: true - - /@pnpm/npm-package-arg@1.0.0: - resolution: {integrity: sha512-oQYP08exi6mOPdAZZWcNIGS+KKPsnNwUBzSuAEGWuCcqwMAt3k/WVCqVIXzBxhO5sP2b43og69VHmPj6IroKqw==} - engines: {node: '>=14.6'} - dependencies: - hosted-git-info: 4.1.0 - semver: 7.6.0 - validate-npm-package-name: 4.0.0 - dev: true - - /@pnpm/npm-resolver@18.1.1(@pnpm/logger@5.0.0): - resolution: {integrity: sha512-NptzncmMD5ZMimbjWkGpMzuBRhlCY+sh7mzypPdBOTNlh5hmEQe/VaRKjNK4V9/b0C/llElkvIePL6acybu86w==} - engines: {node: '>=16.14'} - peerDependencies: - '@pnpm/logger': ^5.0.0 - dependencies: - '@pnpm/core-loggers': 9.0.6(@pnpm/logger@5.0.0) - '@pnpm/error': 5.0.3 - '@pnpm/fetching-types': 5.0.0 - '@pnpm/graceful-fs': 3.2.0 - '@pnpm/logger': 5.0.0 - '@pnpm/resolve-workspace-range': 5.0.1 - '@pnpm/resolver-base': 11.1.0 - '@pnpm/types': 9.4.2 - '@zkochan/retry': 0.2.0 - encode-registry: 3.0.1 - load-json-file: 6.2.0 - lru-cache: 10.2.0 - normalize-path: 3.0.0 - p-limit: 3.1.0 - p-memoize: 4.0.1 - parse-npm-tarball-url: 3.0.0 - path-temp: 2.1.0 - ramda: /@pnpm/ramda@0.28.1 - rename-overwrite: 5.0.0 - semver: 7.6.0 - ssri: 10.0.5 - version-selector-type: 3.0.0 - transitivePeerDependencies: - - domexception - dev: true - - /@pnpm/ramda@0.28.1: - resolution: {integrity: sha512-zcAG+lvU0fMziNeGXpPyCyCJYp5ZVrPElEE4t14jAmViaihohocZ+dDkcRIyAomox8pQsuZnv1EyHR+pOhmUWw==} - dev: true - - /@pnpm/resolve-workspace-range@5.0.1: - resolution: {integrity: sha512-yQ0pMthlw8rTgS/C9hrjne+NEnnSNevCjtdodd7i15I59jMBYciHifZ/vjg0NY+Jl+USTc3dBE+0h/4tdYjMKg==} - engines: {node: '>=16.14'} - dependencies: - semver: 7.6.0 - dev: true - - /@pnpm/resolver-base@11.1.0: - resolution: {integrity: sha512-y2qKaj18pwe1VWc3YXEitdYFo+WqOOt60aqTUuOVkJAirUzz0DzuYh3Ifct4znYWPdgUXHaN5DMphNF5iL85rA==} - engines: {node: '>=16.14'} - dependencies: - '@pnpm/types': 9.4.2 - dev: true - - /@pnpm/types@9.4.2: - resolution: {integrity: sha512-g1hcF8Nv4gd76POilz9gD4LITAPXOe5nX4ijgr8ixCbLQZfcpYiMfJ+C1RlMNRUDo8vhlNB4O3bUlxmT6EAQXA==} - engines: {node: '>=16.14'} - dev: true - - /@pnpm/workspace.pkgs-graph@2.0.15(@pnpm/logger@5.0.0): - resolution: {integrity: sha512-Txxd5FzzVfBfGCTngISaxFlJzZhzdS8BUrCEtAWJfZOFbQzpWy27rzkaS7TaWW2dHiFcCVYzPI/2vgxfeRansA==} - engines: {node: '>=16.14'} - dependencies: - '@pnpm/npm-package-arg': 1.0.0 - '@pnpm/npm-resolver': 18.1.1(@pnpm/logger@5.0.0) - '@pnpm/resolve-workspace-range': 5.0.1 - ramda: /@pnpm/ramda@0.28.1 - transitivePeerDependencies: - - '@pnpm/logger' - - domexception + /@polka/url@1.0.0-next.25: + resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} dev: true /@popperjs/core@2.11.8: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false - /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.73)(react@18.2.0): + /@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: '@types/react': '*' @@ -3995,12 +3854,12 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.1 - '@types/react': 18.2.73 - react: 18.2.0 + '@babel/runtime': 7.24.5 + '@types/react': 18.3.1 + react: 18.3.1 dev: true - /@radix-ui/react-slot@1.0.2(@types/react@18.2.73)(react@18.2.0): + /@radix-ui/react-slot@1.0.2(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: '@types/react': '*' @@ -4009,46 +3868,46 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.24.1 - '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.73)(react@18.2.0) - '@types/react': 18.2.73 - react: 18.2.0 + '@babel/runtime': 7.24.5 + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.1)(react@18.3.1) + '@types/react': 18.3.1 + react: 18.3.1 dev: true - /@reactflow/background@11.3.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-byj/G9pEC8tN0wT/ptcl/LkEP/BBfa33/SvBkqE4XwyofckqF87lKp573qGlisfnsijwAbpDlf81PuFL41So4Q==} + /@reactflow/background@11.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-hkvpVEhgvfTDyCvdlitw4ioKCYLaaiRXnuEG+1QM3Np+7N1DiWF1XOv5I8AFyNoJL07yXEkbECUTsHvkBvcG5A==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.5.2(@types/react@18.2.73)(react@18.2.0) + '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/controls@11.2.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-e8nWplbYfOn83KN1BrxTXS17+enLyFnjZPbyDgHSRLtI5ZGPKF/8iRXV+VXb2LFVzlu4Wh3la/pkxtfP/0aguA==} + /@reactflow/controls@11.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-3xgEg6ALIVkAQCS4NiBjb7ad8Cb3D8CtA7Vvl4Hf5Ar2PIVs6FOaeft9s2iDZGtsWP35ECDYId1rIFVhQL8r+A==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.5.2(@types/react@18.2.73)(react@18.2.0) + '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/core@11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-j3i9b2fsTX/sBbOm+RmNzYEFWbNx4jGWGuGooh2r1jQaE2eV+TLJgiG/VNOp0q5mBl9f6g1IXs3Gm86S9JfcGw==} + /@reactflow/core@11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-+adHdUa7fJSEM93fWfjQwyWXeI92a1eLKwWbIstoCakHpL8UjzwhEh6sn+mN2h/59MlVI7Ehr1iGTt3MsfcIFA==} peerDependencies: react: '>=17' react-dom: '>=17' @@ -4057,74 +3916,74 @@ packages: '@types/d3-drag': 3.0.7 '@types/d3-selection': 3.0.10 '@types/d3-zoom': 3.0.8 - classcat: 5.0.4 + classcat: 5.0.5 d3-drag: 3.0.0 d3-selection: 3.0.0 d3-zoom: 3.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.5.2(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/minimap@11.7.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-le95jyTtt3TEtJ1qa7tZ5hyM4S7gaEQkW43cixcMOZLu33VAdc2aCpJg/fXcRrrf7moN2Mbl9WIMNXUKsp5ILA==} + /@reactflow/minimap@11.7.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-m2MvdiGSyOu44LEcERDEl1Aj6x//UQRWo3HEAejNU4HQTlJnYrSN8tgrYF8TxC1+c/9UdyzQY5VYgrTwW4QWdg==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) '@types/d3-selection': 3.0.10 '@types/d3-zoom': 3.0.8 - classcat: 5.0.4 + classcat: 5.0.5 d3-selection: 3.0.0 d3-zoom: 3.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.5.2(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/node-resizer@2.2.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-HfickMm0hPDIHt9qH997nLdgLt0kayQyslKE0RS/GZvZ4UMQJlx/NRRyj5y47Qyg0NnC66KYOQWDM9LLzRTnUg==} + /@reactflow/node-resizer@2.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-X7ceQ2s3jFLgbkg03n2RYr4hm3jTVrzkW2W/8ANv/SZfuVmF8XJxlERuD8Eka5voKqLda0ywIZGAbw9GoHLfUQ==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 + '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + classcat: 5.0.5 d3-drag: 3.0.0 d3-selection: 3.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.5.2(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reactflow/node-toolbar@1.3.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-VmgxKmToax4sX1biZ9LXA7cj/TBJ+E5cklLGwquCCVVxh+lxpZGTBF3a5FJGVHiUNBBtFsC8ldcSZIK4cAlQww==} + /@reactflow/node-toolbar@1.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-aknvNICO10uWdthFSpgD6ctY/CTBeJUMV9co8T9Ilugr08Nb89IQ4uD0dPmr031ewMQxixtYIkw+sSDDzd2aaQ==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/core': 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - classcat: 5.0.4 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - zustand: 4.5.2(@types/react@18.2.73)(react@18.2.0) + '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /@reduxjs/toolkit@2.2.2(react-redux@9.1.0)(react@18.2.0): - resolution: {integrity: sha512-454GZrEx3G6QSYwIx9ROaso1HR6sTH8qyZBe3KEsdWVGU3ayV8jYCwdaEJV3vl9V6+pi3GRl+7Xl7AeDna6qwQ==} + /@reduxjs/toolkit@2.2.3(react-redux@9.1.2)(react@18.3.1): + resolution: {integrity: sha512-76dll9EnJXg4EVcI5YNxZA/9hSAmZsFqzMmNRHvIlzw2WS/twfcVX3ysYrWGJMClwEmChQFC4yRq74tn6fdzRA==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -4134,9 +3993,9 @@ packages: react-redux: optional: true dependencies: - immer: 10.0.4 - react: 18.2.0 - react-redux: 9.1.0(@types/react@18.2.73)(react@18.2.0)(redux@5.0.1) + immer: 10.1.1 + react: 18.3.1 + react-redux: 9.1.2(@types/react@18.3.1)(react@18.3.1)(redux@5.0.1) redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.0 @@ -4147,7 +4006,7 @@ packages: engines: {node: '>=12.0'} dependencies: boolean: 3.2.0 - globalthis: 1.0.3 + globalthis: 1.0.4 liqe: 3.8.0 dev: false @@ -4173,119 +4032,135 @@ packages: picomatch: 2.3.1 dev: true - /@rollup/rollup-android-arm-eabi@4.13.1: - resolution: {integrity: sha512-4C4UERETjXpC4WpBXDbkgNVgHyWfG3B/NKY46e7w5H134UDOFqUJKpsLm0UYmuupW+aJmRgeScrDNfvZ5WV80A==} + /@rollup/rollup-android-arm-eabi@4.17.2: + resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.13.1: - resolution: {integrity: sha512-TrTaFJ9pXgfXEiJKQ3yQRelpQFqgRzVR9it8DbeRzG0RX7mKUy0bqhCFsgevwXLJepQKTnLl95TnPGf9T9AMOA==} + /@rollup/rollup-android-arm64@4.17.2: + resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.13.1: - resolution: {integrity: sha512-fz7jN6ahTI3cKzDO2otQuybts5cyu0feymg0bjvYCBrZQ8tSgE8pc0sSNEuGvifrQJWiwx9F05BowihmLxeQKw==} + /@rollup/rollup-darwin-arm64@4.17.2: + resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.13.1: - resolution: {integrity: sha512-WTvdz7SLMlJpektdrnWRUN9C0N2qNHwNbWpNo0a3Tod3gb9leX+yrYdCeB7VV36OtoyiPAivl7/xZ3G1z5h20g==} + /@rollup/rollup-darwin-x64@4.17.2: + resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.13.1: - resolution: {integrity: sha512-dBHQl+7wZzBYcIF6o4k2XkAfwP2ks1mYW2q/Gzv9n39uDcDiAGDqEyml08OdY0BIct0yLSPkDTqn4i6czpBLLw==} + /@rollup/rollup-linux-arm-gnueabihf@4.17.2: + resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.13.1: - resolution: {integrity: sha512-bur4JOxvYxfrAmocRJIW0SADs3QdEYK6TQ7dTNz6Z4/lySeu3Z1H/+tl0a4qDYv0bCdBpUYM0sYa/X+9ZqgfSQ==} + /@rollup/rollup-linux-arm-musleabihf@4.17.2: + resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.17.2: + resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.13.1: - resolution: {integrity: sha512-ssp77SjcDIUSoUyj7DU7/5iwM4ZEluY+N8umtCT9nBRs3u045t0KkW02LTyHouHDomnMXaXSZcCSr2bdMK63kA==} + /@rollup/rollup-linux-arm64-musl@4.17.2: + resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.13.1: - resolution: {integrity: sha512-Jv1DkIvwEPAb+v25/Unrnnq9BO3F5cbFPT821n3S5litkz+O5NuXuNhqtPx5KtcwOTtaqkTsO+IVzJOsxd11aQ==} + /@rollup/rollup-linux-powerpc64le-gnu@4.17.2: + resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.17.2: + resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-s390x-gnu@4.13.1: - resolution: {integrity: sha512-U564BrhEfaNChdATQaEODtquCC7Ez+8Hxz1h5MAdMYj0AqD0GA9rHCpElajb/sQcaFL6NXmHc5O+7FXpWMa73Q==} + /@rollup/rollup-linux-s390x-gnu@4.17.2: + resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.13.1: - resolution: {integrity: sha512-zGRDulLTeDemR8DFYyFIQ8kMP02xpUsX4IBikc7lwL9PrwR3gWmX2NopqiGlI2ZVWMl15qZeUjumTwpv18N7sQ==} + /@rollup/rollup-linux-x64-gnu@4.17.2: + resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.13.1: - resolution: {integrity: sha512-VTk/MveyPdMFkYJJPCkYBw07KcTkGU2hLEyqYMsU4NjiOfzoaDTW9PWGRsNwiOA3qI0k/JQPjkl/4FCK1smskQ==} + /@rollup/rollup-linux-x64-musl@4.17.2: + resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.13.1: - resolution: {integrity: sha512-L+hX8Dtibb02r/OYCsp4sQQIi3ldZkFI0EUkMTDwRfFykXBPptoz/tuuGqEd3bThBSLRWPR6wsixDSgOx/U3Zw==} + /@rollup/rollup-win32-arm64-msvc@4.17.2: + resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.13.1: - resolution: {integrity: sha512-+dI2jVPfM5A8zme8riEoNC7UKk0Lzc7jCj/U89cQIrOjrZTCWZl/+IXUeRT2rEZ5j25lnSA9G9H1Ob9azaF/KQ==} + /@rollup/rollup-win32-ia32-msvc@4.17.2: + resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.13.1: - resolution: {integrity: sha512-YY1Exxo2viZ/O2dMHuwQvimJ0SqvL+OAWQLLY6rvXavgQKjhQUzn7nc1Dd29gjB5Fqi00nrBWctJBOyfVMIVxw==} + /@rollup/rollup-win32-x64-msvc@4.17.2: + resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /@rushstack/node-core-library@4.0.2(@types/node@20.11.30): + /@rushstack/node-core-library@4.0.2(@types/node@20.12.10): resolution: {integrity: sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==} peerDependencies: '@types/node': '*' @@ -4293,7 +4168,7 @@ packages: '@types/node': optional: true dependencies: - '@types/node': 20.11.30 + '@types/node': 20.12.10 fs-extra: 7.0.1 import-lazy: 4.0.0 jju: 1.4.0 @@ -4309,7 +4184,7 @@ packages: strip-json-comments: 3.1.1 dev: true - /@rushstack/terminal@0.10.0(@types/node@20.11.30): + /@rushstack/terminal@0.10.0(@types/node@20.12.10): resolution: {integrity: sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==} peerDependencies: '@types/node': '*' @@ -4317,15 +4192,15 @@ packages: '@types/node': optional: true dependencies: - '@rushstack/node-core-library': 4.0.2(@types/node@20.11.30) - '@types/node': 20.11.30 + '@rushstack/node-core-library': 4.0.2(@types/node@20.12.10) + '@types/node': 20.12.10 supports-color: 8.1.1 dev: true - /@rushstack/ts-command-line@4.19.1(@types/node@20.11.30): + /@rushstack/ts-command-line@4.19.1(@types/node@20.12.10): resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} dependencies: - '@rushstack/terminal': 0.10.0(@types/node@20.11.30) + '@rushstack/terminal': 0.10.0(@types/node@20.12.10) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 @@ -4347,14 +4222,14 @@ packages: p-map: 4.0.0 dev: true - /@socket.io/component-emitter@3.1.0: - resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + /@socket.io/component-emitter@3.1.2: + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} dev: false - /@storybook/addon-actions@8.0.4: - resolution: {integrity: sha512-EyCWo+8T11/TJGYNL/AXtW4yaB+q1v2E9mixbumryCLxpTl2NtaeGZ4e0dlwfIMuw/7RWgHk2uIypcIPR/UANQ==} + /@storybook/addon-actions@8.0.10: + resolution: {integrity: sha512-IEuc30UAFl7Ws0GwaY/whjBnGaViVEVjmPc+MXUym2wwwJbnCbI+BKJxPoYi/I7QJb5aUNToAE6pl2pDda2g3Q==} dependencies: - '@storybook/core-events': 8.0.4 + '@storybook/core-events': 8.0.10 '@storybook/global': 5.0.0 '@types/uuid': 9.0.8 dequal: 2.0.3 @@ -4362,18 +4237,18 @@ packages: uuid: 9.0.1 dev: true - /@storybook/addon-backgrounds@8.0.4: - resolution: {integrity: sha512-fef0KD2GhJx2zpicOf8iL7k2LiIsNzEbGaQpIIjoy4DMqM1hIfNCt3DGTLH7LN5O8G+NVCLS1xmQg7RLvIVSCA==} + /@storybook/addon-backgrounds@8.0.10: + resolution: {integrity: sha512-445SUQqOH5xFJWlNeMu74FEgk26O9Zm/5aqnvmeteB0Q2JLaw7k2q9i/W6XFu97QkRxqA1EGbDxLR3+e1xCjaA==} dependencies: '@storybook/global': 5.0.0 memoizerific: 1.11.3 ts-dedent: 2.2.0 dev: true - /@storybook/addon-controls@8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-K5EYBTsUOTJlvIdA7p6Xj31wnV+RbZAkk56UKQvA7nJD7oDuLOq3E9u46F/uZD1vxddd9zFhf2iONfMe3KTTwQ==} + /@storybook/addon-controls@8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-MAUtIJGayNSsfn3VZ6SjQwpRkb4ky+10oVfos+xX9GQ5+7RCs+oYMuE4+aiQvvfXNdV8v0pUGPUPeUzqfJmhOA==} dependencies: - '@storybook/blocks': 8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@storybook/blocks': 8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) lodash: 4.17.21 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -4384,26 +4259,26 @@ packages: - supports-color dev: true - /@storybook/addon-docs@8.0.4: - resolution: {integrity: sha512-m0Y7qGAMnNPLEOEgzW/SBm8GX0xabJBaRN+aYijO6UKTln7F6oXXVve+xPC0Y4s6Gc9HZFdJY8WXZr1YSGEUVA==} + /@storybook/addon-docs@8.0.10: + resolution: {integrity: sha512-y+Agoez/hXZHKUMIZHU96T5V1v0cs4ArSNfjqDg9DPYcyQ88ihJNb6ZabIgzmEaJF/NncCW+LofWeUtkTwalkw==} dependencies: - '@babel/core': 7.24.3 - '@mdx-js/react': 3.0.1(@types/react@18.2.73)(react@18.2.0) - '@storybook/blocks': 8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 8.0.4 - '@storybook/components': 8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-plugin': 8.0.4 - '@storybook/csf-tools': 8.0.4 + '@babel/core': 7.24.5 + '@mdx-js/react': 3.0.1(@types/react@18.3.1)(react@18.3.1) + '@storybook/blocks': 8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@storybook/client-logger': 8.0.10 + '@storybook/components': 8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@storybook/csf-plugin': 8.0.10 + '@storybook/csf-tools': 8.0.10 '@storybook/global': 5.0.0 - '@storybook/node-logger': 8.0.4 - '@storybook/preview-api': 8.0.4 - '@storybook/react-dom-shim': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.4 - '@types/react': 18.2.73 + '@storybook/node-logger': 8.0.10 + '@storybook/preview-api': 8.0.10 + '@storybook/react-dom-shim': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/theming': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/types': 8.0.10 + '@types/react': 18.3.1 fs-extra: 11.2.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) rehype-external-links: 3.0.0 rehype-slug: 6.0.0 ts-dedent: 2.2.0 @@ -4412,22 +4287,22 @@ packages: - supports-color dev: true - /@storybook/addon-essentials@8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-mUIqhAkSz6Qv7nRqAAyCqMLiXBWVsY/8qN7HEIoaMQgdFq38KW3rYwNdzd2JLeXNWP1bBXwfvfcFe7/eqhYJFA==} + /@storybook/addon-essentials@8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-Uy3+vm7QX+b/9rhW/iFa3EYAAbV1T2LljY9Bj4aTPZHas9Bpvl5ZPnOm/PhybcE8UFHEoVTJ0v3uWb0dsUEigw==} dependencies: - '@storybook/addon-actions': 8.0.4 - '@storybook/addon-backgrounds': 8.0.4 - '@storybook/addon-controls': 8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-docs': 8.0.4 - '@storybook/addon-highlight': 8.0.4 - '@storybook/addon-measure': 8.0.4 - '@storybook/addon-outline': 8.0.4 - '@storybook/addon-toolbars': 8.0.4 - '@storybook/addon-viewport': 8.0.4 - '@storybook/core-common': 8.0.4 - '@storybook/manager-api': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 8.0.4 - '@storybook/preview-api': 8.0.4 + '@storybook/addon-actions': 8.0.10 + '@storybook/addon-backgrounds': 8.0.10 + '@storybook/addon-controls': 8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@storybook/addon-docs': 8.0.10 + '@storybook/addon-highlight': 8.0.10 + '@storybook/addon-measure': 8.0.10 + '@storybook/addon-outline': 8.0.10 + '@storybook/addon-toolbars': 8.0.10 + '@storybook/addon-viewport': 8.0.10 + '@storybook/core-common': 8.0.10 + '@storybook/manager-api': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/node-logger': 8.0.10 + '@storybook/preview-api': 8.0.10 ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' @@ -4437,19 +4312,19 @@ packages: - supports-color dev: true - /@storybook/addon-highlight@8.0.4: - resolution: {integrity: sha512-tnEiVaJlXL07v8JBox+QtRPVruoy0YovOTAOWY7fKDiKzF1I9wLaJjQF3wOsvwspHTHu00OZw2gsazgXiH4wLQ==} + /@storybook/addon-highlight@8.0.10: + resolution: {integrity: sha512-40GB82t1e2LCCjqXcC6Z5lq1yIpA1+Yl5E2tKeggOVwg5HHAX02ESNDdBaIOlCqMkU3WKzjGPurDNOLUAbsV2g==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/addon-interactions@8.0.4(vitest@1.4.0): - resolution: {integrity: sha512-wTEOnVUbF1lNJxxocr5IKmpgnmwyO8YsQf6Baw3tTWCHAa/MaWWQYq1OA6CfFfmVGGRjv/w2GTuf1Vyq99O7mg==} + /@storybook/addon-interactions@8.0.10(vitest@1.6.0): + resolution: {integrity: sha512-6yFNmk6+7082/8TRVyjUsKlwumalEdO0XQ5amPbVGuECzc3HFn0ELwzPrQ4TBlN5MRtX4+buoh5dc/1RUDrh9w==} dependencies: '@storybook/global': 5.0.0 - '@storybook/instrumenter': 8.0.4 - '@storybook/test': 8.0.4(vitest@1.4.0) - '@storybook/types': 8.0.4 + '@storybook/instrumenter': 8.0.10 + '@storybook/test': 8.0.10(vitest@1.6.0) + '@storybook/types': 8.0.10 polished: 4.3.1 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -4460,54 +4335,54 @@ packages: - vitest dev: true - /@storybook/addon-links@8.0.4(react@18.2.0): - resolution: {integrity: sha512-SzE+JPZ4mxjprZqbLHf8Hx7UA2fXfMajFjeY9c3JREKQrDoOF1e4r28nAoVsZYF+frWxQB51U4+hOqjlx06wEA==} + /@storybook/addon-links@8.0.10(react@18.3.1): + resolution: {integrity: sha512-+mIyH2UcrgQfAyRM4+ARkB/D0OOY8UMwkZsD8dD23APZ8oru7W/NHX3lXl0WjPfQcOIx/QwWNWI3+DgVZJY3jw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 peerDependenciesMeta: react: optional: true dependencies: - '@storybook/csf': 0.1.3 + '@storybook/csf': 0.1.7 '@storybook/global': 5.0.0 - react: 18.2.0 + react: 18.3.1 ts-dedent: 2.2.0 dev: true - /@storybook/addon-measure@8.0.4: - resolution: {integrity: sha512-GZYKo2ss5Br+dfHinoK3bgTaS90z3oKKDkhv6lrFfjjU1mDYzzMJpxajQhd3apCYxHLr3MbUqMQibWu2T/q2DQ==} + /@storybook/addon-measure@8.0.10: + resolution: {integrity: sha512-quXQwmZJUhOxDIlbXTH6aKYQkwkDpL0UQRkUZn1xuZ2sVKJeaee73QSWqw8HDD4Rz9huS+OrAdVoq/Cz5FoC6A==} dependencies: '@storybook/global': 5.0.0 tiny-invariant: 1.3.3 dev: true - /@storybook/addon-outline@8.0.4: - resolution: {integrity: sha512-6J9ezNDUxdA3rMCh8sUEQbUwAgkrr+M9QdiFr1t+gKrk5FKP5gwubw1sr3sF1IRB9+s/AjljcOtJAVulSfq05w==} + /@storybook/addon-outline@8.0.10: + resolution: {integrity: sha512-1eDO2s/vHhhSJo7W5SetqjleUBTZLI08VNP89c4j7vdRKiMZ1DYhr0dqUGIC3w7cDsawI/nQ24wancHHayAnqw==} dependencies: '@storybook/global': 5.0.0 ts-dedent: 2.2.0 dev: true - /@storybook/addon-storysource@8.0.4: - resolution: {integrity: sha512-qFoB/s4vjjHYFJA6rnOVTeXZ99Y4RTXhCjUrrY2B/c9hssZbEyP/oj57ojQsaIENK8ItCoD7sOExqANwx41qqw==} + /@storybook/addon-storysource@8.0.10: + resolution: {integrity: sha512-LCNgp5pWyI9ZlJMFeN0nvt9gvgHMWneDjfUoAHTOP7Smi0xz4lUDYKB4P53kgE1peHn2+nxAauSBdA1IEFBIRA==} dependencies: - '@storybook/source-loader': 8.0.4 + '@storybook/source-loader': 8.0.10 estraverse: 5.3.0 tiny-invariant: 1.3.3 dev: true - /@storybook/addon-toolbars@8.0.4: - resolution: {integrity: sha512-yodRXDYog/90cNEy84kg6s7L+nxQ+egBjHBTsav1L4cJmQI/uAX8yISHHiX4I5ppNc120Jz3UdHdRxXRlo345g==} + /@storybook/addon-toolbars@8.0.10: + resolution: {integrity: sha512-67HP6mTJU/gjRju01Z5HjeqoRiJMDlrMvMvjGBg7w5+tPNtjYqdelfe2+kcfU+Hf6dfcuqaBDwaUUGSv+RYtRQ==} dev: true - /@storybook/addon-viewport@8.0.4: - resolution: {integrity: sha512-E5IKOsxKcOtlOYc0cWgzVJohQB+dVBWwaJcg5FlslToknfVB9M0kfQ/SQcp3KB0C9/cOmJK1Jm388InW+EjrBQ==} + /@storybook/addon-viewport@8.0.10: + resolution: {integrity: sha512-NJ88Nd/tXreHLyLeF3VP+b8Fu2KtUuJ0L4JYpEMmcdaejGARTrJJOU+pcZBiUqEHFeXQ8rDY8DKXhUJZQFQ1Wg==} dependencies: memoizerific: 1.11.3 dev: true - /@storybook/blocks@8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-9dRXk9zLJVPOmEWsSXm10XUmIfvS/tVgeBgFXNbusFQZXPpexIPNdRgB004pDGg9RvlY78ykpnd3yP143zaXMg==} + /@storybook/blocks@8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-LOaxvcO2d4dT4YoWlQ0bq/c8qA3aHoqtyuvBjwbVn+359bjMtgj/91YuP9Y2+ggZZ4p+ttgvk39PcmJlNXlJsw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -4517,30 +4392,30 @@ packages: react-dom: optional: true dependencies: - '@storybook/channels': 8.0.4 - '@storybook/client-logger': 8.0.4 - '@storybook/components': 8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@storybook/core-events': 8.0.4 - '@storybook/csf': 0.1.3 - '@storybook/docs-tools': 8.0.4 + '@storybook/channels': 8.0.10 + '@storybook/client-logger': 8.0.10 + '@storybook/components': 8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@storybook/core-events': 8.0.10 + '@storybook/csf': 0.1.7 + '@storybook/docs-tools': 8.0.10 '@storybook/global': 5.0.0 - '@storybook/icons': 1.2.9(react-dom@18.2.0)(react@18.2.0) - '@storybook/manager-api': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/preview-api': 8.0.4 - '@storybook/theming': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.4 - '@types/lodash': 4.17.0 + '@storybook/icons': 1.2.9(react-dom@18.3.1)(react@18.3.1) + '@storybook/manager-api': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/preview-api': 8.0.10 + '@storybook/theming': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/types': 8.0.10 + '@types/lodash': 4.17.1 color-convert: 2.0.1 dequal: 2.0.3 lodash: 4.17.21 - markdown-to-jsx: 7.3.2(react@18.2.0) + markdown-to-jsx: 7.3.2(react@18.3.1) memoizerific: 1.11.3 polished: 4.3.1 - react: 18.2.0 - react-colorful: 5.6.1(react-dom@18.2.0)(react@18.2.0) - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-colorful: 5.6.1(react-dom@18.3.1)(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) telejson: 7.2.0 - tocbot: 4.25.0 + tocbot: 4.27.19 ts-dedent: 2.2.0 util-deprecate: 1.0.2 transitivePeerDependencies: @@ -4549,17 +4424,17 @@ packages: - supports-color dev: true - /@storybook/builder-manager@8.0.4: - resolution: {integrity: sha512-BafYVxq77uuTmXdjYo5by42OyOrb6qcpWYKva3ntWK2ZhTaLJlwwqAOdahT1DVzi4VeUP6465YvsTCzIE8fuIw==} + /@storybook/builder-manager@8.0.10: + resolution: {integrity: sha512-lo57jeeYuYCKYrmGOdLg25rMyiGYSTwJ+zYsQ3RvClVICjP6X0I1RCKAJDzkI0BixH6s1+w5ynD6X3PtDnhUuw==} dependencies: '@fal-works/esbuild-plugin-global-externals': 2.1.2 - '@storybook/core-common': 8.0.4 - '@storybook/manager': 8.0.4 - '@storybook/node-logger': 8.0.4 + '@storybook/core-common': 8.0.10 + '@storybook/manager': 8.0.10 + '@storybook/node-logger': 8.0.10 '@types/ejs': 3.1.5 '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.20.2) browser-assert: 1.2.1 - ejs: 3.1.9 + ejs: 3.1.10 esbuild: 0.20.2 esbuild-plugin-alias: 0.2.1 express: 4.19.2 @@ -4571,8 +4446,8 @@ packages: - supports-color dev: true - /@storybook/builder-vite@8.0.4(typescript@5.4.3)(vite@5.2.6): - resolution: {integrity: sha512-Whb001bGkoGQ6/byp9QTQJ4NO61Qa5bh1p5WEEMJ5wYvHm83b+B/IwwilUfU5mL9bJB/RjbwyKcSQqGP6AxMzA==} + /@storybook/builder-vite@8.0.10(typescript@5.4.5)(vite@5.2.11): + resolution: {integrity: sha512-Rod/2jYvF4Ng1MjIMZEXe/3z0lPuxkRtetCTr3ECPgi83lHXpHJ+N0NVfJEMs+pXsVqkLP3iGt2hLn6D6yFMwA==} peerDependencies: '@preact/preset-vite': '*' typescript: '>= 4.3.x' @@ -4586,55 +4461,55 @@ packages: vite-plugin-glimmerx: optional: true dependencies: - '@storybook/channels': 8.0.4 - '@storybook/client-logger': 8.0.4 - '@storybook/core-common': 8.0.4 - '@storybook/core-events': 8.0.4 - '@storybook/csf-plugin': 8.0.4 - '@storybook/node-logger': 8.0.4 - '@storybook/preview': 8.0.4 - '@storybook/preview-api': 8.0.4 - '@storybook/types': 8.0.4 + '@storybook/channels': 8.0.10 + '@storybook/client-logger': 8.0.10 + '@storybook/core-common': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/csf-plugin': 8.0.10 + '@storybook/node-logger': 8.0.10 + '@storybook/preview': 8.0.10 + '@storybook/preview-api': 8.0.10 + '@storybook/types': 8.0.10 '@types/find-cache-dir': 3.2.1 browser-assert: 1.2.1 es-module-lexer: 0.9.3 express: 4.19.2 find-cache-dir: 3.3.2 fs-extra: 11.2.0 - magic-string: 0.30.8 + magic-string: 0.30.10 ts-dedent: 2.2.0 - typescript: 5.4.3 - vite: 5.2.6(@types/node@20.11.30) + typescript: 5.4.5 + vite: 5.2.11(@types/node@20.12.10) transitivePeerDependencies: - encoding - supports-color dev: true - /@storybook/channels@8.0.4: - resolution: {integrity: sha512-haKV+8RbiSzLjicowUfc7h2fTClZHX/nz9SRUecf4IEZUEu2T78OgM/TzqZvL7rA3+/fKqp5iI+3PN3OA75Sdg==} + /@storybook/channels@8.0.10: + resolution: {integrity: sha512-3JLxfD7czlx31dAGvAYJ4J4BNE/Y2+hhj/dsV3xlQTHKVpnWknaoeYEC1a6YScyfsH6W+XmP2rzZKzH4EkLSGQ==} dependencies: - '@storybook/client-logger': 8.0.4 - '@storybook/core-events': 8.0.4 + '@storybook/client-logger': 8.0.10 + '@storybook/core-events': 8.0.10 '@storybook/global': 5.0.0 telejson: 7.2.0 tiny-invariant: 1.3.3 dev: true - /@storybook/cli@8.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-8jb8hrulRMfyFyNXFEapxHBS51xb42ZZGfVAacXIsHOJtjOd5CnOoSUYn0aOkVl19VF/snoa9JOW7BaW/50Eqw==} + /@storybook/cli@8.0.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-KUZEO2lyvOS2sRJEFXovt6+5b65iWsh7F8e8S1cM20fCM1rZAlWtwmoxmDVXDmyEp0wTrq4FrRxKnbo9UO518w==} hasBin: true dependencies: - '@babel/core': 7.24.3 - '@babel/types': 7.24.0 + '@babel/core': 7.24.5 + '@babel/types': 7.24.5 '@ndelangen/get-tarball': 3.0.9 - '@storybook/codemod': 8.0.4 - '@storybook/core-common': 8.0.4 - '@storybook/core-events': 8.0.4 - '@storybook/core-server': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/csf-tools': 8.0.4 - '@storybook/node-logger': 8.0.4 - '@storybook/telemetry': 8.0.4 - '@storybook/types': 8.0.4 + '@storybook/codemod': 8.0.10 + '@storybook/core-common': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/core-server': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/csf-tools': 8.0.10 + '@storybook/node-logger': 8.0.10 + '@storybook/telemetry': 8.0.10 + '@storybook/types': 8.0.10 '@types/semver': 7.5.8 '@yarnpkg/fslib': 2.10.3 '@yarnpkg/libzip': 2.3.0 @@ -4642,14 +4517,14 @@ packages: commander: 6.2.1 cross-spawn: 7.0.3 detect-indent: 6.1.0 - envinfo: 7.11.1 + envinfo: 7.13.0 execa: 5.1.1 find-up: 5.0.0 fs-extra: 11.2.0 get-npm-tarball-url: 2.1.0 giget: 1.2.3 globby: 11.1.0 - jscodeshift: 0.15.2(@babel/preset-env@7.24.3) + jscodeshift: 0.15.2(@babel/preset-env@7.24.5) leven: 3.1.0 ora: 5.4.1 prettier: 3.2.5 @@ -4670,26 +4545,26 @@ packages: - utf-8-validate dev: true - /@storybook/client-logger@8.0.4: - resolution: {integrity: sha512-2SeEg3PT/d0l/+EAVtyj9hmMLTyTPp+bRBSzxYouBjtJPM1jrdKpFagj1o3uBRovwWm9SIVX6/ZsoRC33PEV1g==} + /@storybook/client-logger@8.0.10: + resolution: {integrity: sha512-u38SbZNAunZzxZNHMJb9jkUwFkLyWxmvp4xtiRM3u9sMUShXoTnzbw1yKrxs+kYJjg+58UQPZ1JhEBRcHt5Oww==} dependencies: '@storybook/global': 5.0.0 dev: true - /@storybook/codemod@8.0.4: - resolution: {integrity: sha512-bysG46P4wjlR3RCpr/ntNAUaupWpzLcWYWti3iNtIyZ/iPrX6KtXoA9QCIwJZrlv41us6F+KEZbzLzkgWbymtQ==} + /@storybook/codemod@8.0.10: + resolution: {integrity: sha512-t45jKGs/eyR/nKVX6QgRtMZSAjJo5aXWWk3B24xVbW6ywr0jt1LC100FkHG4Af8cApIfh8uUmS9X05hMG5zGGA==} dependencies: - '@babel/core': 7.24.3 - '@babel/preset-env': 7.24.3(@babel/core@7.24.3) - '@babel/types': 7.24.0 - '@storybook/csf': 0.1.3 - '@storybook/csf-tools': 8.0.4 - '@storybook/node-logger': 8.0.4 - '@storybook/types': 8.0.4 + '@babel/core': 7.24.5 + '@babel/preset-env': 7.24.5(@babel/core@7.24.5) + '@babel/types': 7.24.5 + '@storybook/csf': 0.1.7 + '@storybook/csf-tools': 8.0.10 + '@storybook/node-logger': 8.0.10 + '@storybook/types': 8.0.10 '@types/cross-spawn': 6.0.6 cross-spawn: 7.0.3 globby: 11.1.0 - jscodeshift: 0.15.2(@babel/preset-env@7.24.3) + jscodeshift: 0.15.2(@babel/preset-env@7.24.5) lodash: 4.17.21 prettier: 3.2.5 recast: 0.23.6 @@ -4698,34 +4573,34 @@ packages: - supports-color dev: true - /@storybook/components@8.0.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-i5ngl5GTOLB9nZ1cmpxTjtWct5IuH9UxzFC73a0jHMkCwN26w16IqufRVDaoQv0AvZN4pd4fNM2in/XVHA10dw==} + /@storybook/components@8.0.10(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-eo+oDDcm35YBB3dtDYDfcjJypNVPmRty85VWpAOBsJXpwp/fgU8csx0DM3KmhrQ4cWLf2WzcFowJwI1w+J88Sw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@radix-ui/react-slot': 1.0.2(@types/react@18.2.73)(react@18.2.0) - '@storybook/client-logger': 8.0.4 - '@storybook/csf': 0.1.3 + '@radix-ui/react-slot': 1.0.2(@types/react@18.3.1)(react@18.3.1) + '@storybook/client-logger': 8.0.10 + '@storybook/csf': 0.1.7 '@storybook/global': 5.0.0 - '@storybook/icons': 1.2.9(react-dom@18.2.0)(react@18.2.0) - '@storybook/theming': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.4 + '@storybook/icons': 1.2.9(react-dom@18.3.1)(react@18.3.1) + '@storybook/theming': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/types': 8.0.10 memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) util-deprecate: 1.0.2 transitivePeerDependencies: - '@types/react' dev: true - /@storybook/core-common@8.0.4: - resolution: {integrity: sha512-dzFRLm5FxUa2EFE6Rx/KLDTJNLBIp1S2/+Q1K+rG8V+CLvewCc2Cd486rStZqSXEKI7vDnsRs/aMla+N0X/++Q==} + /@storybook/core-common@8.0.10: + resolution: {integrity: sha512-hsFlPieputaDQoxstnPa3pykTc4bUwEDgCHf8U43+/Z7qmLOQ9fpG+2CFW930rsCRghYpPreOvsmhY7lsGKWLQ==} dependencies: - '@storybook/core-events': 8.0.4 - '@storybook/csf-tools': 8.0.4 - '@storybook/node-logger': 8.0.4 - '@storybook/types': 8.0.4 + '@storybook/core-events': 8.0.10 + '@storybook/csf-tools': 8.0.10 + '@storybook/node-logger': 8.0.10 + '@storybook/types': 8.0.10 '@yarnpkg/fslib': 2.10.3 '@yarnpkg/libzip': 2.3.0 chalk: 4.1.2 @@ -4737,7 +4612,7 @@ packages: find-cache-dir: 3.3.2 find-up: 5.0.0 fs-extra: 11.2.0 - glob: 10.3.10 + glob: 10.3.12 handlebars: 4.7.8 lazy-universal-dotenv: 4.0.0 node-fetch: 2.7.0 @@ -4755,34 +4630,34 @@ packages: - supports-color dev: true - /@storybook/core-events@8.0.4: - resolution: {integrity: sha512-1FgLacIGi9i6/fyxw7ZJDC621RK47IMaA3keH4lc11ASRzCSwJ4YOrXjBFjfPc79EF2BuX72DDJNbhj6ynfF3g==} + /@storybook/core-events@8.0.10: + resolution: {integrity: sha512-TuHPS6p5ZNr4vp4butLb4R98aFx0NRYCI/7VPhJEUH5rPiqNzE3PZd8DC8rnVxavsJ+jO1/y+egNKXRYkEcoPQ==} dependencies: ts-dedent: 2.2.0 dev: true - /@storybook/core-server@8.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-/633Pp7LPcDWXkPLSW+W9VUYUbVkdVBG6peXjuzogV0vzdM0dM9af/T0uV2NQxUhzoy6/7QdSDljE+eEOBs2Lw==} + /@storybook/core-server@8.0.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-HYDw2QFBxg1X/d6g0rUhirOB5Jq6g90HBnyrZzxKoqKWJCNsCADSgM+h9HgtUw0jA97qBpIqmNO9n3mXFPWU/Q==} dependencies: '@aw-web-design/x-default-browser': 1.4.126 - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 '@discoveryjs/json-ext': 0.5.7 - '@storybook/builder-manager': 8.0.4 - '@storybook/channels': 8.0.4 - '@storybook/core-common': 8.0.4 - '@storybook/core-events': 8.0.4 - '@storybook/csf': 0.1.3 - '@storybook/csf-tools': 8.0.4 + '@storybook/builder-manager': 8.0.10 + '@storybook/channels': 8.0.10 + '@storybook/core-common': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/csf': 0.1.7 + '@storybook/csf-tools': 8.0.10 '@storybook/docs-mdx': 3.0.0 '@storybook/global': 5.0.0 - '@storybook/manager': 8.0.4 - '@storybook/manager-api': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/node-logger': 8.0.4 - '@storybook/preview-api': 8.0.4 - '@storybook/telemetry': 8.0.4 - '@storybook/types': 8.0.4 + '@storybook/manager': 8.0.10 + '@storybook/manager-api': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/node-logger': 8.0.10 + '@storybook/preview-api': 8.0.10 + '@storybook/telemetry': 8.0.10 + '@storybook/types': 8.0.10 '@types/detect-port': 1.3.5 - '@types/node': 18.19.26 + '@types/node': 18.19.32 '@types/pretty-hrtime': 1.0.3 '@types/semver': 7.5.8 better-opn: 3.0.2 @@ -4806,7 +4681,7 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.1 - ws: 8.16.0 + ws: 8.17.0 transitivePeerDependencies: - bufferutil - encoding @@ -4816,24 +4691,24 @@ packages: - utf-8-validate dev: true - /@storybook/csf-plugin@8.0.4: - resolution: {integrity: sha512-pEgctWuS/qeKMFZJJUM2JuKwjKBt27ye+216ft7xhNqpsrmCgumJYrkU/ii2CsFJU/qr5Fu9EYw+N+vof1OalQ==} + /@storybook/csf-plugin@8.0.10: + resolution: {integrity: sha512-0EsyEx/06sCjI8sn40r7cABtBU1vUKPMPD+S5mJiZymm73BgdARj0qZOlLoK2LP+t2pcaB/Cn7KX/uyhhv7M2g==} dependencies: - '@storybook/csf-tools': 8.0.4 - unplugin: 1.10.0 + '@storybook/csf-tools': 8.0.10 + unplugin: 1.10.1 transitivePeerDependencies: - supports-color dev: true - /@storybook/csf-tools@8.0.4: - resolution: {integrity: sha512-dMSZxWnXBhmXGOZZOAJ4DKZRCYdA0HaqqZ4/eF9MLLsI+qvW4EklcpjVY6bsIzACgubRWtRZkTpxTnjExi/N1A==} + /@storybook/csf-tools@8.0.10: + resolution: {integrity: sha512-xUc6fVIKoCujf/7JZhkYjrVXeNsTSoDrZFNmqLEmtfktJVqYdXY4LuSAtlBmAIyETi09ULTuuVexrcKFwjzuBA==} dependencies: - '@babel/generator': 7.24.1 - '@babel/parser': 7.24.1 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 - '@storybook/csf': 0.1.3 - '@storybook/types': 8.0.4 + '@babel/generator': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 + '@storybook/csf': 0.1.7 + '@storybook/types': 8.0.10 fs-extra: 11.2.0 recast: 0.23.6 ts-dedent: 2.2.0 @@ -4847,8 +4722,8 @@ packages: lodash: 4.17.21 dev: true - /@storybook/csf@0.1.3: - resolution: {integrity: sha512-IPZvXXo4b3G+gpmgBSBqVM81jbp2ePOKsvhgJdhyZJtkYQCII7rg9KKLQhvBQM5sLaF1eU6r0iuwmyynC9d9SA==} + /@storybook/csf@0.1.7: + resolution: {integrity: sha512-53JeLZBibjQxi0Ep+/AJTfxlofJlxy1jXcSKENlnKxHjWEYyHQCumMP5yTFjf7vhNnMjEpV3zx6t23ssFiGRyw==} dependencies: type-fest: 2.19.0 dev: true @@ -4857,12 +4732,13 @@ packages: resolution: {integrity: sha512-NmiGXl2HU33zpwTv1XORe9XG9H+dRUC1Jl11u92L4xr062pZtrShLmD4VKIsOQujxhhOrbxpwhNOt+6TdhyIdQ==} dev: true - /@storybook/docs-tools@8.0.4: - resolution: {integrity: sha512-PONfG8j/AOHi79NbEkneFRZIscrShbA0sgA+62zeejH4r9+fuIkIKtLnKcAxvr8Bm6uo9aSQbISJZUcBG42WhQ==} + /@storybook/docs-tools@8.0.10: + resolution: {integrity: sha512-rg9KS81vEh13VMr4mAgs+7L4kYqoRtG7kVfV1WHxzJxjR3wYcVR0kP9gPTWV4Xha/TA3onHu9sxKxMTWha0urQ==} dependencies: - '@storybook/core-common': 8.0.4 - '@storybook/preview-api': 8.0.4 - '@storybook/types': 8.0.4 + '@storybook/core-common': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/preview-api': 8.0.10 + '@storybook/types': 8.0.10 '@types/doctrine': 0.0.3 assert: 2.1.0 doctrine: 3.0.0 @@ -4876,41 +4752,41 @@ packages: resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} dev: true - /@storybook/icons@1.2.9(react-dom@18.2.0)(react@18.2.0): + /@storybook/icons@1.2.9(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-cOmylsz25SYXaJL/gvTk/dl3pyk7yBFRfeXTsHvTA3dfhoU/LWSq0NKL9nM7WBasJyn6XPSGnLS4RtKXLw5EUg==} engines: {node: '>=14.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: true - /@storybook/instrumenter@8.0.4: - resolution: {integrity: sha512-lkHv1na12oMTZvuDbzufgqrtFlV1XqdXrAAg7YXZOia/oMz6Z/XMldEqwLPUCLGVodbFJofrpE67Wtw8dNTDQg==} + /@storybook/instrumenter@8.0.10: + resolution: {integrity: sha512-6IYjWeQFA5x68xRoW5dU4yAc1Hwq1ZBkZbXVgJbr5LJw5x+y8eKdZzIaOmSsSKOI96R7J5YWWd2WA1Q0nRurtg==} dependencies: - '@storybook/channels': 8.0.4 - '@storybook/client-logger': 8.0.4 - '@storybook/core-events': 8.0.4 + '@storybook/channels': 8.0.10 + '@storybook/client-logger': 8.0.10 + '@storybook/core-events': 8.0.10 '@storybook/global': 5.0.0 - '@storybook/preview-api': 8.0.4 - '@vitest/utils': 1.4.0 + '@storybook/preview-api': 8.0.10 + '@vitest/utils': 1.6.0 util: 0.12.5 dev: true - /@storybook/manager-api@8.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-TudiRmWlsi8kdjwqW0DDLen76Zp4Sci/AnvTbZvZOWe8C2mruxcr6aaGwuIug6y+uxIyXDvURF6Cek5Twz4isg==} + /@storybook/manager-api@8.0.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-LLu6YKQLWf5QB3h3RO8IevjLrSOew7aidIQPr9DIr9xC8wA7N2fQabr+qrJdE306p3cHZ0nzhYNYZxSjm4Dvdw==} dependencies: - '@storybook/channels': 8.0.4 - '@storybook/client-logger': 8.0.4 - '@storybook/core-events': 8.0.4 - '@storybook/csf': 0.1.3 + '@storybook/channels': 8.0.10 + '@storybook/client-logger': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/csf': 0.1.7 '@storybook/global': 5.0.0 - '@storybook/icons': 1.2.9(react-dom@18.2.0)(react@18.2.0) - '@storybook/router': 8.0.4 - '@storybook/theming': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.4 + '@storybook/icons': 1.2.9(react-dom@18.3.1)(react@18.3.1) + '@storybook/router': 8.0.10 + '@storybook/theming': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/types': 8.0.10 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 @@ -4922,68 +4798,68 @@ packages: - react-dom dev: true - /@storybook/manager@8.0.4: - resolution: {integrity: sha512-M5IofDSxbIQIdAglxUtZOGKjZ1EAq1Mdbh4UolVsF1PKF6dAvBQJLVW6TiLjEbmPBtqgeYKMgrmmYiFNqVcdBQ==} + /@storybook/manager@8.0.10: + resolution: {integrity: sha512-bojGglUQNry48L4siURc2zQKswavLzMh69rqsfL3ZXx+i+USfRfB7593azTlaZh0q6HO4bUAjB24RfQCyifLLQ==} dev: true - /@storybook/node-logger@8.0.4: - resolution: {integrity: sha512-cALLHuX53vLQsoJamGRlquh2pfhPq9copXou2JTmFT6mrCcipo77SzhBDfeeuhaGv6vUWPfmGjPBEHXWGPe4+g==} + /@storybook/node-logger@8.0.10: + resolution: {integrity: sha512-UMmaUaA3VOX/mKLsSvOnbZre2/1tZ6hazA6H0eAnClKb51jRD1AJrsBYK+uHr/CAp7t710bB5U8apPov7hayDw==} dev: true - /@storybook/preview-api@8.0.4: - resolution: {integrity: sha512-uZCgZ/7BZkFTNudCBWx3YPFVdReMQSZJj9EfQVhQaPmfGORHGMvZMRsQXl0ONhPy7zDD4rVQxu5dSKWmIiYoWQ==} + /@storybook/preview-api@8.0.10: + resolution: {integrity: sha512-uZ6btF7Iloz9TnDcKLQ5ydi2YK0cnulv/8FLQhBCwSrzLLLb+T2DGz0cAeuWZEvMUNWNmkWJ9PAFQFs09/8p/Q==} dependencies: - '@storybook/channels': 8.0.4 - '@storybook/client-logger': 8.0.4 - '@storybook/core-events': 8.0.4 - '@storybook/csf': 0.1.3 + '@storybook/channels': 8.0.10 + '@storybook/client-logger': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/csf': 0.1.7 '@storybook/global': 5.0.0 - '@storybook/types': 8.0.4 - '@types/qs': 6.9.14 + '@storybook/types': 8.0.10 + '@types/qs': 6.9.15 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 - qs: 6.12.0 + qs: 6.12.1 tiny-invariant: 1.3.3 ts-dedent: 2.2.0 util-deprecate: 1.0.2 dev: true - /@storybook/preview@8.0.4: - resolution: {integrity: sha512-dJa13bIxQBfa5ZsXAeL6X/oXI6b87Fy31pvpKPkW1o+7M6MC4OvwGQBqgAd7m8yn6NuIHxrdwjEupa7l7PGb6w==} + /@storybook/preview@8.0.10: + resolution: {integrity: sha512-op7gZqop8PSFyPA4tc1Zds8jG6VnskwpYUUsa44pZoEez9PKEFCf4jE+7AQwbBS3hnuCb0CKBfASN8GRyoznbw==} dev: true - /@storybook/react-dom-shim@8.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-H8bci23e+G40WsdYPuPrhAjCeeXypXuAV6mTVvLHGKH+Yb+3wiB1weaXrot/TgzPbkDNybuhTI3Qm48FPLt0bw==} + /@storybook/react-dom-shim@8.0.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-3x8EWEkZebpWpp1pwXEzdabGINwOQt8odM5+hsOlDRtFZBmUqmmzK0rtn7orlcGlOXO4rd6QuZj4Tc5WV28dVQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: true - /@storybook/react-vite@8.0.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3)(vite@5.2.6): - resolution: {integrity: sha512-SlAsLSDc9I1nhMbf0YgXCHaZbnjzDdv458xirmUj4aJhn45e8yhmODpkPYQ8nGn45VWYMyd0sC66lJNWRvI/FA==} + /@storybook/react-vite@8.0.10(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5)(vite@5.2.11): + resolution: {integrity: sha512-J0Tw1jWSQYzc37AWaJCbrFQLlWsCHby0ie0yPx8DVehlnTT6xZWkohiKBq5iwMyYfF9SGrOfZ/dVRiB5q2sOIA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 vite: ^4.0.0 || ^5.0.0 dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.4.3)(vite@5.2.6) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.4.5)(vite@5.2.11) '@rollup/pluginutils': 5.1.0 - '@storybook/builder-vite': 8.0.4(typescript@5.4.3)(vite@5.2.6) - '@storybook/node-logger': 8.0.4 - '@storybook/react': 8.0.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3) + '@storybook/builder-vite': 8.0.10(typescript@5.4.5)(vite@5.2.11) + '@storybook/node-logger': 8.0.10 + '@storybook/react': 8.0.10(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5) find-up: 5.0.0 - magic-string: 0.30.8 - react: 18.2.0 + magic-string: 0.30.10 + react: 18.3.1 react-docgen: 7.0.3 - react-dom: 18.2.0(react@18.2.0) + react-dom: 18.3.1(react@18.3.1) resolve: 1.22.8 tsconfig-paths: 4.2.0 - vite: 5.2.6(@types/node@20.11.30) + vite: 5.2.11(@types/node@20.12.10) transitivePeerDependencies: - '@preact/preset-vite' - encoding @@ -4993,8 +4869,8 @@ packages: - vite-plugin-glimmerx dev: true - /@storybook/react@8.0.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.4.3): - resolution: {integrity: sha512-p4wQSJIhG48UD2fZ6tFDT9zaqrVnvZxjV18+VjSi3dez/pDoEMJ3SWZWcmeDenKwvvk+SPdRH7k5mUHW1Rh0xg==} + /@storybook/react@8.0.10(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5): + resolution: {integrity: sha512-/MIMc02TNmiNXDzk55dm9+ujfNE5LVNeqqK+vxXWLlCZ0aXRAd1/ZLYeRFuYLgEETB7mh7IP8AXjvM68NX5HYg==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -5004,15 +4880,15 @@ packages: typescript: optional: true dependencies: - '@storybook/client-logger': 8.0.4 - '@storybook/docs-tools': 8.0.4 + '@storybook/client-logger': 8.0.10 + '@storybook/docs-tools': 8.0.10 '@storybook/global': 5.0.0 - '@storybook/preview-api': 8.0.4 - '@storybook/react-dom-shim': 8.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 8.0.4 + '@storybook/preview-api': 8.0.10 + '@storybook/react-dom-shim': 8.0.10(react-dom@18.3.1)(react@18.3.1) + '@storybook/types': 8.0.10 '@types/escodegen': 0.0.6 '@types/estree': 0.0.51 - '@types/node': 18.19.26 + '@types/node': 18.19.32 acorn: 7.4.1 acorn-jsx: 5.3.2(acorn@7.4.1) acorn-walk: 7.2.0 @@ -5020,43 +4896,43 @@ packages: html-tags: 3.3.1 lodash: 4.17.21 prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-element-to-jsx-string: 15.0.0(react-dom@18.3.1)(react@18.3.1) semver: 7.6.0 ts-dedent: 2.2.0 type-fest: 2.19.0 - typescript: 5.4.3 + typescript: 5.4.5 util-deprecate: 1.0.2 transitivePeerDependencies: - encoding - supports-color dev: true - /@storybook/router@8.0.4: - resolution: {integrity: sha512-hlR80QvmLBflAqMeGcgtDuSe6TJlzdizwEAkBLE1lDvFI6tvvEyAliCAXBpIDdOZTe0u/zeeJkOUXKSx33caoQ==} + /@storybook/router@8.0.10: + resolution: {integrity: sha512-AZhgiet+EK0ZsPbaDgbbVTAHW2LAMCP1z/Un2uMBbdDeD0Ys29Af47AbEj/Ome5r1cqasLvzq2WXJlVXPNB0Zw==} dependencies: - '@storybook/client-logger': 8.0.4 + '@storybook/client-logger': 8.0.10 memoizerific: 1.11.3 - qs: 6.12.0 + qs: 6.12.1 dev: true - /@storybook/source-loader@8.0.4: - resolution: {integrity: sha512-pqaOMMV+dZvjbTdOzuc5RCFN9mGJ81GDtiaxYTKiIYrAyeK+V4JEZi7vHxCbZV4Tci5byeGNhx4FvQgVF2q0Wg==} + /@storybook/source-loader@8.0.10: + resolution: {integrity: sha512-bv9FRPzELjcoMJLWLDqkUNh1zY0DiCgcvM+9qsZva8pxAD4fzrX+mgCS2vZVJHRg8wMAhw/ymdXixDUodHAvsw==} dependencies: - '@storybook/csf': 0.1.3 - '@storybook/types': 8.0.4 + '@storybook/csf': 0.1.7 + '@storybook/types': 8.0.10 estraverse: 5.3.0 lodash: 4.17.21 prettier: 3.2.5 dev: true - /@storybook/telemetry@8.0.4: - resolution: {integrity: sha512-Q3ITY6J46R/TrrPRIU1fs3WNs69ExpTJZ9UlB8087qOUyV90Ex33SYk3i10xVWRczxCmyC1V58Xuht6nxz7mNQ==} + /@storybook/telemetry@8.0.10: + resolution: {integrity: sha512-s4Uc+KZQkdmD2d+64Qf8wYknhQZwmjf2CxjIjv9b4KLsU/nyfDheK7Fzd1jhBKb2UQUlLW5HhZkBgs1RsZcDHA==} dependencies: - '@storybook/client-logger': 8.0.4 - '@storybook/core-common': 8.0.4 - '@storybook/csf-tools': 8.0.4 + '@storybook/client-logger': 8.0.10 + '@storybook/core-common': 8.0.10 + '@storybook/csf-tools': 8.0.10 chalk: 4.1.2 detect-package-manager: 2.0.1 fetch-retry: 5.0.6 @@ -5067,19 +4943,18 @@ packages: - supports-color dev: true - /@storybook/test@8.0.4(vitest@1.4.0): - resolution: {integrity: sha512-/uvE8Rtu7tIcuyQBUzKq7uuDCsjmADI18BApLdwo/qthmN8ERDxRSz0Ngj2gvBMQFv99At8ESi/xh6oFGu3rWg==} + /@storybook/test@8.0.10(vitest@1.6.0): + resolution: {integrity: sha512-VqjzKJiOCjaZ0CjLeKygYk8uetiaiKbpIox+BrND9GtpEBHcRZA5AeFY2P1aSCOhsaDwuh4KRBxJWFug7DhWGQ==} dependencies: - '@storybook/client-logger': 8.0.4 - '@storybook/core-events': 8.0.4 - '@storybook/instrumenter': 8.0.4 - '@storybook/preview-api': 8.0.4 + '@storybook/client-logger': 8.0.10 + '@storybook/core-events': 8.0.10 + '@storybook/instrumenter': 8.0.10 + '@storybook/preview-api': 8.0.10 '@testing-library/dom': 9.3.4 - '@testing-library/jest-dom': 6.4.2(vitest@1.4.0) + '@testing-library/jest-dom': 6.4.5(vitest@1.6.0) '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4) '@vitest/expect': 1.3.1 - '@vitest/spy': 1.4.0 - chai: 4.4.1 + '@vitest/spy': 1.6.0 util: 0.12.5 transitivePeerDependencies: - '@jest/globals' @@ -5089,8 +4964,8 @@ packages: - vitest dev: true - /@storybook/theming@8.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-NxtTU2wMC0lj375ejoT3Npdcqwv6NeUpLaJl6EZCMXSR41ve9WG4suUNWQ63olhqKxirjzAz0IL7ggH7c3hPvA==} + /@storybook/theming@8.0.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-7NHt7bMC7lPkwz9KdDpa6DkLoQZz5OV6jsx/qY91kcdLo1rpnRPAiVlJvmWesFxi1oXOpVDpHHllWzf8KDBv8A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -5100,24 +4975,24 @@ packages: react-dom: optional: true dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0) - '@storybook/client-logger': 8.0.4 + '@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.3.1) + '@storybook/client-logger': 8.0.10 '@storybook/global': 5.0.0 memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: true - /@storybook/types@8.0.4: - resolution: {integrity: sha512-OO7QY+qZFCYkItDUBACtIV32p75O7sNziAiyS1V2Oxgo7Ln7fwZwr3mJcA1ruBed6ZcrW3c87k7Xs40T2zAWcg==} + /@storybook/types@8.0.10: + resolution: {integrity: sha512-S/hKS7+SqNnYIehwxdQ4M2nnlfGDdYWAXdtPCVJCmS+YF2amgAxeuisiHbUg7eypds6VL0Oxk/j2nPEHOHk9pg==} dependencies: - '@storybook/channels': 8.0.4 + '@storybook/channels': 8.0.10 '@types/express': 4.17.21 file-system-cache: 2.3.0 dev: true - /@swc/core-darwin-arm64@1.4.11: - resolution: {integrity: sha512-C1j1Qp/IHSelVWdEnT7f0iONWxQz6FAqzjCF2iaL+0vFg4V5f2nlgrueY8vj5pNNzSGhrAlxsMxEIp4dj1MXkg==} + /@swc/core-darwin-arm64@1.5.3: + resolution: {integrity: sha512-kRmmV2XqWegzGXvJfVVOj10OXhLgaVOOBjaX3p3Aqg7Do5ksg+bY5wi1gAN/Eul7B08Oqf7GG7WJevjDQGWPOg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -5125,8 +5000,8 @@ packages: dev: true optional: true - /@swc/core-darwin-x64@1.4.11: - resolution: {integrity: sha512-0TTy3Ni8ncgaMCchSQ7FK8ZXQLlamy0FXmGWbR58c+pVZWYZltYPTmheJUvVcR0H2+gPAymRKyfC0iLszDALjg==} + /@swc/core-darwin-x64@1.5.3: + resolution: {integrity: sha512-EYs0+ovaRw6ZN9GBr2nIeC7gUXWA0q4RYR+Og3Vo0Qgv2Mt/XudF44A2lPK9X7M3JIfu6JjnxnTuvsK1Lqojfw==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -5134,8 +5009,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm-gnueabihf@1.4.11: - resolution: {integrity: sha512-XJLB71uw0rog4DjYAPxFGAuGCBQpgJDlPZZK6MTmZOvI/1t0+DelJ24IjHIxk500YYM26Yv47xPabqFPD7I2zQ==} + /@swc/core-linux-arm-gnueabihf@1.5.3: + resolution: {integrity: sha512-RBVUTidSf4wgPdv98VrgJ4rMzMDN/3LBWdT7l+R7mNFH+mtID7ZAhTON0o/m1HkECgAgi1xcbTOVAw1xgd5KLA==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -5143,8 +5018,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-gnu@1.4.11: - resolution: {integrity: sha512-vYQwzJvm/iu052d5Iw27UFALIN5xSrGkPZXxLNMHPySVko2QMNNBv35HLatkEQHbQ3X+VKSW9J9SkdtAvAVRAQ==} + /@swc/core-linux-arm64-gnu@1.5.3: + resolution: {integrity: sha512-DCC6El3MiTYfv98CShxz/g2s4Pxn6tV0mldCQ0UdRqaN2ApUn7E+zTrqaj5bk7yII3A43WhE9Mr6wNPbXUeVyg==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -5152,8 +5027,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-musl@1.4.11: - resolution: {integrity: sha512-eV+KduiRYUFjPsvbZuJ9aknQH9Tj0U2/G9oIZSzLx/18WsYi+upzHbgxmIIHJ2VJgfd7nN40RI/hMtxNsUzR/g==} + /@swc/core-linux-arm64-musl@1.5.3: + resolution: {integrity: sha512-p04ysjYXEyaCGpJvwHm0T0nkPawXtdKBTThWnlh8M5jYULVNVA1YmC9azG2Avs1GDaLgBPVUgodmFYpdSupOYA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -5161,8 +5036,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-gnu@1.4.11: - resolution: {integrity: sha512-WA1iGXZ2HpqM1OR9VCQZJ8sQ1KP2or9O4bO8vWZo6HZJIeoQSo7aa9waaCLRpkZvkng1ct/TF/l6ymqSNFXIzQ==} + /@swc/core-linux-x64-gnu@1.5.3: + resolution: {integrity: sha512-/l4KJu0xwYm6tcVSOvF8RbXrIeIHJAhWnKvuX4ZnYKFkON968kB8Ghx+1yqBQcZf36tMzSuZUC5xBUA9u66lGA==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -5170,8 +5045,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-musl@1.4.11: - resolution: {integrity: sha512-UkVJToKf0owwQYRnGvjHAeYVDfeimCEcx0VQSbJoN7Iy0ckRZi7YPlmWJU31xtKvikE2bQWCOVe0qbSDqqcWXA==} + /@swc/core-linux-x64-musl@1.5.3: + resolution: {integrity: sha512-54DmSnrTXq4fYEKNR0nFAImG3+FxsHlQ6Tol/v3l+rxmg2K0FeeDOpH7wTXeWhMGhFlGrLIyLSnA+SzabfoDIA==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -5179,8 +5054,8 @@ packages: dev: true optional: true - /@swc/core-win32-arm64-msvc@1.4.11: - resolution: {integrity: sha512-35khwkyly7lF5NDSyvIrukBMzxPorgc5iTSDfVO/LvnmN5+fm4lTlrDr4tUfTdOhv3Emy7CsKlsNAeFRJ+Pm+w==} + /@swc/core-win32-arm64-msvc@1.5.3: + resolution: {integrity: sha512-piUMqoHNwDXChBfaaFIMzYgoxepfd8Ci1uXXNVEnuiRKz3FiIcNLmvXaBD7lKUwKcnGgVziH/CrndX6SldKQNQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -5188,8 +5063,8 @@ packages: dev: true optional: true - /@swc/core-win32-ia32-msvc@1.4.11: - resolution: {integrity: sha512-Wx8/6f0ufgQF2pbVPsJ2dAmFLwIOW+xBE5fxnb7VnEbGkTgP1qMDWiiAtD9rtvDSuODG3i1AEmAak/2HAc6i6A==} + /@swc/core-win32-ia32-msvc@1.5.3: + resolution: {integrity: sha512-zV5utPYBUzYhBOomCByAjKAvfVBcOCJtnszx7Zlfz7SAv/cGm8D1QzPDCvv6jDhIlUtLj6KyL8JXeFr+f95Fjw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -5197,8 +5072,8 @@ packages: dev: true optional: true - /@swc/core-win32-x64-msvc@1.4.11: - resolution: {integrity: sha512-0xRFW6K9UZQH2NVC/0pVB0GJXS45lY24f+6XaPBF1YnMHd8A8GoHl7ugyM5yNUTe2AKhSgk5fJV00EJt/XBtdQ==} + /@swc/core-win32-x64-msvc@1.5.3: + resolution: {integrity: sha512-QmUiXiPIV5gBADfDh8e2jKynEhyRC+dcKP/zF9y5KqDUErYzlhocLd68uYS4uIegP6AylYlmigHgcaktGEE9VQ==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -5206,8 +5081,8 @@ packages: dev: true optional: true - /@swc/core@1.4.11: - resolution: {integrity: sha512-WKEakMZxkVwRdgMN4AMJ9K5nysY8g8npgQPczmjBeNK5In7QEAZAJwnyccrWwJZU0XjVeHn2uj+XbOKdDW17rg==} + /@swc/core@1.5.3: + resolution: {integrity: sha512-pSEglypnBGLHBoBcv3aYS7IM2t2LRinubYMyP88UoFIcD2pear2CeB15CbjJ2IzuvERD0ZL/bthM7cDSR9g+aQ==} engines: {node: '>=10'} requiresBuild: true peerDependencies: @@ -5219,16 +5094,16 @@ packages: '@swc/counter': 0.1.3 '@swc/types': 0.1.6 optionalDependencies: - '@swc/core-darwin-arm64': 1.4.11 - '@swc/core-darwin-x64': 1.4.11 - '@swc/core-linux-arm-gnueabihf': 1.4.11 - '@swc/core-linux-arm64-gnu': 1.4.11 - '@swc/core-linux-arm64-musl': 1.4.11 - '@swc/core-linux-x64-gnu': 1.4.11 - '@swc/core-linux-x64-musl': 1.4.11 - '@swc/core-win32-arm64-msvc': 1.4.11 - '@swc/core-win32-ia32-msvc': 1.4.11 - '@swc/core-win32-x64-msvc': 1.4.11 + '@swc/core-darwin-arm64': 1.5.3 + '@swc/core-darwin-x64': 1.5.3 + '@swc/core-linux-arm-gnueabihf': 1.5.3 + '@swc/core-linux-arm64-gnu': 1.5.3 + '@swc/core-linux-arm64-musl': 1.5.3 + '@swc/core-linux-x64-gnu': 1.5.3 + '@swc/core-linux-x64-musl': 1.5.3 + '@swc/core-win32-arm64-msvc': 1.5.3 + '@swc/core-win32-ia32-msvc': 1.5.3 + '@swc/core-win32-x64-msvc': 1.5.3 dev: true /@swc/counter@0.1.3: @@ -5252,7 +5127,7 @@ packages: engines: {node: '>=14'} dependencies: '@babel/code-frame': 7.24.2 - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 '@types/aria-query': 5.0.4 aria-query: 5.1.3 chalk: 4.1.2 @@ -5261,8 +5136,8 @@ packages: pretty-format: 27.5.1 dev: true - /@testing-library/jest-dom@6.4.2(vitest@1.4.0): - resolution: {integrity: sha512-CzqH0AFymEMG48CpzXFriYYkOjk6ZGPCLMhW9e9jg3KMCn5OfJecF8GtGW7yGfR/IgCe3SX8BSwjdzI6BBbZLw==} + /@testing-library/jest-dom@6.4.5(vitest@1.6.0): + resolution: {integrity: sha512-AguB9yvTXmCnySBP1lWjfNNUwpbElsaQ567lt2VdGqAdHtpieLgjmcVyv1q7PMIvLbgpDdkWV5Ydv3FEejyp2A==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} peerDependencies: '@jest/globals': '>= 28' @@ -5283,14 +5158,14 @@ packages: optional: true dependencies: '@adobe/css-tools': 4.3.3 - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 aria-query: 5.3.0 chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 lodash: 4.17.21 redent: 3.0.0 - vitest: 1.4.0(@types/node@20.11.30) + vitest: 1.6.0(@types/node@20.12.10)(@vitest/ui@1.6.0) dev: true /@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4): @@ -5313,8 +5188,8 @@ packages: /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.24.1 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.5 @@ -5323,39 +5198,39 @@ packages: /@types/babel__generator@7.6.8: resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/parser': 7.24.1 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 dev: true /@types/babel__traverse@7.20.5: resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==} dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.5 dev: true /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 20.11.30 + '@types/node': 20.12.10 dev: true /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 20.11.30 + '@types/node': 20.12.10 dev: true /@types/cross-spawn@6.0.6: resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} dependencies: - '@types/node': 20.11.30 + '@types/node': 20.12.10 dev: true /@types/d3-array@3.2.1: @@ -5561,16 +5436,16 @@ packages: resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} dev: true - /@types/emscripten@1.39.10: - resolution: {integrity: sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw==} + /@types/emscripten@1.39.11: + resolution: {integrity: sha512-dOeX2BeNA7j6BTEqJQL3ut0bRCfsyQMd5i4FT8JfHfYhAOuJPCGh0dQFbxVJxUyQ+75x6enhDdndGb624/QszA==} dev: true /@types/escodegen@0.0.6: resolution: {integrity: sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==} dev: true - /@types/eslint@8.56.6: - resolution: {integrity: sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==} + /@types/eslint@8.56.10: + resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==} dependencies: '@types/estree': 1.0.5 '@types/json-schema': 7.0.15 @@ -5584,11 +5459,11 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true - /@types/express-serve-static-core@4.17.43: - resolution: {integrity: sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==} + /@types/express-serve-static-core@4.19.0: + resolution: {integrity: sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==} dependencies: - '@types/node': 20.11.30 - '@types/qs': 6.9.14 + '@types/node': 20.12.10 + '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 dev: true @@ -5597,9 +5472,9 @@ packages: resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} dependencies: '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.17.43 - '@types/qs': 6.9.14 - '@types/serve-static': 1.15.5 + '@types/express-serve-static-core': 4.19.0 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 dev: true /@types/find-cache-dir@3.2.1: @@ -5614,7 +5489,7 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.11.30 + '@types/node': 20.12.10 dev: true /@types/hast@3.0.4: @@ -5642,42 +5517,38 @@ packages: /@types/lodash-es@4.17.12: resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} dependencies: - '@types/lodash': 4.17.0 + '@types/lodash': 4.17.1 dev: true /@types/lodash.mergewith@4.6.7: resolution: {integrity: sha512-3m+lkO5CLRRYU0fhGRp7zbsGi6+BZj0uTVSwvcKU+nSlhjA9/QRNfuSGnD2mX6hQA7ZbmcCkzk5h4ZYGOtk14A==} dependencies: - '@types/lodash': 4.17.0 + '@types/lodash': 4.17.1 dev: false - /@types/lodash@4.17.0: - resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==} + /@types/lodash@4.17.1: + resolution: {integrity: sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q==} - /@types/mdx@2.0.12: - resolution: {integrity: sha512-H9VZ9YqE+H28FQVchC83RCs5xQ2J7mAAv6qdDEaWmXEVl3OpdH+xfrSUzQ1lp7U7oSTRZ0RvW08ASPJsYBi7Cw==} + /@types/mdx@2.0.13: + resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} dev: true /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} dev: true - /@types/mime@3.0.4: - resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} - dev: true - /@types/minimatch@5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} dev: true - /@types/node@18.19.26: - resolution: {integrity: sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==} + /@types/node@18.19.32: + resolution: {integrity: sha512-2bkg93YBSDKk8DLmmHnmj/Rwr18TLx7/n+I23BigFwgexUJoMHZOd8X1OFxuF/W3NN0S2W2E5sVabI5CPinNvA==} dependencies: undici-types: 5.26.5 dev: true - /@types/node@20.11.30: - resolution: {integrity: sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==} + /@types/node@20.12.10: + resolution: {integrity: sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==} dependencies: undici-types: 5.26.5 dev: true @@ -5690,10 +5561,6 @@ packages: resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} dev: false - /@types/picomatch@2.3.3: - resolution: {integrity: sha512-Yll76ZHikRFCyz/pffKGjrCwe/le2CDwOP5F210KQo27kpRE46U2rDnzikNlVn6/ezH3Mhn46bJMTfeVTtcYMg==} - dev: true - /@types/pretty-hrtime@1.0.3: resolution: {integrity: sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==} dev: true @@ -5701,34 +5568,34 @@ packages: /@types/prop-types@15.7.12: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - /@types/qs@6.9.14: - resolution: {integrity: sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==} + /@types/qs@6.9.15: + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} dev: true /@types/range-parser@1.2.7: resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} dev: true - /@types/react-dom@18.2.22: - resolution: {integrity: sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==} + /@types/react-dom@18.3.0: + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} dependencies: - '@types/react': 18.2.73 + '@types/react': 18.3.1 dev: true /@types/react-reconciler@0.28.8: resolution: {integrity: sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==} dependencies: - '@types/react': 18.2.73 + '@types/react': 18.3.1 dev: false /@types/react-transition-group@4.4.10: resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==} dependencies: - '@types/react': 18.2.73 + '@types/react': 18.3.1 dev: false - /@types/react@18.2.73: - resolution: {integrity: sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==} + /@types/react@18.3.1: + resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 @@ -5745,15 +5612,15 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 20.11.30 + '@types/node': 20.12.10 dev: true - /@types/serve-static@1.15.5: - resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + /@types/serve-static@1.15.7: + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} dependencies: '@types/http-errors': 2.0.4 - '@types/mime': 3.0.4 - '@types/node': 20.11.30 + '@types/node': 20.12.10 + '@types/send': 0.17.4 dev: true /@types/unist@3.0.2: @@ -5768,8 +5635,8 @@ packages: resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} dev: true - /@typescript-eslint/eslint-plugin@7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==} + /@typescript-eslint/eslint-plugin@7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-gFTT+ezJmkwutUPmB0skOj3GZJtlEGnlssems4AjkVweUPGj7jRwwqg0Hhg7++kPGJqKtTYx+R05Ftww372aIg==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: '@typescript-eslint/parser': ^7.0.0 @@ -5780,25 +5647,25 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/scope-manager': 7.4.0 - '@typescript-eslint/type-utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3) - '@typescript-eslint/visitor-keys': 7.4.0 + '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/scope-manager': 7.8.0 + '@typescript-eslint/type-utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.8.0 debug: 4.3.4 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@7.4.0(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==} + /@typescript-eslint/parser@7.8.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-KgKQly1pv0l4ltcftP59uQZCi4HUYswCLbTqVZEJu7uLX8CTLyswqMLqLN+2QFz4jCptqWVV4SB7vdxcH2+0kQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -5807,13 +5674,13 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.4.0 - '@typescript-eslint/types': 7.4.0 - '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3) - '@typescript-eslint/visitor-keys': 7.4.0 + '@typescript-eslint/scope-manager': 7.8.0 + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) + '@typescript-eslint/visitor-keys': 7.8.0 debug: 4.3.4 eslint: 8.57.0 - typescript: 5.4.3 + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -5826,16 +5693,16 @@ packages: '@typescript-eslint/visitor-keys': 5.62.0 dev: true - /@typescript-eslint/scope-manager@7.4.0: - resolution: {integrity: sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==} + /@typescript-eslint/scope-manager@7.8.0: + resolution: {integrity: sha512-viEmZ1LmwsGcnr85gIq+FCYI7nO90DVbE37/ll51hjv9aG+YZMb4WDE2fyWpUR4O/UrhGRpYXK/XajcGTk2B8g==} engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 7.4.0 - '@typescript-eslint/visitor-keys': 7.4.0 + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/visitor-keys': 7.8.0 dev: true - /@typescript-eslint/type-utils@7.4.0(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==} + /@typescript-eslint/type-utils@7.8.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-H70R3AefQDQpz9mGv13Uhi121FNMh+WEaRqcXTX09YEDky21km4dV1ZXJIp8QjXc4ZaVkXVdohvWDzbnbHDS+A==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -5844,12 +5711,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3) - '@typescript-eslint/utils': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) + '@typescript-eslint/utils': 7.8.0(eslint@8.57.0)(typescript@5.4.5) debug: 4.3.4 eslint: 8.57.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true @@ -5859,12 +5726,12 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/types@7.4.0: - resolution: {integrity: sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==} + /@typescript-eslint/types@7.8.0: + resolution: {integrity: sha512-wf0peJ+ZGlcH+2ZS23aJbOv+ztjeeP8uQ9GgwMJGVLx/Nj9CJt17GWgWWoSmoRVKAX2X+7fzEnAjxdvK2gqCLw==} engines: {node: ^18.18.0 || >=20.0.0} dev: true - /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.3): + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.4.5): resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -5879,14 +5746,14 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.0 - tsutils: 3.21.0(typescript@5.4.3) - typescript: 5.4.3 + tsutils: 3.21.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.4.0(typescript@5.4.3): - resolution: {integrity: sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==} + /@typescript-eslint/typescript-estree@7.8.0(typescript@5.4.5): + resolution: {integrity: sha512-5pfUCOwK5yjPaJQNy44prjCwtr981dO8Qo9J9PwYXZ0MosgAbfEMB008dJ5sNo3+/BN6ytBPuSvXUg9SAqB0dg==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: typescript: '*' @@ -5894,20 +5761,20 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.4.0 - '@typescript-eslint/visitor-keys': 7.4.0 + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/visitor-keys': 7.8.0 debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.3 + minimatch: 9.0.4 semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.4.3) - typescript: 5.4.3 + ts-api-utils: 1.3.0(typescript@5.4.5) + typescript: 5.4.5 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.3): + /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -5918,7 +5785,7 @@ packages: '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.3) + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.5) eslint: 8.57.0 eslint-scope: 5.1.1 semver: 7.6.0 @@ -5927,8 +5794,8 @@ packages: - typescript dev: true - /@typescript-eslint/utils@7.4.0(eslint@8.57.0)(typescript@5.4.3): - resolution: {integrity: sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==} + /@typescript-eslint/utils@7.8.0(eslint@8.57.0)(typescript@5.4.5): + resolution: {integrity: sha512-L0yFqOCflVqXxiZyXrDr80lnahQfSOfc9ELAAZ75sqicqp2i36kEZZGuUymHNFoYOqxRT05up760b4iGsl02nQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 @@ -5936,9 +5803,9 @@ packages: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.4.0 - '@typescript-eslint/types': 7.4.0 - '@typescript-eslint/typescript-estree': 7.4.0(typescript@5.4.3) + '@typescript-eslint/scope-manager': 7.8.0 + '@typescript-eslint/types': 7.8.0 + '@typescript-eslint/typescript-estree': 7.8.0(typescript@5.4.5) eslint: 8.57.0 semver: 7.6.0 transitivePeerDependencies: @@ -5954,11 +5821,11 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.4.0: - resolution: {integrity: sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==} + /@typescript-eslint/visitor-keys@7.8.0: + resolution: {integrity: sha512-q4/gibTNBQNA0lGyYQCmWRS5D15n8rXh4QjK3KV+MBPlTYHpfBUT3D3PaPR/HeNiI9W6R7FvlkcGhNyAoP+caA==} engines: {node: ^18.18.0 || >=20.0.0} dependencies: - '@typescript-eslint/types': 7.4.0 + '@typescript-eslint/types': 7.8.0 eslint-visitor-keys: 3.4.3 dev: true @@ -5966,17 +5833,40 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true - /@vitejs/plugin-react-swc@3.6.0(vite@5.2.6): + /@vitejs/plugin-react-swc@3.6.0(vite@5.2.11): resolution: {integrity: sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==} peerDependencies: vite: ^4 || ^5 dependencies: - '@swc/core': 1.4.11 - vite: 5.2.6(@types/node@20.11.30) + '@swc/core': 1.5.3 + vite: 5.2.11(@types/node@20.12.10) transitivePeerDependencies: - '@swc/helpers' dev: true + /@vitest/coverage-v8@1.6.0(vitest@1.6.0): + resolution: {integrity: sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==} + peerDependencies: + vitest: 1.6.0 + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.4 + istanbul-reports: 3.1.7 + magic-string: 0.30.10 + magicast: 0.3.4 + picocolors: 1.0.0 + std-env: 3.7.0 + strip-literal: 2.1.0 + test-exclude: 6.0.0 + vitest: 1.6.0(@types/node@20.12.10)(@vitest/ui@1.6.0) + transitivePeerDependencies: + - supports-color + dev: true + /@vitest/expect@1.3.1: resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==} dependencies: @@ -5985,26 +5875,26 @@ packages: chai: 4.4.1 dev: true - /@vitest/expect@1.4.0: - resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + /@vitest/expect@1.6.0: + resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==} dependencies: - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 + '@vitest/spy': 1.6.0 + '@vitest/utils': 1.6.0 chai: 4.4.1 dev: true - /@vitest/runner@1.4.0: - resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} + /@vitest/runner@1.6.0: + resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==} dependencies: - '@vitest/utils': 1.4.0 + '@vitest/utils': 1.6.0 p-limit: 5.0.0 pathe: 1.1.2 dev: true - /@vitest/snapshot@1.4.0: - resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} + /@vitest/snapshot@1.6.0: + resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==} dependencies: - magic-string: 0.30.8 + magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 dev: true @@ -6015,12 +5905,27 @@ packages: tinyspy: 2.2.1 dev: true - /@vitest/spy@1.4.0: - resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} + /@vitest/spy@1.6.0: + resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==} dependencies: tinyspy: 2.2.1 dev: true + /@vitest/ui@1.6.0(vitest@1.6.0): + resolution: {integrity: sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==} + peerDependencies: + vitest: 1.6.0 + dependencies: + '@vitest/utils': 1.6.0 + fast-glob: 3.3.2 + fflate: 0.8.2 + flatted: 3.3.1 + pathe: 1.1.2 + picocolors: 1.0.0 + sirv: 2.0.4 + vitest: 1.6.0(@types/node@20.12.10)(@vitest/ui@1.6.0) + dev: true + /@vitest/utils@1.3.1: resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==} dependencies: @@ -6030,8 +5935,8 @@ packages: pretty-format: 29.7.0 dev: true - /@vitest/utils@1.4.0: - resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} + /@vitest/utils@1.6.0: + resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==} dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 @@ -6058,24 +5963,24 @@ packages: path-browserify: 1.0.1 dev: true - /@vue/compiler-core@3.4.21: - resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==} + /@vue/compiler-core@3.4.26: + resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==} dependencies: - '@babel/parser': 7.24.1 - '@vue/shared': 3.4.21 + '@babel/parser': 7.24.5 + '@vue/shared': 3.4.26 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.0 dev: true - /@vue/compiler-dom@3.4.21: - resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} + /@vue/compiler-dom@3.4.26: + resolution: {integrity: sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==} dependencies: - '@vue/compiler-core': 3.4.21 - '@vue/shared': 3.4.21 + '@vue/compiler-core': 3.4.26 + '@vue/shared': 3.4.26 dev: true - /@vue/language-core@1.8.27(typescript@5.4.3): + /@vue/language-core@1.8.27(typescript@5.4.5): resolution: {integrity: sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==} peerDependencies: typescript: '*' @@ -6085,18 +5990,18 @@ packages: dependencies: '@volar/language-core': 1.11.1 '@volar/source-map': 1.11.1 - '@vue/compiler-dom': 3.4.21 - '@vue/shared': 3.4.21 + '@vue/compiler-dom': 3.4.26 + '@vue/shared': 3.4.26 computeds: 0.0.1 - minimatch: 9.0.3 + minimatch: 9.0.4 muggle-string: 0.3.1 path-browserify: 1.0.1 - typescript: 5.4.3 + typescript: 5.4.5 vue-template-compiler: 2.7.16 dev: true - /@vue/shared@3.4.21: - resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} + /@vue/shared@3.4.26: + resolution: {integrity: sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==} dev: true /@xobotyi/scrollbar-width@1.9.5: @@ -6125,7 +6030,7 @@ packages: resolution: {integrity: sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==} engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'} dependencies: - '@types/emscripten': 1.39.10 + '@types/emscripten': 1.39.11 tslib: 1.14.1 dev: true @@ -6406,7 +6311,7 @@ packages: /@zag-js/number-input@0.32.1: resolution: {integrity: sha512-atyIOvoMITb4hZtQym7yD6I7grvPW83UeMFO8hCQg3HWwd2zR4+63mouWuyMoWb4QrzVFRVQBaU8OG5xGlknEw==} dependencies: - '@internationalized/number': 3.5.1 + '@internationalized/number': 3.5.2 '@zag-js/anatomy': 0.32.1 '@zag-js/core': 0.32.1 '@zag-js/dom-event': 0.32.1 @@ -6516,7 +6421,7 @@ packages: '@zag-js/utils': 0.32.1 dev: false - /@zag-js/react@0.32.1(react-dom@18.2.0)(react@18.2.0): + /@zag-js/react@0.32.1(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-b1SB7hXXv1K6CmXkcy5Y7mb0YRWkyvulyhK8VW5O5hIAPuGxOTx70psmVeZbmVzhjdORCiro9jKx8Ec0LfolFg==} peerDependencies: react: '>=18.0.0' @@ -6526,8 +6431,8 @@ packages: '@zag-js/store': 0.32.1 '@zag-js/types': 0.32.1 proxy-compare: 2.5.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false /@zag-js/rect-utils@0.32.1: @@ -6690,18 +6595,6 @@ packages: resolution: {integrity: sha512-Vzieo4vNulzY/0zqmVfeYW/LcFJp5xtEoyUgR1FBctH8uBPBRhTIEXxKtoMablW6/vccOVo7zcu0UrR5Vx+eYQ==} dev: false - /@zkochan/retry@0.2.0: - resolution: {integrity: sha512-WhB+2B/ZPlW2Xy/kMJBrMbqecWXcbDDgn0K0wKBAgO2OlBTz1iLJrRWduo+DGGn0Akvz1Lu4Xvls7dJojximWw==} - engines: {node: '>=10'} - dev: true - - /@zkochan/rimraf@2.1.3: - resolution: {integrity: sha512-mCfR3gylCzPC+iqdxEA6z5SxJeOgzgbwmyxanKriIne5qZLswDe/M43aD3p5MNzwzXRhbZg/OX+MpES6Zk1a6A==} - engines: {node: '>=12.10'} - dependencies: - rimraf: 3.0.2 - dev: true - /accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -6871,7 +6764,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 @@ -6895,7 +6788,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 @@ -6907,7 +6800,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 @@ -6919,7 +6812,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 dev: true @@ -6929,7 +6822,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 dev: true @@ -6938,7 +6831,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 dev: true @@ -6947,7 +6840,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 dev: true @@ -6959,7 +6852,7 @@ packages: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 get-intrinsic: 1.2.4 is-array-buffer: 3.0.4 @@ -7003,55 +6896,55 @@ packages: possible-typed-array-names: 1.0.0 dev: true - /babel-core@7.0.0-bridge.0(@babel/core@7.24.3): + /babel-core@7.0.0-bridge.0(@babel/core@7.24.5): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.5 dev: true /babel-plugin-macros@3.1.0: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 cosmiconfig: 7.1.0 resolve: 1.22.8 dev: false - /babel-plugin-polyfill-corejs2@0.4.10(@babel/core@7.24.3): - resolution: {integrity: sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==} + /babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.5): + resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/compat-data': 7.24.1 - '@babel/core': 7.24.3 - '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.3) + '@babel/compat-data': 7.24.4 + '@babel/core': 7.24.5 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.5) semver: 6.3.1 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.3): + /babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.5): resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.3) - core-js-compat: 3.36.1 + '@babel/core': 7.24.5 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.5) + core-js-compat: 3.37.0 transitivePeerDependencies: - supports-color dev: true - /babel-plugin-polyfill-regenerator@0.6.1(@babel/core@7.24.3): - resolution: {integrity: sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==} + /babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.5): + resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 dependencies: - '@babel/core': 7.24.3 - '@babel/helper-define-polyfill-provider': 0.6.1(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.5) transitivePeerDependencies: - supports-color dev: true @@ -7114,13 +7007,6 @@ packages: - supports-color dev: true - /bole@5.0.11: - resolution: {integrity: sha512-KB0Ye0iMAW5BnNbnLfMSQcnI186hKUzE2fpkZWqcxsoTR7eqzlTidSOMYPHJOn/yR7VGH7uSZp37qH9q2Et0zQ==} - dependencies: - fast-safe-stringify: 2.1.1 - individual: 3.0.0 - dev: true - /boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} dev: false @@ -7167,10 +7053,10 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001600 - electron-to-chromium: 1.4.719 + caniuse-lite: 1.0.30001616 + electron-to-chromium: 1.4.757 node-releases: 2.0.14 - update-browserslist-db: 1.0.13(browserslist@4.23.0) + update-browserslist-db: 1.0.15(browserslist@4.23.0) dev: true /buffer-from@1.1.2: @@ -7184,12 +7070,6 @@ packages: ieee754: 1.2.1 dev: true - /builtins@5.0.1: - resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} - dependencies: - semver: 7.6.0 - dev: true - /bytes@3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} engines: {node: '>= 0.8'} @@ -7220,8 +7100,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - /caniuse-lite@1.0.30001600: - resolution: {integrity: sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==} + /caniuse-lite@1.0.30001616: + resolution: {integrity: sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==} dev: true /chai@4.4.1: @@ -7237,7 +7117,7 @@ packages: type-detect: 4.0.8 dev: true - /chakra-react-select@4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.4)(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /chakra-react-select@4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.4)(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-ZL43hyXPnWf1g/HjsZDecbeJ4F2Q6tTPYJozlKWkrQ7lIX7ORP0aZYwmc5/Wly4UNzMimj2Vuosl6MmIXH+G2g==} peerDependencies: '@chakra-ui/form-control': ^2.0.0 @@ -7251,17 +7131,17 @@ packages: react: ^18.0.0 react-dom: ^18.0.0 dependencies: - '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0) - '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0) - '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0) - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-select: 5.7.7(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) + '@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.1.8)(react@18.3.1) + '@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.3.1) + '@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.5)(react@18.3.1) + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-select: 5.7.7(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false @@ -7331,8 +7211,8 @@ packages: consola: 3.2.3 dev: true - /classcat@5.0.4: - resolution: {integrity: sha512-sbpkOw6z413p+HDGcBENe498WM9woqWHiJxCq7nvmxe9WmrUmqfAcxpIwAiMtM5Q3AhYkzXcNQHqsWq0mND51g==} + /classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} dev: false /clean-stack@2.2.0: @@ -7493,6 +7373,10 @@ packages: yargs: 17.7.2 dev: true + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true + /consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} @@ -7533,8 +7417,8 @@ packages: toggle-selection: 1.0.6 dev: false - /core-js-compat@3.36.1: - resolution: {integrity: sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==} + /core-js-compat@3.37.0: + resolution: {integrity: sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA==} dependencies: browserslist: 4.23.0 dev: true @@ -7668,11 +7552,6 @@ packages: d3-transition: 3.0.1(d3-selection@3.0.0) dev: false - /data-uri-to-buffer@3.0.1: - resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} - engines: {node: '>= 6'} - dev: true - /data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -7704,7 +7583,7 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 dev: true /dateformat@5.0.3: @@ -7933,7 +7812,7 @@ packages: /dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 csstype: 3.1.3 dev: false @@ -7953,10 +7832,10 @@ packages: dependencies: chalk: 4.1.2 fs-extra: 11.2.0 - glob: 10.3.10 + glob: 10.3.12 ora: 5.4.1 tslib: 2.6.2 - typescript: 5.4.3 + typescript: 5.4.5 yargs: 17.7.2 dev: true @@ -7985,16 +7864,16 @@ packages: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} dev: true - /ejs@3.1.9: - resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==} + /ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true dependencies: - jake: 10.8.7 + jake: 10.9.1 dev: true - /electron-to-chromium@1.4.719: - resolution: {integrity: sha512-FbWy2Q2YgdFzkFUW/W5jBjE9dj+804+98E4Pup78JBPnbdb3pv6IneY2JCPKdeKLh3AOKHQeYf+KwLr7mxGh6Q==} + /electron-to-chromium@1.4.757: + resolution: {integrity: sha512-jftDaCknYSSt/+KKeXzH3LX5E2CvRLm75P3Hj+J/dv3CL0qUYcOt13d5FN1NiL5IJbbhzHrb3BomeG2tkSlZmw==} dev: true /emoji-regex@8.0.0: @@ -8005,13 +7884,6 @@ packages: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} dev: true - /encode-registry@3.0.1: - resolution: {integrity: sha512-6qOwkl1g0fv0DN3Y3ggr2EaZXN71aoAqPp3p/pVaWSBSIo+YjLOWN61Fva43oVyQNPf7kgm8lkudzlzojwE2jw==} - engines: {node: '>=10'} - dependencies: - mem: 8.1.1 - dev: true - /encodeurl@1.0.2: resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} engines: {node: '>= 0.8'} @@ -8026,7 +7898,7 @@ packages: /engine.io-client@6.5.3: resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==} dependencies: - '@socket.io/component-emitter': 3.1.0 + '@socket.io/component-emitter': 3.1.2 debug: 4.3.4 engine.io-parser: 5.2.2 ws: 8.11.0 @@ -8047,16 +7919,12 @@ packages: engines: {node: '>=0.12'} dev: true - /envinfo@7.11.1: - resolution: {integrity: sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==} + /envinfo@7.13.0: + resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} engines: {node: '>=4'} hasBin: true dev: true - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: true - /error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -8068,8 +7936,8 @@ packages: stackframe: 1.3.4 dev: false - /es-abstract@1.23.2: - resolution: {integrity: sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==} + /es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.1 @@ -8087,7 +7955,7 @@ packages: function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 get-symbol-description: 1.0.2 - globalthis: 1.0.3 + globalthis: 1.0.4 gopd: 1.0.1 has-property-descriptors: 1.0.2 has-proto: 1.0.3 @@ -8144,18 +8012,18 @@ packages: stop-iteration-iterator: 1.0.0 dev: true - /es-iterator-helpers@1.0.18: - resolution: {integrity: sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==} + /es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 es-set-tostringtag: 2.0.3 function-bind: 1.1.2 get-intrinsic: 1.2.4 - globalthis: 1.0.3 + globalthis: 1.0.4 has-property-descriptors: 1.0.2 has-proto: 1.0.3 has-symbols: 1.0.3 @@ -8293,7 +8161,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.4.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + /eslint-module-utils@2.8.1(@typescript-eslint/parser@7.8.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} peerDependencies: @@ -8314,7 +8182,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5) debug: 3.2.7 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -8330,7 +8198,7 @@ packages: requireindex: 1.1.0 dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.4.0)(eslint@8.57.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.8.0)(eslint@8.57.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -8340,7 +8208,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 7.4.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/parser': 7.8.0(eslint@8.57.0)(typescript@5.4.5) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -8349,7 +8217,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.4.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.8.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -8375,8 +8243,8 @@ packages: load-tsconfig: 0.2.5 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + /eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 @@ -8404,7 +8272,7 @@ packages: array.prototype.toreversed: 1.1.2 array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 - es-iterator-helpers: 1.0.18 + es-iterator-helpers: 1.0.19 eslint: 8.57.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 @@ -8419,22 +8287,22 @@ packages: string.prototype.matchall: 4.0.11 dev: true - /eslint-plugin-simple-import-sort@12.0.0(eslint@8.57.0): - resolution: {integrity: sha512-8o0dVEdAkYap0Cn5kNeklaKcT1nUsa3LITWEuFk3nJifOoD+5JQGoyDUW2W/iPWwBsNBJpyJS9y4je/BgxLcyQ==} + /eslint-plugin-simple-import-sort@12.1.0(eslint@8.57.0): + resolution: {integrity: sha512-Y2fqAfC11TcG/WP3TrI1Gi3p3nc8XJyEOJYHyEPEGI/UAgNx6akxxlX74p7SbAQdLcgASKhj8M0GKvH3vq/+ig==} peerDependencies: eslint: '>=5.0.0' dependencies: eslint: 8.57.0 dev: true - /eslint-plugin-storybook@0.8.0(eslint@8.57.0)(typescript@5.4.3): + /eslint-plugin-storybook@0.8.0(eslint@8.57.0)(typescript@5.4.5): resolution: {integrity: sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==} engines: {node: '>= 18'} peerDependencies: eslint: '>=6' dependencies: '@storybook/csf': 0.0.1 - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 requireindex: 1.2.0 ts-dedent: 2.2.0 @@ -8443,8 +8311,8 @@ packages: - typescript dev: true - /eslint-plugin-unused-imports@3.1.0(@typescript-eslint/eslint-plugin@7.4.0)(eslint@8.57.0): - resolution: {integrity: sha512-9l1YFCzXKkw1qtAru1RWUtG2EVDZY0a0eChKXcL+EZ5jitG7qxdctu4RnvhOJHv4xfmUf7h+JJPINlVpGhZMrw==} + /eslint-plugin-unused-imports@3.2.0(@typescript-eslint/eslint-plugin@7.8.0)(eslint@8.57.0): + resolution: {integrity: sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': 6 - 7 @@ -8453,7 +8321,7 @@ packages: '@typescript-eslint/eslint-plugin': optional: true dependencies: - '@typescript-eslint/eslint-plugin': 7.4.0(@typescript-eslint/parser@7.4.0)(eslint@8.57.0)(typescript@5.4.3) + '@typescript-eslint/eslint-plugin': 7.8.0(@typescript-eslint/parser@7.8.0)(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-rule-composer: 0.3.0 dev: true @@ -8524,7 +8392,7 @@ packages: lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.3 + optionator: 0.9.4 strip-ansi: 6.0.1 text-table: 0.2.0 transitivePeerDependencies: @@ -8692,10 +8560,6 @@ packages: boolean: 3.2.0 dev: false - /fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - dev: true - /fast-shallow-equal@1.0.0: resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} dev: false @@ -8710,20 +8574,14 @@ packages: reusify: 1.0.4 dev: true - /fetch-blob@2.1.2: - resolution: {integrity: sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow==} - engines: {node: ^10.17.0 || >=12.3.0} - peerDependencies: - domexception: '*' - peerDependenciesMeta: - domexception: - optional: true - dev: true - /fetch-retry@5.0.6: resolution: {integrity: sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==} dev: true + /fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + dev: true + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -8731,6 +8589,13 @@ packages: flat-cache: 3.2.0 dev: true + /file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + dependencies: + flat-cache: 4.0.1 + dev: true + /file-selector@0.6.0: resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} engines: {node: '>= 12'} @@ -8841,12 +8706,20 @@ packages: rimraf: 3.0.2 dev: true + /flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + dev: true + /flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} dev: true - /flow-parser@0.232.0: - resolution: {integrity: sha512-U8vcKyYdM+Kb0tPzfPJ5JyPMU0uXKwHxp0L6BcEc+wBlbTW9qRhOqV5DeGXclgclVvtqQNGEG8Strj/b6c/IxA==} + /flow-parser@0.235.1: + resolution: {integrity: sha512-s04193L4JE+ntEcQXbD6jxRRlyj9QXcgEl2W6xSjH4l9x4b0eHoCHfbYHjqf9LdZFUiM5LhgpiqsvLj/AyOyYQ==} engines: {node: '>=0.4.0'} dev: true @@ -8882,7 +8755,11 @@ packages: engines: {node: '>= 0.6'} dev: true - /framer-motion@10.18.0(react-dom@18.2.0)(react@18.2.0): + /fracturedjsonjs@4.0.1: + resolution: {integrity: sha512-KMhSx7o45aPVj4w27dwdQyKJkNU8oBqw8UiK/s3VzsQB3+pKQ/3AqG/YOEQblV2BDuYE5dKp0OMf8RDsshrjTA==} + dev: false + + /framer-motion@10.18.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==} peerDependencies: react: ^18.0.0 @@ -8893,15 +8770,15 @@ packages: react-dom: optional: true dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) tslib: 2.6.2 optionalDependencies: '@emotion/is-prop-valid': 0.8.8 dev: false - /framer-motion@11.0.22(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-kWyldNJLyKDvLWjPYFmgngQYLiU8973BtAeVBc83r2cnil/NBUQJb1ff/6/EweNQYb5BW3PaXFjZa4D3pn/W2Q==} + /framer-motion@11.1.8(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-W2OGZmNfUarhh6A/rLXernq/JthjekbgeRWqzigPpbaShe/+HfQKUDSjiEdL302XOlINtO+SCFCiR1hlqN3uOA==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 @@ -8914,8 +8791,8 @@ packages: react-dom: optional: true dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) tslib: 2.6.2 dev: false @@ -8934,15 +8811,6 @@ packages: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: true - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - dev: true - /fs-extra@11.1.1: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} @@ -8998,7 +8866,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 functions-have-names: 1.2.3 dev: true @@ -9105,16 +8973,16 @@ packages: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} dev: true - /glob@10.3.10: - resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + /glob@10.3.12: + resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true dependencies: foreground-child: 3.1.1 jackspeak: 2.3.6 - minimatch: 9.0.3 - minipass: 7.0.4 - path-scurry: 1.10.1 + minimatch: 9.0.4 + minipass: 7.1.0 + path-scurry: 1.10.2 dev: true /glob@7.2.3: @@ -9140,11 +9008,12 @@ packages: type-fest: 0.20.2 dev: true - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + /globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.1 + gopd: 1.0.1 /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -9276,18 +9145,8 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true - /hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - dependencies: - lru-cache: 6.0.0 - dev: true - - /hosted-git-info@7.0.1: - resolution: {integrity: sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - lru-cache: 10.2.0 + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} dev: true /html-parse-stringify@3.0.1: @@ -9326,18 +9185,18 @@ packages: resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==} dev: false - /i18next-http-backend@2.5.0: - resolution: {integrity: sha512-Z/aQsGZk1gSxt2/DztXk92DuDD20J+rNudT7ZCdTrNOiK8uQppfvdjq9+DFQfpAnFPn3VZS+KQIr1S/W1KxhpQ==} + /i18next-http-backend@2.5.1: + resolution: {integrity: sha512-+rNX1tghdVxdfjfPt0bI1sNg5ahGW9kA7OboG7b4t03Fp69NdDlRIze6yXhIbN8rbHxJ8IP4dzRm/okZ15lkQg==} dependencies: cross-fetch: 4.0.0 transitivePeerDependencies: - encoding dev: false - /i18next@23.10.1: - resolution: {integrity: sha512-NDiIzFbcs3O9PXpfhkjyf7WdqFn5Vq6mhzhtkXzj51aOcNuPNcTwuYNuXCpHsanZGHlHKL35G7huoFeVic1hng==} + /i18next@23.11.3: + resolution: {integrity: sha512-Pq/aSKowir7JM0rj+Wa23Kb6KKDUGno/HjG+wRQu0PxoTbpQ4N89MAT0rFGvXmLkRLNMb1BbBOKGozl01dabzg==} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 dev: false /iconv-lite@0.4.24: @@ -9364,8 +9223,8 @@ packages: engines: {node: '>= 4'} dev: true - /immer@10.0.4: - resolution: {integrity: sha512-cuBuGK40P/sk5IzWa9QPUaAdvPHjkk1c+xYsd9oZw+YQQEV+10G0P5uMpGctZZKnyQ+ibRO08bD25nWLmYi2pw==} + /immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} dev: false /import-fresh@3.3.0: @@ -9390,10 +9249,6 @@ packages: engines: {node: '>=8'} dev: true - /individual@3.0.0: - resolution: {integrity: sha512-rUY5vtT748NMRbEMrTNiFfy29BgGZwGXUi2NFUVMWQrogSLzlJvQV9eeMWi+g1aVaQ53tpyLAQtd5x/JH0Nh1g==} - dev: true - /inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: @@ -9718,16 +9573,44 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true - /isexe@3.1.1: - resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} - engines: {node: '>=16'} - dev: true - /isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} dev: true + /istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@5.0.4: + resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} + engines: {node: '>=10'} + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.3.4 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + dev: true + /iterable-lookahead@1.0.0: resolution: {integrity: sha512-hJnEP2Xk4+44DDwJqUQGdXal5VbyeWLaPyDl2AQc242Zr7iqz4DgpQOrEzglWVMGHMDCkguLHEKxd1+rOsmgSQ==} engines: {node: '>=4'} @@ -9743,13 +9626,13 @@ packages: set-function-name: 2.0.2 dev: true - /its-fine@1.1.3(react@18.2.0): - resolution: {integrity: sha512-mncCA+yb6tuh5zK26cHqKlsSyxm4zdm4YgJpxycyx6p9fgxgK5PLu3iDVpKhzTn57Yrv3jk/r0aK0RFTT1OjFw==} + /its-fine@1.2.5(react@18.3.1): + resolution: {integrity: sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==} peerDependencies: react: '>=18.0' dependencies: '@types/react-reconciler': 0.28.8 - react: 18.2.0 + react: 18.3.1 dev: false /jackspeak@2.3.6: @@ -9761,8 +9644,8 @@ packages: '@pkgjs/parseargs': 0.11.0 dev: true - /jake@10.8.7: - resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==} + /jake@10.9.1: + resolution: {integrity: sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==} engines: {node: '>=10'} hasBin: true dependencies: @@ -9788,8 +9671,8 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - /js-tokens@8.0.3: - resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==} + /js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} dev: true /js-yaml@4.1.0: @@ -9799,7 +9682,7 @@ packages: argparse: 2.0.1 dev: true - /jscodeshift@0.15.2(@babel/preset-env@7.24.3): + /jscodeshift@0.15.2(@babel/preset-env@7.24.5): resolution: {integrity: sha512-FquR7Okgmc4Sd0aEDwqho3rEiKR3BdvuG9jfdHjLJ6JQoWSMpavug3AoIfnfWhxFlf+5pzQh8qjqz0DWFrNQzA==} hasBin: true peerDependencies: @@ -9808,20 +9691,20 @@ packages: '@babel/preset-env': optional: true dependencies: - '@babel/core': 7.24.3 - '@babel/parser': 7.24.1 - '@babel/plugin-transform-class-properties': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-optional-chaining': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-private-methods': 7.24.1(@babel/core@7.24.3) - '@babel/preset-env': 7.24.3(@babel/core@7.24.3) - '@babel/preset-flow': 7.24.1(@babel/core@7.24.3) - '@babel/preset-typescript': 7.24.1(@babel/core@7.24.3) - '@babel/register': 7.23.7(@babel/core@7.24.3) - babel-core: 7.0.0-bridge.0(@babel/core@7.24.3) + '@babel/core': 7.24.5 + '@babel/parser': 7.24.5 + '@babel/plugin-transform-class-properties': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.1(@babel/core@7.24.5) + '@babel/plugin-transform-optional-chaining': 7.24.5(@babel/core@7.24.5) + '@babel/plugin-transform-private-methods': 7.24.1(@babel/core@7.24.5) + '@babel/preset-env': 7.24.5(@babel/core@7.24.5) + '@babel/preset-flow': 7.24.1(@babel/core@7.24.5) + '@babel/preset-typescript': 7.24.1(@babel/core@7.24.5) + '@babel/register': 7.23.7(@babel/core@7.24.5) + babel-core: 7.0.0-bridge.0(@babel/core@7.24.5) chalk: 4.1.2 - flow-parser: 0.232.0 + flow-parser: 0.235.1 graceful-fs: 4.2.11 micromatch: 4.0.5 neo-async: 2.6.2 @@ -9851,11 +9734,6 @@ packages: /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - /json-parse-even-better-errors@3.0.1: - resolution: {integrity: sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true - /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true @@ -9864,10 +9742,6 @@ packages: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true - /json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - dev: true - /json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -9881,10 +9755,6 @@ packages: hasBin: true dev: true - /jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} - dev: true - /jsondiffpatch@0.6.0: resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -9940,8 +9810,8 @@ packages: engines: {node: '>= 8'} dev: false - /knip@5.6.1(@types/node@20.11.30)(typescript@5.4.3): - resolution: {integrity: sha512-occwYqHrV6KSyM1DbpWj8qQ8pCQzsdxVxYbjhYcryoXxWmHG2scyxxB4HyxVmp3Xdora4Px+3ZV5QQDi2ArerA==} + /knip@5.12.3(@types/node@20.12.10)(typescript@5.4.5): + resolution: {integrity: sha512-LL+NsE+3H0TkUnQW6icHQ+5qSrPENmjHJyMHgzjiZPmunstrIsaRG+QjahnzoH/FjMjVJwrdwVOSvksa8ixFbw==} engines: {node: '>=18.6.0'} hasBin: true peerDependencies: @@ -9950,31 +9820,24 @@ packages: dependencies: '@ericcornelissen/bash-parser': 0.5.2 '@nodelib/fs.walk': 2.0.0 - '@npmcli/map-workspaces': 3.0.4 - '@npmcli/package-json': 5.0.0 - '@pnpm/logger': 5.0.0 - '@pnpm/workspace.pkgs-graph': 2.0.15(@pnpm/logger@5.0.0) '@snyk/github-codeowners': 1.1.0 - '@types/node': 20.11.30 - '@types/picomatch': 2.3.3 + '@types/node': 20.12.10 easy-table: 1.2.0 fast-glob: 3.3.2 + file-entry-cache: 8.0.0 jiti: 1.21.0 js-yaml: 4.1.0 - micromatch: 4.0.5 minimist: 1.2.8 picocolors: 1.0.0 - picomatch: 4.0.1 + picomatch: 4.0.2 pretty-ms: 9.0.0 + resolve: 1.22.8 smol-toml: 1.1.4 strip-json-comments: 5.0.1 summary: 2.1.0 - typescript: 5.4.3 - zod: 3.22.4 - zod-validation-error: 3.0.3(zod@3.22.4) - transitivePeerDependencies: - - bluebird - - domexception + typescript: 5.4.5 + zod: 3.23.6 + zod-validation-error: 3.2.0(zod@3.23.6) dev: true /kolorist@1.8.0: @@ -10018,16 +9881,6 @@ packages: ts-error: 1.0.6 dev: false - /load-json-file@6.2.0: - resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} - engines: {node: '>=8'} - dependencies: - graceful-fs: 4.2.11 - parse-json: 5.2.0 - strip-bom: 4.0.0 - type-fest: 0.6.0 - dev: true - /load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10037,8 +9890,8 @@ packages: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} dependencies: - mlly: 1.6.1 - pkg-types: 1.0.3 + mlly: 1.7.0 + pkg-types: 1.1.0 dev: true /locate-path@3.0.0: @@ -10115,8 +9968,8 @@ packages: get-func-name: 2.0.2 dev: true - /lru-cache@10.2.0: - resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} + /lru-cache@10.2.2: + resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} dev: true @@ -10151,13 +10004,20 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /magic-string@0.30.8: - resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} - engines: {node: '>=12'} + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /magicast@0.3.4: + resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} + dependencies: + '@babel/parser': 7.24.5 + '@babel/types': 7.24.5 + source-map-js: 1.2.0 + dev: true + /make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -10173,11 +10033,11 @@ packages: semver: 6.3.1 dev: true - /map-age-cleaner@0.1.3: - resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} - engines: {node: '>=6'} + /make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} dependencies: - p-defer: 1.0.0 + semver: 7.6.0 dev: true /map-obj@2.0.0: @@ -10189,13 +10049,13 @@ packages: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} dev: true - /markdown-to-jsx@7.3.2(react@18.2.0): + /markdown-to-jsx@7.3.2(react@18.3.1): resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==} engines: {node: '>= 10'} peerDependencies: react: '>= 0.14.0' dependencies: - react: 18.2.0 + react: 18.3.1 dev: true /mdn-data@2.0.14: @@ -10207,22 +10067,6 @@ packages: engines: {node: '>= 0.6'} dev: true - /mem@6.1.1: - resolution: {integrity: sha512-Ci6bIfq/UgcxPTYa8dQQ5FY3BzKkT894bwXWXxC/zqs0XgMO2cT20CGkOqda7gZNkmK5VP4x89IGZ6K7hfbn3Q==} - engines: {node: '>=8'} - dependencies: - map-age-cleaner: 0.1.3 - mimic-fn: 3.1.0 - dev: true - - /mem@8.1.1: - resolution: {integrity: sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==} - engines: {node: '>=10'} - dependencies: - map-age-cleaner: 0.1.3 - mimic-fn: 3.1.0 - dev: true - /memoize-one@6.0.0: resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} dev: false @@ -10282,11 +10126,6 @@ packages: engines: {node: '>=6'} dev: true - /mimic-fn@3.1.0: - resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} - engines: {node: '>=8'} - dev: true - /mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -10316,8 +10155,8 @@ packages: brace-expansion: 2.0.1 dev: true - /minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 @@ -10339,8 +10178,8 @@ packages: engines: {node: '>=8'} dev: true - /minipass@7.0.4: - resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + /minipass@7.1.0: + resolution: {integrity: sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig==} engines: {node: '>=16 || 14 >=14.17'} dev: true @@ -10362,12 +10201,12 @@ packages: hasBin: true dev: true - /mlly@1.6.1: - resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} + /mlly@1.7.0: + resolution: {integrity: sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==} dependencies: acorn: 8.11.3 pathe: 1.1.2 - pkg-types: 1.0.3 + pkg-types: 1.1.0 ufo: 1.5.3 dev: true @@ -10375,6 +10214,11 @@ packages: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} dev: false + /mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} + dev: true + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: true @@ -10390,7 +10234,7 @@ packages: resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} dev: true - /nano-css@5.6.1(react-dom@18.2.0)(react@18.2.0): + /nano-css@5.6.1(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-T2Mhc//CepkTa3X4pUhKgbEheJHYAxD0VptuqFhDbGMUWVV2m+lkNiW/Ieuj35wrfC8Zm0l7HvssQh7zcEttSw==} peerDependencies: react: '*' @@ -10401,11 +10245,11 @@ packages: csstype: 3.1.3 fastest-stable-stringify: 2.0.2 inline-style-prefixer: 7.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) rtl-css-js: 1.16.1 stacktrace-js: 2.0.2 - stylis: 4.3.1 + stylis: 4.3.2 dev: false /nanoid@3.3.7: @@ -10414,8 +10258,8 @@ packages: hasBin: true dev: true - /nanostores@0.10.0: - resolution: {integrity: sha512-Poy5+9wFXOD0jAstn4kv9n686U2BFw48z/W8lms8cS8lcbRz7BU20JxZ3e/kkKQVfRrkm4yLWCUA6GQINdvJCQ==} + /nanostores@0.10.3: + resolution: {integrity: sha512-Nii8O1XqmawqSCf9o2aWqVxhKRN01+iue9/VEd1TiJCr9VT5XxgPFbF1Edl1XN6pwJcZRsl8Ki+z01yb/T/C2g==} engines: {node: ^18.0.0 || >=20.0.0} dev: false @@ -10428,18 +10272,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /ndjson@2.0.0: - resolution: {integrity: sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==} - engines: {node: '>=10'} - hasBin: true - dependencies: - json-stringify-safe: 5.0.1 - minimist: 1.2.8 - readable-stream: 3.6.2 - split2: 3.2.2 - through2: 4.0.2 - dev: true - /nearley@2.20.1: resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} hasBin: true @@ -10486,16 +10318,6 @@ packages: dependencies: whatwg-url: 5.0.0 - /node-fetch@3.0.0-beta.9: - resolution: {integrity: sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg==} - engines: {node: ^10.17 || >=12.3} - dependencies: - data-uri-to-buffer: 3.0.1 - fetch-blob: 2.1.2 - transitivePeerDependencies: - - domexception - dev: true - /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true @@ -10509,53 +10331,11 @@ packages: validate-npm-package-license: 3.0.4 dev: true - /normalize-package-data@6.0.0: - resolution: {integrity: sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - hosted-git-info: 7.0.1 - is-core-module: 2.13.1 - semver: 7.6.0 - validate-npm-package-license: 3.0.4 - dev: true - /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: true - /npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - semver: 7.6.0 - dev: true - - /npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true - - /npm-package-arg@11.0.1: - resolution: {integrity: sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - hosted-git-info: 7.0.1 - proc-log: 3.0.0 - semver: 7.6.0 - validate-npm-package-name: 5.0.0 - dev: true - - /npm-pick-manifest@9.0.0: - resolution: {integrity: sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==} - engines: {node: ^16.14.0 || >=18.0.0} - dependencies: - npm-install-checks: 6.3.0 - npm-normalize-package-bin: 3.0.1 - npm-package-arg: 11.0.1 - semver: 7.6.0 - dev: true - /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -10636,7 +10416,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-object-atoms: 1.0.0 dev: true @@ -10646,7 +10426,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 dev: true /object.hasown@1.1.4: @@ -10654,7 +10434,7 @@ packages: engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-object-atoms: 1.0.0 dev: true @@ -10724,20 +10504,20 @@ packages: fast-glob: 3.3.2 js-yaml: 4.1.0 supports-color: 9.4.0 - undici: 5.28.3 + undici: 5.28.4 yargs-parser: 21.1.1 dev: true - /optionator@0.9.3: - resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + /optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} dependencies: - '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 + word-wrap: 1.2.5 dev: true /ora@5.4.1: @@ -10755,25 +10535,20 @@ packages: wcwidth: 1.0.1 dev: true - /overlayscrollbars-react@0.5.5(overlayscrollbars@2.6.1)(react@18.2.0): - resolution: {integrity: sha512-PakK1QEV/PAi4XniiTykcSeyoBmfDvgv2uBQ290IaY5ThrwvWg3Zk3Z39hosJYkyrS4mJ0zuIWtlHX4AKd2nZQ==} + /overlayscrollbars-react@0.5.6(overlayscrollbars@2.7.3)(react@18.3.1): + resolution: {integrity: sha512-E5To04bL5brn9GVCZ36SnfGanxa2I2MDkWoa4Cjo5wol7l+diAgi4DBc983V7l2nOk/OLJ6Feg4kySspQEGDBw==} peerDependencies: overlayscrollbars: ^2.0.0 react: '>=16.8.0' dependencies: - overlayscrollbars: 2.6.1 - react: 18.2.0 + overlayscrollbars: 2.7.3 + react: 18.3.1 dev: false - /overlayscrollbars@2.6.1: - resolution: {integrity: sha512-V+ZAqWMYMyGBJNRDEcdRC7Ch+WT9RBx9hY8bfJSMyFObQeJoecs1Vqg7ZAzBVcpN6sCUXFAZldCbeySwmmD0RA==} + /overlayscrollbars@2.7.3: + resolution: {integrity: sha512-HmNo8RPtuGUjBhUbVpZBHH7SHci5iSAdg5zSekCZVsjzaM6z8MIr3F9RXrzf4y7m+fOY0nx0+y0emr1fqQmfoA==} dev: false - /p-defer@1.0.0: - resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} - engines: {node: '>=4'} - dev: true - /p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -10823,14 +10598,6 @@ packages: aggregate-error: 3.1.0 dev: true - /p-memoize@4.0.1: - resolution: {integrity: sha512-km0sP12uE0dOZ5qP+s7kGVf07QngxyG0gS8sYFvFWhqlgzOsSy+m71aUejf/0akxj5W7gE//2G74qTv6b4iMog==} - engines: {node: '>=10'} - dependencies: - mem: 6.1.1 - mimic-fn: 3.1.0 - dev: true - /p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -10860,13 +10627,6 @@ packages: engines: {node: '>=18'} dev: true - /parse-npm-tarball-url@3.0.0: - resolution: {integrity: sha512-InpdgIdNe5xWMEUcrVQUniQKwnggBtJ7+SCwh7zQAZwbbIYZV9XdgJyhtmDSSvykFyQXoe4BINnzKTfCwWLs5g==} - engines: {node: '>=8.15'} - dependencies: - semver: 6.3.1 - dev: true - /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -10904,19 +10664,12 @@ packages: /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - /path-scurry@1.10.1: - resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + /path-scurry@1.10.2: + resolution: {integrity: sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==} engines: {node: '>=16 || 14 >=14.17'} dependencies: - lru-cache: 10.2.0 - minipass: 7.0.4 - dev: true - - /path-temp@2.1.0: - resolution: {integrity: sha512-cMMJTAZlion/RWRRC48UbrDymEIt+/YSD/l8NqjneyDw2rDOBQcP5yRkMB4CYGn47KMhZvbblBP7Z79OsMw72w==} - engines: {node: '>=8.15'} - dependencies: - unique-string: 2.0.0 + lru-cache: 10.2.2 + minipass: 7.1.0 dev: true /path-to-regexp@0.1.7: @@ -10951,8 +10704,8 @@ packages: engines: {node: '>=8.6'} dev: true - /picomatch@4.0.1: - resolution: {integrity: sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==} + /picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} dev: true @@ -10987,11 +10740,11 @@ packages: find-up: 5.0.0 dev: true - /pkg-types@1.0.3: - resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + /pkg-types@1.1.0: + resolution: {integrity: sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==} dependencies: - jsonc-parser: 3.2.1 - mlly: 1.6.1 + confbox: 0.1.7 + mlly: 1.7.0 pathe: 1.1.2 dev: true @@ -10999,7 +10752,7 @@ packages: resolution: {integrity: sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==} engines: {node: '>=10'} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 dev: true /possible-typed-array-names@1.0.0: @@ -11042,7 +10795,7 @@ packages: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 - react-is: 18.2.0 + react-is: 18.3.1 dev: true /pretty-hrtime@1.0.3: @@ -11057,11 +10810,6 @@ packages: parse-ms: 4.0.0 dev: true - /proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true - /process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} dev: true @@ -11071,23 +10819,6 @@ packages: engines: {node: '>= 0.6.0'} dev: true - /promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - dev: true - - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - dev: true - /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -11149,8 +10880,8 @@ packages: side-channel: 1.0.6 dev: true - /qs@6.12.0: - resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==} + /qs@6.12.1: + resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} engines: {node: '>=0.6'} dependencies: side-channel: 1.0.6 @@ -11200,39 +10931,39 @@ packages: unpipe: 1.0.0 dev: true - /react-clientside-effect@1.2.6(react@18.2.0): + /react-clientside-effect@1.2.6(react@18.3.1): resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==} peerDependencies: react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 dependencies: '@babel/runtime': 7.24.1 - react: 18.2.0 + react: 18.3.1 dev: false - /react-colorful@5.6.1(react-dom@18.2.0)(react@18.2.0): + /react-colorful@5.6.1(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - /react-docgen-typescript@2.2.2(typescript@5.4.3): + /react-docgen-typescript@2.2.2(typescript@5.4.5): resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==} peerDependencies: typescript: '>= 4.3.x' dependencies: - typescript: 5.4.3 + typescript: 5.4.5 dev: true /react-docgen@7.0.3: resolution: {integrity: sha512-i8aF1nyKInZnANZ4uZrH49qn1paRgBZ7wZiCNBMnenlPzEv0mRl+ShpTVEI6wZNl8sSc79xZkivtgLKQArcanQ==} engines: {node: '>=16.14.0'} dependencies: - '@babel/core': 7.24.3 - '@babel/traverse': 7.24.1 - '@babel/types': 7.24.0 + '@babel/core': 7.24.5 + '@babel/traverse': 7.24.5 + '@babel/types': 7.24.5 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.5 '@types/doctrine': 0.0.9 @@ -11244,16 +10975,16 @@ packages: - supports-color dev: true - /react-dom@18.2.0(react@18.2.0): - resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + /react-dom@18.3.1(react@18.3.1): + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: - react: ^18.2.0 + react: ^18.3.1 dependencies: loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 + react: 18.3.1 + scheduler: 0.23.2 - /react-dropzone@14.2.3(react@18.2.0): + /react-dropzone@14.2.3(react@18.3.1): resolution: {integrity: sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==} engines: {node: '>= 10.13'} peerDependencies: @@ -11262,10 +10993,10 @@ packages: attr-accept: 2.2.2 file-selector: 0.6.0 prop-types: 15.8.1 - react: 18.2.0 + react: 18.3.1 dev: false - /react-element-to-jsx-string@15.0.0(react-dom@18.2.0)(react@18.2.0): + /react-element-to-jsx-string@15.0.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==} peerDependencies: react: ^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0 @@ -11273,25 +11004,25 @@ packages: dependencies: '@base2/pretty-print-object': 1.0.1 is-plain-object: 5.0.0 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) react-is: 18.1.0 dev: true - /react-error-boundary@4.0.13(react@18.2.0): + /react-error-boundary@4.0.13(react@18.3.1): resolution: {integrity: sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==} peerDependencies: react: '>=16.13.1' dependencies: - '@babel/runtime': 7.24.1 - react: 18.2.0 + '@babel/runtime': 7.24.5 + react: 18.3.1 dev: false /react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} dev: false - /react-focus-lock@2.11.1(@types/react@18.2.73)(react@18.2.0): + /react-focus-lock@2.11.1(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-IXLwnTBrLTlKTpASZXqqXJ8oymWrgAlOfuuDYN4XCuN1YJ72dwX198UCaF1QqGUk5C3QOnlMik//n3ufcfe8Ig==} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -11301,36 +11032,36 @@ packages: optional: true dependencies: '@babel/runtime': 7.23.9 - '@types/react': 18.2.73 + '@types/react': 18.3.1 focus-lock: 1.3.3 prop-types: 15.8.1 - react: 18.2.0 - react-clientside-effect: 1.2.6(react@18.2.0) - use-callback-ref: 1.3.1(@types/react@18.2.73)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-clientside-effect: 1.2.6(react@18.3.1) + use-callback-ref: 1.3.1(@types/react@18.3.1)(react@18.3.1) + use-sidecar: 1.1.2(@types/react@18.3.1)(react@18.3.1) dev: false - /react-hook-form@7.51.2(react@18.2.0): - resolution: {integrity: sha512-y++lwaWjtzDt/XNnyGDQy6goHskFualmDlf+jzEZvjvz6KWDf7EboL7pUvRCzPTJd0EOPpdekYaQLEvvG6m6HA==} + /react-hook-form@7.51.4(react@18.3.1): + resolution: {integrity: sha512-V14i8SEkh+V1gs6YtD0hdHYnoL4tp/HX/A45wWQN15CYr9bFRmmRdYStSO5L65lCCZRF+kYiSKhm9alqbcdiVA==} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /react-hotkeys-hook@4.5.0(react-dom@18.2.0)(react@18.2.0): + /react-hotkeys-hook@4.5.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Samb85GSgAWFQNvVt3PS90LPPGSf9mkH/r4au81ZP1yOIFayLC3QAvqTgGtJ8YEDMXtPmaVBs6NgipHO6h4Mug==} peerDependencies: react: '>=16.8.1' react-dom: '>=16.8.1' dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /react-i18next@14.1.0(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3KwX6LHpbvGQ+sBEntjV4sYW3Zovjjl3fpoHbUwSgFHf0uRBcbeCBLR5al6ikncI5+W0EFb71QXZmfop+J6NrQ==} + /react-i18next@14.1.1(i18next@23.11.3)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-QSiKw+ihzJ/CIeIYWrarCmXJUySHDwQr5y8uaNIkbxoGRm/5DukkxZs+RPla79IKyyDPzC/DRlgQCABHtrQuQQ==} peerDependencies: i18next: '>= 23.2.3' react: '>= 16.8.0' @@ -11342,19 +11073,19 @@ packages: react-native: optional: true dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 html-parse-stringify: 3.0.1 - i18next: 23.10.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + i18next: 23.11.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /react-icons@5.0.1(react@18.2.0): - resolution: {integrity: sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==} + /react-icons@5.2.0(react@18.3.1): + resolution: {integrity: sha512-n52Y7Eb4MgQZHsSZOhSXv1zs2668/hBYKfSRIvKh42yExjyhZu0d1IK2CLLZ3BZB1oo13lDfwx2vOh2z9FTV6Q==} peerDependencies: react: '*' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false /react-is@16.13.1: @@ -11368,11 +11099,11 @@ packages: resolution: {integrity: sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==} dev: true - /react-is@18.2.0: - resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + /react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} dev: true - /react-konva@18.2.10(konva@9.3.6)(react-dom@18.2.0)(react@18.2.0): + /react-konva@18.2.10(konva@9.3.6)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g==} peerDependencies: konva: ^8.0.1 || ^7.2.5 || ^9.0.0 @@ -11380,48 +11111,45 @@ packages: react-dom: '>=18.0.0' dependencies: '@types/react-reconciler': 0.28.8 - its-fine: 1.1.3(react@18.2.0) + its-fine: 1.2.5(react@18.3.1) konva: 9.3.6 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-reconciler: 0.29.0(react@18.2.0) - scheduler: 0.23.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-reconciler: 0.29.2(react@18.3.1) + scheduler: 0.23.2 dev: false - /react-reconciler@0.29.0(react@18.2.0): - resolution: {integrity: sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==} + /react-reconciler@0.29.2(react@18.3.1): + resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==} engines: {node: '>=0.10.0'} peerDependencies: - react: ^18.2.0 + react: ^18.3.1 dependencies: loose-envify: 1.4.0 - react: 18.2.0 - scheduler: 0.23.0 + react: 18.3.1 + scheduler: 0.23.2 dev: false - /react-redux@9.1.0(@types/react@18.2.73)(react@18.2.0)(redux@5.0.1): - resolution: {integrity: sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ==} + /react-redux@9.1.2(@types/react@18.3.1)(react@18.3.1)(redux@5.0.1): + resolution: {integrity: sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==} peerDependencies: '@types/react': ^18.2.25 react: ^18.0 - react-native: '>=0.69' redux: ^5.0.0 peerDependenciesMeta: '@types/react': optional: true - react-native: - optional: true redux: optional: true dependencies: - '@types/react': 18.2.73 + '@types/react': 18.3.1 '@types/use-sync-external-store': 0.0.3 - react: 18.2.0 + react: 18.3.1 redux: 5.0.1 - use-sync-external-store: 1.2.0(react@18.2.0) + use-sync-external-store: 1.2.2(react@18.3.1) dev: false - /react-remove-scroll-bar@2.3.5(@types/react@18.2.73)(react@18.2.0): + /react-remove-scroll-bar@2.3.5(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==} engines: {node: '>=10'} peerDependencies: @@ -11431,13 +11159,13 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.73 - react: 18.2.0 - react-style-singleton: 2.2.1(@types/react@18.2.73)(react@18.2.0) + '@types/react': 18.3.1 + react: 18.3.1 + react-style-singleton: 2.2.1(@types/react@18.3.1)(react@18.3.1) tslib: 2.6.2 dev: false - /react-remove-scroll@2.5.7(@types/react@18.2.73)(react@18.2.0): + /react-remove-scroll@2.5.7(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==} engines: {node: '>=10'} peerDependencies: @@ -11447,68 +11175,68 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.73 - react: 18.2.0 - react-remove-scroll-bar: 2.3.5(@types/react@18.2.73)(react@18.2.0) - react-style-singleton: 2.2.1(@types/react@18.2.73)(react@18.2.0) + '@types/react': 18.3.1 + react: 18.3.1 + react-remove-scroll-bar: 2.3.5(@types/react@18.3.1)(react@18.3.1) + react-style-singleton: 2.2.1(@types/react@18.3.1)(react@18.3.1) tslib: 2.6.2 - use-callback-ref: 1.3.1(@types/react@18.2.73)(react@18.2.0) - use-sidecar: 1.1.2(@types/react@18.2.73)(react@18.2.0) + use-callback-ref: 1.3.1(@types/react@18.3.1)(react@18.3.1) + use-sidecar: 1.1.2(@types/react@18.3.1)(react@18.3.1) dev: false - /react-resizable-panels@2.0.16(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-UrnxmTZaTnbCl/xIOX38ig35RicqGfLuqt2x5fytpNlQvCRuxyXZwIBEhmF+pmrEGxfajyXFBoCplNxLvhF0CQ==} + /react-resizable-panels@2.0.19(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-v3E41kfKSuCPIvJVb4nL4mIZjjKIn/gh6YqZF/gDfQDolv/8XnhJBek4EiV2gOr3hhc5A3kOGOayk3DhanpaQw==} peerDependencies: react: ^16.14.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /react-select@5.7.7(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /react-select@5.7.7(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-HhashZZJDRlfF/AKj0a0Lnfs3sRdw/46VJIRd8IbB9/Ovr74+ZIwkAdSBjSPXsFMG+u72c5xShqwLSKIJllzqw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 '@emotion/cache': 11.11.0 - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@floating-ui/dom': 1.6.3 + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@floating-ui/dom': 1.6.5 '@types/react-transition-group': 4.4.10 memoize-one: 6.0.0 prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /react-select@5.8.0(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): + /react-select@5.8.0(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 '@emotion/cache': 11.11.0 - '@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0) - '@floating-ui/dom': 1.6.3 + '@emotion/react': 11.11.4(@types/react@18.3.1)(react@18.3.1) + '@floating-ui/dom': 1.6.5 '@types/react-transition-group': 4.4.10 memoize-one: 6.0.0 prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0) - use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.73)(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) + use-isomorphic-layout-effect: 1.1.2(@types/react@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' dev: false - /react-style-singleton@2.2.1(@types/react@18.2.73)(react@18.2.0): + /react-style-singleton@2.2.1(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} peerDependencies: @@ -11518,38 +11246,38 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.73 + '@types/react': 18.3.1 get-nonce: 1.0.1 invariant: 2.2.4 - react: 18.2.0 + react: 18.3.1 tslib: 2.6.2 dev: false - /react-transition-group@4.4.5(react-dom@18.2.0)(react@18.2.0): + /react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: react: '>=16.6.0' react-dom: '>=16.6.0' dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /react-universal-interface@0.6.2(react@18.2.0)(tslib@2.6.2): + /react-universal-interface@0.6.2(react@18.3.1)(tslib@2.6.2): resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} peerDependencies: react: '*' tslib: '*' dependencies: - react: 18.2.0 + react: 18.3.1 tslib: 2.6.2 dev: false - /react-use@17.5.0(react-dom@18.2.0)(react@18.2.0): + /react-use@17.5.0(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-PbfwSPMwp/hoL847rLnm/qkjg3sTRCvn6YhUZiHaUa3FA6/aNoFX79ul5Xt70O1rK+9GxSVqkY0eTwMdsR/bWg==} peerDependencies: react: '*' @@ -11561,10 +11289,10 @@ packages: fast-deep-equal: 3.1.3 fast-shallow-equal: 1.0.0 js-cookie: 2.2.1 - nano-css: 5.6.1(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - react-universal-interface: 0.6.2(react@18.2.0)(tslib@2.6.2) + nano-css: 5.6.1(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-universal-interface: 0.6.2(react@18.3.1)(tslib@2.6.2) resize-observer-polyfill: 1.5.1 screenfull: 5.2.0 set-harmonic-interval: 1.0.1 @@ -11573,50 +11301,42 @@ packages: tslib: 2.6.2 dev: false - /react-virtuoso@4.7.5(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sYRQ1dHGiLCA/4ngq86U4fjO5SubEbbR53+mmcgcQZjzTK2E+9M300C3nXr54Zgr1ewZfdr9SKt6wpha0CsYUQ==} + /react-virtuoso@4.7.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-l+fnBf/G1Fp6pHCnhFq2Ra4lkZtT6c5XrS9rCS0OA6de7WGLZviCo0y61CUZZG79TeAw3L7O4czeNPiqh9CIrg==} engines: {node: '>=10'} peerDependencies: react: '>=16 || >=17 || >= 18' react-dom: '>=16 || >=17 || >= 18' dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /react@18.2.0: - resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + /react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 - /reactflow@11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-0CApYhtYicXEDg/x2kvUHiUk26Qur8lAtTtiSlptNKuyEuGti6P1y5cS32YGaUoDMoCqkm/m+jcKkfMOvSCVRA==} + /reactflow@11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-wusd1Xpn1wgsSEv7UIa4NNraCwH9syBtubBy4xVNXg3b+CDKM+sFaF3hnMx0tr0et4km9urIDdNvwm34QiZong==} peerDependencies: react: '>=17' react-dom: '>=17' dependencies: - '@reactflow/background': 11.3.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/controls': 11.2.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/core': 11.10.4(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/minimap': 11.7.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-resizer': 2.2.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - '@reactflow/node-toolbar': 1.3.9(@types/react@18.2.73)(react-dom@18.2.0)(react@18.2.0) - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + '@reactflow/background': 11.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@reactflow/controls': 11.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@reactflow/core': 11.11.3(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@reactflow/minimap': 11.7.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@reactflow/node-resizer': 2.2.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + '@reactflow/node-toolbar': 1.3.13(@types/react@18.3.1)(react-dom@18.3.1)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer dev: false - /read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - json-parse-even-better-errors: 3.0.1 - npm-normalize-package-bin: 3.0.1 - dev: true - /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -11717,10 +11437,10 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 get-intrinsic: 1.2.4 - globalthis: 1.0.3 + globalthis: 1.0.4 which-builtin-type: 1.1.3 dev: true @@ -11741,7 +11461,7 @@ packages: /regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 dev: true /regexp.prototype.flags@1.5.2: @@ -11794,14 +11514,6 @@ packages: unist-util-visit: 5.0.0 dev: true - /rename-overwrite@5.0.0: - resolution: {integrity: sha512-vSxE5Ww7Jnyotvaxi3Dj0vOMoojH8KMkBfs9xYeW/qNfJiLTcC1fmwTjrbGUq3mQSOCxkG0DbdcvwTUrpvBN4w==} - engines: {node: '>=12.10'} - dependencies: - '@zkochan/rimraf': 2.1.3 - fs-extra: 10.1.0 - dev: true - /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -11871,11 +11583,6 @@ packages: engines: {node: '>=0.12'} dev: false - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: true - /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -11936,34 +11643,36 @@ packages: fsevents: 2.3.3 dev: true - /rollup@4.13.1: - resolution: {integrity: sha512-hFi+fU132IvJ2ZuihN56dwgpltpmLZHZWsx27rMCTZ2sYwrqlgL5sECGy1eeV2lAihD8EzChBVVhsXci0wD4Tg==} + /rollup@4.17.2: + resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.13.1 - '@rollup/rollup-android-arm64': 4.13.1 - '@rollup/rollup-darwin-arm64': 4.13.1 - '@rollup/rollup-darwin-x64': 4.13.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.13.1 - '@rollup/rollup-linux-arm64-gnu': 4.13.1 - '@rollup/rollup-linux-arm64-musl': 4.13.1 - '@rollup/rollup-linux-riscv64-gnu': 4.13.1 - '@rollup/rollup-linux-s390x-gnu': 4.13.1 - '@rollup/rollup-linux-x64-gnu': 4.13.1 - '@rollup/rollup-linux-x64-musl': 4.13.1 - '@rollup/rollup-win32-arm64-msvc': 4.13.1 - '@rollup/rollup-win32-ia32-msvc': 4.13.1 - '@rollup/rollup-win32-x64-msvc': 4.13.1 + '@rollup/rollup-android-arm-eabi': 4.17.2 + '@rollup/rollup-android-arm64': 4.17.2 + '@rollup/rollup-darwin-arm64': 4.17.2 + '@rollup/rollup-darwin-x64': 4.17.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.17.2 + '@rollup/rollup-linux-arm-musleabihf': 4.17.2 + '@rollup/rollup-linux-arm64-gnu': 4.17.2 + '@rollup/rollup-linux-arm64-musl': 4.17.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2 + '@rollup/rollup-linux-riscv64-gnu': 4.17.2 + '@rollup/rollup-linux-s390x-gnu': 4.17.2 + '@rollup/rollup-linux-x64-gnu': 4.17.2 + '@rollup/rollup-linux-x64-musl': 4.17.2 + '@rollup/rollup-win32-arm64-msvc': 4.17.2 + '@rollup/rollup-win32-ia32-msvc': 4.17.2 + '@rollup/rollup-win32-x64-msvc': 4.17.2 fsevents: 2.3.3 dev: true /rtl-css-js@1.16.1: resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} dependencies: - '@babel/runtime': 7.24.1 + '@babel/runtime': 7.24.5 dev: false /run-parallel@1.2.0: @@ -12014,8 +11723,8 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true - /scheduler@0.23.0: - resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} + /scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} dependencies: loose-envify: 1.4.0 @@ -12175,6 +11884,15 @@ packages: engines: {node: '>=14'} dev: true + /sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.25 + mrmime: 2.0.0 + totalist: 3.0.1 + dev: true + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true @@ -12193,7 +11911,7 @@ packages: resolution: {integrity: sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==} engines: {node: '>=10.0.0'} dependencies: - '@socket.io/component-emitter': 3.1.0 + '@socket.io/component-emitter': 3.1.2 debug: 4.3.4 engine.io-client: 6.5.3 socket.io-parser: 4.2.4 @@ -12207,7 +11925,7 @@ packages: resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} engines: {node: '>=10.0.0'} dependencies: - '@socket.io/component-emitter': 3.1.0 + '@socket.io/component-emitter': 3.1.2 debug: 4.3.4 transitivePeerDependencies: - supports-color @@ -12279,23 +11997,10 @@ packages: engines: {node: '>=12'} dev: false - /split2@3.2.2: - resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} - dependencies: - readable-stream: 3.6.2 - dev: true - /sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} dev: true - /ssri@10.0.5: - resolution: {integrity: sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - minipass: 7.0.4 - dev: true - /stack-generator@2.0.10: resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} dependencies: @@ -12345,11 +12050,11 @@ packages: resolution: {integrity: sha512-4QcZ+yx7nzEFiV4BMLnr/pRa5HYzNITX2ri0Zh6sT9EyQHbBHacC6YigllUPU9X3D0f/22QCgfokpKs52YRrUg==} dev: true - /storybook@8.0.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-FUr3Uc2dSAQ80jINH5fSXz7zD7Ncn08OthROjwRtHAH+jMf4wxyZ+RhF3heFy9xLot2/HXOLIWyHyzZZMtGhxg==} + /storybook@8.0.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-9/4oxISopLyr5xz7Du27mmQgcIfB7UTLlNzkK4IklWTiSgsOgYgZpsmIwymoXNtkrvh+QsqskdcUP1C7nNiEtw==} hasBin: true dependencies: - '@storybook/cli': 8.0.4(react-dom@18.2.0)(react@18.2.0) + '@storybook/cli': 8.0.10(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@babel/preset-env' - bufferutil @@ -12397,7 +12102,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-errors: 1.3.0 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 @@ -12415,7 +12120,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.23.2 + es-abstract: 1.23.3 es-object-atoms: 1.0.0 dev: true @@ -12467,11 +12172,6 @@ packages: engines: {node: '>=4'} dev: true - /strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - dev: true - /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -12506,18 +12206,18 @@ packages: engines: {node: '>=14.16'} dev: true - /strip-literal@2.0.0: - resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==} + /strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} dependencies: - js-tokens: 8.0.3 + js-tokens: 9.0.0 dev: true /stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} dev: false - /stylis@4.3.1: - resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==} + /stylis@4.3.2: + resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} dev: false /summary@2.1.0: @@ -12618,6 +12318,15 @@ packages: unique-string: 2.0.0 dev: true + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + /text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true @@ -12634,21 +12343,15 @@ packages: xtend: 4.0.2 dev: true - /through2@4.0.2: - resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} - dependencies: - readable-stream: 3.6.2 - dev: true - /tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - /tinybench@2.6.0: - resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} + /tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} dev: true - /tinypool@0.8.3: - resolution: {integrity: sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==} + /tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} engines: {node: '>=14.0.0'} dev: true @@ -12684,8 +12387,8 @@ packages: to-no-case: 1.0.2 dev: true - /tocbot@4.25.0: - resolution: {integrity: sha512-kE5wyCQJ40hqUaRVkyQ4z5+4juzYsv/eK+aqD97N62YH0TxFhzJvo22RUQQZdO3YnXAk42ZOfOpjVdy+Z0YokA==} + /tocbot@4.27.19: + resolution: {integrity: sha512-0yu8k0L3gCQ1OVNZnKqpbZp+kLd6qtlNEBxsb+e0G/bS0EXMl2tWqWi1Oy9knRX8rTPYfOxd/sI/OzAj3JowGg==} dev: true /toggle-selection@1.0.6: @@ -12697,6 +12400,11 @@ packages: engines: {node: '>=0.6'} dev: true + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -12705,13 +12413,13 @@ packages: hasBin: true dev: true - /ts-api-utils@1.3.0(typescript@5.4.3): + /ts-api-utils@1.3.0(typescript@5.4.5): resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' dependencies: - typescript: 5.4.3 + typescript: 5.4.5 dev: true /ts-dedent@2.2.0: @@ -12735,7 +12443,7 @@ packages: resolution: {integrity: sha512-gzkapsdbMNwBnTIjgO758GujLCj031IgHK/PKr2mrmkCSJMhSOR5FeOuSxKLMUoYc0vAA4RGEYYbjt/v6afD3g==} dev: true - /tsconfck@3.0.3(typescript@5.4.3): + /tsconfck@3.0.3(typescript@5.4.5): resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==} engines: {node: ^18 || >=20} hasBin: true @@ -12745,7 +12453,7 @@ packages: typescript: optional: true dependencies: - typescript: 5.4.3 + typescript: 5.4.5 dev: true /tsconfig-paths@3.15.0: @@ -12777,14 +12485,14 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - /tsutils@3.21.0(typescript@5.4.3): + /tsutils@3.21.0(typescript@5.4.5): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 5.4.3 + typescript: 5.4.5 dev: true /type-check@0.4.0: @@ -12881,8 +12589,8 @@ packages: hasBin: true dev: true - /typescript@5.4.3: - resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} + /typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true dev: true @@ -12912,8 +12620,8 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: true - /undici@5.28.3: - resolution: {integrity: sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==} + /undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} dependencies: '@fastify/busboy': 2.1.1 @@ -12991,8 +12699,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /unplugin@1.10.0: - resolution: {integrity: sha512-CuZtvvO8ua2Wl+9q2jEaqH6m3DoQ38N7pvBYQbbaeNlWGvK2l6GHiKi29aIHDPoSxdUzQ7Unevf1/ugil5X6Pg==} + /unplugin@1.10.1: + resolution: {integrity: sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg==} engines: {node: '>=14.0.0'} dependencies: acorn: 8.11.3 @@ -13006,8 +12714,8 @@ packages: engines: {node: '>=8'} dev: true - /update-browserslist-db@1.0.13(browserslist@4.23.0): - resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} + /update-browserslist-db@1.0.15(browserslist@4.23.0): + resolution: {integrity: sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -13023,7 +12731,7 @@ packages: punycode: 2.3.1 dev: true - /use-callback-ref@1.3.1(@types/react@18.2.73)(react@18.2.0): + /use-callback-ref@1.3.1(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==} engines: {node: '>=10'} peerDependencies: @@ -13033,39 +12741,39 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.73 - react: 18.2.0 + '@types/react': 18.3.1 + react: 18.3.1 tslib: 2.6.2 dev: false - /use-debounce@10.0.0(react@18.2.0): + /use-debounce@10.0.0(react@18.3.1): resolution: {integrity: sha512-XRjvlvCB46bah9IBXVnq/ACP2lxqXyZj0D9hj4K5OzNroMDpTEBg8Anuh1/UfRTRs7pLhQ+RiNxxwZu9+MVl1A==} engines: {node: '>= 16.0.0'} peerDependencies: react: '>=16.8.0' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /use-device-pixel-ratio@1.1.2(react@18.2.0): + /use-device-pixel-ratio@1.1.2(react@18.3.1): resolution: {integrity: sha512-nFxV0HwLdRUt20kvIgqHYZe6PK/v4mU1X8/eLsT1ti5ck0l2ob0HDRziaJPx+YWzBo6dMm4cTac3mcyk68Gh+A==} peerDependencies: react: '>=16.8.0' dependencies: - react: 18.2.0 + react: 18.3.1 dev: false - /use-image@1.1.1(react-dom@18.2.0)(react@18.2.0): + /use-image@1.1.1(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-n4YO2k8AJG/BcDtxmBx8Aa+47kxY5m335dJiCQA5tTeVU4XdhrhqR6wT0WISRXwdMEOv5CSjqekDZkEMiiWaYQ==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) dev: false - /use-isomorphic-layout-effect@1.1.2(@types/react@18.2.73)(react@18.2.0): + /use-isomorphic-layout-effect@1.1.2(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} peerDependencies: '@types/react': '*' @@ -13074,11 +12782,11 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.73 - react: 18.2.0 + '@types/react': 18.3.1 + react: 18.3.1 dev: false - /use-sidecar@1.1.2(@types/react@18.2.73)(react@18.2.0): + /use-sidecar@1.1.2(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} engines: {node: '>=10'} peerDependencies: @@ -13088,18 +12796,26 @@ packages: '@types/react': optional: true dependencies: - '@types/react': 18.2.73 + '@types/react': 18.3.1 detect-node-es: 1.1.0 - react: 18.2.0 + react: 18.3.1 tslib: 2.6.2 dev: false - /use-sync-external-store@1.2.0(react@18.2.0): + /use-sync-external-store@1.2.0(react@18.3.1): resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: - react: 18.2.0 + react: 18.3.1 + dev: false + + /use-sync-external-store@1.2.2(react@18.3.1): + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.3.1 dev: false /util-deprecate@1.0.2: @@ -13132,20 +12848,6 @@ packages: spdx-expression-parse: 3.0.1 dev: true - /validate-npm-package-name@4.0.0: - resolution: {integrity: sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - builtins: 5.0.1 - dev: true - - /validate-npm-package-name@5.0.0: - resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - builtins: 5.0.1 - dev: true - /validator@13.11.0: resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} engines: {node: '>= 0.10'} @@ -13156,15 +12858,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /version-selector-type@3.0.0: - resolution: {integrity: sha512-PSvMIZS7C1MuVNBXl/CDG2pZq8EXy/NW2dHIdm3bVP5N0PC8utDK8ttXLXj44Gn3J0lQE3U7Mpm1estAOd+eiA==} - engines: {node: '>=10.13'} - dependencies: - semver: 7.6.0 - dev: true - - /vite-node@1.4.0(@types/node@20.11.30): - resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} + /vite-node@1.6.0(@types/node@20.12.10): + resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: @@ -13172,7 +12867,7 @@ packages: debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.2.6(@types/node@20.11.30) + vite: 5.2.11(@types/node@20.12.10) transitivePeerDependencies: - '@types/node' - less @@ -13184,16 +12879,16 @@ packages: - terser dev: true - /vite-plugin-css-injected-by-js@3.5.0(vite@5.2.6): - resolution: {integrity: sha512-d0QaHH9kS93J25SwRqJNEfE29PSuQS5jn51y9N9i2Yoq0FRO7rjuTeLvjM5zwklZlRrIn6SUdtOEDKyHokgJZg==} + /vite-plugin-css-injected-by-js@3.5.1(vite@5.2.11): + resolution: {integrity: sha512-9ioqwDuEBxW55gNoWFEDhfLTrVKXEEZgl5adhWmmqa88EQGKfTmexy4v1Rh0pAS6RhKQs2bUYQArprB32JpUZQ==} peerDependencies: vite: '>2.0.0-0' dependencies: - vite: 5.2.6(@types/node@20.11.30) + vite: 5.2.11(@types/node@20.12.10) dev: true - /vite-plugin-dts@3.8.0(@types/node@20.11.30)(typescript@5.4.3)(vite@5.2.6): - resolution: {integrity: sha512-wt9ST1MwS5lkxHtA3M30+lSA3TO8RnaUu3YUPmGgY1iKm+vWZmB7KBss6qspyUlto9ynLNHYG2eJ09d2Q4/7Qg==} + /vite-plugin-dts@3.9.1(@types/node@20.12.10)(typescript@5.4.5)(vite@5.2.11): + resolution: {integrity: sha512-rVp2KM9Ue22NGWB8dNtWEr+KekN3rIgz1tWD050QnRGlriUCmaDwa7qA5zDEjbXg5lAXhYMSBJtx3q3hQIJZSg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: typescript: '*' @@ -13202,35 +12897,35 @@ packages: vite: optional: true dependencies: - '@microsoft/api-extractor': 7.43.0(@types/node@20.11.30) + '@microsoft/api-extractor': 7.43.0(@types/node@20.12.10) '@rollup/pluginutils': 5.1.0 - '@vue/language-core': 1.8.27(typescript@5.4.3) + '@vue/language-core': 1.8.27(typescript@5.4.5) debug: 4.3.4 kolorist: 1.8.0 - magic-string: 0.30.8 - typescript: 5.4.3 - vite: 5.2.6(@types/node@20.11.30) - vue-tsc: 1.8.27(typescript@5.4.3) + magic-string: 0.30.10 + typescript: 5.4.5 + vite: 5.2.11(@types/node@20.12.10) + vue-tsc: 1.8.27(typescript@5.4.5) transitivePeerDependencies: - '@types/node' - rollup - supports-color dev: true - /vite-plugin-eslint@1.8.1(eslint@8.57.0)(vite@5.2.6): + /vite-plugin-eslint@1.8.1(eslint@8.57.0)(vite@5.2.11): resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==} peerDependencies: eslint: '>=7' vite: '>=2' dependencies: '@rollup/pluginutils': 4.2.1 - '@types/eslint': 8.56.6 + '@types/eslint': 8.56.10 eslint: 8.57.0 rollup: 2.79.1 - vite: 5.2.6(@types/node@20.11.30) + vite: 5.2.11(@types/node@20.12.10) dev: true - /vite-tsconfig-paths@4.3.2(typescript@5.4.3)(vite@5.2.6): + /vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11): resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} peerDependencies: vite: '*' @@ -13240,15 +12935,15 @@ packages: dependencies: debug: 4.3.4 globrex: 0.1.2 - tsconfck: 3.0.3(typescript@5.4.3) - vite: 5.2.6(@types/node@20.11.30) + tsconfck: 3.0.3(typescript@5.4.5) + vite: 5.2.11(@types/node@20.12.10) transitivePeerDependencies: - supports-color - typescript dev: true - /vite@5.2.6(@types/node@20.11.30): - resolution: {integrity: sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==} + /vite@5.2.11(@types/node@20.12.10): + resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -13275,23 +12970,23 @@ packages: terser: optional: true dependencies: - '@types/node': 20.11.30 + '@types/node': 20.12.10 esbuild: 0.20.2 postcss: 8.4.38 - rollup: 4.13.1 + rollup: 4.17.2 optionalDependencies: fsevents: 2.3.3 dev: true - /vitest@1.4.0(@types/node@20.11.30): - resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + /vitest@1.6.0(@types/node@20.12.10)(@vitest/ui@1.6.0): + resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.4.0 - '@vitest/ui': 1.4.0 + '@vitest/browser': 1.6.0 + '@vitest/ui': 1.6.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -13308,26 +13003,27 @@ packages: jsdom: optional: true dependencies: - '@types/node': 20.11.30 - '@vitest/expect': 1.4.0 - '@vitest/runner': 1.4.0 - '@vitest/snapshot': 1.4.0 - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 + '@types/node': 20.12.10 + '@vitest/expect': 1.6.0 + '@vitest/runner': 1.6.0 + '@vitest/snapshot': 1.6.0 + '@vitest/spy': 1.6.0 + '@vitest/ui': 1.6.0(vitest@1.6.0) + '@vitest/utils': 1.6.0 acorn-walk: 8.3.2 chai: 4.4.1 debug: 4.3.4 execa: 8.0.1 local-pkg: 0.5.0 - magic-string: 0.30.8 + magic-string: 0.30.10 pathe: 1.1.2 picocolors: 1.0.0 std-env: 3.7.0 - strip-literal: 2.0.0 - tinybench: 2.6.0 - tinypool: 0.8.3 - vite: 5.2.6(@types/node@20.11.30) - vite-node: 1.4.0(@types/node@20.11.30) + strip-literal: 2.1.0 + tinybench: 2.8.0 + tinypool: 0.8.4 + vite: 5.2.11(@types/node@20.12.10) + vite-node: 1.6.0(@types/node@20.12.10) why-is-node-running: 2.2.2 transitivePeerDependencies: - less @@ -13355,16 +13051,16 @@ packages: he: 1.2.0 dev: true - /vue-tsc@1.8.27(typescript@5.4.3): + /vue-tsc@1.8.27(typescript@5.4.5): resolution: {integrity: sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==} hasBin: true peerDependencies: typescript: '*' dependencies: '@volar/typescript': 1.11.1 - '@vue/language-core': 1.8.27(typescript@5.4.3) + '@vue/language-core': 1.8.27(typescript@5.4.5) semver: 7.6.0 - typescript: 5.4.3 + typescript: 5.4.5 dev: true /watchpack@2.4.1: @@ -13456,14 +13152,6 @@ packages: isexe: 2.0.0 dev: true - /which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} - hasBin: true - dependencies: - isexe: 3.1.1 - dev: true - /why-is-node-running@2.2.2: resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} engines: {node: '>=8'} @@ -13473,6 +13161,11 @@ packages: stackback: 0.0.2 dev: true + /word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + dev: true + /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: true @@ -13520,8 +13213,8 @@ packages: optional: true dev: false - /ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + /ws@8.17.0: + resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -13601,18 +13294,18 @@ packages: commander: 9.5.0 dev: true - /zod-validation-error@3.0.3(zod@3.22.4): - resolution: {integrity: sha512-cETTrcMq3Ze58vhdR0zD37uJm/694I6mAxcf/ei5bl89cC++fBNxrC2z8lkFze/8hVMPwrbtrwXHR2LB50fpHw==} + /zod-validation-error@3.2.0(zod@3.23.6): + resolution: {integrity: sha512-cYlPR6zuyrgmu2wRTdumEAJGuwI7eHVHGT+VyneAQxmRAKtGRL1/7pjz4wfLhz4J05f5qoSZc3rGacswgyTjjw==} engines: {node: '>=18.0.0'} peerDependencies: zod: ^3.18.0 dependencies: - zod: 3.22.4 + zod: 3.23.6 - /zod@3.22.4: - resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} + /zod@3.23.6: + resolution: {integrity: sha512-RTHJlZhsRbuA8Hmp/iNL7jnfc4nZishjsanDAfEY1QpDQZCahUp3xDzl+zfweE9BklxMUcgBgS1b7Lvie/ZVwA==} - /zustand@4.5.2(@types/react@18.2.73)(react@18.2.0): + /zustand@4.5.2(@types/react@18.3.1)(react@18.3.1): resolution: {integrity: sha512-2cN1tPkDVkwCy5ickKrI7vijSjPksFRfqS6237NzT0vqSsztTNnQdHw9mmN7uBdk3gceVXU0a+21jFzFzAc9+g==} engines: {node: '>=12.7.0'} peerDependencies: @@ -13627,7 +13320,7 @@ packages: react: optional: true dependencies: - '@types/react': 18.2.73 - react: 18.2.0 - use-sync-external-store: 1.2.0(react@18.2.0) + '@types/react': 18.3.1 + react: 18.3.1 + use-sync-external-store: 1.2.0(react@18.3.1) dev: false diff --git a/invokeai/frontend/web/public/locales/de.json b/invokeai/frontend/web/public/locales/de.json index 0a104c083b..1db283aabd 100644 --- a/invokeai/frontend/web/public/locales/de.json +++ b/invokeai/frontend/web/public/locales/de.json @@ -76,7 +76,9 @@ "aboutHeading": "Nutzen Sie Ihre kreative Energie", "toResolve": "Lösen", "add": "Hinzufügen", - "loglevel": "Protokoll Stufe" + "loglevel": "Protokoll Stufe", + "selected": "Ausgewählt", + "beta": "Beta" }, "gallery": { "galleryImageSize": "Bildgröße", @@ -86,7 +88,7 @@ "noImagesInGallery": "Keine Bilder in der Galerie", "loading": "Lade", "deleteImage_one": "Lösche Bild", - "deleteImage_other": "", + "deleteImage_other": "Lösche {{count}} Bilder", "copy": "Kopieren", "download": "Runterladen", "setCurrentImage": "Setze aktuelle Bild", @@ -397,7 +399,14 @@ "cancel": "Stornieren", "defaultSettingsSaved": "Standardeinstellungen gespeichert", "addModels": "Model hinzufügen", - "deleteModelImage": "Lösche Model Bild" + "deleteModelImage": "Lösche Model Bild", + "hfTokenInvalidErrorMessage": "Falscher oder fehlender HuggingFace Schlüssel.", + "huggingFaceRepoID": "HuggingFace Repo ID", + "hfToken": "HuggingFace Schlüssel", + "hfTokenInvalid": "Falscher oder fehlender HF Schlüssel", + "huggingFacePlaceholder": "besitzer/model-name", + "hfTokenSaved": "HF Schlüssel gespeichert", + "hfTokenUnableToVerify": "Konnte den HF Schlüssel nicht validieren" }, "parameters": { "images": "Bilder", @@ -686,7 +695,11 @@ "hands": "Hände", "dwOpenpose": "DW Openpose", "dwOpenposeDescription": "Posenschätzung mit DW Openpose", - "selectCLIPVisionModel": "Wähle ein CLIP Vision Model aus" + "selectCLIPVisionModel": "Wähle ein CLIP Vision Model aus", + "ipAdapterMethod": "Methode", + "composition": "Nur Komposition", + "full": "Voll", + "style": "Nur Style" }, "queue": { "status": "Status", @@ -717,7 +730,6 @@ "resume": "Wieder aufnehmen", "item": "Auftrag", "notReady": "Warteschlange noch nicht bereit", - "queueCountPrediction": "{{promptsCount}} Prompts × {{iterations}} Iterationen -> {{count}} Generationen", "clearQueueAlertDialog": "\"Die Warteschlange leeren\" stoppt den aktuellen Prozess und leert die Warteschlange komplett.", "completedIn": "Fertig in", "cancelBatchSucceeded": "Stapel abgebrochen", diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 37a2a7a5da..7de7a8e01c 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -142,8 +142,11 @@ "blue": "Blue", "alpha": "Alpha", "selected": "Selected", - "viewer": "Viewer", - "tab": "Tab" + "tab": "Tab", + "viewing": "Viewing", + "viewingDesc": "Review images in a large gallery view", + "editing": "Editing", + "editingDesc": "Edit on the Control Layers canvas" }, "controlnet": { "controlAdapter_one": "Control Adapter", @@ -258,7 +261,6 @@ "queue": "Queue", "queueFront": "Add to Front of Queue", "queueBack": "Add to Queue", - "queueCountPrediction": "{{promptsCount}} prompts \u00d7 {{iterations}} iterations -> {{count}} generations", "queueEmpty": "Queue Empty", "enqueueing": "Queueing Batch", "resume": "Resume", @@ -311,7 +313,13 @@ "batchFailedToQueue": "Failed to Queue Batch", "graphQueued": "Graph queued", "graphFailedToQueue": "Failed to queue graph", - "openQueue": "Open Queue" + "openQueue": "Open Queue", + "prompts_one": "Prompt", + "prompts_other": "Prompts", + "iterations_one": "Iteration", + "iterations_other": "Iterations", + "generations_one": "Generation", + "generations_other": "Generations" }, "invocationCache": { "invocationCache": "Invocation Cache", @@ -364,8 +372,7 @@ "bulkDownloadRequestFailed": "Problem Preparing Download", "bulkDownloadFailed": "Download Failed", "problemDeletingImages": "Problem Deleting Images", - "problemDeletingImagesDesc": "One or more images could not be deleted", - "switchTo": "Switch to {{ tab }} (Z)" + "problemDeletingImagesDesc": "One or more images could not be deleted" }, "hotkeys": { "searchHotkeys": "Search Hotkeys", @@ -589,13 +596,9 @@ "desc": "Upscale the current image", "title": "Upscale" }, - "backToEditor": { - "desc": "Closes the Image Viewer and shows the Editor View (Text to Image tab only)", - "title": "Back to Editor" - }, - "openImageViewer": { - "desc": "Opens the Image Viewer (Text to Image tab only)", - "title": "Open Image Viewer" + "toggleViewer": { + "desc": "Switches between the Image Viewer and workspace for the current tab.", + "title": "Toggle Image Viewer" } }, "metadata": { @@ -771,6 +774,7 @@ "cannotConnectOutputToOutput": "Cannot connect output to output", "cannotConnectToSelf": "Cannot connect to self", "cannotDuplicateConnection": "Cannot create duplicate connections", + "cannotMixAndMatchCollectionItemTypes": "Cannot mix and match collection item types", "nodePack": "Node pack", "collection": "Collection", "collectionFieldType": "{{name}} Collection", @@ -876,6 +880,7 @@ "versionUnknown": " Version Unknown", "workflow": "Workflow", "graph": "Graph", + "noGraph": "No Graph", "workflowAuthor": "Author", "workflowContact": "Contact", "workflowDescription": "Short Description", @@ -936,17 +941,30 @@ "noModelSelected": "No model selected", "noPrompts": "No prompts generated", "noNodesInGraph": "No nodes in graph", - "systemDisconnected": "System disconnected" + "systemDisconnected": "System disconnected", + "layer": { + "initialImageNoImageSelected": "no initial image selected", + "controlAdapterNoModelSelected": "no Control Adapter model selected", + "controlAdapterIncompatibleBaseModel": "incompatible Control Adapter base model", + "controlAdapterNoImageSelected": "no Control Adapter image selected", + "controlAdapterImageNotProcessed": "Control Adapter image not processed", + "t2iAdapterIncompatibleDimensions": "T2I Adapter requires image dimension to be multiples of 64", + "ipAdapterNoModelSelected": "no IP adapter selected", + "ipAdapterIncompatibleBaseModel": "incompatible IP Adapter base model", + "ipAdapterNoImageSelected": "no IP Adapter image selected", + "rgNoPromptsOrIPAdapters": "no text prompts or IP Adapters", + "rgNoRegion": "no region selected" + } }, "maskBlur": "Mask Blur", "negativePromptPlaceholder": "Negative Prompt", + "globalNegativePromptPlaceholder": "Global Negative Prompt", "noiseThreshold": "Noise Threshold", "patchmatchDownScaleSize": "Downscale", "perlinNoise": "Perlin Noise", "positivePromptPlaceholder": "Positive Prompt", + "globalPositivePromptPlaceholder": "Global Positive Prompt", "iterations": "Iterations", - "iterationsWithCount_one": "{{count}} Iteration", - "iterationsWithCount_other": "{{count}} Iterations", "scale": "Scale", "scaleBeforeProcessing": "Scale Before Processing", "scaledHeight": "Scaled H", @@ -1548,8 +1566,6 @@ "addIPAdapter": "Add $t(common.ipAdapter)", "regionalGuidance": "Regional Guidance", "regionalGuidanceLayer": "$t(controlLayers.regionalGuidance) $t(unifiedCanvas.layer)", - "controlNetLayer": "$t(common.controlNet) $t(unifiedCanvas.layer)", - "ipAdapterLayer": "$t(common.ipAdapter) $t(unifiedCanvas.layer)", "opacity": "Opacity", "globalControlAdapter": "Global $t(controlnet.controlAdapter_one)", "globalControlAdapterLayer": "Global $t(controlnet.controlAdapter_one) $t(unifiedCanvas.layer)", @@ -1560,7 +1576,9 @@ "opacityFilter": "Opacity Filter", "clearProcessor": "Clear Processor", "resetProcessor": "Reset Processor to Defaults", - "noLayersAdded": "No Layers Added" + "noLayersAdded": "No Layers Added", + "layers_one": "Layer", + "layers_other": "Layers" }, "ui": { "tabs": { diff --git a/invokeai/frontend/web/public/locales/es.json b/invokeai/frontend/web/public/locales/es.json index 6b410cd0bf..dbdda8e209 100644 --- a/invokeai/frontend/web/public/locales/es.json +++ b/invokeai/frontend/web/public/locales/es.json @@ -25,7 +25,24 @@ "areYouSure": "¿Estas seguro?", "batch": "Administrador de lotes", "modelManager": "Administrador de modelos", - "communityLabel": "Comunidad" + "communityLabel": "Comunidad", + "direction": "Dirección", + "ai": "Ia", + "add": "Añadir", + "auto": "Automático", + "copyError": "Error $t(gallery.copy)", + "details": "Detalles", + "or": "o", + "checkpoint": "Punto de control", + "controlNet": "ControlNet", + "aboutHeading": "Sea dueño de su poder creativo", + "advanced": "Avanzado", + "data": "Fecha", + "delete": "Borrar", + "copy": "Copiar", + "beta": "Beta", + "on": "En", + "aboutDesc": "¿Utilizas Invoke para trabajar? Mira aquí:" }, "gallery": { "galleryImageSize": "Tamaño de la imagen", @@ -443,7 +460,13 @@ "previousImage": "Imagen anterior", "nextImage": "Siguiente imagen", "showOptionsPanel": "Mostrar el panel lateral", - "menu": "Menú" + "menu": "Menú", + "showGalleryPanel": "Mostrar panel de galería", + "loadMore": "Cargar más", + "about": "Acerca de", + "createIssue": "Crear un problema", + "resetUI": "Interfaz de usuario $t(accessibility.reset)", + "mode": "Modo" }, "nodes": { "zoomInNodes": "Acercar", @@ -456,5 +479,68 @@ "reloadNodeTemplates": "Recargar las plantillas de nodos", "loadWorkflow": "Cargar el flujo de trabajo", "downloadWorkflow": "Descargar el flujo de trabajo en un archivo JSON" + }, + "boards": { + "autoAddBoard": "Agregar panel automáticamente", + "changeBoard": "Cambiar el panel", + "clearSearch": "Borrar la búsqueda", + "deleteBoard": "Borrar el panel", + "selectBoard": "Seleccionar un panel", + "uncategorized": "Sin categoría", + "cancel": "Cancelar", + "addBoard": "Agregar un panel", + "movingImagesToBoard_one": "Moviendo {{count}} imagen al panel:", + "movingImagesToBoard_many": "Moviendo {{count}} imágenes al panel:", + "movingImagesToBoard_other": "Moviendo {{count}} imágenes al panel:", + "bottomMessage": "Al eliminar este panel y las imágenes que contiene, se restablecerán las funciones que los estén utilizando actualmente.", + "deleteBoardAndImages": "Borrar el panel y las imágenes", + "loading": "Cargando...", + "deletedBoardsCannotbeRestored": "Los paneles eliminados no se pueden restaurar", + "move": "Mover", + "menuItemAutoAdd": "Agregar automáticamente a este panel", + "searchBoard": "Buscando paneles…", + "topMessage": "Este panel contiene imágenes utilizadas en las siguientes funciones:", + "downloadBoard": "Descargar panel", + "deleteBoardOnly": "Borrar solo el panel", + "myBoard": "Mi panel", + "noMatching": "No hay paneles que coincidan" + }, + "accordions": { + "compositing": { + "title": "Composición", + "infillTab": "Relleno" + }, + "generation": { + "title": "Generación" + }, + "image": { + "title": "Imagen" + }, + "control": { + "title": "Control" + }, + "advanced": { + "options": "$t(accordions.advanced.title) opciones", + "title": "Avanzado" + } + }, + "ui": { + "tabs": { + "generationTab": "$t(ui.tabs.generation) $t(common.tab)", + "canvas": "Lienzo", + "generation": "Generación", + "queue": "Cola", + "queueTab": "$t(ui.tabs.queue) $t(common.tab)", + "workflows": "Flujos de trabajo", + "models": "Modelos", + "modelsTab": "$t(ui.tabs.models) $t(common.tab)", + "canvasTab": "$t(ui.tabs.canvas) $t(common.tab)", + "workflowsTab": "$t(ui.tabs.workflows) $t(common.tab)" + } + }, + "controlLayers": { + "layers_one": "Capa", + "layers_many": "Capas", + "layers_other": "Capas" } } diff --git a/invokeai/frontend/web/public/locales/it.json b/invokeai/frontend/web/public/locales/it.json index 491b31907b..f365b43e10 100644 --- a/invokeai/frontend/web/public/locales/it.json +++ b/invokeai/frontend/web/public/locales/it.json @@ -5,7 +5,7 @@ "reportBugLabel": "Segnala un errore", "settingsLabel": "Impostazioni", "img2img": "Immagine a Immagine", - "unifiedCanvas": "Tela unificata", + "unifiedCanvas": "Tela", "nodes": "Flussi di lavoro", "upload": "Caricamento", "load": "Carica", @@ -74,7 +74,18 @@ "file": "File", "toResolve": "Da risolvere", "add": "Aggiungi", - "loglevel": "Livello di log" + "loglevel": "Livello di log", + "beta": "Beta", + "positivePrompt": "Prompt positivo", + "negativePrompt": "Prompt negativo", + "selected": "Selezionato", + "goTo": "Vai a", + "editor": "Editor", + "tab": "Scheda", + "viewing": "Visualizza", + "viewingDesc": "Rivedi le immagini in un'ampia vista della galleria", + "editing": "Modifica", + "editingDesc": "Modifica nell'area Livelli di controllo" }, "gallery": { "galleryImageSize": "Dimensione dell'immagine", @@ -180,8 +191,8 @@ "desc": "Mostra le informazioni sui metadati dell'immagine corrente" }, "sendToImageToImage": { - "title": "Invia a Immagine a Immagine", - "desc": "Invia l'immagine corrente a da Immagine a Immagine" + "title": "Invia a Generazione da immagine", + "desc": "Invia l'immagine corrente a Generazione da immagine" }, "deleteImage": { "title": "Elimina immagine", @@ -334,6 +345,10 @@ "remixImage": { "desc": "Utilizza tutti i parametri tranne il seme dell'immagine corrente", "title": "Remixa l'immagine" + }, + "toggleViewer": { + "title": "Attiva/disattiva il visualizzatore di immagini", + "desc": "Passa dal Visualizzatore immagini all'area di lavoro per la scheda corrente." } }, "modelManager": { @@ -471,8 +486,8 @@ "scaledHeight": "Altezza ridimensionata", "infillMethod": "Metodo di riempimento", "tileSize": "Dimensione piastrella", - "sendToImg2Img": "Invia a Immagine a Immagine", - "sendToUnifiedCanvas": "Invia a Tela Unificata", + "sendToImg2Img": "Invia a Generazione da immagine", + "sendToUnifiedCanvas": "Invia alla Tela", "downloadImage": "Scarica l'immagine", "usePrompt": "Usa Prompt", "useSeed": "Usa Seme", @@ -508,13 +523,11 @@ "incompatibleBaseModelForControlAdapter": "Il modello dell'adattatore di controllo #{{number}} non è compatibile con il modello principale.", "missingNodeTemplate": "Modello di nodo mancante", "missingInputForField": "{{nodeLabel}} -> {{fieldLabel}} ingresso mancante", - "missingFieldTemplate": "Modello di campo mancante" + "missingFieldTemplate": "Modello di campo mancante", + "imageNotProcessedForControlAdapter": "L'immagine dell'adattatore di controllo #{{number}} non è stata elaborata" }, "useCpuNoise": "Usa la CPU per generare rumore", "iterations": "Iterazioni", - "iterationsWithCount_one": "{{count}} Iterazione", - "iterationsWithCount_many": "{{count}} Iterazioni", - "iterationsWithCount_other": "{{count}} Iterazioni", "isAllowedToUpscale": { "useX2Model": "L'immagine è troppo grande per l'ampliamento con il modello x4, utilizza il modello x2", "tooLarge": "L'immagine è troppo grande per l'ampliamento, seleziona un'immagine più piccola" @@ -534,7 +547,10 @@ "infillMosaicMinColor": "Colore minimo", "infillMosaicMaxColor": "Colore massimo", "infillMosaicTileHeight": "Altezza piastrella", - "infillColorValue": "Colore di riempimento" + "infillColorValue": "Colore di riempimento", + "globalSettings": "Impostazioni globali", + "globalPositivePromptPlaceholder": "Prompt positivo globale", + "globalNegativePromptPlaceholder": "Prompt negativo globale" }, "settings": { "models": "Modelli", @@ -559,7 +575,7 @@ "intermediatesCleared_one": "Cancellata {{count}} immagine intermedia", "intermediatesCleared_many": "Cancellate {{count}} immagini intermedie", "intermediatesCleared_other": "Cancellate {{count}} immagini intermedie", - "clearIntermediatesDesc1": "La cancellazione delle immagini intermedie ripristinerà lo stato di Tela Unificata e ControlNet.", + "clearIntermediatesDesc1": "La cancellazione delle immagini intermedie ripristinerà lo stato della Tela e degli Adattatori di Controllo.", "intermediatesClearedFailed": "Problema con la cancellazione delle immagini intermedie", "clearIntermediatesWithCount_one": "Cancella {{count}} immagine intermedia", "clearIntermediatesWithCount_many": "Cancella {{count}} immagini intermedie", @@ -575,8 +591,8 @@ "imageCopied": "Immagine copiata", "imageNotLoadedDesc": "Impossibile trovare l'immagine", "canvasMerged": "Tela unita", - "sentToImageToImage": "Inviato a Immagine a Immagine", - "sentToUnifiedCanvas": "Inviato a Tela Unificata", + "sentToImageToImage": "Inviato a Generazione da immagine", + "sentToUnifiedCanvas": "Inviato alla Tela", "parametersNotSet": "Parametri non impostati", "metadataLoadFailed": "Impossibile caricare i metadati", "serverError": "Errore del Server", @@ -795,7 +811,7 @@ "float": "In virgola mobile", "currentImageDescription": "Visualizza l'immagine corrente nell'editor dei nodi", "fieldTypesMustMatch": "I tipi di campo devono corrispondere", - "edge": "Bordo", + "edge": "Collegamento", "currentImage": "Immagine corrente", "integer": "Numero Intero", "inputMayOnlyHaveOneConnection": "L'ingresso può avere solo una connessione", @@ -845,7 +861,9 @@ "resetToDefaultValue": "Ripristina il valore predefinito", "noFieldsViewMode": "Questo flusso di lavoro non ha campi selezionati da visualizzare. Visualizza il flusso di lavoro completo per configurare i valori.", "edit": "Modifica", - "graph": "Grafico" + "graph": "Grafico", + "showEdgeLabelsHelp": "Mostra etichette sui collegamenti, che indicano i nodi collegati", + "showEdgeLabels": "Mostra le etichette del collegamento" }, "boards": { "autoAddBoard": "Aggiungi automaticamente bacheca", @@ -922,7 +940,7 @@ "colorMapTileSize": "Dimensione piastrella", "mediapipeFaceDescription": "Rilevamento dei volti tramite Mediapipe", "hedDescription": "Rilevamento dei bordi nidificati olisticamente", - "setControlImageDimensions": "Imposta le dimensioni dell'immagine di controllo su L/A", + "setControlImageDimensions": "Copia le dimensioni in L/A (ottimizza per il modello)", "maxFaces": "Numero massimo di volti", "addT2IAdapter": "Aggiungi $t(common.t2iAdapter)", "addControlNet": "Aggiungi $t(common.controlNet)", @@ -951,12 +969,17 @@ "mediapipeFace": "Mediapipe Volto", "ip_adapter": "$t(controlnet.controlAdapter_one) #{{number}} ($t(common.ipAdapter))", "t2i_adapter": "$t(controlnet.controlAdapter_one) #{{number}} ($t(common.t2iAdapter))", - "selectCLIPVisionModel": "Seleziona un modello CLIP Vision" + "selectCLIPVisionModel": "Seleziona un modello CLIP Vision", + "ipAdapterMethod": "Metodo", + "full": "Completo", + "composition": "Solo la composizione", + "style": "Solo lo stile", + "beginEndStepPercentShort": "Inizio/Fine %", + "setControlImageDimensionsForce": "Copia le dimensioni in L/A (ignora il modello)" }, "queue": { "queueFront": "Aggiungi all'inizio della coda", "queueBack": "Aggiungi alla coda", - "queueCountPrediction": "{{promptsCount}} prompt × {{iterations}} iterazioni -> {{count}} generazioni", "queue": "Coda", "status": "Stato", "pruneSucceeded": "Rimossi {{item_count}} elementi completati dalla coda", @@ -993,7 +1016,7 @@ "cancelBatchSucceeded": "Lotto annullato", "clearTooltip": "Annulla e cancella tutti gli elementi", "current": "Attuale", - "pauseTooltip": "Sospende l'elaborazione", + "pauseTooltip": "Sospendi l'elaborazione", "failed": "Falliti", "cancelItem": "Annulla l'elemento", "next": "Prossimo", @@ -1394,6 +1417,12 @@ "paragraphs": [ "La dimensione del bordo del passaggio di coerenza." ] + }, + "ipAdapterMethod": { + "heading": "Metodo", + "paragraphs": [ + "Metodo con cui applicare l'adattatore IP corrente." + ] } }, "sdxl": { @@ -1522,5 +1551,56 @@ "compatibleEmbeddings": "Incorporamenti compatibili", "addPromptTrigger": "Aggiungi Trigger nel prompt", "noMatchingTriggers": "Nessun Trigger corrispondente" + }, + "controlLayers": { + "opacityFilter": "Filtro opacità", + "deleteAll": "Cancella tutto", + "addLayer": "Aggiungi Livello", + "moveToFront": "Sposta in primo piano", + "moveToBack": "Sposta in fondo", + "moveForward": "Sposta avanti", + "moveBackward": "Sposta indietro", + "brushSize": "Dimensioni del pennello", + "globalMaskOpacity": "Opacità globale della maschera", + "autoNegative": "Auto Negativo", + "toggleVisibility": "Attiva/disattiva la visibilità dei livelli", + "deletePrompt": "Cancella il prompt", + "debugLayers": "Debug dei Livelli", + "rectangle": "Rettangolo", + "maskPreviewColor": "Colore anteprima maschera", + "addPositivePrompt": "Aggiungi $t(common.positivePrompt)", + "addNegativePrompt": "Aggiungi $t(common.negativePrompt)", + "addIPAdapter": "Aggiungi $t(common.ipAdapter)", + "regionalGuidance": "Guida regionale", + "regionalGuidanceLayer": "$t(unifiedCanvas.layer) $t(controlLayers.regionalGuidance)", + "opacity": "Opacità", + "globalControlAdapter": "$t(controlnet.controlAdapter_one) Globale", + "globalControlAdapterLayer": "$t(controlnet.controlAdapter_one) - $t(unifiedCanvas.layer) Globale", + "globalIPAdapter": "$t(common.ipAdapter) Globale", + "globalIPAdapterLayer": "$t(common.ipAdapter) - $t(unifiedCanvas.layer) Globale", + "globalInitialImage": "Immagine iniziale", + "globalInitialImageLayer": "$t(controlLayers.globalInitialImage) - $t(unifiedCanvas.layer) Globale", + "clearProcessor": "Cancella processore", + "resetProcessor": "Ripristina il processore alle impostazioni predefinite", + "noLayersAdded": "Nessun livello aggiunto", + "resetRegion": "Reimposta la regione", + "controlLayers": "Livelli di controllo", + "layers_one": "Livello", + "layers_many": "Livelli", + "layers_other": "Livelli" + }, + "ui": { + "tabs": { + "generation": "Generazione", + "generationTab": "$t(ui.tabs.generation) $t(common.tab)", + "canvas": "Tela", + "canvasTab": "$t(ui.tabs.canvas) $t(common.tab)", + "workflows": "Flussi di lavoro", + "workflowsTab": "$t(ui.tabs.workflows) $t(common.tab)", + "models": "Modelli", + "modelsTab": "$t(ui.tabs.models) $t(common.tab)", + "queue": "Coda", + "queueTab": "$t(ui.tabs.queue) $t(common.tab)" + } } } diff --git a/invokeai/frontend/web/public/locales/ja.json b/invokeai/frontend/web/public/locales/ja.json index 264593153a..e953944c44 100644 --- a/invokeai/frontend/web/public/locales/ja.json +++ b/invokeai/frontend/web/public/locales/ja.json @@ -570,7 +570,6 @@ "pauseSucceeded": "処理が一時停止されました", "queueFront": "キューの先頭へ追加", "queueBack": "キューに追加", - "queueCountPrediction": "{{promptsCount}} プロンプト × {{iterations}} イテレーション -> {{count}} 枚生成", "pause": "一時停止", "queue": "キュー", "pauseTooltip": "処理を一時停止", diff --git a/invokeai/frontend/web/public/locales/ko.json b/invokeai/frontend/web/public/locales/ko.json index 1c02d86105..db9cd0ca67 100644 --- a/invokeai/frontend/web/public/locales/ko.json +++ b/invokeai/frontend/web/public/locales/ko.json @@ -505,7 +505,6 @@ "completed": "완성된", "queueBack": "Queue에 추가", "cancelFailed": "항목 취소 중 발생한 문제", - "queueCountPrediction": "Queue에 {{predicted}} 추가", "batchQueued": "Batch Queued", "pauseFailed": "프로세서 중지 중 발생한 문제", "clearFailed": "Queue 제거 중 발생한 문제", diff --git a/invokeai/frontend/web/public/locales/nl.json b/invokeai/frontend/web/public/locales/nl.json index 29ceb3227b..76377bd215 100644 --- a/invokeai/frontend/web/public/locales/nl.json +++ b/invokeai/frontend/web/public/locales/nl.json @@ -383,8 +383,6 @@ "useCpuNoise": "Gebruik CPU-ruis", "imageActions": "Afbeeldingshandeling", "iterations": "Iteraties", - "iterationsWithCount_one": "{{count}} iteratie", - "iterationsWithCount_other": "{{count}} iteraties", "coherenceMode": "Modus" }, "settings": { @@ -940,7 +938,6 @@ "completed": "Voltooid", "queueBack": "Voeg toe aan wachtrij", "cancelFailed": "Fout bij annuleren onderdeel", - "queueCountPrediction": "Voeg {{predicted}} toe aan wachtrij", "batchQueued": "Reeks in wachtrij geplaatst", "pauseFailed": "Fout bij onderbreken verwerker", "clearFailed": "Fout bij wissen van wachtrij", diff --git a/invokeai/frontend/web/public/locales/ru.json b/invokeai/frontend/web/public/locales/ru.json index f254b7faa5..7fa1b73e7a 100644 --- a/invokeai/frontend/web/public/locales/ru.json +++ b/invokeai/frontend/web/public/locales/ru.json @@ -76,7 +76,18 @@ "localSystem": "Локальная система", "aboutDesc": "Используя Invoke для работы? Проверьте это:", "add": "Добавить", - "loglevel": "Уровень логов" + "loglevel": "Уровень логов", + "beta": "Бета", + "selected": "Выбрано", + "positivePrompt": "Позитивный запрос", + "negativePrompt": "Негативный запрос", + "editor": "Редактор", + "goTo": "Перейти к", + "tab": "Вкладка", + "viewing": "Просмотр", + "editing": "Редактирование", + "viewingDesc": "Просмотр изображений в режиме большой галереи", + "editingDesc": "Редактировать на холсте слоёв управления" }, "gallery": { "galleryImageSize": "Размер изображений", @@ -87,8 +98,8 @@ "deleteImagePermanent": "Удаленные изображения невозможно восстановить.", "deleteImageBin": "Удаленные изображения будут отправлены в корзину вашей операционной системы.", "deleteImage_one": "Удалить изображение", - "deleteImage_few": "", - "deleteImage_many": "", + "deleteImage_few": "Удалить {{count}} изображения", + "deleteImage_many": "Удалить {{count}} изображений", "assets": "Ресурсы", "autoAssignBoardOnClick": "Авто-назначение доски по клику", "deleteSelection": "Удалить выделенное", @@ -336,6 +347,10 @@ "remixImage": { "desc": "Используйте все параметры, кроме сида из текущего изображения", "title": "Ремикс изображения" + }, + "toggleViewer": { + "title": "Переключить просмотр изображений", + "desc": "Переключение между средством просмотра изображений и рабочей областью для текущей вкладки." } }, "modelManager": { @@ -512,7 +527,8 @@ "missingNodeTemplate": "Отсутствует шаблон узла", "missingFieldTemplate": "Отсутствует шаблон поля", "addingImagesTo": "Добавление изображений в", - "invoke": "Создать" + "invoke": "Создать", + "imageNotProcessedForControlAdapter": "Изображение адаптера контроля №{{number}} не обрабатывается" }, "isAllowedToUpscale": { "useX2Model": "Изображение слишком велико для увеличения с помощью модели x4. Используйте модель x2", @@ -523,9 +539,6 @@ "useCpuNoise": "Использовать шум CPU", "imageActions": "Действия с изображениями", "iterations": "Кол-во", - "iterationsWithCount_one": "{{count}} Интеграция", - "iterationsWithCount_few": "{{count}} Итерации", - "iterationsWithCount_many": "{{count}} Итераций", "useSize": "Использовать размер", "coherenceMode": "Режим", "aspect": "Соотношение", @@ -541,7 +554,10 @@ "infillMosaicTileHeight": "Высота плиток", "infillMosaicMinColor": "Мин цвет", "infillMosaicMaxColor": "Макс цвет", - "infillColorValue": "Цвет заливки" + "infillColorValue": "Цвет заливки", + "globalSettings": "Глобальные настройки", + "globalNegativePromptPlaceholder": "Глобальный негативный запрос", + "globalPositivePromptPlaceholder": "Глобальный запрос" }, "settings": { "models": "Модели", @@ -706,7 +722,9 @@ "coherenceModeBoxBlur": "коробчатое размытие", "discardCurrent": "Отбросить текущее", "invertBrushSizeScrollDirection": "Инвертировать прокрутку для размера кисти", - "initialFitImageSize": "Подогнать размер изображения при перебросе" + "initialFitImageSize": "Подогнать размер изображения при перебросе", + "hideBoundingBox": "Скрыть ограничительную рамку", + "showBoundingBox": "Показать ограничительную рамку" }, "accessibility": { "uploadImage": "Загрузить изображение", @@ -849,7 +867,10 @@ "editMode": "Открыть в редакторе узлов", "resetToDefaultValue": "Сбросить к стандартному значкнию", "edit": "Редактировать", - "noFieldsViewMode": "В этом рабочем процессе нет выбранных полей для отображения. Просмотрите полный рабочий процесс для настройки значений." + "noFieldsViewMode": "В этом рабочем процессе нет выбранных полей для отображения. Просмотрите полный рабочий процесс для настройки значений.", + "graph": "График", + "showEdgeLabels": "Показать метки на ребрах", + "showEdgeLabelsHelp": "Показать метки на ребрах, указывающие на соединенные узлы" }, "controlnet": { "amult": "a_mult", @@ -917,8 +938,8 @@ "lineartAnime": "Контурный рисунок в стиле аниме", "mediapipeFaceDescription": "Обнаружение лиц с помощью Mediapipe", "hedDescription": "Целостное обнаружение границ", - "setControlImageDimensions": "Установите размеры контрольного изображения на Ш/В", - "scribble": "каракули", + "setControlImageDimensions": "Скопируйте размер в Ш/В (оптимизируйте для модели)", + "scribble": "Штрихи", "maxFaces": "Макс Лица", "mlsdDescription": "Минималистичный детектор отрезков линии", "resizeSimple": "Изменить размер (простой)", @@ -933,7 +954,18 @@ "small": "Маленький", "body": "Тело", "hands": "Руки", - "selectCLIPVisionModel": "Выбрать модель CLIP Vision" + "selectCLIPVisionModel": "Выбрать модель CLIP Vision", + "ipAdapterMethod": "Метод", + "full": "Всё", + "mlsd": "M-LSD", + "h": "H", + "style": "Только стиль", + "dwOpenpose": "DW Openpose", + "pidi": "PIDI", + "composition": "Только композиция", + "hed": "HED", + "beginEndStepPercentShort": "Начало/конец %", + "setControlImageDimensionsForce": "Скопируйте размер в Ш/В (игнорируйте модель)" }, "boards": { "autoAddBoard": "Авто добавление Доски", @@ -1312,6 +1344,12 @@ "paragraphs": [ "Плавно укладывайте изображение вдоль вертикальной оси." ] + }, + "ipAdapterMethod": { + "heading": "Метод", + "paragraphs": [ + "Метод, с помощью которого применяется текущий IP-адаптер." + ] } }, "metadata": { @@ -1359,7 +1397,6 @@ "completed": "Выполнено", "queueBack": "Добавить в очередь", "cancelFailed": "Проблема с отменой элемента", - "queueCountPrediction": "{{promptsCount}} запросов × {{iterations}} изображений -> {{count}} генераций", "batchQueued": "Пакетная очередь", "pauseFailed": "Проблема с приостановкой рендеринга", "clearFailed": "Проблема с очисткой очереди", @@ -1475,7 +1512,11 @@ "projectWorkflows": "Рабочие процессы проекта", "defaultWorkflows": "Стандартные рабочие процессы", "name": "Имя", - "noRecentWorkflows": "Нет последних рабочих процессов" + "noRecentWorkflows": "Нет последних рабочих процессов", + "loadWorkflow": "Рабочий процесс $t(common.load)", + "convertGraph": "Конвертировать график", + "loadFromGraph": "Загрузка рабочего процесса из графика", + "autoLayout": "Автоматическое расположение" }, "hrf": { "enableHrf": "Включить исправление высокого разрешения", @@ -1528,5 +1569,56 @@ "addPromptTrigger": "Добавить триггер запроса", "compatibleEmbeddings": "Совместимые встраивания", "noMatchingTriggers": "Нет соответствующих триггеров" + }, + "controlLayers": { + "moveToBack": "На задний план", + "moveForward": "Переместить вперёд", + "moveBackward": "Переместить назад", + "brushSize": "Размер кисти", + "controlLayers": "Слои управления", + "globalMaskOpacity": "Глобальная непрозрачность маски", + "autoNegative": "Авто негатив", + "deletePrompt": "Удалить запрос", + "resetRegion": "Сбросить регион", + "debugLayers": "Слои отладки", + "rectangle": "Прямоугольник", + "maskPreviewColor": "Цвет предпросмотра маски", + "addNegativePrompt": "Добавить $t(common.negativePrompt)", + "regionalGuidance": "Региональная точность", + "opacity": "Непрозрачность", + "globalControlAdapter": "Глобальный $t(controlnet.controlAdapter_one)", + "globalControlAdapterLayer": "Глобальный $t(controlnet.controlAdapter_one) $t(unifiedCanvas.layer)", + "globalIPAdapter": "Глобальный $t(common.ipAdapter)", + "globalIPAdapterLayer": "Глобальный $t(common.ipAdapter) $t(unifiedCanvas.layer)", + "opacityFilter": "Фильтр непрозрачности", + "deleteAll": "Удалить всё", + "addLayer": "Добавить слой", + "moveToFront": "На передний план", + "toggleVisibility": "Переключить видимость слоя", + "addPositivePrompt": "Добавить $t(common.positivePrompt)", + "addIPAdapter": "Добавить $t(common.ipAdapter)", + "regionalGuidanceLayer": "$t(controlLayers.regionalGuidance) $t(unifiedCanvas.layer)", + "resetProcessor": "Сброс процессора по умолчанию", + "clearProcessor": "Чистый процессор", + "globalInitialImage": "Глобальное исходное изображение", + "globalInitialImageLayer": "$t(controlLayers.globalInitialImage) $t(unifiedCanvas.layer)", + "noLayersAdded": "Без слоев", + "layers_one": "Слой", + "layers_few": "Слоя", + "layers_many": "Слоев" + }, + "ui": { + "tabs": { + "generation": "Генерация", + "canvas": "Холст", + "workflowsTab": "$t(ui.tabs.workflows) $t(common.tab)", + "models": "Модели", + "generationTab": "$t(ui.tabs.generation) $t(common.tab)", + "workflows": "Рабочие процессы", + "canvasTab": "$t(ui.tabs.canvas) $t(common.tab)", + "queueTab": "$t(ui.tabs.queue) $t(common.tab)", + "modelsTab": "$t(ui.tabs.models) $t(common.tab)", + "queue": "Очередь" + } } } diff --git a/invokeai/frontend/web/public/locales/zh_CN.json b/invokeai/frontend/web/public/locales/zh_CN.json index 8aff73d2a1..45bab5c6da 100644 --- a/invokeai/frontend/web/public/locales/zh_CN.json +++ b/invokeai/frontend/web/public/locales/zh_CN.json @@ -66,7 +66,7 @@ "saveAs": "保存为", "ai": "ai", "or": "或", - "aboutDesc": "使用 Invoke 工作?查看:", + "aboutDesc": "使用 Invoke 工作?来看看:", "add": "添加", "loglevel": "日志级别", "copy": "复制", @@ -445,7 +445,6 @@ "useX2Model": "图像太大,无法使用 x4 模型,使用 x2 模型作为替代", "tooLarge": "图像太大无法进行放大,请选择更小的图像" }, - "iterationsWithCount_other": "{{count}} 次迭代生成", "cfgRescaleMultiplier": "CFG 重缩放倍数", "useSize": "使用尺寸", "setToOptimalSize": "优化模型大小", @@ -853,7 +852,6 @@ "pruneSucceeded": "从队列修剪 {{item_count}} 个已完成的项目", "notReady": "无法排队", "batchFailedToQueue": "批次加入队列失败", - "queueCountPrediction": "{{promptsCount}} 提示词 × {{iterations}} 迭代次数 -> {{count}} 次生成", "batchQueued": "加入队列的批次", "front": "前", "pruneTooltip": "修剪 {{item_count}} 个已完成的项目", diff --git a/invokeai/frontend/web/scripts/typegen.js b/invokeai/frontend/web/scripts/typegen.js index 758a0ef4f5..fa2d791350 100644 --- a/invokeai/frontend/web/scripts/typegen.js +++ b/invokeai/frontend/web/scripts/typegen.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import fs from 'node:fs'; import openapiTS from 'openapi-typescript'; diff --git a/invokeai/frontend/web/src/app/hooks/useSocketIO.ts b/invokeai/frontend/web/src/app/hooks/useSocketIO.ts index e1c4cebdb9..aaa3b8f6f2 100644 --- a/invokeai/frontend/web/src/app/hooks/useSocketIO.ts +++ b/invokeai/frontend/web/src/app/hooks/useSocketIO.ts @@ -67,6 +67,8 @@ export const useSocketIO = () => { if ($isDebugging.get() || import.meta.env.MODE === 'development') { window.$socketOptions = $socketOptions; + // This is only enabled manually for debugging, console is allowed. + /* eslint-disable-next-line no-console */ console.log('Socket initialized', socket); } @@ -75,6 +77,8 @@ export const useSocketIO = () => { return () => { if ($isDebugging.get() || import.meta.env.MODE === 'development') { window.$socketOptions = undefined; + // This is only enabled manually for debugging, console is allowed. + /* eslint-disable-next-line no-console */ console.log('Socket teardown', socket); } socket.disconnect(); diff --git a/invokeai/frontend/web/src/app/store/middleware/debugLoggerMiddleware.ts b/invokeai/frontend/web/src/app/store/middleware/debugLoggerMiddleware.ts index b6df6dab94..89010275d1 100644 --- a/invokeai/frontend/web/src/app/store/middleware/debugLoggerMiddleware.ts +++ b/invokeai/frontend/web/src/app/store/middleware/debugLoggerMiddleware.ts @@ -1,3 +1,6 @@ +/* eslint-disable no-console */ +// This is only enabled manually for debugging, console is allowed. + import type { Middleware, MiddlewareAPI } from '@reduxjs/toolkit'; import { diff } from 'jsondiffpatch'; diff --git a/invokeai/frontend/web/src/app/store/middleware/devtools/actionSanitizer.ts b/invokeai/frontend/web/src/app/store/middleware/devtools/actionSanitizer.ts index 508109caf5..f0ea175aec 100644 --- a/invokeai/frontend/web/src/app/store/middleware/devtools/actionSanitizer.ts +++ b/invokeai/frontend/web/src/app/store/middleware/devtools/actionSanitizer.ts @@ -1,7 +1,6 @@ import type { UnknownAction } from '@reduxjs/toolkit'; import { deepClone } from 'common/util/deepClone'; import { isAnyGraphBuilt } from 'features/nodes/store/actions'; -import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice'; import { appInfoApi } from 'services/api/endpoints/appInfo'; import type { Graph } from 'services/api/types'; import { socketGeneratorProgress } from 'services/events/actions'; @@ -25,13 +24,6 @@ export const actionSanitizer = (action: A): A => { }; } - if (nodeTemplatesBuilt.match(action)) { - return { - ...action, - payload: '', - }; - } - if (socketGeneratorProgress.match(action)) { const sanitized = deepClone(action); if (sanitized.payload.data.progress_image) { diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts index a0b07b9419..244e0cdf8a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/boardAndImagesDeleted.ts @@ -21,7 +21,7 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS const { canvas, nodes, controlAdapters, controlLayers } = getState(); deleted_images.forEach((image_name) => { - const imageUsage = getImageUsage(canvas, nodes, controlAdapters, controlLayers.present, image_name); + const imageUsage = getImageUsage(canvas, nodes.present, controlAdapters, controlLayers.present, image_name); if (imageUsage.isCanvasImage && !wasCanvasReset) { dispatch(resetCanvas()); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts index 7d5aa27f20..2a59cc0317 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor.ts @@ -1,60 +1,54 @@ import { isAnyOf } from '@reduxjs/toolkit'; import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import type { AppDispatch } from 'app/store/store'; import { parseify } from 'common/util/serialize'; import { caLayerImageChanged, - caLayerIsProcessingImageChanged, caLayerModelChanged, caLayerProcessedImageChanged, caLayerProcessorConfigChanged, + caLayerProcessorPendingBatchIdChanged, + caLayerRecalled, isControlAdapterLayer, } from 'features/controlLayers/store/controlLayersSlice'; import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters'; import { isImageOutput } from 'features/nodes/types/common'; import { addToast } from 'features/system/store/systemSlice'; import { t } from 'i18next'; -import { isEqual } from 'lodash-es'; -import { imagesApi } from 'services/api/endpoints/images'; +import { getImageDTO } from 'services/api/endpoints/images'; import { queueApi } from 'services/api/endpoints/queue'; -import type { BatchConfig, ImageDTO } from 'services/api/types'; +import type { BatchConfig } from 'services/api/types'; import { socketInvocationComplete } from 'services/events/actions'; +import { assert } from 'tsafe'; -const matcher = isAnyOf(caLayerImageChanged, caLayerProcessorConfigChanged, caLayerModelChanged); +const matcher = isAnyOf(caLayerImageChanged, caLayerProcessorConfigChanged, caLayerModelChanged, caLayerRecalled); const DEBOUNCE_MS = 300; const log = logger('session'); +/** + * Simple helper to cancel a batch and reset the pending batch ID + */ +const cancelProcessorBatch = async (dispatch: AppDispatch, layerId: string, batchId: string) => { + const req = dispatch(queueApi.endpoints.cancelByBatchIds.initiate({ batch_ids: [batchId] })); + log.trace({ batchId }, 'Cancelling existing preprocessor batch'); + try { + await req.unwrap(); + } catch { + // no-op + } finally { + req.reset(); + // Always reset the pending batch ID - the cancel req could fail if the batch doesn't exist + dispatch(caLayerProcessorPendingBatchIdChanged({ layerId, batchId: null })); + } +}; + export const addControlAdapterPreprocessor = (startAppListening: AppStartListening) => { startAppListening({ matcher, - effect: async (action, { dispatch, getState, getOriginalState, cancelActiveListeners, delay, take }) => { - const { layerId } = action.payload; - const precheckLayerOriginal = getOriginalState() - .controlLayers.present.layers.filter(isControlAdapterLayer) - .find((l) => l.id === layerId); - const precheckLayer = getState() - .controlLayers.present.layers.filter(isControlAdapterLayer) - .find((l) => l.id === layerId); - - // Conditions to bail - const layerDoesNotExist = !precheckLayer; - const layerHasNoImage = !precheckLayer?.controlAdapter.image; - const layerHasNoProcessorConfig = !precheckLayer?.controlAdapter.processorConfig; - const layerIsAlreadyProcessingImage = precheckLayer?.controlAdapter.isProcessingImage; - const areImageAndProcessorUnchanged = - isEqual(precheckLayer?.controlAdapter.image, precheckLayerOriginal?.controlAdapter.image) && - isEqual(precheckLayer?.controlAdapter.processorConfig, precheckLayerOriginal?.controlAdapter.processorConfig); - - if ( - layerDoesNotExist || - layerHasNoImage || - layerHasNoProcessorConfig || - areImageAndProcessorUnchanged || - layerIsAlreadyProcessingImage - ) { - return; - } + effect: async (action, { dispatch, getState, cancelActiveListeners, delay, take, signal }) => { + const layerId = caLayerRecalled.match(action) ? action.payload.id : action.payload.layerId; // Cancel any in-progress instances of this listener cancelActiveListeners(); @@ -62,19 +56,31 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni // Delay before starting actual work await delay(DEBOUNCE_MS); - dispatch(caLayerIsProcessingImageChanged({ layerId, isProcessingImage: true })); // Double-check that we are still eligible for processing const state = getState(); const layer = state.controlLayers.present.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId); - const image = layer?.controlAdapter.image; - const config = layer?.controlAdapter.processorConfig; // If we have no image or there is no processor config, bail - if (!layer || !image || !config) { + if (!layer) { return; } + const image = layer.controlAdapter.image; + const config = layer.controlAdapter.processorConfig; + + if (!image || !config) { + // The user has reset the image or config, so we should clear the processed image + dispatch(caLayerProcessedImageChanged({ layerId, imageDTO: null })); + } + + // At this point, the user has stopped fiddling with the processor settings and there is a processor selected. + + // If there is a pending processor batch, cancel it. + if (layer.controlAdapter.processorPendingBatchId) { + cancelProcessorBatch(dispatch, layerId, layer.controlAdapter.processorPendingBatchId); + } + // @ts-expect-error: TS isn't able to narrow the typing of buildNode and `config` will error... const processorNode = CA_PROCESSOR_DATA[config.type].buildNode(image, config); const enqueueBatchArg: BatchConfig = { @@ -82,7 +88,11 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni batch: { graph: { nodes: { - [processorNode.id]: { ...processorNode, is_intermediate: true }, + [processorNode.id]: { + ...processorNode, + // Control images are always intermediate - do not save to gallery + is_intermediate: true, + }, }, edges: [], }, @@ -90,16 +100,21 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni }, }; + // Kick off the processor batch + const req = dispatch( + queueApi.endpoints.enqueueBatch.initiate(enqueueBatchArg, { + fixedCacheKey: 'enqueueBatch', + }) + ); + try { - const req = dispatch( - queueApi.endpoints.enqueueBatch.initiate(enqueueBatchArg, { - fixedCacheKey: 'enqueueBatch', - }) - ); const enqueueResult = await req.unwrap(); - req.reset(); + // TODO(psyche): Update the pydantic models, pretty sure we will _always_ have a batch_id here, but the model says it's optional + assert(enqueueResult.batch.batch_id, 'Batch ID not returned from queue'); + dispatch(caLayerProcessorPendingBatchIdChanged({ layerId, batchId: enqueueResult.batch.batch_id })); log.debug({ enqueueResult: parseify(enqueueResult) }, t('queue.graphQueued')); + // Wait for the processor node to complete const [invocationCompleteAction] = await take( (action): action is ReturnType => socketInvocationComplete.match(action) && @@ -108,48 +123,51 @@ export const addControlAdapterPreprocessor = (startAppListening: AppStartListeni ); // We still have to check the output type - if (isImageOutput(invocationCompleteAction.payload.data.result)) { - const { image_name } = invocationCompleteAction.payload.data.result.image; + assert( + isImageOutput(invocationCompleteAction.payload.data.result), + `Processor did not return an image output, got: ${invocationCompleteAction.payload.data.result}` + ); + const { image_name } = invocationCompleteAction.payload.data.result.image; - // Wait for the ImageDTO to be received - const [{ payload }] = await take( - (action) => - imagesApi.endpoints.getImageDTO.matchFulfilled(action) && action.payload.image_name === image_name - ); + const imageDTO = await getImageDTO(image_name); + assert(imageDTO, "Failed to fetch processor output's image DTO"); - const imageDTO = payload as ImageDTO; - - log.debug({ layerId, imageDTO }, 'ControlNet image processed'); - - // Update the processed image in the store - dispatch( - caLayerProcessedImageChanged({ - layerId, - imageDTO, - }) - ); - dispatch(caLayerIsProcessingImageChanged({ layerId, isProcessingImage: false })); - } + // Whew! We made it. Update the layer with the processed image + log.debug({ layerId, imageDTO }, 'ControlNet image processed'); + dispatch(caLayerProcessedImageChanged({ layerId, imageDTO })); + dispatch(caLayerProcessorPendingBatchIdChanged({ layerId, batchId: null })); } catch (error) { - console.log(error); - log.error({ enqueueBatchArg: parseify(enqueueBatchArg) }, t('queue.graphFailedToQueue')); - dispatch(caLayerIsProcessingImageChanged({ layerId, isProcessingImage: false })); + if (signal.aborted) { + // The listener was canceled - we need to cancel the pending processor batch, if there is one (could have changed by now). + const pendingBatchId = getState() + .controlLayers.present.layers.filter(isControlAdapterLayer) + .find((l) => l.id === layerId)?.controlAdapter.processorPendingBatchId; + if (pendingBatchId) { + cancelProcessorBatch(dispatch, layerId, pendingBatchId); + } + log.trace('Control Adapter preprocessor cancelled'); + } else { + // Some other error condition... + log.error({ enqueueBatchArg: parseify(enqueueBatchArg) }, t('queue.graphFailedToQueue')); - if (error instanceof Object) { - if ('data' in error && 'status' in error) { - if (error.status === 403) { - dispatch(caLayerImageChanged({ layerId, imageDTO: null })); - return; + if (error instanceof Object) { + if ('data' in error && 'status' in error) { + if (error.status === 403) { + dispatch(caLayerImageChanged({ layerId, imageDTO: null })); + return; + } } } - } - dispatch( - addToast({ - title: t('queue.graphFailedToQueue'), - status: 'error', - }) - ); + dispatch( + addToast({ + title: t('queue.graphFailedToQueue'), + status: 'error', + }) + ); + } + } finally { + req.reset(); } }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts index cdcc99ade2..a7491ab01b 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas.ts @@ -8,8 +8,8 @@ import { blobToDataURL } from 'features/canvas/util/blobToDataURL'; import { getCanvasData } from 'features/canvas/util/getCanvasData'; import { getCanvasGenerationMode } from 'features/canvas/util/getCanvasGenerationMode'; import { canvasGraphBuilt } from 'features/nodes/store/actions'; -import { buildCanvasGraph } from 'features/nodes/util/graph/buildCanvasGraph'; import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig'; +import { buildCanvasGraph } from 'features/nodes/util/graph/canvas/buildCanvasGraph'; import { imagesApi } from 'services/api/endpoints/images'; import { queueApi } from 'services/api/endpoints/queue'; import type { ImageDTO } from 'services/api/types'; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts index 557220c449..6ca7ee7ffa 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedLinear.ts @@ -1,8 +1,9 @@ import { enqueueRequested } from 'app/store/actions'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; -import { buildGenerationTabGraph } from 'features/nodes/util/graph/buildGenerationTabGraph'; -import { buildGenerationTabSDXLGraph } from 'features/nodes/util/graph/buildGenerationTabSDXLGraph'; +import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice'; import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig'; +import { buildGenerationTabGraph } from 'features/nodes/util/graph/generation/buildGenerationTabGraph'; +import { buildGenerationTabSDXLGraph } from 'features/nodes/util/graph/generation/buildGenerationTabSDXLGraph'; import { queueApi } from 'services/api/endpoints/queue'; export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) => { @@ -11,12 +12,13 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) enqueueRequested.match(action) && action.payload.tabName === 'generation', effect: async (action, { getState, dispatch }) => { const state = getState(); + const { shouldShowProgressInViewer } = state.ui; const model = state.generation.model; const { prepend } = action.payload; let graph; - if (model && model.base === 'sdxl') { + if (model?.base === 'sdxl') { graph = await buildGenerationTabSDXLGraph(state); } else { graph = await buildGenerationTabGraph(state); @@ -29,7 +31,14 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) fixedCacheKey: 'enqueueBatch', }) ); - req.reset(); + try { + await req.unwrap(); + if (shouldShowProgressInViewer) { + dispatch(isImageViewerOpenChanged(true)); + } + } finally { + req.reset(); + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts index 8d39daaef8..c4087aacde 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/enqueueRequestedNodes.ts @@ -11,9 +11,9 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) = enqueueRequested.match(action) && action.payload.tabName === 'workflows', effect: async (action, { getState, dispatch }) => { const state = getState(); - const { nodes, edges } = state.nodes; + const { nodes, edges } = state.nodes.present; const workflow = state.workflow; - const graph = buildNodesGraph(state.nodes); + const graph = buildNodesGraph(state.nodes.present); const builtWorkflow = buildWorkflowWithValidation({ nodes, edges, @@ -39,7 +39,11 @@ export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) = fixedCacheKey: 'enqueueBatch', }) ); - req.reset(); + try { + await req.unwrap(); + } finally { + req.reset(); + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/galleryImageClicked.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/galleryImageClicked.ts index 6b8c9b4ea3..67c6d076ee 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/galleryImageClicked.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/galleryImageClicked.ts @@ -1,7 +1,7 @@ import { createAction } from '@reduxjs/toolkit'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors'; -import { isImageViewerOpenChanged, selectionChanged } from 'features/gallery/store/gallerySlice'; +import { selectionChanged } from 'features/gallery/store/gallerySlice'; import { imagesApi } from 'services/api/endpoints/images'; import type { ImageDTO } from 'services/api/types'; import { imagesSelectors } from 'services/api/util'; @@ -62,7 +62,6 @@ export const addGalleryImageClickedListener = (startAppListening: AppStartListen } else { dispatch(selectionChanged([imageDTO])); } - dispatch(isImageViewerOpenChanged(true)); }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts index acb2bdb698..923b2c0197 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/getOpenAPISchema.ts @@ -1,7 +1,7 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; import { parseify } from 'common/util/serialize'; -import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { parseSchema } from 'features/nodes/util/schema/parseSchema'; import { size } from 'lodash-es'; import { appInfoApi } from 'services/api/endpoints/appInfo'; @@ -9,7 +9,7 @@ import { appInfoApi } from 'services/api/endpoints/appInfo'; export const addGetOpenAPISchemaListener = (startAppListening: AppStartListening) => { startAppListening({ matcher: appInfoApi.endpoints.getOpenAPISchema.matchFulfilled, - effect: (action, { dispatch, getState }) => { + effect: (action, { getState }) => { const log = logger('system'); const schemaJSON = action.payload; @@ -20,7 +20,7 @@ export const addGetOpenAPISchemaListener = (startAppListening: AppStartListening log.debug({ nodeTemplates: parseify(nodeTemplates) }, `Built ${size(nodeTemplates)} node templates`); - dispatch(nodeTemplatesBuilt(nodeTemplates)); + $templates.set(nodeTemplates); }, }); diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts index 95d17da653..8c24badc76 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageDeleted.ts @@ -29,7 +29,7 @@ import type { ImageDTO } from 'services/api/types'; import { imagesSelectors } from 'services/api/util'; const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => { - state.nodes.nodes.forEach((node) => { + state.nodes.present.nodes.forEach((node) => { if (!isInvocationNode(node)) { return; } @@ -73,25 +73,25 @@ const deleteControlAdapterImages = (state: RootState, dispatch: AppDispatch, ima const deleteControlLayerImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => { state.controlLayers.present.layers.forEach((l) => { if (isRegionalGuidanceLayer(l)) { - if (l.ipAdapters.some((ipa) => ipa.image?.imageName === imageDTO.image_name)) { + if (l.ipAdapters.some((ipa) => ipa.image?.name === imageDTO.image_name)) { dispatch(layerDeleted(l.id)); } } if (isControlAdapterLayer(l)) { if ( - l.controlAdapter.image?.imageName === imageDTO.image_name || - l.controlAdapter.processedImage?.imageName === imageDTO.image_name + l.controlAdapter.image?.name === imageDTO.image_name || + l.controlAdapter.processedImage?.name === imageDTO.image_name ) { dispatch(layerDeleted(l.id)); } } if (isIPAdapterLayer(l)) { - if (l.ipAdapter.image?.imageName === imageDTO.image_name) { + if (l.ipAdapter.image?.name === imageDTO.image_name) { dispatch(layerDeleted(l.id)); } } if (isInitialImageLayer(l)) { - if (l.image?.imageName === imageDTO.image_name) { + if (l.image?.name === imageDTO.image_name) { dispatch(layerDeleted(l.id)); } } diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketGeneratorProgress.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketGeneratorProgress.ts index bb113a09ee..2dd598396a 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketGeneratorProgress.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketGeneratorProgress.ts @@ -1,5 +1,8 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { deepClone } from 'common/util/deepClone'; +import { $nodeExecutionStates } from 'features/nodes/hooks/useExecutionState'; +import { zNodeStatus } from 'features/nodes/types/invocation'; import { socketGeneratorProgress } from 'services/events/actions'; const log = logger('socketio'); @@ -9,6 +12,13 @@ export const addGeneratorProgressEventListener = (startAppListening: AppStartLis actionCreator: socketGeneratorProgress, effect: (action) => { log.trace(action.payload, `Generator progress`); + const { source_node_id, step, total_steps, progress_image } = action.payload.data; + const nes = deepClone($nodeExecutionStates.get()[source_node_id]); + if (nes) { + nes.status = zNodeStatus.enum.IN_PROGRESS; + nes.progress = (step + 1) / total_steps; + nes.progressImage = progress_image ?? null; + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts index 279f9aac5b..06dc08d846 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts @@ -1,10 +1,18 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { deepClone } from 'common/util/deepClone'; import { parseify } from 'common/util/serialize'; import { addImageToStagingArea } from 'features/canvas/store/canvasSlice'; -import { boardIdSelected, galleryViewChanged, imageSelected } from 'features/gallery/store/gallerySlice'; +import { + boardIdSelected, + galleryViewChanged, + imageSelected, + isImageViewerOpenChanged, +} from 'features/gallery/store/gallerySlice'; import { IMAGE_CATEGORIES } from 'features/gallery/store/types'; +import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState'; import { isImageOutput } from 'features/nodes/types/common'; +import { zNodeStatus } from 'features/nodes/types/invocation'; import { CANVAS_OUTPUT } from 'features/nodes/util/graph/constants'; import { boardsApi } from 'services/api/endpoints/boards'; import { imagesApi } from 'services/api/endpoints/images'; @@ -23,7 +31,7 @@ export const addInvocationCompleteEventListener = (startAppListening: AppStartLi const { data } = action.payload; log.debug({ data: parseify(data) }, `Invocation complete (${action.payload.data.node.type})`); - const { result, node, queue_batch_id } = data; + const { result, node, queue_batch_id, source_node_id } = data; // This complete event has an associated image output if (isImageOutput(result) && !nodeTypeDenylist.includes(node.type)) { const { image_name } = result.image; @@ -101,9 +109,20 @@ export const addInvocationCompleteEventListener = (startAppListening: AppStartLi } dispatch(imageSelected(imageDTO)); + dispatch(isImageViewerOpenChanged(true)); } } } + + const nes = deepClone($nodeExecutionStates.get()[source_node_id]); + if (nes) { + nes.status = zNodeStatus.enum.COMPLETED; + if (nes.progress !== null) { + nes.progress = 1; + } + nes.outputs.push(result); + upsertExecutionState(nes.nodeId, nes); + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts index fb898b4c7a..ce26c4dd7d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationError.ts @@ -1,5 +1,8 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { deepClone } from 'common/util/deepClone'; +import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState'; +import { zNodeStatus } from 'features/nodes/types/invocation'; import { socketInvocationError } from 'services/events/actions'; const log = logger('socketio'); @@ -9,6 +12,15 @@ export const addInvocationErrorEventListener = (startAppListening: AppStartListe actionCreator: socketInvocationError, effect: (action) => { log.error(action.payload, `Invocation error (${action.payload.data.node.type})`); + const { source_node_id } = action.payload.data; + const nes = deepClone($nodeExecutionStates.get()[source_node_id]); + if (nes) { + nes.status = zNodeStatus.enum.FAILED; + nes.error = action.payload.data.error; + nes.progress = null; + nes.progressImage = null; + upsertExecutionState(nes.nodeId, nes); + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationStarted.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationStarted.ts index baf476a66b..9d6e0ac14d 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationStarted.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationStarted.ts @@ -1,5 +1,8 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { deepClone } from 'common/util/deepClone'; +import { $nodeExecutionStates, upsertExecutionState } from 'features/nodes/hooks/useExecutionState'; +import { zNodeStatus } from 'features/nodes/types/invocation'; import { socketInvocationStarted } from 'services/events/actions'; const log = logger('socketio'); @@ -9,6 +12,12 @@ export const addInvocationStartedEventListener = (startAppListening: AppStartLis actionCreator: socketInvocationStarted, effect: (action) => { log.debug(action.payload, `Invocation started (${action.payload.data.node.type})`); + const { source_node_id } = action.payload.data; + const nes = deepClone($nodeExecutionStates.get()[source_node_id]); + if (nes) { + nes.status = zNodeStatus.enum.IN_PROGRESS; + upsertExecutionState(nes.nodeId, nes); + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts index 84073bb427..2adc529766 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketQueueItemStatusChanged.ts @@ -1,5 +1,9 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; +import { deepClone } from 'common/util/deepClone'; +import { $nodeExecutionStates } from 'features/nodes/hooks/useExecutionState'; +import { zNodeStatus } from 'features/nodes/types/invocation'; +import { forEach } from 'lodash-es'; import { queueApi, queueItemsAdapter } from 'services/api/endpoints/queue'; import { socketQueueItemStatusChanged } from 'services/events/actions'; @@ -54,6 +58,21 @@ export const addSocketQueueItemStatusChangedEventListener = (startAppListening: dispatch( queueApi.util.invalidateTags(['CurrentSessionQueueItem', 'NextSessionQueueItem', 'InvocationCacheStatus']) ); + + if (['in_progress'].includes(action.payload.data.queue_item.status)) { + forEach($nodeExecutionStates.get(), (nes) => { + if (!nes) { + return; + } + const clone = deepClone(nes); + clone.status = zNodeStatus.enum.PENDING; + clone.error = null; + clone.progress = null; + clone.progressImage = null; + clone.outputs = []; + $nodeExecutionStates.setKey(clone.nodeId, clone); + }); + } }, }); }; diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts index 5ee9de3c11..63d960b406 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/updateAllNodesRequested.ts @@ -1,7 +1,7 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; import { updateAllNodesRequested } from 'features/nodes/store/actions'; -import { nodeReplaced } from 'features/nodes/store/nodesSlice'; +import { $templates, nodeReplaced } from 'features/nodes/store/nodesSlice'; import { NodeUpdateError } from 'features/nodes/types/error'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getNeedsUpdate, updateNode } from 'features/nodes/util/node/nodeUpdate'; @@ -14,7 +14,8 @@ export const addUpdateAllNodesRequestedListener = (startAppListening: AppStartLi actionCreator: updateAllNodesRequested, effect: (action, { dispatch, getState }) => { const log = logger('nodes'); - const { nodes, templates } = getState().nodes; + const { nodes } = getState().nodes.present; + const templates = $templates.get(); let unableToUpdateCount = 0; @@ -24,7 +25,7 @@ export const addUpdateAllNodesRequestedListener = (startAppListening: AppStartLi unableToUpdateCount++; return; } - if (!getNeedsUpdate(node, template)) { + if (!getNeedsUpdate(node.data, template)) { // No need to increment the count here, since we're not actually updating return; } diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts index 0227597fe9..a680bbca97 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/workflowLoadRequested.ts @@ -2,32 +2,51 @@ import { logger } from 'app/logging/logger'; import type { AppStartListening } from 'app/store/middleware/listenerMiddleware'; import { parseify } from 'common/util/serialize'; import { workflowLoaded, workflowLoadRequested } from 'features/nodes/store/actions'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { $flow } from 'features/nodes/store/reactFlowInstance'; +import type { Templates } from 'features/nodes/store/types'; import { WorkflowMigrationError, WorkflowVersionError } from 'features/nodes/types/error'; +import { graphToWorkflow } from 'features/nodes/util/workflow/graphToWorkflow'; import { validateWorkflow } from 'features/nodes/util/workflow/validateWorkflow'; import { addToast } from 'features/system/store/systemSlice'; import { makeToast } from 'features/system/util/makeToast'; import { t } from 'i18next'; +import type { GraphAndWorkflowResponse, NonNullableGraph } from 'services/api/types'; import { z } from 'zod'; import { fromZodError } from 'zod-validation-error'; +const getWorkflow = (data: GraphAndWorkflowResponse, templates: Templates) => { + if (data.workflow) { + // Prefer to load the workflow if it's available - it has more information + const parsed = JSON.parse(data.workflow); + return validateWorkflow(parsed, templates); + } else if (data.graph) { + // Else we fall back on the graph, using the graphToWorkflow function to convert and do layout + const parsed = JSON.parse(data.graph); + const workflow = graphToWorkflow(parsed as NonNullableGraph, true); + return validateWorkflow(workflow, templates); + } else { + throw new Error('No workflow or graph provided'); + } +}; + export const addWorkflowLoadRequestedListener = (startAppListening: AppStartListening) => { startAppListening({ actionCreator: workflowLoadRequested, - effect: (action, { dispatch, getState }) => { + effect: (action, { dispatch }) => { const log = logger('nodes'); - const { workflow, asCopy } = action.payload; - const nodeTemplates = getState().nodes.templates; + const { data, asCopy } = action.payload; + const nodeTemplates = $templates.get(); try { - const { workflow: validatedWorkflow, warnings } = validateWorkflow(workflow, nodeTemplates); + const { workflow, warnings } = getWorkflow(data, nodeTemplates); if (asCopy) { // If we're loading a copy, we need to remove the ID so that the backend will create a new workflow - delete validatedWorkflow.id; + delete workflow.id; } - dispatch(workflowLoaded(validatedWorkflow)); + dispatch(workflowLoaded(workflow)); if (!warnings.length) { dispatch( addToast( diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index 9661f57f99..062cdc1cbf 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -21,7 +21,8 @@ import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/galle import { hrfPersistConfig, hrfSlice } from 'features/hrf/store/hrfSlice'; import { loraPersistConfig, loraSlice } from 'features/lora/store/loraSlice'; import { modelManagerV2PersistConfig, modelManagerV2Slice } from 'features/modelManagerV2/store/modelManagerV2Slice'; -import { nodesPersistConfig, nodesSlice } from 'features/nodes/store/nodesSlice'; +import { nodesPersistConfig, nodesSlice, nodesUndoableConfig } from 'features/nodes/store/nodesSlice'; +import { workflowSettingsPersistConfig, workflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice'; import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workflowSlice'; import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice'; import { postprocessingPersistConfig, postprocessingSlice } from 'features/parameters/store/postprocessingSlice'; @@ -50,7 +51,7 @@ const allReducers = { [canvasSlice.name]: canvasSlice.reducer, [gallerySlice.name]: gallerySlice.reducer, [generationSlice.name]: generationSlice.reducer, - [nodesSlice.name]: nodesSlice.reducer, + [nodesSlice.name]: undoable(nodesSlice.reducer, nodesUndoableConfig), [postprocessingSlice.name]: postprocessingSlice.reducer, [systemSlice.name]: systemSlice.reducer, [configSlice.name]: configSlice.reducer, @@ -66,6 +67,7 @@ const allReducers = { [workflowSlice.name]: workflowSlice.reducer, [hrfSlice.name]: hrfSlice.reducer, [controlLayersSlice.name]: undoable(controlLayersSlice.reducer, controlLayersUndoableConfig), + [workflowSettingsSlice.name]: workflowSettingsSlice.reducer, [api.reducerPath]: api.reducer, }; @@ -111,6 +113,7 @@ const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = { [modelManagerV2PersistConfig.name]: modelManagerV2PersistConfig, [hrfPersistConfig.name]: hrfPersistConfig, [controlLayersPersistConfig.name]: controlLayersPersistConfig, + [workflowSettingsPersistConfig.name]: workflowSettingsPersistConfig, }; const unserialize: UnserializeFunction = (data, key) => { diff --git a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx index 01107c21b4..2712334e1e 100644 --- a/invokeai/frontend/web/src/common/components/IAIDndImage.tsx +++ b/invokeai/frontend/web/src/common/components/IAIDndImage.tsx @@ -70,6 +70,7 @@ const IAIDndImage = (props: IAIDndImageProps) => { onMouseOver, onMouseOut, dataTestId, + ...rest } = props; const [isHovered, setIsHovered] = useState(false); @@ -138,6 +139,7 @@ const IAIDndImage = (props: IAIDndImageProps) => { minH={minSize ? minSize : undefined} userSelect="none" cursor={isDragDisabled || !imageDTO ? 'default' : 'pointer'} + {...rest} > {imageDTO && ( = { onChange: (value: T | null) => void; getIsDisabled?: (model: T) => boolean; isLoading?: boolean; + groupByType?: boolean; }; type UseGroupedModelComboboxReturn = { @@ -23,17 +24,21 @@ type UseGroupedModelComboboxReturn = { noOptionsMessage: () => string; }; +const groupByBaseFunc = (model: T) => model.base.toUpperCase(); +const groupByBaseAndTypeFunc = (model: T) => + `${model.base.toUpperCase()} / ${model.type.replaceAll('_', ' ').toUpperCase()}`; + export const useGroupedModelCombobox = ( arg: UseGroupedModelComboboxArg ): UseGroupedModelComboboxReturn => { const { t } = useTranslation(); const base_model = useAppSelector((s) => s.generation.model?.base ?? 'sdxl'); - const { modelConfigs, selectedModel, getIsDisabled, onChange, isLoading } = arg; + const { modelConfigs, selectedModel, getIsDisabled, onChange, isLoading, groupByType = false } = arg; const options = useMemo[]>(() => { if (!modelConfigs) { return []; } - const groupedModels = groupBy(modelConfigs, 'base'); + const groupedModels = groupBy(modelConfigs, groupByType ? groupByBaseAndTypeFunc : groupByBaseFunc); const _options = reduce( groupedModels, (acc, val, label) => { @@ -49,9 +54,9 @@ export const useGroupedModelCombobox = ( }, [] as GroupBase[] ); - _options.sort((a) => (a.label === base_model ? -1 : 1)); + _options.sort((a) => (a.label?.split('/')[0]?.toLowerCase().includes(base_model) ? -1 : 1)); return _options; - }, [getIsDisabled, modelConfigs, base_model]); + }, [modelConfigs, groupByType, getIsDisabled, base_model]); const value = useMemo( () => diff --git a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts index 2aac5b8e72..41d6f4607e 100644 --- a/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts +++ b/invokeai/frontend/web/src/common/hooks/useIsReadyToEnqueue.ts @@ -1,3 +1,4 @@ +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { @@ -6,187 +7,230 @@ import { } from 'features/controlAdapters/store/controlAdaptersSlice'; import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types'; import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice'; +import type { Layer } from 'features/controlLayers/store/types'; import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import type { Templates } from 'features/nodes/store/types'; +import { selectWorkflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { selectGenerationSlice } from 'features/parameters/store/generationSlice'; import { selectSystemSlice } from 'features/system/store/systemSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import i18n from 'i18next'; -import { forEach } from 'lodash-es'; +import { forEach, upperFirst } from 'lodash-es'; +import { useMemo } from 'react'; import { getConnectedEdges } from 'reactflow'; -const selector = createMemoizedSelector( - [ - selectControlAdaptersSlice, - selectGenerationSlice, - selectSystemSlice, - selectNodesSlice, - selectDynamicPromptsSlice, - selectControlLayersSlice, - activeTabNameSelector, - ], - (controlAdapters, generation, system, nodes, dynamicPrompts, controlLayers, activeTabName) => { - const { model } = generation; - const { positivePrompt } = controlLayers.present; +const LAYER_TYPE_TO_TKEY: Record = { + initial_image_layer: 'controlLayers.globalInitialImage', + control_adapter_layer: 'controlLayers.globalControlAdapter', + ip_adapter_layer: 'controlLayers.globalIPAdapter', + regional_guidance_layer: 'controlLayers.regionalGuidance', +}; - const { isConnected } = system; +const createSelector = (templates: Templates) => + createMemoizedSelector( + [ + selectControlAdaptersSlice, + selectGenerationSlice, + selectSystemSlice, + selectNodesSlice, + selectWorkflowSettingsSlice, + selectDynamicPromptsSlice, + selectControlLayersSlice, + activeTabNameSelector, + ], + (controlAdapters, generation, system, nodes, workflowSettings, dynamicPrompts, controlLayers, activeTabName) => { + const { model } = generation; + const { size } = controlLayers.present; + const { positivePrompt } = controlLayers.present; - const reasons: string[] = []; + const { isConnected } = system; - // Cannot generate if not connected - if (!isConnected) { - reasons.push(i18n.t('parameters.invoke.systemDisconnected')); - } + const reasons: { prefix?: string; content: string }[] = []; - if (activeTabName === 'workflows') { - if (nodes.shouldValidateGraph) { - if (!nodes.nodes.length) { - reasons.push(i18n.t('parameters.invoke.noNodesInGraph')); + // Cannot generate if not connected + if (!isConnected) { + reasons.push({ content: i18n.t('parameters.invoke.systemDisconnected') }); + } + + if (activeTabName === 'workflows') { + if (workflowSettings.shouldValidateGraph) { + if (!nodes.nodes.length) { + reasons.push({ content: i18n.t('parameters.invoke.noNodesInGraph') }); + } + + nodes.nodes.forEach((node) => { + if (!isInvocationNode(node)) { + return; + } + + const nodeTemplate = templates[node.data.type]; + + if (!nodeTemplate) { + // Node type not found + reasons.push({ content: i18n.t('parameters.invoke.missingNodeTemplate') }); + return; + } + + const connectedEdges = getConnectedEdges([node], nodes.edges); + + forEach(node.data.inputs, (field) => { + const fieldTemplate = nodeTemplate.inputs[field.name]; + const hasConnection = connectedEdges.some( + (edge) => edge.target === node.id && edge.targetHandle === field.name + ); + + if (!fieldTemplate) { + reasons.push({ content: i18n.t('parameters.invoke.missingFieldTemplate') }); + return; + } + + if (fieldTemplate.required && field.value === undefined && !hasConnection) { + reasons.push({ + content: i18n.t('parameters.invoke.missingInputForField', { + nodeLabel: node.data.label || nodeTemplate.title, + fieldLabel: field.label || fieldTemplate.title, + }), + }); + return; + } + }); + }); + } + } else { + if (dynamicPrompts.prompts.length === 0 && getShouldProcessPrompt(positivePrompt)) { + reasons.push({ content: i18n.t('parameters.invoke.noPrompts') }); } - nodes.nodes.forEach((node) => { - if (!isInvocationNode(node)) { - return; - } + if (!model) { + reasons.push({ content: i18n.t('parameters.invoke.noModelSelected') }); + } - const nodeTemplate = nodes.templates[node.data.type]; + if (activeTabName === 'generation') { + // Handling for generation tab + controlLayers.present.layers + .filter((l) => l.isEnabled) + .forEach((l, i) => { + const layerLiteral = i18n.t('controlLayers.layers_one'); + const layerNumber = i + 1; + const layerType = i18n.t(LAYER_TYPE_TO_TKEY[l.type]); + const prefix = `${layerLiteral} #${layerNumber} (${layerType})`; + const problems: string[] = []; + if (l.type === 'control_adapter_layer') { + // Must have model + if (!l.controlAdapter.model) { + problems.push(i18n.t('parameters.invoke.layer.controlAdapterNoModelSelected')); + } + // Model base must match + if (l.controlAdapter.model?.base !== model?.base) { + problems.push(i18n.t('parameters.invoke.layer.controlAdapterIncompatibleBaseModel')); + } + // Must have a control image OR, if it has a processor, it must have a processed image + if (!l.controlAdapter.image) { + problems.push(i18n.t('parameters.invoke.layer.controlAdapterNoImageSelected')); + } else if (l.controlAdapter.processorConfig && !l.controlAdapter.processedImage) { + problems.push(i18n.t('parameters.invoke.layer.controlAdapterImageNotProcessed')); + } + // T2I Adapters require images have dimensions that are multiples of 64 (SD1.5) or 32 (SDXL) + if (l.controlAdapter.type === 't2i_adapter') { + const multiple = model?.base === 'sdxl' ? 32 : 64; + if (size.width % multiple !== 0 || size.height % multiple !== 0) { + problems.push(i18n.t('parameters.invoke.layer.t2iAdapterIncompatibleDimensions')); + } + } + } - if (!nodeTemplate) { - // Node type not found - reasons.push(i18n.t('parameters.invoke.missingNodeTemplate')); - return; - } + if (l.type === 'ip_adapter_layer') { + // Must have model + if (!l.ipAdapter.model) { + problems.push(i18n.t('parameters.invoke.layer.ipAdapterNoModelSelected')); + } + // Model base must match + if (l.ipAdapter.model?.base !== model?.base) { + problems.push(i18n.t('parameters.invoke.layer.ipAdapterIncompatibleBaseModel')); + } + // Must have an image + if (!l.ipAdapter.image) { + problems.push(i18n.t('parameters.invoke.layer.ipAdapterNoImageSelected')); + } + } - const connectedEdges = getConnectedEdges([node], nodes.edges); + if (l.type === 'initial_image_layer') { + // Must have an image + if (!l.image) { + problems.push(i18n.t('parameters.invoke.layer.initialImageNoImageSelected')); + } + } - forEach(node.data.inputs, (field) => { - const fieldTemplate = nodeTemplate.inputs[field.name]; - const hasConnection = connectedEdges.some( - (edge) => edge.target === node.id && edge.targetHandle === field.name - ); + if (l.type === 'regional_guidance_layer') { + // Must have a region + if (l.maskObjects.length === 0) { + problems.push(i18n.t('parameters.invoke.layer.rgNoRegion')); + } + // Must have at least 1 prompt or IP Adapter + if (l.positivePrompt === null && l.negativePrompt === null && l.ipAdapters.length === 0) { + problems.push(i18n.t('parameters.invoke.layer.rgNoPromptsOrIPAdapters')); + } + l.ipAdapters.forEach((ipAdapter) => { + // Must have model + if (!ipAdapter.model) { + problems.push(i18n.t('parameters.invoke.layer.ipAdapterNoModelSelected')); + } + // Model base must match + if (ipAdapter.model?.base !== model?.base) { + problems.push(i18n.t('parameters.invoke.layer.ipAdapterIncompatibleBaseModel')); + } + // Must have an image + if (!ipAdapter.image) { + problems.push(i18n.t('parameters.invoke.layer.ipAdapterNoImageSelected')); + } + }); + } - if (!fieldTemplate) { - reasons.push(i18n.t('parameters.invoke.missingFieldTemplate')); - return; - } + if (problems.length) { + const content = upperFirst(problems.join(', ')); + reasons.push({ prefix, content }); + } + }); + } else { + // Handling for all other tabs + selectControlAdapterAll(controlAdapters) + .filter((ca) => ca.isEnabled) + .forEach((ca, i) => { + if (!ca.isEnabled) { + return; + } - if (fieldTemplate.required && field.value === undefined && !hasConnection) { - reasons.push( - i18n.t('parameters.invoke.missingInputForField', { - nodeLabel: node.data.label || nodeTemplate.title, - fieldLabel: field.label || fieldTemplate.title, - }) - ); - return; - } - }); - }); - } - } else { - if (dynamicPrompts.prompts.length === 0 && getShouldProcessPrompt(positivePrompt)) { - reasons.push(i18n.t('parameters.invoke.noPrompts')); + if (!ca.model) { + reasons.push({ content: i18n.t('parameters.invoke.noModelForControlAdapter', { number: i + 1 }) }); + } else if (ca.model.base !== model?.base) { + // This should never happen, just a sanity check + reasons.push({ + content: i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', { number: i + 1 }), + }); + } + + if ( + !ca.controlImage || + (isControlNetOrT2IAdapter(ca) && !ca.processedControlImage && ca.processorType !== 'none') + ) { + reasons.push({ + content: i18n.t('parameters.invoke.noControlImageForControlAdapter', { number: i + 1 }), + }); + } + }); + } } - if (!model) { - reasons.push(i18n.t('parameters.invoke.noModelSelected')); - } - - if (activeTabName === 'generation') { - // Handling for generation tab - controlLayers.present.layers - .filter((l) => l.isEnabled) - .flatMap((l) => { - if (l.type === 'control_adapter_layer') { - return l.controlAdapter; - } else if (l.type === 'ip_adapter_layer') { - return l.ipAdapter; - } else if (l.type === 'regional_guidance_layer') { - return l.ipAdapters; - } - return []; - }) - .forEach((ca, i) => { - const hasNoModel = !ca.model; - const mismatchedModelBase = ca.model?.base !== model?.base; - const hasNoImage = !ca.image; - const imageNotProcessed = - (ca.type === 'controlnet' || ca.type === 't2i_adapter') && !ca.processedImage && ca.processorConfig; - - if (hasNoModel) { - reasons.push( - i18n.t('parameters.invoke.noModelForControlAdapter', { - number: i + 1, - }) - ); - } - if (mismatchedModelBase) { - // This should never happen, just a sanity check - reasons.push( - i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', { - number: i + 1, - }) - ); - } - if (hasNoImage) { - reasons.push( - i18n.t('parameters.invoke.noControlImageForControlAdapter', { - number: i + 1, - }) - ); - } - if (imageNotProcessed) { - reasons.push( - i18n.t('parameters.invoke.imageNotProcessedForControlAdapter', { - number: i + 1, - }) - ); - } - }); - } else { - // Handling for all other tabs - selectControlAdapterAll(controlAdapters) - .filter((ca) => ca.isEnabled) - .forEach((ca, i) => { - if (!ca.isEnabled) { - return; - } - - if (!ca.model) { - reasons.push( - i18n.t('parameters.invoke.noModelForControlAdapter', { - number: i + 1, - }) - ); - } else if (ca.model.base !== model?.base) { - // This should never happen, just a sanity check - reasons.push( - i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', { - number: i + 1, - }) - ); - } - - if ( - !ca.controlImage || - (isControlNetOrT2IAdapter(ca) && !ca.processedControlImage && ca.processorType !== 'none') - ) { - reasons.push( - i18n.t('parameters.invoke.noControlImageForControlAdapter', { - number: i + 1, - }) - ); - } - }); - } + return { isReady: !reasons.length, reasons }; } - - return { isReady: !reasons.length, reasons }; - } -); + ); export const useIsReadyToEnqueue = () => { - const { isReady, reasons } = useAppSelector(selector); - return { isReady, reasons }; + const templates = useStore($templates); + const selector = useMemo(() => createSelector(templates), [templates]); + const value = useAppSelector(selector); + return value; }; 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 15d38b9f76..5ed5ffe573 100644 --- a/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx +++ b/invokeai/frontend/web/src/features/canvas/components/IAICanvasToolbar/IAICanvasToolbar.tsx @@ -21,8 +21,6 @@ import { setShouldShowBoundingBox, } from 'features/canvas/store/canvasSlice'; import type { CanvasLayer } from 'features/canvas/store/canvasTypes'; -import { LAYER_NAMES_DICT } from 'features/canvas/store/canvasTypes'; -import { ViewerButton } from 'features/gallery/components/ImageViewer/ViewerButton'; import { memo, useCallback, useMemo } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; @@ -217,110 +215,107 @@ const IAICanvasToolbar = () => { [dispatch, isMaskEnabled] ); - const value = useMemo(() => LAYER_NAMES_DICT.filter((o) => o.value === layer)[0], [layer]); + const layerOptions = useMemo<{ label: string; value: CanvasLayer }[]>( + () => [ + { label: t('unifiedCanvas.base'), value: 'base' }, + { label: t('unifiedCanvas.mask'), value: 'mask' }, + ], + [t] + ); + const layerValue = useMemo(() => layerOptions.filter((o) => o.value === layer)[0] ?? null, [layer, layerOptions]); return ( - - - - - - - - - - + + + + + + - - + + - - } - isChecked={tool === 'move' || isStaging} - onClick={handleSelectMoveTool} - /> - : } - onClick={handleSetShouldShowBoundingBox} - isDisabled={isStaging} - /> - } - onClick={handleClickResetCanvasView} - /> - + + } + isChecked={tool === 'move' || isStaging} + onClick={handleSelectMoveTool} + /> + : } + onClick={handleSetShouldShowBoundingBox} + isDisabled={isStaging} + /> + } + onClick={handleClickResetCanvasView} + /> + - + + } + onClick={handleMergeVisible} + isDisabled={isStaging} + /> + } + onClick={handleSaveToGallery} + isDisabled={isStaging} + /> + {isClipboardAPIAvailable && ( } - onClick={handleMergeVisible} + aria-label={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`} + tooltip={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`} + icon={} + onClick={handleCopyImageToClipboard} isDisabled={isStaging} /> - } - onClick={handleSaveToGallery} - isDisabled={isStaging} - /> - {isClipboardAPIAvailable && ( - } - onClick={handleCopyImageToClipboard} - isDisabled={isStaging} - /> - )} - } - onClick={handleDownloadAsImage} - isDisabled={isStaging} - /> - - - - - + )} + } + onClick={handleDownloadAsImage} + isDisabled={isStaging} + /> + + + + + - - } - isDisabled={isStaging} - {...getUploadButtonProps()} - /> - - } - onClick={handleResetCanvas} - colorScheme="error" - isDisabled={isStaging} - /> - - - - - - - - - - + + } + isDisabled={isStaging} + {...getUploadButtonProps()} + /> + + } + onClick={handleResetCanvas} + colorScheme="error" + isDisabled={isStaging} + /> + + + + ); }; diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts b/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts index 2d30e18760..c41c6f329f 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasTypes.ts @@ -5,11 +5,6 @@ import { z } from 'zod'; export type CanvasLayer = 'base' | 'mask'; -export const LAYER_NAMES_DICT: { label: string; value: CanvasLayer }[] = [ - { label: 'Base', value: 'base' }, - { label: 'Mask', value: 'mask' }, -]; - const zBoundingBoxScaleMethod = z.enum(['none', 'auto', 'manual']); export type BoundingBoxScaleMethod = z.infer; export const isBoundingBoxScaleMethod = (v: unknown): v is BoundingBoxScaleMethod => diff --git a/invokeai/frontend/web/src/features/controlAdapters/store/types.ts b/invokeai/frontend/web/src/features/controlAdapters/store/types.ts index 7e2f18af5c..b76a729263 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/store/types.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/store/types.ts @@ -5,22 +5,7 @@ import type { ParameterT2IAdapterModel, } from 'features/parameters/types/parameterSchemas'; import type { components } from 'services/api/schema'; -import type { - CannyImageProcessorInvocation, - ColorMapImageProcessorInvocation, - ContentShuffleImageProcessorInvocation, - DepthAnythingImageProcessorInvocation, - DWOpenposeImageProcessorInvocation, - HedImageProcessorInvocation, - LineartAnimeImageProcessorInvocation, - LineartImageProcessorInvocation, - MediapipeFaceProcessorInvocation, - MidasDepthImageProcessorInvocation, - MlsdImageProcessorInvocation, - NormalbaeImageProcessorInvocation, - PidiImageProcessorInvocation, - ZoeDepthImageProcessorInvocation, -} from 'services/api/types'; +import type { Invocation } from 'services/api/types'; import type { O } from 'ts-toolbelt'; import { z } from 'zod'; @@ -28,20 +13,20 @@ import { z } from 'zod'; * Any ControlNet processor node */ export type ControlAdapterProcessorNode = - | CannyImageProcessorInvocation - | ColorMapImageProcessorInvocation - | ContentShuffleImageProcessorInvocation - | DepthAnythingImageProcessorInvocation - | HedImageProcessorInvocation - | LineartAnimeImageProcessorInvocation - | LineartImageProcessorInvocation - | MediapipeFaceProcessorInvocation - | MidasDepthImageProcessorInvocation - | MlsdImageProcessorInvocation - | NormalbaeImageProcessorInvocation - | DWOpenposeImageProcessorInvocation - | PidiImageProcessorInvocation - | ZoeDepthImageProcessorInvocation; + | Invocation<'canny_image_processor'> + | Invocation<'color_map_image_processor'> + | Invocation<'content_shuffle_image_processor'> + | Invocation<'depth_anything_image_processor'> + | Invocation<'hed_image_processor'> + | Invocation<'lineart_anime_image_processor'> + | Invocation<'lineart_image_processor'> + | Invocation<'mediapipe_face_processor'> + | Invocation<'midas_depth_image_processor'> + | Invocation<'mlsd_image_processor'> + | Invocation<'normalbae_image_processor'> + | Invocation<'dw_openpose_image_processor'> + | Invocation<'pidi_image_processor'> + | Invocation<'zoe_depth_image_processor'>; /** * Any ControlNet processor type @@ -71,7 +56,7 @@ export const isControlAdapterProcessorType = (v: unknown): v is ControlAdapterPr * The Canny processor node, with parameters flagged as required */ export type RequiredCannyImageProcessorInvocation = O.Required< - CannyImageProcessorInvocation, + Invocation<'canny_image_processor'>, 'type' | 'low_threshold' | 'high_threshold' | 'image_resolution' | 'detect_resolution' >; @@ -79,7 +64,7 @@ export type RequiredCannyImageProcessorInvocation = O.Required< * The Color Map processor node, with parameters flagged as required */ export type RequiredColorMapImageProcessorInvocation = O.Required< - ColorMapImageProcessorInvocation, + Invocation<'color_map_image_processor'>, 'type' | 'color_map_tile_size' >; @@ -87,7 +72,7 @@ export type RequiredColorMapImageProcessorInvocation = O.Required< * The ContentShuffle processor node, with parameters flagged as required */ export type RequiredContentShuffleImageProcessorInvocation = O.Required< - ContentShuffleImageProcessorInvocation, + Invocation<'content_shuffle_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' | 'w' | 'h' | 'f' >; @@ -95,7 +80,7 @@ export type RequiredContentShuffleImageProcessorInvocation = O.Required< * The DepthAnything processor node, with parameters flagged as required */ export type RequiredDepthAnythingImageProcessorInvocation = O.Required< - DepthAnythingImageProcessorInvocation, + Invocation<'depth_anything_image_processor'>, 'type' | 'model_size' | 'resolution' | 'offload' >; @@ -108,7 +93,7 @@ export const isDepthAnythingModelSize = (v: unknown): v is DepthAnythingModelSiz * The HED processor node, with parameters flagged as required */ export type RequiredHedImageProcessorInvocation = O.Required< - HedImageProcessorInvocation, + Invocation<'hed_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' | 'scribble' >; @@ -116,7 +101,7 @@ export type RequiredHedImageProcessorInvocation = O.Required< * The Lineart Anime processor node, with parameters flagged as required */ export type RequiredLineartAnimeImageProcessorInvocation = O.Required< - LineartAnimeImageProcessorInvocation, + Invocation<'lineart_anime_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' >; @@ -124,7 +109,7 @@ export type RequiredLineartAnimeImageProcessorInvocation = O.Required< * The Lineart processor node, with parameters flagged as required */ export type RequiredLineartImageProcessorInvocation = O.Required< - LineartImageProcessorInvocation, + Invocation<'lineart_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' | 'coarse' >; @@ -132,7 +117,7 @@ export type RequiredLineartImageProcessorInvocation = O.Required< * The MediapipeFace processor node, with parameters flagged as required */ export type RequiredMediapipeFaceProcessorInvocation = O.Required< - MediapipeFaceProcessorInvocation, + Invocation<'mediapipe_face_processor'>, 'type' | 'max_faces' | 'min_confidence' | 'image_resolution' | 'detect_resolution' >; @@ -140,7 +125,7 @@ export type RequiredMediapipeFaceProcessorInvocation = O.Required< * The MidasDepth processor node, with parameters flagged as required */ export type RequiredMidasDepthImageProcessorInvocation = O.Required< - MidasDepthImageProcessorInvocation, + Invocation<'midas_depth_image_processor'>, 'type' | 'a_mult' | 'bg_th' | 'image_resolution' | 'detect_resolution' >; @@ -148,7 +133,7 @@ export type RequiredMidasDepthImageProcessorInvocation = O.Required< * The MLSD processor node, with parameters flagged as required */ export type RequiredMlsdImageProcessorInvocation = O.Required< - MlsdImageProcessorInvocation, + Invocation<'mlsd_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' | 'thr_v' | 'thr_d' >; @@ -156,7 +141,7 @@ export type RequiredMlsdImageProcessorInvocation = O.Required< * The NormalBae processor node, with parameters flagged as required */ export type RequiredNormalbaeImageProcessorInvocation = O.Required< - NormalbaeImageProcessorInvocation, + Invocation<'normalbae_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' >; @@ -164,7 +149,7 @@ export type RequiredNormalbaeImageProcessorInvocation = O.Required< * The DW Openpose processor node, with parameters flagged as required */ export type RequiredDWOpenposeImageProcessorInvocation = O.Required< - DWOpenposeImageProcessorInvocation, + Invocation<'dw_openpose_image_processor'>, 'type' | 'image_resolution' | 'draw_body' | 'draw_face' | 'draw_hands' >; @@ -172,14 +157,14 @@ export type RequiredDWOpenposeImageProcessorInvocation = O.Required< * The Pidi processor node, with parameters flagged as required */ export type RequiredPidiImageProcessorInvocation = O.Required< - PidiImageProcessorInvocation, + Invocation<'pidi_image_processor'>, 'type' | 'detect_resolution' | 'image_resolution' | 'safe' | 'scribble' >; /** * The ZoeDepth processor node, with parameters flagged as required */ -export type RequiredZoeDepthImageProcessorInvocation = O.Required; +export type RequiredZoeDepthImageProcessorInvocation = O.Required, 'type'>; /** * Any ControlNet Processor node, with its parameters flagged as required diff --git a/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx index 3102e4afa8..c7a49da8c7 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/AddLayerButton.tsx @@ -18,7 +18,12 @@ export const AddLayerButton = memo(() => { return ( - } variant="ghost"> + } + variant="ghost" + data-testid="control-layers-add-layer-menu-button" + > {t('controlLayers.addLayer')} diff --git a/invokeai/frontend/web/src/features/controlLayers/components/CALayer/CALayer.tsx b/invokeai/frontend/web/src/features/controlLayers/components/CALayer/CALayer.tsx index 984331a050..36509ec1d3 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/CALayer/CALayer.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/CALayer/CALayer.tsx @@ -19,7 +19,6 @@ export const CALayer = memo(({ layerId }: Props) => { const dispatch = useAppDispatch(); const isSelected = useAppSelector((s) => selectCALayerOrThrow(s.controlLayers.present, layerId).isSelected); const onClick = useCallback(() => { - // Must be capture so that the layer is selected before deleting/resetting/etc dispatch(layerSelected(layerId)); }, [dispatch, layerId]); const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true }); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx index e6c6aae286..4d93eb12ec 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/ControlAdapterImagePreview.tsx @@ -42,10 +42,10 @@ export const ControlAdapterImagePreview = memo( const [isMouseOverImage, setIsMouseOverImage] = useState(false); const { currentData: controlImage, isError: isErrorControlImage } = useGetImageDTOQuery( - controlAdapter.image?.imageName ?? skipToken + controlAdapter.image?.name ?? skipToken ); const { currentData: processedControlImage, isError: isErrorProcessedControlImage } = useGetImageDTOQuery( - controlAdapter.processedImage?.imageName ?? skipToken + controlAdapter.processedImage?.name ?? skipToken ); const [changeIsIntermediate] = useChangeImageIsIntermediateMutation(); @@ -124,7 +124,7 @@ export const ControlAdapterImagePreview = memo( controlImage && processedControlImage && !isMouseOverImage && - !controlAdapter.isProcessingImage && + !controlAdapter.processorPendingBatchId && controlAdapter.processorConfig !== null; useEffect(() => { @@ -190,7 +190,7 @@ export const ControlAdapterImagePreview = memo( /> - {controlAdapter.isProcessingImage && ( + {controlAdapter.processorPendingBatchId !== null && ( { onChangeImage(null); }, [onChangeImage]); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/processors/DepthAnythingProcessor.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/processors/DepthAnythingProcessor.tsx index 00993789b1..b5da990b6f 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/processors/DepthAnythingProcessor.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlAndIPAdapter/processors/DepthAnythingProcessor.tsx @@ -2,14 +2,13 @@ import type { ComboboxOnChange } from '@invoke-ai/ui-library'; import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types'; import type { DepthAnythingModelSize, DepthAnythingProcessorConfig } from 'features/controlLayers/util/controlAdapters'; -import { CA_PROCESSOR_DATA, isDepthAnythingModelSize } from 'features/controlLayers/util/controlAdapters'; +import { isDepthAnythingModelSize } from 'features/controlLayers/util/controlAdapters'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import ProcessorWrapper from './ProcessorWrapper'; type Props = ProcessorComponentProps; -const DEFAULTS = CA_PROCESSOR_DATA['depth_anything_image_processor'].buildDefaults(); export const DepthAnythingProcessor = memo(({ onChange, config }: Props) => { const { t } = useTranslation(); @@ -38,12 +37,7 @@ export const DepthAnythingProcessor = memo(({ onChange, config }: Props) => { {t('controlnet.modelSize')} - + ); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersPanelContent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersPanelContent.tsx index 1dd79d0220..d3ddc07139 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersPanelContent.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersPanelContent.tsx @@ -32,7 +32,7 @@ export const ControlLayersPanelContent = memo(() => { {layerIdTypePairs.length > 0 && ( - + {layerIdTypePairs.map(({ id, type }) => ( ))} diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx index 89032b7c76..1f3307bdf1 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersSettingsPopover.tsx @@ -1,12 +1,30 @@ -import { Flex, IconButton, Popover, PopoverBody, PopoverContent, PopoverTrigger } from '@invoke-ai/ui-library'; +import { + Checkbox, + Flex, + FormControl, + FormLabel, + IconButton, + Popover, + PopoverBody, + PopoverContent, + PopoverTrigger, +} from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { setShouldInvertBrushSizeScrollDirection } from 'features/canvas/store/canvasSlice'; import { GlobalMaskLayerOpacity } from 'features/controlLayers/components/GlobalMaskLayerOpacity'; -import { memo } from 'react'; +import type { ChangeEvent } from 'react'; +import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { RiSettings4Fill } from 'react-icons/ri'; const ControlLayersSettingsPopover = () => { const { t } = useTranslation(); - + const dispatch = useAppDispatch(); + const shouldInvertBrushSizeScrollDirection = useAppSelector((s) => s.canvas.shouldInvertBrushSizeScrollDirection); + const handleChangeShouldInvertBrushSizeScrollDirection = useCallback( + (e: ChangeEvent) => dispatch(setShouldInvertBrushSizeScrollDirection(e.target.checked)), + [dispatch] + ); return ( @@ -16,6 +34,13 @@ const ControlLayersSettingsPopover = () => { + + {t('unifiedCanvas.invertBrushSizeScrollDirection')} + + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx index b78910700d..8cc3aa93fe 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/ControlLayersToolbar.tsx @@ -4,14 +4,17 @@ import { BrushSize } from 'features/controlLayers/components/BrushSize'; import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover'; import { ToolChooser } from 'features/controlLayers/components/ToolChooser'; import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup'; -import { ViewerButton } from 'features/gallery/components/ImageViewer/ViewerButton'; +import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton'; +import { ViewerToggleMenu } from 'features/gallery/components/ImageViewer/ViewerToggleMenu'; import { memo } from 'react'; export const ControlLayersToolbar = memo(() => { return ( - + + + @@ -21,7 +24,7 @@ export const ControlLayersToolbar = memo(() => { - + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx b/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx index dad102b470..00487fcc43 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/DeleteAllLayersButton.tsx @@ -20,6 +20,7 @@ export const DeleteAllLayersButton = memo(() => { variant="ghost" colorScheme="error" isDisabled={isDisabled} + data-testid="control-layers-delete-all-layers-button" > {t('controlLayers.deleteAll')} diff --git a/invokeai/frontend/web/src/features/controlLayers/components/IILayer/IILayer.tsx b/invokeai/frontend/web/src/features/controlLayers/components/IILayer/IILayer.tsx index 772dbd7332..c6efd041ca 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/IILayer/IILayer.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/IILayer/IILayer.tsx @@ -8,6 +8,7 @@ import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerT import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle'; import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper'; import { + iiLayerDenoisingStrengthChanged, iiLayerImageChanged, layerSelected, selectIILayerOrThrow, @@ -36,6 +37,13 @@ export const IILayer = memo(({ layerId }: Props) => { [dispatch, layerId] ); + const onChangeDenoisingStrength = useCallback( + (denoisingStrength: number) => { + dispatch(iiLayerDenoisingStrengthChanged({ layerId, denoisingStrength })); + }, + [dispatch, layerId] + ); + const droppableData = useMemo( () => ({ actionType: 'SET_II_LAYER_IMAGE', @@ -67,7 +75,7 @@ export const IILayer = memo(({ layerId }: Props) => { {isOpen && ( - + { onChangeImage(null); diff --git a/invokeai/frontend/web/src/features/controlLayers/components/IPALayer/IPALayer.tsx b/invokeai/frontend/web/src/features/controlLayers/components/IPALayer/IPALayer.tsx index 02a161608d..2077700104 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/IPALayer/IPALayer.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/IPALayer/IPALayer.tsx @@ -1,19 +1,26 @@ import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { IPALayerIPAdapterWrapper } from 'features/controlLayers/components/IPALayer/IPALayerIPAdapterWrapper'; import { LayerDeleteButton } from 'features/controlLayers/components/LayerCommon/LayerDeleteButton'; import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle'; import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle'; import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper'; -import { memo } from 'react'; +import { layerSelected, selectIPALayerOrThrow } from 'features/controlLayers/store/controlLayersSlice'; +import { memo, useCallback } from 'react'; type Props = { layerId: string; }; export const IPALayer = memo(({ layerId }: Props) => { + const dispatch = useAppDispatch(); + const isSelected = useAppSelector((s) => selectIPALayerOrThrow(s.controlLayers.present, layerId).isSelected); const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true }); + const onClick = useCallback(() => { + dispatch(layerSelected(layerId)); + }, [dispatch, layerId]); return ( - + diff --git a/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx b/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx index 9d5fb6ea4b..9757cf3972 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/LayerCommon/LayerWrapper.tsx @@ -10,7 +10,16 @@ type Props = PropsWithChildren<{ export const LayerWrapper = memo(({ onClick, borderColor, children }: Props) => { return ( - + {children} diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerNegativePrompt.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerNegativePrompt.tsx index ba02aa9242..ce02811ebf 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerNegativePrompt.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerNegativePrompt.tsx @@ -45,6 +45,7 @@ export const RGLayerNegativePrompt = memo(({ layerId }: Props) => { variant="darkFilled" paddingRight={30} fontSize="sm" + spellCheck={false} /> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerPositivePrompt.tsx b/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerPositivePrompt.tsx index 6f85ea077c..56d3953e25 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerPositivePrompt.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/RGLayer/RGLayerPositivePrompt.tsx @@ -45,6 +45,7 @@ export const RGLayerPositivePrompt = memo(({ layerId }: Props) => { variant="darkFilled" paddingRight={30} minH={28} + spellCheck={false} /> diff --git a/invokeai/frontend/web/src/features/controlLayers/components/StageComponent.tsx b/invokeai/frontend/web/src/features/controlLayers/components/StageComponent.tsx index d0d693a5f2..08956e73dc 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/StageComponent.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/StageComponent.tsx @@ -130,11 +130,11 @@ const useStageRenderer = ( }, [stage, state.size.width, state.size.height, wrapper]); useLayoutEffect(() => { - log.trace('Rendering tool preview'); if (asPreview) { // Preview should not display tool return; } + log.trace('Rendering tool preview'); renderers.renderToolPreview( stage, tool, @@ -178,15 +178,24 @@ const useStageRenderer = ( // Preview should not display bboxes return; } - renderers.renderBbox(stage, state.layers, tool, onBboxChanged); + renderers.renderBboxes(stage, state.layers, tool); }, [stage, asPreview, state.layers, tool, onBboxChanged, renderers]); useLayoutEffect(() => { - log.trace('Rendering background'); + if (asPreview) { + // Preview should not check for transparency + return; + } + log.trace('Updating bboxes'); + debouncedRenderers.updateBboxes(stage, state.layers, onBboxChanged); + }, [stage, asPreview, state.layers, onBboxChanged]); + + useLayoutEffect(() => { if (asPreview) { // The preview should not have a background return; } + log.trace('Rendering background'); renderers.renderBackground(stage, state.size.width, state.size.height); }, [stage, asPreview, state.size.width, state.size.height, renderers]); @@ -196,11 +205,11 @@ const useStageRenderer = ( }, [stage, layerIds, renderers]); useLayoutEffect(() => { - log.trace('Rendering no layers message'); if (asPreview) { // The preview should not display the no layers message return; } + log.trace('Rendering no layers message'); renderers.renderNoLayersMessage(stage, layerCount, state.size.width, state.size.height); }, [stage, layerCount, renderers, asPreview, state.size.width, state.size.height]); @@ -233,7 +242,14 @@ export const StageComponent = memo(({ asPreview = false }: Props) => { return ( - + ); diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/mouseEventHooks.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/mouseEventHooks.ts index 8f69c165ca..514e8c35ff 100644 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/mouseEventHooks.ts +++ b/invokeai/frontend/web/src/features/controlLayers/hooks/mouseEventHooks.ts @@ -188,15 +188,15 @@ export const useMouseEvents = () => { return; } const pos = syncCursorPos(stage); + $isDrawing.set(false); + $lastCursorPos.set(null); + $lastMouseDownPos.set(null); if (!pos || !selectedLayerId || selectedLayerType !== 'regional_guidance_layer') { return; } if (getIsFocused(stage) && getIsMouseDown(e) && (tool === 'brush' || tool === 'eraser')) { dispatch(rgLayerPointsAdded({ layerId: selectedLayerId, point: [pos.x, pos.y] })); } - $isDrawing.set(false); - $lastCursorPos.set(null); - $lastMouseDownPos.set(null); }, [selectedLayerId, selectedLayerType, tool, dispatch] ); @@ -224,5 +224,10 @@ export const useMouseEvents = () => { [selectedLayerType, tool, shouldInvertBrushSizeScrollDirection, dispatch, brushSize] ); - return { onMouseDown, onMouseUp, onMouseMove, onMouseLeave, onMouseWheel }; + const handlers = useMemo( + () => ({ onMouseDown, onMouseUp, onMouseMove, onMouseLeave, onMouseWheel }), + [onMouseDown, onMouseUp, onMouseMove, onMouseLeave, onMouseWheel] + ); + + return handlers; }; diff --git a/invokeai/frontend/web/src/features/controlLayers/hooks/useControlLayersTitle.ts b/invokeai/frontend/web/src/features/controlLayers/hooks/useControlLayersTitle.ts deleted file mode 100644 index bf0fa661a9..0000000000 --- a/invokeai/frontend/web/src/features/controlLayers/hooks/useControlLayersTitle.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppSelector } from 'app/store/storeHooks'; -import { - isControlAdapterLayer, - isInitialImageLayer, - isIPAdapterLayer, - isRegionalGuidanceLayer, - selectControlLayersSlice, -} from 'features/controlLayers/store/controlLayersSlice'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; - -const selectValidLayerCount = createSelector(selectControlLayersSlice, (controlLayers) => { - let count = 0; - controlLayers.present.layers.forEach((l) => { - if (isRegionalGuidanceLayer(l)) { - const hasTextPrompt = Boolean(l.positivePrompt || l.negativePrompt); - const hasAtLeastOneImagePrompt = l.ipAdapters.filter((ipa) => Boolean(ipa.image)).length > 0; - if (hasTextPrompt || hasAtLeastOneImagePrompt) { - count += 1; - } - } - if (isControlAdapterLayer(l)) { - if (l.controlAdapter.image || l.controlAdapter.processedImage) { - count += 1; - } - } - if (isIPAdapterLayer(l)) { - if (l.ipAdapter.image) { - count += 1; - } - } - if (isInitialImageLayer(l)) { - if (l.image) { - count += 1; - } - } - }); - - return count; -}); - -export const useControlLayersTitle = () => { - const { t } = useTranslation(); - const validLayerCount = useAppSelector(selectValidLayerCount); - const title = useMemo(() => { - const suffix = validLayerCount > 0 ? ` (${validLayerCount})` : ''; - return `${t('controlLayers.controlLayers')}${suffix}`; - }, [t, validLayerCount]); - return title; -}; diff --git a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts index 1ef90ead3a..32e29918ae 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/controlLayersSlice.ts @@ -27,7 +27,7 @@ import { modelChanged } from 'features/parameters/store/generationSlice'; import type { ParameterAutoNegative } from 'features/parameters/types/parameterSchemas'; import { getIsSizeOptimal, getOptimalDimension } from 'features/parameters/util/optimalDimension'; import type { IRect, Vector2d } from 'konva/lib/types'; -import { isEqual, partition } from 'lodash-es'; +import { isEqual, partition, unset } from 'lodash-es'; import { atom } from 'nanostores'; import type { RgbColor } from 'react-colorful'; import type { UndoableOptions } from 'redux-undo'; @@ -49,7 +49,7 @@ import type { } from './types'; export const initialControlLayersState: ControlLayersState = { - _version: 1, + _version: 3, selectedLayerId: null, brushSize: 100, layers: [], @@ -124,6 +124,12 @@ const getVectorMaskPreviewColor = (state: ControlLayersState): RgbColor => { const lastColor = rgLayers[rgLayers.length - 1]?.previewColor; return LayerColors.next(lastColor); }; +const exclusivelySelectLayer = (state: ControlLayersState, layerId: string) => { + for (const layer of state.layers) { + layer.isSelected = layer.id === layerId; + } + state.selectedLayerId = layerId; +}; export const controlLayersSlice = createSlice({ name: 'controlLayers', @@ -131,14 +137,7 @@ export const controlLayersSlice = createSlice({ reducers: { //#region Any Layer Type layerSelected: (state, action: PayloadAction) => { - for (const layer of state.layers.filter(isRenderableLayer)) { - if (layer.id === action.payload) { - layer.isSelected = true; - state.selectedLayerId = action.payload; - } else { - layer.isSelected = false; - } - } + exclusivelySelectLayer(state, action.payload); }, layerVisibilityToggled: (state, action: PayloadAction) => { const layer = state.layers.find((l) => l.id === action.payload); @@ -167,7 +166,6 @@ export const controlLayersSlice = createSlice({ // The layer was fully erased, empty its objects to prevent accumulation of invisible objects layer.maskObjects = []; layer.uploadedMaskImage = null; - layer.needsPixelBbox = false; } } }, @@ -178,7 +176,6 @@ export const controlLayersSlice = createSlice({ layer.maskObjects = []; layer.bbox = null; layer.isEnabled = true; - layer.needsPixelBbox = false; layer.bboxNeedsUpdate = false; layer.uploadedMaskImage = null; } @@ -244,17 +241,16 @@ export const controlLayersSlice = createSlice({ controlAdapter, }; state.layers.push(layer); - state.selectedLayerId = layer.id; - for (const layer of state.layers.filter(isRenderableLayer)) { - if (layer.id !== layerId) { - layer.isSelected = false; - } - } + exclusivelySelectLayer(state, layer.id); }, prepare: (controlAdapter: ControlNetConfigV2 | T2IAdapterConfigV2) => ({ payload: { layerId: uuidv4(), controlAdapter }, }), }, + caLayerRecalled: (state, action: PayloadAction) => { + state.layers.push({ ...action.payload, isSelected: true }); + exclusivelySelectLayer(state, action.payload.id); + }, caLayerImageChanged: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => { const { layerId, imageDTO } = action.payload; const layer = selectCALayerOrThrow(state, layerId); @@ -338,19 +334,13 @@ export const controlLayersSlice = createSlice({ const layer = selectCALayerOrThrow(state, layerId); layer.opacity = opacity; }, - caLayerIsProcessingImageChanged: ( + caLayerProcessorPendingBatchIdChanged: ( state, - action: PayloadAction<{ layerId: string; isProcessingImage: boolean }> + action: PayloadAction<{ layerId: string; batchId: string | null }> ) => { - const { layerId, isProcessingImage } = action.payload; + const { layerId, batchId } = action.payload; const layer = selectCALayerOrThrow(state, layerId); - layer.controlAdapter.isProcessingImage = isProcessingImage; - }, - caLayerControlNetsDeleted: (state) => { - state.layers = state.layers.filter((l) => !isControlAdapterLayer(l) || l.controlAdapter.type !== 'controlnet'); - }, - caLayerT2IAdaptersDeleted: (state) => { - state.layers = state.layers.filter((l) => !isControlAdapterLayer(l) || l.controlAdapter.type !== 't2i_adapter'); + layer.controlAdapter.processorPendingBatchId = batchId; }, //#endregion @@ -362,12 +352,17 @@ export const controlLayersSlice = createSlice({ id: getIPALayerId(layerId), type: 'ip_adapter_layer', isEnabled: true, + isSelected: true, ipAdapter, }; state.layers.push(layer); + exclusivelySelectLayer(state, layer.id); }, prepare: (ipAdapter: IPAdapterConfigV2) => ({ payload: { layerId: uuidv4(), ipAdapter } }), }, + ipaLayerRecalled: (state, action: PayloadAction) => { + state.layers.push(action.payload); + }, ipaLayerImageChanged: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => { const { layerId, imageDTO } = action.payload; const layer = selectIPALayerOrThrow(state, layerId); @@ -401,9 +396,6 @@ export const controlLayersSlice = createSlice({ const layer = selectIPALayerOrThrow(state, layerId); layer.ipAdapter.clipVisionModel = clipVisionModel; }, - ipaLayersDeleted: (state) => { - state.layers = state.layers.filter((l) => !isIPAdapterLayer(l)); - }, //#endregion //#region CA or IPA Layers @@ -445,7 +437,6 @@ export const controlLayersSlice = createSlice({ x: 0, y: 0, autoNegative: 'invert', - needsPixelBbox: false, positivePrompt: '', negativePrompt: null, ipAdapters: [], @@ -453,15 +444,14 @@ export const controlLayersSlice = createSlice({ uploadedMaskImage: null, }; state.layers.push(layer); - state.selectedLayerId = layer.id; - for (const layer of state.layers.filter(isRenderableLayer)) { - if (layer.id !== layerId) { - layer.isSelected = false; - } - } + exclusivelySelectLayer(state, layer.id); }, prepare: () => ({ payload: { layerId: uuidv4() } }), }, + rgLayerRecalled: (state, action: PayloadAction) => { + state.layers.push({ ...action.payload, isSelected: true }); + exclusivelySelectLayer(state, action.payload.id); + }, rgLayerPositivePromptChanged: (state, action: PayloadAction<{ layerId: string; prompt: string | null }>) => { const { layerId, prompt } = action.payload; const layer = selectRGLayerOrThrow(state, layerId); @@ -501,9 +491,6 @@ export const controlLayersSlice = createSlice({ }); layer.bboxNeedsUpdate = true; layer.uploadedMaskImage = null; - if (!layer.needsPixelBbox && tool === 'eraser') { - layer.needsPixelBbox = true; - } }, prepare: (payload: { layerId: string; points: [number, number, number, number]; tool: DrawingTool }) => ({ payload: { ...payload, lineUuid: uuidv4() }, @@ -642,16 +629,17 @@ export const controlLayersSlice = createSlice({ isEnabled: true, image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, isSelected: true, + denoisingStrength: 0.75, }; state.layers.push(layer); - state.selectedLayerId = layer.id; - for (const layer of state.layers.filter(isRenderableLayer)) { - if (layer.id !== layerId) { - layer.isSelected = false; - } - } + exclusivelySelectLayer(state, layer.id); }, - prepare: (imageDTO: ImageDTO | null) => ({ payload: { layerId: 'initial_image_layer', imageDTO } }), + prepare: (imageDTO: ImageDTO | null) => ({ payload: { layerId: INITIAL_IMAGE_LAYER_ID, imageDTO } }), + }, + iiLayerRecalled: (state, action: PayloadAction) => { + state.layers = state.layers.filter((l) => (isInitialImageLayer(l) ? false : true)); + state.layers.push({ ...action.payload, isSelected: true }); + exclusivelySelectLayer(state, action.payload.id); }, iiLayerImageChanged: (state, action: PayloadAction<{ layerId: string; imageDTO: ImageDTO | null }>) => { const { layerId, imageDTO } = action.payload; @@ -666,6 +654,11 @@ export const controlLayersSlice = createSlice({ const layer = selectIILayerOrThrow(state, layerId); layer.opacity = opacity; }, + iiLayerDenoisingStrengthChanged: (state, action: PayloadAction<{ layerId: string; denoisingStrength: number }>) => { + const { layerId, denoisingStrength } = action.payload; + const layer = selectIILayerOrThrow(state, layerId); + layer.denoisingStrength = denoisingStrength; + }, //#endregion //#region Globals @@ -799,6 +792,7 @@ export const { allLayersDeleted, // CA Layers caLayerAdded, + caLayerRecalled, caLayerImageChanged, caLayerProcessedImageChanged, caLayerModelChanged, @@ -806,21 +800,20 @@ export const { caLayerProcessorConfigChanged, caLayerIsFilterEnabledChanged, caLayerOpacityChanged, - caLayerIsProcessingImageChanged, - caLayerControlNetsDeleted, - caLayerT2IAdaptersDeleted, + caLayerProcessorPendingBatchIdChanged, // IPA Layers ipaLayerAdded, + ipaLayerRecalled, ipaLayerImageChanged, ipaLayerMethodChanged, ipaLayerModelChanged, ipaLayerCLIPVisionModelChanged, - ipaLayersDeleted, // CA or IPA Layers caOrIPALayerWeightChanged, caOrIPALayerBeginEndStepPctChanged, // RG Layers rgLayerAdded, + rgLayerRecalled, rgLayerPositivePromptChanged, rgLayerNegativePromptChanged, rgLayerPreviewColorChanged, @@ -839,8 +832,10 @@ export const { rgLayerIPAdapterCLIPVisionModelChanged, // II Layer iiLayerAdded, + iiLayerRecalled, iiLayerImageChanged, iiLayerOpacityChanged, + iiLayerDenoisingStrengthChanged, // Globals positivePromptChanged, negativePromptChanged, @@ -860,6 +855,19 @@ export const selectControlLayersSlice = (state: RootState) => state.controlLayer /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ const migrateControlLayersState = (state: any): any => { + if (state._version === 1) { + // Reset state for users on v1 (e.g. beta users), some changes could cause + state = deepClone(initialControlLayersState); + } + if (state._version === 2) { + // The CA `isProcessingImage` flag was replaced with a `processorPendingBatchId` property, fix up CA layers + for (const layer of (state as ControlLayersState).layers) { + if (layer.type === 'control_adapter_layer') { + layer.controlAdapter.processorPendingBatchId = null; + unset(layer.controlAdapter, 'isProcessingImage'); + } + } + } return state; }; @@ -886,21 +894,22 @@ export const RG_LAYER_NAME = 'regional_guidance_layer'; export const RG_LAYER_LINE_NAME = 'regional_guidance_layer.line'; export const RG_LAYER_OBJECT_GROUP_NAME = 'regional_guidance_layer.object_group'; export const RG_LAYER_RECT_NAME = 'regional_guidance_layer.rect'; +export const INITIAL_IMAGE_LAYER_ID = 'singleton_initial_image_layer'; export const INITIAL_IMAGE_LAYER_NAME = 'initial_image_layer'; export const INITIAL_IMAGE_LAYER_IMAGE_NAME = 'initial_image_layer.image'; export const LAYER_BBOX_NAME = 'layer.bbox'; export const COMPOSITING_RECT_NAME = 'compositing-rect'; // Getters for non-singleton layer and object IDs -const getRGLayerId = (layerId: string) => `${RG_LAYER_NAME}_${layerId}`; +export const getRGLayerId = (layerId: string) => `${RG_LAYER_NAME}_${layerId}`; const getRGLayerLineId = (layerId: string, lineId: string) => `${layerId}.line_${lineId}`; const getRGLayerRectId = (layerId: string, lineId: string) => `${layerId}.rect_${lineId}`; export const getRGLayerObjectGroupId = (layerId: string, groupId: string) => `${layerId}.objectGroup_${groupId}`; export const getLayerBboxId = (layerId: string) => `${layerId}.bbox`; -const getCALayerId = (layerId: string) => `control_adapter_layer_${layerId}`; +export const getCALayerId = (layerId: string) => `control_adapter_layer_${layerId}`; export const getCALayerImageId = (layerId: string, imageName: string) => `${layerId}.image_${imageName}`; export const getIILayerImageId = (layerId: string, imageName: string) => `${layerId}.image_${imageName}`; -const getIPALayerId = (layerId: string) => `ip_adapter_layer_${layerId}`; +export const getIPALayerId = (layerId: string) => `ip_adapter_layer_${layerId}`; export const controlLayersPersistConfig: PersistConfig = { name: controlLayersSlice.name, diff --git a/invokeai/frontend/web/src/features/controlLayers/store/types.ts b/invokeai/frontend/web/src/features/controlLayers/store/types.ts index afb04aae37..771e5060e1 100644 --- a/invokeai/frontend/web/src/features/controlLayers/store/types.ts +++ b/invokeai/frontend/web/src/features/controlLayers/store/types.ts @@ -1,90 +1,119 @@ -import type { - ControlNetConfigV2, - ImageWithDims, - IPAdapterConfigV2, - T2IAdapterConfigV2, +import { + zControlNetConfigV2, + zImageWithDims, + zIPAdapterConfigV2, + zT2IAdapterConfigV2, } from 'features/controlLayers/util/controlAdapters'; import type { AspectRatioState } from 'features/parameters/components/ImageSize/types'; -import type { - ParameterAutoNegative, - ParameterHeight, - ParameterNegativePrompt, - ParameterNegativeStylePromptSDXL, - ParameterPositivePrompt, - ParameterPositiveStylePromptSDXL, - ParameterWidth, +import { + type ParameterHeight, + type ParameterNegativePrompt, + type ParameterNegativeStylePromptSDXL, + type ParameterPositivePrompt, + type ParameterPositiveStylePromptSDXL, + type ParameterWidth, + zAutoNegative, + zParameterNegativePrompt, + zParameterPositivePrompt, + zParameterStrength, } from 'features/parameters/types/parameterSchemas'; -import type { IRect } from 'konva/lib/types'; -import type { RgbColor } from 'react-colorful'; +import { z } from 'zod'; -export type DrawingTool = 'brush' | 'eraser'; +const zTool = z.enum(['brush', 'eraser', 'move', 'rect']); +export type Tool = z.infer; +const zDrawingTool = zTool.extract(['brush', 'eraser']); +export type DrawingTool = z.infer; -export type Tool = DrawingTool | 'move' | 'rect'; +const zPoints = z.array(z.number()).refine((points) => points.length % 2 === 0, { + message: 'Must have an even number of points', +}); +const zVectorMaskLine = z.object({ + id: z.string(), + type: z.literal('vector_mask_line'), + tool: zDrawingTool, + strokeWidth: z.number().min(1), + points: zPoints, +}); +export type VectorMaskLine = z.infer; -export type VectorMaskLine = { - id: string; - type: 'vector_mask_line'; - tool: DrawingTool; - strokeWidth: number; - points: number[]; -}; +const zVectorMaskRect = z.object({ + id: z.string(), + type: z.literal('vector_mask_rect'), + x: z.number(), + y: z.number(), + width: z.number().min(1), + height: z.number().min(1), +}); +export type VectorMaskRect = z.infer; -export type VectorMaskRect = { - id: string; - type: 'vector_mask_rect'; - x: number; - y: number; - width: number; - height: number; -}; +const zLayerBase = z.object({ + id: z.string(), + isEnabled: z.boolean().default(true), + isSelected: z.boolean().default(true), +}); -type LayerBase = { - id: string; - isEnabled: boolean; -}; +const zRect = z.object({ + x: z.number(), + y: z.number(), + width: z.number().min(1), + height: z.number().min(1), +}); +const zRenderableLayerBase = zLayerBase.extend({ + x: z.number(), + y: z.number(), + bbox: zRect.nullable(), + bboxNeedsUpdate: z.boolean(), +}); -type RenderableLayerBase = LayerBase & { - x: number; - y: number; - bbox: IRect | null; - bboxNeedsUpdate: boolean; - isSelected: boolean; -}; +const zControlAdapterLayer = zRenderableLayerBase.extend({ + type: z.literal('control_adapter_layer'), + opacity: z.number().gte(0).lte(1), + isFilterEnabled: z.boolean(), + controlAdapter: z.discriminatedUnion('type', [zControlNetConfigV2, zT2IAdapterConfigV2]), +}); +export type ControlAdapterLayer = z.infer; -export type ControlAdapterLayer = RenderableLayerBase & { - type: 'control_adapter_layer'; // technically, also t2i adapter layer - opacity: number; - isFilterEnabled: boolean; - controlAdapter: ControlNetConfigV2 | T2IAdapterConfigV2; -}; +const zIPAdapterLayer = zLayerBase.extend({ + type: z.literal('ip_adapter_layer'), + ipAdapter: zIPAdapterConfigV2, +}); +export type IPAdapterLayer = z.infer; -export type IPAdapterLayer = LayerBase & { - type: 'ip_adapter_layer'; - ipAdapter: IPAdapterConfigV2; -}; +const zRgbColor = z.object({ + r: z.number().int().min(0).max(255), + g: z.number().int().min(0).max(255), + b: z.number().int().min(0).max(255), +}); +const zRegionalGuidanceLayer = zRenderableLayerBase.extend({ + type: z.literal('regional_guidance_layer'), + maskObjects: z.array(z.discriminatedUnion('type', [zVectorMaskLine, zVectorMaskRect])), + positivePrompt: zParameterPositivePrompt.nullable(), + negativePrompt: zParameterNegativePrompt.nullable(), + ipAdapters: z.array(zIPAdapterConfigV2), + previewColor: zRgbColor, + autoNegative: zAutoNegative, + uploadedMaskImage: zImageWithDims.nullable(), +}); +export type RegionalGuidanceLayer = z.infer; -export type RegionalGuidanceLayer = RenderableLayerBase & { - type: 'regional_guidance_layer'; - maskObjects: (VectorMaskLine | VectorMaskRect)[]; - positivePrompt: ParameterPositivePrompt | null; - negativePrompt: ParameterNegativePrompt | null; // Up to one text prompt per mask - ipAdapters: IPAdapterConfigV2[]; // Any number of image prompts - previewColor: RgbColor; - autoNegative: ParameterAutoNegative; - needsPixelBbox: boolean; // Needs the slower pixel-based bbox calculation - set to true when an there is an eraser object - uploadedMaskImage: ImageWithDims | null; -}; +const zInitialImageLayer = zRenderableLayerBase.extend({ + type: z.literal('initial_image_layer'), + opacity: z.number().gte(0).lte(1), + image: zImageWithDims.nullable(), + denoisingStrength: zParameterStrength, +}); +export type InitialImageLayer = z.infer; -export type InitialImageLayer = RenderableLayerBase & { - type: 'initial_image_layer'; - opacity: number; - image: ImageWithDims | null; -}; - -export type Layer = RegionalGuidanceLayer | ControlAdapterLayer | IPAdapterLayer | InitialImageLayer; +export const zLayer = z.discriminatedUnion('type', [ + zRegionalGuidanceLayer, + zControlAdapterLayer, + zIPAdapterLayer, + zInitialImageLayer, +]); +export type Layer = z.infer; export type ControlLayersState = { - _version: 1; + _version: 3; selectedLayerId: string | null; layers: Layer[]; brushSize: number; diff --git a/invokeai/frontend/web/src/features/controlLayers/util/bbox.ts b/invokeai/frontend/web/src/features/controlLayers/util/bbox.ts index 72aefe1eb4..3b037863c9 100644 --- a/invokeai/frontend/web/src/features/controlLayers/util/bbox.ts +++ b/invokeai/frontend/web/src/features/controlLayers/util/bbox.ts @@ -2,7 +2,6 @@ import openBase64ImageInTab from 'common/util/openBase64ImageInTab'; import { imageDataToDataURL } from 'features/canvas/util/blobToDataURL'; import { RG_LAYER_OBJECT_GROUP_NAME } from 'features/controlLayers/store/controlLayersSlice'; import Konva from 'konva'; -import type { Layer as KonvaLayerType } from 'konva/lib/Layer'; import type { IRect } from 'konva/lib/types'; import { assert } from 'tsafe'; @@ -54,34 +53,30 @@ const getImageDataBbox = (imageData: ImageData): Extents | null => { }; /** - * Get the bounding box of a regional prompt konva layer. This function has special handling for regional prompt layers. - * @param layer The konva layer to get the bounding box of. - * @param preview Whether to open a new tab displaying the rendered layer, which is used to calculate the bbox. + * Clones a regional guidance konva layer onto an offscreen stage/canvas. This allows the pixel data for a given layer + * to be captured, manipulated or analyzed without interference from other layers. + * @param layer The konva layer to clone. + * @returns The cloned stage and layer. */ -export const getLayerBboxPixels = (layer: KonvaLayerType, preview: boolean = false): IRect | null => { - // To calculate the layer's bounding box, we must first export it to a pixel array, then do some math. - // - // Though it is relatively fast, we can't use Konva's `getClientRect`. It programmatically determines the rect - // by calculating the extents of individual shapes from their "vector" shape data. - // - // This doesn't work when some shapes are drawn with composite operations that "erase" pixels, like eraser lines. - // These shapes' extents are still calculated as if they were solid, leading to a bounding box that is too large. +const getIsolatedRGLayerClone = (layer: Konva.Layer): { stageClone: Konva.Stage; layerClone: Konva.Layer } => { const stage = layer.getStage(); - // Construct and offscreen canvas on which we will do the bbox calculations. + // Construct an offscreen canvas with the same dimensions as the layer's stage. const offscreenStageContainer = document.createElement('div'); - const offscreenStage = new Konva.Stage({ + const stageClone = new Konva.Stage({ container: offscreenStageContainer, + x: stage.x(), + y: stage.y(), width: stage.width(), height: stage.height(), }); // Clone the layer and filter out unwanted children. const layerClone = layer.clone(); - offscreenStage.add(layerClone); + stageClone.add(layerClone); for (const child of layerClone.getChildren()) { - if (child.name() === RG_LAYER_OBJECT_GROUP_NAME) { + if (child.name() === RG_LAYER_OBJECT_GROUP_NAME && child.hasChildren()) { // We need to cache the group to ensure it composites out eraser strokes correctly child.opacity(1); child.cache(); @@ -91,11 +86,31 @@ export const getLayerBboxPixels = (layer: KonvaLayerType, preview: boolean = fal } } + return { stageClone, layerClone }; +}; + +/** + * Get the bounding box of a regional prompt konva layer. This function has special handling for regional prompt layers. + * @param layer The konva layer to get the bounding box of. + * @param preview Whether to open a new tab displaying the rendered layer, which is used to calculate the bbox. + */ +export const getLayerBboxPixels = (layer: Konva.Layer, preview: boolean = false): IRect | null => { + // To calculate the layer's bounding box, we must first export it to a pixel array, then do some math. + // + // Though it is relatively fast, we can't use Konva's `getClientRect`. It programmatically determines the rect + // by calculating the extents of individual shapes from their "vector" shape data. + // + // This doesn't work when some shapes are drawn with composite operations that "erase" pixels, like eraser lines. + // These shapes' extents are still calculated as if they were solid, leading to a bounding box that is too large. + const { stageClone, layerClone } = getIsolatedRGLayerClone(layer); + // Get a worst-case rect using the relatively fast `getClientRect`. const layerRect = layerClone.getClientRect(); - + if (layerRect.width === 0 || layerRect.height === 0) { + return null; + } // Capture the image data with the above rect. - const layerImageData = offscreenStage + const layerImageData = stageClone .toCanvas(layerRect) .getContext('2d') ?.getImageData(0, 0, layerRect.width, layerRect.height); @@ -114,8 +129,8 @@ export const getLayerBboxPixels = (layer: KonvaLayerType, preview: boolean = fal // Correct the bounding box to be relative to the layer's position. const correctedLayerBbox = { - x: layerBbox.minX - Math.floor(stage.x()) + layerRect.x - Math.floor(layer.x()), - y: layerBbox.minY - Math.floor(stage.y()) + layerRect.y - Math.floor(layer.y()), + x: layerBbox.minX - Math.floor(stageClone.x()) + layerRect.x - Math.floor(layer.x()), + y: layerBbox.minY - Math.floor(stageClone.y()) + layerRect.y - Math.floor(layer.y()), width: layerBbox.maxX - layerBbox.minX, height: layerBbox.maxY - layerBbox.minY, }; @@ -123,7 +138,13 @@ export const getLayerBboxPixels = (layer: KonvaLayerType, preview: boolean = fal return correctedLayerBbox; }; -export const getLayerBboxFast = (layer: KonvaLayerType): IRect => { +/** + * Get the bounding box of a konva layer. This function is faster than `getLayerBboxPixels` but less accurate. It + * should only be used when there are no eraser strokes or shapes in the layer. + * @param layer The konva layer to get the bounding box of. + * @returns The bounding box of the layer. + */ +export const getLayerBboxFast = (layer: Konva.Layer): IRect => { const bbox = layer.getClientRect(GET_CLIENT_RECT_CONFIG); return { x: Math.floor(bbox.x), diff --git a/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.test.ts b/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.test.ts index 880514bf7c..22f54d622c 100644 --- a/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.test.ts +++ b/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.test.ts @@ -1,23 +1,93 @@ -import type { S } from 'services/api/types'; +import type { Invocation } from 'services/api/types'; import type { Equals } from 'tsafe'; import { assert } from 'tsafe'; import { describe, test } from 'vitest'; import type { + CannyProcessorConfig, CLIPVisionModelV2, + ColorMapProcessorConfig, + ContentShuffleProcessorConfig, ControlModeV2, DepthAnythingModelSize, + DepthAnythingProcessorConfig, + DWOpenposeProcessorConfig, + HedProcessorConfig, IPMethodV2, + LineartAnimeProcessorConfig, + LineartProcessorConfig, + MediapipeFaceProcessorConfig, + MidasDepthProcessorConfig, + MlsdProcessorConfig, + NormalbaeProcessorConfig, + PidiProcessorConfig, ProcessorConfig, ProcessorTypeV2, + ZoeDepthProcessorConfig, } from './controlAdapters'; describe('Control Adapter Types', () => { - test('ProcessorType', () => assert>()); - test('IP Adapter Method', () => assert, IPMethodV2>>()); - test('CLIP Vision Model', () => - assert, CLIPVisionModelV2>>()); - test('Control Mode', () => assert, ControlModeV2>>()); - test('DepthAnything Model Size', () => - assert, DepthAnythingModelSize>>()); + test('ProcessorType', () => { + assert>(); + }); + test('IP Adapter Method', () => { + assert['method']>, IPMethodV2>>(); + }); + test('CLIP Vision Model', () => { + assert['clip_vision_model']>, CLIPVisionModelV2>>(); + }); + test('Control Mode', () => { + assert['control_mode']>, ControlModeV2>>(); + }); + test('DepthAnything Model Size', () => { + assert['model_size']>, DepthAnythingModelSize>>(); + }); + test('Processor Configs', () => { + // The processor configs are manually modeled zod schemas. This test ensures that the inferred types are correct. + // The types prefixed with `_` are types generated from OpenAPI, while the types without the prefix are manually modeled. + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + assert>(); + }); }); + +// Types derived from OpenAPI +type _CannyProcessorConfig = Required< + Pick, 'id' | 'type' | 'low_threshold' | 'high_threshold'> +>; +type _ColorMapProcessorConfig = Required< + Pick, 'id' | 'type' | 'color_map_tile_size'> +>; +type _ContentShuffleProcessorConfig = Required< + Pick, 'id' | 'type' | 'w' | 'h' | 'f'> +>; +type _DepthAnythingProcessorConfig = Required< + Pick, 'id' | 'type' | 'model_size'> +>; +type _HedProcessorConfig = Required, 'id' | 'type' | 'scribble'>>; +type _LineartAnimeProcessorConfig = Required, 'id' | 'type'>>; +type _LineartProcessorConfig = Required, 'id' | 'type' | 'coarse'>>; +type _MediapipeFaceProcessorConfig = Required< + Pick, 'id' | 'type' | 'max_faces' | 'min_confidence'> +>; +type _MidasDepthProcessorConfig = Required< + Pick, 'id' | 'type' | 'a_mult' | 'bg_th'> +>; +type _MlsdProcessorConfig = Required, 'id' | 'type' | 'thr_v' | 'thr_d'>>; +type _NormalbaeProcessorConfig = Required, 'id' | 'type'>>; +type _DWOpenposeProcessorConfig = Required< + Pick, 'id' | 'type' | 'draw_body' | 'draw_face' | 'draw_hands'> +>; +type _PidiProcessorConfig = Required, 'id' | 'type' | 'safe' | 'scribble'>>; +type _ZoeDepthProcessorConfig = Required, 'id' | 'type'>>; diff --git a/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.ts b/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.ts index 2964a2eb6c..708e089008 100644 --- a/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.ts +++ b/invokeai/frontend/web/src/features/controlLayers/util/controlAdapters.ts @@ -1,117 +1,176 @@ import { deepClone } from 'common/util/deepClone'; -import type { - ParameterControlNetModel, - ParameterIPAdapterModel, - ParameterT2IAdapterModel, -} from 'features/parameters/types/parameterSchemas'; +import { zModelIdentifierField } from 'features/nodes/types/common'; import { merge, omit } from 'lodash-es'; -import type { - BaseModelType, - CannyImageProcessorInvocation, - ColorMapImageProcessorInvocation, - ContentShuffleImageProcessorInvocation, - ControlNetModelConfig, - DepthAnythingImageProcessorInvocation, - DWOpenposeImageProcessorInvocation, - Graph, - HedImageProcessorInvocation, - ImageDTO, - LineartAnimeImageProcessorInvocation, - LineartImageProcessorInvocation, - MediapipeFaceProcessorInvocation, - MidasDepthImageProcessorInvocation, - MlsdImageProcessorInvocation, - NormalbaeImageProcessorInvocation, - PidiImageProcessorInvocation, - T2IAdapterModelConfig, - ZoeDepthImageProcessorInvocation, -} from 'services/api/types'; +import type { BaseModelType, ControlNetModelConfig, Graph, ImageDTO, T2IAdapterModelConfig } from 'services/api/types'; import { z } from 'zod'; +const zId = z.string().min(1); + +const zCannyProcessorConfig = z.object({ + id: zId, + type: z.literal('canny_image_processor'), + low_threshold: z.number().int().gte(0).lte(255), + high_threshold: z.number().int().gte(0).lte(255), +}); +export type CannyProcessorConfig = z.infer; + +const zColorMapProcessorConfig = z.object({ + id: zId, + type: z.literal('color_map_image_processor'), + color_map_tile_size: z.number().int().gte(1), +}); +export type ColorMapProcessorConfig = z.infer; + +const zContentShuffleProcessorConfig = z.object({ + id: zId, + type: z.literal('content_shuffle_image_processor'), + w: z.number().int().gte(0), + h: z.number().int().gte(0), + f: z.number().int().gte(0), +}); +export type ContentShuffleProcessorConfig = z.infer; + const zDepthAnythingModelSize = z.enum(['large', 'base', 'small']); export type DepthAnythingModelSize = z.infer; export const isDepthAnythingModelSize = (v: unknown): v is DepthAnythingModelSize => zDepthAnythingModelSize.safeParse(v).success; +const zDepthAnythingProcessorConfig = z.object({ + id: zId, + type: z.literal('depth_anything_image_processor'), + model_size: zDepthAnythingModelSize, +}); +export type DepthAnythingProcessorConfig = z.infer; -export type CannyProcessorConfig = Required< - Pick ->; -export type ColorMapProcessorConfig = Required< - Pick ->; -export type ContentShuffleProcessorConfig = Required< - Pick ->; -export type DepthAnythingProcessorConfig = Required< - Pick ->; -export type HedProcessorConfig = Required>; -type LineartAnimeProcessorConfig = Required>; -export type LineartProcessorConfig = Required>; -export type MediapipeFaceProcessorConfig = Required< - Pick ->; -export type MidasDepthProcessorConfig = Required< - Pick ->; -export type MlsdProcessorConfig = Required>; -type NormalbaeProcessorConfig = Required>; -export type DWOpenposeProcessorConfig = Required< - Pick ->; -export type PidiProcessorConfig = Required>; -type ZoeDepthProcessorConfig = Required>; +const zHedProcessorConfig = z.object({ + id: zId, + type: z.literal('hed_image_processor'), + scribble: z.boolean(), +}); +export type HedProcessorConfig = z.infer; -export type ProcessorConfig = - | CannyProcessorConfig - | ColorMapProcessorConfig - | ContentShuffleProcessorConfig - | DepthAnythingProcessorConfig - | HedProcessorConfig - | LineartAnimeProcessorConfig - | LineartProcessorConfig - | MediapipeFaceProcessorConfig - | MidasDepthProcessorConfig - | MlsdProcessorConfig - | NormalbaeProcessorConfig - | DWOpenposeProcessorConfig - | PidiProcessorConfig - | ZoeDepthProcessorConfig; +const zLineartAnimeProcessorConfig = z.object({ + id: zId, + type: z.literal('lineart_anime_image_processor'), +}); +export type LineartAnimeProcessorConfig = z.infer; -export type ImageWithDims = { - imageName: string; - width: number; - height: number; -}; +const zLineartProcessorConfig = z.object({ + id: zId, + type: z.literal('lineart_image_processor'), + coarse: z.boolean(), +}); +export type LineartProcessorConfig = z.infer; -type ControlAdapterBase = { - id: string; - weight: number; - image: ImageWithDims | null; - processedImage: ImageWithDims | null; - isProcessingImage: boolean; - processorConfig: ProcessorConfig | null; - beginEndStepPct: [number, number]; -}; +const zMediapipeFaceProcessorConfig = z.object({ + id: zId, + type: z.literal('mediapipe_face_processor'), + max_faces: z.number().int().gte(1), + min_confidence: z.number().gte(0).lte(1), +}); +export type MediapipeFaceProcessorConfig = z.infer; + +const zMidasDepthProcessorConfig = z.object({ + id: zId, + type: z.literal('midas_depth_image_processor'), + a_mult: z.number().gte(0), + bg_th: z.number().gte(0), +}); +export type MidasDepthProcessorConfig = z.infer; + +const zMlsdProcessorConfig = z.object({ + id: zId, + type: z.literal('mlsd_image_processor'), + thr_v: z.number().gte(0), + thr_d: z.number().gte(0), +}); +export type MlsdProcessorConfig = z.infer; + +const zNormalbaeProcessorConfig = z.object({ + id: zId, + type: z.literal('normalbae_image_processor'), +}); +export type NormalbaeProcessorConfig = z.infer; + +const zDWOpenposeProcessorConfig = z.object({ + id: zId, + type: z.literal('dw_openpose_image_processor'), + draw_body: z.boolean(), + draw_face: z.boolean(), + draw_hands: z.boolean(), +}); +export type DWOpenposeProcessorConfig = z.infer; + +const zPidiProcessorConfig = z.object({ + id: zId, + type: z.literal('pidi_image_processor'), + safe: z.boolean(), + scribble: z.boolean(), +}); +export type PidiProcessorConfig = z.infer; + +const zZoeDepthProcessorConfig = z.object({ + id: zId, + type: z.literal('zoe_depth_image_processor'), +}); +export type ZoeDepthProcessorConfig = z.infer; + +const zProcessorConfig = z.discriminatedUnion('type', [ + zCannyProcessorConfig, + zColorMapProcessorConfig, + zContentShuffleProcessorConfig, + zDepthAnythingProcessorConfig, + zHedProcessorConfig, + zLineartAnimeProcessorConfig, + zLineartProcessorConfig, + zMediapipeFaceProcessorConfig, + zMidasDepthProcessorConfig, + zMlsdProcessorConfig, + zNormalbaeProcessorConfig, + zDWOpenposeProcessorConfig, + zPidiProcessorConfig, + zZoeDepthProcessorConfig, +]); +export type ProcessorConfig = z.infer; + +export const zImageWithDims = z.object({ + name: z.string(), + width: z.number().int().positive(), + height: z.number().int().positive(), +}); +export type ImageWithDims = z.infer; + +const zBeginEndStepPct = z + .tuple([z.number().gte(0).lte(1), z.number().gte(0).lte(1)]) + .refine(([begin, end]) => begin < end, { + message: 'Begin must be less than end', + }); + +const zControlAdapterBase = z.object({ + id: zId, + weight: z.number().gte(0).lte(1), + image: zImageWithDims.nullable(), + processedImage: zImageWithDims.nullable(), + processorConfig: zProcessorConfig.nullable(), + processorPendingBatchId: z.string().nullable().default(null), + beginEndStepPct: zBeginEndStepPct, +}); const zControlModeV2 = z.enum(['balanced', 'more_prompt', 'more_control', 'unbalanced']); export type ControlModeV2 = z.infer; export const isControlModeV2 = (v: unknown): v is ControlModeV2 => zControlModeV2.safeParse(v).success; -export type ControlNetConfigV2 = ControlAdapterBase & { - type: 'controlnet'; - model: ParameterControlNetModel | null; - controlMode: ControlModeV2; -}; -export const isControlNetConfigV2 = (ca: ControlNetConfigV2 | T2IAdapterConfigV2): ca is ControlNetConfigV2 => - ca.type === 'controlnet'; +export const zControlNetConfigV2 = zControlAdapterBase.extend({ + type: z.literal('controlnet'), + model: zModelIdentifierField.nullable(), + controlMode: zControlModeV2, +}); +export type ControlNetConfigV2 = z.infer; -export type T2IAdapterConfigV2 = ControlAdapterBase & { - type: 't2i_adapter'; - model: ParameterT2IAdapterModel | null; -}; -export const isT2IAdapterConfigV2 = (ca: ControlNetConfigV2 | T2IAdapterConfigV2): ca is T2IAdapterConfigV2 => - ca.type === 't2i_adapter'; +export const zT2IAdapterConfigV2 = zControlAdapterBase.extend({ + type: z.literal('t2i_adapter'), + model: zModelIdentifierField.nullable(), +}); +export type T2IAdapterConfigV2 = z.infer; const zCLIPVisionModelV2 = z.enum(['ViT-H', 'ViT-G']); export type CLIPVisionModelV2 = z.infer; @@ -121,16 +180,17 @@ const zIPMethodV2 = z.enum(['full', 'style', 'composition']); export type IPMethodV2 = z.infer; export const isIPMethodV2 = (v: unknown): v is IPMethodV2 => zIPMethodV2.safeParse(v).success; -export type IPAdapterConfigV2 = { - id: string; - type: 'ip_adapter'; - weight: number; - method: IPMethodV2; - image: ImageWithDims | null; - model: ParameterIPAdapterModel | null; - clipVisionModel: CLIPVisionModelV2; - beginEndStepPct: [number, number]; -}; +export const zIPAdapterConfigV2 = z.object({ + id: zId, + type: z.literal('ip_adapter'), + weight: z.number().gte(0).lte(1), + method: zIPMethodV2, + image: zImageWithDims.nullable(), + model: zModelIdentifierField.nullable(), + clipVisionModel: zCLIPVisionModelV2, + beginEndStepPct: zBeginEndStepPct, +}); +export type IPAdapterConfigV2 = z.infer; const zProcessorTypeV2 = z.enum([ 'canny_image_processor', @@ -190,7 +250,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { buildNode: (image, config) => ({ ...config, type: 'canny_image_processor', - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -207,7 +267,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { buildNode: (image, config) => ({ ...config, type: 'color_map_image_processor', - image: { image_name: image.imageName }, + image: { image_name: image.name }, }), }, content_shuffle_image_processor: { @@ -223,7 +283,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -239,7 +299,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, resolution: minDim(image), }), }, @@ -254,7 +314,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -269,7 +329,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -285,7 +345,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -302,7 +362,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -319,7 +379,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -336,7 +396,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -351,7 +411,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -369,7 +429,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, image_resolution: minDim(image), }), }, @@ -385,7 +445,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, detect_resolution: minDim(image), image_resolution: minDim(image), }), @@ -400,7 +460,7 @@ export const CA_PROCESSOR_DATA: CAProcessorsData = { }), buildNode: (image, config) => ({ ...config, - image: { image_name: image.imageName }, + image: { image_name: image.name }, }), }, }; @@ -413,8 +473,8 @@ export const initialControlNetV2: Omit = { controlMode: 'balanced', image: null, processedImage: null, - isProcessingImage: false, processorConfig: CA_PROCESSOR_DATA.canny_image_processor.buildDefaults(), + processorPendingBatchId: null, }; export const initialT2IAdapterV2: Omit = { @@ -424,8 +484,8 @@ export const initialT2IAdapterV2: Omit = { beginEndStepPct: [0, 1], image: null, processedImage: null, - isProcessingImage: false, processorConfig: CA_PROCESSOR_DATA.canny_image_processor.buildDefaults(), + processorPendingBatchId: null, }; export const initialIPAdapterV2: Omit = { @@ -462,7 +522,7 @@ export const buildControlAdapterProcessorV2 = ( }; export const imageDTOToImageWithDims = ({ image_name, width, height }: ImageDTO): ImageWithDims => ({ - imageName: image_name, + name: image_name, width, height, }); diff --git a/invokeai/frontend/web/src/features/controlLayers/util/renderers.ts b/invokeai/frontend/web/src/features/controlLayers/util/renderers.ts index f58b1e3b74..25ac30387b 100644 --- a/invokeai/frontend/web/src/features/controlLayers/util/renderers.ts +++ b/invokeai/frontend/web/src/features/controlLayers/util/renderers.ts @@ -437,8 +437,8 @@ const renderRegionalGuidanceLayer = ( konvaObjectGroup.opacity(1); compositingRect.setAttrs({ - // The rect should be the size of the layer - use the fast method bc it's OK if the rect is larger - ...getLayerBboxFast(konvaLayer), + // The rect should be the size of the layer - use the fast method if we don't have a pixel-perfect bbox already + ...(!reduxLayer.bboxNeedsUpdate && reduxLayer.bbox ? reduxLayer.bbox : getLayerBboxFast(konvaLayer)), fill: rgbColor, opacity: globalMaskLayerOpacity, // Draw this rect only where there are non-transparent pixels under it (e.g. the mask shapes) @@ -464,6 +464,7 @@ const createInitialImageLayer = (stage: Konva.Stage, reduxLayer: InitialImageLay id: reduxLayer.id, name: INITIAL_IMAGE_LAYER_NAME, imageSmoothingEnabled: true, + listening: false, }); stage.add(konvaLayer); return konvaLayer; @@ -483,6 +484,9 @@ const updateInitialImageLayerImageAttrs = ( konvaImage: Konva.Image, reduxLayer: InitialImageLayer ) => { + // Konva erroneously reports NaN for width and height when the stage is hidden. This causes errors when caching, + // but it doesn't seem to break anything. + // TODO(psyche): Investigate and report upstream. const newWidth = stage.width() / stage.scaleX(); const newHeight = stage.height() / stage.scaleY(); if ( @@ -510,7 +514,7 @@ const updateInitialImageLayerImageSource = async ( reduxLayer: InitialImageLayer ) => { if (reduxLayer.image) { - const { imageName } = reduxLayer.image; + const imageName = reduxLayer.image.name; const req = getStore().dispatch(imagesApi.endpoints.getImageDTO.initiate(imageName)); const imageDTO = await req.unwrap(); req.unsubscribe(); @@ -543,7 +547,7 @@ const renderInitialImageLayer = (stage: Konva.Stage, reduxLayer: InitialImageLay let imageSourceNeedsUpdate = false; if (canvasImageSource instanceof HTMLImageElement) { const image = reduxLayer.image; - if (image && canvasImageSource.id !== getCALayerImageId(reduxLayer.id, image.imageName)) { + if (image && canvasImageSource.id !== getCALayerImageId(reduxLayer.id, image.name)) { imageSourceNeedsUpdate = true; } else if (!image) { imageSourceNeedsUpdate = true; @@ -564,6 +568,7 @@ const createControlNetLayer = (stage: Konva.Stage, reduxLayer: ControlAdapterLay id: reduxLayer.id, name: CA_LAYER_NAME, imageSmoothingEnabled: true, + listening: false, }); stage.add(konvaLayer); return konvaLayer; @@ -585,7 +590,7 @@ const updateControlNetLayerImageSource = async ( ) => { const image = reduxLayer.controlAdapter.processedImage ?? reduxLayer.controlAdapter.image; if (image) { - const { imageName } = image; + const imageName = image.name; const req = getStore().dispatch(imagesApi.endpoints.getImageDTO.initiate(imageName)); const imageDTO = await req.unwrap(); req.unsubscribe(); @@ -618,6 +623,9 @@ const updateControlNetLayerImageAttrs = ( reduxLayer: ControlAdapterLayer ) => { let needsCache = false; + // Konva erroneously reports NaN for width and height when the stage is hidden. This causes errors when caching, + // but it doesn't seem to break anything. + // TODO(psyche): Investigate and report upstream. const newWidth = stage.width() / stage.scaleX(); const newHeight = stage.height() / stage.scaleY(); const hasFilter = konvaImage.filters() !== null && konvaImage.filters().length > 0; @@ -653,7 +661,7 @@ const renderControlNetLayer = (stage: Konva.Stage, reduxLayer: ControlAdapterLay let imageSourceNeedsUpdate = false; if (canvasImageSource instanceof HTMLImageElement) { const image = reduxLayer.controlAdapter.processedImage ?? reduxLayer.controlAdapter.image; - if (image && canvasImageSource.id !== getCALayerImageId(reduxLayer.id, image.imageName)) { + if (image && canvasImageSource.id !== getCALayerImageId(reduxLayer.id, image.name)) { imageSourceNeedsUpdate = true; } else if (!image) { imageSourceNeedsUpdate = true; @@ -702,6 +710,7 @@ const renderLayers = ( if (isInitialImageLayer(reduxLayer)) { renderInitialImageLayer(stage, reduxLayer); } + // IP Adapter layers are not rendered } }; @@ -716,6 +725,7 @@ const createBboxRect = (reduxLayer: Layer, konvaLayer: Konva.Layer) => { id: getLayerBboxId(reduxLayer.id), name: LAYER_BBOX_NAME, strokeWidth: 1, + visible: false, }); konvaLayer.add(rect); return rect; @@ -725,18 +735,10 @@ const createBboxRect = (reduxLayer: Layer, konvaLayer: Konva.Layer) => { * Renders the bounding boxes for the layers. * @param stage The konva stage to render on * @param reduxLayers An array of all redux layers to draw bboxes for - * @param selectedLayerId The selected layer's id * @param tool The current tool - * @param onBboxChanged Callback for when the bbox is changed - * @param onBboxMouseDown Callback for when the bbox is clicked * @returns */ -const renderBbox = ( - stage: Konva.Stage, - reduxLayers: Layer[], - tool: Tool, - onBboxChanged: (layerId: string, bbox: IRect | null) => void -) => { +const renderBboxes = (stage: Konva.Stage, reduxLayers: Layer[], tool: Tool) => { // Hide all bboxes so they don't interfere with getClientRect for (const bboxRect of stage.find(`.${LAYER_BBOX_NAME}`)) { bboxRect.visible(false); @@ -747,36 +749,59 @@ const renderBbox = ( return; } - for (const reduxLayer of reduxLayers) { - if (reduxLayer.type === 'regional_guidance_layer') { - const konvaLayer = stage.findOne(`#${reduxLayer.id}`); - assert(konvaLayer, `Layer ${reduxLayer.id} not found in stage`); + for (const reduxLayer of reduxLayers.filter(isRegionalGuidanceLayer)) { + if (!reduxLayer.bbox) { + continue; + } + const konvaLayer = stage.findOne(`#${reduxLayer.id}`); + assert(konvaLayer, `Layer ${reduxLayer.id} not found in stage`); - let bbox = reduxLayer.bbox; + const bboxRect = konvaLayer.findOne(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(reduxLayer, konvaLayer); - // We only need to recalculate the bbox if the layer has changed and it has objects - if (reduxLayer.bboxNeedsUpdate && reduxLayer.maskObjects.length) { - // We only need to use the pixel-perfect bounding box if the layer has eraser strokes - bbox = reduxLayer.needsPixelBbox ? getLayerBboxPixels(konvaLayer) : getLayerBboxFast(konvaLayer); - // Update the layer's bbox in the redux store - onBboxChanged(reduxLayer.id, bbox); + bboxRect.setAttrs({ + visible: !reduxLayer.bboxNeedsUpdate, + listening: reduxLayer.isSelected, + x: reduxLayer.bbox.x, + y: reduxLayer.bbox.y, + width: reduxLayer.bbox.width, + height: reduxLayer.bbox.height, + stroke: reduxLayer.isSelected ? BBOX_SELECTED_STROKE : '', + }); + } +}; + +/** + * Calculates the bbox of each regional guidance layer. Only calculates if the mask has changed. + * @param stage The konva stage to render on. + * @param reduxLayers An array of redux layers to calculate bboxes for + * @param onBboxChanged Callback for when the bounding box changes + */ +const updateBboxes = ( + stage: Konva.Stage, + reduxLayers: Layer[], + onBboxChanged: (layerId: string, bbox: IRect | null) => void +) => { + for (const rgLayer of reduxLayers.filter(isRegionalGuidanceLayer)) { + const konvaLayer = stage.findOne(`#${rgLayer.id}`); + assert(konvaLayer, `Layer ${rgLayer.id} not found in stage`); + // We only need to recalculate the bbox if the layer has changed + if (rgLayer.bboxNeedsUpdate) { + const bboxRect = konvaLayer.findOne(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(rgLayer, konvaLayer); + + // Hide the bbox while we calculate the new bbox, else the bbox will be included in the calculation + const visible = bboxRect.visible(); + bboxRect.visible(false); + + if (rgLayer.maskObjects.length === 0) { + // No objects - no bbox to calculate + onBboxChanged(rgLayer.id, null); + } else { + // Calculate the bbox by rendering the layer and checking its pixels + onBboxChanged(rgLayer.id, getLayerBboxPixels(konvaLayer)); } - if (!bbox) { - continue; - } - - const rect = konvaLayer.findOne(`.${LAYER_BBOX_NAME}`) ?? createBboxRect(reduxLayer, konvaLayer); - - rect.setAttrs({ - visible: true, - listening: reduxLayer.isSelected, - x: bbox.x, - y: bbox.y, - width: bbox.width, - height: bbox.height, - stroke: reduxLayer.isSelected ? BBOX_SELECTED_STROKE : '', - }); + // Restore the visibility of the bbox + bboxRect.visible(visible); } } }; @@ -893,10 +918,11 @@ const renderNoLayersMessage = (stage: Konva.Stage, layerCount: number, width: nu export const renderers = { renderToolPreview, renderLayers, - renderBbox, + renderBboxes, renderBackground, renderNoLayersMessage, arrangeLayers, + updateBboxes, }; const DEBOUNCE_MS = 300; @@ -904,10 +930,11 @@ const DEBOUNCE_MS = 300; export const debouncedRenderers = { renderToolPreview: debounce(renderToolPreview, DEBOUNCE_MS), renderLayers: debounce(renderLayers, DEBOUNCE_MS), - renderBbox: debounce(renderBbox, DEBOUNCE_MS), + renderBboxes: debounce(renderBboxes, DEBOUNCE_MS), renderBackground: debounce(renderBackground, DEBOUNCE_MS), renderNoLayersMessage: debounce(renderNoLayersMessage, DEBOUNCE_MS), arrangeLayers: debounce(arrangeLayers, DEBOUNCE_MS), + updateBboxes: debounce(updateBboxes, DEBOUNCE_MS), }; /** diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts index ce989de7b1..a7934f72d2 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts +++ b/invokeai/frontend/web/src/features/deleteImageModal/store/selectors.ts @@ -46,18 +46,16 @@ export const getImageUsage = ( const isControlLayerImage = controlLayers.layers.some((l) => { if (isRegionalGuidanceLayer(l)) { - return l.ipAdapters.some((ipa) => ipa.image?.imageName === image_name); + return l.ipAdapters.some((ipa) => ipa.image?.name === image_name); } if (isControlAdapterLayer(l)) { - return ( - l.controlAdapter.image?.imageName === image_name || l.controlAdapter.processedImage?.imageName === image_name - ); + return l.controlAdapter.image?.name === image_name || l.controlAdapter.processedImage?.name === image_name; } if (isIPAdapterLayer(l)) { - return l.ipAdapter.image?.imageName === image_name; + return l.ipAdapter.image?.name === image_name; } if (isInitialImageLayer(l)) { - return l.image?.imageName === image_name; + return l.image?.name === image_name; } return false; }); diff --git a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts index f3f0c50f03..ce93611ff4 100644 --- a/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts +++ b/invokeai/frontend/web/src/features/dnd/hooks/useScaledCenteredModifer.ts @@ -1,23 +1,21 @@ import type { Modifier } from '@dnd-kit/core'; import { getEventCoordinates } from '@dnd-kit/utilities'; -import { createSelector } from '@reduxjs/toolkit'; +import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $viewport } from 'features/nodes/store/nodesSlice'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { useCallback } from 'react'; -const selectZoom = createSelector([selectNodesSlice, activeTabNameSelector], (nodes, activeTabName) => - activeTabName === 'workflows' ? nodes.viewport.zoom : 1 -); - /** * Applies scaling to the drag transform (if on node editor tab) and centers it on cursor. */ export const useScaledModifer = () => { - const zoom = useAppSelector(selectZoom); + const activeTabName = useAppSelector(activeTabNameSelector); + const workflowsViewport = useStore($viewport); const modifier: Modifier = useCallback( ({ activatorEvent, draggingNodeRect, transform }) => { if (draggingNodeRect && activatorEvent) { + const zoom = activeTabName === 'workflows' ? workflowsViewport.zoom : 1; const activatorCoordinates = getEventCoordinates(activatorEvent); if (!activatorCoordinates) { @@ -42,7 +40,7 @@ export const useScaledModifer = () => { return transform; }, - [zoom] + [activeTabName, workflowsViewport.zoom] ); return modifier; 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 7bfb4050fb..a25f6d8c0e 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageContextMenu/SingleSelectionMenuItems.tsx @@ -73,6 +73,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => { const handleSendToImageToImage = useCallback(() => { dispatch(sentImageToImg2Img()); dispatch(iiLayerAdded(imageDTO)); + dispatch(setActiveTab('generation')); }, [dispatch, imageDTO]); const handleSendToCanvas = useCallback(() => { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx index 2788b1095d..2c53599ba3 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGrid/GalleryImage.tsx @@ -11,6 +11,7 @@ import type { GallerySelectionDraggableData, ImageDraggableData, TypesafeDraggab import { getGalleryImageDataTestId } from 'features/gallery/components/ImageGrid/getGalleryImageDataTestId'; import { useMultiselect } from 'features/gallery/hooks/useMultiselect'; import { useScrollIntoView } from 'features/gallery/hooks/useScrollIntoView'; +import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice'; import type { MouseEvent } from 'react'; import { memo, useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -102,6 +103,10 @@ const GalleryImage = (props: HoverableImageProps) => { setIsHovered(true); }, []); + const onDoubleClick = useCallback(() => { + dispatch(isImageViewerOpenChanged(true)); + }, [dispatch]); + const handleMouseOut = useCallback(() => { setIsHovered(false); }, []); @@ -143,6 +148,7 @@ const GalleryImage = (props: HoverableImageProps) => { > { const { label, data, fileName, withDownload = true, withCopy = true, extraCopyActions } = props; - const dataString = useMemo(() => (isString(data) ? data : JSON.stringify(data, null, 2)), [data]); + const dataString = useMemo(() => (isString(data) ? data : formatter.Serialize(data)) ?? '', [data]); const shift = useShiftModifier(); const handleCopy = useCallback(() => { navigator.clipboard.writeText(dataString); 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 c73f5b1817..a192ff4fbb 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataActions.tsx @@ -1,12 +1,10 @@ import { useAppSelector } from 'app/store/storeHooks'; import { MetadataControlNets } from 'features/metadata/components/MetadataControlNets'; -import { MetadataControlNetsV2 } from 'features/metadata/components/MetadataControlNetsV2'; import { MetadataIPAdapters } from 'features/metadata/components/MetadataIPAdapters'; -import { MetadataIPAdaptersV2 } from 'features/metadata/components/MetadataIPAdaptersV2'; import { MetadataItem } from 'features/metadata/components/MetadataItem'; +import { MetadataLayers } from 'features/metadata/components/MetadataLayers'; import { MetadataLoRAs } from 'features/metadata/components/MetadataLoRAs'; import { MetadataT2IAdapters } from 'features/metadata/components/MetadataT2IAdapters'; -import { MetadataT2IAdaptersV2 } from 'features/metadata/components/MetadataT2IAdaptersV2'; import { handlers } from 'features/metadata/util/handlers'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { memo } from 'react'; @@ -39,8 +37,7 @@ const ImageMetadataActions = (props: Props) => { - - + {activeTabName !== 'generation' && } @@ -52,12 +49,10 @@ const ImageMetadataActions = (props: Props) => { + {activeTabName === 'generation' && } {activeTabName !== 'generation' && } {activeTabName !== 'generation' && } {activeTabName !== 'generation' && } - {activeTabName === 'generation' && } - {activeTabName === 'generation' && } - {activeTabName === 'generation' && } ); }; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx new file mode 100644 index 0000000000..9f7cac4a3e --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent.tsx @@ -0,0 +1,34 @@ +import { IAINoContentFallback } from 'common/components/IAIImageFallback'; +import { memo, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDebouncedImageWorkflow } from 'services/api/hooks/useDebouncedImageWorkflow'; +import type { ImageDTO } from 'services/api/types'; + +import DataViewer from './DataViewer'; + +type Props = { + image: ImageDTO; +}; + +const ImageMetadataGraphTabContent = ({ image }: Props) => { + const { t } = useTranslation(); + const { currentData } = useDebouncedImageWorkflow(image); + const graph = useMemo(() => { + if (currentData?.graph) { + try { + return JSON.parse(currentData.graph); + } catch { + return null; + } + } + return null; + }, [currentData]); + + if (!graph) { + return ; + } + + return ; +}; + +export default memo(ImageMetadataGraphTabContent); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx index ccc4436452..46121f9724 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataViewer.tsx @@ -1,6 +1,7 @@ import { ExternalLink, Flex, Tab, TabList, TabPanel, TabPanels, Tabs, Text } from '@invoke-ai/ui-library'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; +import ImageMetadataGraphTabContent from 'features/gallery/components/ImageMetadataViewer/ImageMetadataGraphTabContent'; import { useMetadataItem } from 'features/metadata/hooks/useMetadataItem'; import { handlers } from 'features/metadata/util/handlers'; import { memo } from 'react'; @@ -52,6 +53,7 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => { {t('metadata.metadata')} {t('metadata.imageDetails')} {t('metadata.workflow')} + {t('nodes.graph')} @@ -81,6 +83,9 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => { + + + diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx index 60cda21028..fe4ce3e701 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageMetadataViewer/ImageMetadataWorkflowTabContent.tsx @@ -1,5 +1,5 @@ import { IAINoContentFallback } from 'common/components/IAIImageFallback'; -import { memo } from 'react'; +import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useDebouncedImageWorkflow } from 'services/api/hooks/useDebouncedImageWorkflow'; import type { ImageDTO } from 'services/api/types'; @@ -12,7 +12,17 @@ type Props = { const ImageMetadataWorkflowTabContent = ({ image }: Props) => { const { t } = useTranslation(); - const { workflow } = useDebouncedImageWorkflow(image); + const { currentData } = useDebouncedImageWorkflow(image); + const workflow = useMemo(() => { + if (currentData?.workflow) { + try { + return JSON.parse(currentData.workflow); + } catch { + return null; + } + } + return null; + }, [currentData]); if (!workflow) { return ; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx index f93f48e51b..ada9c35d28 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImageButtons.tsx @@ -16,6 +16,7 @@ import ParamUpscalePopover from 'features/parameters/components/Upscale/ParamUps import { useIsQueueMutationInProgress } from 'features/queue/hooks/useIsQueueMutationInProgress'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { selectSystemSlice } from 'features/system/store/systemSlice'; +import { setActiveTab } from 'features/ui/store/uiSlice'; import { useGetAndLoadEmbeddedWorkflow } from 'features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow'; import { memo, useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; @@ -84,6 +85,7 @@ const CurrentImageButtons = () => { } dispatch(sentImageToImg2Img()); dispatch(iiLayerAdded(imageDTO)); + dispatch(setActiveTab('generation')); }, [dispatch, imageDTO]); useHotkeys('shift+i', handleSendToImageToImage, [imageDTO]); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx index 35abf07965..f40ecfca32 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/CurrentImagePreview.tsx @@ -22,7 +22,21 @@ const selectLastSelectedImageName = createSelector( (lastSelectedImage) => lastSelectedImage?.image_name ); -const CurrentImagePreview = () => { +type Props = { + isDragDisabled?: boolean; + isDropDisabled?: boolean; + withNextPrevButtons?: boolean; + withMetadata?: boolean; + alwaysShowProgress?: boolean; +}; + +const CurrentImagePreview = ({ + isDragDisabled = false, + isDropDisabled = false, + withNextPrevButtons = true, + withMetadata = true, + alwaysShowProgress = false, +}: Props) => { const { t } = useTranslation(); const shouldShowImageDetails = useAppSelector((s) => s.ui.shouldShowImageDetails); const imageName = useAppSelector(selectLastSelectedImageName); @@ -72,13 +86,15 @@ const CurrentImagePreview = () => { justifyContent="center" position="relative" > - {hasDenoiseProgress && shouldShowProgressInViewer ? ( + {hasDenoiseProgress && (shouldShowProgressInViewer || alwaysShowProgress) ? ( ) : ( { dataTestId="image-preview" /> )} + {shouldShowImageDetails && imageDTO && withMetadata && ( + + + + )} - {shouldShowImageDetails && imageDTO && ( - - - - )} - - - {shouldShowNextPrevButtons && imageDTO && ( + {withNextPrevButtons && shouldShowNextPrevButtons && imageDTO && ( = { - generation: 'controlLayers.controlLayers', - canvas: 'ui.tabs.canvas', - workflows: 'ui.tabs.workflows', - models: 'ui.tabs.models', - queue: 'ui.tabs.queue', -}; - -export const EditorButton = () => { - const { t } = useTranslation(); - const { onClose } = useImageViewer(); - const activeTabName = useAppSelector(activeTabNameSelector); - const tooltip = useMemo( - () => t('gallery.switchTo', { tab: t(TAB_NAME_TO_TKEY_SHORT[activeTabName]) }), - [t, activeTabName] - ); - - return ( - - ); -}; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx index 949e72fad1..7064e553dc 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewer.tsx @@ -5,26 +5,12 @@ import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/To import { useImageViewer } from 'features/gallery/components/ImageViewer/useImageViewer'; import type { InvokeTabName } from 'features/ui/store/tabMap'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; -import type { AnimationProps } from 'framer-motion'; -import { AnimatePresence, motion } from 'framer-motion'; import { memo, useMemo } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import CurrentImageButtons from './CurrentImageButtons'; import CurrentImagePreview from './CurrentImagePreview'; -import { EditorButton } from './EditorButton'; - -const initial: AnimationProps['initial'] = { - opacity: 0, -}; -const animate: AnimationProps['animate'] = { - opacity: 1, - transition: { duration: 0.07 }, -}; -const exit: AnimationProps['exit'] = { - opacity: 0, - transition: { duration: 0.07 }, -}; +import { ViewerToggleMenu } from './ViewerToggleMenu'; const VIEWER_ENABLED_TABS: InvokeTabName[] = ['canvas', 'generation', 'workflows']; @@ -42,50 +28,44 @@ export const ImageViewer = memo(() => { useHotkeys('z', onToggle, { enabled: isViewerEnabled }, [isViewerEnabled, onToggle]); useHotkeys('esc', onClose, { enabled: isViewerEnabled }, [isViewerEnabled, onClose]); - // The AnimatePresence mode must be wait - else framer can get confused if you spam the toggle button + if (!shouldShowViewer) { + return null; + } + return ( - - {shouldShowViewer && ( - - - - - - - - - - - - - - - - + + + + + + - - )} - + + + + + + + + + + + ); }); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerWorkflows.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerWorkflows.tsx new file mode 100644 index 0000000000..fe09f11be6 --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ImageViewerWorkflows.tsx @@ -0,0 +1,45 @@ +import { Flex } from '@invoke-ai/ui-library'; +import { ToggleMetadataViewerButton } from 'features/gallery/components/ImageViewer/ToggleMetadataViewerButton'; +import { ToggleProgressButton } from 'features/gallery/components/ImageViewer/ToggleProgressButton'; +import { memo } from 'react'; + +import CurrentImageButtons from './CurrentImageButtons'; +import CurrentImagePreview from './CurrentImagePreview'; + +export const ImageViewerWorkflows = memo(() => { + return ( + + + + + + + + + + + + + + + + + + ); +}); + +ImageViewerWorkflows.displayName = 'ImageViewerWorkflows'; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx index a298ebda56..4bf55116db 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleMetadataViewerButton.tsx @@ -35,6 +35,7 @@ export const ToggleMetadataViewerButton = memo(() => { isDisabled={!imageDTO} variant="outline" colorScheme={shouldShowImageDetails ? 'invokeBlue' : 'base'} + data-testid="toggle-show-metadata-button" /> ); }); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx index 994a8bf10e..ee698130fb 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ToggleProgressButton.tsx @@ -22,6 +22,7 @@ export const ToggleProgressButton = memo(() => { onClick={onClick} variant="outline" colorScheme={shouldShowProgressInViewer ? 'invokeBlue' : 'base'} + data-testid="toggle-show-progress-button" /> ); }); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerButton.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerButton.tsx deleted file mode 100644 index edceb5099c..0000000000 --- a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Button } from '@invoke-ai/ui-library'; -import { useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { PiArrowsDownUpBold } from 'react-icons/pi'; - -import { useImageViewer } from './useImageViewer'; - -export const ViewerButton = () => { - const { t } = useTranslation(); - const { onOpen } = useImageViewer(); - const tooltip = useMemo(() => t('gallery.switchTo', { tab: t('common.viewer') }), [t]); - - return ( - - ); -}; diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToggleMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToggleMenu.tsx new file mode 100644 index 0000000000..3552c28a5b --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/ImageViewer/ViewerToggleMenu.tsx @@ -0,0 +1,67 @@ +import { + Button, + Flex, + Icon, + Popover, + PopoverArrow, + PopoverBody, + PopoverContent, + PopoverTrigger, + Text, +} from '@invoke-ai/ui-library'; +import { useTranslation } from 'react-i18next'; +import { PiCaretDownBold, PiCheckBold, PiEyeBold, PiPencilBold } from 'react-icons/pi'; + +import { useImageViewer } from './useImageViewer'; + +export const ViewerToggleMenu = () => { + const { t } = useTranslation(); + const { isOpen, onClose, onOpen } = useImageViewer(); + + return ( + + + + + + + + + + + + + + + ); +}; diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 5248977825..af19017486 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -1,7 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; -import { setActiveTab } from 'features/ui/store/uiSlice'; import { uniqBy } from 'lodash-es'; import { boardsApi } from 'services/api/endpoints/boards'; import { imagesApi } from 'services/api/endpoints/images'; @@ -22,7 +21,7 @@ const initialGalleryState: GalleryState = { boardSearchText: '', limit: INITIAL_IMAGE_LIMIT, offset: 0, - isImageViewerOpen: false, + isImageViewerOpen: true, }; export const gallerySlice = createSlice({ @@ -82,9 +81,6 @@ export const gallerySlice = createSlice({ }, }, extraReducers: (builder) => { - builder.addCase(setActiveTab, (state) => { - state.isImageViewerOpen = false; - }); builder.addMatcher(isAnyBoardDeleted, (state, action) => { const deletedBoardId = action.meta.arg.originalArgs; if (deletedBoardId === state.selectedBoardId) { diff --git a/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx b/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx index ddcdd58e75..f7261b4608 100644 --- a/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx +++ b/invokeai/frontend/web/src/features/lora/components/LoRACard.tsx @@ -75,8 +75,8 @@ export const LoRACard = memo((props: LoRACardProps) => { { - const [controlNets, setControlNets] = useState([]); - - useEffect(() => { - const parse = async () => { - try { - const parsed = await handlers.controlNetsV2.parse(metadata); - setControlNets(parsed); - } catch (e) { - setControlNets([]); - } - }; - parse(); - }, [metadata]); - - const label = useMemo(() => handlers.controlNetsV2.getLabel(), []); - - return ( - <> - {controlNets.map((controlNet) => ( - - ))} - - ); -}; - -const MetadataViewControlNet = ({ - label, - controlNet, - handlers, -}: { - label: string; - controlNet: ControlNetConfigV2Metadata; - handlers: MetadataHandlers; -}) => { - const onRecall = useCallback(() => { - if (!handlers.recallItem) { - return; - } - handlers.recallItem(controlNet, true); - }, [handlers, controlNet]); - - const [renderedValue, setRenderedValue] = useState(null); - useEffect(() => { - const _renderValue = async () => { - if (!handlers.renderItemValue) { - setRenderedValue(null); - return; - } - const rendered = await handlers.renderItemValue(controlNet); - setRenderedValue(rendered); - }; - - _renderValue(); - }, [handlers, controlNet]); - - return ; -}; diff --git a/invokeai/frontend/web/src/features/metadata/components/MetadataItem.tsx b/invokeai/frontend/web/src/features/metadata/components/MetadataItem.tsx index 66d101f458..7489b05158 100644 --- a/invokeai/frontend/web/src/features/metadata/components/MetadataItem.tsx +++ b/invokeai/frontend/web/src/features/metadata/components/MetadataItem.tsx @@ -3,6 +3,7 @@ import { MetadataItemView } from 'features/metadata/components/MetadataItemView' import { useMetadataItem } from 'features/metadata/hooks/useMetadataItem'; import type { MetadataHandlers } from 'features/metadata/types'; import { MetadataParseFailedToken } from 'features/metadata/util/parsers'; +import { isSymbol } from 'lodash-es'; type MetadataItemProps = { metadata: unknown; @@ -17,6 +18,10 @@ const _MetadataItem = typedMemo(({ metadata, handlers, direction = 'row' }: return null; } + if (handlers.getIsVisible && !isSymbol(value) && !handlers.getIsVisible(value)) { + return null; + } + return ( { - const [ipAdapters, setIPAdapters] = useState([]); +export const MetadataLayers = ({ metadata }: Props) => { + const [layers, setLayers] = useState([]); useEffect(() => { const parse = async () => { try { - const parsed = await handlers.ipAdaptersV2.parse(metadata); - setIPAdapters(parsed); + const parsed = await handlers.layers.parse(metadata); + setLayers(parsed); } catch (e) { - setIPAdapters([]); + setLayers([]); } }; parse(); }, [metadata]); - const label = useMemo(() => handlers.ipAdaptersV2.getLabel(), []); + const label = useMemo(() => handlers.layers.getLabel(), []); return ( <> - {ipAdapters.map((ipAdapter) => ( - + {layers.map((layer) => ( + ))} ); }; -const MetadataViewIPAdapter = ({ +const MetadataViewLayer = ({ label, - ipAdapter, + layer, handlers, }: { label: string; - ipAdapter: IPAdapterConfigV2Metadata; - handlers: MetadataHandlers; + layer: Layer; + handlers: MetadataHandlers; }) => { const onRecall = useCallback(() => { if (!handlers.recallItem) { return; } - handlers.recallItem(ipAdapter, true); - }, [handlers, ipAdapter]); + handlers.recallItem(layer, true); + }, [handlers, layer]); const [renderedValue, setRenderedValue] = useState(null); useEffect(() => { @@ -61,12 +57,12 @@ const MetadataViewIPAdapter = ({ setRenderedValue(null); return; } - const rendered = await handlers.renderItemValue(ipAdapter); + const rendered = await handlers.renderItemValue(layer); setRenderedValue(rendered); }; _renderValue(); - }, [handlers, ipAdapter]); + }, [handlers, layer]); return ; }; diff --git a/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdaptersV2.tsx b/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdaptersV2.tsx deleted file mode 100644 index 42d3de2ec2..0000000000 --- a/invokeai/frontend/web/src/features/metadata/components/MetadataT2IAdaptersV2.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { MetadataItemView } from 'features/metadata/components/MetadataItemView'; -import type { MetadataHandlers, T2IAdapterConfigV2Metadata } from 'features/metadata/types'; -import { handlers } from 'features/metadata/util/handlers'; -import { useCallback, useEffect, useMemo, useState } from 'react'; - -type Props = { - metadata: unknown; -}; - -export const MetadataT2IAdaptersV2 = ({ metadata }: Props) => { - const [t2iAdapters, setT2IAdapters] = useState([]); - - useEffect(() => { - const parse = async () => { - try { - const parsed = await handlers.t2iAdaptersV2.parse(metadata); - setT2IAdapters(parsed); - } catch (e) { - setT2IAdapters([]); - } - }; - parse(); - }, [metadata]); - - const label = useMemo(() => handlers.t2iAdaptersV2.getLabel(), []); - - return ( - <> - {t2iAdapters.map((t2iAdapter) => ( - - ))} - - ); -}; - -const MetadataViewT2IAdapter = ({ - label, - t2iAdapter, - handlers, -}: { - label: string; - t2iAdapter: T2IAdapterConfigV2Metadata; - handlers: MetadataHandlers; -}) => { - const onRecall = useCallback(() => { - if (!handlers.recallItem) { - return; - } - handlers.recallItem(t2iAdapter, true); - }, [handlers, t2iAdapter]); - - const [renderedValue, setRenderedValue] = useState(null); - useEffect(() => { - const _renderValue = async () => { - if (!handlers.renderItemValue) { - setRenderedValue(null); - return; - } - const rendered = await handlers.renderItemValue(t2iAdapter); - setRenderedValue(rendered); - }; - - _renderValue(); - }, [handlers, t2iAdapter]); - - return ; -}; diff --git a/invokeai/frontend/web/src/features/metadata/types.ts b/invokeai/frontend/web/src/features/metadata/types.ts index 30a34ec0c6..dfc1e828c9 100644 --- a/invokeai/frontend/web/src/features/metadata/types.ts +++ b/invokeai/frontend/web/src/features/metadata/types.ts @@ -1,9 +1,4 @@ import type { ControlNetConfig, IPAdapterConfig, T2IAdapterConfig } from 'features/controlAdapters/store/types'; -import type { - ControlNetConfigV2, - IPAdapterConfigV2, - T2IAdapterConfigV2, -} from 'features/controlLayers/util/controlAdapters'; import type { O } from 'ts-toolbelt'; /** @@ -50,6 +45,14 @@ export type MetadataParseFunc = (metadata: unknown) => Promise; */ export type MetadataValidateFunc = (value: T) => Promise; +/** + * A function that determines whether a metadata item should be visible. + * + * @param value The value to check. + * @returns True if the item should be visible, false otherwise. + */ +type MetadataGetIsVisibleFunc = (value: T) => boolean; + export type MetadataHandlers = { /** * Gets the label of the current metadata item as a string. @@ -111,6 +114,14 @@ export type MetadataHandlers = { * @returns The rendered item. */ renderItemValue?: MetadataRenderValueFunc; + /** + * Checks if a parsed metadata value should be visible. + * If not provided, the item is always visible. + * + * @param value The value to check. + * @returns True if the item should be visible, false otherwise. + */ + getIsVisible?: MetadataGetIsVisibleFunc; }; // TODO(psyche): The types for item handlers should be able to be inferred from the type of the value: @@ -127,6 +138,7 @@ type BuildMetadataHandlersArg = { getLabel: MetadataGetLabelFunc; renderValue?: MetadataRenderValueFunc; renderItemValue?: MetadataRenderValueFunc; + getIsVisible?: MetadataGetIsVisibleFunc; }; export type BuildMetadataHandlers = ( @@ -140,11 +152,3 @@ export type AnyControlAdapterConfigMetadata = | ControlNetConfigMetadata | T2IAdapterConfigMetadata | IPAdapterConfigMetadata; - -export type ControlNetConfigV2Metadata = O.NonNullable; -export type T2IAdapterConfigV2Metadata = O.NonNullable; -export type IPAdapterConfigV2Metadata = O.NonNullable; -export type AnyControlAdapterConfigV2Metadata = - | ControlNetConfigV2Metadata - | T2IAdapterConfigV2Metadata - | IPAdapterConfigV2Metadata; diff --git a/invokeai/frontend/web/src/features/metadata/util/handlers.ts b/invokeai/frontend/web/src/features/metadata/util/handlers.ts index 467f702cea..b0d0e22688 100644 --- a/invokeai/frontend/web/src/features/metadata/util/handlers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/handlers.ts @@ -1,9 +1,9 @@ import { objectKeys } from 'common/util/objectKeys'; import { toast } from 'common/util/toast'; +import type { Layer } from 'features/controlLayers/store/types'; import type { LoRA } from 'features/lora/store/loraSlice'; import type { AnyControlAdapterConfigMetadata, - AnyControlAdapterConfigV2Metadata, BuildMetadataHandlers, MetadataGetLabelFunc, MetadataHandlers, @@ -16,6 +16,7 @@ import { fetchModelConfig } from 'features/metadata/util/modelFetchingHelpers'; import { validators } from 'features/metadata/util/validators'; import type { ModelIdentifierField } from 'features/nodes/types/common'; import { t } from 'i18next'; +import { assert } from 'tsafe'; import { parsers } from './parsers'; import { recallers } from './recallers'; @@ -44,13 +45,48 @@ const renderControlAdapterValue: MetadataRenderValueFunc = async (value) => { - try { - const modelConfig = await fetchModelConfig(value.model.key ?? 'none'); - return `${modelConfig.name} (${modelConfig.base.toUpperCase()}) - ${value.weight}`; - } catch { - return `${value.model.key} (${value.model.base.toUpperCase()}) - ${value.weight}`; +const renderLayerValue: MetadataRenderValueFunc = async (layer) => { + if (layer.type === 'initial_image_layer') { + let rendered = t('controlLayers.globalInitialImageLayer'); + if (layer.image) { + rendered += ` (${layer.image})`; + } + return rendered; } + if (layer.type === 'control_adapter_layer') { + let rendered = t('controlLayers.globalControlAdapterLayer'); + const model = layer.controlAdapter.model; + if (model) { + rendered += ` (${model.name} - ${model.base.toUpperCase()})`; + } + return rendered; + } + if (layer.type === 'ip_adapter_layer') { + let rendered = t('controlLayers.globalIPAdapterLayer'); + const model = layer.ipAdapter.model; + if (model) { + rendered += ` (${model.name} - ${model.base.toUpperCase()})`; + } + return rendered; + } + if (layer.type === 'regional_guidance_layer') { + const rendered = t('controlLayers.regionalGuidanceLayer'); + const items: string[] = []; + if (layer.positivePrompt) { + items.push(`Positive: ${layer.positivePrompt}`); + } + if (layer.negativePrompt) { + items.push(`Negative: ${layer.negativePrompt}`); + } + if (layer.ipAdapters.length > 0) { + items.push(`${layer.ipAdapters.length} IP Adapters`); + } + return `${rendered} (${items.join(', ')})`; + } + assert(false, 'Unknown layer type'); +}; +const renderLayersValue: MetadataRenderValueFunc = async (layers) => { + return `${layers.length} ${t('controlLayers.layers', { count: layers.length })}`; }; const parameterSetToast = (parameter: string, description?: string) => { @@ -73,26 +109,6 @@ const parameterNotSetToast = (parameter: string, description?: string) => { }); }; -// const allParameterSetToast = (description?: string) => { -// toast({ -// title: t('toast.parametersSet'), -// status: 'info', -// description, -// duration: 2500, -// isClosable: true, -// }); -// }; - -// const allParameterNotSetToast = (description?: string) => { -// toast({ -// title: t('toast.parametersNotSet'), -// status: 'warning', -// description, -// duration: 2500, -// isClosable: true, -// }); -// }; - const buildParse = (arg: { parser: MetadataParseFunc; @@ -171,6 +187,7 @@ const buildHandlers: BuildMetadataHandlers = ({ itemValidator, renderValue, renderItemValue, + getIsVisible, }) => ({ parse: buildParse({ parser, getLabel }), parseItem: itemParser ? buildParseItem({ itemParser, getLabel }) : undefined, @@ -179,6 +196,7 @@ const buildHandlers: BuildMetadataHandlers = ({ getLabel, renderValue: renderValue ?? resolveToString, renderItemValue: renderItemValue ?? resolveToString, + getIsVisible, }); export const handlers = { @@ -198,12 +216,6 @@ export const handlers = { recaller: recallers.cfgScale, }), height: buildHandlers({ getLabel: () => t('metadata.height'), parser: parsers.height, recaller: recallers.height }), - initialImage: buildHandlers({ - getLabel: () => t('metadata.initImage'), - parser: parsers.initialImage, - recaller: recallers.initialImage, - renderValue: async (imageDTO) => imageDTO.image_name, - }), negativePrompt: buildHandlers({ getLabel: () => t('metadata.negativePrompt'), parser: parsers.negativePrompt, @@ -350,35 +362,17 @@ export const handlers = { itemValidator: validators.t2iAdapter, renderItemValue: renderControlAdapterValue, }), - controlNetsV2: buildHandlers({ - getLabel: () => t('common.controlNet'), - parser: parsers.controlNetsV2, - itemParser: parsers.controlNetV2, - recaller: recallers.controlNetsV2, - itemRecaller: recallers.controlNetV2, - validator: validators.controlNetsV2, - itemValidator: validators.controlNetV2, - renderItemValue: renderControlAdapterValueV2, - }), - ipAdaptersV2: buildHandlers({ - getLabel: () => t('common.ipAdapter'), - parser: parsers.ipAdaptersV2, - itemParser: parsers.ipAdapterV2, - recaller: recallers.ipAdaptersV2, - itemRecaller: recallers.ipAdapterV2, - validator: validators.ipAdaptersV2, - itemValidator: validators.ipAdapterV2, - renderItemValue: renderControlAdapterValueV2, - }), - t2iAdaptersV2: buildHandlers({ - getLabel: () => t('common.t2iAdapter'), - parser: parsers.t2iAdaptersV2, - itemParser: parsers.t2iAdapterV2, - recaller: recallers.t2iAdaptersV2, - itemRecaller: recallers.t2iAdapterV2, - validator: validators.t2iAdaptersV2, - itemValidator: validators.t2iAdapterV2, - renderItemValue: renderControlAdapterValueV2, + layers: buildHandlers({ + getLabel: () => t('controlLayers.layers_one'), + parser: parsers.layers, + itemParser: parsers.layer, + recaller: recallers.layers, + itemRecaller: recallers.layer, + validator: validators.layers, + itemValidator: validators.layer, + renderItemValue: renderLayerValue, + renderValue: renderLayersValue, + getIsVisible: (value) => value.length > 0, }), } as const; @@ -435,9 +429,9 @@ export const parseAndRecallImageDimensions = async (metadata: unknown) => { }; // These handlers should be omitted when recalling to control layers -const TO_CONTROL_LAYERS_SKIP_KEYS: (keyof typeof handlers)[] = ['controlNets', 'ipAdapters', 't2iAdapters']; +const TO_CONTROL_LAYERS_SKIP_KEYS: (keyof typeof handlers)[] = ['controlNets', 'ipAdapters', 't2iAdapters', 'strength']; // These handlers should be omitted when recalling to the rest of the app -const NOT_TO_CONTROL_LAYERS_SKIP_KEYS: (keyof typeof handlers)[] = ['controlNetsV2', 'ipAdaptersV2', 't2iAdaptersV2']; +const NOT_TO_CONTROL_LAYERS_SKIP_KEYS: (keyof typeof handlers)[] = ['layers']; export const parseAndRecallAllMetadata = async ( metadata: unknown, diff --git a/invokeai/frontend/web/src/features/metadata/util/modelFetchingHelpers.ts b/invokeai/frontend/web/src/features/metadata/util/modelFetchingHelpers.ts index a237582ed8..a2db414937 100644 --- a/invokeai/frontend/web/src/features/metadata/util/modelFetchingHelpers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/modelFetchingHelpers.ts @@ -1,4 +1,5 @@ import { getStore } from 'app/store/nanostores/store'; +import type { ModelIdentifierField } from 'features/nodes/types/common'; import { isModelIdentifier, isModelIdentifierV2 } from 'features/nodes/types/common'; import { modelsApi } from 'services/api/endpoints/models'; import type { AnyModelConfig, BaseModelType, ModelType } from 'services/api/types'; @@ -68,6 +69,24 @@ const fetchModelConfigByAttrs = async (name: string, base: BaseModelType, type: } }; +/** + * Fetches the model config given an identifier. First attempts to fetch by key, then falls back to fetching by attrs. + * @param identifier The model identifier. + * @returns A promise that resolves to the model config. + * @throws {ModelConfigNotFoundError} If the model config is unable to be fetched. + */ +export const fetchModelConfigByIdentifier = async (identifier: ModelIdentifierField): Promise => { + try { + return await fetchModelConfig(identifier.key); + } catch { + try { + return await fetchModelConfigByAttrs(identifier.name, identifier.base, identifier.type); + } catch { + throw new ModelConfigNotFoundError(`Unable to retrieve model config for identifier ${identifier}`); + } + } +}; + /** * Fetches the model config for a given model key and type, and ensures that the model config is of a specific type. * @param key The model key. diff --git a/invokeai/frontend/web/src/features/metadata/util/parsers.ts b/invokeai/frontend/web/src/features/metadata/util/parsers.ts index 8641977b1f..0757d2e8db 100644 --- a/invokeai/frontend/web/src/features/metadata/util/parsers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/parsers.ts @@ -1,10 +1,12 @@ -import { getStore } from 'app/store/nanostores/store'; import { initialControlNet, initialIPAdapter, initialT2IAdapter, } from 'features/controlAdapters/util/buildControlAdapter'; import { buildControlAdapterProcessor } from 'features/controlAdapters/util/buildControlAdapterProcessor'; +import { getCALayerId, getIPALayerId, INITIAL_IMAGE_LAYER_ID } from 'features/controlLayers/store/controlLayersSlice'; +import type { ControlAdapterLayer, InitialImageLayer, IPAdapterLayer, Layer } from 'features/controlLayers/store/types'; +import { zLayer } from 'features/controlLayers/store/types'; import { CA_PROCESSOR_DATA, imageDTOToImageWithDims, @@ -17,12 +19,9 @@ import type { LoRA } from 'features/lora/store/loraSlice'; import { defaultLoRAConfig } from 'features/lora/store/loraSlice'; import type { ControlNetConfigMetadata, - ControlNetConfigV2Metadata, IPAdapterConfigMetadata, - IPAdapterConfigV2Metadata, MetadataParseFunc, T2IAdapterConfigMetadata, - T2IAdapterConfigV2Metadata, } from 'features/metadata/types'; import { fetchModelConfigWithTypeGuard, getModelKey } from 'features/metadata/util/modelFetchingHelpers'; import { zControlField, zIPAdapterField, zModelIdentifierField, zT2IAdapterField } from 'features/nodes/types/common'; @@ -69,8 +68,7 @@ import { isParameterWidth, } from 'features/parameters/types/parameterSchemas'; import { get, isArray, isString } from 'lodash-es'; -import { getImageDTO, imagesApi } from 'services/api/endpoints/images'; -import type { ImageDTO } from 'services/api/types'; +import { getImageDTO } from 'services/api/endpoints/images'; import { isControlNetModelConfig, isIPAdapterModelConfig, @@ -80,6 +78,7 @@ import { isT2IAdapterModelConfig, isVAEModelConfig, } from 'services/api/types'; +import { assert } from 'tsafe'; import { v4 as uuidv4 } from 'uuid'; export const MetadataParsePendingToken = Symbol('pending'); @@ -149,14 +148,6 @@ const parseCFGRescaleMultiplier: MetadataParseFunc = (metadata) => getProperty(metadata, 'scheduler', isParameterScheduler); -const parseInitialImage: MetadataParseFunc = async (metadata) => { - const imageName = await getProperty(metadata, 'init_image', isString); - const imageDTORequest = getStore().dispatch(imagesApi.endpoints.getImageDTO.initiate(imageName)); - const imageDTO = await imageDTORequest.unwrap(); - imageDTORequest.unsubscribe(); - return imageDTO; -}; - const parseWidth: MetadataParseFunc = (metadata) => getProperty(metadata, 'width', isParameterWidth); const parseHeight: MetadataParseFunc = (metadata) => @@ -309,7 +300,7 @@ const parseControlNet: MetadataParseFunc = async (meta const parseAllControlNets: MetadataParseFunc = async (metadata) => { try { - const controlNetsRaw = await getProperty(metadata, 'controlnets', isArray || undefined); + const controlNetsRaw = await getProperty(metadata, 'controlnets', isArray); const parseResults = await Promise.allSettled(controlNetsRaw.map((cn) => parseControlNet(cn))); const controlNets = parseResults .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') @@ -439,8 +430,103 @@ const parseAllIPAdapters: MetadataParseFunc = async ( } }; -//#region V2/Control Layers -const parseControlNetV2: MetadataParseFunc = async (metadataItem) => { +//#region Control Layers +const parseLayer: MetadataParseFunc = async (metadataItem) => zLayer.parseAsync(metadataItem); + +const parseLayers: MetadataParseFunc = async (metadata) => { + // We need to support recalling pre-Control Layers metadata into Control Layers. A separate set of parsers handles + // taking pre-CL metadata and parsing it into layers. It doesn't always map 1-to-1, so this is best-effort. For + // example, CL Control Adapters don't support resize mode, so we simply omit that property. + + try { + const layers: Layer[] = []; + + try { + const control_layers = await getProperty(metadata, 'control_layers'); + const controlLayersRaw = await getProperty(control_layers, 'layers', isArray); + const controlLayersParseResults = await Promise.allSettled(controlLayersRaw.map(parseLayer)); + const controlLayers = controlLayersParseResults + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .map((result) => result.value); + layers.push(...controlLayers); + } catch { + // no-op + } + + try { + const controlNetsRaw = await getProperty(metadata, 'controlnets', isArray); + const controlNetsParseResults = await Promise.allSettled( + controlNetsRaw.map(async (cn) => await parseControlNetToControlAdapterLayer(cn)) + ); + const controlNetsAsLayers = controlNetsParseResults + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .map((result) => result.value); + layers.push(...controlNetsAsLayers); + } catch { + // no-op + } + + try { + const t2iAdaptersRaw = await getProperty(metadata, 't2iAdapters', isArray); + const t2iAdaptersParseResults = await Promise.allSettled( + t2iAdaptersRaw.map(async (cn) => await parseT2IAdapterToControlAdapterLayer(cn)) + ); + const t2iAdaptersAsLayers = t2iAdaptersParseResults + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .map((result) => result.value); + layers.push(...t2iAdaptersAsLayers); + } catch { + // no-op + } + + try { + const ipAdaptersRaw = await getProperty(metadata, 'ipAdapters', isArray); + const ipAdaptersParseResults = await Promise.allSettled( + ipAdaptersRaw.map(async (cn) => await parseIPAdapterToIPAdapterLayer(cn)) + ); + const ipAdaptersAsLayers = ipAdaptersParseResults + .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') + .map((result) => result.value); + layers.push(...ipAdaptersAsLayers); + } catch { + // no-op + } + + try { + const initialImageLayer = await parseInitialImageToInitialImageLayer(metadata); + layers.push(initialImageLayer); + } catch { + // no-op + } + + return layers; + } catch { + return []; + } +}; + +const parseInitialImageToInitialImageLayer: MetadataParseFunc = async (metadata) => { + const denoisingStrength = await getProperty(metadata, 'strength', isParameterStrength); + const imageName = await getProperty(metadata, 'init_image', isString); + const imageDTO = await getImageDTO(imageName); + assert(imageDTO, 'ImageDTO is null'); + const layer: InitialImageLayer = { + id: INITIAL_IMAGE_LAYER_ID, + type: 'initial_image_layer', + bbox: null, + bboxNeedsUpdate: true, + x: 0, + y: 0, + isEnabled: true, + opacity: 1, + image: imageDTOToImageWithDims(imageDTO), + isSelected: true, + denoisingStrength, + }; + return layer; +}; + +const parseControlNetToControlAdapterLayer: MetadataParseFunc = async (metadataItem) => { const control_model = await getProperty(metadataItem, 'control_model'); const key = await getModelKey(control_model, 'controlnet'); const controlNetModel = await fetchModelConfigWithTypeGuard(key, isControlNetModelConfig); @@ -469,7 +555,6 @@ const parseControlNetV2: MetadataParseFunc = async ( .catch(null) .parse(await getProperty(metadataItem, 'control_mode')); - const id = uuidv4(); const defaultPreprocessor = controlNetModel.default_settings?.preprocessor; const processorConfig = isProcessorTypeV2(defaultPreprocessor) ? CA_PROCESSOR_DATA[defaultPreprocessor].buildDefaults() @@ -481,36 +566,35 @@ const parseControlNetV2: MetadataParseFunc = async ( const imageDTO = image ? await getImageDTO(image.image_name) : null; const processedImageDTO = processedImage ? await getImageDTO(processedImage.image_name) : null; - const controlNet: ControlNetConfigV2Metadata = { - id, - type: 'controlnet', - model: zModelIdentifierField.parse(controlNetModel), - weight: typeof control_weight === 'number' ? control_weight : initialControlNetV2.weight, - beginEndStepPct, - controlMode: control_mode ?? initialControlNetV2.controlMode, - image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, - processedImage: processedImageDTO ? imageDTOToImageWithDims(processedImageDTO) : null, - processorConfig, - isProcessingImage: false, + const layer: ControlAdapterLayer = { + id: getCALayerId(uuidv4()), + bbox: null, + bboxNeedsUpdate: true, + isEnabled: true, + isFilterEnabled: true, + isSelected: true, + opacity: 1, + type: 'control_adapter_layer', + x: 0, + y: 0, + controlAdapter: { + id: uuidv4(), + type: 'controlnet', + model: zModelIdentifierField.parse(controlNetModel), + weight: typeof control_weight === 'number' ? control_weight : initialControlNetV2.weight, + beginEndStepPct, + controlMode: control_mode ?? initialControlNetV2.controlMode, + image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, + processedImage: processedImageDTO ? imageDTOToImageWithDims(processedImageDTO) : null, + processorConfig, + processorPendingBatchId: null, + }, }; - return controlNet; + return layer; }; -const parseAllControlNetsV2: MetadataParseFunc = async (metadata) => { - try { - const controlNetsRaw = await getProperty(metadata, 'controlnets', isArray || undefined); - const parseResults = await Promise.allSettled(controlNetsRaw.map((cn) => parseControlNetV2(cn))); - const controlNets = parseResults - .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') - .map((result) => result.value); - return controlNets; - } catch { - return []; - } -}; - -const parseT2IAdapterV2: MetadataParseFunc = async (metadataItem) => { +const parseT2IAdapterToControlAdapterLayer: MetadataParseFunc = async (metadataItem) => { const t2i_adapter_model = await getProperty(metadataItem, 't2i_adapter_model'); const key = await getModelKey(t2i_adapter_model, 't2i_adapter'); const t2iAdapterModel = await fetchModelConfigWithTypeGuard(key, isT2IAdapterModelConfig); @@ -536,7 +620,6 @@ const parseT2IAdapterV2: MetadataParseFunc = async ( .catch(null) .parse(await getProperty(metadataItem, 'end_step_percent')); - const id = uuidv4(); const defaultPreprocessor = t2iAdapterModel.default_settings?.preprocessor; const processorConfig = isProcessorTypeV2(defaultPreprocessor) ? CA_PROCESSOR_DATA[defaultPreprocessor].buildDefaults() @@ -548,35 +631,34 @@ const parseT2IAdapterV2: MetadataParseFunc = async ( const imageDTO = image ? await getImageDTO(image.image_name) : null; const processedImageDTO = processedImage ? await getImageDTO(processedImage.image_name) : null; - const t2iAdapter: T2IAdapterConfigV2Metadata = { - id, - type: 't2i_adapter', - model: zModelIdentifierField.parse(t2iAdapterModel), - weight: typeof weight === 'number' ? weight : initialT2IAdapterV2.weight, - beginEndStepPct, - image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, - processedImage: processedImageDTO ? imageDTOToImageWithDims(processedImageDTO) : null, - processorConfig, - isProcessingImage: false, + const layer: ControlAdapterLayer = { + id: getCALayerId(uuidv4()), + bbox: null, + bboxNeedsUpdate: true, + isEnabled: true, + isFilterEnabled: true, + isSelected: true, + opacity: 1, + type: 'control_adapter_layer', + x: 0, + y: 0, + controlAdapter: { + id: uuidv4(), + type: 't2i_adapter', + model: zModelIdentifierField.parse(t2iAdapterModel), + weight: typeof weight === 'number' ? weight : initialT2IAdapterV2.weight, + beginEndStepPct, + image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, + processedImage: processedImageDTO ? imageDTOToImageWithDims(processedImageDTO) : null, + processorConfig, + processorPendingBatchId: null, + }, }; - return t2iAdapter; + return layer; }; -const parseAllT2IAdaptersV2: MetadataParseFunc = async (metadata) => { - try { - const t2iAdaptersRaw = await getProperty(metadata, 't2iAdapters', isArray); - const parseResults = await Promise.allSettled(t2iAdaptersRaw.map((t2iAdapter) => parseT2IAdapterV2(t2iAdapter))); - const t2iAdapters = parseResults - .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') - .map((result) => result.value); - return t2iAdapters; - } catch { - return []; - } -}; - -const parseIPAdapterV2: MetadataParseFunc = async (metadataItem) => { +const parseIPAdapterToIPAdapterLayer: MetadataParseFunc = async (metadataItem) => { const ip_adapter_model = await getProperty(metadataItem, 'ip_adapter_model'); const key = await getModelKey(ip_adapter_model, 'ip_adapter'); const ipAdapterModel = await fetchModelConfigWithTypeGuard(key, isIPAdapterModelConfig); @@ -602,39 +684,32 @@ const parseIPAdapterV2: MetadataParseFunc = async (me .catch(null) .parse(await getProperty(metadataItem, 'end_step_percent')); - const id = uuidv4(); const beginEndStepPct: [number, number] = [ begin_step_percent ?? initialIPAdapterV2.beginEndStepPct[0], end_step_percent ?? initialIPAdapterV2.beginEndStepPct[1], ]; const imageDTO = image ? await getImageDTO(image.image_name) : null; - const ipAdapter: IPAdapterConfigV2Metadata = { - id, - type: 'ip_adapter', - model: zModelIdentifierField.parse(ipAdapterModel), - weight: typeof weight === 'number' ? weight : initialIPAdapterV2.weight, - beginEndStepPct, - image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, - clipVisionModel: initialIPAdapterV2.clipVisionModel, // TODO: This needs to be added to the zIPAdapterField... - method: method ?? initialIPAdapterV2.method, + const layer: IPAdapterLayer = { + id: getIPALayerId(uuidv4()), + type: 'ip_adapter_layer', + isEnabled: true, + isSelected: true, + ipAdapter: { + id: uuidv4(), + type: 'ip_adapter', + model: zModelIdentifierField.parse(ipAdapterModel), + weight: typeof weight === 'number' ? weight : initialIPAdapterV2.weight, + beginEndStepPct, + image: imageDTO ? imageDTOToImageWithDims(imageDTO) : null, + clipVisionModel: initialIPAdapterV2.clipVisionModel, // TODO: This needs to be added to the zIPAdapterField... + method: method ?? initialIPAdapterV2.method, + }, }; - return ipAdapter; -}; - -const parseAllIPAdaptersV2: MetadataParseFunc = async (metadata) => { - try { - const ipAdaptersRaw = await getProperty(metadata, 'ipAdapters', isArray); - const parseResults = await Promise.allSettled(ipAdaptersRaw.map((ipAdapter) => parseIPAdapterV2(ipAdapter))); - const ipAdapters = parseResults - .filter((result): result is PromiseFulfilledResult => result.status === 'fulfilled') - .map((result) => result.value); - return ipAdapters; - } catch { - return []; - } + return layer; }; +//#endregion export const parsers = { createdBy: parseCreatedBy, @@ -647,7 +722,6 @@ export const parsers = { cfgScale: parseCFGScale, cfgRescaleMultiplier: parseCFGRescaleMultiplier, scheduler: parseScheduler, - initialImage: parseInitialImage, width: parseWidth, height: parseHeight, steps: parseSteps, @@ -672,10 +746,9 @@ export const parsers = { t2iAdapters: parseAllT2IAdapters, ipAdapter: parseIPAdapter, ipAdapters: parseAllIPAdapters, - controlNetV2: parseControlNetV2, - controlNetsV2: parseAllControlNetsV2, - t2iAdapterV2: parseT2IAdapterV2, - t2iAdaptersV2: parseAllT2IAdaptersV2, - ipAdapterV2: parseIPAdapterV2, - ipAdaptersV2: parseAllIPAdaptersV2, + controlNetToControlLayer: parseControlNetToControlAdapterLayer, + t2iAdapterToControlAdapterLayer: parseT2IAdapterToControlAdapterLayer, + ipAdapterToIPAdapterLayer: parseIPAdapterToIPAdapterLayer, + layer: parseLayer, + layers: parseLayers, } as const; diff --git a/invokeai/frontend/web/src/features/metadata/util/recallers.ts b/invokeai/frontend/web/src/features/metadata/util/recallers.ts index b29d937159..5a17fd4b5d 100644 --- a/invokeai/frontend/web/src/features/metadata/util/recallers.ts +++ b/invokeai/frontend/web/src/features/metadata/util/recallers.ts @@ -1,4 +1,5 @@ import { getStore } from 'app/store/nanostores/store'; +import { deepClone } from 'common/util/deepClone'; import { controlAdapterRecalled, controlNetsReset, @@ -6,31 +7,32 @@ import { t2iAdaptersReset, } from 'features/controlAdapters/store/controlAdaptersSlice'; import { - caLayerAdded, - caLayerControlNetsDeleted, - caLayerT2IAdaptersDeleted, + allLayersDeleted, + caLayerRecalled, + getCALayerId, + getIPALayerId, + getRGLayerId, heightChanged, - iiLayerAdded, - ipaLayerAdded, - ipaLayersDeleted, + iiLayerRecalled, + ipaLayerRecalled, negativePrompt2Changed, negativePromptChanged, positivePrompt2Changed, positivePromptChanged, + rgLayerRecalled, widthChanged, } from 'features/controlLayers/store/controlLayersSlice'; +import type { Layer } from 'features/controlLayers/store/types'; import { setHrfEnabled, setHrfMethod, setHrfStrength } from 'features/hrf/store/hrfSlice'; import type { LoRA } from 'features/lora/store/loraSlice'; import { loraRecalled, lorasReset } from 'features/lora/store/loraSlice'; import type { ControlNetConfigMetadata, - ControlNetConfigV2Metadata, IPAdapterConfigMetadata, - IPAdapterConfigV2Metadata, MetadataRecallFunc, T2IAdapterConfigMetadata, - T2IAdapterConfigV2Metadata, } from 'features/metadata/types'; +import { fetchModelConfigByIdentifier } from 'features/metadata/util/modelFetchingHelpers'; import { modelSelected } from 'features/parameters/store/actions'; import { setCfgRescaleMultiplier, @@ -72,7 +74,8 @@ import { setRefinerStart, setRefinerSteps, } from 'features/sdxl/store/sdxlSlice'; -import type { ImageDTO } from 'services/api/types'; +import { getImageDTO } from 'services/api/endpoints/images'; +import { v4 as uuidv4 } from 'uuid'; const recallPositivePrompt: MetadataRecallFunc = (positivePrompt) => { getStore().dispatch(positivePromptChanged(positivePrompt)); @@ -106,10 +109,6 @@ const recallScheduler: MetadataRecallFunc = (scheduler) => { getStore().dispatch(setScheduler(scheduler)); }; -const recallInitialImage: MetadataRecallFunc = async (imageDTO) => { - getStore().dispatch(iiLayerAdded(imageDTO)); -}; - const setSizeOptions = { updateAspectRatio: true, clamp: true }; const recallWidth: MetadataRecallFunc = (width) => { @@ -244,50 +243,96 @@ const recallIPAdapters: MetadataRecallFunc = (ipAdapt }); }; -//#region V2/Control Layer -const recallControlNetV2: MetadataRecallFunc = (controlNet) => { - getStore().dispatch(caLayerAdded(controlNet)); -}; - -const recallControlNetsV2: MetadataRecallFunc = (controlNets) => { +//#region Control Layers +const recallLayer: MetadataRecallFunc = async (layer) => { const { dispatch } = getStore(); - dispatch(caLayerControlNetsDeleted()); - if (!controlNets.length) { + // We need to check for the existence of all images and models when recalling. If they do not exist, SMITE THEM! + // Also, we need fresh IDs for all objects when recalling, to prevent multiple layers with the same ID. + if (layer.type === 'control_adapter_layer') { + const clone = deepClone(layer); + if (clone.controlAdapter.image) { + const imageDTO = await getImageDTO(clone.controlAdapter.image.name); + if (!imageDTO) { + clone.controlAdapter.image = null; + } + } + if (clone.controlAdapter.processedImage) { + const imageDTO = await getImageDTO(clone.controlAdapter.processedImage.name); + if (!imageDTO) { + clone.controlAdapter.processedImage = null; + } + } + if (clone.controlAdapter.model) { + try { + await fetchModelConfigByIdentifier(clone.controlAdapter.model); + } catch { + clone.controlAdapter.model = null; + } + } + clone.id = getCALayerId(uuidv4()); + clone.controlAdapter.id = uuidv4(); + dispatch(caLayerRecalled(clone)); return; } - controlNets.forEach((controlNet) => { - dispatch(caLayerAdded(controlNet)); - }); -}; - -const recallT2IAdapterV2: MetadataRecallFunc = (t2iAdapter) => { - getStore().dispatch(caLayerAdded(t2iAdapter)); -}; - -const recallT2IAdaptersV2: MetadataRecallFunc = (t2iAdapters) => { - const { dispatch } = getStore(); - dispatch(caLayerT2IAdaptersDeleted()); - if (!t2iAdapters.length) { + if (layer.type === 'ip_adapter_layer') { + const clone = deepClone(layer); + if (clone.ipAdapter.image) { + const imageDTO = await getImageDTO(clone.ipAdapter.image.name); + if (!imageDTO) { + clone.ipAdapter.image = null; + } + } + if (clone.ipAdapter.model) { + try { + await fetchModelConfigByIdentifier(clone.ipAdapter.model); + } catch { + clone.ipAdapter.model = null; + } + } + clone.id = getIPALayerId(uuidv4()); + clone.ipAdapter.id = uuidv4(); + dispatch(ipaLayerRecalled(clone)); return; } - t2iAdapters.forEach((t2iAdapters) => { - dispatch(caLayerAdded(t2iAdapters)); - }); -}; -const recallIPAdapterV2: MetadataRecallFunc = (ipAdapter) => { - getStore().dispatch(ipaLayerAdded(ipAdapter)); -}; + if (layer.type === 'regional_guidance_layer') { + const clone = deepClone(layer); + // Strip out the uploaded mask image property - this is an intermediate image + clone.uploadedMaskImage = null; -const recallIPAdaptersV2: MetadataRecallFunc = (ipAdapters) => { - const { dispatch } = getStore(); - dispatch(ipaLayersDeleted()); - if (!ipAdapters.length) { + for (const ipAdapter of clone.ipAdapters) { + if (ipAdapter.image) { + const imageDTO = await getImageDTO(ipAdapter.image.name); + if (!imageDTO) { + ipAdapter.image = null; + } + } + if (ipAdapter.model) { + try { + await fetchModelConfigByIdentifier(ipAdapter.model); + } catch { + ipAdapter.model = null; + } + } + ipAdapter.id = uuidv4(); + } + clone.id = getRGLayerId(uuidv4()); + dispatch(rgLayerRecalled(clone)); return; } - ipAdapters.forEach((ipAdapter) => { - dispatch(ipaLayerAdded(ipAdapter)); - }); + + if (layer.type === 'initial_image_layer') { + dispatch(iiLayerRecalled(layer)); + return; + } +}; + +const recallLayers: MetadataRecallFunc = (layers) => { + const { dispatch } = getStore(); + dispatch(allLayersDeleted()); + for (const l of layers) { + recallLayer(l); + } }; export const recallers = { @@ -299,7 +344,6 @@ export const recallers = { cfgScale: recallCFGScale, cfgRescaleMultiplier: recallCFGRescaleMultiplier, scheduler: recallScheduler, - initialImage: recallInitialImage, width: recallWidth, height: recallHeight, steps: recallSteps, @@ -324,10 +368,6 @@ export const recallers = { t2iAdapter: recallT2IAdapter, ipAdapters: recallIPAdapters, ipAdapter: recallIPAdapter, - controlNetV2: recallControlNetV2, - controlNetsV2: recallControlNetsV2, - t2iAdapterV2: recallT2IAdapterV2, - t2iAdaptersV2: recallT2IAdaptersV2, - ipAdapterV2: recallIPAdapterV2, - ipAdaptersV2: recallIPAdaptersV2, + layer: recallLayer, + layers: recallLayers, } as const; diff --git a/invokeai/frontend/web/src/features/metadata/util/validators.ts b/invokeai/frontend/web/src/features/metadata/util/validators.ts index d09321003f..759e8ba561 100644 --- a/invokeai/frontend/web/src/features/metadata/util/validators.ts +++ b/invokeai/frontend/web/src/features/metadata/util/validators.ts @@ -1,17 +1,16 @@ import { getStore } from 'app/store/nanostores/store'; +import type { Layer } from 'features/controlLayers/store/types'; import type { LoRA } from 'features/lora/store/loraSlice'; import type { ControlNetConfigMetadata, - ControlNetConfigV2Metadata, IPAdapterConfigMetadata, - IPAdapterConfigV2Metadata, MetadataValidateFunc, T2IAdapterConfigMetadata, - T2IAdapterConfigV2Metadata, } from 'features/metadata/types'; import { InvalidModelConfigError } from 'features/metadata/util/modelFetchingHelpers'; import type { ParameterSDXLRefinerModel, ParameterVAEModel } from 'features/parameters/types/parameterSchemas'; import type { BaseModelType } from 'services/api/types'; +import { assert } from 'tsafe'; /** * Checks the given base model type against the currently-selected model's base type and throws an error if they are @@ -111,58 +110,39 @@ const validateIPAdapters: MetadataValidateFunc = (ipA return new Promise((resolve) => resolve(validatedIPAdapters)); }; -const validateControlNetV2: MetadataValidateFunc = (controlNet) => { - validateBaseCompatibility(controlNet.model?.base, 'ControlNet incompatible with currently-selected model'); - return new Promise((resolve) => resolve(controlNet)); -}; - -const validateControlNetsV2: MetadataValidateFunc = (controlNets) => { - const validatedControlNets: ControlNetConfigV2Metadata[] = []; - controlNets.forEach((controlNet) => { - try { - validateBaseCompatibility(controlNet.model?.base, 'ControlNet incompatible with currently-selected model'); - validatedControlNets.push(controlNet); - } catch { - // This is a no-op - we want to continue validating the rest of the ControlNets, and an empty list is valid. +const validateLayer: MetadataValidateFunc = async (layer) => { + if (layer.type === 'control_adapter_layer') { + const model = layer.controlAdapter.model; + assert(model, 'Control Adapter layer missing model'); + validateBaseCompatibility(model.base, 'Layer incompatible with currently-selected model'); + } + if (layer.type === 'ip_adapter_layer') { + const model = layer.ipAdapter.model; + assert(model, 'IP Adapter layer missing model'); + validateBaseCompatibility(model.base, 'Layer incompatible with currently-selected model'); + } + if (layer.type === 'regional_guidance_layer') { + for (const ipa of layer.ipAdapters) { + const model = ipa.model; + assert(model, 'IP Adapter layer missing model'); + validateBaseCompatibility(model.base, 'Layer incompatible with currently-selected model'); } - }); - return new Promise((resolve) => resolve(validatedControlNets)); + } + + return layer; }; -const validateT2IAdapterV2: MetadataValidateFunc = (t2iAdapter) => { - validateBaseCompatibility(t2iAdapter.model?.base, 'T2I Adapter incompatible with currently-selected model'); - return new Promise((resolve) => resolve(t2iAdapter)); -}; - -const validateT2IAdaptersV2: MetadataValidateFunc = (t2iAdapters) => { - const validatedT2IAdapters: T2IAdapterConfigV2Metadata[] = []; - t2iAdapters.forEach((t2iAdapter) => { +const validateLayers: MetadataValidateFunc = async (layers) => { + const validatedLayers: Layer[] = []; + for (const l of layers) { try { - validateBaseCompatibility(t2iAdapter.model?.base, 'T2I Adapter incompatible with currently-selected model'); - validatedT2IAdapters.push(t2iAdapter); + const validated = await validateLayer(l); + validatedLayers.push(validated); } catch { - // This is a no-op - we want to continue validating the rest of the T2I Adapters, and an empty list is valid. + // This is a no-op - we want to continue validating the rest of the layers, and an empty list is valid. } - }); - return new Promise((resolve) => resolve(validatedT2IAdapters)); -}; - -const validateIPAdapterV2: MetadataValidateFunc = (ipAdapter) => { - validateBaseCompatibility(ipAdapter.model?.base, 'IP Adapter incompatible with currently-selected model'); - return new Promise((resolve) => resolve(ipAdapter)); -}; - -const validateIPAdaptersV2: MetadataValidateFunc = (ipAdapters) => { - const validatedIPAdapters: IPAdapterConfigV2Metadata[] = []; - ipAdapters.forEach((ipAdapter) => { - try { - validateBaseCompatibility(ipAdapter.model?.base, 'IP Adapter incompatible with currently-selected model'); - validatedIPAdapters.push(ipAdapter); - } catch { - // This is a no-op - we want to continue validating the rest of the IP Adapters, and an empty list is valid. - } - }); - return new Promise((resolve) => resolve(validatedIPAdapters)); + } + return new Promise((resolve) => resolve(validatedLayers)); }; export const validators = { @@ -176,10 +156,6 @@ export const validators = { t2iAdapters: validateT2IAdapters, ipAdapter: validateIPAdapter, ipAdapters: validateIPAdapters, - controlNetV2: validateControlNetV2, - controlNetsV2: validateControlNetsV2, - t2iAdapterV2: validateT2IAdapterV2, - t2iAdaptersV2: validateT2IAdaptersV2, - ipAdapterV2: validateIPAdapterV2, - ipAdaptersV2: validateIPAdaptersV2, + layer: validateLayer, + layers: validateLayers, } as const; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx index 061209cafc..95104c683c 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/AddNodePopover/AddNodePopover.tsx @@ -2,26 +2,34 @@ import 'reactflow/dist/style.css'; import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui-library'; import { Combobox, Flex, Popover, PopoverAnchor, PopoverBody, PopoverContent } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { useAppToaster } from 'app/components/Toaster'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useAppDispatch, useAppStore } from 'app/store/storeHooks'; import type { SelectInstance } from 'chakra-react-select'; import { useBuildNode } from 'features/nodes/hooks/useBuildNode'; import { - addNodePopoverClosed, - addNodePopoverOpened, + $cursorPos, + $isAddNodePopoverOpen, + $pendingConnection, + $templates, + closeAddNodePopover, + connectionMade, nodeAdded, - selectNodesSlice, + openAddNodePopover, } from 'features/nodes/store/nodesSlice'; +import { getFirstValidConnection } from 'features/nodes/store/util/findConnectionToValidHandle'; import { validateSourceAndTargetTypes } from 'features/nodes/store/util/validateSourceAndTargetTypes'; +import type { AnyNode } from 'features/nodes/types/invocation'; +import { isInvocationNode } from 'features/nodes/types/invocation'; import { filter, map, memoize, some } from 'lodash-es'; import type { KeyboardEventHandler } from 'react'; -import { memo, useCallback, useRef } from 'react'; +import { memo, useCallback, useMemo, useRef } from 'react'; import { flushSync } from 'react-dom'; import { useHotkeys } from 'react-hotkeys-hook'; import type { HotkeyCallback } from 'react-hotkeys-hook/dist/types'; import { useTranslation } from 'react-i18next'; import type { FilterOptionOption } from 'react-select/dist/declarations/src/filters'; +import { assert } from 'tsafe'; const createRegex = memoize( (inputValue: string) => @@ -54,26 +62,30 @@ const AddNodePopover = () => { const { t } = useTranslation(); const selectRef = useRef | null>(null); const inputRef = useRef(null); + const templates = useStore($templates); + const pendingConnection = useStore($pendingConnection); + const isOpen = useStore($isAddNodePopoverOpen); + const store = useAppStore(); - const fieldFilter = useAppSelector((s) => s.nodes.connectionStartFieldType); - const handleFilter = useAppSelector((s) => s.nodes.connectionStartParams?.handleType); - - const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { + const filteredTemplates = useMemo(() => { // If we have a connection in progress, we need to filter the node choices - const filteredNodeTemplates = fieldFilter - ? filter(nodes.templates, (template) => { - const handles = handleFilter === 'source' ? template.inputs : template.outputs; + if (!pendingConnection) { + return map(templates); + } - return some(handles, (handle) => { - const sourceType = handleFilter === 'source' ? fieldFilter : handle.type; - const targetType = handleFilter === 'target' ? fieldFilter : handle.type; + return filter(templates, (template) => { + const pendingFieldKind = pendingConnection.fieldTemplate.fieldKind; + const fields = pendingFieldKind === 'input' ? template.outputs : template.inputs; + return some(fields, (field) => { + const sourceType = pendingFieldKind === 'input' ? field.type : pendingConnection.fieldTemplate.type; + const targetType = pendingFieldKind === 'output' ? field.type : pendingConnection.fieldTemplate.type; + return validateSourceAndTargetTypes(sourceType, targetType); + }); + }); + }, [templates, pendingConnection]); - return validateSourceAndTargetTypes(sourceType, targetType); - }); - }) - : map(nodes.templates); - - const options: ComboboxOption[] = map(filteredNodeTemplates, (template) => { + const options = useMemo(() => { + const _options: ComboboxOption[] = map(filteredTemplates, (template) => { return { label: template.title, value: template.type, @@ -83,15 +95,15 @@ const AddNodePopover = () => { }); //We only want these nodes if we're not filtered - if (fieldFilter === null) { - options.push({ + if (!pendingConnection) { + _options.push({ label: t('nodes.currentImage'), value: 'current_image', description: t('nodes.currentImageDescription'), tags: ['progress'], }); - options.push({ + _options.push({ label: t('nodes.notes'), value: 'notes', description: t('nodes.notesDescription'), @@ -99,18 +111,15 @@ const AddNodePopover = () => { }); } - options.sort((a, b) => a.label.localeCompare(b.label)); + _options.sort((a, b) => a.label.localeCompare(b.label)); - return { options }; - }); - - const { options } = useAppSelector(selector); - const isOpen = useAppSelector((s) => s.nodes.isAddNodePopoverOpen); + return _options; + }, [filteredTemplates, pendingConnection, t]); const addNode = useCallback( - (nodeType: string) => { - const invocation = buildInvocation(nodeType); - if (!invocation) { + (nodeType: string): AnyNode | null => { + const node = buildInvocation(nodeType); + if (!node) { const errorMessage = t('nodes.unknownNode', { nodeType: nodeType, }); @@ -118,10 +127,11 @@ const AddNodePopover = () => { status: 'error', title: errorMessage, }); - return; + return null; } - - dispatch(nodeAdded(invocation)); + const cursorPos = $cursorPos.get(); + dispatch(nodeAdded({ node, cursorPos })); + return node; }, [dispatch, buildInvocation, toaster, t] ); @@ -131,52 +141,50 @@ const AddNodePopover = () => { if (!v) { return; } - addNode(v.value); - dispatch(addNodePopoverClosed()); + const node = addNode(v.value); + + // Auto-connect an edge if we just added a node and have a pending connection + if (pendingConnection && isInvocationNode(node)) { + const template = templates[node.data.type]; + assert(template, 'Template not found'); + const { nodes, edges } = store.getState().nodes.present; + const connection = getFirstValidConnection(templates, nodes, edges, pendingConnection, node, template); + if (connection) { + dispatch(connectionMade(connection)); + } + } + + closeAddNodePopover(); }, - [addNode, dispatch] + [addNode, dispatch, pendingConnection, store, templates] ); - const onClose = useCallback(() => { - dispatch(addNodePopoverClosed()); - }, [dispatch]); - - const onOpen = useCallback(() => { - dispatch(addNodePopoverOpened()); - }, [dispatch]); - - const handleHotkeyOpen: HotkeyCallback = useCallback( - (e) => { - e.preventDefault(); - onOpen(); - flushSync(() => { - selectRef.current?.inputRef?.focus(); - }); - }, - [onOpen] - ); + const handleHotkeyOpen: HotkeyCallback = useCallback((e) => { + e.preventDefault(); + openAddNodePopover(); + flushSync(() => { + selectRef.current?.inputRef?.focus(); + }); + }, []); const handleHotkeyClose: HotkeyCallback = useCallback(() => { - onClose(); - }, [onClose]); + closeAddNodePopover(); + }, []); useHotkeys(['shift+a', 'space'], handleHotkeyOpen); useHotkeys(['escape'], handleHotkeyClose); - const onKeyDown: KeyboardEventHandler = useCallback( - (e) => { - if (e.key === 'Escape') { - onClose(); - } - }, - [onClose] - ); + const onKeyDown: KeyboardEventHandler = useCallback((e) => { + if (e.key === 'Escape') { + closeAddNodePopover(); + } + }, []); const noOptionsMessage = useCallback(() => t('nodes.noMatchingNodes'), [t]); return ( { noOptionsMessage={noOptionsMessage} filterOption={filterOption} onChange={onChange} - onMenuClose={onClose} + onMenuClose={closeAddNodePopover} onKeyDown={onKeyDown} inputRef={inputRef} closeMenuOnSelect={false} diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx index 4b9249e94f..656de737c7 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/Flow.tsx @@ -1,34 +1,33 @@ import { useGlobalMenuClose, useToken } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useConnection } from 'features/nodes/hooks/useConnection'; +import { useCopyPaste } from 'features/nodes/hooks/useCopyPaste'; +import { useSyncExecutionState } from 'features/nodes/hooks/useExecutionState'; import { useIsValidConnection } from 'features/nodes/hooks/useIsValidConnection'; -import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode'; import { useWorkflowWatcher } from 'features/nodes/hooks/useWorkflowWatcher'; import { - connectionEnded, + $cursorPos, + $isAddNodePopoverOpen, + $isUpdatingEdge, + $pendingConnection, + $viewport, connectionMade, - connectionStarted, edgeAdded, - edgeChangeStarted, edgeDeleted, edgesChanged, edgesDeleted, nodesChanged, nodesDeleted, + redo, selectedAll, - selectedEdgesChanged, - selectedNodesChanged, - selectionCopied, - selectionPasted, - viewportChanged, + undo, } from 'features/nodes/store/nodesSlice'; import { $flow } from 'features/nodes/store/reactFlowInstance'; import type { CSSProperties, MouseEvent } from 'react'; import { memo, useCallback, useMemo, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import type { - OnConnect, - OnConnectEnd, - OnConnectStart, OnEdgesChange, OnEdgesDelete, OnEdgeUpdateFunc, @@ -36,12 +35,11 @@ import type { OnMoveEnd, OnNodesChange, OnNodesDelete, - OnSelectionChangeFunc, ProOptions, ReactFlowProps, - XYPosition, + ReactFlowState, } from 'reactflow'; -import { Background, ReactFlow } from 'reactflow'; +import { Background, ReactFlow, useStore as useReactFlowStore } from 'reactflow'; import CustomConnectionLine from './connectionLines/CustomConnectionLine'; import InvocationCollapsedEdge from './edges/InvocationCollapsedEdge'; @@ -68,17 +66,23 @@ const proOptions: ProOptions = { hideAttribution: true }; const snapGrid: [number, number] = [25, 25]; +const selectCancelConnection = (state: ReactFlowState) => state.cancelConnection; + export const Flow = memo(() => { const dispatch = useAppDispatch(); - const nodes = useAppSelector((s) => s.nodes.nodes); - const edges = useAppSelector((s) => s.nodes.edges); - const viewport = useAppSelector((s) => s.nodes.viewport); - const shouldSnapToGrid = useAppSelector((s) => s.nodes.shouldSnapToGrid); - const selectionMode = useAppSelector((s) => s.nodes.selectionMode); + const nodes = useAppSelector((s) => s.nodes.present.nodes); + const edges = useAppSelector((s) => s.nodes.present.edges); + const viewport = useStore($viewport); + const mayUndo = useAppSelector((s) => s.nodes.past.length > 0); + const mayRedo = useAppSelector((s) => s.nodes.future.length > 0); + const shouldSnapToGrid = useAppSelector((s) => s.workflowSettings.shouldSnapToGrid); + const selectionMode = useAppSelector((s) => s.workflowSettings.selectionMode); + const { onConnectStart, onConnect, onConnectEnd } = useConnection(); const flowWrapper = useRef(null); - const cursorPosition = useRef(null); const isValidConnection = useIsValidConnection(); + const cancelConnection = useReactFlowStore(selectCancelConnection); useWorkflowWatcher(); + useSyncExecutionState(); const [borderRadius] = useToken('radii', ['base']); const flowStyles = useMemo( @@ -102,32 +106,6 @@ export const Flow = memo(() => { [dispatch] ); - const onConnectStart: OnConnectStart = useCallback( - (event, params) => { - dispatch(connectionStarted(params)); - }, - [dispatch] - ); - - const onConnect: OnConnect = useCallback( - (connection) => { - dispatch(connectionMade(connection)); - }, - [dispatch] - ); - - const onConnectEnd: OnConnectEnd = useCallback(() => { - if (!cursorPosition.current) { - return; - } - dispatch( - connectionEnded({ - cursorPosition: cursorPosition.current, - mouseOverNodeId: $mouseOverNode.get(), - }) - ); - }, [dispatch]); - const onEdgesDelete: OnEdgesDelete = useCallback( (edges) => { dispatch(edgesDeleted(edges)); @@ -142,20 +120,9 @@ export const Flow = memo(() => { [dispatch] ); - const handleSelectionChange: OnSelectionChangeFunc = useCallback( - ({ nodes, edges }) => { - dispatch(selectedNodesChanged(nodes ? nodes.map((n) => n.id) : [])); - dispatch(selectedEdgesChanged(edges ? edges.map((e) => e.id) : [])); - }, - [dispatch] - ); - - const handleMoveEnd: OnMoveEnd = useCallback( - (e, viewport) => { - dispatch(viewportChanged(viewport)); - }, - [dispatch] - ); + const handleMoveEnd: OnMoveEnd = useCallback((e, viewport) => { + $viewport.set(viewport); + }, []); const { onCloseGlobal } = useGlobalMenuClose(); const handlePaneClick = useCallback(() => { @@ -169,11 +136,12 @@ export const Flow = memo(() => { const onMouseMove = useCallback((event: MouseEvent) => { if (flowWrapper.current?.getBoundingClientRect()) { - cursorPosition.current = + $cursorPos.set( $flow.get()?.screenToFlowPosition({ x: event.clientX, y: event.clientY, - }) ?? null; + }) ?? null + ); } }, []); @@ -195,19 +163,18 @@ export const Flow = memo(() => { const onEdgeUpdateStart: NonNullable = useCallback( (e, edge, _handleType) => { + $isUpdatingEdge.set(true); // update mouse event edgeUpdateMouseEvent.current = e; // always delete the edge when starting an updated dispatch(edgeDeleted(edge.id)); - dispatch(edgeChangeStarted()); }, [dispatch] ); const onEdgeUpdate: OnEdgeUpdateFunc = useCallback( (_oldEdge, newConnection) => { - // instead of updating the edge (we deleted it earlier), we instead create - // a new one. + // Because we deleted the edge when the update started, we must create a new edge from the connection dispatch(connectionMade(newConnection)); }, [dispatch] @@ -215,8 +182,10 @@ export const Flow = memo(() => { const onEdgeUpdateEnd: NonNullable = useCallback( (e, edge, _handleType) => { - // Handle the case where user begins a drag but didn't move the cursor - - // bc we deleted the edge, we need to add it back + $isUpdatingEdge.set(false); + $pendingConnection.set(null); + // Handle the case where user begins a drag but didn't move the cursor - we deleted the edge when starting + // the edge update - we need to add it back if ( // ignore touch events !('touches' in e) && @@ -233,23 +202,64 @@ export const Flow = memo(() => { // #endregion - useHotkeys(['Ctrl+c', 'Meta+c'], (e) => { - e.preventDefault(); - dispatch(selectionCopied()); - }); + const { copySelection, pasteSelection } = useCopyPaste(); - useHotkeys(['Ctrl+a', 'Meta+a'], (e) => { - e.preventDefault(); - dispatch(selectedAll()); - }); + const onCopyHotkey = useCallback( + (e: KeyboardEvent) => { + e.preventDefault(); + copySelection(); + }, + [copySelection] + ); + useHotkeys(['Ctrl+c', 'Meta+c'], onCopyHotkey); - useHotkeys(['Ctrl+v', 'Meta+v'], (e) => { - if (!cursorPosition.current) { - return; + const onSelectAllHotkey = useCallback( + (e: KeyboardEvent) => { + e.preventDefault(); + dispatch(selectedAll()); + }, + [dispatch] + ); + useHotkeys(['Ctrl+a', 'Meta+a'], onSelectAllHotkey); + + const onPasteHotkey = useCallback( + (e: KeyboardEvent) => { + e.preventDefault(); + pasteSelection(); + }, + [pasteSelection] + ); + useHotkeys(['Ctrl+v', 'Meta+v'], onPasteHotkey); + + const onPasteWithEdgesToNodesHotkey = useCallback( + (e: KeyboardEvent) => { + e.preventDefault(); + pasteSelection(true); + }, + [pasteSelection] + ); + useHotkeys(['Ctrl+shift+v', 'Meta+shift+v'], onPasteWithEdgesToNodesHotkey); + + const onUndoHotkey = useCallback(() => { + if (mayUndo) { + dispatch(undo()); } - e.preventDefault(); - dispatch(selectionPasted({ cursorPosition: cursorPosition.current })); - }); + }, [dispatch, mayUndo]); + useHotkeys(['meta+z', 'ctrl+z'], onUndoHotkey); + + const onRedoHotkey = useCallback(() => { + if (mayRedo) { + dispatch(redo()); + } + }, [dispatch, mayRedo]); + useHotkeys(['meta+shift+z', 'ctrl+shift+z'], onRedoHotkey); + + const onEscapeHotkey = useCallback(() => { + $pendingConnection.set(null); + $isAddNodePopoverOpen.set(false); + cancelConnection(); + }, [cancelConnection]); + useHotkeys('esc', onEscapeHotkey); return ( { onConnectEnd={onConnectEnd} onMoveEnd={handleMoveEnd} connectionLineComponent={CustomConnectionLine} - onSelectionChange={handleSelectionChange} isValidConnection={isValidConnection} minZoom={0.1} snapToGrid={shouldSnapToGrid} @@ -285,6 +294,7 @@ export const Flow = memo(() => { onPaneClick={handlePaneClick} deleteKeyCode={DELETE_KEYS} selectionMode={selectionMode} + elevateEdgesOnSelect > diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx index 61efcea06a..09c88c713b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/connectionLines/CustomConnectionLine.tsx @@ -1,26 +1,33 @@ -import { createSelector } from '@reduxjs/toolkit'; +import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { getFieldColor } from 'features/nodes/components/flow/edges/util/getEdgeColor'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $pendingConnection } from 'features/nodes/store/nodesSlice'; import type { CSSProperties } from 'react'; -import { memo } from 'react'; +import { memo, useMemo } from 'react'; import type { ConnectionLineComponentProps } from 'reactflow'; import { getBezierPath } from 'reactflow'; -const selectStroke = createSelector(selectNodesSlice, (nodes) => - nodes.shouldColorEdges ? getFieldColor(nodes.connectionStartFieldType) : colorTokenToCssVar('base.500') -); - -const selectClassName = createSelector(selectNodesSlice, (nodes) => - nodes.shouldAnimateEdges ? 'react-flow__custom_connection-path animated' : 'react-flow__custom_connection-path' -); - const pathStyles: CSSProperties = { opacity: 0.8 }; const CustomConnectionLine = ({ fromX, fromY, fromPosition, toX, toY, toPosition }: ConnectionLineComponentProps) => { - const stroke = useAppSelector(selectStroke); - const className = useAppSelector(selectClassName); + const pendingConnection = useStore($pendingConnection); + const shouldColorEdges = useAppSelector((state) => state.workflowSettings.shouldColorEdges); + const shouldAnimateEdges = useAppSelector((state) => state.workflowSettings.shouldAnimateEdges); + const stroke = useMemo(() => { + if (shouldColorEdges && pendingConnection) { + return getFieldColor(pendingConnection.fieldTemplate.type); + } else { + return colorTokenToCssVar('base.500'); + } + }, [pendingConnection, shouldColorEdges]); + const className = useMemo(() => { + if (shouldAnimateEdges) { + return 'react-flow__custom_connection-path animated'; + } else { + return 'react-flow__custom_connection-path'; + } + }, [shouldAnimateEdges]); const pathParams = { sourceX: fromX, diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx index eae7970804..2e2fb31154 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationCollapsedEdge.tsx @@ -1,6 +1,8 @@ import { Badge, Flex } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; import { useChakraThemeTokens } from 'common/hooks/useChakraThemeTokens'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { memo, useMemo } from 'react'; import type { EdgeProps } from 'reactflow'; import { BaseEdge, EdgeLabelRenderer, getBezierPath } from 'reactflow'; @@ -22,9 +24,10 @@ const InvocationCollapsedEdge = ({ sourceHandleId, targetHandleId, }: EdgeProps<{ count: number }>) => { + const templates = useStore($templates); const selector = useMemo( - () => makeEdgeSelector(source, sourceHandleId, target, targetHandleId, selected), - [selected, source, sourceHandleId, target, targetHandleId] + () => makeEdgeSelector(templates, source, sourceHandleId, target, targetHandleId, selected), + [templates, selected, source, sourceHandleId, target, targetHandleId] ); const { isSelected, shouldAnimate } = useAppSelector(selector); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx index 77be60a945..2e4340975b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/InvocationDefaultEdge.tsx @@ -1,5 +1,7 @@ import { Flex, Text } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; +import { $templates } from 'features/nodes/store/nodesSlice'; import type { CSSProperties } from 'react'; import { memo, useMemo } from 'react'; import type { EdgeProps } from 'reactflow'; @@ -21,13 +23,14 @@ const InvocationDefaultEdge = ({ sourceHandleId, targetHandleId, }: EdgeProps) => { + const templates = useStore($templates); const selector = useMemo( - () => makeEdgeSelector(source, sourceHandleId, target, targetHandleId, selected), - [source, sourceHandleId, target, targetHandleId, selected] + () => makeEdgeSelector(templates, source, sourceHandleId, target, targetHandleId, selected), + [templates, source, sourceHandleId, target, targetHandleId, selected] ); const { isSelected, shouldAnimate, stroke, label } = useAppSelector(selector); - const shouldShowEdgeLabels = useAppSelector((s) => s.nodes.shouldShowEdgeLabels); + const shouldShowEdgeLabels = useAppSelector((s) => s.workflowSettings.shouldShowEdgeLabels); const [edgePath, labelX, labelY] = getBezierPath({ sourceX, diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts index a485bf64c1..87ef8eb629 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts +++ b/invokeai/frontend/web/src/features/nodes/components/flow/edges/util/makeEdgeSelector.ts @@ -1,7 +1,8 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { colorTokenToCssVar } from 'common/util/colorTokenToCssVar'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectFieldOutputTemplate, selectNodeTemplate } from 'features/nodes/store/selectors'; +import type { Templates } from 'features/nodes/store/types'; +import { selectWorkflowSettingsSlice } from 'features/nodes/store/workflowSettingsSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getFieldColor } from './getEdgeColor'; @@ -14,6 +15,7 @@ const defaultReturnValue = { }; export const makeEdgeSelector = ( + templates: Templates, source: string, sourceHandleId: string | null | undefined, target: string, @@ -22,7 +24,8 @@ export const makeEdgeSelector = ( ) => createMemoizedSelector( selectNodesSlice, - (nodes): { isSelected: boolean; shouldAnimate: boolean; stroke: string; label: string } => { + selectWorkflowSettingsSlice, + (nodes, workflowSettings): { isSelected: boolean; shouldAnimate: boolean; stroke: string; label: string } => { const sourceNode = nodes.nodes.find((node) => node.id === source); const targetNode = nodes.nodes.find((node) => node.id === target); @@ -33,19 +36,20 @@ export const makeEdgeSelector = ( return defaultReturnValue; } - const outputFieldTemplate = selectFieldOutputTemplate(nodes, sourceNode.id, sourceHandleId); + const sourceNodeTemplate = templates[sourceNode.data.type]; + const targetNodeTemplate = templates[targetNode.data.type]; + + const outputFieldTemplate = sourceNodeTemplate?.outputs[sourceHandleId]; const sourceType = isInvocationToInvocationEdge ? outputFieldTemplate?.type : undefined; - const stroke = sourceType && nodes.shouldColorEdges ? getFieldColor(sourceType) : colorTokenToCssVar('base.500'); - - const sourceNodeTemplate = selectNodeTemplate(nodes, sourceNode.id); - const targetNodeTemplate = selectNodeTemplate(nodes, targetNode.id); + const stroke = + sourceType && workflowSettings.shouldColorEdges ? getFieldColor(sourceType) : colorTokenToCssVar('base.500'); const label = `${sourceNodeTemplate?.title || sourceNode.data?.label} -> ${targetNodeTemplate?.title || targetNode.data?.label}`; return { isSelected, - shouldAnimate: nodes.shouldAnimateEdges && isSelected, + shouldAnimate: workflowSettings.shouldAnimateEdges && isSelected, stroke, label, }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx index 3138cb32fe..b58f6fe8ba 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeStatusIndicator.tsx @@ -1,12 +1,10 @@ import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { Badge, CircularProgress, Flex, Icon, Image, Text, Tooltip } from '@invoke-ai/ui-library'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { useExecutionState } from 'features/nodes/hooks/useExecutionState'; import { DRAG_HANDLE_CLASSNAME } from 'features/nodes/types/constants'; import type { NodeExecutionState } from 'features/nodes/types/invocation'; import { zNodeStatus } from 'features/nodes/types/invocation'; -import { memo, useMemo } from 'react'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiCheckBold, PiDotsThreeOutlineFill, PiWarningBold } from 'react-icons/pi'; @@ -24,12 +22,7 @@ const circleStyles: SystemStyleObject = { }; const InvocationNodeStatusIndicator = ({ nodeId }: Props) => { - const selectNodeExecutionState = useMemo( - () => createMemoizedSelector(selectNodesSlice, (nodes) => nodes.nodeExecutionStates[nodeId]), - [nodeId] - ); - - const nodeExecutionState = useAppSelector(selectNodeExecutionState); + const nodeExecutionState = useExecutionState(nodeId); if (!nodeExecutionState) { return null; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx index 0fe81c0882..1d12b6a454 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/InvocationNodeWrapper.tsx @@ -1,7 +1,7 @@ -import { createSelector } from '@reduxjs/toolkit'; +import { useStore } from '@nanostores/react'; import { useAppSelector } from 'app/store/storeHooks'; import InvocationNode from 'features/nodes/components/flow/nodes/Invocation/InvocationNode'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $templates } from 'features/nodes/store/nodesSlice'; import type { InvocationNodeData } from 'features/nodes/types/invocation'; import { memo, useMemo } from 'react'; import type { NodeProps } from 'reactflow'; @@ -11,13 +11,13 @@ import InvocationNodeUnknownFallback from './InvocationNodeUnknownFallback'; const InvocationNodeWrapper = (props: NodeProps) => { const { data, selected } = props; const { id: nodeId, type, isOpen, label } = data; + const templates = useStore($templates); + const hasTemplate = useMemo(() => Boolean(templates[type]), [templates, type]); + const nodeExists = useAppSelector((s) => Boolean(s.nodes.present.nodes.find((n) => n.id === nodeId))); - const hasTemplateSelector = useMemo( - () => createSelector(selectNodesSlice, (nodes) => Boolean(nodes.templates[type])), - [type] - ); - - const hasTemplate = useAppSelector(hasTemplateSelector); + if (!nodeExists) { + return null; + } if (!hasTemplate) { return ( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/EditableFieldTitle.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/EditableFieldTitle.tsx index e02b1a1474..617b6141c8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/EditableFieldTitle.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/EditableFieldTitle.tsx @@ -25,10 +25,11 @@ interface Props { kind: 'inputs' | 'outputs'; isMissingInput?: boolean; withTooltip?: boolean; + shouldDim?: boolean; } const EditableFieldTitle = forwardRef((props: Props, ref) => { - const { nodeId, fieldName, kind, isMissingInput = false, withTooltip = false } = props; + const { nodeId, fieldName, kind, isMissingInput = false, withTooltip = false, shouldDim = false } = props; const label = useFieldLabel(nodeId, fieldName); const fieldTemplateTitle = useFieldTemplateTitle(nodeId, fieldName, kind); const { t } = useTranslation(); @@ -37,14 +38,13 @@ const EditableFieldTitle = forwardRef((props: Props, ref) => { const [localTitle, setLocalTitle] = useState(label || fieldTemplateTitle || t('nodes.unknownField')); const handleSubmit = useCallback( - async (newTitle: string) => { - if (newTitle && (newTitle === label || newTitle === fieldTemplateTitle)) { - return; - } - setLocalTitle(newTitle || fieldTemplateTitle || t('nodes.unknownField')); - dispatch(fieldLabelChanged({ nodeId, fieldName, label: newTitle })); + async (newTitleRaw: string) => { + const newTitle = newTitleRaw.trim(); + const finalTitle = newTitle || fieldTemplateTitle || t('nodes.unknownField'); + setLocalTitle(finalTitle); + dispatch(fieldLabelChanged({ nodeId, fieldName, label: finalTitle })); }, - [label, fieldTemplateTitle, dispatch, nodeId, fieldName, t] + [fieldTemplateTitle, dispatch, nodeId, fieldName, t] ); const handleChange = useCallback((newTitle: string) => { @@ -57,33 +57,34 @@ const EditableFieldTitle = forwardRef((props: Props, ref) => { }, [label, fieldTemplateTitle, t]); return ( - : undefined} - openDelay={HANDLE_TOOLTIP_OPEN_DELAY} + - : undefined} + openDelay={HANDLE_TOOLTIP_OPEN_DELAY} > - - - - + + + + ); }); @@ -127,7 +128,15 @@ const EditableControls = memo(() => { } return ( - + ); }); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputField.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputField.tsx index 66b0d3f755..cd0878412b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputField.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/InputField.tsx @@ -69,7 +69,7 @@ const InputField = ({ nodeId, fieldName }: Props) => { ); } - if (fieldTemplate.input === 'connection') { + if (fieldTemplate.input === 'connection' || isConnected) { return ( @@ -79,6 +79,7 @@ const InputField = ({ nodeId, fieldName }: Props) => { kind="inputs" isMissingInput={isMissingInput} withTooltip + shouldDim /> @@ -95,7 +96,15 @@ const InputField = ({ nodeId, fieldName }: Props) => { return ( - + { const { nodeId, width, children, selected } = props; const { isMouseOverNode, handleMouseOut, handleMouseOver } = useMouseOverNode(nodeId); - const selectIsInProgress = useMemo( - () => - createSelector( - selectNodesSlice, - (nodes) => nodes.nodeExecutionStates[nodeId]?.status === zNodeStatus.enum.IN_PROGRESS - ), - [nodeId] - ); - - const isInProgress = useAppSelector(selectIsInProgress); + const executionState = useExecutionState(nodeId); + const isInProgress = executionState?.status === zNodeStatus.enum.IN_PROGRESS; const [nodeInProgress, shadowsXl, shadowsBase] = useToken('shadows', [ 'nodeInProgress', @@ -39,7 +31,7 @@ const NodeWrapper = (props: NodeWrapperProps) => { const dispatch = useAppDispatch(); - const opacity = useAppSelector((s) => s.nodes.nodeOpacity); + const opacity = useAppSelector((s) => s.workflowSettings.nodeOpacity); const { onCloseGlobal } = useGlobalMenuClose(); const handleClick = useCallback( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx index b24b2058bd..7a46782f1b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/NodeOpacitySlider.tsx @@ -1,12 +1,12 @@ import { CompositeSlider, Flex } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { nodeOpacityChanged } from 'features/nodes/store/nodesSlice'; +import { nodeOpacityChanged } from 'features/nodes/store/workflowSettingsSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; const NodeOpacitySlider = () => { const dispatch = useAppDispatch(); - const nodeOpacity = useAppSelector((s) => s.nodes.nodeOpacity); + const nodeOpacity = useAppSelector((s) => s.workflowSettings.nodeOpacity); const { t } = useTranslation(); const handleChange = useCallback( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx index b2251480d7..f2624de58e 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/BottomLeftPanel/ViewportControls.tsx @@ -1,9 +1,6 @@ import { ButtonGroup, IconButton } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { - // shouldShowFieldTypeLegendChanged, - shouldShowMinimapPanelChanged, -} from 'features/nodes/store/nodesSlice'; +import { shouldShowMinimapPanelChanged } from 'features/nodes/store/workflowSettingsSlice'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { @@ -19,9 +16,9 @@ const ViewportControls = () => { const { zoomIn, zoomOut, fitView } = useReactFlow(); const dispatch = useAppDispatch(); // const shouldShowFieldTypeLegend = useAppSelector( - // (s) => s.nodes.shouldShowFieldTypeLegend + // (s) => s.nodes.present.shouldShowFieldTypeLegend // ); - const shouldShowMinimapPanel = useAppSelector((s) => s.nodes.shouldShowMinimapPanel); + const shouldShowMinimapPanel = useAppSelector((s) => s.workflowSettings.shouldShowMinimapPanel); const handleClickedZoomIn = useCallback(() => { zoomIn(); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx index b34ae11c85..92668c3fa8 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/MinimapPanel/MinimapPanel.tsx @@ -16,7 +16,7 @@ const minimapStyles: SystemStyleObject = { }; const MinimapPanel = () => { - const shouldShowMinimapPanel = useAppSelector((s) => s.nodes.shouldShowMinimapPanel); + const shouldShowMinimapPanel = useAppSelector((s) => s.workflowSettings.shouldShowMinimapPanel); return ( diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx index 63a5e7eccb..c7eb9bdbb0 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/AddNodeButton.tsx @@ -1,23 +1,18 @@ import { IconButton } from '@invoke-ai/ui-library'; -import { useAppDispatch } from 'app/store/storeHooks'; -import { addNodePopoverOpened } from 'features/nodes/store/nodesSlice'; -import { memo, useCallback } from 'react'; +import { openAddNodePopover } from 'features/nodes/store/nodesSlice'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; import { PiPlusBold } from 'react-icons/pi'; const AddNodeButton = () => { - const dispatch = useAppDispatch(); const { t } = useTranslation(); - const handleOpenAddNodePopover = useCallback(() => { - dispatch(addNodePopoverOpened()); - }, [dispatch]); return ( } - onClick={handleOpenAddNodePopover} + onClick={openAddNodePopover} pointerEvents="auto" /> ); diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx index 2a08fb840e..93856a21c4 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopPanel/TopPanel.tsx @@ -1,6 +1,5 @@ import { Flex, Spacer } from '@invoke-ai/ui-library'; import { useAppSelector } from 'app/store/storeHooks'; -import { ViewerButton } from 'features/gallery/components/ImageViewer/ViewerButton'; import AddNodeButton from 'features/nodes/components/flow/panels/TopPanel/AddNodeButton'; import ClearFlowButton from 'features/nodes/components/flow/panels/TopPanel/ClearFlowButton'; import SaveWorkflowButton from 'features/nodes/components/flow/panels/TopPanel/SaveWorkflowButton'; @@ -23,7 +22,6 @@ const TopCenterPanel = () => { - ); }; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx index b366737e59..37fac8ee7b 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx @@ -21,13 +21,13 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import ReloadNodeTemplatesButton from 'features/nodes/components/flow/panels/TopRightPanel/ReloadSchemaButton'; import { selectionModeChanged, - selectNodesSlice, + selectWorkflowSettingsSlice, shouldAnimateEdgesChanged, shouldColorEdgesChanged, shouldShowEdgeLabelsChanged, shouldSnapToGridChanged, shouldValidateGraphChanged, -} from 'features/nodes/store/nodesSlice'; +} from 'features/nodes/store/workflowSettingsSlice'; import type { ChangeEvent, ReactNode } from 'react'; import { memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -35,7 +35,7 @@ import { SelectionMode } from 'reactflow'; const formLabelProps: FormLabelProps = { flexGrow: 1 }; -const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { +const selector = createMemoizedSelector(selectWorkflowSettingsSlice, (workflowSettings) => { const { shouldAnimateEdges, shouldValidateGraph, @@ -43,7 +43,7 @@ const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { shouldColorEdges, shouldShowEdgeLabels, selectionMode, - } = nodes; + } = workflowSettings; return { shouldAnimateEdges, shouldValidateGraph, diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx index 8f1a3249ee..af0ea710d6 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDataTab.tsx @@ -3,27 +3,21 @@ import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectLastSelectedNode } from 'features/nodes/store/selectors'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { - const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; - const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId); - - return { - data: lastSelectedNode?.data, - }; -}); +const selector = createMemoizedSelector(selectNodesSlice, (nodes) => selectLastSelectedNode(nodes)); const InspectorDataTab = () => { const { t } = useTranslation(); - const { data } = useAppSelector(selector); + const lastSelectedNode = useAppSelector(selector); - if (!data) { + if (!lastSelectedNode) { return ; } - return ; + return ; }; export default memo(InspectorDataTab); 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 index d72d2f5aa8..f38fa819dd 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorDetailsTab.tsx @@ -1,36 +1,39 @@ import { Box, Flex, FormControl, FormLabel, HStack, Text } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import NotesTextarea from 'features/nodes/components/flow/nodes/Invocation/NotesTextarea'; import { useNodeNeedsUpdate } from 'features/nodes/hooks/useNodeNeedsUpdate'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectLastSelectedNode } from 'features/nodes/store/selectors'; import { isInvocationNode } from 'features/nodes/types/invocation'; -import { memo } from 'react'; +import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import EditableNodeTitle from './details/EditableNodeTitle'; -const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { - const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; - - const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId); - - const lastSelectedNodeTemplate = lastSelectedNode ? nodes.templates[lastSelectedNode.data.type] : undefined; - - if (!isInvocationNode(lastSelectedNode) || !lastSelectedNodeTemplate) { - return; - } - - return { - nodeId: lastSelectedNode.data.id, - nodeVersion: lastSelectedNode.data.version, - templateTitle: lastSelectedNodeTemplate.title, - }; -}); - const InspectorDetailsTab = () => { + const templates = useStore($templates); + const selector = useMemo( + () => + createMemoizedSelector(selectNodesSlice, (nodes) => { + const lastSelectedNode = selectLastSelectedNode(nodes); + const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined; + + if (!isInvocationNode(lastSelectedNode) || !lastSelectedNodeTemplate) { + return; + } + + return { + nodeId: lastSelectedNode.data.id, + nodeVersion: lastSelectedNode.data.version, + templateTitle: lastSelectedNodeTemplate.title, + }; + }), + [templates] + ); const data = useAppSelector(selector); const { t } = useTranslation(); diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx index 978eeddd24..d4150243b9 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorOutputsTab.tsx @@ -1,46 +1,49 @@ import { Box, Flex } from '@invoke-ai/ui-library'; +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent'; import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { useExecutionState } from 'features/nodes/hooks/useExecutionState'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectLastSelectedNode } from 'features/nodes/store/selectors'; import { isInvocationNode } from 'features/nodes/types/invocation'; -import { memo } from 'react'; +import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import type { ImageOutput } from 'services/api/types'; import type { AnyResult } from 'services/events/types'; import ImageOutputPreview from './outputs/ImageOutputPreview'; -const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { - const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; - - const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId); - - const lastSelectedNodeTemplate = lastSelectedNode ? nodes.templates[lastSelectedNode.data.type] : undefined; - - const nes = nodes.nodeExecutionStates[lastSelectedNodeId ?? '__UNKNOWN_NODE__']; - - if (!isInvocationNode(lastSelectedNode) || !nes || !lastSelectedNodeTemplate) { - return; - } - - return { - outputs: nes.outputs, - outputType: lastSelectedNodeTemplate.outputType, - }; -}); - const InspectorOutputsTab = () => { + const templates = useStore($templates); + const selector = useMemo( + () => + createMemoizedSelector(selectNodesSlice, (nodes) => { + const lastSelectedNode = selectLastSelectedNode(nodes); + const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined; + + if (!isInvocationNode(lastSelectedNode) || !lastSelectedNodeTemplate) { + return; + } + + return { + nodeId: lastSelectedNode.id, + outputType: lastSelectedNodeTemplate.outputType, + }; + }), + [templates] + ); const data = useAppSelector(selector); + const nes = useExecutionState(data?.nodeId); const { t } = useTranslation(); - if (!data) { + if (!data || !nes) { return ; } - if (data.outputs.length === 0) { + if (nes.outputs.length === 0) { return ; } @@ -49,11 +52,11 @@ const InspectorOutputsTab = () => { {data.outputType === 'image_output' ? ( - data.outputs.map((result, i) => ( + nes.outputs.map((result, i) => ( )) ) : ( - + )} diff --git a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx index ea6e8ed704..d95b215dd6 100644 --- a/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/sidePanel/inspector/InspectorTemplateTab.tsx @@ -1,25 +1,26 @@ +import { useStore } from '@nanostores/react'; import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { useAppSelector } from 'app/store/storeHooks'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { memo } from 'react'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectLastSelectedNode } from 'features/nodes/store/selectors'; +import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -const selector = createMemoizedSelector(selectNodesSlice, (nodes) => { - const lastSelectedNodeId = nodes.selectedNodes[nodes.selectedNodes.length - 1]; - - const lastSelectedNode = nodes.nodes.find((node) => node.id === lastSelectedNodeId); - - const lastSelectedNodeTemplate = lastSelectedNode ? nodes.templates[lastSelectedNode.data.type] : undefined; - - return { - template: lastSelectedNodeTemplate, - }; -}); - const NodeTemplateInspector = () => { - const { template } = useAppSelector(selector); + const templates = useStore($templates); + const selector = useMemo( + () => + createMemoizedSelector(selectNodesSlice, (nodes) => { + const lastSelectedNode = selectLastSelectedNode(nodes); + const lastSelectedNodeTemplate = lastSelectedNode ? templates[lastSelectedNode.data.type] : undefined; + + return lastSelectedNodeTemplate; + }), + [templates] + ); + const template = useAppSelector(selector); const { t } = useTranslation(); if (!template) { diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts b/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts index 4d0e54b239..3b7a1b74c1 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useAnyOrDirectInputFieldNames.ts @@ -1,31 +1,26 @@ import { EMPTY_ARRAY } from 'app/store/constants'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { getSortedFilteredFieldNames } from 'features/nodes/util/node/getSortedFilteredFieldNames'; import { TEMPLATE_BUILDER_MAP } from 'features/nodes/util/schema/buildFieldInputTemplate'; import { keys, map } from 'lodash-es'; import { useMemo } from 'react'; export const useAnyOrDirectInputFieldNames = (nodeId: string): string[] => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - const template = selectNodeTemplate(nodes, nodeId); - if (!template) { - return EMPTY_ARRAY; - } - const fields = map(template.inputs).filter( - (field) => - (['any', 'direct'].includes(field.input) || field.type.isCollectionOrScalar) && - keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) - ); - return getSortedFilteredFieldNames(fields); - }), - [nodeId] - ); + const template = useNodeTemplate(nodeId); + + const fieldNames = useMemo(() => { + const fields = map(template.inputs).filter((field) => { + return ( + (['any', 'direct'].includes(field.input) || field.type.isCollectionOrScalar) && + keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) + ); + }); + const _fieldNames = getSortedFilteredFieldNames(fields); + if (_fieldNames.length === 0) { + return EMPTY_ARRAY; + } + return _fieldNames; + }, [template.inputs]); - const fieldNames = useAppSelector(selector); return fieldNames; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts b/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts index cce2265d83..4e96c219f8 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useBuildNode.ts @@ -1,4 +1,5 @@ -import { useAppSelector } from 'app/store/storeHooks'; +import { useStore } from '@nanostores/react'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { NODE_WIDTH } from 'features/nodes/types/constants'; import type { AnyNode, InvocationTemplate } from 'features/nodes/types/invocation'; import { buildCurrentImageNode } from 'features/nodes/util/node/buildCurrentImageNode'; @@ -8,8 +9,7 @@ import { useCallback } from 'react'; import { useReactFlow } from 'reactflow'; export const useBuildNode = () => { - const nodeTemplates = useAppSelector((s) => s.nodes.templates); - + const templates = useStore($templates); const flow = useReactFlow(); return useCallback( @@ -41,10 +41,10 @@ export const useBuildNode = () => { // TODO: Keep track of invocation types so we do not need to cast this // We know it is safe because the caller of this function gets the `type` arg from the list of invocation templates. - const template = nodeTemplates[type] as InvocationTemplate; + const template = templates[type] as InvocationTemplate; return buildInvocationNode(position, template); }, - [nodeTemplates, flow] + [templates, flow] ); }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts b/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts new file mode 100644 index 0000000000..df628ba5af --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/hooks/useConnection.ts @@ -0,0 +1,93 @@ +import { useStore } from '@nanostores/react'; +import { useAppStore } from 'app/store/storeHooks'; +import { $mouseOverNode } from 'features/nodes/hooks/useMouseOverNode'; +import { + $isAddNodePopoverOpen, + $isUpdatingEdge, + $pendingConnection, + $templates, + connectionMade, +} from 'features/nodes/store/nodesSlice'; +import { getFirstValidConnection } from 'features/nodes/store/util/findConnectionToValidHandle'; +import { isInvocationNode } from 'features/nodes/types/invocation'; +import { useCallback, useMemo } from 'react'; +import type { OnConnect, OnConnectEnd, OnConnectStart } from 'reactflow'; +import { assert } from 'tsafe'; + +export const useConnection = () => { + const store = useAppStore(); + const templates = useStore($templates); + + const onConnectStart = useCallback( + (event, params) => { + const nodes = store.getState().nodes.present.nodes; + const { nodeId, handleId, handleType } = params; + assert(nodeId && handleId && handleType, `Invalid connection start params: ${JSON.stringify(params)}`); + const node = nodes.find((n) => n.id === nodeId); + assert(isInvocationNode(node), `Invalid node during connection: ${JSON.stringify(node)}`); + const template = templates[node.data.type]; + assert(template, `Template not found for node type: ${node.data.type}`); + const fieldTemplate = handleType === 'source' ? template.outputs[handleId] : template.inputs[handleId]; + assert(fieldTemplate, `Field template not found for field: ${node.data.type}.${handleId}`); + $pendingConnection.set({ + node, + template, + fieldTemplate, + }); + }, + [store, templates] + ); + const onConnect = useCallback( + (connection) => { + const { dispatch } = store; + dispatch(connectionMade(connection)); + $pendingConnection.set(null); + }, + [store] + ); + const onConnectEnd = useCallback(() => { + const { dispatch } = store; + const pendingConnection = $pendingConnection.get(); + const isUpdatingEdge = $isUpdatingEdge.get(); + const mouseOverNodeId = $mouseOverNode.get(); + + // If we are in the middle of an edge update, and the mouse isn't over a node, we should just bail so the edge + // update logic can finish up + if (isUpdatingEdge && !mouseOverNodeId) { + $pendingConnection.set(null); + return; + } + + if (!pendingConnection) { + return; + } + const { nodes, edges } = store.getState().nodes.present; + if (mouseOverNodeId) { + const candidateNode = nodes.filter(isInvocationNode).find((n) => n.id === mouseOverNodeId); + if (!candidateNode) { + // The mouse is over a non-invocation node - bail + return; + } + const candidateTemplate = templates[candidateNode.data.type]; + assert(candidateTemplate, `Template not found for node type: ${candidateNode.data.type}`); + const connection = getFirstValidConnection( + templates, + nodes, + edges, + pendingConnection, + candidateNode, + candidateTemplate + ); + if (connection) { + dispatch(connectionMade(connection)); + } + $pendingConnection.set(null); + } else { + // The mouse is not over a node - we should open the add node popover + $isAddNodePopoverOpen.set(true); + } + }, [store, templates]); + + const api = useMemo(() => ({ onConnectStart, onConnect, onConnectEnd }), [onConnectStart, onConnect, onConnectEnd]); + return api; +}; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts index d332bf46e3..d071ac76d2 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionInputFieldNames.ts @@ -1,34 +1,28 @@ import { EMPTY_ARRAY } from 'app/store/constants'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { getSortedFilteredFieldNames } from 'features/nodes/util/node/getSortedFilteredFieldNames'; import { TEMPLATE_BUILDER_MAP } from 'features/nodes/util/schema/buildFieldInputTemplate'; import { keys, map } from 'lodash-es'; import { useMemo } from 'react'; export const useConnectionInputFieldNames = (nodeId: string): string[] => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - const template = selectNodeTemplate(nodes, nodeId); - if (!template) { - return EMPTY_ARRAY; - } + const template = useNodeTemplate(nodeId); + const fieldNames = useMemo(() => { + // get the visible fields + const fields = map(template.inputs).filter( + (field) => + (field.input === 'connection' && !field.type.isCollectionOrScalar) || + !keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) + ); - // get the visible fields - const fields = map(template.inputs).filter( - (field) => - (field.input === 'connection' && !field.type.isCollectionOrScalar) || - !keys(TEMPLATE_BUILDER_MAP).includes(field.type.name) - ); + const _fieldNames = getSortedFilteredFieldNames(fields); - return getSortedFilteredFieldNames(fields); - }), - [nodeId] - ); + if (_fieldNames.length === 0) { + return EMPTY_ARRAY; + } + + return _fieldNames; + }, [template.inputs]); - const fieldNames = useAppSelector(selector); return fieldNames; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts index f0a512277a..728b492453 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useConnectionState.ts @@ -1,16 +1,12 @@ +import { useStore } from '@nanostores/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $pendingConnection, $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { makeConnectionErrorSelector } from 'features/nodes/store/util/makeIsConnectionValidSelector'; import { useMemo } from 'react'; import { useFieldType } from './useFieldType.ts'; -const selectIsConnectionInProgress = createSelector( - selectNodesSlice, - (nodes) => nodes.connectionStartFieldType !== null && nodes.connectionStartParams !== null -); - type UseConnectionStateProps = { nodeId: string; fieldName: string; @@ -18,6 +14,8 @@ type UseConnectionStateProps = { }; export const useConnectionState = ({ nodeId, fieldName, kind }: UseConnectionStateProps) => { + const pendingConnection = useStore($pendingConnection); + const templates = useStore($templates); const fieldType = useFieldType(nodeId, fieldName, kind); const selectIsConnected = useMemo( @@ -36,25 +34,30 @@ export const useConnectionState = ({ nodeId, fieldName, kind }: UseConnectionSta ); const selectConnectionError = useMemo( - () => makeConnectionErrorSelector(nodeId, fieldName, kind === 'inputs' ? 'target' : 'source', fieldType), - [nodeId, fieldName, kind, fieldType] - ); - - const selectIsConnectionStartField = useMemo( () => - createSelector(selectNodesSlice, (nodes) => - Boolean( - nodes.connectionStartParams?.nodeId === nodeId && - nodes.connectionStartParams?.handleId === fieldName && - nodes.connectionStartParams?.handleType === { inputs: 'target', outputs: 'source' }[kind] - ) + makeConnectionErrorSelector( + templates, + pendingConnection, + nodeId, + fieldName, + kind === 'inputs' ? 'target' : 'source', + fieldType ), - [fieldName, kind, nodeId] + [templates, pendingConnection, nodeId, fieldName, kind, fieldType] ); const isConnected = useAppSelector(selectIsConnected); - const isConnectionInProgress = useAppSelector(selectIsConnectionInProgress); - const isConnectionStartField = useAppSelector(selectIsConnectionStartField); + const isConnectionInProgress = useMemo(() => Boolean(pendingConnection), [pendingConnection]); + const isConnectionStartField = useMemo(() => { + if (!pendingConnection) { + return false; + } + return ( + pendingConnection.node.id === nodeId && + pendingConnection.fieldTemplate.name === fieldName && + pendingConnection.fieldTemplate.fieldKind === { inputs: 'input', outputs: 'output' }[kind] + ); + }, [fieldName, kind, nodeId, pendingConnection]); const connectionError = useAppSelector(selectConnectionError); const shouldDim = useMemo( diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts b/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts new file mode 100644 index 0000000000..08def1514c --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/hooks/useCopyPaste.ts @@ -0,0 +1,78 @@ +import { getStore } from 'app/store/nanostores/store'; +import { deepClone } from 'common/util/deepClone'; +import { + $copiedEdges, + $copiedNodes, + $cursorPos, + $edgesToCopiedNodes, + selectionPasted, + selectNodesSlice, +} from 'features/nodes/store/nodesSlice'; +import { findUnoccupiedPosition } from 'features/nodes/store/util/findUnoccupiedPosition'; +import { isEqual, uniqWith } from 'lodash-es'; +import { v4 as uuidv4 } from 'uuid'; + +const copySelection = () => { + // Use the imperative API here so we don't have to pass the whole slice around + const { getState } = getStore(); + const { nodes, edges } = selectNodesSlice(getState()); + const selectedNodes = nodes.filter((node) => node.selected); + const selectedEdges = edges.filter((edge) => edge.selected); + const edgesToSelectedNodes = edges.filter((edge) => selectedNodes.some((node) => node.id === edge.target)); + $copiedNodes.set(selectedNodes); + $copiedEdges.set(selectedEdges); + $edgesToCopiedNodes.set(edgesToSelectedNodes); +}; + +const pasteSelection = (withEdgesToCopiedNodes?: boolean) => { + const { getState, dispatch } = getStore(); + const currentNodes = selectNodesSlice(getState()).nodes; + const cursorPos = $cursorPos.get(); + + const copiedNodes = deepClone($copiedNodes.get()); + let copiedEdges = deepClone($copiedEdges.get()); + + if (withEdgesToCopiedNodes) { + const edgesToCopiedNodes = deepClone($edgesToCopiedNodes.get()); + copiedEdges = uniqWith([...copiedEdges, ...edgesToCopiedNodes], isEqual); + } + + // Calculate an offset to reposition nodes to surround the cursor position, maintaining relative positioning + const xCoords = copiedNodes.map((node) => node.position.x); + const yCoords = copiedNodes.map((node) => node.position.y); + const minX = Math.min(...xCoords); + const minY = Math.min(...yCoords); + const offsetX = cursorPos ? cursorPos.x - minX : 50; + const offsetY = cursorPos ? cursorPos.y - minY : 50; + + copiedNodes.forEach((node) => { + const { x, y } = findUnoccupiedPosition(currentNodes, node.position.x + offsetX, node.position.y + offsetY); + node.position.x = x; + node.position.y = y; + // Pasted nodes are selected + node.selected = true; + // Also give em a fresh id + const id = uuidv4(); + // Update the edges to point to the new node id + for (const edge of copiedEdges) { + if (edge.source === node.id) { + edge.source = id; + edge.id = edge.id.replace(node.data.id, id); + } + if (edge.target === node.id) { + edge.target = id; + edge.id = edge.id.replace(node.data.id, id); + } + } + node.id = id; + node.data.id = id; + }); + + dispatch(selectionPasted({ nodes: copiedNodes, edges: copiedEdges })); +}; + +const api = { copySelection, pasteSelection }; + +export const useCopyPaste = () => { + return api; +}; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useExecutionState.ts b/invokeai/frontend/web/src/features/nodes/hooks/useExecutionState.ts new file mode 100644 index 0000000000..0e5dc1ac43 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/hooks/useExecutionState.ts @@ -0,0 +1,56 @@ +import { useStore } from '@nanostores/react'; +import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; +import { useAppSelector } from 'app/store/storeHooks'; +import { deepClone } from 'common/util/deepClone'; +import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import type { NodeExecutionStates } from 'features/nodes/store/types'; +import type { NodeExecutionState } from 'features/nodes/types/invocation'; +import { zNodeStatus } from 'features/nodes/types/invocation'; +import { map } from 'nanostores'; +import { useEffect, useMemo } from 'react'; + +export const $nodeExecutionStates = map({}); + +const initialNodeExecutionState: Omit = { + status: zNodeStatus.enum.PENDING, + error: null, + progress: null, + progressImage: null, + outputs: [], +}; + +export const useExecutionState = (nodeId?: string) => { + const executionStates = useStore($nodeExecutionStates, nodeId ? { keys: [nodeId] } : undefined); + const executionState = useMemo(() => (nodeId ? executionStates[nodeId] : undefined), [executionStates, nodeId]); + return executionState; +}; + +const removeNodeExecutionState = (nodeId: string) => { + $nodeExecutionStates.setKey(nodeId, undefined); +}; + +export const upsertExecutionState = (nodeId: string, updates?: Partial) => { + const state = $nodeExecutionStates.get()[nodeId]; + if (!state) { + $nodeExecutionStates.setKey(nodeId, { ...deepClone(initialNodeExecutionState), nodeId, ...updates }); + } else { + $nodeExecutionStates.setKey(nodeId, { ...state, ...updates }); + } +}; + +const selectNodeIds = createMemoizedSelector(selectNodesSlice, (nodesSlice) => nodesSlice.nodes.map((node) => node.id)); + +export const useSyncExecutionState = () => { + const nodeIds = useAppSelector(selectNodeIds); + useEffect(() => { + const nodeExecutionStates = $nodeExecutionStates.get(); + const nodeIdsToAdd = nodeIds.filter((id) => !nodeExecutionStates[id]); + const nodeIdsToRemove = Object.keys(nodeExecutionStates).filter((id) => !nodeIds.includes(id)); + for (const id of nodeIdsToAdd) { + upsertExecutionState(id); + } + for (const id of nodeIdsToRemove) { + removeNodeExecutionState(id); + } + }, [nodeIds]); +}; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts index e8289d7e07..4b70847ad1 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldInputTemplate.ts @@ -1,20 +1,9 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectFieldInputTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import type { FieldInputTemplate } from 'features/nodes/types/field'; import { useMemo } from 'react'; export const useFieldInputTemplate = (nodeId: string, fieldName: string): FieldInputTemplate | null => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - return selectFieldInputTemplate(nodes, nodeId, fieldName); - }), - [fieldName, nodeId] - ); - - const fieldTemplate = useAppSelector(selector); - + const template = useNodeTemplate(nodeId); + const fieldTemplate = useMemo(() => template.inputs[fieldName] ?? null, [fieldName, template.inputs]); return fieldTemplate; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts index cb154071e9..585ef3fe1c 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldOutputTemplate.ts @@ -1,20 +1,9 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectFieldOutputTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import type { FieldOutputTemplate } from 'features/nodes/types/field'; import { useMemo } from 'react'; export const useFieldOutputTemplate = (nodeId: string, fieldName: string): FieldOutputTemplate | null => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - return selectFieldOutputTemplate(nodes, nodeId, fieldName); - }), - [fieldName, nodeId] - ); - - const fieldTemplate = useAppSelector(selector); - + const template = useNodeTemplate(nodeId); + const fieldTemplate = useMemo(() => template.outputs[fieldName] ?? null, [fieldName, template.outputs]); return fieldTemplate; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts index 7be4ecfd4d..a7e1911720 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplate.ts @@ -1,27 +1,36 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; +import { useStore } from '@nanostores/react'; +import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectFieldInputTemplate, selectFieldOutputTemplate } from 'features/nodes/store/selectors'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectInvocationNodeType } from 'features/nodes/store/selectors'; import type { FieldInputTemplate, FieldOutputTemplate } from 'features/nodes/types/field'; import { useMemo } from 'react'; +import { assert } from 'tsafe'; export const useFieldTemplate = ( nodeId: string, fieldName: string, kind: 'inputs' | 'outputs' -): FieldInputTemplate | FieldOutputTemplate | null => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - if (kind === 'inputs') { - return selectFieldInputTemplate(nodes, nodeId, fieldName); - } - return selectFieldOutputTemplate(nodes, nodeId, fieldName); - }), - [fieldName, kind, nodeId] +): FieldInputTemplate | FieldOutputTemplate => { + const templates = useStore($templates); + const selectNodeType = useMemo( + () => createSelector(selectNodesSlice, (nodes) => selectInvocationNodeType(nodes, nodeId)), + [nodeId] ); - - const fieldTemplate = useAppSelector(selector); + const nodeType = useAppSelector(selectNodeType); + const fieldTemplate = useMemo(() => { + const template = templates[nodeType]; + assert(template, `Template for node type ${nodeType} not found`); + if (kind === 'inputs') { + const fieldTemplate = template.inputs[fieldName]; + assert(fieldTemplate, `Field template for field ${fieldName} not found`); + return fieldTemplate; + } else { + const fieldTemplate = template.outputs[fieldName]; + assert(fieldTemplate, `Field template for field ${fieldName} not found`); + return fieldTemplate; + } + }, [fieldName, kind, nodeType, templates]); return fieldTemplate; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts index e41e019572..5484044e9a 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldTemplateTitle.ts @@ -1,22 +1,8 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectFieldInputTemplate, selectFieldOutputTemplate } from 'features/nodes/store/selectors'; +import { useFieldTemplate } from 'features/nodes/hooks/useFieldTemplate'; import { useMemo } from 'react'; export const useFieldTemplateTitle = (nodeId: string, fieldName: string, kind: 'inputs' | 'outputs'): string | null => { - const selector = useMemo( - () => - createSelector(selectNodesSlice, (nodes) => { - if (kind === 'inputs') { - return selectFieldInputTemplate(nodes, nodeId, fieldName)?.title ?? null; - } - return selectFieldOutputTemplate(nodes, nodeId, fieldName)?.title ?? null; - }), - [fieldName, kind, nodeId] - ); - - const fieldTemplateTitle = useAppSelector(selector); - + const fieldTemplate = useFieldTemplate(nodeId, fieldName, kind); + const fieldTemplateTitle = useMemo(() => fieldTemplate.title, [fieldTemplate]); return fieldTemplateTitle; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts b/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts index a71a4d044e..90c08a94aa 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useFieldType.ts.ts @@ -1,23 +1,9 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectFieldInputTemplate, selectFieldOutputTemplate } from 'features/nodes/store/selectors'; +import { useFieldTemplate } from 'features/nodes/hooks/useFieldTemplate'; import type { FieldType } from 'features/nodes/types/field'; import { useMemo } from 'react'; -export const useFieldType = (nodeId: string, fieldName: string, kind: 'inputs' | 'outputs'): FieldType | null => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - if (kind === 'inputs') { - return selectFieldInputTemplate(nodes, nodeId, fieldName)?.type ?? null; - } - return selectFieldOutputTemplate(nodes, nodeId, fieldName)?.type ?? null; - }), - [fieldName, kind, nodeId] - ); - - const fieldType = useAppSelector(selector); - +export const useFieldType = (nodeId: string, fieldName: string, kind: 'inputs' | 'outputs'): FieldType => { + const fieldTemplate = useFieldTemplate(nodeId, fieldName, kind); + const fieldType = useMemo(() => fieldTemplate.type, [fieldTemplate]); return fieldType; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts index 71344197d5..adf724bdcd 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useGetNodesNeedUpdate.ts @@ -1,20 +1,26 @@ +import { useStore } from '@nanostores/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; import { isInvocationNode } from 'features/nodes/types/invocation'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; - -const selector = createSelector(selectNodesSlice, (nodes) => - nodes.nodes.filter(isInvocationNode).some((node) => { - const template = nodes.templates[node.data.type]; - if (!template) { - return false; - } - return getNeedsUpdate(node, template); - }) -); +import { useMemo } from 'react'; export const useGetNodesNeedUpdate = () => { - const getNeedsUpdate = useAppSelector(selector); - return getNeedsUpdate; + const templates = useStore($templates); + const selector = useMemo( + () => + createSelector(selectNodesSlice, (nodes) => + nodes.nodes.filter(isInvocationNode).some((node) => { + const template = templates[node.data.type]; + if (!template) { + return false; + } + return getNeedsUpdate(node.data, template); + }) + ), + [templates] + ); + const needsUpdate = useAppSelector(selector); + return needsUpdate; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts b/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts index 3ac3cabb22..1078b18cc6 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useHasImageOutput.ts @@ -1,26 +1,20 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { some } from 'lodash-es'; import { useMemo } from 'react'; export const useHasImageOutput = (nodeId: string): boolean => { - const selector = useMemo( + const template = useNodeTemplate(nodeId); + const hasImageOutput = useMemo( () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - const template = selectNodeTemplate(nodes, nodeId); - return some( - template?.outputs, - (output) => - output.type.name === 'ImageField' && - // the image primitive node (node type "image") does not actually save the image, do not show the image-saving checkboxes - template?.type !== 'image' - ); - }), - [nodeId] + some( + template?.outputs, + (output) => + output.type.name === 'ImageField' && + // the image primitive node (node type "image") does not actually save the image, do not show the image-saving checkboxes + template?.type !== 'image' + ), + [template] ); - const hasImageOutput = useAppSelector(selector); return hasImageOutput; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts b/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts index ded05c7b9b..00b4b40176 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts @@ -1,8 +1,12 @@ // TODO: enable this at some point +import { useStore } from '@nanostores/react'; import { useAppSelector, useAppStore } from 'app/store/storeHooks'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { getIsGraphAcyclic } from 'features/nodes/store/util/getIsGraphAcyclic'; +import { getCollectItemType } from 'features/nodes/store/util/makeIsConnectionValidSelector'; import { validateSourceAndTargetTypes } from 'features/nodes/store/util/validateSourceAndTargetTypes'; import type { InvocationNodeData } from 'features/nodes/types/invocation'; +import { isEqual } from 'lodash-es'; import { useCallback } from 'react'; import type { Connection, Node } from 'reactflow'; @@ -13,7 +17,8 @@ import type { Connection, Node } from 'reactflow'; export const useIsValidConnection = () => { const store = useAppStore(); - const shouldValidateGraph = useAppSelector((s) => s.nodes.shouldValidateGraph); + const templates = useStore($templates); + const shouldValidateGraph = useAppSelector((s) => s.workflowSettings.shouldValidateGraph); const isValidConnection = useCallback( ({ source, sourceHandle, target, targetHandle }: Connection): boolean => { // Connection must have valid targets @@ -27,7 +32,7 @@ export const useIsValidConnection = () => { } const state = store.getState(); - const { nodes, edges, templates } = state.nodes; + const { nodes, edges } = state.nodes.present; // Find the source and target nodes const sourceNode = nodes.find((node) => node.id === source) as Node; @@ -40,6 +45,10 @@ export const useIsValidConnection = () => { return false; } + if (targetFieldTemplate.input === 'direct') { + return false; + } + if (!shouldValidateGraph) { // manual override! return true; @@ -57,6 +66,14 @@ export const useIsValidConnection = () => { return false; } + if (targetNode.data.type === 'collect' && targetFieldTemplate.name === 'item') { + // Collect nodes shouldn't mix and match field types + const collectItemType = getCollectItemType(templates, nodes, edges, targetNode.id); + if (collectItemType) { + return isEqual(sourceFieldTemplate.type, collectItemType); + } + } + // Connection is invalid if target already has a connection if ( edges.find((edge) => { @@ -76,7 +93,7 @@ export const useIsValidConnection = () => { // Graphs much be acyclic (no loops!) return getIsGraphAcyclic(source, target, nodes, edges); }, - [shouldValidateGraph, store] + [shouldValidateGraph, templates, store] ); return isValidConnection; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts index bab8ff3f19..75431c949f 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeClassification.ts @@ -1,19 +1,9 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import type { Classification } from 'features/nodes/types/common'; import { useMemo } from 'react'; -export const useNodeClassification = (nodeId: string): Classification | null => { - const selector = useMemo( - () => - createSelector(selectNodesSlice, (nodes) => { - return selectNodeTemplate(nodes, nodeId)?.classification ?? null; - }), - [nodeId] - ); - - const title = useAppSelector(selector); - return title; +export const useNodeClassification = (nodeId: string): Classification => { + const template = useNodeTemplate(nodeId); + const classification = useMemo(() => template.classification, [template]); + return classification; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts index fa21008ff8..738cf80aba 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeData.ts @@ -5,7 +5,7 @@ import { selectNodeData } from 'features/nodes/store/selectors'; import type { InvocationNodeData } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; -export const useNodeData = (nodeId: string): InvocationNodeData | null => { +export const useNodeData = (nodeId: string): InvocationNodeData => { const selector = useMemo( () => createMemoizedSelector(selectNodesSlice, (nodes) => { diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts index aa0294f70f..519bb4728d 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeNeedsUpdate.ts @@ -1,25 +1,11 @@ -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectInvocationNode, selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeData } from 'features/nodes/hooks/useNodeData'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; import { useMemo } from 'react'; export const useNodeNeedsUpdate = (nodeId: string) => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - const node = selectInvocationNode(nodes, nodeId); - const template = selectNodeTemplate(nodes, nodeId); - if (!node || !template) { - return false; - } - return getNeedsUpdate(node, template); - }), - [nodeId] - ); - - const needsUpdate = useAppSelector(selector); - + const data = useNodeData(nodeId); + const template = useNodeTemplate(nodeId); + const needsUpdate = useMemo(() => getNeedsUpdate(data, template), [data, template]); return needsUpdate; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts index 866c9275fb..8b076ade1f 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplate.ts @@ -1,20 +1,23 @@ +import { useStore } from '@nanostores/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { $templates, selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import { selectInvocationNodeType } from 'features/nodes/store/selectors'; import type { InvocationTemplate } from 'features/nodes/types/invocation'; import { useMemo } from 'react'; +import { assert } from 'tsafe'; -export const useNodeTemplate = (nodeId: string): InvocationTemplate | null => { - const selector = useMemo( - () => - createSelector(selectNodesSlice, (nodes) => { - return selectNodeTemplate(nodes, nodeId); - }), +export const useNodeTemplate = (nodeId: string): InvocationTemplate => { + const templates = useStore($templates); + const selectNodeType = useMemo( + () => createSelector(selectNodesSlice, (nodes) => selectInvocationNodeType(nodes, nodeId)), [nodeId] ); - - const nodeTemplate = useAppSelector(selector); - - return nodeTemplate; + const nodeType = useAppSelector(selectNodeType); + const template = useMemo(() => { + const t = templates[nodeType]; + assert(t, `Template for node type ${nodeType} not found`); + return t; + }, [nodeType, templates]); + return template; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts index 120b8c758b..a63e0433aa 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useNodeTemplateTitle.ts @@ -1,18 +1,8 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { useMemo } from 'react'; export const useNodeTemplateTitle = (nodeId: string): string | null => { - const selector = useMemo( - () => - createSelector(selectNodesSlice, (nodes) => { - return selectNodeTemplate(nodes, nodeId)?.title ?? null; - }), - [nodeId] - ); - - const title = useAppSelector(selector); + const template = useNodeTemplate(nodeId); + const title = useMemo(() => template.title, [template.title]); return title; }; diff --git a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts b/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts index 54c092370b..b19d20ab80 100644 --- a/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts +++ b/invokeai/frontend/web/src/features/nodes/hooks/useOutputFieldNames.ts @@ -1,26 +1,10 @@ -import { EMPTY_ARRAY } from 'app/store/constants'; -import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; -import { useAppSelector } from 'app/store/storeHooks'; -import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; -import { selectNodeTemplate } from 'features/nodes/store/selectors'; +import { useNodeTemplate } from 'features/nodes/hooks/useNodeTemplate'; import { getSortedFilteredFieldNames } from 'features/nodes/util/node/getSortedFilteredFieldNames'; import { map } from 'lodash-es'; import { useMemo } from 'react'; -export const useOutputFieldNames = (nodeId: string) => { - const selector = useMemo( - () => - createMemoizedSelector(selectNodesSlice, (nodes) => { - const template = selectNodeTemplate(nodes, nodeId); - if (!template) { - return EMPTY_ARRAY; - } - - return getSortedFilteredFieldNames(map(template.outputs)); - }), - [nodeId] - ); - - const fieldNames = useAppSelector(selector); +export const useOutputFieldNames = (nodeId: string): string[] => { + const template = useNodeTemplate(nodeId); + const fieldNames = useMemo(() => getSortedFilteredFieldNames(map(template.outputs)), [template.outputs]); return fieldNames; }; diff --git a/invokeai/frontend/web/src/features/nodes/store/actions.ts b/invokeai/frontend/web/src/features/nodes/store/actions.ts index 52f1bf6e38..080acb4d95 100644 --- a/invokeai/frontend/web/src/features/nodes/store/actions.ts +++ b/invokeai/frontend/web/src/features/nodes/store/actions.ts @@ -1,6 +1,6 @@ import { createAction, isAnyOf } from '@reduxjs/toolkit'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; -import type { Graph } from 'services/api/types'; +import type { Graph, GraphAndWorkflowResponse } from 'services/api/types'; const textToImageGraphBuilt = createAction('nodes/textToImageGraphBuilt'); const imageToImageGraphBuilt = createAction('nodes/imageToImageGraphBuilt'); @@ -15,7 +15,7 @@ export const isAnyGraphBuilt = isAnyOf( ); export const workflowLoadRequested = createAction<{ - workflow: unknown; + data: GraphAndWorkflowResponse; asCopy: boolean; }>('nodes/workflowLoadRequested'); diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index 0f0417cf71..1f61c77e83 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -1,7 +1,6 @@ -import type { PayloadAction } from '@reduxjs/toolkit'; +import type { PayloadAction, UnknownAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; import type { PersistConfig, RootState } from 'app/store/store'; -import { deepClone } from 'common/util/deepClone'; import { workflowLoaded } from 'features/nodes/store/actions'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; import type { @@ -43,76 +42,21 @@ import { zT2IAdapterModelFieldValue, zVAEModelFieldValue, } from 'features/nodes/types/field'; -import type { AnyNode, InvocationTemplate, NodeExecutionState } from 'features/nodes/types/invocation'; -import { isInvocationNode, isNotesNode, zNodeStatus } from 'features/nodes/types/invocation'; -import { forEach } from 'lodash-es'; -import type { - Connection, - Edge, - EdgeChange, - EdgeRemoveChange, - Node, - NodeChange, - OnConnectStartParams, - Viewport, - XYPosition, -} from 'reactflow'; -import { - addEdge, - applyEdgeChanges, - applyNodeChanges, - getConnectedEdges, - getIncomers, - getOutgoers, - SelectionMode, -} from 'reactflow'; -import { - socketGeneratorProgress, - socketInvocationComplete, - socketInvocationError, - socketInvocationStarted, - socketQueueItemStatusChanged, -} from 'services/events/actions'; -import { v4 as uuidv4 } from 'uuid'; +import type { AnyNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; +import { isInvocationNode, isNotesNode } from 'features/nodes/types/invocation'; +import { atom } from 'nanostores'; +import type { Connection, Edge, EdgeChange, EdgeRemoveChange, Node, NodeChange, Viewport, XYPosition } from 'reactflow'; +import { addEdge, applyEdgeChanges, applyNodeChanges, getConnectedEdges, getIncomers, getOutgoers } from 'reactflow'; +import type { UndoableOptions } from 'redux-undo'; import type { z } from 'zod'; -import type { NodesState } from './types'; -import { findConnectionToValidHandle } from './util/findConnectionToValidHandle'; +import type { NodesState, PendingConnection, Templates } from './types'; import { findUnoccupiedPosition } from './util/findUnoccupiedPosition'; -const initialNodeExecutionState: Omit = { - status: zNodeStatus.enum.PENDING, - error: null, - progress: null, - progressImage: null, - outputs: [], -}; - const initialNodesState: NodesState = { _version: 1, nodes: [], edges: [], - templates: {}, - connectionStartParams: null, - connectionStartFieldType: null, - connectionMade: false, - modifyingEdge: false, - addNewNodePosition: null, - shouldShowMinimapPanel: true, - shouldValidateGraph: true, - shouldAnimateEdges: true, - shouldSnapToGrid: false, - shouldColorEdges: true, - shouldShowEdgeLabels: false, - isAddNodePopoverOpen: false, - nodeOpacity: 1, - selectedNodes: [], - selectedEdges: [], - nodeExecutionStates: {}, - viewport: { x: 0, y: 0, zoom: 1 }, - nodesToCopy: [], - edgesToCopy: [], - selectionMode: SelectionMode.Partial, }; type FieldValueAction = PayloadAction<{ @@ -154,12 +98,12 @@ export const nodesSlice = createSlice({ } state.nodes[nodeIndex] = action.payload.node; }, - nodeAdded: (state, action: PayloadAction) => { - const node = action.payload; + nodeAdded: (state, action: PayloadAction<{ node: AnyNode; cursorPos: XYPosition | null }>) => { + const { node, cursorPos } = action.payload; const position = findUnoccupiedPosition( state.nodes, - state.addNewNodePosition?.x ?? node.position.x, - state.addNewNodePosition?.y ?? node.position.y + cursorPos?.x ?? node.position.x, + cursorPos?.y ?? node.position.y ); node.position = position; node.selected = true; @@ -175,40 +119,6 @@ export const nodesSlice = createSlice({ ); state.nodes.push(node); - - if (!isInvocationNode(node)) { - return; - } - - state.nodeExecutionStates[node.id] = { - nodeId: node.id, - ...initialNodeExecutionState, - }; - - if (state.connectionStartParams) { - const { nodeId, handleId, handleType } = state.connectionStartParams; - if (nodeId && handleId && handleType && state.connectionStartFieldType) { - const newConnection = findConnectionToValidHandle( - node, - state.nodes, - state.edges, - state.templates, - nodeId, - handleId, - handleType, - state.connectionStartFieldType - ); - if (newConnection) { - state.edges = addEdge({ ...newConnection, type: 'default' }, state.edges); - } - } - } - - state.connectionStartParams = null; - state.connectionStartFieldType = null; - }, - edgeChangeStarted: (state) => { - state.modifyingEdge = true; }, edgesChanged: (state, action: PayloadAction) => { state.edges = applyEdgeChanges(action.payload, state.edges); @@ -216,71 +126,8 @@ export const nodesSlice = createSlice({ edgeAdded: (state, action: PayloadAction) => { state.edges = addEdge(action.payload, state.edges); }, - connectionStarted: (state, action: PayloadAction) => { - state.connectionStartParams = action.payload; - state.connectionMade = state.modifyingEdge; - const { nodeId, handleId, handleType } = action.payload; - if (!nodeId || !handleId) { - return; - } - const node = state.nodes.find((n) => n.id === nodeId); - if (!isInvocationNode(node)) { - return; - } - const template = state.templates[node.data.type]; - const field = handleType === 'source' ? template?.outputs[handleId] : template?.inputs[handleId]; - state.connectionStartFieldType = field?.type ?? null; - }, connectionMade: (state, action: PayloadAction) => { - const fieldType = state.connectionStartFieldType; - if (!fieldType) { - return; - } state.edges = addEdge({ ...action.payload, type: 'default' }, state.edges); - - state.connectionMade = true; - }, - connectionEnded: ( - state, - action: PayloadAction<{ - cursorPosition: XYPosition; - mouseOverNodeId: string | null; - }> - ) => { - const { cursorPosition, mouseOverNodeId } = action.payload; - if (!state.connectionMade) { - if (mouseOverNodeId) { - const nodeIndex = state.nodes.findIndex((n) => n.id === mouseOverNodeId); - const mouseOverNode = state.nodes?.[nodeIndex]; - if (mouseOverNode && state.connectionStartParams) { - const { nodeId, handleId, handleType } = state.connectionStartParams; - if (nodeId && handleId && handleType && state.connectionStartFieldType) { - const newConnection = findConnectionToValidHandle( - mouseOverNode, - state.nodes, - state.edges, - state.templates, - nodeId, - handleId, - handleType, - state.connectionStartFieldType - ); - if (newConnection) { - state.edges = addEdge({ ...newConnection, type: 'default' }, state.edges); - } - } - } - state.connectionStartParams = null; - state.connectionStartFieldType = null; - } else { - state.addNewNodePosition = cursorPosition; - state.isAddNodePopoverOpen = true; - } - } else { - state.connectionStartParams = null; - state.connectionStartFieldType = null; - } - state.modifyingEdge = false; }, fieldLabelChanged: ( state, @@ -442,7 +289,6 @@ export const nodesSlice = createSlice({ if (!isInvocationNode(node)) { return; } - delete state.nodeExecutionStates[node.id]; }); }, nodeLabelChanged: (state, action: PayloadAction<{ nodeId: string; label: string }>) => { @@ -474,12 +320,6 @@ export const nodesSlice = createSlice({ state.nodes ); }, - selectedNodesChanged: (state, action: PayloadAction) => { - state.selectedNodes = action.payload; - }, - selectedEdgesChanged: (state, action: PayloadAction) => { - state.selectedEdges = action.payload; - }, fieldValueReset: (state, action: FieldValueAction) => { fieldValueReducer(state, action, zStatefulFieldValue); }, @@ -537,34 +377,10 @@ export const nodesSlice = createSlice({ } node.data.notes = value; }, - shouldShowMinimapPanelChanged: (state, action: PayloadAction) => { - state.shouldShowMinimapPanel = action.payload; - }, nodeEditorReset: (state) => { state.nodes = []; state.edges = []; }, - shouldValidateGraphChanged: (state, action: PayloadAction) => { - state.shouldValidateGraph = action.payload; - }, - shouldAnimateEdgesChanged: (state, action: PayloadAction) => { - state.shouldAnimateEdges = action.payload; - }, - shouldShowEdgeLabelsChanged: (state, action: PayloadAction) => { - state.shouldShowEdgeLabels = action.payload; - }, - shouldSnapToGridChanged: (state, action: PayloadAction) => { - state.shouldSnapToGrid = action.payload; - }, - shouldColorEdgesChanged: (state, action: PayloadAction) => { - state.shouldColorEdges = action.payload; - }, - nodeOpacityChanged: (state, action: PayloadAction) => { - state.nodeOpacity = action.payload; - }, - viewportChanged: (state, action: PayloadAction) => { - state.viewport = action.payload; - }, selectedAll: (state) => { state.nodes = applyNodeChanges( state.nodes.map((n) => ({ id: n.id, type: 'select', selected: true })), @@ -575,136 +391,49 @@ export const nodesSlice = createSlice({ state.edges ); }, - selectionCopied: (state) => { - const nodesToCopy: AnyNode[] = []; - const edgesToCopy: Edge[] = []; + selectionPasted: (state, action: PayloadAction<{ nodes: AnyNode[]; edges: InvocationNodeEdge[] }>) => { + const { nodes, edges } = action.payload; - for (const node of state.nodes) { - if (node.selected) { - nodesToCopy.push(deepClone(node)); - } - } + const nodeChanges: NodeChange[] = []; - for (const edge of state.edges) { - if (edge.selected) { - edgesToCopy.push(deepClone(edge)); - } - } - - state.nodesToCopy = nodesToCopy; - state.edgesToCopy = edgesToCopy; - - if (state.nodesToCopy.length > 0) { - const averagePosition = { x: 0, y: 0 }; - state.nodesToCopy.forEach((e) => { - const xOffset = 0.15 * (e.width ?? 0); - const yOffset = 0.5 * (e.height ?? 0); - averagePosition.x += e.position.x + xOffset; - averagePosition.y += e.position.y + yOffset; + // Deselect existing nodes + state.nodes.forEach((n) => { + nodeChanges.push({ + id: n.data.id, + type: 'select', + selected: false, }); - - averagePosition.x /= state.nodesToCopy.length; - averagePosition.y /= state.nodesToCopy.length; - - state.nodesToCopy.forEach((e) => { - e.position.x -= averagePosition.x; - e.position.y -= averagePosition.y; + }); + // Add new nodes + nodes.forEach((n) => { + nodeChanges.push({ + item: n, + type: 'add', }); - } - }, - selectionPasted: (state, action: PayloadAction<{ cursorPosition?: XYPosition }>) => { - const { cursorPosition } = action.payload; - const newNodes: AnyNode[] = []; - - for (const node of state.nodesToCopy) { - newNodes.push(deepClone(node)); - } - - const oldNodeIds = newNodes.map((n) => n.data.id); - - const newEdges: Edge[] = []; - - for (const edge of state.edgesToCopy) { - if (oldNodeIds.includes(edge.source) && oldNodeIds.includes(edge.target)) { - newEdges.push(deepClone(edge)); - } - } - - newEdges.forEach((e) => (e.selected = true)); - - newNodes.forEach((node) => { - const newNodeId = uuidv4(); - newEdges.forEach((edge) => { - if (edge.source === node.data.id) { - edge.source = newNodeId; - edge.id = edge.id.replace(node.data.id, newNodeId); - } - if (edge.target === node.data.id) { - edge.target = newNodeId; - edge.id = edge.id.replace(node.data.id, newNodeId); - } - }); - node.selected = true; - node.id = newNodeId; - node.data.id = newNodeId; - - const position = findUnoccupiedPosition( - state.nodes, - node.position.x + (cursorPosition?.x ?? 0), - node.position.y + (cursorPosition?.y ?? 0) - ); - - node.position = position; }); - const nodeAdditions: NodeChange[] = newNodes.map((n) => ({ - item: n, - type: 'add', - })); - const nodeSelectionChanges: NodeChange[] = state.nodes.map((n) => ({ - id: n.data.id, - type: 'select', - selected: false, - })); - - const edgeAdditions: EdgeChange[] = newEdges.map((e) => ({ - item: e, - type: 'add', - })); - const edgeSelectionChanges: EdgeChange[] = state.edges.map((e) => ({ - id: e.id, - type: 'select', - selected: false, - })); - - state.nodes = applyNodeChanges(nodeAdditions.concat(nodeSelectionChanges), state.nodes); - - state.edges = applyEdgeChanges(edgeAdditions.concat(edgeSelectionChanges), state.edges); - - newNodes.forEach((node) => { - state.nodeExecutionStates[node.id] = { - nodeId: node.id, - ...initialNodeExecutionState, - }; + const edgeChanges: EdgeChange[] = []; + // Deselect existing edges + state.edges.forEach((e) => { + edgeChanges.push({ + id: e.id, + type: 'select', + selected: false, + }); + }); + // Add new edges + edges.forEach((e) => { + edgeChanges.push({ + item: e, + type: 'add', + }); }); - }, - addNodePopoverOpened: (state) => { - state.addNewNodePosition = null; //Create the node in viewport center by default - state.isAddNodePopoverOpen = true; - }, - addNodePopoverClosed: (state) => { - state.isAddNodePopoverOpen = false; - //Make sure these get reset if we close the popover and haven't selected a node - state.connectionStartParams = null; - state.connectionStartFieldType = null; - }, - selectionModeChanged: (state, action: PayloadAction) => { - state.selectionMode = action.payload ? SelectionMode.Full : SelectionMode.Partial; - }, - nodeTemplatesBuilt: (state, action: PayloadAction>) => { - state.templates = action.payload; + state.nodes = applyNodeChanges(nodeChanges, state.nodes); + state.edges = applyEdgeChanges(edgeChanges, state.edges); }, + undo: (state) => state, + redo: (state) => state, }, extraReducers: (builder) => { builder.addCase(workflowLoaded, (state, action) => { @@ -720,75 +449,13 @@ export const nodesSlice = createSlice({ edges.map((edge) => ({ item: edge, type: 'add' })), [] ); - - state.nodeExecutionStates = nodes.reduce>((acc, node) => { - acc[node.id] = { - nodeId: node.id, - ...initialNodeExecutionState, - }; - return acc; - }, {}); - }); - - builder.addCase(socketInvocationStarted, (state, action) => { - const { source_node_id } = action.payload.data; - const node = state.nodeExecutionStates[source_node_id]; - if (node) { - node.status = zNodeStatus.enum.IN_PROGRESS; - } - }); - builder.addCase(socketInvocationComplete, (state, action) => { - const { source_node_id, result } = action.payload.data; - const nes = state.nodeExecutionStates[source_node_id]; - if (nes) { - nes.status = zNodeStatus.enum.COMPLETED; - if (nes.progress !== null) { - nes.progress = 1; - } - nes.outputs.push(result); - } - }); - builder.addCase(socketInvocationError, (state, action) => { - const { source_node_id } = action.payload.data; - const node = state.nodeExecutionStates[source_node_id]; - if (node) { - node.status = zNodeStatus.enum.FAILED; - node.error = action.payload.data.error; - node.progress = null; - node.progressImage = null; - } - }); - builder.addCase(socketGeneratorProgress, (state, action) => { - const { source_node_id, step, total_steps, progress_image } = action.payload.data; - const node = state.nodeExecutionStates[source_node_id]; - if (node) { - node.status = zNodeStatus.enum.IN_PROGRESS; - node.progress = (step + 1) / total_steps; - node.progressImage = progress_image ?? null; - } - }); - builder.addCase(socketQueueItemStatusChanged, (state, action) => { - if (['in_progress'].includes(action.payload.data.queue_item.status)) { - forEach(state.nodeExecutionStates, (nes) => { - nes.status = zNodeStatus.enum.PENDING; - nes.error = null; - nes.progress = null; - nes.progressImage = null; - nes.outputs = []; - }); - } }); }, }); export const { - addNodePopoverClosed, - addNodePopoverOpened, - connectionEnded, connectionMade, - connectionStarted, edgeDeleted, - edgeChangeStarted, edgesChanged, edgesDeleted, fieldValueReset, @@ -816,31 +483,97 @@ export const { nodeIsOpenChanged, nodeLabelChanged, nodeNotesChanged, - nodeOpacityChanged, nodesChanged, nodesDeleted, nodeUseCacheChanged, notesNodeValueChanged, selectedAll, - selectedEdgesChanged, - selectedNodesChanged, - selectionCopied, - selectionModeChanged, selectionPasted, - shouldAnimateEdgesChanged, - shouldColorEdgesChanged, - shouldShowMinimapPanelChanged, - shouldSnapToGridChanged, - shouldValidateGraphChanged, - viewportChanged, edgeAdded, - nodeTemplatesBuilt, - shouldShowEdgeLabelsChanged, + undo, + redo, } = nodesSlice.actions; +export const $cursorPos = atom(null); +export const $templates = atom({}); +export const $copiedNodes = atom([]); +export const $copiedEdges = atom([]); +export const $edgesToCopiedNodes = atom([]); +export const $pendingConnection = atom(null); +export const $isUpdatingEdge = atom(false); +export const $viewport = atom({ x: 0, y: 0, zoom: 1 }); +export const $isAddNodePopoverOpen = atom(false); +export const closeAddNodePopover = () => { + $isAddNodePopoverOpen.set(false); + $pendingConnection.set(null); +}; +export const openAddNodePopover = () => { + $isAddNodePopoverOpen.set(true); +}; + +export const selectNodesSlice = (state: RootState) => state.nodes.present; + +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +const migrateNodesState = (state: any): any => { + if (!('_version' in state)) { + state._version = 1; + } + return state; +}; + +export const nodesPersistConfig: PersistConfig = { + name: nodesSlice.name, + initialState: initialNodesState, + migrate: migrateNodesState, + persistDenylist: [], +}; + +const selectionMatcher = isAnyOf(selectedAll, selectionPasted, nodeExclusivelySelected); + +const isSelectionAction = (action: UnknownAction) => { + if (selectionMatcher(action)) { + return true; + } + if (nodesChanged.match(action)) { + if (action.payload.every((change) => change.type === 'select')) { + return true; + } + } + return false; +}; + +const individualGroupByMatcher = isAnyOf(nodesChanged); + +export const nodesUndoableConfig: UndoableOptions = { + limit: 64, + undoType: nodesSlice.actions.undo.type, + redoType: nodesSlice.actions.redo.type, + groupBy: (action, state, history) => { + if (isSelectionAction(action)) { + // Changes to selection should never be recorded on their own + return history.group; + } + if (individualGroupByMatcher(action)) { + return action.type; + } + return null; + }, + filter: (action, _state, _history) => { + // Ignore all actions from other slices + if (!action.type.startsWith(nodesSlice.name)) { + return false; + } + if (nodesChanged.match(action)) { + if (action.payload.every((change) => change.type === 'dimensions')) { + return false; + } + } + return true; + }, +}; + // This is used for tracking `state.workflow.isTouched` export const isAnyNodeOrEdgeMutation = isAnyOf( - connectionEnded, connectionMade, edgeDeleted, edgesChanged, @@ -873,30 +606,3 @@ export const isAnyNodeOrEdgeMutation = isAnyOf( selectionPasted, edgeAdded ); - -export const selectNodesSlice = (state: RootState) => state.nodes; - -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -const migrateNodesState = (state: any): any => { - if (!('_version' in state)) { - state._version = 1; - } - return state; -}; - -export const nodesPersistConfig: PersistConfig = { - name: nodesSlice.name, - initialState: initialNodesState, - migrate: migrateNodesState, - persistDenylist: [ - 'connectionStartParams', - 'connectionStartFieldType', - 'selectedNodes', - 'selectedEdges', - 'nodesToCopy', - 'edgesToCopy', - 'connectionMade', - 'modifyingEdge', - 'addNewNodePosition', - ], -}; diff --git a/invokeai/frontend/web/src/features/nodes/store/selectors.ts b/invokeai/frontend/web/src/features/nodes/store/selectors.ts index 90675d6270..4739a77e1c 100644 --- a/invokeai/frontend/web/src/features/nodes/store/selectors.ts +++ b/invokeai/frontend/web/src/features/nodes/store/selectors.ts @@ -1,26 +1,23 @@ import type { NodesState } from 'features/nodes/store/types'; -import type { FieldInputInstance, FieldInputTemplate, FieldOutputTemplate } from 'features/nodes/types/field'; -import type { InvocationNode, InvocationNodeData, InvocationTemplate } from 'features/nodes/types/invocation'; +import type { FieldInputInstance } from 'features/nodes/types/field'; +import type { InvocationNode, InvocationNodeData } from 'features/nodes/types/invocation'; import { isInvocationNode } from 'features/nodes/types/invocation'; +import { assert } from 'tsafe'; -export const selectInvocationNode = (nodesSlice: NodesState, nodeId: string): InvocationNode | null => { +const selectInvocationNode = (nodesSlice: NodesState, nodeId: string): InvocationNode => { const node = nodesSlice.nodes.find((node) => node.id === nodeId); - if (!isInvocationNode(node)) { - return null; - } + assert(isInvocationNode(node), `Node ${nodeId} is not an invocation node`); return node; }; -export const selectNodeData = (nodesSlice: NodesState, nodeId: string): InvocationNodeData | null => { - return selectInvocationNode(nodesSlice, nodeId)?.data ?? null; +export const selectInvocationNodeType = (nodesSlice: NodesState, nodeId: string): string => { + const node = selectInvocationNode(nodesSlice, nodeId); + return node.data.type; }; -export const selectNodeTemplate = (nodesSlice: NodesState, nodeId: string): InvocationTemplate | null => { +export const selectNodeData = (nodesSlice: NodesState, nodeId: string): InvocationNodeData => { const node = selectInvocationNode(nodesSlice, nodeId); - if (!node) { - return null; - } - return nodesSlice.templates[node.data.type] ?? null; + return node.data; }; export const selectFieldInputInstance = ( @@ -32,20 +29,10 @@ export const selectFieldInputInstance = ( return data?.inputs[fieldName] ?? null; }; -export const selectFieldInputTemplate = ( - nodesSlice: NodesState, - nodeId: string, - fieldName: string -): FieldInputTemplate | null => { - const template = selectNodeTemplate(nodesSlice, nodeId); - return template?.inputs[fieldName] ?? null; -}; - -export const selectFieldOutputTemplate = ( - nodesSlice: NodesState, - nodeId: string, - fieldName: string -): FieldOutputTemplate | null => { - const template = selectNodeTemplate(nodesSlice, nodeId); - return template?.outputs[fieldName] ?? null; +export const selectLastSelectedNode = (nodesSlice: NodesState) => { + const selectedNodes = nodesSlice.nodes.filter((n) => n.selected); + if (selectedNodes.length === 1) { + return selectedNodes[0]; + } + return null; }; diff --git a/invokeai/frontend/web/src/features/nodes/store/types.ts b/invokeai/frontend/web/src/features/nodes/store/types.ts index 89b4855193..2f514bdb5b 100644 --- a/invokeai/frontend/web/src/features/nodes/store/types.ts +++ b/invokeai/frontend/web/src/features/nodes/store/types.ts @@ -1,38 +1,31 @@ -import type { FieldIdentifier, FieldType, StatefulFieldValue } from 'features/nodes/types/field'; +import type { + FieldIdentifier, + FieldInputTemplate, + FieldOutputTemplate, + StatefulFieldValue, +} from 'features/nodes/types/field'; import type { AnyNode, + InvocationNode, InvocationNodeEdge, InvocationTemplate, NodeExecutionState, } from 'features/nodes/types/invocation'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; -import type { OnConnectStartParams, SelectionMode, Viewport, XYPosition } from 'reactflow'; + +export type Templates = Record; +export type NodeExecutionStates = Record; + +export type PendingConnection = { + node: InvocationNode; + template: InvocationTemplate; + fieldTemplate: FieldInputTemplate | FieldOutputTemplate; +}; export type NodesState = { _version: 1; nodes: AnyNode[]; edges: InvocationNodeEdge[]; - templates: Record; - connectionStartParams: OnConnectStartParams | null; - connectionStartFieldType: FieldType | null; - connectionMade: boolean; - modifyingEdge: boolean; - shouldShowMinimapPanel: boolean; - shouldValidateGraph: boolean; - shouldAnimateEdges: boolean; - nodeOpacity: number; - shouldSnapToGrid: boolean; - shouldColorEdges: boolean; - shouldShowEdgeLabels: boolean; - selectedNodes: string[]; - selectedEdges: string[]; - nodeExecutionStates: Record; - viewport: Viewport; - nodesToCopy: AnyNode[]; - edgesToCopy: InvocationNodeEdge[]; - isAddNodePopoverOpen: boolean; - addNewNodePosition: XYPosition | null; - selectionMode: SelectionMode; }; export type WorkflowMode = 'edit' | 'view'; diff --git a/invokeai/frontend/web/src/features/nodes/store/util/findConnectionToValidHandle.ts b/invokeai/frontend/web/src/features/nodes/store/util/findConnectionToValidHandle.ts index ef899c5f41..1f33c52371 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/findConnectionToValidHandle.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/findConnectionToValidHandle.ts @@ -1,112 +1,105 @@ -import type { FieldInputTemplate, FieldOutputTemplate, FieldType } from 'features/nodes/types/field'; -import type { AnyNode, InvocationNodeEdge, InvocationTemplate } from 'features/nodes/types/invocation'; -import { isInvocationNode } from 'features/nodes/types/invocation'; -import type { Connection, Edge, HandleType, Node } from 'reactflow'; +import type { PendingConnection, Templates } from 'features/nodes/store/types'; +import { getCollectItemType } from 'features/nodes/store/util/makeIsConnectionValidSelector'; +import type { AnyNode, InvocationNode, InvocationNodeEdge, InvocationTemplate } from 'features/nodes/types/invocation'; +import { differenceWith, isEqual, map } from 'lodash-es'; +import type { Connection } from 'reactflow'; +import { assert } from 'tsafe'; import { getIsGraphAcyclic } from './getIsGraphAcyclic'; import { validateSourceAndTargetTypes } from './validateSourceAndTargetTypes'; -const isValidConnection = ( - edges: Edge[], - handleCurrentType: HandleType, - handleCurrentFieldType: FieldType, - node: Node, - handle: FieldInputTemplate | FieldOutputTemplate -) => { - let isValidConnection = true; - if (handleCurrentType === 'source') { - if ( - edges.find((edge) => { - return edge.target === node.id && edge.targetHandle === handle.name; - }) - ) { - isValidConnection = false; - } - } else { - if ( - edges.find((edge) => { - return edge.source === node.id && edge.sourceHandle === handle.name; - }) - ) { - isValidConnection = false; - } - } - - if (!validateSourceAndTargetTypes(handleCurrentFieldType, handle.type)) { - isValidConnection = false; - } - - return isValidConnection; -}; - -export const findConnectionToValidHandle = ( - node: AnyNode, +export const getFirstValidConnection = ( + templates: Templates, nodes: AnyNode[], edges: InvocationNodeEdge[], - templates: Record, - handleCurrentNodeId: string, - handleCurrentName: string, - handleCurrentType: HandleType, - handleCurrentFieldType: FieldType + pendingConnection: PendingConnection, + candidateNode: InvocationNode, + candidateTemplate: InvocationTemplate ): Connection | null => { - if (node.id === handleCurrentNodeId || !isInvocationNode(node)) { + if (pendingConnection.node.id === candidateNode.id) { + // Cannot connect to self return null; } - const template = templates[node.data.type]; + const pendingFieldKind = pendingConnection.fieldTemplate.fieldKind === 'input' ? 'target' : 'source'; - if (!template) { - return null; - } - - const handles = handleCurrentType === 'source' ? template.inputs : template.outputs; - - //Prioritize handles whos name matches the node we're coming from - const handle = handles[handleCurrentName]; - - if (handle) { - const sourceID = handleCurrentType === 'source' ? handleCurrentNodeId : node.id; - const targetID = handleCurrentType === 'source' ? node.id : handleCurrentNodeId; - const sourceHandle = handleCurrentType === 'source' ? handleCurrentName : handle.name; - const targetHandle = handleCurrentType === 'source' ? handle.name : handleCurrentName; - - const isGraphAcyclic = getIsGraphAcyclic(sourceID, targetID, nodes, edges); - - const valid = isValidConnection(edges, handleCurrentType, handleCurrentFieldType, node, handle); - - if (isGraphAcyclic && valid) { + if (pendingFieldKind === 'source') { + // Connecting from a source to a target + if (!getIsGraphAcyclic(pendingConnection.node.id, candidateNode.id, nodes, edges)) { + return null; + } + if (candidateNode.data.type === 'collect') { + // Special handling for collect node - the `item` field takes any number of connections return { - source: sourceID, - sourceHandle: sourceHandle, - target: targetID, - targetHandle: targetHandle, + source: pendingConnection.node.id, + sourceHandle: pendingConnection.fieldTemplate.name, + target: candidateNode.id, + targetHandle: 'item', + }; + } + // Only one connection per target field is allowed - look for an unconnected target field + const candidateFields = map(candidateTemplate.inputs).filter((i) => i.input !== 'direct'); + const candidateConnectedFields = edges + .filter((edge) => edge.target === candidateNode.id) + .map((edge) => { + // Edges must always have a targetHandle, safe to assert here + assert(edge.targetHandle); + return edge.targetHandle; + }); + const candidateUnconnectedFields = differenceWith( + candidateFields, + candidateConnectedFields, + (field, connectedFieldName) => field.name === connectedFieldName + ); + const candidateField = candidateUnconnectedFields.find((field) => + validateSourceAndTargetTypes(pendingConnection.fieldTemplate.type, field.type) + ); + if (candidateField) { + return { + source: pendingConnection.node.id, + sourceHandle: pendingConnection.fieldTemplate.name, + target: candidateNode.id, + targetHandle: candidateField.name, + }; + } + } else { + // Connecting from a target to a source + // Ensure we there is not already an edge to the target, except for collect nodes + const isCollect = pendingConnection.node.data.type === 'collect'; + const isTargetAlreadyConnected = edges.some( + (e) => e.target === pendingConnection.node.id && e.targetHandle === pendingConnection.fieldTemplate.name + ); + if (!isCollect && isTargetAlreadyConnected) { + return null; + } + + if (!getIsGraphAcyclic(candidateNode.id, pendingConnection.node.id, nodes, edges)) { + return null; + } + + // Sources/outputs can have any number of edges, we can take the first matching output field + let candidateFields = map(candidateTemplate.outputs); + if (isCollect) { + // Narrow candidates to same field type as already is connected to the collect node + const collectItemType = getCollectItemType(templates, nodes, edges, pendingConnection.node.id); + if (collectItemType) { + candidateFields = candidateFields.filter((field) => isEqual(field.type, collectItemType)); + } + } + const candidateField = candidateFields.find((field) => { + const isValid = validateSourceAndTargetTypes(field.type, pendingConnection.fieldTemplate.type); + const isAlreadyConnected = edges.some((e) => e.source === candidateNode.id && e.sourceHandle === field.name); + return isValid && !isAlreadyConnected; + }); + if (candidateField) { + return { + source: candidateNode.id, + sourceHandle: candidateField.name, + target: pendingConnection.node.id, + targetHandle: pendingConnection.fieldTemplate.name, }; } } - for (const handleName in handles) { - const handle = handles[handleName]; - if (!handle) { - continue; - } - - const sourceID = handleCurrentType === 'source' ? handleCurrentNodeId : node.id; - const targetID = handleCurrentType === 'source' ? node.id : handleCurrentNodeId; - const sourceHandle = handleCurrentType === 'source' ? handleCurrentName : handle.name; - const targetHandle = handleCurrentType === 'source' ? handle.name : handleCurrentName; - - const isGraphAcyclic = getIsGraphAcyclic(sourceID, targetID, nodes, edges); - - const valid = isValidConnection(edges, handleCurrentType, handleCurrentFieldType, node, handle); - - if (isGraphAcyclic && valid) { - return { - source: sourceID, - sourceHandle: sourceHandle, - target: targetID, - targetHandle: targetHandle, - }; - } - } return null; }; diff --git a/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts b/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts index 114633e875..bd110a50a1 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/findUnoccupiedPosition.ts @@ -4,8 +4,8 @@ export const findUnoccupiedPosition = (nodes: Node[], x: number, y: number) => { let newX = x; let newY = y; while (nodes.find((n) => n.position.x === newX && n.position.y === newY)) { - newX = newX + 50; - newY = newY + 50; + newX = Math.floor(newX + 50); + newY = Math.floor(newY + 50); } return { x: newX, y: newY }; }; diff --git a/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts b/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts index d6ea0d9c86..90e75e0d87 100644 --- a/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts +++ b/invokeai/frontend/web/src/features/nodes/store/util/makeIsConnectionValidSelector.ts @@ -1,39 +1,66 @@ import { createSelector } from '@reduxjs/toolkit'; import { selectNodesSlice } from 'features/nodes/store/nodesSlice'; +import type { PendingConnection, Templates } from 'features/nodes/store/types'; import type { FieldType } from 'features/nodes/types/field'; +import type { AnyNode, InvocationNodeEdge } from 'features/nodes/types/invocation'; import i18n from 'i18next'; +import { isEqual } from 'lodash-es'; import type { HandleType } from 'reactflow'; +import { assert } from 'tsafe'; import { getIsGraphAcyclic } from './getIsGraphAcyclic'; import { validateSourceAndTargetTypes } from './validateSourceAndTargetTypes'; +export const getCollectItemType = ( + templates: Templates, + nodes: AnyNode[], + edges: InvocationNodeEdge[], + nodeId: string +): FieldType | null => { + const firstEdgeToCollect = edges.find((edge) => edge.target === nodeId && edge.targetHandle === 'item'); + if (!firstEdgeToCollect?.sourceHandle) { + return null; + } + const node = nodes.find((n) => n.id === firstEdgeToCollect.source); + if (!node) { + return null; + } + const template = templates[node.data.type]; + if (!template) { + return null; + } + const fieldType = template.outputs[firstEdgeToCollect.sourceHandle]?.type ?? null; + return fieldType; +}; + /** * NOTE: The logic here must be duplicated in `invokeai/frontend/web/src/features/nodes/hooks/useIsValidConnection.ts` * TODO: Figure out how to do this without duplicating all the logic */ export const makeConnectionErrorSelector = ( + templates: Templates, + pendingConnection: PendingConnection | null, nodeId: string, fieldName: string, handleType: HandleType, fieldType?: FieldType | null ) => { return createSelector(selectNodesSlice, (nodesSlice) => { + const { nodes, edges } = nodesSlice; + if (!fieldType) { return i18n.t('nodes.noFieldType'); } - const { connectionStartFieldType, connectionStartParams, nodes, edges } = nodesSlice; - - if (!connectionStartParams || !connectionStartFieldType) { + if (!pendingConnection) { return i18n.t('nodes.noConnectionInProgress'); } - const { - handleType: connectionHandleType, - nodeId: connectionNodeId, - handleId: connectionFieldName, - } = connectionStartParams; + const connectionNodeId = pendingConnection.node.id; + const connectionFieldName = pendingConnection.fieldTemplate.name; + const connectionHandleType = pendingConnection.fieldTemplate.fieldKind === 'input' ? 'target' : 'source'; + const connectionStartFieldType = pendingConnection.fieldTemplate.type; if (!connectionHandleType || !connectionNodeId || !connectionFieldName) { return i18n.t('nodes.noConnectionData'); @@ -54,26 +81,45 @@ export const makeConnectionErrorSelector = ( } // we have to figure out which is the target and which is the source - const target = handleType === 'target' ? nodeId : connectionNodeId; - const targetHandle = handleType === 'target' ? fieldName : connectionFieldName; - const source = handleType === 'source' ? nodeId : connectionNodeId; - const sourceHandle = handleType === 'source' ? fieldName : connectionFieldName; + const targetNodeId = handleType === 'target' ? nodeId : connectionNodeId; + const targetFieldName = handleType === 'target' ? fieldName : connectionFieldName; + const sourceNodeId = handleType === 'source' ? nodeId : connectionNodeId; + const sourceFieldName = handleType === 'source' ? fieldName : connectionFieldName; if ( edges.find((edge) => { - edge.target === target && - edge.targetHandle === targetHandle && - edge.source === source && - edge.sourceHandle === sourceHandle; + edge.target === targetNodeId && + edge.targetHandle === targetFieldName && + edge.source === sourceNodeId && + edge.sourceHandle === sourceFieldName; }) ) { // We already have a connection from this source to this target return i18n.t('nodes.cannotDuplicateConnection'); } + const targetNode = nodes.find((node) => node.id === targetNodeId); + assert(targetNode, `Target node not found: ${targetNodeId}`); + const targetTemplate = templates[targetNode.data.type]; + assert(targetTemplate, `Target template not found: ${targetNode.data.type}`); + + if (targetTemplate.inputs[targetFieldName]?.input === 'direct') { + return i18n.t('nodes.cannotConnectToDirectInput'); + } + + if (targetNode.data.type === 'collect' && targetFieldName === 'item') { + // Collect nodes shouldn't mix and match field types + const collectItemType = getCollectItemType(templates, nodes, edges, targetNode.id); + if (collectItemType) { + if (!isEqual(sourceType, collectItemType)) { + return i18n.t('nodes.cannotMixAndMatchCollectionItemTypes'); + } + } + } + if ( edges.find((edge) => { - return edge.target === target && edge.targetHandle === targetHandle; + return edge.target === targetNodeId && edge.targetHandle === targetFieldName; }) && // except CollectionItem inputs can have multiples targetType.name !== 'CollectionItemField' diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts b/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts new file mode 100644 index 0000000000..4a2e45abde --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/store/workflowSettingsSlice.ts @@ -0,0 +1,87 @@ +import type { PayloadAction } from '@reduxjs/toolkit'; +import { createSlice } from '@reduxjs/toolkit'; +import type { PersistConfig, RootState } from 'app/store/store'; +import { SelectionMode } from 'reactflow'; + +type WorkflowSettingsState = { + _version: 1; + shouldShowMinimapPanel: boolean; + shouldValidateGraph: boolean; + shouldAnimateEdges: boolean; + nodeOpacity: number; + shouldSnapToGrid: boolean; + shouldColorEdges: boolean; + shouldShowEdgeLabels: boolean; + selectionMode: SelectionMode; +}; + +const initialState: WorkflowSettingsState = { + _version: 1, + shouldShowMinimapPanel: true, + shouldValidateGraph: true, + shouldAnimateEdges: true, + shouldSnapToGrid: false, + shouldColorEdges: true, + shouldShowEdgeLabels: false, + nodeOpacity: 1, + selectionMode: SelectionMode.Partial, +}; + +export const workflowSettingsSlice = createSlice({ + name: 'workflowSettings', + initialState, + reducers: { + shouldShowMinimapPanelChanged: (state, action: PayloadAction) => { + state.shouldShowMinimapPanel = action.payload; + }, + shouldValidateGraphChanged: (state, action: PayloadAction) => { + state.shouldValidateGraph = action.payload; + }, + shouldAnimateEdgesChanged: (state, action: PayloadAction) => { + state.shouldAnimateEdges = action.payload; + }, + shouldShowEdgeLabelsChanged: (state, action: PayloadAction) => { + state.shouldShowEdgeLabels = action.payload; + }, + shouldSnapToGridChanged: (state, action: PayloadAction) => { + state.shouldSnapToGrid = action.payload; + }, + shouldColorEdgesChanged: (state, action: PayloadAction) => { + state.shouldColorEdges = action.payload; + }, + nodeOpacityChanged: (state, action: PayloadAction) => { + state.nodeOpacity = action.payload; + }, + selectionModeChanged: (state, action: PayloadAction) => { + state.selectionMode = action.payload ? SelectionMode.Full : SelectionMode.Partial; + }, + }, +}); + +export const { + shouldAnimateEdgesChanged, + shouldColorEdgesChanged, + shouldShowMinimapPanelChanged, + shouldShowEdgeLabelsChanged, + shouldSnapToGridChanged, + shouldValidateGraphChanged, + nodeOpacityChanged, + selectionModeChanged, +} = workflowSettingsSlice.actions; + +export const selectWorkflowSettingsSlice = (state: RootState) => state.workflowSettings; + +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +const migrateWorkflowSettingsState = (state: any): any => { + if (!('_version' in state)) { + state._version = 1; + } + return state; +}; + +export const workflowSettingsPersistConfig: PersistConfig = { + name: workflowSettingsSlice.name, + initialState, + migrate: migrateWorkflowSettingsState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts b/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts index f2ebf94b06..bdf0b81fe5 100644 --- a/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts +++ b/invokeai/frontend/web/src/features/nodes/types/common.test-d.ts @@ -11,7 +11,7 @@ import type { SchedulerField, T2IAdapterField, } from 'features/nodes/types/common'; -import type { S } from 'services/api/types'; +import type { Invocation, S } from 'services/api/types'; import type { Equals, Extends } from 'tsafe'; import { assert } from 'tsafe'; import { describe, test } from 'vitest'; @@ -26,7 +26,7 @@ describe('Common types', () => { test('ImageField', () => assert>()); test('BoardField', () => assert>()); test('ColorField', () => assert>()); - test('SchedulerField', () => assert>>()); + test('SchedulerField', () => assert['scheduler']>>>()); test('ControlField', () => assert>()); // @ts-expect-error TODO(psyche): fix types test('IPAdapterField', () => assert>()); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addControlLayersToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/addControlLayersToGraph.ts deleted file mode 100644 index 30c15fae10..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addControlLayersToGraph.ts +++ /dev/null @@ -1,692 +0,0 @@ -import { getStore } from 'app/store/nanostores/store'; -import type { RootState } from 'app/store/store'; -import { - isControlAdapterLayer, - isIPAdapterLayer, - isRegionalGuidanceLayer, - rgLayerMaskImageUploaded, -} from 'features/controlLayers/store/controlLayersSlice'; -import type { RegionalGuidanceLayer } from 'features/controlLayers/store/types'; -import { - type ControlNetConfigV2, - type ImageWithDims, - type IPAdapterConfigV2, - isControlNetConfigV2, - isT2IAdapterConfigV2, - type ProcessorConfig, - type T2IAdapterConfigV2, -} from 'features/controlLayers/util/controlAdapters'; -import { getRegionalPromptLayerBlobs } from 'features/controlLayers/util/getLayerBlobs'; -import type { ImageField } from 'features/nodes/types/common'; -import { - CONTROL_NET_COLLECT, - IP_ADAPTER_COLLECT, - NEGATIVE_CONDITIONING, - NEGATIVE_CONDITIONING_COLLECT, - POSITIVE_CONDITIONING, - POSITIVE_CONDITIONING_COLLECT, - PROMPT_REGION_INVERT_TENSOR_MASK_PREFIX, - PROMPT_REGION_MASK_TO_TENSOR_PREFIX, - PROMPT_REGION_NEGATIVE_COND_PREFIX, - PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX, - PROMPT_REGION_POSITIVE_COND_PREFIX, - T2I_ADAPTER_COLLECT, -} from 'features/nodes/util/graph/constants'; -import { upsertMetadata } from 'features/nodes/util/graph/metadata'; -import { size } from 'lodash-es'; -import { getImageDTO, imagesApi } from 'services/api/endpoints/images'; -import type { - CollectInvocation, - ControlNetInvocation, - CoreMetadataInvocation, - Edge, - ImageDTO, - IPAdapterInvocation, - NonNullableGraph, - S, - T2IAdapterInvocation, -} from 'services/api/types'; -import { assert } from 'tsafe'; - -const buildControlImage = ( - image: ImageWithDims | null, - processedImage: ImageWithDims | null, - processorConfig: ProcessorConfig | null -): ImageField => { - if (processedImage && processorConfig) { - // We've processed the image in the app - use it for the control image. - return { - image_name: processedImage.imageName, - }; - } else if (image) { - // No processor selected, and we have an image - the user provided a processed image, use it for the control image. - return { - image_name: image.imageName, - }; - } - assert(false, 'Attempted to add unprocessed control image'); -}; - -const buildControlNetMetadata = (controlNet: ControlNetConfigV2): S['ControlNetMetadataField'] => { - const { beginEndStepPct, controlMode, image, model, processedImage, processorConfig, weight } = controlNet; - - assert(model, 'ControlNet model is required'); - assert(image, 'ControlNet image is required'); - - const processed_image = - processedImage && processorConfig - ? { - image_name: processedImage.imageName, - } - : null; - - return { - control_model: model, - control_weight: weight, - control_mode: controlMode, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - resize_mode: 'just_resize', - image: { - image_name: image.imageName, - }, - processed_image, - }; -}; - -const addControlNetCollectorSafe = (graph: NonNullableGraph, denoiseNodeId: string) => { - if (graph.nodes[CONTROL_NET_COLLECT]) { - // You see, we've already got one! - return; - } - // Add the ControlNet collector - const controlNetIterateNode: CollectInvocation = { - id: CONTROL_NET_COLLECT, - type: 'collect', - is_intermediate: true, - }; - graph.nodes[CONTROL_NET_COLLECT] = controlNetIterateNode; - graph.edges.push({ - source: { node_id: CONTROL_NET_COLLECT, field: 'collection' }, - destination: { - node_id: denoiseNodeId, - field: 'control', - }, - }); -}; - -const addGlobalControlNetsToGraph = async ( - controlNets: ControlNetConfigV2[], - graph: NonNullableGraph, - denoiseNodeId: string -) => { - if (controlNets.length === 0) { - return; - } - const controlNetMetadata: CoreMetadataInvocation['controlnets'] = []; - addControlNetCollectorSafe(graph, denoiseNodeId); - - for (const controlNet of controlNets) { - if (!controlNet.model) { - return; - } - const { id, beginEndStepPct, controlMode, image, model, processedImage, processorConfig, weight } = controlNet; - - const controlNetNode: ControlNetInvocation = { - id: `control_net_${id}`, - type: 'controlnet', - is_intermediate: true, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - control_mode: controlMode, - resize_mode: 'just_resize', - control_model: model, - control_weight: weight, - image: buildControlImage(image, processedImage, processorConfig), - }; - - graph.nodes[controlNetNode.id] = controlNetNode; - - controlNetMetadata.push(buildControlNetMetadata(controlNet)); - - graph.edges.push({ - source: { node_id: controlNetNode.id, field: 'control' }, - destination: { - node_id: CONTROL_NET_COLLECT, - field: 'item', - }, - }); - } - upsertMetadata(graph, { controlnets: controlNetMetadata }); -}; - -const buildT2IAdapterMetadata = (t2iAdapter: T2IAdapterConfigV2): S['T2IAdapterMetadataField'] => { - const { beginEndStepPct, image, model, processedImage, processorConfig, weight } = t2iAdapter; - - assert(model, 'T2I Adapter model is required'); - assert(image, 'T2I Adapter image is required'); - - const processed_image = - processedImage && processorConfig - ? { - image_name: processedImage.imageName, - } - : null; - - return { - t2i_adapter_model: model, - weight, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - resize_mode: 'just_resize', - image: { - image_name: image.imageName, - }, - processed_image, - }; -}; - -const addT2IAdapterCollectorSafe = (graph: NonNullableGraph, denoiseNodeId: string) => { - if (graph.nodes[T2I_ADAPTER_COLLECT]) { - // You see, we've already got one! - return; - } - // Even though denoise_latents' t2i adapter input is collection or scalar, keep it simple and always use a collect - const t2iAdapterCollectNode: CollectInvocation = { - id: T2I_ADAPTER_COLLECT, - type: 'collect', - is_intermediate: true, - }; - graph.nodes[T2I_ADAPTER_COLLECT] = t2iAdapterCollectNode; - graph.edges.push({ - source: { node_id: T2I_ADAPTER_COLLECT, field: 'collection' }, - destination: { - node_id: denoiseNodeId, - field: 't2i_adapter', - }, - }); -}; - -const addGlobalT2IAdaptersToGraph = async ( - t2iAdapters: T2IAdapterConfigV2[], - graph: NonNullableGraph, - denoiseNodeId: string -) => { - if (t2iAdapters.length === 0) { - return; - } - const t2iAdapterMetadata: CoreMetadataInvocation['t2iAdapters'] = []; - addT2IAdapterCollectorSafe(graph, denoiseNodeId); - - for (const t2iAdapter of t2iAdapters) { - if (!t2iAdapter.model) { - return; - } - const { id, beginEndStepPct, image, model, processedImage, processorConfig, weight } = t2iAdapter; - - const t2iAdapterNode: T2IAdapterInvocation = { - id: `t2i_adapter_${id}`, - type: 't2i_adapter', - is_intermediate: true, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - resize_mode: 'just_resize', - t2i_adapter_model: model, - weight: weight, - image: buildControlImage(image, processedImage, processorConfig), - }; - - graph.nodes[t2iAdapterNode.id] = t2iAdapterNode; - - t2iAdapterMetadata.push(buildT2IAdapterMetadata(t2iAdapter)); - - graph.edges.push({ - source: { node_id: t2iAdapterNode.id, field: 't2i_adapter' }, - destination: { - node_id: T2I_ADAPTER_COLLECT, - field: 'item', - }, - }); - } - - upsertMetadata(graph, { t2iAdapters: t2iAdapterMetadata }); -}; - -const buildIPAdapterMetadata = (ipAdapter: IPAdapterConfigV2): S['IPAdapterMetadataField'] => { - const { weight, model, clipVisionModel, method, beginEndStepPct, image } = ipAdapter; - - assert(model, 'IP Adapter model is required'); - assert(image, 'IP Adapter image is required'); - - return { - ip_adapter_model: model, - clip_vision_model: clipVisionModel, - weight, - method, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - image: { - image_name: image.imageName, - }, - }; -}; - -const addIPAdapterCollectorSafe = (graph: NonNullableGraph, denoiseNodeId: string) => { - if (graph.nodes[IP_ADAPTER_COLLECT]) { - // You see, we've already got one! - return; - } - - const ipAdapterCollectNode: CollectInvocation = { - id: IP_ADAPTER_COLLECT, - type: 'collect', - is_intermediate: true, - }; - graph.nodes[IP_ADAPTER_COLLECT] = ipAdapterCollectNode; - graph.edges.push({ - source: { node_id: IP_ADAPTER_COLLECT, field: 'collection' }, - destination: { - node_id: denoiseNodeId, - field: 'ip_adapter', - }, - }); -}; - -const addGlobalIPAdaptersToGraph = async ( - ipAdapters: IPAdapterConfigV2[], - graph: NonNullableGraph, - denoiseNodeId: string -) => { - if (ipAdapters.length === 0) { - return; - } - const ipAdapterMetdata: CoreMetadataInvocation['ipAdapters'] = []; - addIPAdapterCollectorSafe(graph, denoiseNodeId); - - for (const ipAdapter of ipAdapters) { - const { id, weight, model, clipVisionModel, method, beginEndStepPct, image } = ipAdapter; - assert(image, 'IP Adapter image is required'); - assert(model, 'IP Adapter model is required'); - - const ipAdapterNode: IPAdapterInvocation = { - id: `ip_adapter_${id}`, - type: 'ip_adapter', - is_intermediate: true, - weight, - method, - ip_adapter_model: model, - clip_vision_model: clipVisionModel, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - image: { - image_name: image.imageName, - }, - }; - - graph.nodes[ipAdapterNode.id] = ipAdapterNode; - - ipAdapterMetdata.push(buildIPAdapterMetadata(ipAdapter)); - - graph.edges.push({ - source: { node_id: ipAdapterNode.id, field: 'ip_adapter' }, - destination: { - node_id: IP_ADAPTER_COLLECT, - field: 'item', - }, - }); - } - - upsertMetadata(graph, { ipAdapters: ipAdapterMetdata }); -}; - -export const addControlLayersToGraph = async (state: RootState, graph: NonNullableGraph, denoiseNodeId: string) => { - const mainModel = state.generation.model; - assert(mainModel, 'Missing main model when building graph'); - const isSDXL = mainModel.base === 'sdxl'; - - // Add global control adapters - const globalControlNets = state.controlLayers.present.layers - // Must be a CA layer - .filter(isControlAdapterLayer) - // Must be enabled - .filter((l) => l.isEnabled) - // We want the CAs themselves - .map((l) => l.controlAdapter) - // Must be a ControlNet - .filter(isControlNetConfigV2) - .filter((ca) => { - const hasModel = Boolean(ca.model); - const modelMatchesBase = ca.model?.base === mainModel.base; - const hasControlImage = ca.image || (ca.processedImage && ca.processorConfig); - return hasModel && modelMatchesBase && hasControlImage; - }); - addGlobalControlNetsToGraph(globalControlNets, graph, denoiseNodeId); - - const globalT2IAdapters = state.controlLayers.present.layers - // Must be a CA layer - .filter(isControlAdapterLayer) - // Must be enabled - .filter((l) => l.isEnabled) - // We want the CAs themselves - .map((l) => l.controlAdapter) - // Must have a ControlNet CA - .filter(isT2IAdapterConfigV2) - .filter((ca) => { - const hasModel = Boolean(ca.model); - const modelMatchesBase = ca.model?.base === mainModel.base; - const hasControlImage = ca.image || (ca.processedImage && ca.processorConfig); - return hasModel && modelMatchesBase && hasControlImage; - }); - addGlobalT2IAdaptersToGraph(globalT2IAdapters, graph, denoiseNodeId); - - const globalIPAdapters = state.controlLayers.present.layers - // Must be an IP Adapter layer - .filter(isIPAdapterLayer) - // Must be enabled - .filter((l) => l.isEnabled) - // We want the IP Adapters themselves - .map((l) => l.ipAdapter) - .filter((ca) => { - const hasModel = Boolean(ca.model); - const modelMatchesBase = ca.model?.base === mainModel.base; - const hasControlImage = Boolean(ca.image); - return hasModel && modelMatchesBase && hasControlImage; - }); - addGlobalIPAdaptersToGraph(globalIPAdapters, graph, denoiseNodeId); - - const rgLayers = state.controlLayers.present.layers - // Only RG layers are get masks - .filter(isRegionalGuidanceLayer) - // Only visible layers are rendered on the canvas - .filter((l) => l.isEnabled) - // Only layers with prompts get added to the graph - .filter((l) => { - const hasTextPrompt = Boolean(l.positivePrompt || l.negativePrompt); - const hasIPAdapter = l.ipAdapters.length !== 0; - return hasTextPrompt || hasIPAdapter; - }); - - // TODO: We should probably just use conditioning collectors by default, and skip all this fanagling with re-routing - // the existing conditioning nodes. - - // With regional prompts we have multiple conditioning nodes which much be routed into collectors. Set those up - const posCondCollectNode: CollectInvocation = { - id: POSITIVE_CONDITIONING_COLLECT, - type: 'collect', - }; - graph.nodes[POSITIVE_CONDITIONING_COLLECT] = posCondCollectNode; - const negCondCollectNode: CollectInvocation = { - id: NEGATIVE_CONDITIONING_COLLECT, - type: 'collect', - }; - graph.nodes[NEGATIVE_CONDITIONING_COLLECT] = negCondCollectNode; - - // Re-route the denoise node's OG conditioning inputs to the collect nodes - const newEdges: Edge[] = []; - for (const edge of graph.edges) { - if (edge.destination.node_id === denoiseNodeId && edge.destination.field === 'positive_conditioning') { - newEdges.push({ - source: edge.source, - destination: { - node_id: POSITIVE_CONDITIONING_COLLECT, - field: 'item', - }, - }); - } else if (edge.destination.node_id === denoiseNodeId && edge.destination.field === 'negative_conditioning') { - newEdges.push({ - source: edge.source, - destination: { - node_id: NEGATIVE_CONDITIONING_COLLECT, - field: 'item', - }, - }); - } else { - newEdges.push(edge); - } - } - graph.edges = newEdges; - - // Connect collectors to the denoise nodes - must happen _after_ rerouting else you get cycles - graph.edges.push({ - source: { - node_id: POSITIVE_CONDITIONING_COLLECT, - field: 'collection', - }, - destination: { - node_id: denoiseNodeId, - field: 'positive_conditioning', - }, - }); - graph.edges.push({ - source: { - node_id: NEGATIVE_CONDITIONING_COLLECT, - field: 'collection', - }, - destination: { - node_id: denoiseNodeId, - field: 'negative_conditioning', - }, - }); - - const layerIds = rgLayers.map((l) => l.id); - const blobs = await getRegionalPromptLayerBlobs(layerIds); - assert(size(blobs) === size(layerIds), 'Mismatch between layer IDs and blobs'); - - for (const layer of rgLayers) { - const blob = blobs[layer.id]; - assert(blob, `Blob for layer ${layer.id} not found`); - // Upload the mask image, or get the cached image if it exists - const { image_name } = await getMaskImage(layer, blob); - - // The main mask-to-tensor node - const maskToTensorNode: S['AlphaMaskToTensorInvocation'] = { - id: `${PROMPT_REGION_MASK_TO_TENSOR_PREFIX}_${layer.id}`, - type: 'alpha_mask_to_tensor', - image: { - image_name, - }, - }; - graph.nodes[maskToTensorNode.id] = maskToTensorNode; - - if (layer.positivePrompt) { - // The main positive conditioning node - const regionalPositiveCondNode: S['SDXLCompelPromptInvocation'] | S['CompelInvocation'] = isSDXL - ? { - type: 'sdxl_compel_prompt', - id: `${PROMPT_REGION_POSITIVE_COND_PREFIX}_${layer.id}`, - prompt: layer.positivePrompt, - style: layer.positivePrompt, // TODO: Should we put the positive prompt in both fields? - } - : { - type: 'compel', - id: `${PROMPT_REGION_POSITIVE_COND_PREFIX}_${layer.id}`, - prompt: layer.positivePrompt, - }; - graph.nodes[regionalPositiveCondNode.id] = regionalPositiveCondNode; - - // Connect the mask to the conditioning - graph.edges.push({ - source: { node_id: maskToTensorNode.id, field: 'mask' }, - destination: { node_id: regionalPositiveCondNode.id, field: 'mask' }, - }); - - // Connect the conditioning to the collector - graph.edges.push({ - source: { node_id: regionalPositiveCondNode.id, field: 'conditioning' }, - destination: { node_id: posCondCollectNode.id, field: 'item' }, - }); - - // Copy the connections to the "global" positive conditioning node to the regional cond - for (const edge of graph.edges) { - if (edge.destination.node_id === POSITIVE_CONDITIONING && edge.destination.field !== 'prompt') { - graph.edges.push({ - source: edge.source, - destination: { node_id: regionalPositiveCondNode.id, field: edge.destination.field }, - }); - } - } - } - - if (layer.negativePrompt) { - // The main negative conditioning node - const regionalNegativeCondNode: S['SDXLCompelPromptInvocation'] | S['CompelInvocation'] = isSDXL - ? { - type: 'sdxl_compel_prompt', - id: `${PROMPT_REGION_NEGATIVE_COND_PREFIX}_${layer.id}`, - prompt: layer.negativePrompt, - style: layer.negativePrompt, - } - : { - type: 'compel', - id: `${PROMPT_REGION_NEGATIVE_COND_PREFIX}_${layer.id}`, - prompt: layer.negativePrompt, - }; - graph.nodes[regionalNegativeCondNode.id] = regionalNegativeCondNode; - - // Connect the mask to the conditioning - graph.edges.push({ - source: { node_id: maskToTensorNode.id, field: 'mask' }, - destination: { node_id: regionalNegativeCondNode.id, field: 'mask' }, - }); - - // Connect the conditioning to the collector - graph.edges.push({ - source: { node_id: regionalNegativeCondNode.id, field: 'conditioning' }, - destination: { node_id: negCondCollectNode.id, field: 'item' }, - }); - - // Copy the connections to the "global" negative conditioning node to the regional cond - for (const edge of graph.edges) { - if (edge.destination.node_id === NEGATIVE_CONDITIONING && edge.destination.field !== 'prompt') { - graph.edges.push({ - source: edge.source, - destination: { node_id: regionalNegativeCondNode.id, field: edge.destination.field }, - }); - } - } - } - - // If we are using the "invert" auto-negative setting, we need to add an additional negative conditioning node - if (layer.autoNegative === 'invert' && layer.positivePrompt) { - // We re-use the mask image, but invert it when converting to tensor - const invertTensorMaskNode: S['InvertTensorMaskInvocation'] = { - id: `${PROMPT_REGION_INVERT_TENSOR_MASK_PREFIX}_${layer.id}`, - type: 'invert_tensor_mask', - }; - graph.nodes[invertTensorMaskNode.id] = invertTensorMaskNode; - - // Connect the OG mask image to the inverted mask-to-tensor node - graph.edges.push({ - source: { - node_id: maskToTensorNode.id, - field: 'mask', - }, - destination: { - node_id: invertTensorMaskNode.id, - field: 'mask', - }, - }); - - // Create the conditioning node. It's going to be connected to the negative cond collector, but it uses the - // positive prompt - const regionalPositiveCondInvertedNode: S['SDXLCompelPromptInvocation'] | S['CompelInvocation'] = isSDXL - ? { - type: 'sdxl_compel_prompt', - id: `${PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX}_${layer.id}`, - prompt: layer.positivePrompt, - style: layer.positivePrompt, - } - : { - type: 'compel', - id: `${PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX}_${layer.id}`, - prompt: layer.positivePrompt, - }; - graph.nodes[regionalPositiveCondInvertedNode.id] = regionalPositiveCondInvertedNode; - // Connect the inverted mask to the conditioning - graph.edges.push({ - source: { node_id: invertTensorMaskNode.id, field: 'mask' }, - destination: { node_id: regionalPositiveCondInvertedNode.id, field: 'mask' }, - }); - // Connect the conditioning to the negative collector - graph.edges.push({ - source: { node_id: regionalPositiveCondInvertedNode.id, field: 'conditioning' }, - destination: { node_id: negCondCollectNode.id, field: 'item' }, - }); - // Copy the connections to the "global" positive conditioning node to our regional node - for (const edge of graph.edges) { - if (edge.destination.node_id === POSITIVE_CONDITIONING && edge.destination.field !== 'prompt') { - graph.edges.push({ - source: edge.source, - destination: { node_id: regionalPositiveCondInvertedNode.id, field: edge.destination.field }, - }); - } - } - } - - // TODO(psyche): For some reason, I have to explicitly annotate regionalIPAdapters here. Not sure why. - const regionalIPAdapters: IPAdapterConfigV2[] = layer.ipAdapters.filter((ipAdapter) => { - const hasModel = Boolean(ipAdapter.model); - const modelMatchesBase = ipAdapter.model?.base === mainModel.base; - const hasControlImage = Boolean(ipAdapter.image); - return hasModel && modelMatchesBase && hasControlImage; - }); - - for (const ipAdapter of regionalIPAdapters) { - addIPAdapterCollectorSafe(graph, denoiseNodeId); - const { id, weight, model, clipVisionModel, method, beginEndStepPct, image } = ipAdapter; - assert(model, 'IP Adapter model is required'); - assert(image, 'IP Adapter image is required'); - - const ipAdapterNode: IPAdapterInvocation = { - id: `ip_adapter_${id}`, - type: 'ip_adapter', - is_intermediate: true, - weight, - method, - ip_adapter_model: model, - clip_vision_model: clipVisionModel, - begin_step_percent: beginEndStepPct[0], - end_step_percent: beginEndStepPct[1], - image: { - image_name: image.imageName, - }, - }; - - graph.nodes[ipAdapterNode.id] = ipAdapterNode; - - // Connect the mask to the conditioning - graph.edges.push({ - source: { node_id: maskToTensorNode.id, field: 'mask' }, - destination: { node_id: ipAdapterNode.id, field: 'mask' }, - }); - - graph.edges.push({ - source: { node_id: ipAdapterNode.id, field: 'ip_adapter' }, - destination: { - node_id: IP_ADAPTER_COLLECT, - field: 'item', - }, - }); - } - } -}; - -const getMaskImage = async (layer: RegionalGuidanceLayer, blob: Blob): Promise => { - if (layer.uploadedMaskImage) { - const imageDTO = await getImageDTO(layer.uploadedMaskImage.imageName); - if (imageDTO) { - return imageDTO; - } - } - const { dispatch } = getStore(); - // No cached mask, or the cached image no longer exists - we need to upload the mask image - const file = new File([blob], `${layer.id}_mask.png`, { type: 'image/png' }); - const req = dispatch( - imagesApi.endpoints.uploadImage.initiate({ file, image_category: 'mask', is_intermediate: true }) - ); - req.reset(); - - const imageDTO = await req.unwrap(); - dispatch(rgLayerMaskImageUploaded({ layerId: layer.id, imageDTO })); - return imageDTO; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addHrfToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/addHrfToGraph.ts deleted file mode 100644 index d6709f7058..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addHrfToGraph.ts +++ /dev/null @@ -1,356 +0,0 @@ -import { logger } from 'app/logging/logger'; -import type { RootState } from 'app/store/store'; -import { roundToMultiple } from 'common/util/roundDownToMultiple'; -import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; -import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; -import type { - DenoiseLatentsInvocation, - Edge, - ESRGANInvocation, - LatentsToImageInvocation, - NoiseInvocation, - NonNullableGraph, -} from 'services/api/types'; - -import { - DENOISE_LATENTS, - DENOISE_LATENTS_HRF, - ESRGAN_HRF, - IMAGE_TO_LATENTS_HRF, - LATENTS_TO_IMAGE, - LATENTS_TO_IMAGE_HRF_HR, - LATENTS_TO_IMAGE_HRF_LR, - MAIN_MODEL_LOADER, - NOISE, - NOISE_HRF, - RESIZE_HRF, - SEAMLESS, - VAE_LOADER, -} from './constants'; -import { setMetadataReceivingNode, upsertMetadata } from './metadata'; - -// Copy certain connections from previous DENOISE_LATENTS to new DENOISE_LATENTS_HRF. -function copyConnectionsToDenoiseLatentsHrf(graph: NonNullableGraph): void { - const destinationFields = [ - 'control', - 'ip_adapter', - 'metadata', - 'unet', - 'positive_conditioning', - 'negative_conditioning', - ]; - const newEdges: Edge[] = []; - - // Loop through the existing edges connected to DENOISE_LATENTS - graph.edges.forEach((edge: Edge) => { - if (edge.destination.node_id === DENOISE_LATENTS && destinationFields.includes(edge.destination.field)) { - // Add a similar connection to DENOISE_LATENTS_HRF - newEdges.push({ - source: { - node_id: edge.source.node_id, - field: edge.source.field, - }, - destination: { - node_id: DENOISE_LATENTS_HRF, - field: edge.destination.field, - }, - }); - } - }); - graph.edges = graph.edges.concat(newEdges); -} - -/** - * Calculates the new resolution for high-resolution features (HRF) based on base model type. - * Adjusts the width and height to maintain the aspect ratio and constrains them by the model's dimension limits, - * rounding down to the nearest multiple of 8. - * - * @param {number} optimalDimension The optimal dimension for the base model. - * @param {number} width The current width to be adjusted for HRF. - * @param {number} height The current height to be adjusted for HRF. - * @return {{newWidth: number, newHeight: number}} The new width and height, adjusted and rounded as needed. - */ -function calculateHrfRes( - optimalDimension: number, - width: number, - height: number -): { newWidth: number; newHeight: number } { - const aspect = width / height; - - const minDimension = Math.floor(optimalDimension * 0.5); - const modelArea = optimalDimension * optimalDimension; // Assuming square images for model_area - - let initWidth; - let initHeight; - - if (aspect > 1.0) { - initHeight = Math.max(minDimension, Math.sqrt(modelArea / aspect)); - initWidth = initHeight * aspect; - } else { - initWidth = Math.max(minDimension, Math.sqrt(modelArea * aspect)); - initHeight = initWidth / aspect; - } - // Cap initial height and width to final height and width. - initWidth = Math.min(width, initWidth); - initHeight = Math.min(height, initHeight); - - const newWidth = roundToMultiple(Math.floor(initWidth), 8); - const newHeight = roundToMultiple(Math.floor(initHeight), 8); - - return { newWidth, newHeight }; -} - -// Adds the high-res fix feature to the given graph. -export const addHrfToGraph = (state: RootState, graph: NonNullableGraph): void => { - // Double check hrf is enabled. - if (!state.hrf.hrfEnabled || state.config.disabledSDFeatures.includes('hrf')) { - return; - } - const log = logger('generation'); - - const { vae, seamlessXAxis, seamlessYAxis } = state.generation; - const { hrfStrength, hrfEnabled, hrfMethod } = state.hrf; - const { width, height } = state.controlLayers.present.size; - const isAutoVae = !vae; - const isSeamlessEnabled = seamlessXAxis || seamlessYAxis; - const optimalDimension = selectOptimalDimension(state); - const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(optimalDimension, width, height); - - // Pre-existing (original) graph nodes. - const originalDenoiseLatentsNode = graph.nodes[DENOISE_LATENTS] as DenoiseLatentsInvocation | undefined; - const originalNoiseNode = graph.nodes[NOISE] as NoiseInvocation | undefined; - const originalLatentsToImageNode = graph.nodes[LATENTS_TO_IMAGE] as LatentsToImageInvocation | undefined; - if (!originalDenoiseLatentsNode) { - log.error('originalDenoiseLatentsNode is undefined'); - return; - } - if (!originalNoiseNode) { - log.error('originalNoiseNode is undefined'); - return; - } - if (!originalLatentsToImageNode) { - log.error('originalLatentsToImageNode is undefined'); - return; - } - - // Change height and width of original noise node to initial resolution. - if (originalNoiseNode) { - originalNoiseNode.width = hrfWidth; - originalNoiseNode.height = hrfHeight; - } - - // Define new nodes and their connections, roughly in order of operations. - graph.nodes[LATENTS_TO_IMAGE_HRF_LR] = { - type: 'l2i', - id: LATENTS_TO_IMAGE_HRF_LR, - fp32: originalLatentsToImageNode?.fp32, - is_intermediate: true, - }; - graph.edges.push( - { - source: { - node_id: DENOISE_LATENTS, - field: 'latents', - }, - destination: { - node_id: LATENTS_TO_IMAGE_HRF_LR, - field: 'latents', - }, - }, - { - source: { - node_id: isSeamlessEnabled ? SEAMLESS : isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER, - field: 'vae', - }, - destination: { - node_id: LATENTS_TO_IMAGE_HRF_LR, - field: 'vae', - }, - } - ); - - graph.nodes[RESIZE_HRF] = { - id: RESIZE_HRF, - type: 'img_resize', - is_intermediate: true, - width: width, - height: height, - }; - if (hrfMethod === 'ESRGAN') { - let model_name: ESRGANInvocation['model_name'] = 'RealESRGAN_x2plus.pth'; - if ((width * height) / (hrfWidth * hrfHeight) > 2) { - model_name = 'RealESRGAN_x4plus.pth'; - } - graph.nodes[ESRGAN_HRF] = { - id: ESRGAN_HRF, - type: 'esrgan', - model_name, - is_intermediate: true, - }; - graph.edges.push( - { - source: { - node_id: LATENTS_TO_IMAGE_HRF_LR, - field: 'image', - }, - destination: { - node_id: ESRGAN_HRF, - field: 'image', - }, - }, - { - source: { - node_id: ESRGAN_HRF, - field: 'image', - }, - destination: { - node_id: RESIZE_HRF, - field: 'image', - }, - } - ); - } else { - graph.edges.push({ - source: { - node_id: LATENTS_TO_IMAGE_HRF_LR, - field: 'image', - }, - destination: { - node_id: RESIZE_HRF, - field: 'image', - }, - }); - } - - graph.nodes[NOISE_HRF] = { - type: 'noise', - id: NOISE_HRF, - seed: originalNoiseNode?.seed, - use_cpu: originalNoiseNode?.use_cpu, - is_intermediate: true, - }; - graph.edges.push( - { - source: { - node_id: RESIZE_HRF, - field: 'height', - }, - destination: { - node_id: NOISE_HRF, - field: 'height', - }, - }, - { - source: { - node_id: RESIZE_HRF, - field: 'width', - }, - destination: { - node_id: NOISE_HRF, - field: 'width', - }, - } - ); - - graph.nodes[IMAGE_TO_LATENTS_HRF] = { - type: 'i2l', - id: IMAGE_TO_LATENTS_HRF, - is_intermediate: true, - }; - graph.edges.push( - { - source: { - node_id: isSeamlessEnabled ? SEAMLESS : isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER, - field: 'vae', - }, - destination: { - node_id: IMAGE_TO_LATENTS_HRF, - field: 'vae', - }, - }, - { - source: { - node_id: RESIZE_HRF, - field: 'image', - }, - destination: { - node_id: IMAGE_TO_LATENTS_HRF, - field: 'image', - }, - } - ); - - graph.nodes[DENOISE_LATENTS_HRF] = { - type: 'denoise_latents', - id: DENOISE_LATENTS_HRF, - is_intermediate: true, - cfg_scale: originalDenoiseLatentsNode?.cfg_scale, - scheduler: originalDenoiseLatentsNode?.scheduler, - steps: originalDenoiseLatentsNode?.steps, - denoising_start: 1 - hrfStrength, - denoising_end: 1, - }; - graph.edges.push( - { - source: { - node_id: IMAGE_TO_LATENTS_HRF, - field: 'latents', - }, - destination: { - node_id: DENOISE_LATENTS_HRF, - field: 'latents', - }, - }, - { - source: { - node_id: NOISE_HRF, - field: 'noise', - }, - destination: { - node_id: DENOISE_LATENTS_HRF, - field: 'noise', - }, - } - ); - copyConnectionsToDenoiseLatentsHrf(graph); - - // The original l2i node is unnecessary now, remove it - graph.edges = graph.edges.filter((edge) => edge.destination.node_id !== LATENTS_TO_IMAGE); - delete graph.nodes[LATENTS_TO_IMAGE]; - - graph.nodes[LATENTS_TO_IMAGE_HRF_HR] = { - type: 'l2i', - id: LATENTS_TO_IMAGE_HRF_HR, - fp32: originalLatentsToImageNode?.fp32, - is_intermediate: getIsIntermediate(state), - board: getBoardField(state), - }; - graph.edges.push( - { - source: { - node_id: isSeamlessEnabled ? SEAMLESS : isAutoVae ? MAIN_MODEL_LOADER : VAE_LOADER, - field: 'vae', - }, - destination: { - node_id: LATENTS_TO_IMAGE_HRF_HR, - field: 'vae', - }, - }, - { - source: { - node_id: DENOISE_LATENTS_HRF, - field: 'latents', - }, - destination: { - node_id: LATENTS_TO_IMAGE_HRF_HR, - field: 'latents', - }, - } - ); - upsertMetadata(graph, { - hrf_strength: hrfStrength, - hrf_enabled: hrfEnabled, - hrf_method: hrfMethod, - }); - setMetadataReceivingNode(graph, LATENTS_TO_IMAGE_HRF_HR); -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addInitialImageToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/addInitialImageToLinearGraph.ts deleted file mode 100644 index eae45acc5b..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addInitialImageToLinearGraph.ts +++ /dev/null @@ -1,130 +0,0 @@ -import type { RootState } from 'app/store/store'; -import { isInitialImageLayer } from 'features/controlLayers/store/controlLayersSlice'; -import { upsertMetadata } from 'features/nodes/util/graph/metadata'; -import type { ImageResizeInvocation, ImageToLatentsInvocation, NonNullableGraph } from 'services/api/types'; -import { assert } from 'tsafe'; - -import { IMAGE_TO_LATENTS, NOISE, RESIZE } from './constants'; - -/** - * Returns true if an initial image was added, false if not. - */ -export const addInitialImageToLinearGraph = ( - state: RootState, - graph: NonNullableGraph, - denoiseNodeId: string -): boolean => { - // Remove Existing UNet Connections - const { img2imgStrength, vaePrecision, model } = state.generation; - const { refinerModel, refinerStart } = state.sdxl; - const { width, height } = state.controlLayers.present.size; - const initialImageLayer = state.controlLayers.present.layers.find(isInitialImageLayer); - const initialImage = initialImageLayer?.isEnabled ? initialImageLayer?.image : null; - - if (!initialImage) { - return false; - } - - const isSDXL = model?.base === 'sdxl'; - const useRefinerStartEnd = isSDXL && Boolean(refinerModel); - - const denoiseNode = graph.nodes[denoiseNodeId]; - assert(denoiseNode?.type === 'denoise_latents', `Missing denoise node or incorrect type: ${denoiseNode?.type}`); - - denoiseNode.denoising_start = useRefinerStartEnd ? Math.min(refinerStart, 1 - img2imgStrength) : 1 - img2imgStrength; - denoiseNode.denoising_end = useRefinerStartEnd ? refinerStart : 1; - - // We conditionally hook the image in depending on if a resize is needed - const i2lNode: ImageToLatentsInvocation = { - type: 'i2l', - id: IMAGE_TO_LATENTS, - is_intermediate: true, - use_cache: true, - fp32: vaePrecision === 'fp32', - }; - - graph.nodes[i2lNode.id] = i2lNode; - graph.edges.push({ - source: { - node_id: IMAGE_TO_LATENTS, - field: 'latents', - }, - destination: { - node_id: denoiseNode.id, - field: 'latents', - }, - }); - - if (initialImage.width !== width || initialImage.height !== height) { - // The init image needs to be resized to the specified width and height before being passed to `IMAGE_TO_LATENTS` - - // Create a resize node, explicitly setting its image - const resizeNode: ImageResizeInvocation = { - id: RESIZE, - type: 'img_resize', - image: { - image_name: initialImage.imageName, - }, - is_intermediate: true, - width, - height, - }; - - graph.nodes[RESIZE] = resizeNode; - - // The `RESIZE` node then passes its image to `IMAGE_TO_LATENTS` - graph.edges.push({ - source: { node_id: RESIZE, field: 'image' }, - destination: { - node_id: IMAGE_TO_LATENTS, - field: 'image', - }, - }); - - // The `RESIZE` node also passes its width and height to `NOISE` - graph.edges.push({ - source: { node_id: RESIZE, field: 'width' }, - destination: { - node_id: NOISE, - field: 'width', - }, - }); - - graph.edges.push({ - source: { node_id: RESIZE, field: 'height' }, - destination: { - node_id: NOISE, - field: 'height', - }, - }); - } else { - // We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly - i2lNode.image = { - image_name: initialImage.imageName, - }; - - // Pass the image's dimensions to the `NOISE` node - graph.edges.push({ - source: { node_id: IMAGE_TO_LATENTS, field: 'width' }, - destination: { - node_id: NOISE, - field: 'width', - }, - }); - graph.edges.push({ - source: { node_id: IMAGE_TO_LATENTS, field: 'height' }, - destination: { - node_id: NOISE, - field: 'height', - }, - }); - } - - upsertMetadata(graph, { - generation_mode: isSDXL ? 'sdxl_img2img' : 'img2img', - strength: img2imgStrength, - init_image: initialImage.imageName, - }); - - return true; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts index 6c90dafd25..60343c5e89 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildAdHocUpscaleGraph.ts @@ -1,9 +1,9 @@ import type { RootState } from 'app/store/store'; import { getBoardField } from 'features/nodes/util/graph/graphBuilderUtils'; -import type { ESRGANInvocation, Graph, NonNullableGraph } from 'services/api/types'; +import type { Graph, Invocation, NonNullableGraph } from 'services/api/types'; +import { addCoreMetadataNode, upsertMetadata } from './canvas/metadata'; import { ESRGAN } from './constants'; -import { addCoreMetadataNode, upsertMetadata } from './metadata'; type Arg = { image_name: string; @@ -13,7 +13,7 @@ type Arg = { export const buildAdHocUpscaleGraph = ({ image_name, state }: Arg): Graph => { const { esrganModelName } = state.postprocessing; - const realesrganNode: ESRGANInvocation = { + const realesrganNode: Invocation<'esrgan'> = { id: ESRGAN, type: 'esrgan', image: { image_name }, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildGenerationTabGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildGenerationTabGraph.ts deleted file mode 100644 index 41f9f4f748..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildGenerationTabGraph.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { logger } from 'app/logging/logger'; -import type { RootState } from 'app/store/store'; -import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import { addControlLayersToGraph } from 'features/nodes/util/graph/addControlLayersToGraph'; -import { addInitialImageToLinearGraph } from 'features/nodes/util/graph/addInitialImageToLinearGraph'; -import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; -import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; - -import { addHrfToGraph } from './addHrfToGraph'; -import { addLoRAsToGraph } from './addLoRAsToGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; -import { - CLIP_SKIP, - CONTROL_LAYERS_GRAPH, - DENOISE_LATENTS, - LATENTS_TO_IMAGE, - MAIN_MODEL_LOADER, - NEGATIVE_CONDITIONING, - NOISE, - POSITIVE_CONDITIONING, - SEAMLESS, -} from './constants'; -import { addCoreMetadataNode, getModelMetadataField } from './metadata'; - -export const buildGenerationTabGraph = async (state: RootState): Promise => { - const log = logger('nodes'); - const { - model, - cfgScale: cfg_scale, - cfgRescaleMultiplier: cfg_rescale_multiplier, - scheduler, - steps, - clipSkip, - shouldUseCpuNoise, - vaePrecision, - seamlessXAxis, - seamlessYAxis, - seed, - } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; - const { width, height } = state.controlLayers.present.size; - - const use_cpu = shouldUseCpuNoise; - - if (!model) { - log.error('No model found in state'); - throw new Error('No model found in state'); - } - - const fp32 = vaePrecision === 'fp32'; - const is_intermediate = true; - - let modelLoaderNodeId = MAIN_MODEL_LOADER; - - /** - * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the - * full graph here as a template. Then use the parameters from app state and set friendlier node - * ids. - * - * The only thing we need extra logic for is handling randomized seed, control net, and for img2img, - * the `fit` param. These are added to the graph at the end. - */ - - // copy-pasted graph from node editor, filled in with state values & friendly node ids - - const graph: NonNullableGraph = { - id: CONTROL_LAYERS_GRAPH, - nodes: { - [modelLoaderNodeId]: { - type: 'main_model_loader', - id: modelLoaderNodeId, - is_intermediate, - model, - }, - [CLIP_SKIP]: { - type: 'clip_skip', - id: CLIP_SKIP, - skipped_layers: clipSkip, - is_intermediate, - }, - [POSITIVE_CONDITIONING]: { - type: 'compel', - id: POSITIVE_CONDITIONING, - prompt: positivePrompt, - is_intermediate, - }, - [NEGATIVE_CONDITIONING]: { - type: 'compel', - id: NEGATIVE_CONDITIONING, - prompt: negativePrompt, - is_intermediate, - }, - [NOISE]: { - type: 'noise', - id: NOISE, - seed, - width, - height, - use_cpu, - is_intermediate, - }, - [DENOISE_LATENTS]: { - type: 'denoise_latents', - id: DENOISE_LATENTS, - is_intermediate, - cfg_scale, - cfg_rescale_multiplier, - scheduler, - steps, - denoising_start: 0, - denoising_end: 1, - }, - [LATENTS_TO_IMAGE]: { - type: 'l2i', - id: LATENTS_TO_IMAGE, - fp32, - is_intermediate: getIsIntermediate(state), - board: getBoardField(state), - use_cache: false, - }, - }, - edges: [ - // Connect Model Loader to UNet and CLIP Skip - { - source: { - node_id: modelLoaderNodeId, - field: 'unet', - }, - destination: { - node_id: DENOISE_LATENTS, - field: 'unet', - }, - }, - { - source: { - node_id: modelLoaderNodeId, - field: 'clip', - }, - destination: { - node_id: CLIP_SKIP, - field: 'clip', - }, - }, - // Connect CLIP Skip to Conditioning - { - source: { - node_id: CLIP_SKIP, - field: 'clip', - }, - destination: { - node_id: POSITIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: CLIP_SKIP, - field: 'clip', - }, - destination: { - node_id: NEGATIVE_CONDITIONING, - field: 'clip', - }, - }, - // Connect everything to Denoise Latents - { - source: { - node_id: POSITIVE_CONDITIONING, - field: 'conditioning', - }, - destination: { - node_id: DENOISE_LATENTS, - field: 'positive_conditioning', - }, - }, - { - source: { - node_id: NEGATIVE_CONDITIONING, - field: 'conditioning', - }, - destination: { - node_id: DENOISE_LATENTS, - field: 'negative_conditioning', - }, - }, - { - source: { - node_id: NOISE, - field: 'noise', - }, - destination: { - node_id: DENOISE_LATENTS, - field: 'noise', - }, - }, - // Decode Denoised Latents To Image - { - source: { - node_id: DENOISE_LATENTS, - field: 'latents', - }, - destination: { - node_id: LATENTS_TO_IMAGE, - field: 'latents', - }, - }, - ], - }; - - const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig); - - addCoreMetadataNode( - graph, - { - generation_mode: 'txt2img', - cfg_scale, - cfg_rescale_multiplier, - height, - width, - positive_prompt: positivePrompt, - negative_prompt: negativePrompt, - model: getModelMetadataField(modelConfig), - seed, - steps, - rand_device: use_cpu ? 'cpu' : 'cuda', - scheduler, - clip_skip: clipSkip, - }, - LATENTS_TO_IMAGE - ); - - const didAddInitialImage = addInitialImageToLinearGraph(state, graph, DENOISE_LATENTS); - - // Add Seamless To Graph - if (seamlessXAxis || seamlessYAxis) { - addSeamlessToLinearGraph(state, graph, modelLoaderNodeId); - modelLoaderNodeId = SEAMLESS; - } - - // optionally add custom VAE - await addVAEToGraph(state, graph, modelLoaderNodeId); - - // add LoRA support - await addLoRAsToGraph(state, graph, DENOISE_LATENTS, modelLoaderNodeId); - - await addControlLayersToGraph(state, graph, DENOISE_LATENTS); - - // High resolution fix. - if (state.hrf.hrfEnabled && !didAddInitialImage) { - addHrfToGraph(state, graph); - } - - // NSFW & watermark - must be last thing added to graph - if (state.system.shouldUseNSFWChecker) { - // must add before watermarker! - addNSFWCheckerToGraph(state, graph); - } - - if (state.system.shouldUseWatermarker) { - // must add after nsfw checker! - addWatermarkerToGraph(state, graph); - } - - return graph; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildGenerationTabSDXLGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildGenerationTabSDXLGraph.ts deleted file mode 100644 index 900e993602..0000000000 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildGenerationTabSDXLGraph.ts +++ /dev/null @@ -1,281 +0,0 @@ -import { logger } from 'app/logging/logger'; -import type { RootState } from 'app/store/store'; -import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import { addControlLayersToGraph } from 'features/nodes/util/graph/addControlLayersToGraph'; -import { addInitialImageToLinearGraph } from 'features/nodes/util/graph/addInitialImageToLinearGraph'; -import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; - -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; -import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; -import { - LATENTS_TO_IMAGE, - NEGATIVE_CONDITIONING, - NOISE, - POSITIVE_CONDITIONING, - SDXL_CONTROL_LAYERS_GRAPH, - SDXL_DENOISE_LATENTS, - SDXL_MODEL_LOADER, - SDXL_REFINER_SEAMLESS, - SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from './graphBuilderUtils'; -import { addCoreMetadataNode, getModelMetadataField } from './metadata'; - -export const buildGenerationTabSDXLGraph = async (state: RootState): Promise => { - const log = logger('nodes'); - const { - model, - cfgScale: cfg_scale, - cfgRescaleMultiplier: cfg_rescale_multiplier, - scheduler, - seed, - steps, - shouldUseCpuNoise, - vaePrecision, - seamlessXAxis, - seamlessYAxis, - } = state.generation; - const { positivePrompt, negativePrompt } = state.controlLayers.present; - const { width, height } = state.controlLayers.present.size; - - const { refinerModel, refinerStart } = state.sdxl; - - const use_cpu = shouldUseCpuNoise; - - if (!model) { - log.error('No model found in state'); - throw new Error('No model found in state'); - } - - const fp32 = vaePrecision === 'fp32'; - const is_intermediate = true; - - // Construct Style Prompt - const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); - - // Model Loader ID - let modelLoaderNodeId = SDXL_MODEL_LOADER; - - /** - * The easiest way to build linear graphs is to do it in the node editor, then copy and paste the - * full graph here as a template. Then use the parameters from app state and set friendlier node - * ids. - * - * The only thing we need extra logic for is handling randomized seed, control net, and for img2img, - * the `fit` param. These are added to the graph at the end. - */ - - // copy-pasted graph from node editor, filled in with state values & friendly node ids - const graph: NonNullableGraph = { - id: SDXL_CONTROL_LAYERS_GRAPH, - nodes: { - [modelLoaderNodeId]: { - type: 'sdxl_model_loader', - id: modelLoaderNodeId, - model, - is_intermediate, - }, - [POSITIVE_CONDITIONING]: { - type: 'sdxl_compel_prompt', - id: POSITIVE_CONDITIONING, - prompt: positivePrompt, - style: positiveStylePrompt, - is_intermediate, - }, - [NEGATIVE_CONDITIONING]: { - type: 'sdxl_compel_prompt', - id: NEGATIVE_CONDITIONING, - prompt: negativePrompt, - style: negativeStylePrompt, - is_intermediate, - }, - [NOISE]: { - type: 'noise', - id: NOISE, - seed, - width, - height, - use_cpu, - is_intermediate, - }, - [SDXL_DENOISE_LATENTS]: { - type: 'denoise_latents', - id: SDXL_DENOISE_LATENTS, - cfg_scale, - cfg_rescale_multiplier, - scheduler, - steps, - denoising_start: 0, - denoising_end: refinerModel ? refinerStart : 1, - is_intermediate, - }, - [LATENTS_TO_IMAGE]: { - type: 'l2i', - id: LATENTS_TO_IMAGE, - fp32, - is_intermediate: getIsIntermediate(state), - board: getBoardField(state), - use_cache: false, - }, - }, - edges: [ - // Connect Model Loader to UNet, VAE & CLIP - { - source: { - node_id: modelLoaderNodeId, - field: 'unet', - }, - destination: { - node_id: SDXL_DENOISE_LATENTS, - field: 'unet', - }, - }, - { - source: { - node_id: modelLoaderNodeId, - field: 'clip', - }, - destination: { - node_id: POSITIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: modelLoaderNodeId, - field: 'clip2', - }, - destination: { - node_id: POSITIVE_CONDITIONING, - field: 'clip2', - }, - }, - { - source: { - node_id: modelLoaderNodeId, - field: 'clip', - }, - destination: { - node_id: NEGATIVE_CONDITIONING, - field: 'clip', - }, - }, - { - source: { - node_id: modelLoaderNodeId, - field: 'clip2', - }, - destination: { - node_id: NEGATIVE_CONDITIONING, - field: 'clip2', - }, - }, - // Connect everything to Denoise Latents - { - source: { - node_id: POSITIVE_CONDITIONING, - field: 'conditioning', - }, - destination: { - node_id: SDXL_DENOISE_LATENTS, - field: 'positive_conditioning', - }, - }, - { - source: { - node_id: NEGATIVE_CONDITIONING, - field: 'conditioning', - }, - destination: { - node_id: SDXL_DENOISE_LATENTS, - field: 'negative_conditioning', - }, - }, - { - source: { - node_id: NOISE, - field: 'noise', - }, - destination: { - node_id: SDXL_DENOISE_LATENTS, - field: 'noise', - }, - }, - // Decode Denoised Latents To Image - { - source: { - node_id: SDXL_DENOISE_LATENTS, - field: 'latents', - }, - destination: { - node_id: LATENTS_TO_IMAGE, - field: 'latents', - }, - }, - ], - }; - - const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig); - - addCoreMetadataNode( - graph, - { - generation_mode: 'txt2img', - cfg_scale, - cfg_rescale_multiplier, - height, - width, - positive_prompt: positivePrompt, - negative_prompt: negativePrompt, - model: getModelMetadataField(modelConfig), - seed, - steps, - rand_device: use_cpu ? 'cpu' : 'cuda', - scheduler, - positive_style_prompt: positiveStylePrompt, - negative_style_prompt: negativeStylePrompt, - }, - LATENTS_TO_IMAGE - ); - - addInitialImageToLinearGraph(state, graph, SDXL_DENOISE_LATENTS); - - // Add Seamless To Graph - if (seamlessXAxis || seamlessYAxis) { - addSeamlessToLinearGraph(state, graph, modelLoaderNodeId); - modelLoaderNodeId = SEAMLESS; - } - - // Add Refiner if enabled - if (refinerModel) { - await addSDXLRefinerToGraph(state, graph, SDXL_DENOISE_LATENTS); - if (seamlessXAxis || seamlessYAxis) { - modelLoaderNodeId = SDXL_REFINER_SEAMLESS; - } - } - - // optionally add custom VAE - await addVAEToGraph(state, graph, modelLoaderNodeId); - - // add LoRA support - await addSDXLLoRAsToGraph(state, graph, SDXL_DENOISE_LATENTS, modelLoaderNodeId); - - await addControlLayersToGraph(state, graph, SDXL_DENOISE_LATENTS); - - // NSFW & watermark - must be last thing added to graph - if (state.system.shouldUseNSFWChecker) { - // must add before watermarker! - addNSFWCheckerToGraph(state, graph); - } - - if (state.system.shouldUseWatermarker) { - // must add after nsfw checker! - addWatermarkerToGraph(state, graph); - } - - return graph; -}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts index 7bda644dfe..28eb083844 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildLinearBatchConfig.ts @@ -5,8 +5,8 @@ import { range } from 'lodash-es'; import type { components } from 'services/api/schema'; import type { Batch, BatchConfig, NonNullableGraph } from 'services/api/types'; +import { getHasMetadata, removeMetadata } from './canvas/metadata'; import { CANVAS_COHERENCE_NOISE, METADATA, NOISE, POSITIVE_CONDITIONING } from './constants'; -import { getHasMetadata, removeMetadata } from './metadata'; export const prepareLinearUIBatch = (state: RootState, graph: NonNullableGraph, prepend: boolean): BatchConfig => { const { iterations, model, shouldRandomizeSeed, seed } = state.generation; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addControlNetToLinearGraph.ts similarity index 91% rename from invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addControlNetToLinearGraph.ts index 531c88335b..2feba262c2 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addControlNetToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addControlNetToLinearGraph.ts @@ -2,25 +2,18 @@ import type { RootState } from 'app/store/store'; import { selectValidControlNets } from 'features/controlAdapters/store/controlAdaptersSlice'; import type { ControlAdapterProcessorType, ControlNetConfig } from 'features/controlAdapters/store/types'; import type { ImageField } from 'features/nodes/types/common'; +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; +import { CONTROL_NET_COLLECT } from 'features/nodes/util/graph/constants'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; -import type { - CollectInvocation, - ControlNetInvocation, - CoreMetadataInvocation, - NonNullableGraph, - S, -} from 'services/api/types'; +import type { Invocation, NonNullableGraph, S } from 'services/api/types'; import { assert } from 'tsafe'; -import { CONTROL_NET_COLLECT } from './constants'; -import { upsertMetadata } from './metadata'; - export const addControlNetToLinearGraph = async ( state: RootState, graph: NonNullableGraph, baseNodeId: string ): Promise => { - const controlNetMetadata: CoreMetadataInvocation['controlnets'] = []; + const controlNetMetadata: S['CoreMetadataInvocation']['controlnets'] = []; const controlNets = selectValidControlNets(state.controlAdapters).filter( ({ model, processedControlImage, processorType, controlImage, isEnabled }) => { const hasModel = Boolean(model); @@ -37,7 +30,7 @@ export const addControlNetToLinearGraph = async ( if (controlNets.length) { // Even though denoise_latents' control input is collection or scalar, keep it simple and always use a collect - const controlNetIterateNode: CollectInvocation = { + const controlNetIterateNode: Invocation<'collect'> = { id: CONTROL_NET_COLLECT, type: 'collect', is_intermediate: true, @@ -68,7 +61,7 @@ export const addControlNetToLinearGraph = async ( weight, } = controlNet; - const controlNetNode: ControlNetInvocation = { + const controlNetNode: Invocation<'controlnet'> = { id: `control_net_${id}`, type: 'controlnet', is_intermediate: true, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addIPAdapterToLinearGraph.ts similarity index 88% rename from invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addIPAdapterToLinearGraph.ts index 2cf93100eb..e9d9bd4663 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addIPAdapterToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addIPAdapterToLinearGraph.ts @@ -2,19 +2,12 @@ import type { RootState } from 'app/store/store'; import { selectValidIPAdapters } from 'features/controlAdapters/store/controlAdaptersSlice'; import type { IPAdapterConfig } from 'features/controlAdapters/store/types'; import type { ImageField } from 'features/nodes/types/common'; +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; +import { IP_ADAPTER_COLLECT } from 'features/nodes/util/graph/constants'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; -import type { - CollectInvocation, - CoreMetadataInvocation, - IPAdapterInvocation, - NonNullableGraph, - S, -} from 'services/api/types'; +import type { Invocation, NonNullableGraph, S } from 'services/api/types'; import { assert } from 'tsafe'; -import { IP_ADAPTER_COLLECT } from './constants'; -import { upsertMetadata } from './metadata'; - export const addIPAdapterToLinearGraph = async ( state: RootState, graph: NonNullableGraph, @@ -33,7 +26,7 @@ export const addIPAdapterToLinearGraph = async ( if (ipAdapters.length) { // Even though denoise_latents' ip adapter input is collection or scalar, keep it simple and always use a collect - const ipAdapterCollectNode: CollectInvocation = { + const ipAdapterCollectNode: Invocation<'collect'> = { id: IP_ADAPTER_COLLECT, type: 'collect', is_intermediate: true, @@ -47,7 +40,7 @@ export const addIPAdapterToLinearGraph = async ( }, }); - const ipAdapterMetdata: CoreMetadataInvocation['ipAdapters'] = []; + const ipAdapterMetdata: S['CoreMetadataInvocation']['ipAdapters'] = []; for (const ipAdapter of ipAdapters) { if (!ipAdapter.model) { @@ -57,7 +50,7 @@ export const addIPAdapterToLinearGraph = async ( assert(controlImage, 'IP Adapter image is required'); - const ipAdapterNode: IPAdapterInvocation = { + const ipAdapterNode: Invocation<'ip_adapter'> = { id: `ip_adapter_${id}`, type: 'ip_adapter', is_intermediate: true, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addLoRAsToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addLoRAsToGraph.ts similarity index 90% rename from invokeai/frontend/web/src/features/nodes/util/graph/addLoRAsToGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addLoRAsToGraph.ts index 28981a1a8a..6c4ac9fc69 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addLoRAsToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addLoRAsToGraph.ts @@ -1,10 +1,15 @@ import type { RootState } from 'app/store/store'; import { zModelIdentifierField } from 'features/nodes/types/common'; +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; +import { + CLIP_SKIP, + LORA_LOADER, + MAIN_MODEL_LOADER, + NEGATIVE_CONDITIONING, + POSITIVE_CONDITIONING, +} from 'features/nodes/util/graph/constants'; import { filter, size } from 'lodash-es'; -import type { CoreMetadataInvocation, LoRALoaderInvocation, NonNullableGraph } from 'services/api/types'; - -import { CLIP_SKIP, LORA_LOADER, MAIN_MODEL_LOADER, NEGATIVE_CONDITIONING, POSITIVE_CONDITIONING } from './constants'; -import { upsertMetadata } from './metadata'; +import type { Invocation, NonNullableGraph, S } from 'services/api/types'; export const addLoRAsToGraph = async ( state: RootState, @@ -38,7 +43,7 @@ export const addLoRAsToGraph = async ( // we need to remember the last lora so we can chain from it let lastLoraNodeId = ''; let currentLoraIndex = 0; - const loraMetadata: CoreMetadataInvocation['loras'] = []; + const loraMetadata: S['CoreMetadataInvocation']['loras'] = []; enabledLoRAs.forEach(async (lora) => { const { weight } = lora; @@ -46,7 +51,7 @@ export const addLoRAsToGraph = async ( const currentLoraNodeId = `${LORA_LOADER}_${key}`; const parsedModel = zModelIdentifierField.parse(lora.model); - const loraLoaderNode: LoRALoaderInvocation = { + const loraLoaderNode: Invocation<'lora_loader'> = { type: 'lora_loader', id: currentLoraNodeId, is_intermediate: true, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addNSFWCheckerToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addNSFWCheckerToGraph.ts similarity index 58% rename from invokeai/frontend/web/src/features/nodes/util/graph/addNSFWCheckerToGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addNSFWCheckerToGraph.ts index 35fc324689..56bc3cc2ad 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addNSFWCheckerToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addNSFWCheckerToGraph.ts @@ -1,15 +1,14 @@ import type { RootState } from 'app/store/store'; -import type { ImageNSFWBlurInvocation, LatentsToImageInvocation, NonNullableGraph } from 'services/api/types'; - -import { LATENTS_TO_IMAGE, NSFW_CHECKER } from './constants'; -import { getBoardField, getIsIntermediate } from './graphBuilderUtils'; +import { LATENTS_TO_IMAGE, NSFW_CHECKER } from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { Invocation, NonNullableGraph } from 'services/api/types'; export const addNSFWCheckerToGraph = ( state: RootState, graph: NonNullableGraph, nodeIdToAddTo = LATENTS_TO_IMAGE ): void => { - const nodeToAddTo = graph.nodes[nodeIdToAddTo] as LatentsToImageInvocation | undefined; + const nodeToAddTo = graph.nodes[nodeIdToAddTo] as Invocation<'l2i'> | undefined; if (!nodeToAddTo) { // something has gone terribly awry @@ -19,14 +18,14 @@ export const addNSFWCheckerToGraph = ( nodeToAddTo.is_intermediate = true; nodeToAddTo.use_cache = true; - const nsfwCheckerNode: ImageNSFWBlurInvocation = { + const nsfwCheckerNode: Invocation<'img_nsfw'> = { id: NSFW_CHECKER, type: 'img_nsfw', is_intermediate: getIsIntermediate(state), board: getBoardField(state), }; - graph.nodes[NSFW_CHECKER] = nsfwCheckerNode as ImageNSFWBlurInvocation; + graph.nodes[NSFW_CHECKER] = nsfwCheckerNode; graph.edges.push({ source: { node_id: nodeIdToAddTo, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addSDXLLoRAstoGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLLoRAstoGraph.ts similarity index 94% rename from invokeai/frontend/web/src/features/nodes/util/graph/addSDXLLoRAstoGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLLoRAstoGraph.ts index 1a803102b1..695b7a73ed 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addSDXLLoRAstoGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLLoRAstoGraph.ts @@ -1,8 +1,6 @@ import type { RootState } from 'app/store/store'; import { zModelIdentifierField } from 'features/nodes/types/common'; -import { filter, size } from 'lodash-es'; -import type { CoreMetadataInvocation, NonNullableGraph, SDXLLoRALoaderInvocation } from 'services/api/types'; - +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; import { LORA_LOADER, NEGATIVE_CONDITIONING, @@ -10,8 +8,9 @@ import { SDXL_MODEL_LOADER, SDXL_REFINER_INPAINT_CREATE_MASK, SEAMLESS, -} from './constants'; -import { upsertMetadata } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import { filter, size } from 'lodash-es'; +import type { Invocation, NonNullableGraph, S } from 'services/api/types'; export const addSDXLLoRAsToGraph = async ( state: RootState, @@ -35,7 +34,7 @@ export const addSDXLLoRAsToGraph = async ( return; } - const loraMetadata: CoreMetadataInvocation['loras'] = []; + const loraMetadata: S['CoreMetadataInvocation']['loras'] = []; // Handle Seamless Plugs const unetLoaderId = modelLoaderNodeId; @@ -61,7 +60,7 @@ export const addSDXLLoRAsToGraph = async ( const currentLoraNodeId = `${LORA_LOADER}_${lora.model.key}`; const parsedModel = zModelIdentifierField.parse(lora.model); - const loraLoaderNode: SDXLLoRALoaderInvocation = { + const loraLoaderNode: Invocation<'sdxl_lora_loader'> = { type: 'sdxl_lora_loader', id: currentLoraNodeId, is_intermediate: true, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addSDXLRefinerToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts similarity index 95% rename from invokeai/frontend/web/src/features/nodes/util/graph/addSDXLRefinerToGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts index 9df1d63630..6fc406ca74 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addSDXLRefinerToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSDXLRefinerToGraph.ts @@ -1,8 +1,6 @@ import type { RootState } from 'app/store/store'; import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import type { NonNullableGraph, SeamlessModeInvocation } from 'services/api/types'; -import { isRefinerMainModelModelConfig } from 'services/api/types'; - +import { getModelMetadataField, upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; import { CANVAS_OUTPUT, INPAINT_CREATE_MASK, @@ -17,9 +15,10 @@ import { SDXL_REFINER_NEGATIVE_CONDITIONING, SDXL_REFINER_POSITIVE_CONDITIONING, SDXL_REFINER_SEAMLESS, -} from './constants'; -import { getSDXLStylePrompts } from './graphBuilderUtils'; -import { getModelMetadataField, upsertMetadata } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import { getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { NonNullableGraph } from 'services/api/types'; +import { isRefinerMainModelModelConfig } from 'services/api/types'; export const addSDXLRefinerToGraph = async ( state: RootState, @@ -101,7 +100,7 @@ export const addSDXLRefinerToGraph = async ( type: 'seamless', seamless_x: seamlessXAxis, seamless_y: seamlessYAxis, - } as SeamlessModeInvocation; + }; graph.edges.push( { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addSeamlessToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSeamlessToLinearGraph.ts similarity index 92% rename from invokeai/frontend/web/src/features/nodes/util/graph/addSeamlessToLinearGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSeamlessToLinearGraph.ts index d6fcd411a4..357b3357e2 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addSeamlessToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addSeamlessToLinearGraph.ts @@ -1,6 +1,5 @@ import type { RootState } from 'app/store/store'; -import type { NonNullableGraph, SeamlessModeInvocation } from 'services/api/types'; - +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; import { DENOISE_LATENTS, SDXL_CANVAS_IMAGE_TO_IMAGE_GRAPH, @@ -11,8 +10,8 @@ import { SDXL_DENOISE_LATENTS, SEAMLESS, VAE_LOADER, -} from './constants'; -import { upsertMetadata } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import type { NonNullableGraph } from 'services/api/types'; export const addSeamlessToLinearGraph = ( state: RootState, @@ -28,7 +27,7 @@ export const addSeamlessToLinearGraph = ( type: 'seamless', seamless_x: seamlessXAxis, seamless_y: seamlessYAxis, - } as SeamlessModeInvocation; + }; if (!isAutoVae) { graph.nodes[VAE_LOADER] = { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addT2IAdapterToLinearGraph.ts similarity index 90% rename from invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addT2IAdapterToLinearGraph.ts index ee21bbff1b..7c51d9488f 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addT2IAdapterToLinearGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addT2IAdapterToLinearGraph.ts @@ -2,19 +2,12 @@ import type { RootState } from 'app/store/store'; import { selectValidT2IAdapters } from 'features/controlAdapters/store/controlAdaptersSlice'; import type { ControlAdapterProcessorType, T2IAdapterConfig } from 'features/controlAdapters/store/types'; import type { ImageField } from 'features/nodes/types/common'; +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; +import { T2I_ADAPTER_COLLECT } from 'features/nodes/util/graph/constants'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; -import type { - CollectInvocation, - CoreMetadataInvocation, - NonNullableGraph, - S, - T2IAdapterInvocation, -} from 'services/api/types'; +import type { Invocation, NonNullableGraph, S } from 'services/api/types'; import { assert } from 'tsafe'; -import { T2I_ADAPTER_COLLECT } from './constants'; -import { upsertMetadata } from './metadata'; - export const addT2IAdaptersToLinearGraph = async ( state: RootState, graph: NonNullableGraph, @@ -36,7 +29,7 @@ export const addT2IAdaptersToLinearGraph = async ( if (t2iAdapters.length) { // Even though denoise_latents' t2i adapter input is collection or scalar, keep it simple and always use a collect - const t2iAdapterCollectNode: CollectInvocation = { + const t2iAdapterCollectNode: Invocation<'collect'> = { id: T2I_ADAPTER_COLLECT, type: 'collect', is_intermediate: true, @@ -50,7 +43,7 @@ export const addT2IAdaptersToLinearGraph = async ( }, }); - const t2iAdapterMetadata: CoreMetadataInvocation['t2iAdapters'] = []; + const t2iAdapterMetadata: S['CoreMetadataInvocation']['t2iAdapters'] = []; for (const t2iAdapter of t2iAdapters) { if (!t2iAdapter.model) { @@ -68,7 +61,7 @@ export const addT2IAdaptersToLinearGraph = async ( weight, } = t2iAdapter; - const t2iAdapterNode: T2IAdapterInvocation = { + const t2iAdapterNode: Invocation<'t2i_adapter'> = { id: `t2i_adapter_${id}`, type: 't2i_adapter', is_intermediate: true, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addVAEToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addVAEToGraph.ts similarity index 97% rename from invokeai/frontend/web/src/features/nodes/util/graph/addVAEToGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addVAEToGraph.ts index f464723381..dfa3818d07 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addVAEToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addVAEToGraph.ts @@ -1,6 +1,5 @@ import type { RootState } from 'app/store/store'; -import type { NonNullableGraph } from 'services/api/types'; - +import { upsertMetadata } from 'features/nodes/util/graph/canvas/metadata'; import { CANVAS_IMAGE_TO_IMAGE_GRAPH, CANVAS_INPAINT_GRAPH, @@ -21,8 +20,8 @@ import { SDXL_REFINER_SEAMLESS, SEAMLESS, VAE_LOADER, -} from './constants'; -import { upsertMetadata } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import type { NonNullableGraph } from 'services/api/types'; export const addVAEToGraph = async ( state: RootState, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/addWatermarkerToGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addWatermarkerToGraph.ts similarity index 70% rename from invokeai/frontend/web/src/features/nodes/util/graph/addWatermarkerToGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/addWatermarkerToGraph.ts index 61beb11df4..f87517b5d1 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/addWatermarkerToGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/addWatermarkerToGraph.ts @@ -1,29 +1,23 @@ import type { RootState } from 'app/store/store'; -import type { - ImageNSFWBlurInvocation, - ImageWatermarkInvocation, - LatentsToImageInvocation, - NonNullableGraph, -} from 'services/api/types'; - -import { LATENTS_TO_IMAGE, NSFW_CHECKER, WATERMARKER } from './constants'; -import { getBoardField, getIsIntermediate } from './graphBuilderUtils'; +import { LATENTS_TO_IMAGE, NSFW_CHECKER, WATERMARKER } from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { Invocation, NonNullableGraph } from 'services/api/types'; export const addWatermarkerToGraph = ( state: RootState, graph: NonNullableGraph, nodeIdToAddTo = LATENTS_TO_IMAGE ): void => { - const nodeToAddTo = graph.nodes[nodeIdToAddTo] as LatentsToImageInvocation | undefined; + const nodeToAddTo = graph.nodes[nodeIdToAddTo] as Invocation<'l2i'> | undefined; - const nsfwCheckerNode = graph.nodes[NSFW_CHECKER] as ImageNSFWBlurInvocation | undefined; + const nsfwCheckerNode = graph.nodes[NSFW_CHECKER] as Invocation<'img_nsfw'> | undefined; if (!nodeToAddTo) { // something has gone terribly awry return; } - const watermarkerNode: ImageWatermarkInvocation = { + const watermarkerNode: Invocation<'img_watermark'> = { id: WATERMARKER, type: 'img_watermark', is_intermediate: getIsIntermediate(state), diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasGraph.ts similarity index 100% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasGraph.ts diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts similarity index 96% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasImageToImageGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts index f2c9957edc..8f5fe9f2b8 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasImageToImageGraph.ts @@ -1,22 +1,7 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; -import { - type ImageDTO, - type ImageToLatentsInvocation, - isNonRefinerMainModelConfig, - type NonNullableGraph, -} from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addLoRAsToGraph } from './addLoRAsToGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; +import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata'; import { CANVAS_IMAGE_TO_IMAGE_GRAPH, CANVAS_OUTPUT, @@ -30,8 +15,19 @@ import { NOISE, POSITIVE_CONDITIONING, SEAMLESS, -} from './constants'; -import { addCoreMetadataNode, getModelMetadataField } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; +import { isNonRefinerMainModelConfig } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addLoRAsToGraph } from './addLoRAsToGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Image to Image graph. @@ -300,7 +296,7 @@ export const buildCanvasImageToImageGraph = async ( use_cache: false, }; - (graph.nodes[IMAGE_TO_LATENTS] as ImageToLatentsInvocation).image = initialImage; + (graph.nodes[IMAGE_TO_LATENTS] as Invocation<'i2l'>).image = initialImage; graph.edges.push({ source: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts similarity index 93% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasInpaintGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts index ab73953008..c995c38a3c 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasInpaintGraph.ts @@ -1,22 +1,5 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; -import type { - CanvasPasteBackInvocation, - CreateGradientMaskInvocation, - ImageDTO, - ImageToLatentsInvocation, - NoiseInvocation, - NonNullableGraph, -} from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addLoRAsToGraph } from './addLoRAsToGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; import { CANVAS_INPAINT_GRAPH, CANVAS_OUTPUT, @@ -34,8 +17,18 @@ import { NOISE, POSITIVE_CONDITIONING, SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate } from './graphBuilderUtils'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addLoRAsToGraph } from './addLoRAsToGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Inpaint graph. @@ -316,8 +309,8 @@ export const buildCanvasInpaintGraph = async ( height: height, }; - (graph.nodes[NOISE] as NoiseInvocation).width = scaledWidth; - (graph.nodes[NOISE] as NoiseInvocation).height = scaledHeight; + (graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth; + (graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight; // Connect Nodes graph.edges.push( @@ -397,22 +390,22 @@ export const buildCanvasInpaintGraph = async ( ); } else { // Add Images To Nodes - (graph.nodes[NOISE] as NoiseInvocation).width = width; - (graph.nodes[NOISE] as NoiseInvocation).height = height; + (graph.nodes[NOISE] as Invocation<'noise'>).width = width; + (graph.nodes[NOISE] as Invocation<'noise'>).height = height; graph.nodes[INPAINT_IMAGE] = { - ...(graph.nodes[INPAINT_IMAGE] as ImageToLatentsInvocation), + ...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>), image: canvasInitImage, }; graph.nodes[INPAINT_CREATE_MASK] = { - ...(graph.nodes[INPAINT_CREATE_MASK] as CreateGradientMaskInvocation), + ...(graph.nodes[INPAINT_CREATE_MASK] as Invocation<'create_gradient_mask'>), mask: canvasMaskImage, }; // Paste Back graph.nodes[CANVAS_OUTPUT] = { - ...(graph.nodes[CANVAS_OUTPUT] as CanvasPasteBackInvocation), + ...(graph.nodes[CANVAS_OUTPUT] as Invocation<'canvas_paste_back'>), mask: canvasMaskImage, }; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasOutpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts similarity index 95% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasOutpaintGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts index 6b564f464e..e4a9b11b96 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasOutpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasOutpaintGraph.ts @@ -1,22 +1,5 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; -import type { - ImageDTO, - ImageToLatentsInvocation, - InfillPatchMatchInvocation, - InfillTileInvocation, - NoiseInvocation, - NonNullableGraph, -} from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addLoRAsToGraph } from './addLoRAsToGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; import { CANVAS_OUTPAINT_GRAPH, CANVAS_OUTPUT, @@ -38,8 +21,18 @@ import { NOISE, POSITIVE_CONDITIONING, SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate } from './graphBuilderUtils'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addLoRAsToGraph } from './addLoRAsToGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Outpaint graph. @@ -437,8 +430,8 @@ export const buildCanvasOutpaintGraph = async ( height: height, }; - (graph.nodes[NOISE] as NoiseInvocation).width = scaledWidth; - (graph.nodes[NOISE] as NoiseInvocation).height = scaledHeight; + (graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth; + (graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight; // Connect Nodes graph.edges.push( @@ -528,8 +521,8 @@ export const buildCanvasOutpaintGraph = async ( }, { source: { - node_id: MASK_RESIZE_DOWN, - field: 'image', + node_id: INPAINT_CREATE_MASK, + field: 'expanded_mask_area', }, destination: { node_id: CANVAS_OUTPUT, @@ -540,15 +533,15 @@ export const buildCanvasOutpaintGraph = async ( } else { // Add Images To Nodes graph.nodes[INPAINT_INFILL] = { - ...(graph.nodes[INPAINT_INFILL] as InfillTileInvocation | InfillPatchMatchInvocation), + ...(graph.nodes[INPAINT_INFILL] as Invocation<'infill_tile'> | Invocation<'infill_patchmatch'>), image: canvasInitImage, }; - (graph.nodes[NOISE] as NoiseInvocation).width = width; - (graph.nodes[NOISE] as NoiseInvocation).height = height; + (graph.nodes[NOISE] as Invocation<'noise'>).width = width; + (graph.nodes[NOISE] as Invocation<'noise'>).height = height; graph.nodes[INPAINT_IMAGE] = { - ...(graph.nodes[INPAINT_IMAGE] as ImageToLatentsInvocation), + ...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>), image: canvasInitImage, }; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLImageToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts similarity index 95% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLImageToImageGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts index ee918d1470..186dfa53b3 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLImageToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLImageToImageGraph.ts @@ -1,22 +1,7 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import { - type ImageDTO, - type ImageToLatentsInvocation, - isNonRefinerMainModelConfig, - type NonNullableGraph, -} from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; -import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; +import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata'; import { CANVAS_OUTPUT, IMAGE_TO_LATENTS, @@ -30,9 +15,20 @@ import { SDXL_MODEL_LOADER, SDXL_REFINER_SEAMLESS, SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from './graphBuilderUtils'; -import { addCoreMetadataNode, getModelMetadataField } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; +import { isNonRefinerMainModelConfig } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; +import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Image to Image graph. @@ -301,7 +297,7 @@ export const buildCanvasSDXLImageToImageGraph = async ( use_cache: false, }; - (graph.nodes[IMAGE_TO_LATENTS] as ImageToLatentsInvocation).image = initialImage; + (graph.nodes[IMAGE_TO_LATENTS] as Invocation<'i2l'>).image = initialImage; graph.edges.push({ source: { diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLInpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts similarity index 94% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLInpaintGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts index 68b948a44a..277b713079 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLInpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLInpaintGraph.ts @@ -1,23 +1,5 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; -import type { - CanvasPasteBackInvocation, - CreateGradientMaskInvocation, - ImageDTO, - ImageToLatentsInvocation, - NoiseInvocation, - NonNullableGraph, -} from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; -import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; import { CANVAS_OUTPUT, INPAINT_CREATE_MASK, @@ -35,8 +17,19 @@ import { SDXL_MODEL_LOADER, SDXL_REFINER_SEAMLESS, SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from './graphBuilderUtils'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; +import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Inpaint graph. @@ -327,8 +320,8 @@ export const buildCanvasSDXLInpaintGraph = async ( height: height, }; - (graph.nodes[NOISE] as NoiseInvocation).width = scaledWidth; - (graph.nodes[NOISE] as NoiseInvocation).height = scaledHeight; + (graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth; + (graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight; // Connect Nodes graph.edges.push( @@ -408,22 +401,22 @@ export const buildCanvasSDXLInpaintGraph = async ( ); } else { // Add Images To Nodes - (graph.nodes[NOISE] as NoiseInvocation).width = width; - (graph.nodes[NOISE] as NoiseInvocation).height = height; + (graph.nodes[NOISE] as Invocation<'noise'>).width = width; + (graph.nodes[NOISE] as Invocation<'noise'>).height = height; graph.nodes[INPAINT_IMAGE] = { - ...(graph.nodes[INPAINT_IMAGE] as ImageToLatentsInvocation), + ...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>), image: canvasInitImage, }; graph.nodes[INPAINT_CREATE_MASK] = { - ...(graph.nodes[INPAINT_CREATE_MASK] as CreateGradientMaskInvocation), + ...(graph.nodes[INPAINT_CREATE_MASK] as Invocation<'create_gradient_mask'>), mask: canvasMaskImage, }; // Paste Back graph.nodes[CANVAS_OUTPUT] = { - ...(graph.nodes[CANVAS_OUTPUT] as CanvasPasteBackInvocation), + ...(graph.nodes[CANVAS_OUTPUT] as Invocation<'canvas_paste_back'>), mask: canvasMaskImage, }; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLOutpaintGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts similarity index 95% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLOutpaintGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts index c5c40b695a..b09d7d8b90 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLOutpaintGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLOutpaintGraph.ts @@ -1,23 +1,5 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; -import type { - ImageDTO, - ImageToLatentsInvocation, - InfillPatchMatchInvocation, - InfillTileInvocation, - NoiseInvocation, - NonNullableGraph, -} from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; -import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; import { CANVAS_OUTPUT, INPAINT_CREATE_MASK, @@ -39,8 +21,19 @@ import { SDXL_MODEL_LOADER, SDXL_REFINER_SEAMLESS, SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from './graphBuilderUtils'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { ImageDTO, Invocation, NonNullableGraph } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; +import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Outpaint graph. @@ -446,8 +439,8 @@ export const buildCanvasSDXLOutpaintGraph = async ( height: height, }; - (graph.nodes[NOISE] as NoiseInvocation).width = scaledWidth; - (graph.nodes[NOISE] as NoiseInvocation).height = scaledHeight; + (graph.nodes[NOISE] as Invocation<'noise'>).width = scaledWidth; + (graph.nodes[NOISE] as Invocation<'noise'>).height = scaledHeight; // Connect Nodes graph.edges.push( @@ -549,15 +542,15 @@ export const buildCanvasSDXLOutpaintGraph = async ( } else { // Add Images To Nodes graph.nodes[INPAINT_INFILL] = { - ...(graph.nodes[INPAINT_INFILL] as InfillTileInvocation | InfillPatchMatchInvocation), + ...(graph.nodes[INPAINT_INFILL] as Invocation<'infill_tile'> | Invocation<'infill_patchmatch'>), image: canvasInitImage, }; - (graph.nodes[NOISE] as NoiseInvocation).width = width; - (graph.nodes[NOISE] as NoiseInvocation).height = height; + (graph.nodes[NOISE] as Invocation<'noise'>).width = width; + (graph.nodes[NOISE] as Invocation<'noise'>).height = height; graph.nodes[INPAINT_IMAGE] = { - ...(graph.nodes[INPAINT_IMAGE] as ImageToLatentsInvocation), + ...(graph.nodes[INPAINT_IMAGE] as Invocation<'i2l'>), image: canvasInitImage, }; @@ -584,8 +577,8 @@ export const buildCanvasSDXLOutpaintGraph = async ( }, { source: { - node_id: MASK_COMBINE, - field: 'image', + node_id: INPAINT_CREATE_MASK, + field: 'expanded_mask_area', }, destination: { node_id: CANVAS_OUTPUT, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts similarity index 97% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLTextToImageGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts index f6ac645580..b2a8aa6ada 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasSDXLTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasSDXLTextToImageGraph.ts @@ -1,17 +1,7 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; -import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; +import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata'; import { CANVAS_OUTPUT, LATENTS_TO_IMAGE, @@ -23,9 +13,19 @@ import { SDXL_MODEL_LOADER, SDXL_REFINER_SEAMLESS, SEAMLESS, -} from './constants'; -import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from './graphBuilderUtils'; -import { addCoreMetadataNode, getModelMetadataField } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate, getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSDXLLoRAsToGraph } from './addSDXLLoRAstoGraph'; +import { addSDXLRefinerToGraph } from './addSDXLRefinerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Text to Image graph. diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasTextToImageGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts similarity index 98% rename from invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasTextToImageGraph.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts index 0749308fb8..8ce5134480 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildCanvasTextToImageGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/buildCanvasTextToImageGraph.ts @@ -1,17 +1,7 @@ import { logger } from 'app/logging/logger'; import type { RootState } from 'app/store/store'; import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; -import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; -import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; - -import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; -import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; -import { addLoRAsToGraph } from './addLoRAsToGraph'; -import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; -import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; -import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; -import { addVAEToGraph } from './addVAEToGraph'; -import { addWatermarkerToGraph } from './addWatermarkerToGraph'; +import { addCoreMetadataNode, getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata'; import { CANVAS_OUTPUT, CANVAS_TEXT_TO_IMAGE_GRAPH, @@ -23,8 +13,18 @@ import { NOISE, POSITIVE_CONDITIONING, SEAMLESS, -} from './constants'; -import { addCoreMetadataNode, getModelMetadataField } from './metadata'; +} from 'features/nodes/util/graph/constants'; +import { getBoardField, getIsIntermediate } from 'features/nodes/util/graph/graphBuilderUtils'; +import { isNonRefinerMainModelConfig, type NonNullableGraph } from 'services/api/types'; + +import { addControlNetToLinearGraph } from './addControlNetToLinearGraph'; +import { addIPAdapterToLinearGraph } from './addIPAdapterToLinearGraph'; +import { addLoRAsToGraph } from './addLoRAsToGraph'; +import { addNSFWCheckerToGraph } from './addNSFWCheckerToGraph'; +import { addSeamlessToLinearGraph } from './addSeamlessToLinearGraph'; +import { addT2IAdaptersToLinearGraph } from './addT2IAdapterToLinearGraph'; +import { addVAEToGraph } from './addVAEToGraph'; +import { addWatermarkerToGraph } from './addWatermarkerToGraph'; /** * Builds the Canvas tab's Text to Image graph. diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/metadata.ts similarity index 54% rename from invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts rename to invokeai/frontend/web/src/features/nodes/util/graph/canvas/metadata.ts index 9e8b5a5a6b..97f77f58d9 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/canvas/metadata.ts @@ -1,12 +1,11 @@ import type { JSONObject } from 'common/types'; import type { ModelIdentifierField } from 'features/nodes/types/common'; -import type { AnyModelConfig, CoreMetadataInvocation, NonNullableGraph } from 'services/api/types'; - -import { METADATA } from './constants'; +import { METADATA } from 'features/nodes/util/graph/constants'; +import type { AnyModelConfig, NonNullableGraph, S } from 'services/api/types'; export const addCoreMetadataNode = ( graph: NonNullableGraph, - metadata: Partial, + metadata: Partial, nodeId: string ): void => { graph.nodes[METADATA] = { @@ -31,9 +30,9 @@ export const addCoreMetadataNode = ( export const upsertMetadata = ( graph: NonNullableGraph, - metadata: Partial | JSONObject + metadata: Partial | JSONObject ): void => { - const metadataNode = graph.nodes[METADATA] as CoreMetadataInvocation | undefined; + const metadataNode = graph.nodes[METADATA] as S['CoreMetadataInvocation'] | undefined; if (!metadataNode) { return; @@ -42,8 +41,8 @@ export const upsertMetadata = ( Object.assign(metadataNode, metadata); }; -export const removeMetadata = (graph: NonNullableGraph, key: keyof CoreMetadataInvocation): void => { - const metadataNode = graph.nodes[METADATA] as CoreMetadataInvocation | undefined; +export const removeMetadata = (graph: NonNullableGraph, key: keyof S['CoreMetadataInvocation']): void => { + const metadataNode = graph.nodes[METADATA] as S['CoreMetadataInvocation'] | undefined; if (!metadataNode) { return; @@ -53,26 +52,11 @@ export const removeMetadata = (graph: NonNullableGraph, key: keyof CoreMetadataI }; export const getHasMetadata = (graph: NonNullableGraph): boolean => { - const metadataNode = graph.nodes[METADATA] as CoreMetadataInvocation | undefined; + const metadataNode = graph.nodes[METADATA] as S['CoreMetadataInvocation'] | undefined; return Boolean(metadataNode); }; -export const setMetadataReceivingNode = (graph: NonNullableGraph, nodeId: string) => { - graph.edges = graph.edges.filter((edge) => edge.source.node_id !== METADATA); - - graph.edges.push({ - source: { - node_id: METADATA, - field: 'metadata', - }, - destination: { - node_id: nodeId, - field: 'metadata', - }, - }); -}; - export const getModelMetadataField = ({ key, hash, name, base, type }: AnyModelConfig): ModelIdentifierField => ({ key, hash, diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts new file mode 100644 index 0000000000..a8be96e484 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.test.ts @@ -0,0 +1,579 @@ +import { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { AnyInvocation, Invocation } from 'services/api/types'; +import { assert, AssertionError, is } from 'tsafe'; +import { validate } from 'uuid'; +import { describe, expect, it } from 'vitest'; + +describe('Graph', () => { + describe('constructor', () => { + it('should create a new graph with the correct id', () => { + const g = new Graph('test-id'); + expect(g._graph.id).toBe('test-id'); + }); + it('should create a new graph with a uuid id if none is provided', () => { + const g = new Graph(); + expect(g._graph.id).not.toBeUndefined(); + expect(validate(g._graph.id)).toBeTruthy(); + }); + }); + + describe('addNode', () => { + const testNode = { + id: 'test-node', + type: 'add', + } as const; + it('should add a node to the graph', () => { + const g = new Graph(); + g.addNode(testNode); + expect(g._graph.nodes['test-node']).not.toBeUndefined(); + expect(g._graph.nodes['test-node']?.type).toBe('add'); + }); + it('should set is_intermediate to true if not provided', () => { + const g = new Graph(); + g.addNode(testNode); + expect(g._graph.nodes['test-node']?.is_intermediate).toBe(true); + }); + it('should not overwrite is_intermediate if provided', () => { + const g = new Graph(); + g.addNode({ + ...testNode, + is_intermediate: false, + }); + expect(g._graph.nodes['test-node']?.is_intermediate).toBe(false); + }); + it('should set use_cache to true if not provided', () => { + const g = new Graph(); + g.addNode(testNode); + expect(g._graph.nodes['test-node']?.use_cache).toBe(true); + }); + it('should not overwrite use_cache if provided', () => { + const g = new Graph(); + g.addNode({ + ...testNode, + use_cache: false, + }); + expect(g._graph.nodes['test-node']?.use_cache).toBe(false); + }); + it('should error if the node id is already in the graph', () => { + const g = new Graph(); + g.addNode(testNode); + expect(() => g.addNode(testNode)).toThrowError(AssertionError); + }); + it('should infer the types if provided', () => { + const g = new Graph(); + const node = g.addNode(testNode); + assert(is>(node)); + const g2 = new Graph(); + // @ts-expect-error The node object is an `add` type, but the generic is a `sub` type + g2.addNode<'sub'>(testNode); + }); + }); + + describe('addEdge', () => { + const add: Invocation<'add'> = { + id: 'from-node', + type: 'add', + }; + const sub: Invocation<'sub'> = { + id: 'to-node', + type: 'sub', + }; + it('should add an edge to the graph with the provided values', () => { + const g = new Graph(); + g.addNode(add); + g.addNode(sub); + g.addEdge(add, 'value', sub, 'b'); + expect(g._graph.edges.length).toBe(1); + expect(g._graph.edges[0]).toEqual({ + source: { node_id: 'from-node', field: 'value' }, + destination: { node_id: 'to-node', field: 'b' }, + }); + }); + it('should throw an error if the edge already exists', () => { + const g = new Graph(); + g.addEdge(add, 'value', sub, 'b'); + expect(() => g.addEdge(add, 'value', sub, 'b')).toThrowError(AssertionError); + }); + it('should infer field names', () => { + const g = new Graph(); + // @ts-expect-error The first field must be a valid output field of the first type arg + g.addEdge(add, 'not-a-valid-field', add, 'a'); + // @ts-expect-error The second field must be a valid input field of the second type arg + g.addEdge(add, 'value', sub, 'not-a-valid-field'); + // @ts-expect-error The first field must be any valid output field + g.addEdge(add, 'not-a-valid-field', sub, 'a'); + // @ts-expect-error The second field must be any valid input field + g.addEdge(add, 'clip', sub, 'not-a-valid-field'); + }); + }); + + describe('addEdgeFromObj', () => { + it('should add an edge to the graph with the provided values', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'add', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'sub', + }); + g.addEdgeFromObj({ + source: { node_id: n1.id, field: 'value' }, + destination: { node_id: n2.id, field: 'b' }, + }); + expect(g._graph.edges.length).toBe(1); + expect(g._graph.edges[0]).toEqual({ + source: { node_id: n1.id, field: 'value' }, + destination: { node_id: n2.id, field: 'b' }, + }); + }); + }); + + describe('getNode', () => { + const g = new Graph(); + const node = g.addNode({ + id: 'test-node', + type: 'add', + }); + + it('should return the node with the provided id', () => { + const n = g.getNode('test-node'); + expect(n).toBe(node); + }); + it('should throw an error if the node is not found', () => { + expect(() => g.getNode('not-found')).toThrowError(AssertionError); + }); + }); + + describe('deleteNode', () => { + it('should delete the node with the provided id', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'add', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'add', + }); + g.addEdge(n1, 'value', n2, 'a'); + g.addEdge(n2, 'value', n3, 'a'); + // This edge should not be deleted bc it doesn't touch n2 + g.addEdge(n1, 'value', n3, 'a'); + g.deleteNode(n2.id); + expect(g.hasNode(n1.id)).toBe(true); + expect(g.hasNode(n2.id)).toBe(false); + expect(g.hasNode(n3.id)).toBe(true); + // Should delete edges to and from the node + expect(g.getEdges().length).toBe(1); + }); + }); + + describe('hasNode', () => { + const g = new Graph(); + g.addNode({ + id: 'test-node', + type: 'add', + }); + + it('should return true if the node is in the graph', () => { + expect(g.hasNode('test-node')).toBe(true); + }); + it('should return false if the node is not in the graph', () => { + expect(g.hasNode('not-found')).toBe(false); + }); + }); + + describe('getEdge', () => { + const g = new Graph(); + const add: Invocation<'add'> = { + id: 'from-node', + type: 'add', + }; + const sub: Invocation<'sub'> = { + id: 'to-node', + type: 'sub', + }; + g.addEdge(add, 'value', sub, 'b'); + it('should return the edge with the provided values', () => { + expect(g.getEdge(add, 'value', sub, 'b')).toEqual({ + source: { node_id: 'from-node', field: 'value' }, + destination: { node_id: 'to-node', field: 'b' }, + }); + }); + it('should throw an error if the edge is not found', () => { + expect(() => g.getEdge(add, 'value', sub, 'a')).toThrowError(AssertionError); + }); + }); + + describe('getEdges', () => { + it('should get all edges in the graph', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'add', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'add', + }); + const e1 = g.addEdge(n1, 'value', n2, 'a'); + const e2 = g.addEdge(n2, 'value', n3, 'a'); + expect(g.getEdges()).toEqual([e1, e2]); + }); + }); + + describe('hasEdge', () => { + const g = new Graph(); + const add: Invocation<'add'> = { + id: 'from-node', + type: 'add', + }; + const sub: Invocation<'sub'> = { + id: 'to-node', + type: 'sub', + }; + g.addEdge(add, 'value', sub, 'b'); + it('should return true if the edge is in the graph', () => { + expect(g.hasEdge(add, 'value', sub, 'b')).toBe(true); + }); + it('should return false if the edge is not in the graph', () => { + expect(g.hasEdge(add, 'value', sub, 'a')).toBe(false); + }); + }); + + describe('getGraph', () => { + it('should return the graph', () => { + const g = new Graph(); + expect(g.getGraph()).toBe(g._graph); + }); + it('should raise an error if the graph is invalid', () => { + const g = new Graph(); + const add: Invocation<'add'> = { + id: 'from-node', + type: 'add', + }; + const sub: Invocation<'sub'> = { + id: 'to-node', + type: 'sub', + }; + g.addEdge(add, 'value', sub, 'b'); + expect(() => g.getGraph()).toThrowError(AssertionError); + }); + }); + + describe('getGraphSafe', () => { + it('should return the graph even if it is invalid', () => { + const g = new Graph(); + const add: Invocation<'add'> = { + id: 'from-node', + type: 'add', + }; + const sub: Invocation<'sub'> = { + id: 'to-node', + type: 'sub', + }; + g.addEdge(add, 'value', sub, 'b'); + expect(g.getGraphSafe()).toBe(g._graph); + }); + }); + + describe('validate', () => { + it('should not throw an error if the graph is valid', () => { + const g = new Graph(); + expect(() => g.validate()).not.toThrow(); + }); + it("should throw an error if the graph contains an edge without a source or destination node that doesn't exist", () => { + const g = new Graph(); + // These nodes do not get added to the graph, only used to add the edge + const add: Invocation<'add'> = { + id: 'from-node', + type: 'add', + }; + const sub: Invocation<'sub'> = { + id: 'to-node', + type: 'sub', + }; + // edge from nowhere to nowhere + g.addEdge(add, 'value', sub, 'b'); + expect(() => g.validate()).toThrowError(AssertionError); + }); + it('should throw an error if a destination node is not a collect node and has multiple edges to it', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'add', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'add', + }); + g.addEdge(n1, 'value', n3, 'a'); + g.addEdge(n2, 'value', n3, 'a'); + expect(() => g.validate()).toThrowError(AssertionError); + }); + it('should not throw an error if a destination node is a collect node and has multiple edges to it', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'add', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'collect', + }); + g.addEdge(n1, 'value', n3, 'item'); + g.addEdge(n2, 'value', n3, 'item'); + expect(() => g.validate()).not.toThrow(); + }); + }); + + describe('traversal', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'add', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'alpha_mask_to_tensor', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'add', + }); + const n4 = g.addNode({ + id: 'n4', + type: 'add', + }); + const n5 = g.addNode({ + id: 'n5', + type: 'add', + }); + const e1 = g.addEdge(n1, 'value', n3, 'a'); + const e2 = g.addEdge(n2, 'height', n3, 'b'); + const e3 = g.addEdge(n3, 'value', n4, 'a'); + const e4 = g.addEdge(n3, 'value', n5, 'b'); + describe('getEdgesFrom', () => { + it('should return the edges that start at the provided node', () => { + expect(g.getEdgesFrom(n3)).toEqual([e3, e4]); + }); + it('should return the edges that start at the provided node and have the provided field', () => { + expect(g.getEdgesFrom(n3, ['value'])).toEqual([e3, e4]); + }); + }); + describe('getEdgesTo', () => { + it('should return the edges that end at the provided node', () => { + expect(g.getEdgesTo(n3)).toEqual([e1, e2]); + }); + it('should return the edges that end at the provided node and have the provided field', () => { + expect(g.getEdgesTo(n3, ['b', 'a'])).toEqual([e1, e2]); + }); + }); + describe('getIncomers', () => { + it('should return the nodes that have an edge to the provided node', () => { + expect(g.getIncomers(n3)).toEqual([n1, n2]); + }); + }); + describe('getOutgoers', () => { + it('should return the nodes that the provided node has an edge to', () => { + expect(g.getOutgoers(n3)).toEqual([n4, n5]); + }); + }); + }); + + describe('deleteEdgesFrom', () => { + it('should delete edges from the provided node', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'img_resize', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const _e1 = g.addEdge(n1, 'height', n2, 'a'); + const _e2 = g.addEdge(n1, 'width', n2, 'b'); + g.deleteEdgesFrom(n1); + expect(g.getEdgesFrom(n1)).toEqual([]); + }); + it('should delete edges from the provided node, with the provided field', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'img_resize', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'add', + }); + const _e1 = g.addEdge(n1, 'height', n2, 'a'); + const e2 = g.addEdge(n1, 'width', n2, 'b'); + const e3 = g.addEdge(n1, 'width', n3, 'b'); + g.deleteEdgesFrom(n1, ['height']); + expect(g.getEdgesFrom(n1)).toEqual([e2, e3]); + }); + }); + + describe('deleteEdgesTo', () => { + it('should delete edges to the provided node', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'img_resize', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'add', + }); + const _e1 = g.addEdge(n1, 'height', n2, 'a'); + const _e2 = g.addEdge(n1, 'width', n2, 'b'); + g.deleteEdgesTo(n2); + expect(g.getEdgesTo(n2)).toEqual([]); + }); + it('should delete edges to the provided node, with the provided field', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'img_resize', + }); + const n2 = g.addNode({ + id: 'n2', + type: 'img_resize', + }); + const n3 = g.addNode({ + id: 'n3', + type: 'add', + }); + const _e1 = g.addEdge(n1, 'height', n3, 'a'); + const e2 = g.addEdge(n1, 'width', n3, 'b'); + const _e3 = g.addEdge(n2, 'width', n3, 'a'); + g.deleteEdgesTo(n3, ['a']); + expect(g.getEdgesTo(n3)).toEqual([e2]); + }); + }); + + describe('metadata utils', () => { + describe('_getMetadataNode', () => { + it("should get the metadata node, creating it if it doesn't exist", () => { + const g = new Graph(); + const metadata = g._getMetadataNode(); + expect(metadata.id).toBe('core_metadata'); + expect(metadata.type).toBe('core_metadata'); + g.upsertMetadata({ test: 'test' }); + const metadata2 = g._getMetadataNode(); + expect(metadata2).toHaveProperty('test'); + }); + }); + + describe('upsertMetadata', () => { + it('should add metadata to the metadata node', () => { + const g = new Graph(); + g.upsertMetadata({ test: 'test' }); + const metadata = g._getMetadataNode(); + expect(metadata).toHaveProperty('test'); + }); + it('should update metadata on the metadata node', () => { + const g = new Graph(); + g.upsertMetadata({ test: 'test' }); + g.upsertMetadata({ test: 'test2' }); + const metadata = g._getMetadataNode(); + expect(metadata.test).toBe('test2'); + }); + }); + + describe('removeMetadata', () => { + it('should remove metadata from the metadata node', () => { + const g = new Graph(); + g.upsertMetadata({ test: 'test', test2: 'test2' }); + g.removeMetadata(['test']); + const metadata = g._getMetadataNode(); + expect(metadata).not.toHaveProperty('test'); + }); + it('should remove multiple metadata from the metadata node', () => { + const g = new Graph(); + g.upsertMetadata({ test: 'test', test2: 'test2' }); + g.removeMetadata(['test', 'test2']); + const metadata = g._getMetadataNode(); + expect(metadata).not.toHaveProperty('test'); + expect(metadata).not.toHaveProperty('test2'); + }); + }); + + describe('setMetadataReceivingNode', () => { + it('should set the metadata receiving node', () => { + const g = new Graph(); + const n1 = g.addNode({ + id: 'n1', + type: 'img_resize', + }); + g.upsertMetadata({ test: 'test' }); + g.setMetadataReceivingNode(n1); + const metadata = g._getMetadataNode(); + expect(g.getEdgesFrom(metadata as unknown as AnyInvocation).length).toBe(1); + expect(g.getEdgesTo(n1).length).toBe(1); + }); + }); + + describe('getModelMetadataField', () => { + it('should return a ModelIdentifierField', () => { + const field = Graph.getModelMetadataField({ + key: 'b00ee8df-523d-40d2-9578-597283b07cb2', + hash: 'random:9adf270422f525715297afa1649c4ff007a55f09937f57ca628278305624d194', + path: 'sdxl/main/stable-diffusion-xl-1.0-inpainting-0.1', + name: 'stable-diffusion-xl-1.0-inpainting-0.1', + base: 'sdxl', + description: 'sdxl main model stable-diffusion-xl-1.0-inpainting-0.1', + source: '/home/bat/invokeai-4.0.0/models/sdxl/main/stable-diffusion-xl-1.0-inpainting-0.1', + source_type: 'path', + source_api_response: null, + cover_image: null, + type: 'main', + trigger_phrases: null, + default_settings: { + vae: null, + vae_precision: null, + scheduler: null, + steps: null, + cfg_scale: null, + cfg_rescale_multiplier: null, + width: 1024, + height: 1024, + }, + variant: 'inpaint', + format: 'diffusers', + repo_variant: 'fp16', + }); + expect(field).toEqual({ + key: 'b00ee8df-523d-40d2-9578-597283b07cb2', + hash: 'random:9adf270422f525715297afa1649c4ff007a55f09937f57ca628278305624d194', + name: 'stable-diffusion-xl-1.0-inpainting-0.1', + base: 'sdxl', + type: 'main', + }); + }); + }); + }); +}); diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts new file mode 100644 index 0000000000..008f86918a --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/Graph.ts @@ -0,0 +1,419 @@ +import { type ModelIdentifierField, zModelIdentifierField } from 'features/nodes/types/common'; +import { METADATA } from 'features/nodes/util/graph/constants'; +import { forEach, groupBy, isEqual, unset, values } from 'lodash-es'; +import type { + AnyInvocation, + AnyInvocationIncMetadata, + AnyInvocationInputField, + AnyInvocationOutputField, + AnyModelConfig, + InputFields, + Invocation, + InvocationType, + OutputFields, + S, +} from 'services/api/types'; +import { assert } from 'tsafe'; +import { v4 as uuidv4 } from 'uuid'; + +type Edge = { + source: { + node_id: string; + field: AnyInvocationOutputField; + }; + destination: { + node_id: string; + field: AnyInvocationInputField; + }; +}; + +export type GraphType = { id: string; nodes: Record; edges: Edge[] }; + +export class Graph { + _graph: GraphType; + + constructor(id?: string) { + this._graph = { + id: id ?? uuidv4(), + nodes: {}, + edges: [], + }; + } + + //#region Node Operations + + /** + * Add a node to the graph. If a node with the same id already exists, an `AssertionError` is raised. + * The optional `is_intermediate` and `use_cache` fields are both set to `true`, if not set on the node. + * @param node The node to add. + * @returns The added node. + * @raises `AssertionError` if a node with the same id already exists. + */ + addNode(node: Invocation): Invocation { + assert(this._graph.nodes[node.id] === undefined, `Node with id ${node.id} already exists`); + if (node.is_intermediate === undefined) { + node.is_intermediate = true; + } + if (node.use_cache === undefined) { + node.use_cache = true; + } + this._graph.nodes[node.id] = node; + return node; + } + + /** + * Gets a node from the graph. + * @param id The id of the node to get. + * @returns The node. + * @raises `AssertionError` if the node does not exist. + */ + getNode(id: string): AnyInvocation { + const node = this._graph.nodes[id]; + assert(node !== undefined, `Node with id ${id} not found`); + return node; + } + + /** + * Deletes a node from the graph. All edges to and from the node are also deleted. + * @param id The id of the node to delete. + */ + deleteNode(id: string): void { + const node = this._graph.nodes[id]; + if (node) { + this.deleteEdgesFrom(node); + this.deleteEdgesTo(node); + delete this._graph.nodes[id]; + } + } + + /** + * Check if a node exists in the graph. + * @param id The id of the node to check. + * @returns Whether the graph has a node with the given id. + */ + hasNode(id: string): boolean { + try { + this.getNode(id); + return true; + } catch { + return false; + } + } + + /** + * Get the immediate incomers of a node. + * @param node The node to get the incomers of. + * @returns The incoming nodes. + * @raises `AssertionError` if one of the target node's incoming edges has an invalid source node. + */ + getIncomers(node: AnyInvocation): AnyInvocation[] { + return this.getEdgesTo(node).map((edge) => this.getNode(edge.source.node_id)); + } + + /** + * Get the immediate outgoers of a node. + * @param node The node to get the outgoers of. + * @returns The outgoing nodes. + * @raises `AssertionError` if one of the target node's outgoing edges has an invalid destination node. + */ + getOutgoers(node: AnyInvocation): AnyInvocation[] { + return this.getEdgesFrom(node).map((edge) => this.getNode(edge.destination.node_id)); + } + //#endregion + + //#region Edge Operations + + /** + * Add an edge to the graph. If an edge with the same source and destination already exists, an `AssertionError` is raised. + * @param fromNode The source node. + * @param fromField The field of the source node. + * @param toNode The destination node. + * @param toField The field of the destination node. + * @returns The added edge. + * @raises `AssertionError` if an edge with the same source and destination already exists. + */ + addEdge( + fromNode: TFrom, + fromField: OutputFields, + toNode: TTo, + toField: InputFields + ): Edge { + const edge: Edge = { + source: { node_id: fromNode.id, field: fromField }, + destination: { node_id: toNode.id, field: toField }, + }; + const edgeAlreadyExists = this._graph.edges.some((e) => isEqual(e, edge)); + assert(!edgeAlreadyExists, `Edge ${Graph.edgeToString(edge)} already exists`); + this._graph.edges.push(edge); + return edge; + } + + /** + * Add an edge to the graph. If an edge with the same source and destination already exists, an `AssertionError` is raised. + * @param edge The edge to add. + * @returns The added edge. + * @raises `AssertionError` if an edge with the same source and destination already exists. + */ + addEdgeFromObj(edge: Edge): Edge { + const edgeAlreadyExists = this._graph.edges.some((e) => isEqual(e, edge)); + assert(!edgeAlreadyExists, `Edge ${Graph.edgeToString(edge)} already exists`); + this._graph.edges.push(edge); + return edge; + } + + /** + * Get an edge from the graph. If the edge does not exist, an `AssertionError` is raised. + * @param fromNode The source node. + * @param fromField The field of the source node. + * @param toNode The destination node. + * @param toField The field of the destination node. + * @returns The edge. + * @raises `AssertionError` if the edge does not exist. + */ + getEdge( + fromNode: TFrom, + fromField: OutputFields, + toNode: TTo, + toField: InputFields + ): Edge { + const edge = this._graph.edges.find( + (e) => + e.source.node_id === fromNode.id && + e.source.field === fromField && + e.destination.node_id === toNode.id && + e.destination.field === toField + ); + assert(edge !== undefined, `Edge ${Graph.edgeToString(fromNode.id, fromField, toNode.id, toField)} not found`); + return edge; + } + + /** + * Get all edges in the graph. + * @returns The edges. + */ + getEdges(): Edge[] { + return this._graph.edges; + } + + /** + * Check if a graph has an edge. + * @param fromNode The source node. + * @param fromField The field of the source node. + * @param toNode The destination node. + * @param toField The field of the destination node. + * @returns Whether the graph has the edge. + */ + + hasEdge( + fromNode: TFrom, + fromField: OutputFields, + toNode: TTo, + toField: InputFields + ): boolean { + try { + this.getEdge(fromNode, fromField, toNode, toField); + return true; + } catch { + return false; + } + } + + /** + * Get all edges from a node. If `fromFields` is provided, only edges from those fields are returned. + * @param fromNode The source node. + * @param fromFields The fields of the source node (optional). + * @returns The edges. + */ + getEdgesFrom(fromNode: T, fromFields?: OutputFields[]): Edge[] { + let edges = this._graph.edges.filter((edge) => edge.source.node_id === fromNode.id); + if (fromFields) { + // TODO(psyche): figure out how to satisfy TS here without casting - this is _not_ an unsafe cast + edges = edges.filter((edge) => (fromFields as AnyInvocationOutputField[]).includes(edge.source.field)); + } + return edges; + } + + /** + * Get all edges to a node. If `toFields` is provided, only edges to those fields are returned. + * @param toNodeId The destination node. + * @param toFields The fields of the destination node (optional). + * @returns The edges. + */ + getEdgesTo(toNode: T, toFields?: InputFields[]): Edge[] { + let edges = this._graph.edges.filter((edge) => edge.destination.node_id === toNode.id); + if (toFields) { + edges = edges.filter((edge) => (toFields as AnyInvocationInputField[]).includes(edge.destination.field)); + } + return edges; + } + + /** + * INTERNAL: Delete _all_ matching edges from the graph. Uses _.isEqual for comparison. + * @param edge The edge to delete + */ + private _deleteEdge(edge: Edge): void { + this._graph.edges = this._graph.edges.filter((e) => !isEqual(e, edge)); + } + + /** + * Delete all edges to a node. If `toFields` is provided, only edges to those fields are deleted. + * @param toNode The destination node. + * @param toFields The fields of the destination node (optional). + */ + deleteEdgesTo(toNode: T, toFields?: InputFields[]): void { + for (const edge of this.getEdgesTo(toNode, toFields)) { + this._deleteEdge(edge); + } + } + + /** + * Delete all edges from a node. If `fromFields` is provided, only edges from those fields are deleted. + * @param toNode The id of the source node. + * @param fromFields The fields of the source node (optional). + */ + deleteEdgesFrom(fromNode: T, fromFields?: OutputFields[]): void { + for (const edge of this.getEdgesFrom(fromNode, fromFields)) { + this._deleteEdge(edge); + } + } + //#endregion + + //#region Graph Ops + + /** + * Validate the graph. Checks that all edges have valid source and destination nodes. + * @raises `AssertionError` if an edge has an invalid source or destination node. + */ + validate(): void { + // TODO(psyche): Add more validation checks - cycles, valid invocation types, etc. + for (const edge of this._graph.edges) { + this.getNode(edge.source.node_id); + this.getNode(edge.destination.node_id); + assert( + !this._graph.edges.filter((e) => e !== edge).find((e) => isEqual(e, edge)), + `Duplicate edge: ${Graph.edgeToString(edge)}` + ); + } + for (const node of values(this._graph.nodes)) { + const edgesTo = this.getEdgesTo(node); + // Validate that no node has multiple incoming edges with the same field + forEach(groupBy(edgesTo, 'destination.field'), (group, field) => { + if (node.type === 'collect' && field === 'item') { + // Collectors' item field accepts multiple incoming edges + return; + } + assert( + group.length === 1, + `Node ${node.id} has multiple incoming edges with field ${field}: ${group.map(Graph.edgeToString).join(', ')}` + ); + }); + } + } + + /** + * Gets the graph after validating it. + * @returns The graph. + * @raises `AssertionError` if the graph is invalid. + */ + getGraph(): GraphType { + this.validate(); + return this._graph; + } + + /** + * Gets the graph without validating it. + * @returns The graph. + */ + getGraphSafe(): GraphType { + return this._graph; + } + //#endregion + + //#region Metadata + + /** + * INTERNAL: Get the metadata node. If it does not exist, it is created. + * @returns The metadata node. + */ + _getMetadataNode(): S['CoreMetadataInvocation'] { + try { + const node = this.getNode(METADATA) as AnyInvocationIncMetadata; + assert(node.type === 'core_metadata'); + return node; + } catch { + const node: S['CoreMetadataInvocation'] = { id: METADATA, type: 'core_metadata' }; + // @ts-expect-error `Graph` excludes `core_metadata` nodes due to its excessively wide typing + return this.addNode(node); + } + } + + /** + * Add metadata to the graph. If the metadata node does not exist, it is created. If the specific metadata key exists, + * it is overwritten. + * @param metadata The metadata to add. + * @returns The metadata node. + */ + upsertMetadata(metadata: Partial): S['CoreMetadataInvocation'] { + const node = this._getMetadataNode(); + Object.assign(node, metadata); + return node; + } + + /** + * Remove metadata from the graph. + * @param keys The keys of the metadata to remove + * @returns The metadata node + */ + removeMetadata(keys: string[]): S['CoreMetadataInvocation'] { + const metadataNode = this._getMetadataNode(); + for (const k of keys) { + unset(metadataNode, k); + } + return metadataNode; + } + + /** + * Set the node that should receive metadata. All other edges from the metadata node are deleted. + * @param node The node to set as the receiving node + */ + setMetadataReceivingNode(node: AnyInvocation): void { + // @ts-expect-error `Graph` excludes `core_metadata` nodes due to its excessively wide typing + this.deleteEdgesFrom(this._getMetadataNode()); + // @ts-expect-error `Graph` excludes `core_metadata` nodes due to its excessively wide typing + this.addEdge(this._getMetadataNode(), 'metadata', node, 'metadata'); + } + //#endregion + + //#region Util + /** + * Given a model config, return the model metadata field. + * @param modelConfig The model config entity + * @returns + */ + static getModelMetadataField(modelConfig: AnyModelConfig): ModelIdentifierField { + return zModelIdentifierField.parse(modelConfig); + } + + /** + * Given an edge object, return a string representation of the edge. + * @param edge The edge object + */ + static edgeToString(edge: Edge): string; + /** + * Given the source and destination nodes and fields, return a string representation of the edge. + * @param fromNodeId The source node id + * @param fromField The source field + * @param toNodeId The destination node id + * @param toField The destination field + */ + static edgeToString(fromNodeId: string, fromField: string, toNodeId: string, toField: string): string; + static edgeToString(fromNodeId: string | Edge, fromField?: string, toNodeId?: string, toField?: string): string { + if (typeof fromNodeId === 'object') { + const e = fromNodeId; + return `${e.source.node_id}.${e.source.field} -> ${e.destination.node_id}.${e.destination.field}`; + } + assert(fromField !== undefined && toNodeId !== undefined && toField !== undefined, 'Invalid edge arguments'); + return `${fromNodeId}.${fromField} -> ${toNodeId}.${toField}`; + } + //#endregion +} diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlLayers.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlLayers.ts new file mode 100644 index 0000000000..b5e5f8f246 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addControlLayers.ts @@ -0,0 +1,559 @@ +import { getStore } from 'app/store/nanostores/store'; +import type { RootState } from 'app/store/store'; +import { deepClone } from 'common/util/deepClone'; +import { + isControlAdapterLayer, + isInitialImageLayer, + isIPAdapterLayer, + isRegionalGuidanceLayer, + rgLayerMaskImageUploaded, +} from 'features/controlLayers/store/controlLayersSlice'; +import type { InitialImageLayer, Layer, RegionalGuidanceLayer } from 'features/controlLayers/store/types'; +import type { + ControlNetConfigV2, + ImageWithDims, + IPAdapterConfigV2, + ProcessorConfig, + T2IAdapterConfigV2, +} from 'features/controlLayers/util/controlAdapters'; +import { getRegionalPromptLayerBlobs } from 'features/controlLayers/util/getLayerBlobs'; +import type { ImageField } from 'features/nodes/types/common'; +import { + CONTROL_NET_COLLECT, + IMAGE_TO_LATENTS, + IP_ADAPTER_COLLECT, + PROMPT_REGION_INVERT_TENSOR_MASK_PREFIX, + PROMPT_REGION_MASK_TO_TENSOR_PREFIX, + PROMPT_REGION_NEGATIVE_COND_PREFIX, + PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX, + PROMPT_REGION_POSITIVE_COND_PREFIX, + RESIZE, + T2I_ADAPTER_COLLECT, +} from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { size } from 'lodash-es'; +import { getImageDTO, imagesApi } from 'services/api/endpoints/images'; +import type { BaseModelType, ImageDTO, Invocation } from 'services/api/types'; +import { assert } from 'tsafe'; + +/** + * Adds the control layers to the graph + * @param state The app root state + * @param g The graph to add the layers to + * @param base The base model type + * @param denoise The main denoise node + * @param posCond The positive conditioning node + * @param negCond The negative conditioning node + * @param posCondCollect The positive conditioning collector + * @param negCondCollect The negative conditioning collector + * @param noise The noise node + * @param vaeSource The VAE source (either seamless, vae_loader, main_model_loader, or sdxl_model_loader) + * @returns A promise that resolves to the layers that were added to the graph + */ +export const addControlLayers = async ( + state: RootState, + g: Graph, + base: BaseModelType, + denoise: Invocation<'denoise_latents'>, + posCond: Invocation<'compel'> | Invocation<'sdxl_compel_prompt'>, + negCond: Invocation<'compel'> | Invocation<'sdxl_compel_prompt'>, + posCondCollect: Invocation<'collect'>, + negCondCollect: Invocation<'collect'>, + noise: Invocation<'noise'>, + vaeSource: + | Invocation<'seamless'> + | Invocation<'vae_loader'> + | Invocation<'main_model_loader'> + | Invocation<'sdxl_model_loader'> +): Promise => { + const isSDXL = base === 'sdxl'; + + const validLayers = state.controlLayers.present.layers.filter((l) => isValidLayer(l, base)); + + const validControlAdapters = validLayers.filter(isControlAdapterLayer).map((l) => l.controlAdapter); + for (const ca of validControlAdapters) { + addGlobalControlAdapterToGraph(ca, g, denoise); + } + + const validIPAdapters = validLayers.filter(isIPAdapterLayer).map((l) => l.ipAdapter); + for (const ipAdapter of validIPAdapters) { + addGlobalIPAdapterToGraph(ipAdapter, g, denoise); + } + + const initialImageLayers = validLayers.filter(isInitialImageLayer); + assert(initialImageLayers.length <= 1, 'Only one initial image layer allowed'); + if (initialImageLayers[0]) { + addInitialImageLayerToGraph(state, g, base, denoise, noise, vaeSource, initialImageLayers[0]); + } + // TODO: We should probably just use conditioning collectors by default, and skip all this fanagling with re-routing + // the existing conditioning nodes. + + const validRGLayers = validLayers.filter(isRegionalGuidanceLayer); + const layerIds = validRGLayers.map((l) => l.id); + const blobs = await getRegionalPromptLayerBlobs(layerIds); + assert(size(blobs) === size(layerIds), 'Mismatch between layer IDs and blobs'); + + for (const layer of validRGLayers) { + const blob = blobs[layer.id]; + assert(blob, `Blob for layer ${layer.id} not found`); + // Upload the mask image, or get the cached image if it exists + const { image_name } = await getMaskImage(layer, blob); + + // The main mask-to-tensor node + const maskToTensor = g.addNode({ + id: `${PROMPT_REGION_MASK_TO_TENSOR_PREFIX}_${layer.id}`, + type: 'alpha_mask_to_tensor', + image: { + image_name, + }, + }); + + if (layer.positivePrompt) { + // The main positive conditioning node + const regionalPosCond = g.addNode( + isSDXL + ? { + type: 'sdxl_compel_prompt', + id: `${PROMPT_REGION_POSITIVE_COND_PREFIX}_${layer.id}`, + prompt: layer.positivePrompt, + style: layer.positivePrompt, // TODO: Should we put the positive prompt in both fields? + } + : { + type: 'compel', + id: `${PROMPT_REGION_POSITIVE_COND_PREFIX}_${layer.id}`, + prompt: layer.positivePrompt, + } + ); + // Connect the mask to the conditioning + g.addEdge(maskToTensor, 'mask', regionalPosCond, 'mask'); + // Connect the conditioning to the collector + g.addEdge(regionalPosCond, 'conditioning', posCondCollect, 'item'); + // Copy the connections to the "global" positive conditioning node to the regional cond + if (posCond.type === 'compel') { + for (const edge of g.getEdgesTo(posCond, ['clip', 'mask'])) { + // Clone the edge, but change the destination node to the regional conditioning node + const clone = deepClone(edge); + clone.destination.node_id = regionalPosCond.id; + g.addEdgeFromObj(clone); + } + } else { + for (const edge of g.getEdgesTo(posCond, ['clip', 'clip2', 'mask'])) { + // Clone the edge, but change the destination node to the regional conditioning node + const clone = deepClone(edge); + clone.destination.node_id = regionalPosCond.id; + g.addEdgeFromObj(clone); + } + } + } + + if (layer.negativePrompt) { + // The main negative conditioning node + const regionalNegCond = g.addNode( + isSDXL + ? { + type: 'sdxl_compel_prompt', + id: `${PROMPT_REGION_NEGATIVE_COND_PREFIX}_${layer.id}`, + prompt: layer.negativePrompt, + style: layer.negativePrompt, + } + : { + type: 'compel', + id: `${PROMPT_REGION_NEGATIVE_COND_PREFIX}_${layer.id}`, + prompt: layer.negativePrompt, + } + ); + // Connect the mask to the conditioning + g.addEdge(maskToTensor, 'mask', regionalNegCond, 'mask'); + // Connect the conditioning to the collector + g.addEdge(regionalNegCond, 'conditioning', negCondCollect, 'item'); + // Copy the connections to the "global" negative conditioning node to the regional cond + if (negCond.type === 'compel') { + for (const edge of g.getEdgesTo(negCond, ['clip', 'mask'])) { + const clone = deepClone(edge); + clone.destination.node_id = regionalNegCond.id; + g.addEdgeFromObj(clone); + } + } else { + for (const edge of g.getEdgesTo(negCond, ['clip', 'clip2', 'mask'])) { + const clone = deepClone(edge); + clone.destination.node_id = regionalNegCond.id; + g.addEdgeFromObj(clone); + } + } + } + + // If we are using the "invert" auto-negative setting, we need to add an additional negative conditioning node + if (layer.autoNegative === 'invert' && layer.positivePrompt) { + // We re-use the mask image, but invert it when converting to tensor + const invertTensorMask = g.addNode({ + id: `${PROMPT_REGION_INVERT_TENSOR_MASK_PREFIX}_${layer.id}`, + type: 'invert_tensor_mask', + }); + // Connect the OG mask image to the inverted mask-to-tensor node + g.addEdge(maskToTensor, 'mask', invertTensorMask, 'mask'); + // Create the conditioning node. It's going to be connected to the negative cond collector, but it uses the positive prompt + const regionalPosCondInverted = g.addNode( + isSDXL + ? { + type: 'sdxl_compel_prompt', + id: `${PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX}_${layer.id}`, + prompt: layer.positivePrompt, + style: layer.positivePrompt, + } + : { + type: 'compel', + id: `${PROMPT_REGION_POSITIVE_COND_INVERTED_PREFIX}_${layer.id}`, + prompt: layer.positivePrompt, + } + ); + // Connect the inverted mask to the conditioning + g.addEdge(invertTensorMask, 'mask', regionalPosCondInverted, 'mask'); + // Connect the conditioning to the negative collector + g.addEdge(regionalPosCondInverted, 'conditioning', negCondCollect, 'item'); + // Copy the connections to the "global" positive conditioning node to our regional node + if (posCond.type === 'compel') { + for (const edge of g.getEdgesTo(posCond, ['clip', 'mask'])) { + const clone = deepClone(edge); + clone.destination.node_id = regionalPosCondInverted.id; + g.addEdgeFromObj(clone); + } + } else { + for (const edge of g.getEdgesTo(posCond, ['clip', 'clip2', 'mask'])) { + const clone = deepClone(edge); + clone.destination.node_id = regionalPosCondInverted.id; + g.addEdgeFromObj(clone); + } + } + } + + const validRegionalIPAdapters: IPAdapterConfigV2[] = layer.ipAdapters.filter((ipa) => isValidIPAdapter(ipa, base)); + + for (const ipAdapterConfig of validRegionalIPAdapters) { + const ipAdapterCollect = addIPAdapterCollectorSafe(g, denoise); + const { id, weight, model, clipVisionModel, method, beginEndStepPct, image } = ipAdapterConfig; + assert(model, 'IP Adapter model is required'); + assert(image, 'IP Adapter image is required'); + + const ipAdapter = g.addNode({ + id: `ip_adapter_${id}`, + type: 'ip_adapter', + weight, + method, + ip_adapter_model: model, + clip_vision_model: clipVisionModel, + begin_step_percent: beginEndStepPct[0], + end_step_percent: beginEndStepPct[1], + image: { + image_name: image.name, + }, + }); + + // Connect the mask to the conditioning + g.addEdge(maskToTensor, 'mask', ipAdapter, 'mask'); + g.addEdge(ipAdapter, 'ip_adapter', ipAdapterCollect, 'item'); + } + } + + g.upsertMetadata({ control_layers: { layers: validLayers, version: state.controlLayers.present._version } }); + return validLayers; +}; + +//#region Control Adapters +const addGlobalControlAdapterToGraph = ( + controlAdapterConfig: ControlNetConfigV2 | T2IAdapterConfigV2, + g: Graph, + denoise: Invocation<'denoise_latents'> +): void => { + if (controlAdapterConfig.type === 'controlnet') { + addGlobalControlNetToGraph(controlAdapterConfig, g, denoise); + } + if (controlAdapterConfig.type === 't2i_adapter') { + addGlobalT2IAdapterToGraph(controlAdapterConfig, g, denoise); + } +}; + +const addControlNetCollectorSafe = (g: Graph, denoise: Invocation<'denoise_latents'>): Invocation<'collect'> => { + try { + // Attempt to retrieve the collector + const controlNetCollect = g.getNode(CONTROL_NET_COLLECT); + assert(controlNetCollect.type === 'collect'); + return controlNetCollect; + } catch { + // Add the ControlNet collector + const controlNetCollect = g.addNode({ + id: CONTROL_NET_COLLECT, + type: 'collect', + }); + g.addEdge(controlNetCollect, 'collection', denoise, 'control'); + return controlNetCollect; + } +}; + +const addGlobalControlNetToGraph = ( + controlNetConfig: ControlNetConfigV2, + g: Graph, + denoise: Invocation<'denoise_latents'> +) => { + const { id, beginEndStepPct, controlMode, image, model, processedImage, processorConfig, weight } = controlNetConfig; + assert(model, 'ControlNet model is required'); + const controlImage = buildControlImage(image, processedImage, processorConfig); + const controlNetCollect = addControlNetCollectorSafe(g, denoise); + + const controlNet = g.addNode({ + id: `control_net_${id}`, + type: 'controlnet', + begin_step_percent: beginEndStepPct[0], + end_step_percent: beginEndStepPct[1], + control_mode: controlMode, + resize_mode: 'just_resize', + control_model: model, + control_weight: weight, + image: controlImage, + }); + g.addEdge(controlNet, 'control', controlNetCollect, 'item'); +}; + +const addT2IAdapterCollectorSafe = (g: Graph, denoise: Invocation<'denoise_latents'>): Invocation<'collect'> => { + try { + // You see, we've already got one! + const t2iAdapterCollect = g.getNode(T2I_ADAPTER_COLLECT); + assert(t2iAdapterCollect.type === 'collect'); + return t2iAdapterCollect; + } catch { + const t2iAdapterCollect = g.addNode({ + id: T2I_ADAPTER_COLLECT, + type: 'collect', + }); + + g.addEdge(t2iAdapterCollect, 'collection', denoise, 't2i_adapter'); + + return t2iAdapterCollect; + } +}; + +const addGlobalT2IAdapterToGraph = ( + t2iAdapterConfig: T2IAdapterConfigV2, + g: Graph, + denoise: Invocation<'denoise_latents'> +) => { + const { id, beginEndStepPct, image, model, processedImage, processorConfig, weight } = t2iAdapterConfig; + assert(model, 'T2I Adapter model is required'); + const controlImage = buildControlImage(image, processedImage, processorConfig); + const t2iAdapterCollect = addT2IAdapterCollectorSafe(g, denoise); + + const t2iAdapter = g.addNode({ + id: `t2i_adapter_${id}`, + type: 't2i_adapter', + begin_step_percent: beginEndStepPct[0], + end_step_percent: beginEndStepPct[1], + resize_mode: 'just_resize', + t2i_adapter_model: model, + weight: weight, + image: controlImage, + }); + + g.addEdge(t2iAdapter, 't2i_adapter', t2iAdapterCollect, 'item'); +}; + +//#region IP Adapter +const addIPAdapterCollectorSafe = (g: Graph, denoise: Invocation<'denoise_latents'>): Invocation<'collect'> => { + try { + // You see, we've already got one! + const ipAdapterCollect = g.getNode(IP_ADAPTER_COLLECT); + assert(ipAdapterCollect.type === 'collect'); + return ipAdapterCollect; + } catch { + const ipAdapterCollect = g.addNode({ + id: IP_ADAPTER_COLLECT, + type: 'collect', + }); + g.addEdge(ipAdapterCollect, 'collection', denoise, 'ip_adapter'); + return ipAdapterCollect; + } +}; + +const addGlobalIPAdapterToGraph = ( + ipAdapterConfig: IPAdapterConfigV2, + g: Graph, + denoise: Invocation<'denoise_latents'> +) => { + const { id, weight, model, clipVisionModel, method, beginEndStepPct, image } = ipAdapterConfig; + assert(image, 'IP Adapter image is required'); + assert(model, 'IP Adapter model is required'); + const ipAdapterCollect = addIPAdapterCollectorSafe(g, denoise); + + const ipAdapter = g.addNode({ + id: `ip_adapter_${id}`, + type: 'ip_adapter', + weight, + method, + ip_adapter_model: model, + clip_vision_model: clipVisionModel, + begin_step_percent: beginEndStepPct[0], + end_step_percent: beginEndStepPct[1], + image: { + image_name: image.name, + }, + }); + g.addEdge(ipAdapter, 'ip_adapter', ipAdapterCollect, 'item'); +}; +//#endregion + +//#region Initial Image +const addInitialImageLayerToGraph = ( + state: RootState, + g: Graph, + base: BaseModelType, + denoise: Invocation<'denoise_latents'>, + noise: Invocation<'noise'>, + vaeSource: + | Invocation<'seamless'> + | Invocation<'vae_loader'> + | Invocation<'main_model_loader'> + | Invocation<'sdxl_model_loader'>, + layer: InitialImageLayer +) => { + const { vaePrecision } = state.generation; + const { refinerModel, refinerStart } = state.sdxl; + const { width, height } = state.controlLayers.present.size; + assert(layer.isEnabled, 'Initial image layer is not enabled'); + assert(layer.image, 'Initial image layer has no image'); + + const isSDXL = base === 'sdxl'; + const useRefinerStartEnd = isSDXL && Boolean(refinerModel); + + const { denoisingStrength } = layer; + denoise.denoising_start = useRefinerStartEnd ? Math.min(refinerStart, 1 - denoisingStrength) : 1 - denoisingStrength; + denoise.denoising_end = useRefinerStartEnd ? refinerStart : 1; + + const i2l = g.addNode({ + type: 'i2l', + id: IMAGE_TO_LATENTS, + fp32: vaePrecision === 'fp32', + }); + + g.addEdge(i2l, 'latents', denoise, 'latents'); + g.addEdge(vaeSource, 'vae', i2l, 'vae'); + + if (layer.image.width !== width || layer.image.height !== height) { + // The init image needs to be resized to the specified width and height before being passed to `IMAGE_TO_LATENTS` + + // Create a resize node, explicitly setting its image + const resize = g.addNode({ + id: RESIZE, + type: 'img_resize', + image: { + image_name: layer.image.name, + }, + width, + height, + }); + + // The `RESIZE` node then passes its image to `IMAGE_TO_LATENTS` + g.addEdge(resize, 'image', i2l, 'image'); + // The `RESIZE` node also passes its width and height to `NOISE` + g.addEdge(resize, 'width', noise, 'width'); + g.addEdge(resize, 'height', noise, 'height'); + } else { + // We are not resizing, so we need to set the image on the `IMAGE_TO_LATENTS` node explicitly + i2l.image = { + image_name: layer.image.name, + }; + + // Pass the image's dimensions to the `NOISE` node + g.addEdge(i2l, 'width', noise, 'width'); + g.addEdge(i2l, 'height', noise, 'height'); + } + + g.upsertMetadata({ generation_mode: isSDXL ? 'sdxl_img2img' : 'img2img' }); +}; +//#endregion + +//#region Layer validators +const isValidControlAdapter = (ca: ControlNetConfigV2 | T2IAdapterConfigV2, base: BaseModelType): boolean => { + // Must be have a model that matches the current base and must have a control image + const hasModel = Boolean(ca.model); + const modelMatchesBase = ca.model?.base === base; + const hasControlImage = Boolean(ca.image || (ca.processedImage && ca.processorConfig)); + return hasModel && modelMatchesBase && hasControlImage; +}; + +const isValidIPAdapter = (ipa: IPAdapterConfigV2, base: BaseModelType): boolean => { + // Must be have a model that matches the current base and must have a control image + const hasModel = Boolean(ipa.model); + const modelMatchesBase = ipa.model?.base === base; + const hasImage = Boolean(ipa.image); + return hasModel && modelMatchesBase && hasImage; +}; + +const isValidLayer = (layer: Layer, base: BaseModelType) => { + if (isControlAdapterLayer(layer)) { + if (!layer.isEnabled) { + return false; + } + return isValidControlAdapter(layer.controlAdapter, base); + } + if (isIPAdapterLayer(layer)) { + if (!layer.isEnabled) { + return false; + } + return isValidIPAdapter(layer.ipAdapter, base); + } + if (isInitialImageLayer(layer)) { + if (!layer.isEnabled) { + return false; + } + if (!layer.image) { + return false; + } + return true; + } + if (isRegionalGuidanceLayer(layer)) { + const hasTextPrompt = Boolean(layer.positivePrompt || layer.negativePrompt); + const hasIPAdapter = layer.ipAdapters.filter((ipa) => isValidIPAdapter(ipa, base)).length > 0; + return hasTextPrompt || hasIPAdapter; + } + return false; +}; +//#endregion + +//#region Helpers +const getMaskImage = async (layer: RegionalGuidanceLayer, blob: Blob): Promise => { + if (layer.uploadedMaskImage) { + const imageDTO = await getImageDTO(layer.uploadedMaskImage.name); + if (imageDTO) { + return imageDTO; + } + } + const { dispatch } = getStore(); + // No cached mask, or the cached image no longer exists - we need to upload the mask image + const file = new File([blob], `${layer.id}_mask.png`, { type: 'image/png' }); + const req = dispatch( + imagesApi.endpoints.uploadImage.initiate({ file, image_category: 'mask', is_intermediate: true }) + ); + req.reset(); + + const imageDTO = await req.unwrap(); + dispatch(rgLayerMaskImageUploaded({ layerId: layer.id, imageDTO })); + return imageDTO; +}; + +const buildControlImage = ( + image: ImageWithDims | null, + processedImage: ImageWithDims | null, + processorConfig: ProcessorConfig | null +): ImageField => { + if (processedImage && processorConfig) { + // We've processed the image in the app - use it for the control image. + return { + image_name: processedImage.name, + }; + } else if (image) { + // No processor selected, and we have an image - the user provided a processed image, use it for the control image. + return { + image_name: image.name, + }; + } + assert(false, 'Attempted to add unprocessed control image'); +}; +//#endregion diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addHRF.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addHRF.ts new file mode 100644 index 0000000000..68286e337c --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addHRF.ts @@ -0,0 +1,166 @@ +import type { RootState } from 'app/store/store'; +import { deepClone } from 'common/util/deepClone'; +import { roundToMultiple } from 'common/util/roundDownToMultiple'; +import { + DENOISE_LATENTS_HRF, + ESRGAN_HRF, + IMAGE_TO_LATENTS_HRF, + LATENTS_TO_IMAGE_HRF_HR, + LATENTS_TO_IMAGE_HRF_LR, + NOISE_HRF, + RESIZE_HRF, +} from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { getBoardField } from 'features/nodes/util/graph/graphBuilderUtils'; +import { selectOptimalDimension } from 'features/parameters/store/generationSlice'; +import type { Invocation } from 'services/api/types'; + +/** + * Calculates the new resolution for high-resolution features (HRF) based on base model type. + * Adjusts the width and height to maintain the aspect ratio and constrains them by the model's dimension limits, + * rounding down to the nearest multiple of 8. + * + * @param {number} optimalDimension The optimal dimension for the base model. + * @param {number} width The current width to be adjusted for HRF. + * @param {number} height The current height to be adjusted for HRF. + * @return {{newWidth: number, newHeight: number}} The new width and height, adjusted and rounded as needed. + */ +function calculateHrfRes( + optimalDimension: number, + width: number, + height: number +): { newWidth: number; newHeight: number } { + const aspect = width / height; + + const minDimension = Math.floor(optimalDimension * 0.5); + const modelArea = optimalDimension * optimalDimension; // Assuming square images for model_area + + let initWidth; + let initHeight; + + if (aspect > 1.0) { + initHeight = Math.max(minDimension, Math.sqrt(modelArea / aspect)); + initWidth = initHeight * aspect; + } else { + initWidth = Math.max(minDimension, Math.sqrt(modelArea * aspect)); + initHeight = initWidth / aspect; + } + // Cap initial height and width to final height and width. + initWidth = Math.min(width, initWidth); + initHeight = Math.min(height, initHeight); + + const newWidth = roundToMultiple(Math.floor(initWidth), 8); + const newHeight = roundToMultiple(Math.floor(initHeight), 8); + + return { newWidth, newHeight }; +} + +/** + * Adds HRF to the graph. + * @param state The root redux state + * @param g The graph to add HRF to + * @param denoise The denoise node + * @param noise The noise node + * @param l2i The l2i node + * @param vaeSource The VAE source node (may be a model loader, VAE loader, or seamless node) + * @returns The HRF image output node. + */ +export const addHRF = ( + state: RootState, + g: Graph, + denoise: Invocation<'denoise_latents'>, + noise: Invocation<'noise'>, + l2i: Invocation<'l2i'>, + vaeSource: Invocation<'vae_loader'> | Invocation<'main_model_loader'> | Invocation<'seamless'> +): Invocation<'l2i'> => { + const { hrfStrength, hrfEnabled, hrfMethod } = state.hrf; + const { width, height } = state.controlLayers.present.size; + const optimalDimension = selectOptimalDimension(state); + const { newWidth: hrfWidth, newHeight: hrfHeight } = calculateHrfRes(optimalDimension, width, height); + + // Change height and width of original noise node to initial resolution. + if (noise) { + noise.width = hrfWidth; + noise.height = hrfHeight; + } + + // Define new nodes and their connections, roughly in order of operations. + const l2iHrfLR = g.addNode({ type: 'l2i', id: LATENTS_TO_IMAGE_HRF_LR, fp32: l2i.fp32 }); + g.addEdge(denoise, 'latents', l2iHrfLR, 'latents'); + g.addEdge(vaeSource, 'vae', l2iHrfLR, 'vae'); + + const resizeHrf = g.addNode({ + id: RESIZE_HRF, + type: 'img_resize', + width: width, + height: height, + }); + + if (hrfMethod === 'ESRGAN') { + let model_name: Invocation<'esrgan'>['model_name'] = 'RealESRGAN_x2plus.pth'; + if ((width * height) / (hrfWidth * hrfHeight) > 2) { + model_name = 'RealESRGAN_x4plus.pth'; + } + const esrganHrf = g.addNode({ id: ESRGAN_HRF, type: 'esrgan', model_name }); + g.addEdge(l2iHrfLR, 'image', esrganHrf, 'image'); + g.addEdge(esrganHrf, 'image', resizeHrf, 'image'); + } else { + g.addEdge(l2iHrfLR, 'image', resizeHrf, 'image'); + } + + const noiseHrf = g.addNode({ + type: 'noise', + id: NOISE_HRF, + seed: noise.seed, + use_cpu: noise.use_cpu, + }); + g.addEdge(resizeHrf, 'height', noiseHrf, 'height'); + g.addEdge(resizeHrf, 'width', noiseHrf, 'width'); + + const i2lHrf = g.addNode({ type: 'i2l', id: IMAGE_TO_LATENTS_HRF }); + g.addEdge(vaeSource, 'vae', i2lHrf, 'vae'); + g.addEdge(resizeHrf, 'image', i2lHrf, 'image'); + + const denoiseHrf = g.addNode({ + type: 'denoise_latents', + id: DENOISE_LATENTS_HRF, + cfg_scale: denoise.cfg_scale, + scheduler: denoise.scheduler, + steps: denoise.steps, + denoising_start: 1 - hrfStrength, + denoising_end: 1, + }); + g.addEdge(i2lHrf, 'latents', denoiseHrf, 'latents'); + g.addEdge(noiseHrf, 'noise', denoiseHrf, 'noise'); + + // Copy edges to the original denoise into the new denoise + g.getEdgesTo(denoise, ['control', 'ip_adapter', 'unet', 'positive_conditioning', 'negative_conditioning']).forEach( + (edge) => { + const clone = deepClone(edge); + clone.destination.node_id = denoiseHrf.id; + g.addEdgeFromObj(clone); + } + ); + + // The original l2i node is unnecessary now, remove it + g.deleteNode(l2i.id); + + const l2iHrfHR = g.addNode({ + type: 'l2i', + id: LATENTS_TO_IMAGE_HRF_HR, + fp32: l2i.fp32, + is_intermediate: false, + board: getBoardField(state), + }); + g.addEdge(vaeSource, 'vae', l2iHrfHR, 'vae'); + g.addEdge(denoiseHrf, 'latents', l2iHrfHR, 'latents'); + + g.upsertMetadata({ + hrf_strength: hrfStrength, + hrf_enabled: hrfEnabled, + hrf_method: hrfMethod, + }); + g.setMetadataReceivingNode(l2iHrfHR); + + return l2iHrfHR; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts new file mode 100644 index 0000000000..3623343367 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addLoRAs.ts @@ -0,0 +1,71 @@ +import type { RootState } from 'app/store/store'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import { LORA_LOADER } from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { filter, size } from 'lodash-es'; +import type { Invocation, S } from 'services/api/types'; + +export const addLoRAs = ( + state: RootState, + g: Graph, + denoise: Invocation<'denoise_latents'>, + modelLoader: Invocation<'main_model_loader'>, + seamless: Invocation<'seamless'> | null, + clipSkip: Invocation<'clip_skip'>, + posCond: Invocation<'compel'>, + negCond: Invocation<'compel'> +): void => { + const enabledLoRAs = filter(state.lora.loras, (l) => l.isEnabled ?? false); + const loraCount = size(enabledLoRAs); + + if (loraCount === 0) { + return; + } + + const loraMetadata: S['LoRAMetadataField'][] = []; + + // We will collect LoRAs into a single collection node, then pass them to the LoRA collection loader, which applies + // each LoRA to the UNet and CLIP. + const loraCollector = g.addNode({ + id: `${LORA_LOADER}_collect`, + type: 'collect', + }); + const loraCollectionLoader = g.addNode({ + id: LORA_LOADER, + type: 'lora_collection_loader', + }); + + g.addEdge(loraCollector, 'collection', loraCollectionLoader, 'loras'); + // Use seamless as UNet input if it exists, otherwise use the model loader + g.addEdge(seamless ?? modelLoader, 'unet', loraCollectionLoader, 'unet'); + g.addEdge(clipSkip, 'clip', loraCollectionLoader, 'clip'); + // Reroute UNet & CLIP connections through the LoRA collection loader + g.deleteEdgesTo(denoise, ['unet']); + g.deleteEdgesTo(posCond, ['clip']); + g.deleteEdgesTo(negCond, ['clip']); + g.addEdge(loraCollectionLoader, 'unet', denoise, 'unet'); + g.addEdge(loraCollectionLoader, 'clip', posCond, 'clip'); + g.addEdge(loraCollectionLoader, 'clip', negCond, 'clip'); + + for (const lora of enabledLoRAs) { + const { weight } = lora; + const { key } = lora.model; + const parsedModel = zModelIdentifierField.parse(lora.model); + + const loraSelector = g.addNode({ + type: 'lora_selector', + id: `${LORA_LOADER}_${key}`, + lora: parsedModel, + weight, + }); + + loraMetadata.push({ + model: parsedModel, + weight, + }); + + g.addEdge(loraSelector, 'lora', loraCollector, 'item'); + } + + g.upsertMetadata({ loras: loraMetadata }); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addNSFWChecker.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addNSFWChecker.ts new file mode 100644 index 0000000000..7850413195 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addNSFWChecker.ts @@ -0,0 +1,31 @@ +import { NSFW_CHECKER } from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation } from 'services/api/types'; + +/** + * Adds the NSFW checker to the output image + * @param g The graph + * @param imageOutput The current image output node + * @returns The nsfw checker node + */ +export const addNSFWChecker = ( + g: Graph, + imageOutput: Invocation<'l2i'> | Invocation<'img_nsfw'> | Invocation<'img_watermark'> +): Invocation<'img_nsfw'> => { + const nsfw = g.addNode({ + id: NSFW_CHECKER, + type: 'img_nsfw', + is_intermediate: imageOutput.is_intermediate, + board: imageOutput.board, + use_cache: false, + }); + + // The NSFW checker node is the new image output - make the previous one intermediate + imageOutput.is_intermediate = true; + imageOutput.use_cache = true; + imageOutput.board = undefined; + + g.addEdge(imageOutput, 'image', nsfw, 'image'); + + return nsfw; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts new file mode 100644 index 0000000000..f38e8de570 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLLoRAs.ts @@ -0,0 +1,73 @@ +import type { RootState } from 'app/store/store'; +import { zModelIdentifierField } from 'features/nodes/types/common'; +import { LORA_LOADER } from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { filter, size } from 'lodash-es'; +import type { Invocation, S } from 'services/api/types'; + +export const addSDXLLoRas = ( + state: RootState, + g: Graph, + denoise: Invocation<'denoise_latents'>, + modelLoader: Invocation<'sdxl_model_loader'>, + seamless: Invocation<'seamless'> | null, + posCond: Invocation<'sdxl_compel_prompt'>, + negCond: Invocation<'sdxl_compel_prompt'> +): void => { + const enabledLoRAs = filter(state.lora.loras, (l) => l.isEnabled ?? false); + const loraCount = size(enabledLoRAs); + + if (loraCount === 0) { + return; + } + + const loraMetadata: S['LoRAMetadataField'][] = []; + + // We will collect LoRAs into a single collection node, then pass them to the LoRA collection loader, which applies + // each LoRA to the UNet and CLIP. + const loraCollector = g.addNode({ + id: `${LORA_LOADER}_collect`, + type: 'collect', + }); + const loraCollectionLoader = g.addNode({ + id: LORA_LOADER, + type: 'sdxl_lora_collection_loader', + }); + + g.addEdge(loraCollector, 'collection', loraCollectionLoader, 'loras'); + // Use seamless as UNet input if it exists, otherwise use the model loader + g.addEdge(seamless ?? modelLoader, 'unet', loraCollectionLoader, 'unet'); + g.addEdge(modelLoader, 'clip', loraCollectionLoader, 'clip'); + g.addEdge(modelLoader, 'clip2', loraCollectionLoader, 'clip2'); + // Reroute UNet & CLIP connections through the LoRA collection loader + g.deleteEdgesTo(denoise, ['unet']); + g.deleteEdgesTo(posCond, ['clip', 'clip2']); + g.deleteEdgesTo(negCond, ['clip', 'clip2']); + g.addEdge(loraCollectionLoader, 'unet', denoise, 'unet'); + g.addEdge(loraCollectionLoader, 'clip', posCond, 'clip'); + g.addEdge(loraCollectionLoader, 'clip', negCond, 'clip'); + g.addEdge(loraCollectionLoader, 'clip2', posCond, 'clip2'); + g.addEdge(loraCollectionLoader, 'clip2', negCond, 'clip2'); + + for (const lora of enabledLoRAs) { + const { weight } = lora; + const { key } = lora.model; + const parsedModel = zModelIdentifierField.parse(lora.model); + + const loraSelector = g.addNode({ + type: 'lora_selector', + id: `${LORA_LOADER}_${key}`, + lora: parsedModel, + weight, + }); + + loraMetadata.push({ + model: parsedModel, + weight, + }); + + g.addEdge(loraSelector, 'lora', loraCollector, 'item'); + } + + g.upsertMetadata({ loras: loraMetadata }); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLRefiner.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLRefiner.ts new file mode 100644 index 0000000000..caab153b60 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSDXLRefiner.ts @@ -0,0 +1,100 @@ +import type { RootState } from 'app/store/store'; +import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; +import { getModelMetadataField } from 'features/nodes/util/graph/canvas/metadata'; +import { + SDXL_REFINER_DENOISE_LATENTS, + SDXL_REFINER_MODEL_LOADER, + SDXL_REFINER_NEGATIVE_CONDITIONING, + SDXL_REFINER_POSITIVE_CONDITIONING, + SDXL_REFINER_SEAMLESS, +} from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation } from 'services/api/types'; +import { isRefinerMainModelModelConfig } from 'services/api/types'; +import { assert } from 'tsafe'; + +export const addSDXLRefiner = async ( + state: RootState, + g: Graph, + denoise: Invocation<'denoise_latents'>, + seamless: Invocation<'seamless'> | null, + posCond: Invocation<'sdxl_compel_prompt'>, + negCond: Invocation<'sdxl_compel_prompt'>, + l2i: Invocation<'l2i'> +): Promise => { + const { + refinerModel, + refinerPositiveAestheticScore, + refinerNegativeAestheticScore, + refinerSteps, + refinerScheduler, + refinerCFGScale, + refinerStart, + } = state.sdxl; + + assert(refinerModel, 'No refiner model found in state'); + + const modelConfig = await fetchModelConfigWithTypeGuard(refinerModel.key, isRefinerMainModelModelConfig); + + // We need to re-route latents to the refiner + g.deleteEdgesFrom(denoise, ['latents']); + // Latents will now come from refiner - delete edges to the l2i VAE decode + g.deleteEdgesTo(l2i, ['latents']); + + const refinerModelLoader = g.addNode({ + type: 'sdxl_refiner_model_loader', + id: SDXL_REFINER_MODEL_LOADER, + model: refinerModel, + }); + const refinerPosCond = g.addNode({ + type: 'sdxl_refiner_compel_prompt', + id: SDXL_REFINER_POSITIVE_CONDITIONING, + style: posCond.style, + aesthetic_score: refinerPositiveAestheticScore, + }); + const refinerNegCond = g.addNode({ + type: 'sdxl_refiner_compel_prompt', + id: SDXL_REFINER_NEGATIVE_CONDITIONING, + style: negCond.style, + aesthetic_score: refinerNegativeAestheticScore, + }); + const refinerDenoise = g.addNode({ + type: 'denoise_latents', + id: SDXL_REFINER_DENOISE_LATENTS, + cfg_scale: refinerCFGScale, + steps: refinerSteps, + scheduler: refinerScheduler, + denoising_start: refinerStart, + denoising_end: 1, + }); + + if (seamless) { + const refinerSeamless = g.addNode({ + id: SDXL_REFINER_SEAMLESS, + type: 'seamless', + seamless_x: seamless.seamless_x, + seamless_y: seamless.seamless_y, + }); + g.addEdge(refinerModelLoader, 'unet', refinerSeamless, 'unet'); + g.addEdge(refinerSeamless, 'unet', refinerDenoise, 'unet'); + } else { + g.addEdge(refinerModelLoader, 'unet', refinerDenoise, 'unet'); + } + + g.addEdge(refinerModelLoader, 'clip2', refinerPosCond, 'clip2'); + g.addEdge(refinerModelLoader, 'clip2', refinerNegCond, 'clip2'); + g.addEdge(refinerPosCond, 'conditioning', refinerDenoise, 'positive_conditioning'); + g.addEdge(refinerNegCond, 'conditioning', refinerDenoise, 'negative_conditioning'); + g.addEdge(denoise, 'latents', refinerDenoise, 'latents'); + g.addEdge(refinerDenoise, 'latents', l2i, 'latents'); + + g.upsertMetadata({ + refiner_model: getModelMetadataField(modelConfig), + refiner_positive_aesthetic_score: refinerPositiveAestheticScore, + refiner_negative_aesthetic_score: refinerNegativeAestheticScore, + refiner_cfg_scale: refinerCFGScale, + refiner_scheduler: refinerScheduler, + refiner_start: refinerStart, + refiner_steps: refinerSteps, + }); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSeamless.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSeamless.ts new file mode 100644 index 0000000000..25a3e7e3ac --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addSeamless.ts @@ -0,0 +1,51 @@ +import type { RootState } from 'app/store/store'; +import { SEAMLESS } from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation } from 'services/api/types'; + +/** + * Adds the seamless node to the graph and connects it to the model loader and denoise node. + * Because the seamless node may insert a VAE loader node between the model loader and itself, + * future nodes should be connected to the return value of this function. + * @param state The current Redux state + * @param g The graph to add the seamless node to + * @param denoise The denoise node in the graph + * @param modelLoader The model loader node in the graph + * @param vaeLoader The VAE loader node in the graph, if it exists + * @returns The seamless node, if it was added to the graph + */ +export const addSeamless = ( + state: RootState, + g: Graph, + denoise: Invocation<'denoise_latents'>, + modelLoader: Invocation<'main_model_loader'> | Invocation<'sdxl_model_loader'>, + vaeLoader: Invocation<'vae_loader'> | null +): Invocation<'seamless'> | null => { + const { seamlessXAxis: seamless_x, seamlessYAxis: seamless_y } = state.generation; + + if (!seamless_x && !seamless_y) { + return null; + } + + const seamless = g.addNode({ + id: SEAMLESS, + type: 'seamless', + seamless_x, + seamless_y, + }); + + g.upsertMetadata({ + seamless_x: seamless_x || undefined, + seamless_y: seamless_y || undefined, + }); + + // Seamless slots into the graph between the model loader and the denoise node + g.deleteEdgesFrom(modelLoader, ['unet']); + g.deleteEdgesFrom(modelLoader, ['vae']); + + g.addEdge(modelLoader, 'unet', seamless, 'unet'); + g.addEdge(vaeLoader ?? modelLoader, 'vae', seamless, 'vae'); + g.addEdge(seamless, 'unet', denoise, 'unet'); + + return seamless; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWatermarker.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWatermarker.ts new file mode 100644 index 0000000000..2a7af866f8 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/addWatermarker.ts @@ -0,0 +1,31 @@ +import { WATERMARKER } from 'features/nodes/util/graph/constants'; +import type { Graph } from 'features/nodes/util/graph/generation/Graph'; +import type { Invocation } from 'services/api/types'; + +/** + * Adds a watermark to the output image + * @param g The graph + * @param imageOutput The image output node + * @returns The watermark node + */ +export const addWatermarker = ( + g: Graph, + imageOutput: Invocation<'l2i'> | Invocation<'img_nsfw'> | Invocation<'img_watermark'> +): Invocation<'img_watermark'> => { + const watermark = g.addNode({ + id: WATERMARKER, + type: 'img_watermark', + is_intermediate: imageOutput.is_intermediate, + board: imageOutput.board, + use_cache: false, + }); + + // The watermarker node is the new image output - make the previous one intermediate + imageOutput.is_intermediate = true; + imageOutput.use_cache = true; + imageOutput.board = undefined; + + g.addEdge(imageOutput, 'image', watermark, 'image'); + + return watermark; +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabGraph.ts new file mode 100644 index 0000000000..d6afbace72 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabGraph.ts @@ -0,0 +1,182 @@ +import type { RootState } from 'app/store/store'; +import { isInitialImageLayer, isRegionalGuidanceLayer } from 'features/controlLayers/store/controlLayersSlice'; +import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; +import { + CLIP_SKIP, + CONTROL_LAYERS_GRAPH, + DENOISE_LATENTS, + LATENTS_TO_IMAGE, + MAIN_MODEL_LOADER, + NEGATIVE_CONDITIONING, + NEGATIVE_CONDITIONING_COLLECT, + NOISE, + POSITIVE_CONDITIONING, + POSITIVE_CONDITIONING_COLLECT, + VAE_LOADER, +} from 'features/nodes/util/graph/constants'; +import { addControlLayers } from 'features/nodes/util/graph/generation/addControlLayers'; +import { addHRF } from 'features/nodes/util/graph/generation/addHRF'; +import { addLoRAs } from 'features/nodes/util/graph/generation/addLoRAs'; +import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChecker'; +import { addSeamless } from 'features/nodes/util/graph/generation/addSeamless'; +import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker'; +import type { GraphType } from 'features/nodes/util/graph/generation/Graph'; +import { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { getBoardField } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { Invocation } from 'services/api/types'; +import { isNonRefinerMainModelConfig } from 'services/api/types'; +import { assert } from 'tsafe'; + +export const buildGenerationTabGraph = async (state: RootState): Promise => { + const { + model, + cfgScale: cfg_scale, + cfgRescaleMultiplier: cfg_rescale_multiplier, + scheduler, + steps, + clipSkip: skipped_layers, + shouldUseCpuNoise, + vaePrecision, + seed, + vae, + } = state.generation; + const { positivePrompt, negativePrompt } = state.controlLayers.present; + const { width, height } = state.controlLayers.present.size; + + assert(model, 'No model found in state'); + + const g = new Graph(CONTROL_LAYERS_GRAPH); + const modelLoader = g.addNode({ + type: 'main_model_loader', + id: MAIN_MODEL_LOADER, + model, + }); + const clipSkip = g.addNode({ + type: 'clip_skip', + id: CLIP_SKIP, + skipped_layers, + }); + const posCond = g.addNode({ + type: 'compel', + id: POSITIVE_CONDITIONING, + prompt: positivePrompt, + }); + const posCondCollect = g.addNode({ + type: 'collect', + id: POSITIVE_CONDITIONING_COLLECT, + }); + const negCond = g.addNode({ + type: 'compel', + id: NEGATIVE_CONDITIONING, + prompt: negativePrompt, + }); + const negCondCollect = g.addNode({ + type: 'collect', + id: NEGATIVE_CONDITIONING_COLLECT, + }); + const noise = g.addNode({ + type: 'noise', + id: NOISE, + seed, + width, + height, + use_cpu: shouldUseCpuNoise, + }); + const denoise = g.addNode({ + type: 'denoise_latents', + id: DENOISE_LATENTS, + cfg_scale, + cfg_rescale_multiplier, + scheduler, + steps, + denoising_start: 0, + denoising_end: 1, + }); + const l2i = g.addNode({ + type: 'l2i', + id: LATENTS_TO_IMAGE, + fp32: vaePrecision === 'fp32', + board: getBoardField(state), + // This is the terminal node and must always save to gallery. + is_intermediate: false, + use_cache: false, + }); + const vaeLoader = + vae?.base === model.base + ? g.addNode({ + type: 'vae_loader', + id: VAE_LOADER, + vae_model: vae, + }) + : null; + + let imageOutput: Invocation<'l2i'> | Invocation<'img_nsfw'> | Invocation<'img_watermark'> = l2i; + + g.addEdge(modelLoader, 'unet', denoise, 'unet'); + g.addEdge(modelLoader, 'clip', clipSkip, 'clip'); + g.addEdge(clipSkip, 'clip', posCond, 'clip'); + g.addEdge(clipSkip, 'clip', negCond, 'clip'); + g.addEdge(posCond, 'conditioning', posCondCollect, 'item'); + g.addEdge(negCond, 'conditioning', negCondCollect, 'item'); + g.addEdge(posCondCollect, 'collection', denoise, 'positive_conditioning'); + g.addEdge(negCondCollect, 'collection', denoise, 'negative_conditioning'); + g.addEdge(noise, 'noise', denoise, 'noise'); + g.addEdge(denoise, 'latents', l2i, 'latents'); + + const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig); + assert(modelConfig.base === 'sd-1' || modelConfig.base === 'sd-2'); + + g.upsertMetadata({ + generation_mode: 'txt2img', + cfg_scale, + cfg_rescale_multiplier, + height, + width, + positive_prompt: positivePrompt, + negative_prompt: negativePrompt, + model: Graph.getModelMetadataField(modelConfig), + seed, + steps, + rand_device: shouldUseCpuNoise ? 'cpu' : 'cuda', + scheduler, + clip_skip: skipped_layers, + vae: vae ?? undefined, + }); + + const seamless = addSeamless(state, g, denoise, modelLoader, vaeLoader); + + addLoRAs(state, g, denoise, modelLoader, seamless, clipSkip, posCond, negCond); + + // We might get the VAE from the main model, custom VAE, or seamless node. + const vaeSource = seamless ?? vaeLoader ?? modelLoader; + g.addEdge(vaeSource, 'vae', l2i, 'vae'); + + const addedLayers = await addControlLayers( + state, + g, + modelConfig.base, + denoise, + posCond, + negCond, + posCondCollect, + negCondCollect, + noise, + vaeSource + ); + + const isHRFAllowed = !addedLayers.some((l) => isInitialImageLayer(l) || isRegionalGuidanceLayer(l)); + if (isHRFAllowed && state.hrf.hrfEnabled) { + imageOutput = addHRF(state, g, denoise, noise, l2i, vaeSource); + } + + if (state.system.shouldUseNSFWChecker) { + imageOutput = addNSFWChecker(g, imageOutput); + } + + if (state.system.shouldUseWatermarker) { + imageOutput = addWatermarker(g, imageOutput); + } + + g.setMetadataReceivingNode(imageOutput); + return g.getGraph(); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts new file mode 100644 index 0000000000..416e81a632 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/graph/generation/buildGenerationTabSDXLGraph.ts @@ -0,0 +1,174 @@ +import type { RootState } from 'app/store/store'; +import { fetchModelConfigWithTypeGuard } from 'features/metadata/util/modelFetchingHelpers'; +import { + LATENTS_TO_IMAGE, + NEGATIVE_CONDITIONING, + NEGATIVE_CONDITIONING_COLLECT, + NOISE, + POSITIVE_CONDITIONING, + POSITIVE_CONDITIONING_COLLECT, + SDXL_CONTROL_LAYERS_GRAPH, + SDXL_DENOISE_LATENTS, + SDXL_MODEL_LOADER, + VAE_LOADER, +} from 'features/nodes/util/graph/constants'; +import { addControlLayers } from 'features/nodes/util/graph/generation/addControlLayers'; +import { addNSFWChecker } from 'features/nodes/util/graph/generation/addNSFWChecker'; +import { addSDXLLoRas } from 'features/nodes/util/graph/generation/addSDXLLoRAs'; +import { addSDXLRefiner } from 'features/nodes/util/graph/generation/addSDXLRefiner'; +import { addSeamless } from 'features/nodes/util/graph/generation/addSeamless'; +import { addWatermarker } from 'features/nodes/util/graph/generation/addWatermarker'; +import { Graph } from 'features/nodes/util/graph/generation/Graph'; +import { getBoardField, getSDXLStylePrompts } from 'features/nodes/util/graph/graphBuilderUtils'; +import type { Invocation, NonNullableGraph } from 'services/api/types'; +import { isNonRefinerMainModelConfig } from 'services/api/types'; +import { assert } from 'tsafe'; + +export const buildGenerationTabSDXLGraph = async (state: RootState): Promise => { + const { + model, + cfgScale: cfg_scale, + cfgRescaleMultiplier: cfg_rescale_multiplier, + scheduler, + seed, + steps, + shouldUseCpuNoise, + vaePrecision, + vae, + } = state.generation; + const { positivePrompt, negativePrompt } = state.controlLayers.present; + const { width, height } = state.controlLayers.present.size; + + const { refinerModel, refinerStart } = state.sdxl; + + assert(model, 'No model found in state'); + + const { positiveStylePrompt, negativeStylePrompt } = getSDXLStylePrompts(state); + + const g = new Graph(SDXL_CONTROL_LAYERS_GRAPH); + const modelLoader = g.addNode({ + type: 'sdxl_model_loader', + id: SDXL_MODEL_LOADER, + model, + }); + const posCond = g.addNode({ + type: 'sdxl_compel_prompt', + id: POSITIVE_CONDITIONING, + prompt: positivePrompt, + style: positiveStylePrompt, + }); + const posCondCollect = g.addNode({ + type: 'collect', + id: POSITIVE_CONDITIONING_COLLECT, + }); + const negCond = g.addNode({ + type: 'sdxl_compel_prompt', + id: NEGATIVE_CONDITIONING, + prompt: negativePrompt, + style: negativeStylePrompt, + }); + const negCondCollect = g.addNode({ + type: 'collect', + id: NEGATIVE_CONDITIONING_COLLECT, + }); + const noise = g.addNode({ type: 'noise', id: NOISE, seed, width, height, use_cpu: shouldUseCpuNoise }); + const denoise = g.addNode({ + type: 'denoise_latents', + id: SDXL_DENOISE_LATENTS, + cfg_scale, + cfg_rescale_multiplier, + scheduler, + steps, + denoising_start: 0, + denoising_end: refinerModel ? refinerStart : 1, + }); + const l2i = g.addNode({ + type: 'l2i', + id: LATENTS_TO_IMAGE, + fp32: vaePrecision === 'fp32', + board: getBoardField(state), + // This is the terminal node and must always save to gallery. + is_intermediate: false, + use_cache: false, + }); + const vaeLoader = + vae?.base === model.base + ? g.addNode({ + type: 'vae_loader', + id: VAE_LOADER, + vae_model: vae, + }) + : null; + + let imageOutput: Invocation<'l2i'> | Invocation<'img_nsfw'> | Invocation<'img_watermark'> = l2i; + + g.addEdge(modelLoader, 'unet', denoise, 'unet'); + g.addEdge(modelLoader, 'clip', posCond, 'clip'); + g.addEdge(modelLoader, 'clip', negCond, 'clip'); + g.addEdge(modelLoader, 'clip2', posCond, 'clip2'); + g.addEdge(modelLoader, 'clip2', negCond, 'clip2'); + g.addEdge(posCond, 'conditioning', posCondCollect, 'item'); + g.addEdge(negCond, 'conditioning', negCondCollect, 'item'); + g.addEdge(posCondCollect, 'collection', denoise, 'positive_conditioning'); + g.addEdge(negCondCollect, 'collection', denoise, 'negative_conditioning'); + g.addEdge(noise, 'noise', denoise, 'noise'); + g.addEdge(denoise, 'latents', l2i, 'latents'); + + const modelConfig = await fetchModelConfigWithTypeGuard(model.key, isNonRefinerMainModelConfig); + assert(modelConfig.base === 'sdxl'); + + g.upsertMetadata({ + generation_mode: 'sdxl_txt2img', + cfg_scale, + cfg_rescale_multiplier, + height, + width, + positive_prompt: positivePrompt, + negative_prompt: negativePrompt, + model: Graph.getModelMetadataField(modelConfig), + seed, + steps, + rand_device: shouldUseCpuNoise ? 'cpu' : 'cuda', + scheduler, + positive_style_prompt: positiveStylePrompt, + negative_style_prompt: negativeStylePrompt, + vae: vae ?? undefined, + }); + + const seamless = addSeamless(state, g, denoise, modelLoader, vaeLoader); + + addSDXLLoRas(state, g, denoise, modelLoader, seamless, posCond, negCond); + + // We might get the VAE from the main model, custom VAE, or seamless node. + const vaeSource = seamless ?? vaeLoader ?? modelLoader; + g.addEdge(vaeSource, 'vae', l2i, 'vae'); + + // Add Refiner if enabled + if (refinerModel) { + await addSDXLRefiner(state, g, denoise, seamless, posCond, negCond, l2i); + } + + await addControlLayers( + state, + g, + modelConfig.base, + denoise, + posCond, + negCond, + posCondCollect, + negCondCollect, + noise, + vaeSource + ); + + if (state.system.shouldUseNSFWChecker) { + imageOutput = addNSFWChecker(g, imageOutput); + } + + if (state.system.shouldUseWatermarker) { + imageOutput = addWatermarker(g, imageOutput); + } + + g.setMetadataReceivingNode(imageOutput); + return g.getGraph(); +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts b/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts index 7fa5e1552d..1851698374 100644 --- a/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts +++ b/invokeai/frontend/web/src/features/nodes/util/node/nodeUpdate.ts @@ -1,17 +1,17 @@ import { deepClone } from 'common/util/deepClone'; import { satisfies } from 'compare-versions'; import { NodeUpdateError } from 'features/nodes/types/error'; -import type { InvocationNode, InvocationTemplate } from 'features/nodes/types/invocation'; +import type { InvocationNode, InvocationNodeData, InvocationTemplate } from 'features/nodes/types/invocation'; import { zParsedSemver } from 'features/nodes/types/semver'; import { defaultsDeep, keys, pick } from 'lodash-es'; import { buildInvocationNode } from './buildInvocationNode'; -export const getNeedsUpdate = (node: InvocationNode, template: InvocationTemplate): boolean => { - if (node.data.type !== template.type) { +export const getNeedsUpdate = (data: InvocationNodeData, template: InvocationTemplate): boolean => { + if (data.type !== template.type) { return true; } - return node.data.version !== template.version; + return data.version !== template.version; }; /** @@ -20,7 +20,7 @@ export const getNeedsUpdate = (node: InvocationNode, template: InvocationTemplat * @param template The invocation template to check against. */ const getMayUpdateNode = (node: InvocationNode, template: InvocationTemplate): boolean => { - const needsUpdate = getNeedsUpdate(node, template); + const needsUpdate = getNeedsUpdate(node.data, template); if (!needsUpdate || node.data.type !== template.type) { return false; } diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.test.ts b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.test.ts new file mode 100644 index 0000000000..6c0a6635c7 --- /dev/null +++ b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.test.ts @@ -0,0 +1,790 @@ +import { parseSchema } from 'features/nodes/util/schema/parseSchema'; +import { omit, pick } from 'lodash-es'; +import type { OpenAPIV3_1 } from 'openapi-types'; +import { describe, expect, it } from 'vitest'; + +describe('parseSchema', () => { + it('should parse the schema', () => { + const templates = parseSchema(schema); + expect(templates).toEqual(expected); + }); + it('should omit denied nodes', () => { + const templates = parseSchema(schema, undefined, ['add']); + expect(templates).toEqual(omit(expected, 'add')); + }); + it('should include only allowed nodes', () => { + const templates = parseSchema(schema, ['add']); + expect(templates).toEqual(pick(expected, 'add')); + }); +}); + +const expected = { + add: { + title: 'Add Integers', + type: 'add', + version: '1.0.1', + tags: ['math', 'add'], + description: 'Adds two numbers', + outputType: 'integer_output', + inputs: { + a: { + name: 'a', + title: 'A', + required: false, + description: 'The first number', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { + name: 'IntegerField', + isCollection: false, + isCollectionOrScalar: false, + }, + default: 0, + }, + b: { + name: 'b', + title: 'B', + required: false, + description: 'The second number', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + type: { + name: 'IntegerField', + isCollection: false, + isCollectionOrScalar: false, + }, + default: 0, + }, + }, + outputs: { + value: { + fieldKind: 'output', + name: 'value', + title: 'Value', + description: 'The output integer', + type: { + name: 'IntegerField', + isCollection: false, + isCollectionOrScalar: false, + }, + ui_hidden: false, + }, + }, + useCache: true, + nodePack: 'invokeai', + classification: 'stable', + }, + scheduler: { + title: 'Scheduler', + type: 'scheduler', + version: '1.0.0', + tags: ['scheduler'], + description: 'Selects a scheduler.', + outputType: 'scheduler_output', + inputs: { + scheduler: { + name: 'scheduler', + title: 'Scheduler', + required: false, + description: 'Scheduler to use during inference', + fieldKind: 'input', + input: 'any', + ui_hidden: false, + ui_type: 'SchedulerField', + type: { + name: 'SchedulerField', + isCollection: false, + isCollectionOrScalar: false, + }, + default: 'euler', + }, + }, + outputs: { + scheduler: { + fieldKind: 'output', + name: 'scheduler', + title: 'Scheduler', + description: 'Scheduler to use during inference', + type: { + name: 'SchedulerField', + isCollection: false, + isCollectionOrScalar: false, + }, + ui_hidden: false, + ui_type: 'SchedulerField', + }, + }, + useCache: true, + nodePack: 'invokeai', + classification: 'stable', + }, + main_model_loader: { + title: 'Main Model', + type: 'main_model_loader', + version: '1.0.2', + tags: ['model'], + description: 'Loads a main model, outputting its submodels.', + outputType: 'model_loader_output', + inputs: { + model: { + name: 'model', + title: 'Model', + required: true, + description: 'Main model (UNet, VAE, CLIP) to load', + fieldKind: 'input', + input: 'direct', + ui_hidden: false, + ui_type: 'MainModelField', + type: { + name: 'MainModelField', + isCollection: false, + isCollectionOrScalar: false, + }, + }, + }, + outputs: { + vae: { + fieldKind: 'output', + name: 'vae', + title: 'VAE', + description: 'VAE', + type: { + name: 'VAEField', + isCollection: false, + isCollectionOrScalar: false, + }, + ui_hidden: false, + }, + clip: { + fieldKind: 'output', + name: 'clip', + title: 'CLIP', + description: 'CLIP (tokenizer, text encoder, LoRAs) and skipped layer count', + type: { + name: 'CLIPField', + isCollection: false, + isCollectionOrScalar: false, + }, + ui_hidden: false, + }, + unet: { + fieldKind: 'output', + name: 'unet', + title: 'UNet', + description: 'UNet (scheduler, LoRAs)', + type: { + name: 'UNetField', + isCollection: false, + isCollectionOrScalar: false, + }, + ui_hidden: false, + }, + }, + useCache: true, + nodePack: 'invokeai', + classification: 'stable', + }, +}; + +const schema = { + openapi: '3.1.0', + info: { + title: 'Invoke - Community Edition', + description: 'An API for invoking AI image operations', + version: '1.0.0', + }, + components: { + schemas: { + AddInvocation: { + properties: { + id: { + type: 'string', + title: 'Id', + description: 'The id of this instance of an invocation. Must be unique among all instances of invocations.', + field_kind: 'node_attribute', + }, + is_intermediate: { + type: 'boolean', + title: 'Is Intermediate', + description: 'Whether or not this is an intermediate invocation.', + default: false, + field_kind: 'node_attribute', + ui_type: 'IsIntermediate', + }, + use_cache: { + type: 'boolean', + title: 'Use Cache', + description: 'Whether or not to use the cache', + default: true, + field_kind: 'node_attribute', + }, + a: { + type: 'integer', + title: 'A', + description: 'The first number', + default: 0, + field_kind: 'input', + input: 'any', + orig_default: 0, + orig_required: false, + ui_hidden: false, + }, + b: { + type: 'integer', + title: 'B', + description: 'The second number', + default: 0, + field_kind: 'input', + input: 'any', + orig_default: 0, + orig_required: false, + ui_hidden: false, + }, + type: { + type: 'string', + enum: ['add'], + const: 'add', + title: 'type', + default: 'add', + field_kind: 'node_attribute', + }, + }, + type: 'object', + required: ['type', 'id'], + title: 'Add Integers', + description: 'Adds two numbers', + category: 'math', + classification: 'stable', + node_pack: 'invokeai', + tags: ['math', 'add'], + version: '1.0.1', + output: { + $ref: '#/components/schemas/IntegerOutput', + }, + class: 'invocation', + }, + IntegerOutput: { + description: 'Base class for nodes that output a single integer', + properties: { + value: { + description: 'The output integer', + field_kind: 'output', + title: 'Value', + type: 'integer', + ui_hidden: false, + }, + type: { + const: 'integer_output', + default: 'integer_output', + enum: ['integer_output'], + field_kind: 'node_attribute', + title: 'type', + type: 'string', + }, + }, + required: ['value', 'type', 'type'], + title: 'IntegerOutput', + type: 'object', + class: 'output', + }, + SchedulerInvocation: { + properties: { + id: { + type: 'string', + title: 'Id', + description: 'The id of this instance of an invocation. Must be unique among all instances of invocations.', + field_kind: 'node_attribute', + }, + is_intermediate: { + type: 'boolean', + title: 'Is Intermediate', + description: 'Whether or not this is an intermediate invocation.', + default: false, + field_kind: 'node_attribute', + ui_type: 'IsIntermediate', + }, + use_cache: { + type: 'boolean', + title: 'Use Cache', + description: 'Whether or not to use the cache', + default: true, + field_kind: 'node_attribute', + }, + scheduler: { + type: 'string', + enum: [ + 'ddim', + 'ddpm', + 'deis', + 'lms', + 'lms_k', + 'pndm', + 'heun', + 'heun_k', + 'euler', + 'euler_k', + 'euler_a', + 'kdpm_2', + 'kdpm_2_a', + 'dpmpp_2s', + 'dpmpp_2s_k', + 'dpmpp_2m', + 'dpmpp_2m_k', + 'dpmpp_2m_sde', + 'dpmpp_2m_sde_k', + 'dpmpp_sde', + 'dpmpp_sde_k', + 'unipc', + 'lcm', + 'tcd', + ], + title: 'Scheduler', + description: 'Scheduler to use during inference', + default: 'euler', + field_kind: 'input', + input: 'any', + orig_default: 'euler', + orig_required: false, + ui_hidden: false, + ui_type: 'SchedulerField', + }, + type: { + type: 'string', + enum: ['scheduler'], + const: 'scheduler', + title: 'type', + default: 'scheduler', + field_kind: 'node_attribute', + }, + }, + type: 'object', + required: ['type', 'id'], + title: 'Scheduler', + description: 'Selects a scheduler.', + category: 'latents', + classification: 'stable', + node_pack: 'invokeai', + tags: ['scheduler'], + version: '1.0.0', + output: { + $ref: '#/components/schemas/SchedulerOutput', + }, + class: 'invocation', + }, + SchedulerOutput: { + properties: { + scheduler: { + description: 'Scheduler to use during inference', + enum: [ + 'ddim', + 'ddpm', + 'deis', + 'lms', + 'lms_k', + 'pndm', + 'heun', + 'heun_k', + 'euler', + 'euler_k', + 'euler_a', + 'kdpm_2', + 'kdpm_2_a', + 'dpmpp_2s', + 'dpmpp_2s_k', + 'dpmpp_2m', + 'dpmpp_2m_k', + 'dpmpp_2m_sde', + 'dpmpp_2m_sde_k', + 'dpmpp_sde', + 'dpmpp_sde_k', + 'unipc', + 'lcm', + 'tcd', + ], + field_kind: 'output', + title: 'Scheduler', + type: 'string', + ui_hidden: false, + ui_type: 'SchedulerField', + }, + type: { + const: 'scheduler_output', + default: 'scheduler_output', + enum: ['scheduler_output'], + field_kind: 'node_attribute', + title: 'type', + type: 'string', + }, + }, + required: ['scheduler', 'type', 'type'], + title: 'SchedulerOutput', + type: 'object', + class: 'output', + }, + MainModelLoaderInvocation: { + properties: { + id: { + type: 'string', + title: 'Id', + description: 'The id of this instance of an invocation. Must be unique among all instances of invocations.', + field_kind: 'node_attribute', + }, + is_intermediate: { + type: 'boolean', + title: 'Is Intermediate', + description: 'Whether or not this is an intermediate invocation.', + default: false, + field_kind: 'node_attribute', + ui_type: 'IsIntermediate', + }, + use_cache: { + type: 'boolean', + title: 'Use Cache', + description: 'Whether or not to use the cache', + default: true, + field_kind: 'node_attribute', + }, + model: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Main model (UNet, VAE, CLIP) to load', + field_kind: 'input', + input: 'direct', + orig_required: true, + ui_hidden: false, + ui_type: 'MainModelField', + }, + type: { + type: 'string', + enum: ['main_model_loader'], + const: 'main_model_loader', + title: 'type', + default: 'main_model_loader', + field_kind: 'node_attribute', + }, + }, + type: 'object', + required: ['model', 'type', 'id'], + title: 'Main Model', + description: 'Loads a main model, outputting its submodels.', + category: 'model', + classification: 'stable', + node_pack: 'invokeai', + tags: ['model'], + version: '1.0.2', + output: { + $ref: '#/components/schemas/ModelLoaderOutput', + }, + class: 'invocation', + }, + ModelIdentifierField: { + properties: { + key: { + description: "The model's unique key", + title: 'Key', + type: 'string', + }, + hash: { + description: "The model's BLAKE3 hash", + title: 'Hash', + type: 'string', + }, + name: { + description: "The model's name", + title: 'Name', + type: 'string', + }, + base: { + allOf: [ + { + $ref: '#/components/schemas/BaseModelType', + }, + ], + description: "The model's base model type", + }, + type: { + allOf: [ + { + $ref: '#/components/schemas/ModelType', + }, + ], + description: "The model's type", + }, + submodel_type: { + anyOf: [ + { + $ref: '#/components/schemas/SubModelType', + }, + { + type: 'null', + }, + ], + default: null, + description: 'The submodel to load, if this is a main model', + }, + }, + required: ['key', 'hash', 'name', 'base', 'type'], + title: 'ModelIdentifierField', + type: 'object', + }, + BaseModelType: { + description: 'Base model type.', + enum: ['any', 'sd-1', 'sd-2', 'sdxl', 'sdxl-refiner'], + title: 'BaseModelType', + type: 'string', + }, + ModelType: { + description: 'Model type.', + enum: ['onnx', 'main', 'vae', 'lora', 'controlnet', 'embedding', 'ip_adapter', 'clip_vision', 't2i_adapter'], + title: 'ModelType', + type: 'string', + }, + SubModelType: { + description: 'Submodel type.', + enum: [ + 'unet', + 'text_encoder', + 'text_encoder_2', + 'tokenizer', + 'tokenizer_2', + 'vae', + 'vae_decoder', + 'vae_encoder', + 'scheduler', + 'safety_checker', + ], + title: 'SubModelType', + type: 'string', + }, + ModelLoaderOutput: { + description: 'Model loader output', + properties: { + vae: { + allOf: [ + { + $ref: '#/components/schemas/VAEField', + }, + ], + description: 'VAE', + field_kind: 'output', + title: 'VAE', + ui_hidden: false, + }, + type: { + const: 'model_loader_output', + default: 'model_loader_output', + enum: ['model_loader_output'], + field_kind: 'node_attribute', + title: 'type', + type: 'string', + }, + clip: { + allOf: [ + { + $ref: '#/components/schemas/CLIPField', + }, + ], + description: 'CLIP (tokenizer, text encoder, LoRAs) and skipped layer count', + field_kind: 'output', + title: 'CLIP', + ui_hidden: false, + }, + unet: { + allOf: [ + { + $ref: '#/components/schemas/UNetField', + }, + ], + description: 'UNet (scheduler, LoRAs)', + field_kind: 'output', + title: 'UNet', + ui_hidden: false, + }, + }, + required: ['vae', 'type', 'clip', 'unet', 'type'], + title: 'ModelLoaderOutput', + type: 'object', + class: 'output', + }, + UNetField: { + properties: { + unet: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Info to load unet submodel', + }, + scheduler: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Info to load scheduler submodel', + }, + loras: { + description: 'LoRAs to apply on model loading', + items: { + $ref: '#/components/schemas/LoRAField', + }, + title: 'Loras', + type: 'array', + }, + seamless_axes: { + description: 'Axes("x" and "y") to which apply seamless', + items: { + type: 'string', + }, + title: 'Seamless Axes', + type: 'array', + }, + freeu_config: { + anyOf: [ + { + $ref: '#/components/schemas/FreeUConfig', + }, + { + type: 'null', + }, + ], + default: null, + description: 'FreeU configuration', + }, + }, + required: ['unet', 'scheduler', 'loras'], + title: 'UNetField', + type: 'object', + class: 'output', + }, + LoRAField: { + properties: { + lora: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Info to load lora model', + }, + weight: { + description: 'Weight to apply to lora model', + title: 'Weight', + type: 'number', + }, + }, + required: ['lora', 'weight'], + title: 'LoRAField', + type: 'object', + class: 'output', + }, + FreeUConfig: { + description: + 'Configuration for the FreeU hyperparameters.\n- https://huggingface.co/docs/diffusers/main/en/using-diffusers/freeu\n- https://github.com/ChenyangSi/FreeU', + properties: { + s1: { + description: + 'Scaling factor for stage 1 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process.', + maximum: 3.0, + minimum: -1.0, + title: 'S1', + type: 'number', + }, + s2: { + description: + 'Scaling factor for stage 2 to attenuate the contributions of the skip features. This is done to mitigate the "oversmoothing effect" in the enhanced denoising process.', + maximum: 3.0, + minimum: -1.0, + title: 'S2', + type: 'number', + }, + b1: { + description: 'Scaling factor for stage 1 to amplify the contributions of backbone features.', + maximum: 3.0, + minimum: -1.0, + title: 'B1', + type: 'number', + }, + b2: { + description: 'Scaling factor for stage 2 to amplify the contributions of backbone features.', + maximum: 3.0, + minimum: -1.0, + title: 'B2', + type: 'number', + }, + }, + required: ['s1', 's2', 'b1', 'b2'], + title: 'FreeUConfig', + type: 'object', + class: 'output', + }, + VAEField: { + properties: { + vae: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Info to load vae submodel', + }, + seamless_axes: { + description: 'Axes("x" and "y") to which apply seamless', + items: { + type: 'string', + }, + title: 'Seamless Axes', + type: 'array', + }, + }, + required: ['vae'], + title: 'VAEField', + type: 'object', + class: 'output', + }, + CLIPField: { + properties: { + tokenizer: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Info to load tokenizer submodel', + }, + text_encoder: { + allOf: [ + { + $ref: '#/components/schemas/ModelIdentifierField', + }, + ], + description: 'Info to load text_encoder submodel', + }, + skipped_layers: { + description: 'Number of skipped layers in text_encoder', + title: 'Skipped Layers', + type: 'integer', + }, + loras: { + description: 'LoRAs to apply on model loading', + items: { + $ref: '#/components/schemas/LoRAField', + }, + title: 'Loras', + type: 'array', + }, + }, + required: ['tokenizer', 'text_encoder', 'skipped_layers', 'loras'], + title: 'CLIPField', + type: 'object', + class: 'output', + }, + }, + }, +} as OpenAPIV3_1.Document; diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts index 990c517ad3..3178209f93 100644 --- a/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts +++ b/invokeai/frontend/web/src/features/nodes/util/schema/parseSchema.ts @@ -1,5 +1,6 @@ import { logger } from 'app/logging/logger'; import { parseify } from 'common/util/serialize'; +import type { Templates } from 'features/nodes/store/types'; import { FieldParseError } from 'features/nodes/types/error'; import type { FieldInputTemplate, FieldOutputTemplate } from 'features/nodes/types/field'; import type { InvocationTemplate } from 'features/nodes/types/invocation'; @@ -58,14 +59,14 @@ export const parseSchema = ( openAPI: OpenAPIV3_1.Document, nodesAllowlistExtra: string[] | undefined = undefined, nodesDenylistExtra: string[] | undefined = undefined -): Record => { +): Templates => { const filteredSchemas = Object.values(openAPI.components?.schemas ?? {}) .filter(isInvocationSchemaObject) .filter(isNotInDenylist) .filter((schema) => (nodesAllowlistExtra ? nodesAllowlistExtra.includes(schema.properties.type.default) : true)) .filter((schema) => (nodesDenylistExtra ? !nodesDenylistExtra.includes(schema.properties.type.default) : true)); - const invocations = filteredSchemas.reduce>((invocationsAccumulator, schema) => { + const invocations = filteredSchemas.reduce((invocationsAccumulator, schema) => { const type = schema.properties.type.default; const title = schema.title.replace('Invocation', ''); const tags = schema.tags ?? []; diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts index eec9c6cf4b..af66d3cc6b 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/graphToWorkflow.ts @@ -1,11 +1,10 @@ import * as dagre from '@dagrejs/dagre'; import { logger } from 'app/logging/logger'; -import { getStore } from 'app/store/nanostores/store'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { NODE_WIDTH } from 'features/nodes/types/constants'; import type { FieldInputInstance } from 'features/nodes/types/field'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; import { buildFieldInputInstance } from 'features/nodes/util/schema/buildFieldInputInstance'; -import { t } from 'i18next'; import { forEach } from 'lodash-es'; import type { NonNullableGraph } from 'services/api/types'; import { v4 as uuidv4 } from 'uuid'; @@ -18,11 +17,7 @@ import { v4 as uuidv4 } from 'uuid'; * @returns The workflow. */ export const graphToWorkflow = (graph: NonNullableGraph, autoLayout = true): WorkflowV3 => { - const invocationTemplates = getStore().getState().nodes.templates; - - if (!invocationTemplates) { - throw new Error(t('app.storeNotInitialized')); - } + const templates = $templates.get(); // Initialize the workflow const workflow: WorkflowV3 = { @@ -44,11 +39,11 @@ export const graphToWorkflow = (graph: NonNullableGraph, autoLayout = true): Wor // Convert nodes forEach(graph.nodes, (node) => { - const template = invocationTemplates[node.type]; + const template = templates[node.type]; // Skip missing node templates - this is a best-effort if (!template) { - logger('nodes').warn(`Node type ${node.type} not found in invocationTemplates`); + logger('nodes').warn(`Node type ${node.type} not found in templates`); return; } diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts index 56fb04d61d..32369b88c9 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/migrations.ts @@ -1,5 +1,5 @@ -import { $store } from 'app/store/nanostores/store'; import { deepClone } from 'common/util/deepClone'; +import { $templates } from 'features/nodes/store/nodesSlice'; import { WorkflowMigrationError, WorkflowVersionError } from 'features/nodes/types/error'; import type { FieldType } from 'features/nodes/types/field'; import type { InvocationNodeData } from 'features/nodes/types/invocation'; @@ -33,11 +33,7 @@ const zWorkflowMetaVersion = z.object({ * - Workflow schema version bumped to 2.0.0 */ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => { - const invocationTemplates = $store.get()?.getState().nodes.templates; - - if (!invocationTemplates) { - throw new Error(t('app.storeNotInitialized')); - } + const templates = $templates.get(); workflowToMigrate.nodes.forEach((node) => { if (node.type === 'invocation') { @@ -57,7 +53,7 @@ const migrateV1toV2 = (workflowToMigrate: WorkflowV1): WorkflowV2 => { (output.type as unknown as FieldType) = newFieldType; }); // Add node pack - const invocationTemplate = invocationTemplates[node.data.type]; + const invocationTemplate = templates[node.data.type]; const nodePack = invocationTemplate ? invocationTemplate.nodePack : t('common.unknown'); (node.data as unknown as InvocationNodeData).nodePack = nodePack; diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts index b402f2f8af..d2d3d64cb0 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts @@ -1,6 +1,6 @@ import type { JSONObject } from 'common/types'; import { parseify } from 'common/util/serialize'; -import type { InvocationTemplate } from 'features/nodes/types/invocation'; +import type { Templates } from 'features/nodes/store/types'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; import { isWorkflowInvocationNode } from 'features/nodes/types/workflow'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; @@ -31,10 +31,7 @@ type ValidateWorkflowResult = { * @throws {WorkflowVersionError} If the workflow version is not recognized. * @throws {z.ZodError} If there is a validation error. */ -export const validateWorkflow = ( - workflow: unknown, - invocationTemplates: Record -): ValidateWorkflowResult => { +export const validateWorkflow = (workflow: unknown, invocationTemplates: Templates): ValidateWorkflowResult => { // Parse the raw workflow data & migrate it to the latest version const _workflow = parseAndMigrateWorkflow(workflow); @@ -68,7 +65,7 @@ export const validateWorkflow = ( return; } - if (getNeedsUpdate(node, template)) { + if (getNeedsUpdate(node.data, template)) { // This node needs to be updated, based on comparison of its version to the template version const message = t('nodes.mismatchedVersion', { node: node.id, diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/ParamImageToImageStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/ParamImageToImageStrength.tsx new file mode 100644 index 0000000000..2519f9dad0 --- /dev/null +++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/ParamImageToImageStrength.tsx @@ -0,0 +1,20 @@ +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength'; +import { setImg2imgStrength } from 'features/parameters/store/generationSlice'; +import { memo, useCallback } from 'react'; + +const ParamImageToImageStrength = () => { + const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength); + const dispatch = useAppDispatch(); + + const onChange = useCallback( + (v: number) => { + dispatch(setImg2imgStrength(v)); + }, + [dispatch] + ); + + return ; +}; + +export default memo(ParamImageToImageStrength); diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx index 5702cf8bd7..4dfbbd1f7f 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamNegativePrompt.tsx @@ -33,12 +33,13 @@ export const ParamNegativePrompt = memo(() => { name="negativePrompt" ref={textareaRef} value={prompt} - placeholder={t('parameters.negativePromptPlaceholder')} + placeholder={t('parameters.globalNegativePromptPlaceholder')} onChange={onChange} onKeyDown={onKeyDown} fontSize="sm" variant="darkFilled" paddingRight={30} + spellCheck={false} /> diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx index 0b2890875e..9e00bea079 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamPositivePrompt.tsx @@ -49,12 +49,13 @@ export const ParamPositivePrompt = memo(() => { name="prompt" ref={textareaRef} value={prompt} - placeholder={t('parameters.positivePromptPlaceholder')} + placeholder={t('parameters.globalPositivePromptPlaceholder')} onChange={onChange} minH={28} onKeyDown={onKeyDown} variant="darkFilled" paddingRight={30} + spellCheck={false} /> diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx index e92831ecd9..c56fef1903 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx @@ -1,14 +1,17 @@ import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useAppSelector } from 'app/store/storeHooks'; import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover'; -import { setImg2imgStrength } from 'features/parameters/store/generationSlice'; -import { memo, useCallback } from 'react'; +import { memo } from 'react'; import { useTranslation } from 'react-i18next'; const marks = [0, 0.5, 1]; -const ImageToImageStrength = () => { - const img2imgStrength = useAppSelector((s) => s.generation.img2imgStrength); +type Props = { + value: number; + onChange: (v: number) => void; +}; + +const ImageToImageStrength = ({ value, onChange }: Props) => { const initial = useAppSelector((s) => s.config.sd.img2imgStrength.initial); const sliderMin = useAppSelector((s) => s.config.sd.img2imgStrength.sliderMin); const sliderMax = useAppSelector((s) => s.config.sd.img2imgStrength.sliderMax); @@ -16,11 +19,8 @@ const ImageToImageStrength = () => { const numberInputMax = useAppSelector((s) => s.config.sd.img2imgStrength.numberInputMax); const coarseStep = useAppSelector((s) => s.config.sd.img2imgStrength.coarseStep); const fineStep = useAppSelector((s) => s.config.sd.img2imgStrength.fineStep); - const dispatch = useAppDispatch(); const { t } = useTranslation(); - const handleChange = useCallback((v: number) => dispatch(setImg2imgStrength(v)), [dispatch]); - return ( @@ -31,8 +31,8 @@ const ImageToImageStrength = () => { fineStep={fineStep} min={sliderMin} max={sliderMax} - onChange={handleChange} - value={img2imgStrength} + onChange={onChange} + value={value} defaultValue={initial} marks={marks} /> @@ -41,8 +41,8 @@ const ImageToImageStrength = () => { fineStep={fineStep} min={numberInputMin} max={numberInputMax} - onChange={handleChange} - value={img2imgStrength} + onChange={onChange} + value={value} defaultValue={initial} /> diff --git a/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts b/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts index 20d771d75d..d892906fcd 100644 --- a/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts +++ b/invokeai/frontend/web/src/features/parameters/hooks/usePreselectedImage.ts @@ -38,6 +38,7 @@ export const usePreselectedImage = (selectedImage?: { const handleSendToImg2Img = useCallback(() => { if (selectedImageDto) { dispatch(iiLayerAdded(selectedImageDto)); + dispatch(setActiveTab('generation')); } }, [dispatch, selectedImageDto]); diff --git a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts index a18cc7f86d..8a808ed0c5 100644 --- a/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts +++ b/invokeai/frontend/web/src/features/parameters/types/parameterSchemas.ts @@ -16,14 +16,14 @@ import { z } from 'zod'; */ // #region Positive prompt -const zParameterPositivePrompt = z.string(); +export const zParameterPositivePrompt = z.string(); export type ParameterPositivePrompt = z.infer; export const isParameterPositivePrompt = (val: unknown): val is ParameterPositivePrompt => zParameterPositivePrompt.safeParse(val).success; // #endregion // #region Negative prompt -const zParameterNegativePrompt = z.string(); +export const zParameterNegativePrompt = z.string(); export type ParameterNegativePrompt = z.infer; export const isParameterNegativePrompt = (val: unknown): val is ParameterNegativePrompt => zParameterNegativePrompt.safeParse(val).success; @@ -127,7 +127,7 @@ export type ParameterT2IAdapterModel = z.infer // #endregion // #region Strength (l2l strength) -const zParameterStrength = z.number().min(0).max(1); +export const zParameterStrength = z.number().min(0).max(1); export type ParameterStrength = z.infer; export const isParameterStrength = (val: unknown): val is ParameterStrength => zParameterStrength.safeParse(val).success; @@ -198,6 +198,6 @@ export const isParameterLoRAWeight = (val: unknown): val is ParameterLoRAWeight // #endregion // #region Regional Prompts AutoNegative -const zAutoNegative = z.enum(['off', 'invert']); +export const zAutoNegative = z.enum(['off', 'invert']); export type ParameterAutoNegative = z.infer; // #endregion diff --git a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx b/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx index 314616da59..52dc5e24af 100644 --- a/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/InvokeQueueBackButton.tsx @@ -16,25 +16,26 @@ export const InvokeQueueBackButton = memo(() => { return ( - + + + ); }); diff --git a/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx b/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx index f63e96c45f..498414d377 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueButtonTooltip.tsx @@ -1,10 +1,11 @@ -import { Divider, Flex, ListItem, Text, UnorderedList } from '@invoke-ai/ui-library'; +import { Divider, Flex, ListItem, Text, Tooltip, UnorderedList } from '@invoke-ai/ui-library'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; import { useIsReadyToEnqueue } from 'common/hooks/useIsReadyToEnqueue'; import { selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice'; import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; import { getShouldProcessPrompt } from 'features/dynamicPrompts/util/getShouldProcessPrompt'; +import type { PropsWithChildren } from 'react'; import { memo, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useEnqueueBatchMutation } from 'services/api/endpoints/queue'; @@ -21,17 +22,32 @@ type Props = { prepend?: boolean; }; -export const QueueButtonTooltip = memo(({ prepend = false }: Props) => { +export const QueueButtonTooltip = (props: PropsWithChildren) => { + return ( + } maxW={512}> + {props.children} + + ); +}; + +const TooltipContent = memo(({ prepend = false }: Props) => { const { t } = useTranslation(); const { isReady, reasons } = useIsReadyToEnqueue(); const isLoadingDynamicPrompts = useAppSelector((s) => s.dynamicPrompts.isLoading); const promptsCount = useAppSelector(selectPromptsCount); - const iterations = useAppSelector((s) => s.generation.iterations); + const iterationsCount = useAppSelector((s) => s.generation.iterations); const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId); const autoAddBoardName = useBoardName(autoAddBoardId); const [_, { isLoading }] = useEnqueueBatchMutation({ fixedCacheKey: 'enqueueBatch', }); + const queueCountPredictionLabel = useMemo(() => { + const generationCount = Math.min(promptsCount * iterationsCount, 10000); + const prompts = t('queue.prompts', { count: promptsCount }); + const iterations = t('queue.iterations', { count: iterationsCount }); + const generations = t('queue.generations', { count: generationCount }); + return `${promptsCount} ${prompts} \u00d7 ${iterationsCount} ${iterations} -> ${generationCount} ${generations}`.toLowerCase(); + }, [iterationsCount, promptsCount, t]); const label = useMemo(() => { if (isLoading) { @@ -52,20 +68,21 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => { return ( {label} - - {t('queue.queueCountPrediction', { - promptsCount, - iterations, - count: Math.min(promptsCount * iterations, 10000), - })} - + {queueCountPredictionLabel} {reasons.length > 0 && ( <> {reasons.map((reason, i) => ( - - {reason} + + + {reason.prefix && ( + + {reason.prefix}:{' '} + + )} + {reason.content} + ))} @@ -82,4 +99,4 @@ export const QueueButtonTooltip = memo(({ prepend = false }: Props) => { ); }); -QueueButtonTooltip.displayName = 'QueueButtonTooltip'; +TooltipContent.displayName = 'QueueButtonTooltipContent'; diff --git a/invokeai/frontend/web/src/features/queue/components/QueueFrontButton.tsx b/invokeai/frontend/web/src/features/queue/components/QueueFrontButton.tsx index 07ad0f5b3c..eb0e72950f 100644 --- a/invokeai/frontend/web/src/features/queue/components/QueueFrontButton.tsx +++ b/invokeai/frontend/web/src/features/queue/components/QueueFrontButton.tsx @@ -10,15 +10,16 @@ const QueueFrontButton = () => { const { t } = useTranslation(); const { queueFront, isLoading, isDisabled } = useQueueFront(); return ( - } - icon={} - size="lg" - /> + + } + size="lg" + /> + ); }; diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx index bba9e0b32d..afc116a903 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLNegativeStylePrompt.tsx @@ -42,6 +42,7 @@ export const ParamSDXLNegativeStylePrompt = memo(() => { fontSize="sm" variant="darkFilled" paddingRight={30} + spellCheck={false} /> diff --git a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx index 3828136c74..b16730db45 100644 --- a/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx +++ b/invokeai/frontend/web/src/features/sdxl/components/SDXLPrompts/ParamSDXLPositiveStylePrompt.tsx @@ -39,6 +39,7 @@ export const ParamSDXLPositiveStylePrompt = memo(() => { fontSize="sm" variant="darkFilled" paddingRight={30} + spellCheck={false} /> diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion.tsx index e9a9263605..47392cdb8c 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/ImageSettingsAccordion/ImageSettingsAccordion.tsx @@ -9,7 +9,7 @@ import { selectHrfSlice } from 'features/hrf/store/hrfSlice'; import ParamScaleBeforeProcessing from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing'; import ParamScaledHeight from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledHeight'; import ParamScaledWidth from 'features/parameters/components/Canvas/InfillAndScaling/ParamScaledWidth'; -import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength'; +import ParamImageToImageStrength from 'features/parameters/components/Canvas/ParamImageToImageStrength'; import { ParamSeedNumberInput } from 'features/parameters/components/Seed/ParamSeedNumberInput'; import { ParamSeedRandomize } from 'features/parameters/components/Seed/ParamSeedRandomize'; import { ParamSeedShuffle } from 'features/parameters/components/Seed/ParamSeedShuffle'; @@ -85,7 +85,10 @@ export const ImageSettingsAccordion = memo(() => { onToggle={onToggleAccordion} > - {activeTabName === 'canvas' ? : } + + {activeTabName === 'canvas' ? : } + {activeTabName === 'canvas' && } + @@ -93,7 +96,6 @@ export const ImageSettingsAccordion = memo(() => { - {activeTabName === 'canvas' && } {activeTabName === 'generation' && !isSDXL && } {activeTabName === 'canvas' && ( <> diff --git a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts b/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts index 806b85ca59..79d957d7d3 100644 --- a/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts +++ b/invokeai/frontend/web/src/features/system/components/HotkeysModal/useHotkeyData.ts @@ -141,14 +141,9 @@ export const useHotkeyData = (): HotkeyGroup[] => { hotkeys: [['Arrow Right']], }, { - title: t('hotkeys.openImageViewer.title'), - desc: t('hotkeys.openImageViewer.desc'), - hotkeys: [['I']], - }, - { - title: t('hotkeys.backToEditor.title'), - desc: t('hotkeys.backToEditor.desc'), - hotkeys: [['Esc']], + title: t('hotkeys.toggleViewer.title'), + desc: t('hotkeys.toggleViewer.desc'), + hotkeys: [['Z']], }, ], }), diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx index 32611b2354..5a8273b7fc 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx @@ -63,16 +63,17 @@ const FloatingSidePanelButtons = (props: Props) => { sx={floatingButtonStyles} icon={} /> - } - sx={floatingButtonStyles} - /> + + + diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index 42df03872c..1968c64161 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -4,7 +4,6 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector'; import { $customNavComponent } from 'app/store/nanostores/customNavComponent'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import ImageGalleryContent from 'features/gallery/components/ImageGalleryContent'; -import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer'; import NodeEditorPanelGroup from 'features/nodes/components/sidePanel/NodeEditorPanelGroup'; import InvokeAILogoComponent from 'features/system/components/InvokeAILogoComponent'; import SettingsMenu from 'features/system/components/SettingsModal/SettingsMenu'; @@ -255,9 +254,8 @@ const InvokeTabs = () => { )} - + {tabPanels} - {shouldShowGalleryPanel && ( diff --git a/invokeai/frontend/web/src/features/ui/components/ParametersPanelTextToImage.tsx b/invokeai/frontend/web/src/features/ui/components/ParametersPanelTextToImage.tsx index abd78d00e4..b78d5dce9a 100644 --- a/invokeai/frontend/web/src/features/ui/components/ParametersPanelTextToImage.tsx +++ b/invokeai/frontend/web/src/features/ui/components/ParametersPanelTextToImage.tsx @@ -1,9 +1,9 @@ import type { ChakraProps } from '@invoke-ai/ui-library'; import { Box, Flex, Tab, TabList, TabPanel, TabPanels, Tabs } from '@invoke-ai/ui-library'; -import { useAppSelector } from 'app/store/storeHooks'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants'; import { ControlLayersPanelContent } from 'features/controlLayers/components/ControlLayersPanelContent'; -import { useControlLayersTitle } from 'features/controlLayers/hooks/useControlLayersTitle'; +import { isImageViewerOpenChanged } from 'features/gallery/store/gallerySlice'; import { Prompts } from 'features/parameters/components/Prompts/Prompts'; import QueueControls from 'features/queue/components/QueueControls'; import { SDXLPrompts } from 'features/sdxl/components/SDXLPrompts/SDXLPrompts'; @@ -16,7 +16,7 @@ import { RefinerSettingsAccordion } from 'features/settingsAccordions/components import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import type { CSSProperties } from 'react'; -import { memo } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; const overlayScrollbarsStyles: CSSProperties = { @@ -24,34 +24,38 @@ const overlayScrollbarsStyles: CSSProperties = { width: '100%', }; -const unselectedStyles: ChakraProps['sx'] = { - bg: 'none', - color: 'base.300', +const baseStyles: ChakraProps['sx'] = { fontWeight: 'semibold', fontSize: 'sm', - w: '50%', - borderWidth: 1, - borderRadius: 'base', + color: 'base.300', }; const selectedStyles: ChakraProps['sx'] = { + borderColor: 'base.800', + borderBottomColor: 'base.900', color: 'invokeBlue.300', - borderColor: 'invokeBlueAlpha.400', - _hover: { - color: 'invokeBlue.200', - }, -}; - -const hoverStyles: ChakraProps['sx'] = { - bg: 'base.850', - color: 'base.100', }; const ParametersPanelTextToImage = () => { const { t } = useTranslation(); + const dispatch = useAppDispatch(); const activeTabName = useAppSelector(activeTabNameSelector); - const controlLayersTitle = useControlLayersTitle(); + const controlLayersCount = useAppSelector((s) => s.controlLayers.present.layers.length); + const controlLayersTitle = useMemo(() => { + if (controlLayersCount === 0) { + return t('controlLayers.controlLayers'); + } + return `${t('controlLayers.controlLayers')} (${controlLayersCount})`; + }, [controlLayersCount, t]); const isSDXL = useAppSelector((s) => s.generation.model?.base === 'sdxl'); + const onChangeTabs = useCallback( + (i: number) => { + if (i === 1) { + dispatch(isImageViewerOpenChanged(false)); + } + }, + [dispatch] + ); return ( @@ -61,12 +65,24 @@ const ParametersPanelTextToImage = () => { {isSDXL ? : } - - - + + + {t('common.settingsLabel')} - + {controlLayersTitle} diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx index 2ee21bfadf..b4f473ae03 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/NodesTab.tsx @@ -1,9 +1,20 @@ import { Box } from '@invoke-ai/ui-library'; +import { useAppSelector } from 'app/store/storeHooks'; +import { ImageViewerWorkflows } from 'features/gallery/components/ImageViewer/ImageViewerWorkflows'; import NodeEditor from 'features/nodes/components/NodeEditor'; import { memo } from 'react'; import { ReactFlowProvider } from 'reactflow'; const NodesTab = () => { + const mode = useAppSelector((s) => s.workflow.mode); + if (mode === 'view') { + return ( + + + + ); + } + return ( diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImageTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImageTab.tsx index 74845a9ca9..1c1c9c24a4 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/TextToImageTab.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/TextToImageTab.tsx @@ -1,11 +1,13 @@ import { Box } from '@invoke-ai/ui-library'; import { ControlLayersEditor } from 'features/controlLayers/components/ControlLayersEditor'; +import { ImageViewer } from 'features/gallery/components/ImageViewer/ImageViewer'; import { memo } from 'react'; const TextToImageTab = () => { return ( + ); }; diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx index ecb4aa7dd4..6ecb51a528 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/components/LoadWorkflowFromGraphModal/LoadWorkflowFromGraphModal.tsx @@ -58,8 +58,7 @@ export const LoadWorkflowFromGraphModal = () => { setWorkflowRaw(JSON.stringify(workflow, null, 2)); }, [graphRaw, shouldAutoLayout]); const loadWorkflow = useCallback(() => { - const workflow = JSON.parse(workflowRaw); - dispatch(workflowLoadRequested({ workflow, asCopy: true })); + dispatch(workflowLoadRequested({ data: { workflow: workflowRaw, graph: null }, asCopy: true })); onClose(); }, [dispatch, onClose, workflowRaw]); return ( diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts index b55a01dd6d..7ea9329540 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts +++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadEmbeddedWorkflow.ts @@ -27,10 +27,17 @@ export const useGetAndLoadEmbeddedWorkflow: UseGetAndLoadEmbeddedWorkflow = ({ o const getAndLoadEmbeddedWorkflow = useCallback( async (imageName: string) => { try { - const workflow = await _getAndLoadEmbeddedWorkflow(imageName); - dispatch(workflowLoadRequested({ workflow: workflow.data, asCopy: true })); - // No toast - the listener for this action does that after the workflow is loaded - onSuccess && onSuccess(); + const { data } = await _getAndLoadEmbeddedWorkflow(imageName); + if (data) { + dispatch(workflowLoadRequested({ data, asCopy: true })); + // No toast - the listener for this action does that after the workflow is loaded + onSuccess && onSuccess(); + } else { + toaster({ + title: t('toast.problemRetrievingWorkflow'), + status: 'error', + }); + } } catch { toaster({ title: t('toast.problemRetrievingWorkflow'), diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts index ca4e72e34c..f616812175 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts +++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useGetAndLoadLibraryWorkflow.ts @@ -27,8 +27,9 @@ export const useGetAndLoadLibraryWorkflow: UseGetAndLoadLibraryWorkflow = ({ onS const getAndLoadWorkflow = useCallback( async (workflow_id: string) => { try { - const data = await _getAndLoadWorkflow(workflow_id).unwrap(); - dispatch(workflowLoadRequested({ workflow: data.workflow, asCopy: false })); + const { workflow } = await _getAndLoadWorkflow(workflow_id).unwrap(); + // This action expects a stringified workflow, instead of updating the routes and services we will just stringify it here + dispatch(workflowLoadRequested({ data: { workflow: JSON.stringify(workflow), graph: null }, asCopy: false })); // No toast - the listener for this action does that after the workflow is loaded onSuccess && onSuccess(); } catch { diff --git a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx index 8610fa87e0..7a39d4ecd0 100644 --- a/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx +++ b/invokeai/frontend/web/src/features/workflowLibrary/hooks/useLoadWorkflowFromFile.tsx @@ -29,8 +29,7 @@ export const useLoadWorkflowFromFile: UseLoadWorkflowFromFile = ({ resetRef, onS const rawJSON = reader.result; try { - const parsedJSON = JSON.parse(String(rawJSON)); - dispatch(workflowLoadRequested({ workflow: parsedJSON, asCopy: true })); + dispatch(workflowLoadRequested({ data: { workflow: String(rawJSON), graph: null }, asCopy: true })); dispatch(workflowLoadedFromFile()); onSuccess && onSuccess(); } catch (e) { diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts index 70358ebc8c..98c253b479 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/images.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts @@ -10,6 +10,7 @@ import { keyBy } from 'lodash-es'; import type { components, paths } from 'services/api/schema'; import type { DeleteBoardResult, + GraphAndWorkflowResponse, ImageCategory, ImageDTO, ListImagesArgs, @@ -122,10 +123,7 @@ export const imagesApi = api.injectEndpoints({ providesTags: (result, error, image_name) => [{ type: 'ImageMetadata', id: image_name }], keepUnusedDataFor: 86400, // 24 hours }), - getImageWorkflow: build.query< - paths['/api/v1/images/i/{image_name}/workflow']['get']['responses']['200']['content']['application/json'], - string - >({ + getImageWorkflow: build.query({ query: (image_name) => ({ url: buildImagesUrl(`i/${image_name}/workflow`) }), providesTags: (result, error, image_name) => [{ type: 'ImageWorkflow', id: image_name }], keepUnusedDataFor: 86400, // 24 hours diff --git a/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts b/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts index 2945de612c..3f303fbf10 100644 --- a/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts +++ b/invokeai/frontend/web/src/services/api/hooks/useDebouncedImageWorkflow.ts @@ -9,7 +9,7 @@ export const useDebouncedImageWorkflow = (imageDTO?: ImageDTO | null) => { const [debouncedImageName] = useDebounce(imageDTO?.has_workflow ? imageDTO.image_name : null, workflowFetchDebounce); - const { data: workflow, isLoading } = useGetImageWorkflowQuery(debouncedImageName ?? skipToken); + const result = useGetImageWorkflowQuery(debouncedImageName ?? skipToken); - return { workflow, isLoading }; + return result; }; diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts index 27a3c670da..c1f9486bc7 100644 --- a/invokeai/frontend/web/src/services/api/schema.ts +++ b/invokeai/frontend/web/src/services/api/schema.ts @@ -581,6 +581,7 @@ export type components = { * type * @default add * @constant + * @enum {string} */ type: "add"; }; @@ -618,6 +619,7 @@ export type components = { * type * @default alpha_mask_to_tensor * @constant + * @enum {string} */ type: "alpha_mask_to_tensor"; }; @@ -743,6 +745,7 @@ export type components = { * Type * @default basemetadata * @constant + * @enum {string} */ type?: "basemetadata"; }; @@ -895,6 +898,7 @@ export type components = { * type * @default blank_image * @constant + * @enum {string} */ type: "blank_image"; }; @@ -934,6 +938,7 @@ export type components = { * type * @default lblend * @constant + * @enum {string} */ type: "lblend"; }; @@ -1203,6 +1208,7 @@ export type components = { * type * @default boolean_collection * @constant + * @enum {string} */ type: "boolean_collection"; }; @@ -1220,6 +1226,7 @@ export type components = { * type * @default boolean_collection_output * @constant + * @enum {string} */ type: "boolean_collection_output"; }; @@ -1255,6 +1262,7 @@ export type components = { * type * @default boolean * @constant + * @enum {string} */ type: "boolean"; }; @@ -1272,6 +1280,7 @@ export type components = { * type * @default boolean_output * @constant + * @enum {string} */ type: "boolean_output"; }; @@ -1306,6 +1315,7 @@ export type components = { * type * @default clip_output * @constant + * @enum {string} */ type: "clip_output"; }; @@ -1346,6 +1356,7 @@ export type components = { * type * @default clip_skip * @constant + * @enum {string} */ type: "clip_skip"; }; @@ -1364,6 +1375,7 @@ export type components = { * type * @default clip_skip_output * @constant + * @enum {string} */ type: "clip_skip_output"; }; @@ -1419,6 +1431,7 @@ export type components = { /** * Format * @constant + * @enum {string} */ format: "diffusers"; /** @default */ @@ -1427,6 +1440,7 @@ export type components = { * Type * @default clip_vision * @constant + * @enum {string} */ type: "clip_vision"; }; @@ -1462,6 +1476,7 @@ export type components = { * type * @default infill_cv2 * @constant + * @enum {string} */ type: "infill_cv2"; }; @@ -1521,6 +1536,7 @@ export type components = { * type * @default calculate_image_tiles_even_split * @constant + * @enum {string} */ type: "calculate_image_tiles_even_split"; }; @@ -1580,6 +1596,7 @@ export type components = { * type * @default calculate_image_tiles * @constant + * @enum {string} */ type: "calculate_image_tiles"; }; @@ -1639,6 +1656,7 @@ export type components = { * type * @default calculate_image_tiles_min_overlap * @constant + * @enum {string} */ type: "calculate_image_tiles_min_overlap"; }; @@ -1653,6 +1671,7 @@ export type components = { * type * @default calculate_image_tiles_output * @constant + * @enum {string} */ type: "calculate_image_tiles_output"; }; @@ -1723,6 +1742,7 @@ export type components = { * type * @default canny_image_processor * @constant + * @enum {string} */ type: "canny_image_processor"; }; @@ -1768,6 +1788,7 @@ export type components = { * type * @default canvas_paste_back * @constant + * @enum {string} */ type: "canvas_paste_back"; }; @@ -1823,6 +1844,7 @@ export type components = { * type * @default img_pad_crop * @constant + * @enum {string} */ type: "img_pad_crop"; }; @@ -1874,6 +1896,7 @@ export type components = { * type * @default collect * @constant + * @enum {string} */ type: "collect"; }; @@ -1888,6 +1911,7 @@ export type components = { * type * @default collect_output * @constant + * @enum {string} */ type: "collect_output"; }; @@ -1905,6 +1929,7 @@ export type components = { * type * @default color_collection_output * @constant + * @enum {string} */ type: "color_collection_output"; }; @@ -1951,6 +1976,7 @@ export type components = { * type * @default color_correct * @constant + * @enum {string} */ type: "color_correct"; }; @@ -2016,6 +2042,7 @@ export type components = { * type * @default color * @constant + * @enum {string} */ type: "color"; }; @@ -2057,6 +2084,7 @@ export type components = { * type * @default color_map_image_processor * @constant + * @enum {string} */ type: "color_map_image_processor"; }; @@ -2071,6 +2099,7 @@ export type components = { * type * @default color_output * @constant + * @enum {string} */ type: "color_output"; }; @@ -2113,6 +2142,7 @@ export type components = { * type * @default compel * @constant + * @enum {string} */ type: "compel"; }; @@ -2148,6 +2178,7 @@ export type components = { * type * @default conditioning_collection * @constant + * @enum {string} */ type: "conditioning_collection"; }; @@ -2165,6 +2196,7 @@ export type components = { * type * @default conditioning_collection_output * @constant + * @enum {string} */ type: "conditioning_collection_output"; }; @@ -2212,6 +2244,7 @@ export type components = { * type * @default conditioning * @constant + * @enum {string} */ type: "conditioning"; }; @@ -2226,6 +2259,7 @@ export type components = { * type * @default conditioning_output * @constant + * @enum {string} */ type: "conditioning_output"; }; @@ -2291,6 +2325,7 @@ export type components = { * type * @default content_shuffle_image_processor * @constant + * @enum {string} */ type: "content_shuffle_image_processor"; }; @@ -2393,6 +2428,7 @@ export type components = { * Format * @default checkpoint * @constant + * @enum {string} */ format: "checkpoint"; /** @@ -2409,6 +2445,7 @@ export type components = { * Type * @default controlnet * @constant + * @enum {string} */ type: "controlnet"; }; @@ -2467,6 +2504,7 @@ export type components = { * Format * @default diffusers * @constant + * @enum {string} */ format: "diffusers"; /** @default */ @@ -2475,6 +2513,7 @@ export type components = { * Type * @default controlnet * @constant + * @enum {string} */ type: "controlnet"; }; @@ -2540,6 +2579,7 @@ export type components = { * type * @default controlnet * @constant + * @enum {string} */ type: "controlnet"; }; @@ -2595,6 +2635,7 @@ export type components = { * type * @default control_output * @constant + * @enum {string} */ type: "control_output"; }; @@ -2785,6 +2826,7 @@ export type components = { * type * @default core_metadata * @constant + * @enum {string} */ type: "core_metadata"; [key: string]: unknown; @@ -2833,6 +2875,7 @@ export type components = { * type * @default create_denoise_mask * @constant + * @enum {string} */ type: "create_denoise_mask"; }; @@ -2909,6 +2952,7 @@ export type components = { * type * @default create_gradient_mask * @constant + * @enum {string} */ type: "create_gradient_mask"; }; @@ -2961,6 +3005,7 @@ export type components = { * type * @default crop_latents * @constant + * @enum {string} */ type: "crop_latents"; }; @@ -3016,6 +3061,7 @@ export type components = { * type * @default cv_inpaint * @constant + * @enum {string} */ type: "cv_inpaint"; }; @@ -3072,6 +3118,7 @@ export type components = { * type * @default dw_openpose_image_processor * @constant + * @enum {string} */ type: "dw_openpose_image_processor"; }; @@ -3194,6 +3241,7 @@ export type components = { * type * @default denoise_latents * @constant + * @enum {string} */ type: "denoise_latents"; }; @@ -3231,6 +3279,7 @@ export type components = { * type * @default denoise_mask_output * @constant + * @enum {string} */ type: "denoise_mask_output"; }; @@ -3279,6 +3328,7 @@ export type components = { * type * @default depth_anything_image_processor * @constant + * @enum {string} */ type: "depth_anything_image_processor"; }; @@ -3320,6 +3370,7 @@ export type components = { * type * @default div * @constant + * @enum {string} */ type: "div"; }; @@ -3454,6 +3505,7 @@ export type components = { * type * @default dynamic_prompt * @constant + * @enum {string} */ type: "dynamic_prompt"; }; @@ -3509,6 +3561,7 @@ export type components = { * type * @default esrgan * @constant + * @enum {string} */ type: "esrgan"; }; @@ -3608,6 +3661,7 @@ export type components = { * type * @default face_identifier * @constant + * @enum {string} */ type: "face_identifier"; }; @@ -3677,6 +3731,7 @@ export type components = { * type * @default face_mask_detection * @constant + * @enum {string} */ type: "face_mask_detection"; }; @@ -3701,6 +3756,7 @@ export type components = { * type * @default face_mask_output * @constant + * @enum {string} */ type: "face_mask_output"; /** @description The output mask */ @@ -3772,6 +3828,7 @@ export type components = { * type * @default face_off * @constant + * @enum {string} */ type: "face_off"; }; @@ -3796,6 +3853,7 @@ export type components = { * type * @default face_off_output * @constant + * @enum {string} */ type: "face_off_output"; /** @description The output mask */ @@ -3843,6 +3901,7 @@ export type components = { * type * @default float_collection * @constant + * @enum {string} */ type: "float_collection"; }; @@ -3860,6 +3919,7 @@ export type components = { * type * @default float_collection_output * @constant + * @enum {string} */ type: "float_collection_output"; }; @@ -3895,6 +3955,7 @@ export type components = { * type * @default float * @constant + * @enum {string} */ type: "float"; }; @@ -3942,6 +4003,7 @@ export type components = { * type * @default float_range * @constant + * @enum {string} */ type: "float_range"; }; @@ -3990,6 +4052,7 @@ export type components = { * type * @default float_math * @constant + * @enum {string} */ type: "float_math"; }; @@ -4007,6 +4070,7 @@ export type components = { * type * @default float_output * @constant + * @enum {string} */ type: "float_output"; }; @@ -4055,6 +4119,7 @@ export type components = { * type * @default float_to_int * @constant + * @enum {string} */ type: "float_to_int"; }; @@ -4158,6 +4223,7 @@ export type components = { * type * @default freeu * @constant + * @enum {string} */ type: "freeu"; }; @@ -4174,6 +4240,7 @@ export type components = { * type * @default gradient_mask_output * @constant + * @enum {string} */ type: "gradient_mask_output"; }; @@ -4189,7 +4256,7 @@ export type components = { * @description The nodes in this graph */ nodes: { - [key: string]: components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["NoiseInvocation"]; + [key: string]: components["schemas"]["IdealSizeInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["ColorMapImageProcessorInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["LineartImageProcessorInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["TileResamplerProcessorInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["ZoeDepthImageProcessorInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["StepParamEasingInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["NormalbaeImageProcessorInvocation"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MlsdImageProcessorInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["CannyImageProcessorInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["SegmentAnythingProcessorInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["AddInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["MediapipeFaceProcessorInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["PidiImageProcessorInvocation"] | components["schemas"]["DepthAnythingImageProcessorInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["ContentShuffleImageProcessorInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["LeresImageProcessorInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["StringInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["DWOpenposeImageProcessorInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["FloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["MidasDepthImageProcessorInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["HedImageProcessorInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["LineartAnimeImageProcessorInvocation"] | components["schemas"]["ImageScaleInvocation"]; }; /** * Edges @@ -4226,7 +4293,7 @@ export type components = { * @description The results of node executions */ results: { - [key: string]: components["schemas"]["ImageOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["String2Output"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["LoRALoaderOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["CLIPSkipInvocationOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["CLIPOutput"]; + [key: string]: components["schemas"]["LoRALoaderOutput"] | components["schemas"]["FloatOutput"] | components["schemas"]["FloatCollectionOutput"] | components["schemas"]["FaceOffOutput"] | components["schemas"]["TileToPropertiesOutput"] | components["schemas"]["MetadataOutput"] | components["schemas"]["CLIPOutput"] | components["schemas"]["SeamlessModeOutput"] | components["schemas"]["BooleanCollectionOutput"] | components["schemas"]["StringCollectionOutput"] | components["schemas"]["IdealSizeOutput"] | components["schemas"]["ImageOutput"] | components["schemas"]["LatentsOutput"] | components["schemas"]["ConditioningCollectionOutput"] | components["schemas"]["IntegerCollectionOutput"] | components["schemas"]["StringOutput"] | components["schemas"]["ModelLoaderOutput"] | components["schemas"]["ControlOutput"] | components["schemas"]["T2IAdapterOutput"] | components["schemas"]["IntegerOutput"] | components["schemas"]["CalculateImageTilesOutput"] | components["schemas"]["SDXLModelLoaderOutput"] | components["schemas"]["PairTileImageOutput"] | components["schemas"]["NoiseOutput"] | components["schemas"]["MetadataItemOutput"] | components["schemas"]["SDXLLoRALoaderOutput"] | components["schemas"]["ColorCollectionOutput"] | components["schemas"]["ImageCollectionOutput"] | components["schemas"]["StringPosNegOutput"] | components["schemas"]["SDXLRefinerModelLoaderOutput"] | components["schemas"]["UNetOutput"] | components["schemas"]["BooleanOutput"] | components["schemas"]["FaceMaskOutput"] | components["schemas"]["CollectInvocationOutput"] | components["schemas"]["GradientMaskOutput"] | components["schemas"]["MaskOutput"] | components["schemas"]["IPAdapterOutput"] | components["schemas"]["LoRASelectorOutput"] | components["schemas"]["String2Output"] | components["schemas"]["LatentsCollectionOutput"] | components["schemas"]["ConditioningOutput"] | components["schemas"]["IterateInvocationOutput"] | components["schemas"]["SchedulerOutput"] | components["schemas"]["ColorOutput"] | components["schemas"]["VAEOutput"] | components["schemas"]["DenoiseMaskOutput"] | components["schemas"]["CLIPSkipInvocationOutput"]; }; /** * Errors @@ -4269,6 +4336,7 @@ export type components = { * Type * @default hf * @constant + * @enum {string} */ type?: "hf"; }; @@ -4327,6 +4395,7 @@ export type components = { * type * @default hed_image_processor * @constant + * @enum {string} */ type: "hed_image_processor"; }; @@ -4370,6 +4439,7 @@ export type components = { * type * @default heuristic_resize * @constant + * @enum {string} */ type: "heuristic_resize"; }; @@ -4392,6 +4462,7 @@ export type components = { * Type * @default huggingface * @constant + * @enum {string} */ type?: "huggingface"; /** @@ -4482,11 +4553,13 @@ export type components = { * Type * @default ip_adapter * @constant + * @enum {string} */ type: "ip_adapter"; /** * Format * @constant + * @enum {string} */ format: "checkpoint"; }; @@ -4601,6 +4674,7 @@ export type components = { * type * @default ip_adapter * @constant + * @enum {string} */ type: "ip_adapter"; }; @@ -4657,6 +4731,7 @@ export type components = { * Type * @default ip_adapter * @constant + * @enum {string} */ type: "ip_adapter"; /** Image Encoder Model Id */ @@ -4664,6 +4739,7 @@ export type components = { /** * Format * @constant + * @enum {string} */ format: "invokeai"; }; @@ -4715,6 +4791,7 @@ export type components = { * type * @default ip_adapter_output * @constant + * @enum {string} */ type: "ip_adapter_output"; }; @@ -4764,6 +4841,7 @@ export type components = { * type * @default ideal_size * @constant + * @enum {string} */ type: "ideal_size"; }; @@ -4786,6 +4864,7 @@ export type components = { * type * @default ideal_size_output * @constant + * @enum {string} */ type: "ideal_size_output"; }; @@ -4834,6 +4913,7 @@ export type components = { * type * @default img_blur * @constant + * @enum {string} */ type: "img_blur"; }; @@ -4888,6 +4968,7 @@ export type components = { * type * @default img_chan * @constant + * @enum {string} */ type: "img_chan"; }; @@ -4941,6 +5022,7 @@ export type components = { * type * @default img_channel_multiply * @constant + * @enum {string} */ type: "img_channel_multiply"; }; @@ -4988,6 +5070,7 @@ export type components = { * type * @default img_channel_offset * @constant + * @enum {string} */ type: "img_channel_offset"; }; @@ -5022,6 +5105,7 @@ export type components = { * type * @default image_collection * @constant + * @enum {string} */ type: "image_collection"; }; @@ -5039,6 +5123,7 @@ export type components = { * type * @default image_collection_output * @constant + * @enum {string} */ type: "image_collection_output"; }; @@ -5081,6 +5166,7 @@ export type components = { * type * @default img_conv * @constant + * @enum {string} */ type: "img_conv"; }; @@ -5140,6 +5226,7 @@ export type components = { * type * @default img_crop * @constant + * @enum {string} */ type: "img_crop"; }; @@ -5272,6 +5359,7 @@ export type components = { * type * @default img_hue_adjust * @constant + * @enum {string} */ type: "img_hue_adjust"; }; @@ -5319,6 +5407,7 @@ export type components = { * type * @default img_ilerp * @constant + * @enum {string} */ type: "img_ilerp"; }; @@ -5350,6 +5439,7 @@ export type components = { * type * @default image * @constant + * @enum {string} */ type: "image"; }; @@ -5397,6 +5487,7 @@ export type components = { * type * @default img_lerp * @constant + * @enum {string} */ type: "img_lerp"; }; @@ -5442,6 +5533,7 @@ export type components = { * type * @default image_mask_to_tensor * @constant + * @enum {string} */ type: "image_mask_to_tensor"; }; @@ -5479,6 +5571,7 @@ export type components = { * type * @default img_mul * @constant + * @enum {string} */ type: "img_mul"; }; @@ -5514,6 +5607,7 @@ export type components = { * type * @default img_nsfw * @constant + * @enum {string} */ type: "img_nsfw"; }; @@ -5538,6 +5632,7 @@ export type components = { * type * @default image_output * @constant + * @enum {string} */ type: "image_output"; }; @@ -5595,6 +5690,7 @@ export type components = { * type * @default img_paste * @constant + * @enum {string} */ type: "img_paste"; }; @@ -5679,6 +5775,7 @@ export type components = { * type * @default img_resize * @constant + * @enum {string} */ type: "img_resize"; }; @@ -5727,6 +5824,7 @@ export type components = { * type * @default img_scale * @constant + * @enum {string} */ type: "img_scale"; }; @@ -5772,6 +5870,7 @@ export type components = { * type * @default i2l * @constant + * @enum {string} */ type: "i2l"; }; @@ -5834,6 +5933,7 @@ export type components = { * type * @default img_watermark * @constant + * @enum {string} */ type: "img_watermark"; }; @@ -5900,6 +6000,7 @@ export type components = { * type * @default infill_rgba * @constant + * @enum {string} */ type: "infill_rgba"; }; @@ -5948,6 +6049,7 @@ export type components = { * type * @default infill_patchmatch * @constant + * @enum {string} */ type: "infill_patchmatch"; }; @@ -5995,6 +6097,7 @@ export type components = { * type * @default infill_tile * @constant + * @enum {string} */ type: "infill_tile"; }; @@ -6036,6 +6139,7 @@ export type components = { * type * @default integer_collection * @constant + * @enum {string} */ type: "integer_collection"; }; @@ -6053,6 +6157,7 @@ export type components = { * type * @default integer_collection_output * @constant + * @enum {string} */ type: "integer_collection_output"; }; @@ -6088,6 +6193,7 @@ export type components = { * type * @default integer * @constant + * @enum {string} */ type: "integer"; }; @@ -6136,6 +6242,7 @@ export type components = { * type * @default integer_math * @constant + * @enum {string} */ type: "integer_math"; }; @@ -6153,6 +6260,7 @@ export type components = { * type * @default integer_output * @constant + * @enum {string} */ type: "integer_output"; }; @@ -6184,6 +6292,7 @@ export type components = { * type * @default invert_tensor_mask * @constant + * @enum {string} */ type: "invert_tensor_mask"; }; @@ -6253,6 +6362,7 @@ export type components = { * type * @default iterate * @constant + * @enum {string} */ type: "iterate"; }; @@ -6280,6 +6390,7 @@ export type components = { * type * @default iterate_output * @constant + * @enum {string} */ type: "iterate_output"; }; @@ -6316,6 +6427,7 @@ export type components = { * type * @default infill_lama * @constant + * @enum {string} */ type: "infill_lama"; }; @@ -6350,6 +6462,7 @@ export type components = { * type * @default latents_collection * @constant + * @enum {string} */ type: "latents_collection"; }; @@ -6367,6 +6480,7 @@ export type components = { * type * @default latents_collection_output * @constant + * @enum {string} */ type: "latents_collection_output"; }; @@ -6415,6 +6529,7 @@ export type components = { * type * @default latents * @constant + * @enum {string} */ type: "latents"; }; @@ -6439,6 +6554,7 @@ export type components = { * type * @default latents_output * @constant + * @enum {string} */ type: "latents_output"; }; @@ -6488,6 +6604,7 @@ export type components = { * type * @default l2i * @constant + * @enum {string} */ type: "l2i"; }; @@ -6553,6 +6670,7 @@ export type components = { * type * @default leres_image_processor * @constant + * @enum {string} */ type: "leres_image_processor"; }; @@ -6600,6 +6718,7 @@ export type components = { * type * @default lineart_anime_image_processor * @constant + * @enum {string} */ type: "lineart_anime_image_processor"; }; @@ -6653,9 +6772,55 @@ export type components = { * type * @default lineart_image_processor * @constant + * @enum {string} */ type: "lineart_image_processor"; }; + /** + * LoRA Collection Loader + * @description Applies a collection of LoRAs to the provided UNet and CLIP models. + */ + LoRACollectionLoader: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. + */ + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][]; + /** + * UNet + * @description UNet (scheduler, LoRAs) + */ + unet?: components["schemas"]["UNetField"] | null; + /** + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip?: components["schemas"]["CLIPField"] | null; + /** + * type + * @default lora_collection_loader + * @constant + * @enum {string} + */ + type: "lora_collection_loader"; + }; /** * LoRADiffusersConfig * @description Model config for LoRA/Diffusers models. @@ -6709,6 +6874,7 @@ export type components = { * Type * @default lora * @constant + * @enum {string} */ type: "lora"; /** @@ -6720,6 +6886,7 @@ export type components = { * Format * @default diffusers * @constant + * @enum {string} */ format: "diffusers"; }; @@ -6780,6 +6947,7 @@ export type components = { * type * @default lora_loader * @constant + * @enum {string} */ type: "lora_loader"; }; @@ -6804,6 +6972,7 @@ export type components = { * type * @default lora_loader_output * @constant + * @enum {string} */ type: "lora_loader_output"; }; @@ -6860,6 +7029,7 @@ export type components = { * Type * @default lora * @constant + * @enum {string} */ type: "lora"; /** @@ -6871,6 +7041,7 @@ export type components = { * Format * @default lycoris * @constant + * @enum {string} */ format: "lycoris"; }; @@ -6887,6 +7058,65 @@ export type components = { */ weight: number; }; + /** + * LoRA Selector + * @description Selects a LoRA model and weight. + */ + LoRASelectorInvocation: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * LoRA + * @description LoRA model to load + */ + lora: components["schemas"]["ModelIdentifierField"]; + /** + * Weight + * @description The weight at which the LoRA is applied to each model + * @default 0.75 + */ + weight?: number; + /** + * type + * @default lora_selector + * @constant + * @enum {string} + */ + type: "lora_selector"; + }; + /** + * LoRASelectorOutput + * @description Model loader output + */ + LoRASelectorOutput: { + /** + * LoRA + * @description LoRA model and weight + */ + lora: components["schemas"]["LoRAField"]; + /** + * type + * @default lora_selector_output + * @constant + * @enum {string} + */ + type: "lora_selector_output"; + }; /** * LocalModelSource * @description A local file or directory path. @@ -6903,6 +7133,7 @@ export type components = { * Type * @default local * @constant + * @enum {string} */ type?: "local"; }; @@ -6964,6 +7195,7 @@ export type components = { * Type * @default main * @constant + * @enum {string} */ type: "main"; /** @@ -6979,6 +7211,7 @@ export type components = { * Format * @default checkpoint * @constant + * @enum {string} */ format: "checkpoint"; /** @@ -7052,6 +7285,7 @@ export type components = { * Type * @default main * @constant + * @enum {string} */ type: "main"; /** @@ -7067,6 +7301,7 @@ export type components = { * Format * @default diffusers * @constant + * @enum {string} */ format: "diffusers"; /** @default */ @@ -7143,6 +7378,7 @@ export type components = { * type * @default main_model_loader * @constant + * @enum {string} */ type: "main_model_loader"; }; @@ -7180,6 +7416,7 @@ export type components = { * type * @default mask_combine * @constant + * @enum {string} */ type: "mask_combine"; }; @@ -7235,6 +7472,7 @@ export type components = { * type * @default mask_edge * @constant + * @enum {string} */ type: "mask_edge"; }; @@ -7276,6 +7514,7 @@ export type components = { * type * @default tomask * @constant + * @enum {string} */ type: "tomask"; }; @@ -7325,6 +7564,7 @@ export type components = { * type * @default mask_from_id * @constant + * @enum {string} */ type: "mask_from_id"; }; @@ -7349,6 +7589,7 @@ export type components = { * type * @default mask_output * @constant + * @enum {string} */ type: "mask_output"; }; @@ -7408,6 +7649,7 @@ export type components = { * type * @default mediapipe_face_processor * @constant + * @enum {string} */ type: "mediapipe_face_processor"; }; @@ -7442,6 +7684,7 @@ export type components = { * type * @default merge_metadata * @constant + * @enum {string} */ type: "merge_metadata"; }; @@ -7493,6 +7736,7 @@ export type components = { * type * @default merge_tiles_to_image * @constant + * @enum {string} */ type: "merge_tiles_to_image"; }; @@ -7533,6 +7777,7 @@ export type components = { * type * @default metadata * @constant + * @enum {string} */ type: "metadata"; }; @@ -7585,6 +7830,7 @@ export type components = { * type * @default metadata_item * @constant + * @enum {string} */ type: "metadata_item"; }; @@ -7599,6 +7845,7 @@ export type components = { * type * @default metadata_item_output * @constant + * @enum {string} */ type: "metadata_item_output"; }; @@ -7610,6 +7857,7 @@ export type components = { * type * @default metadata_output * @constant + * @enum {string} */ type: "metadata_output"; }; @@ -7669,6 +7917,7 @@ export type components = { * type * @default midas_depth_image_processor * @constant + * @enum {string} */ type: "midas_depth_image_processor"; }; @@ -7728,6 +7977,7 @@ export type components = { * type * @default mlsd_image_processor * @constant + * @enum {string} */ type: "mlsd_image_processor"; }; @@ -7858,6 +8108,7 @@ export type components = { * type * @default model_loader_output * @constant + * @enum {string} */ type: "model_loader_output"; /** @@ -7988,6 +8239,7 @@ export type components = { * type * @default mul * @constant + * @enum {string} */ type: "mul"; }; @@ -8059,6 +8311,7 @@ export type components = { * type * @default noise * @constant + * @enum {string} */ type: "noise"; }; @@ -8083,6 +8336,7 @@ export type components = { * type * @default noise_output * @constant + * @enum {string} */ type: "noise_output"; }; @@ -8130,6 +8384,7 @@ export type components = { * type * @default normalbae_image_processor * @constant + * @enum {string} */ type: "normalbae_image_processor"; }; @@ -8237,6 +8492,7 @@ export type components = { * type * @default pair_tile_image * @constant + * @enum {string} */ type: "pair_tile_image"; }; @@ -8248,6 +8504,7 @@ export type components = { * type * @default pair_tile_image_output * @constant + * @enum {string} */ type: "pair_tile_image_output"; }; @@ -8307,6 +8564,7 @@ export type components = { * type * @default pidi_image_processor * @constant + * @enum {string} */ type: "pidi_image_processor"; }; @@ -8363,6 +8621,7 @@ export type components = { * type * @default prompt_from_file * @constant + * @enum {string} */ type: "prompt_from_file"; }; @@ -8421,6 +8680,7 @@ export type components = { * type * @default rand_float * @constant + * @enum {string} */ type: "rand_float"; }; @@ -8462,6 +8722,7 @@ export type components = { * type * @default rand_int * @constant + * @enum {string} */ type: "rand_int"; }; @@ -8515,6 +8776,7 @@ export type components = { * type * @default random_range * @constant + * @enum {string} */ type: "random_range"; }; @@ -8562,6 +8824,7 @@ export type components = { * type * @default range * @constant + * @enum {string} */ type: "range"; }; @@ -8609,6 +8872,7 @@ export type components = { * type * @default range_of_size * @constant + * @enum {string} */ type: "range_of_size"; }; @@ -8670,6 +8934,7 @@ export type components = { * type * @default rectangle_mask * @constant + * @enum {string} */ type: "rectangle_mask"; }; @@ -8760,6 +9025,7 @@ export type components = { * type * @default lresize * @constant + * @enum {string} */ type: "lresize"; }; @@ -8811,6 +9077,7 @@ export type components = { * type * @default round_float * @constant + * @enum {string} */ type: "round_float"; }; @@ -8894,9 +9161,60 @@ export type components = { * type * @default sdxl_compel_prompt * @constant + * @enum {string} */ type: "sdxl_compel_prompt"; }; + /** + * SDXL LoRA Collection Loader + * @description Applies a collection of SDXL LoRAs to the provided UNet and CLIP models. + */ + SDXLLoRACollectionLoader: { + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * LoRAs + * @description LoRA models and weights. May be a single LoRA or collection. + */ + loras?: components["schemas"]["LoRAField"] | components["schemas"]["LoRAField"][]; + /** + * UNet + * @description UNet (scheduler, LoRAs) + */ + unet?: components["schemas"]["UNetField"] | null; + /** + * CLIP + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip?: components["schemas"]["CLIPField"] | null; + /** + * CLIP 2 + * @description CLIP (tokenizer, text encoder, LoRAs) and skipped layer count + */ + clip2?: components["schemas"]["CLIPField"] | null; + /** + * type + * @default sdxl_lora_collection_loader + * @constant + * @enum {string} + */ + type: "sdxl_lora_collection_loader"; + }; /** * SDXL LoRA * @description Apply selected lora to unet and text_encoder. @@ -8949,6 +9267,7 @@ export type components = { * type * @default sdxl_lora_loader * @constant + * @enum {string} */ type: "sdxl_lora_loader"; }; @@ -8979,6 +9298,7 @@ export type components = { * type * @default sdxl_lora_loader_output * @constant + * @enum {string} */ type: "sdxl_lora_loader_output"; }; @@ -9010,6 +9330,7 @@ export type components = { * type * @default sdxl_model_loader * @constant + * @enum {string} */ type: "sdxl_model_loader"; }; @@ -9042,6 +9363,7 @@ export type components = { * type * @default sdxl_model_loader_output * @constant + * @enum {string} */ type: "sdxl_model_loader_output"; }; @@ -9105,6 +9427,7 @@ export type components = { * type * @default sdxl_refiner_compel_prompt * @constant + * @enum {string} */ type: "sdxl_refiner_compel_prompt"; }; @@ -9136,6 +9459,7 @@ export type components = { * type * @default sdxl_refiner_model_loader * @constant + * @enum {string} */ type: "sdxl_refiner_model_loader"; }; @@ -9163,6 +9487,7 @@ export type components = { * type * @default sdxl_refiner_model_loader_output * @constant + * @enum {string} */ type: "sdxl_refiner_model_loader_output"; }; @@ -9203,6 +9528,7 @@ export type components = { * type * @default save_image * @constant + * @enum {string} */ type: "save_image"; }; @@ -9252,6 +9578,7 @@ export type components = { * type * @default lscale * @constant + * @enum {string} */ type: "lscale"; }; @@ -9288,6 +9615,7 @@ export type components = { * type * @default scheduler * @constant + * @enum {string} */ type: "scheduler"; }; @@ -9303,6 +9631,7 @@ export type components = { * type * @default scheduler_output * @constant + * @enum {string} */ type: "scheduler_output"; }; @@ -9360,6 +9689,7 @@ export type components = { * type * @default seamless * @constant + * @enum {string} */ type: "seamless"; }; @@ -9384,6 +9714,7 @@ export type components = { * type * @default seamless_output * @constant + * @enum {string} */ type: "seamless_output"; }; @@ -9431,6 +9762,7 @@ export type components = { * type * @default segment_anything_processor * @constant + * @enum {string} */ type: "segment_anything_processor"; }; @@ -9672,6 +10004,7 @@ export type components = { * type * @default show_image * @constant + * @enum {string} */ type: "show_image"; }; @@ -9794,6 +10127,7 @@ export type components = { * type * @default step_param_easing * @constant + * @enum {string} */ type: "step_param_easing"; }; @@ -9816,6 +10150,7 @@ export type components = { * type * @default string_2_output * @constant + * @enum {string} */ type: "string_2_output"; }; @@ -9851,6 +10186,7 @@ export type components = { * type * @default string_collection * @constant + * @enum {string} */ type: "string_collection"; }; @@ -9868,6 +10204,7 @@ export type components = { * type * @default string_collection_output * @constant + * @enum {string} */ type: "string_collection_output"; }; @@ -9903,6 +10240,7 @@ export type components = { * type * @default string * @constant + * @enum {string} */ type: "string"; }; @@ -9944,6 +10282,7 @@ export type components = { * type * @default string_join * @constant + * @enum {string} */ type: "string_join"; }; @@ -9991,6 +10330,7 @@ export type components = { * type * @default string_join_three * @constant + * @enum {string} */ type: "string_join_three"; }; @@ -10008,6 +10348,7 @@ export type components = { * type * @default string_output * @constant + * @enum {string} */ type: "string_output"; }; @@ -10030,6 +10371,7 @@ export type components = { * type * @default string_pos_neg_output * @constant + * @enum {string} */ type: "string_pos_neg_output"; }; @@ -10083,6 +10425,7 @@ export type components = { * type * @default string_replace * @constant + * @enum {string} */ type: "string_replace"; }; @@ -10124,6 +10467,7 @@ export type components = { * type * @default string_split * @constant + * @enum {string} */ type: "string_split"; }; @@ -10159,6 +10503,7 @@ export type components = { * type * @default string_split_neg * @constant + * @enum {string} */ type: "string_split_neg"; }; @@ -10206,6 +10551,7 @@ export type components = { * type * @default sub * @constant + * @enum {string} */ type: "sub"; }; @@ -10263,6 +10609,7 @@ export type components = { /** * Format * @constant + * @enum {string} */ format: "diffusers"; /** @default */ @@ -10271,6 +10618,7 @@ export type components = { * Type * @default t2i_adapter * @constant + * @enum {string} */ type: "t2i_adapter"; }; @@ -10364,6 +10712,7 @@ export type components = { * type * @default t2i_adapter * @constant + * @enum {string} */ type: "t2i_adapter"; }; @@ -10412,6 +10761,7 @@ export type components = { * type * @default t2i_adapter_output * @constant + * @enum {string} */ type: "t2i_adapter_output"; }; @@ -10490,12 +10840,14 @@ export type components = { * Type * @default embedding * @constant + * @enum {string} */ type: "embedding"; /** * Format * @default embedding_file * @constant + * @enum {string} */ format: "embedding_file"; }; @@ -10552,12 +10904,14 @@ export type components = { * Type * @default embedding * @constant + * @enum {string} */ type: "embedding"; /** * Format * @default embedding_folder * @constant + * @enum {string} */ format: "embedding_folder"; }; @@ -10606,6 +10960,7 @@ export type components = { * type * @default tile_image_processor * @constant + * @enum {string} */ type: "tile_image_processor"; }; @@ -10637,6 +10992,7 @@ export type components = { * type * @default tile_to_properties * @constant + * @enum {string} */ type: "tile_to_properties"; }; @@ -10696,6 +11052,7 @@ export type components = { * type * @default tile_to_properties_output * @constant + * @enum {string} */ type: "tile_to_properties_output"; }; @@ -10740,6 +11097,7 @@ export type components = { * type * @default unet_output * @constant + * @enum {string} */ type: "unet_output"; }; @@ -10759,6 +11117,7 @@ export type components = { * Type * @default url * @constant + * @enum {string} */ type?: "url"; }; @@ -10806,6 +11165,7 @@ export type components = { * type * @default unsharp_mask * @constant + * @enum {string} */ type: "unsharp_mask"; }; @@ -10875,6 +11235,7 @@ export type components = { * Format * @default checkpoint * @constant + * @enum {string} */ format: "checkpoint"; /** @@ -10891,6 +11252,7 @@ export type components = { * Type * @default vae * @constant + * @enum {string} */ type: "vae"; }; @@ -10947,12 +11309,14 @@ export type components = { * Type * @default vae * @constant + * @enum {string} */ type: "vae"; /** * Format * @default diffusers * @constant + * @enum {string} */ format: "diffusers"; }; @@ -10997,6 +11361,7 @@ export type components = { * type * @default vae_loader * @constant + * @enum {string} */ type: "vae_loader"; }; @@ -11014,6 +11379,7 @@ export type components = { * type * @default vae_output * @constant + * @enum {string} */ type: "vae_output"; }; @@ -11090,6 +11456,19 @@ export type components = { */ id: string; }; + /** WorkflowAndGraphResponse */ + WorkflowAndGraphResponse: { + /** + * Workflow + * @description The workflow used to generate the image, as stringified JSON + */ + workflow: string | null; + /** + * Graph + * @description The graph used to generate the image, as stringified JSON + */ + graph: string | null; + }; /** * WorkflowCategory * @enum {string} @@ -11270,6 +11649,7 @@ export type components = { * type * @default zoe_depth_image_processor * @constant + * @enum {string} */ type: "zoe_depth_image_processor"; }; @@ -11460,6 +11840,145 @@ export type components = { * @enum {string} */ UIType: "MainModelField" | "SDXLMainModelField" | "SDXLRefinerModelField" | "ONNXModelField" | "VAEModelField" | "LoRAModelField" | "ControlNetModelField" | "IPAdapterModelField" | "T2IAdapterModelField" | "SchedulerField" | "AnyField" | "CollectionField" | "CollectionItemField" | "DEPRECATED_Boolean" | "DEPRECATED_Color" | "DEPRECATED_Conditioning" | "DEPRECATED_Control" | "DEPRECATED_Float" | "DEPRECATED_Image" | "DEPRECATED_Integer" | "DEPRECATED_Latents" | "DEPRECATED_String" | "DEPRECATED_BooleanCollection" | "DEPRECATED_ColorCollection" | "DEPRECATED_ConditioningCollection" | "DEPRECATED_ControlCollection" | "DEPRECATED_FloatCollection" | "DEPRECATED_ImageCollection" | "DEPRECATED_IntegerCollection" | "DEPRECATED_LatentsCollection" | "DEPRECATED_StringCollection" | "DEPRECATED_BooleanPolymorphic" | "DEPRECATED_ColorPolymorphic" | "DEPRECATED_ConditioningPolymorphic" | "DEPRECATED_ControlPolymorphic" | "DEPRECATED_FloatPolymorphic" | "DEPRECATED_ImagePolymorphic" | "DEPRECATED_IntegerPolymorphic" | "DEPRECATED_LatentsPolymorphic" | "DEPRECATED_StringPolymorphic" | "DEPRECATED_UNet" | "DEPRECATED_Vae" | "DEPRECATED_CLIP" | "DEPRECATED_Collection" | "DEPRECATED_CollectionItem" | "DEPRECATED_Enum" | "DEPRECATED_WorkflowField" | "DEPRECATED_IsIntermediate" | "DEPRECATED_BoardField" | "DEPRECATED_MetadataItem" | "DEPRECATED_MetadataItemCollection" | "DEPRECATED_MetadataItemPolymorphic" | "DEPRECATED_MetadataDict"; + InvocationOutputMap: { + ideal_size: components["schemas"]["IdealSizeOutput"]; + lora_collection_loader: components["schemas"]["LoRALoaderOutput"]; + color_map_image_processor: components["schemas"]["ImageOutput"]; + img_resize: components["schemas"]["ImageOutput"]; + calculate_image_tiles_min_overlap: components["schemas"]["CalculateImageTilesOutput"]; + lineart_image_processor: components["schemas"]["ImageOutput"]; + boolean_collection: components["schemas"]["BooleanCollectionOutput"]; + ip_adapter: components["schemas"]["IPAdapterOutput"]; + face_mask_detection: components["schemas"]["FaceMaskOutput"]; + string_replace: components["schemas"]["StringOutput"]; + infill_lama: components["schemas"]["ImageOutput"]; + calculate_image_tiles_even_split: components["schemas"]["CalculateImageTilesOutput"]; + tile_image_processor: components["schemas"]["ImageOutput"]; + calculate_image_tiles: components["schemas"]["CalculateImageTilesOutput"]; + img_blur: components["schemas"]["ImageOutput"]; + scheduler: components["schemas"]["SchedulerOutput"]; + range: components["schemas"]["IntegerCollectionOutput"]; + lora_selector: components["schemas"]["LoRASelectorOutput"]; + metadata: components["schemas"]["MetadataOutput"]; + clip_skip: components["schemas"]["CLIPSkipInvocationOutput"]; + rand_float: components["schemas"]["FloatOutput"]; + float_collection: components["schemas"]["FloatCollectionOutput"]; + zoe_depth_image_processor: components["schemas"]["ImageOutput"]; + create_gradient_mask: components["schemas"]["GradientMaskOutput"]; + i2l: components["schemas"]["LatentsOutput"]; + dynamic_prompt: components["schemas"]["StringCollectionOutput"]; + create_denoise_mask: components["schemas"]["DenoiseMaskOutput"]; + img_ilerp: components["schemas"]["ImageOutput"]; + tile_to_properties: components["schemas"]["TileToPropertiesOutput"]; + infill_cv2: components["schemas"]["ImageOutput"]; + string_join_three: components["schemas"]["StringOutput"]; + denoise_latents: components["schemas"]["LatentsOutput"]; + iterate: components["schemas"]["IterateInvocationOutput"]; + step_param_easing: components["schemas"]["FloatCollectionOutput"]; + img_nsfw: components["schemas"]["ImageOutput"]; + infill_patchmatch: components["schemas"]["ImageOutput"]; + pair_tile_image: components["schemas"]["PairTileImageOutput"]; + alpha_mask_to_tensor: components["schemas"]["MaskOutput"]; + lora_loader: components["schemas"]["LoRALoaderOutput"]; + normalbae_image_processor: components["schemas"]["ImageOutput"]; + img_hue_adjust: components["schemas"]["ImageOutput"]; + conditioning_collection: components["schemas"]["ConditioningCollectionOutput"]; + image_mask_to_tensor: components["schemas"]["MaskOutput"]; + t2i_adapter: components["schemas"]["T2IAdapterOutput"]; + infill_rgba: components["schemas"]["ImageOutput"]; + vae_loader: components["schemas"]["VAEOutput"]; + blank_image: components["schemas"]["ImageOutput"]; + latents: components["schemas"]["LatentsOutput"]; + sdxl_lora_loader: components["schemas"]["SDXLLoRALoaderOutput"]; + boolean: components["schemas"]["BooleanOutput"]; + float_range: components["schemas"]["FloatCollectionOutput"]; + integer: components["schemas"]["IntegerOutput"]; + mul: components["schemas"]["IntegerOutput"]; + img_crop: components["schemas"]["ImageOutput"]; + face_identifier: components["schemas"]["ImageOutput"]; + main_model_loader: components["schemas"]["ModelLoaderOutput"]; + mlsd_image_processor: components["schemas"]["ImageOutput"]; + esrgan: components["schemas"]["ImageOutput"]; + integer_math: components["schemas"]["IntegerOutput"]; + sdxl_lora_collection_loader: components["schemas"]["SDXLLoRALoaderOutput"]; + img_chan: components["schemas"]["ImageOutput"]; + round_float: components["schemas"]["FloatOutput"]; + random_range: components["schemas"]["IntegerCollectionOutput"]; + image_collection: components["schemas"]["ImageCollectionOutput"]; + sub: components["schemas"]["IntegerOutput"]; + lblend: components["schemas"]["LatentsOutput"]; + sdxl_refiner_model_loader: components["schemas"]["SDXLRefinerModelLoaderOutput"]; + cv_inpaint: components["schemas"]["ImageOutput"]; + sdxl_model_loader: components["schemas"]["SDXLModelLoaderOutput"]; + invert_tensor_mask: components["schemas"]["MaskOutput"]; + image: components["schemas"]["ImageOutput"]; + img_mul: components["schemas"]["ImageOutput"]; + l2i: components["schemas"]["ImageOutput"]; + canny_image_processor: components["schemas"]["ImageOutput"]; + save_image: components["schemas"]["ImageOutput"]; + string_split: components["schemas"]["String2Output"]; + segment_anything_processor: components["schemas"]["ImageOutput"]; + heuristic_resize: components["schemas"]["ImageOutput"]; + face_off: components["schemas"]["FaceOffOutput"]; + img_channel_offset: components["schemas"]["ImageOutput"]; + img_conv: components["schemas"]["ImageOutput"]; + add: components["schemas"]["IntegerOutput"]; + infill_tile: components["schemas"]["ImageOutput"]; + color: components["schemas"]["ColorOutput"]; + mediapipe_face_processor: components["schemas"]["ImageOutput"]; + freeu: components["schemas"]["UNetOutput"]; + pidi_image_processor: components["schemas"]["ImageOutput"]; + depth_anything_image_processor: components["schemas"]["ImageOutput"]; + noise: components["schemas"]["NoiseOutput"]; + collect: components["schemas"]["CollectInvocationOutput"]; + content_shuffle_image_processor: components["schemas"]["ImageOutput"]; + string_split_neg: components["schemas"]["StringPosNegOutput"]; + img_lerp: components["schemas"]["ImageOutput"]; + leres_image_processor: components["schemas"]["ImageOutput"]; + div: components["schemas"]["IntegerOutput"]; + lscale: components["schemas"]["LatentsOutput"]; + metadata_item: components["schemas"]["MetadataItemOutput"]; + seamless: components["schemas"]["SeamlessModeOutput"]; + img_paste: components["schemas"]["ImageOutput"]; + string: components["schemas"]["StringOutput"]; + mask_combine: components["schemas"]["ImageOutput"]; + float_math: components["schemas"]["FloatOutput"]; + tomask: components["schemas"]["ImageOutput"]; + img_channel_multiply: components["schemas"]["ImageOutput"]; + sdxl_compel_prompt: components["schemas"]["ConditioningOutput"]; + mask_edge: components["schemas"]["ImageOutput"]; + merge_tiles_to_image: components["schemas"]["ImageOutput"]; + range_of_size: components["schemas"]["IntegerCollectionOutput"]; + sdxl_refiner_compel_prompt: components["schemas"]["ConditioningOutput"]; + canvas_paste_back: components["schemas"]["ImageOutput"]; + controlnet: components["schemas"]["ControlOutput"]; + dw_openpose_image_processor: components["schemas"]["ImageOutput"]; + string_collection: components["schemas"]["StringCollectionOutput"]; + float_to_int: components["schemas"]["IntegerOutput"]; + color_correct: components["schemas"]["ImageOutput"]; + unsharp_mask: components["schemas"]["ImageOutput"]; + float: components["schemas"]["FloatOutput"]; + rand_int: components["schemas"]["IntegerOutput"]; + mask_from_id: components["schemas"]["ImageOutput"]; + latents_collection: components["schemas"]["LatentsCollectionOutput"]; + conditioning: components["schemas"]["ConditioningOutput"]; + integer_collection: components["schemas"]["IntegerCollectionOutput"]; + string_join: components["schemas"]["StringOutput"]; + compel: components["schemas"]["ConditioningOutput"]; + crop_latents: components["schemas"]["LatentsOutput"]; + img_watermark: components["schemas"]["ImageOutput"]; + rectangle_mask: components["schemas"]["MaskOutput"]; + prompt_from_file: components["schemas"]["StringCollectionOutput"]; + merge_metadata: components["schemas"]["MetadataOutput"]; + img_pad_crop: components["schemas"]["ImageOutput"]; + midas_depth_image_processor: components["schemas"]["ImageOutput"]; + core_metadata: components["schemas"]["MetadataOutput"]; + show_image: components["schemas"]["ImageOutput"]; + hed_image_processor: components["schemas"]["ImageOutput"]; + lresize: components["schemas"]["LatentsOutput"]; + lineart_anime_image_processor: components["schemas"]["ImageOutput"]; + img_scale: components["schemas"]["ImageOutput"]; + }; }; responses: never; parameters: never; @@ -12402,7 +12921,7 @@ export type operations = { /** @description Successful Response */ 200: { content: { - "application/json": components["schemas"]["WorkflowWithoutID"] | null; + "application/json": components["schemas"]["WorkflowAndGraphResponse"]; }; }; /** @description Validation Error */ diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index a153780712..1160a2bee5 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -16,6 +16,9 @@ export type UpdateBoardArg = paths['/api/v1/boards/{board_id}']['patch']['parame changes: paths['/api/v1/boards/{board_id}']['patch']['requestBody']['content']['application/json']; }; +export type GraphAndWorkflowResponse = + paths['/api/v1/images/i/{image_name}/workflow']['get']['responses']['200']['content']['application/json']; + export type BatchConfig = paths['/api/v1/queue/{queue_id}/enqueue_batch']['post']['requestBody']['content']['application/json']; @@ -121,7 +124,6 @@ export type ModelInstallStatus = S['InstallStatus']; // Graphs export type Graph = S['Graph']; export type NonNullableGraph = O.Required; -export type Edge = S['Edge']; export type GraphExecutionState = S['GraphExecutionState']; export type Batch = S['Batch']; export type SessionQueueItemDTO = S['SessionQueueItemDTO']; @@ -129,43 +131,31 @@ export type WorkflowRecordOrderBy = S['WorkflowRecordOrderBy']; export type SQLiteDirection = S['SQLiteDirection']; export type WorkflowRecordListItemDTO = S['WorkflowRecordListItemDTO']; -// General nodes -export type CollectInvocation = S['CollectInvocation']; -export type ImageResizeInvocation = S['ImageResizeInvocation']; -export type InfillPatchMatchInvocation = S['InfillPatchMatchInvocation']; -export type InfillTileInvocation = S['InfillTileInvocation']; -export type CreateGradientMaskInvocation = S['CreateGradientMaskInvocation']; -export type CanvasPasteBackInvocation = S['CanvasPasteBackInvocation']; -export type NoiseInvocation = S['NoiseInvocation']; -export type DenoiseLatentsInvocation = S['DenoiseLatentsInvocation']; -export type SDXLLoRALoaderInvocation = S['SDXLLoRALoaderInvocation']; -export type ImageToLatentsInvocation = S['ImageToLatentsInvocation']; -export type LatentsToImageInvocation = S['LatentsToImageInvocation']; -export type LoRALoaderInvocation = S['LoRALoaderInvocation']; -export type ESRGANInvocation = S['ESRGANInvocation']; -export type ImageNSFWBlurInvocation = S['ImageNSFWBlurInvocation']; -export type ImageWatermarkInvocation = S['ImageWatermarkInvocation']; -export type SeamlessModeInvocation = S['SeamlessModeInvocation']; -export type CoreMetadataInvocation = S['CoreMetadataInvocation']; +type KeysOfUnion = T extends T ? keyof T : never; -// ControlNet Nodes -export type ControlNetInvocation = S['ControlNetInvocation']; -export type T2IAdapterInvocation = S['T2IAdapterInvocation']; -export type IPAdapterInvocation = S['IPAdapterInvocation']; -export type CannyImageProcessorInvocation = S['CannyImageProcessorInvocation']; -export type ColorMapImageProcessorInvocation = S['ColorMapImageProcessorInvocation']; -export type ContentShuffleImageProcessorInvocation = S['ContentShuffleImageProcessorInvocation']; -export type DepthAnythingImageProcessorInvocation = S['DepthAnythingImageProcessorInvocation']; -export type HedImageProcessorInvocation = S['HedImageProcessorInvocation']; -export type LineartAnimeImageProcessorInvocation = S['LineartAnimeImageProcessorInvocation']; -export type LineartImageProcessorInvocation = S['LineartImageProcessorInvocation']; -export type MediapipeFaceProcessorInvocation = S['MediapipeFaceProcessorInvocation']; -export type MidasDepthImageProcessorInvocation = S['MidasDepthImageProcessorInvocation']; -export type MlsdImageProcessorInvocation = S['MlsdImageProcessorInvocation']; -export type NormalbaeImageProcessorInvocation = S['NormalbaeImageProcessorInvocation']; -export type DWOpenposeImageProcessorInvocation = S['DWOpenposeImageProcessorInvocation']; -export type PidiImageProcessorInvocation = S['PidiImageProcessorInvocation']; -export type ZoeDepthImageProcessorInvocation = S['ZoeDepthImageProcessorInvocation']; +export type AnyInvocation = Exclude< + Graph['nodes'][string], + S['CoreMetadataInvocation'] | S['MetadataInvocation'] | S['MetadataItemInvocation'] | S['MergeMetadataInvocation'] +>; +export type AnyInvocationIncMetadata = S['Graph']['nodes'][string]; + +export type InvocationType = AnyInvocation['type']; +type InvocationOutputMap = S['InvocationOutputMap']; +type AnyInvocationOutput = InvocationOutputMap[InvocationType]; + +export type Invocation = Extract; +// export type InvocationOutput = InvocationOutputMap[T]; + +type NonInputFields = 'id' | 'type' | 'is_intermediate' | 'use_cache' | 'board' | 'metadata'; +export type AnyInvocationInputField = Exclude>, NonInputFields>; +export type InputFields = Extract; + +type NonOutputFields = 'type'; +export type AnyInvocationOutputField = Exclude>, NonOutputFields>; +export type OutputFields = Extract< + keyof InvocationOutputMap[T['type']], + AnyInvocationOutputField +>; // Node Outputs export type ImageOutput = S['ImageOutput']; diff --git a/invokeai/frontend/web/tsconfig.json b/invokeai/frontend/web/tsconfig.json index 91906c9abe..b1e4ebfc0b 100644 --- a/invokeai/frontend/web/tsconfig.json +++ b/invokeai/frontend/web/tsconfig.json @@ -15,6 +15,7 @@ // "resolveJsonModule": true, "noUncheckedIndexedAccess": true, "strictNullChecks": true, + "strictPropertyInitialization": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", diff --git a/invokeai/frontend/web/vite.config.mts b/invokeai/frontend/web/vite.config.mts index 21edc076db..a40c515465 100644 --- a/invokeai/frontend/web/vite.config.mts +++ b/invokeai/frontend/web/vite.config.mts @@ -93,6 +93,11 @@ export default defineConfig(({ mode }) => { enabled: true, ignoreSourceErrors: true, }, + coverage: { + provider: 'v8', + all: false, + reporter: ['html'], + }, }, }; }); diff --git a/invokeai/version/invokeai_version.py b/invokeai/version/invokeai_version.py index afcedcd6bb..aef46acb47 100644 --- a/invokeai/version/invokeai_version.py +++ b/invokeai/version/invokeai_version.py @@ -1 +1 @@ -__version__ = "4.2.0b1" +__version__ = "4.2.1"