From 4333852c37b898c31626f682cd90aa82e066bac7 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 18:36:46 +1000 Subject: [PATCH 01/42] fix(nodes): fix missing `context` arg in LatentsToLatents --- invokeai/app/invocations/latent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index c6ddcb0396..40575c1f64 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -285,7 +285,7 @@ class LatentsToLatentsInvocation(TextToLatentsInvocation): self.dispatch_progress(context, source_node_id, state) model = self.get_model(context.services.model_manager) - conditioning_data = self.get_conditioning_data(model) + conditioning_data = self.get_conditioning_data(context, model) # TODO: Verify the noise is the right size From 93ced0bec6879494d01419d56444aa592755d7f5 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 20:23:02 +1000 Subject: [PATCH 02/42] feat(nodes): add w/h to latents outputs This reduces the number of nodes needed when working with latents (ie fewer plain integer value nodes) Also correct a few mistakes in the fields --- invokeai/app/invocations/latent.py | 65 +++++++++++++++++++----------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 40575c1f64..3b6eb95fa0 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -40,17 +40,40 @@ class LatentsField(BaseModel): class LatentsOutput(BaseInvocationOutput): """Base class for invocations that output latents""" #fmt: off - type: Literal["latent_output"] = "latent_output" - latents: LatentsField = Field(default=None, description="The output latents") + type: Literal["latents_output"] = "latents_output" + + # Inputs + latents: LatentsField = Field(default=None, description="The output latents") + width: int = Field(description="The width of the latents in pixels") + height: int = Field(description="The height of the latents in pixels") #fmt: on + +def build_latents_output(latents_name: str, latents: torch.Tensor): + return LatentsOutput( + latents=LatentsField(latents_name=latents_name), + width=latents.size()[3] * 8, + height=latents.size()[2] * 8, + ) + class NoiseOutput(BaseInvocationOutput): """Invocation noise output""" #fmt: off - type: Literal["noise_output"] = "noise_output" + type: Literal["noise_output"] = "noise_output" + + # Inputs noise: LatentsField = Field(default=None, description="The output noise") + width: int = Field(description="The width of the noise in pixels") + height: int = Field(description="The height of the noise in pixels") #fmt: on +def build_noise_output(latents_name: str, latents: torch.Tensor): + return NoiseOutput( + noise=LatentsField(latents_name=latents_name), + width=latents.size()[3] * 8, + height=latents.size()[2] * 8, + ) + # TODO: this seems like a hack scheduler_map = dict( @@ -130,9 +153,7 @@ class NoiseInvocation(BaseInvocation): name = f'{context.graph_execution_state_id}__{self.id}' context.services.latents.set(name, noise) - return NoiseOutput( - noise=LatentsField(latents_name=name) - ) + return build_noise_output(latents_name=name, latents=noise) # Text to image @@ -248,9 +269,7 @@ class TextToLatentsInvocation(BaseInvocation): name = f'{context.graph_execution_state_id}__{self.id}' context.services.latents.set(name, result_latents) - return LatentsOutput( - latents=LatentsField(latents_name=name) - ) + return build_latents_output(latents_name=name, latents=result_latents) class LatentsToLatentsInvocation(TextToLatentsInvocation): @@ -313,9 +332,7 @@ class LatentsToLatentsInvocation(TextToLatentsInvocation): name = f'{context.graph_execution_state_id}__{self.id}' context.services.latents.set(name, result_latents) - return LatentsOutput( - latents=LatentsField(latents_name=name) - ) + return build_latents_output(latents_name=name, latents=result_latents) # Latent to image @@ -379,11 +396,11 @@ class ResizeLatentsInvocation(BaseInvocation): type: Literal["lresize"] = "lresize" # Inputs - latents: Optional[LatentsField] = Field(description="The latents to resize") - width: int = Field(ge=64, multiple_of=8, description="The width to resize to (px)") - height: int = Field(ge=64, multiple_of=8, description="The height to resize to (px)") - mode: Optional[LATENTS_INTERPOLATION_MODE] = Field(default="bilinear", description="The interpolation mode") - antialias: Optional[bool] = Field(default=False, description="Whether or not to antialias (applied in bilinear and bicubic modes only)") + latents: Optional[LatentsField] = Field(description="The latents to resize") + width: int = Field(ge=64, multiple_of=8, description="The width to resize to (px)") + height: int = Field(ge=64, multiple_of=8, description="The height to resize to (px)") + mode: LATENTS_INTERPOLATION_MODE = Field(default="bilinear", description="The interpolation mode") + antialias: bool = Field(default=False, description="Whether or not to antialias (applied in bilinear and bicubic modes only)") def invoke(self, context: InvocationContext) -> LatentsOutput: latents = context.services.latents.get(self.latents.latents_name) @@ -400,7 +417,7 @@ class ResizeLatentsInvocation(BaseInvocation): name = f"{context.graph_execution_state_id}__{self.id}" context.services.latents.set(name, resized_latents) - return LatentsOutput(latents=LatentsField(latents_name=name)) + return build_latents_output(latents_name=name, latents=resized_latents) class ScaleLatentsInvocation(BaseInvocation): @@ -409,10 +426,10 @@ class ScaleLatentsInvocation(BaseInvocation): type: Literal["lscale"] = "lscale" # Inputs - latents: Optional[LatentsField] = Field(description="The latents to scale") - scale_factor: float = Field(gt=0, description="The factor by which to scale the latents") - mode: Optional[LATENTS_INTERPOLATION_MODE] = Field(default="bilinear", description="The interpolation mode") - antialias: Optional[bool] = Field(default=False, description="Whether or not to antialias (applied in bilinear and bicubic modes only)") + latents: Optional[LatentsField] = Field(description="The latents to scale") + scale_factor: float = Field(gt=0, description="The factor by which to scale the latents") + mode: LATENTS_INTERPOLATION_MODE = Field(default="bilinear", description="The interpolation mode") + antialias: bool = Field(default=False, description="Whether or not to antialias (applied in bilinear and bicubic modes only)") def invoke(self, context: InvocationContext) -> LatentsOutput: latents = context.services.latents.get(self.latents.latents_name) @@ -430,7 +447,7 @@ class ScaleLatentsInvocation(BaseInvocation): name = f"{context.graph_execution_state_id}__{self.id}" context.services.latents.set(name, resized_latents) - return LatentsOutput(latents=LatentsField(latents_name=name)) + return build_latents_output(latents_name=name, latents=resized_latents) class ImageToLatentsInvocation(BaseInvocation): @@ -474,4 +491,4 @@ class ImageToLatentsInvocation(BaseInvocation): name = f"{context.graph_execution_state_id}__{self.id}" context.services.latents.set(name, latents) - return LatentsOutput(latents=LatentsField(latents_name=name)) + return build_latents_output(latents_name=name, latents=latents) From 483f2ccb56aebc4f464c9eeac1f0362c35049255 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 20:31:48 +1000 Subject: [PATCH 03/42] feat(nodes): add RandomIntInvocation just outputs a single random int --- invokeai/app/invocations/math.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/invokeai/app/invocations/math.py b/invokeai/app/invocations/math.py index afb0e75377..b7d2bc5af6 100644 --- a/invokeai/app/invocations/math.py +++ b/invokeai/app/invocations/math.py @@ -3,6 +3,9 @@ from typing import Literal from pydantic import BaseModel, Field +import numpy as np + +from invokeai.app.util.misc import get_random_seed from .baseinvocation import BaseInvocation, BaseInvocationOutput, InvocationContext, InvocationConfig @@ -73,3 +76,12 @@ class DivideInvocation(BaseInvocation, MathInvocationConfig): def invoke(self, context: InvocationContext) -> IntOutput: return IntOutput(a=int(self.a / self.b)) + + +class RandomIntInvocation(BaseInvocation): + """Outputs a single random integer.""" + #fmt: off + type: Literal["rand_int"] = "rand_int" + #fmt: on + def invoke(self, context: InvocationContext) -> IntOutput: + return IntOutput(a=np.random.randint(0, np.iinfo(np.int32).max)) \ No newline at end of file From 06b5800d28667ff085c90beb9088bfbfcdd3e3fa Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Thu, 11 May 2023 20:52:37 +1200 Subject: [PATCH 04/42] Add UniPC Scheduler --- invokeai/app/invocations/latent.py | 32 +++++++++--------- invokeai/backend/args.py | 1 + invokeai/backend/generate.py | 25 +++++++------- invokeai/backend/generator/base.py | 28 ++++++++-------- .../convert_ckpt_to_diffusers.py | 3 ++ .../stable_diffusion/diffusers_pipeline.py | 33 ++++++++++--------- invokeai/backend/web/modules/parameters.py | 1 + invokeai/frontend/web/src/app/constants.ts | 1 + 8 files changed, 67 insertions(+), 57 deletions(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index c6ddcb0396..82396e358a 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -54,16 +54,17 @@ class NoiseOutput(BaseInvocationOutput): # TODO: this seems like a hack scheduler_map = dict( - ddim=diffusers.DDIMScheduler, - dpmpp_2=diffusers.DPMSolverMultistepScheduler, - k_dpm_2=diffusers.KDPM2DiscreteScheduler, - k_dpm_2_a=diffusers.KDPM2AncestralDiscreteScheduler, - k_dpmpp_2=diffusers.DPMSolverMultistepScheduler, - k_euler=diffusers.EulerDiscreteScheduler, - k_euler_a=diffusers.EulerAncestralDiscreteScheduler, - k_heun=diffusers.HeunDiscreteScheduler, - k_lms=diffusers.LMSDiscreteScheduler, - plms=diffusers.PNDMScheduler, + ddim=(diffusers.DDIMScheduler, dict()), + dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), + k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict()), + k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict()), + k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), + k_euler=(diffusers.EulerDiscreteScheduler, dict()), + k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict()), + k_heun=(diffusers.HeunDiscreteScheduler, dict()), + k_lms=(diffusers.LMSDiscreteScheduler, dict()), + plms=(diffusers.PNDMScheduler, dict()), + unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) ) @@ -73,8 +74,9 @@ SAMPLER_NAME_VALUES = Literal[ def get_scheduler(scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: - scheduler_class = scheduler_map.get(scheduler_name,'ddim') - scheduler = scheduler_class.from_config(model.scheduler.config) + scheduler_class, scheduler_extra_config = scheduler_map.get(scheduler_name,'ddim') + scheduler_config = {**model.scheduler.config, **scheduler_extra_config} + scheduler = scheduler_class.from_config(scheduler_config) # hack copied over from generate.py if not hasattr(scheduler, 'uses_inpainting_model'): scheduler.uses_inpainting_model = lambda: False @@ -293,11 +295,7 @@ class LatentsToLatentsInvocation(TextToLatentsInvocation): latent, device=model.device, dtype=latent.dtype ) - timesteps, _ = model.get_img2img_timesteps( - self.steps, - self.strength, - device=model.device, - ) + timesteps, _ = model.get_img2img_timesteps(self.steps, self.strength) result_latents, result_attention_map_saver = model.latents_from_embeddings( latents=initial_latents, diff --git a/invokeai/backend/args.py b/invokeai/backend/args.py index eb8b396ee0..8cbeea10ad 100644 --- a/invokeai/backend/args.py +++ b/invokeai/backend/args.py @@ -119,6 +119,7 @@ SAMPLER_CHOICES = [ "plms", # diffusers: "pndm", + "unipc" ] PRECISION_CHOICES = [ diff --git a/invokeai/backend/generate.py b/invokeai/backend/generate.py index 4f3df60f1c..237f609b2f 100644 --- a/invokeai/backend/generate.py +++ b/invokeai/backend/generate.py @@ -1049,27 +1049,28 @@ class Generate: # See https://github.com/huggingface/diffusers/issues/277#issuecomment-1371428672 scheduler_map = dict( - ddim=diffusers.DDIMScheduler, - dpmpp_2=diffusers.DPMSolverMultistepScheduler, - k_dpm_2=diffusers.KDPM2DiscreteScheduler, - k_dpm_2_a=diffusers.KDPM2AncestralDiscreteScheduler, + ddim=(diffusers.DDIMScheduler, dict()), + dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), + k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict()), + k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict()), # DPMSolverMultistepScheduler is technically not `k_` anything, as it is neither # the k-diffusers implementation nor included in EDM (Karras 2022), but we can # provide an alias for compatibility. - k_dpmpp_2=diffusers.DPMSolverMultistepScheduler, - k_euler=diffusers.EulerDiscreteScheduler, - k_euler_a=diffusers.EulerAncestralDiscreteScheduler, - k_heun=diffusers.HeunDiscreteScheduler, - k_lms=diffusers.LMSDiscreteScheduler, - plms=diffusers.PNDMScheduler, + k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), + k_euler=(diffusers.EulerDiscreteScheduler, dict()), + k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict()), + k_heun=(diffusers.HeunDiscreteScheduler, dict()), + k_lms=(diffusers.LMSDiscreteScheduler, dict()), + plms=(diffusers.PNDMScheduler, dict()), + unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) ) if self.sampler_name in scheduler_map: - sampler_class = scheduler_map[self.sampler_name] + sampler_class, sampler_extra_config = scheduler_map[self.sampler_name] msg = ( f"Setting Sampler to {self.sampler_name} ({sampler_class.__name__})" ) - self.sampler = sampler_class.from_config(self.model.scheduler.config) + self.sampler = sampler_class.from_config({**self.model.scheduler.config, **sampler_extra_config}) else: msg = ( f" Unsupported Sampler: {self.sampler_name} "+ diff --git a/invokeai/backend/generator/base.py b/invokeai/backend/generator/base.py index 9887434e90..f898d18345 100644 --- a/invokeai/backend/generator/base.py +++ b/invokeai/backend/generator/base.py @@ -72,17 +72,18 @@ class InvokeAIGeneratorOutput: # old code that calls Generate will continue to work. class InvokeAIGenerator(metaclass=ABCMeta): scheduler_map = dict( - ddim=diffusers.DDIMScheduler, - dpmpp_2=diffusers.DPMSolverMultistepScheduler, - k_dpm_2=diffusers.KDPM2DiscreteScheduler, - k_dpm_2_a=diffusers.KDPM2AncestralDiscreteScheduler, - k_dpmpp_2=diffusers.DPMSolverMultistepScheduler, - k_euler=diffusers.EulerDiscreteScheduler, - k_euler_a=diffusers.EulerAncestralDiscreteScheduler, - k_heun=diffusers.HeunDiscreteScheduler, - k_lms=diffusers.LMSDiscreteScheduler, - plms=diffusers.PNDMScheduler, - ) + ddim=(diffusers.DDIMScheduler, dict()), + dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), + k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict()), + k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict()), + k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), + k_euler=(diffusers.EulerDiscreteScheduler, dict()), + k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict()), + k_heun=(diffusers.HeunDiscreteScheduler, dict()), + k_lms=(diffusers.LMSDiscreteScheduler, dict()), + plms=(diffusers.PNDMScheduler, dict()), + unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) + ) def __init__(self, model_info: dict, @@ -181,8 +182,9 @@ class InvokeAIGenerator(metaclass=ABCMeta): return generator_class(model, self.params.precision) def get_scheduler(self, scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: - scheduler_class = self.scheduler_map.get(scheduler_name,'ddim') - scheduler = scheduler_class.from_config(model.scheduler.config) + scheduler_class, scheduler_extra_config = self.scheduler_map.get(scheduler_name,'ddim') + scheduler_config = {**model.scheduler.config, **scheduler_extra_config} + scheduler = scheduler_class.from_config(scheduler_config) # hack copied over from generate.py if not hasattr(scheduler, 'uses_inpainting_model'): scheduler.uses_inpainting_model = lambda: False diff --git a/invokeai/backend/model_management/convert_ckpt_to_diffusers.py b/invokeai/backend/model_management/convert_ckpt_to_diffusers.py index 8aec5a01d9..1ebd044d54 100644 --- a/invokeai/backend/model_management/convert_ckpt_to_diffusers.py +++ b/invokeai/backend/model_management/convert_ckpt_to_diffusers.py @@ -47,6 +47,7 @@ from diffusers import ( LDMTextToImagePipeline, LMSDiscreteScheduler, PNDMScheduler, + UniPCMultistepScheduler, StableDiffusionPipeline, UNet2DConditionModel, ) @@ -1209,6 +1210,8 @@ def load_pipeline_from_original_stable_diffusion_ckpt( scheduler = EulerAncestralDiscreteScheduler.from_config(scheduler.config) elif scheduler_type == "dpm": scheduler = DPMSolverMultistepScheduler.from_config(scheduler.config) + elif scheduler_type == 'unipc': + scheduler = UniPCMultistepScheduler.from_config(scheduler.config) elif scheduler_type == "ddim": scheduler = scheduler else: diff --git a/invokeai/backend/stable_diffusion/diffusers_pipeline.py b/invokeai/backend/stable_diffusion/diffusers_pipeline.py index 94ec9da7e8..c8a932b9e9 100644 --- a/invokeai/backend/stable_diffusion/diffusers_pipeline.py +++ b/invokeai/backend/stable_diffusion/diffusers_pipeline.py @@ -509,10 +509,13 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline): run_id=None, callback: Callable[[PipelineIntermediateState], None] = None, ) -> tuple[torch.Tensor, Optional[AttentionMapSaver]]: + if self.scheduler.config.get("cpu_only", False): + scheduler_device = torch.device('cpu') + else: + scheduler_device = self._model_group.device_for(self.unet) + if timesteps is None: - self.scheduler.set_timesteps( - num_inference_steps, device=self._model_group.device_for(self.unet) - ) + self.scheduler.set_timesteps(num_inference_steps, device=scheduler_device) timesteps = self.scheduler.timesteps infer_latents_from_embeddings = GeneratorToCallbackinator( self.generate_latents_from_embeddings, PipelineIntermediateState @@ -725,12 +728,8 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline): noise: torch.Tensor, run_id=None, callback=None, - ) -> InvokeAIStableDiffusionPipelineOutput: - timesteps, _ = self.get_img2img_timesteps( - num_inference_steps, - strength, - device=self._model_group.device_for(self.unet), - ) + ) -> InvokeAIStableDiffusionPipelineOutput: + timesteps, _ = self.get_img2img_timesteps(num_inference_steps, strength) result_latents, result_attention_maps = self.latents_from_embeddings( latents=initial_latents if strength < 1.0 else torch.zeros_like( initial_latents, device=initial_latents.device, dtype=initial_latents.dtype @@ -756,13 +755,19 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline): return self.check_for_safety(output, dtype=conditioning_data.dtype) def get_img2img_timesteps( - self, num_inference_steps: int, strength: float, device + self, num_inference_steps: int, strength: float, device=None ) -> (torch.Tensor, int): img2img_pipeline = StableDiffusionImg2ImgPipeline(**self.components) assert img2img_pipeline.scheduler is self.scheduler - img2img_pipeline.scheduler.set_timesteps(num_inference_steps, device=device) + + if self.scheduler.config.get("cpu_only", False): + scheduler_device = torch.device('cpu') + else: + scheduler_device = self._model_group.device_for(self.unet) + + img2img_pipeline.scheduler.set_timesteps(num_inference_steps, device=scheduler_device) timesteps, adjusted_steps = img2img_pipeline.get_timesteps( - num_inference_steps, strength, device=device + num_inference_steps, strength, device=scheduler_device ) # Workaround for low strength resulting in zero timesteps. # TODO: submit upstream fix for zero-step img2img @@ -796,9 +801,7 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline): if init_image.dim() == 3: init_image = init_image.unsqueeze(0) - timesteps, _ = self.get_img2img_timesteps( - num_inference_steps, strength, device=device - ) + timesteps, _ = self.get_img2img_timesteps(num_inference_steps, strength) # 6. Prepare latent variables # can't quite use upstream StableDiffusionImg2ImgPipeline.prepare_latents diff --git a/invokeai/backend/web/modules/parameters.py b/invokeai/backend/web/modules/parameters.py index 3c9c530dd2..9c06f68097 100644 --- a/invokeai/backend/web/modules/parameters.py +++ b/invokeai/backend/web/modules/parameters.py @@ -15,6 +15,7 @@ SAMPLER_CHOICES = [ "plms", # diffusers: "pndm", + "unipc" ] diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index 534ca9e29a..2d89ffe31e 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -11,6 +11,7 @@ export const DIFFUSERS_SCHEDULERS: Array = [ 'k_euler', 'k_euler_a', 'k_heun', + 'unipc', ]; // Valid image widths From d1029138d289a2713bb5b47ce2bdf30617342b24 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Thu, 11 May 2023 22:49:28 +1200 Subject: [PATCH 05/42] Default to DDIM if scheduler is missing --- invokeai/app/invocations/latent.py | 2 +- invokeai/backend/generator/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 82396e358a..cbd39fdb5f 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -74,7 +74,7 @@ SAMPLER_NAME_VALUES = Literal[ def get_scheduler(scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: - scheduler_class, scheduler_extra_config = scheduler_map.get(scheduler_name,'ddim') + scheduler_class, scheduler_extra_config = scheduler_map.get(scheduler_name, scheduler_map['ddim']) scheduler_config = {**model.scheduler.config, **scheduler_extra_config} scheduler = scheduler_class.from_config(scheduler_config) # hack copied over from generate.py diff --git a/invokeai/backend/generator/base.py b/invokeai/backend/generator/base.py index f898d18345..f286ad0ad7 100644 --- a/invokeai/backend/generator/base.py +++ b/invokeai/backend/generator/base.py @@ -182,7 +182,7 @@ class InvokeAIGenerator(metaclass=ABCMeta): return generator_class(model, self.params.precision) def get_scheduler(self, scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: - scheduler_class, scheduler_extra_config = self.scheduler_map.get(scheduler_name,'ddim') + scheduler_class, scheduler_extra_config = self.scheduler_map.get(scheduler_name, self.scheduler_map['ddim']) scheduler_config = {**model.scheduler.config, **scheduler_extra_config} scheduler = scheduler_class.from_config(scheduler_config) # hack copied over from generate.py From 6cf308004a38dd20073bee22cdb65434c1e8f7d8 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 20:54:28 +1000 Subject: [PATCH 06/42] fix(nodes): remove Optionals on ImageOutputs --- invokeai/app/invocations/image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/app/invocations/image.py b/invokeai/app/invocations/image.py index d32f96857d..8b4163c4c6 100644 --- a/invokeai/app/invocations/image.py +++ b/invokeai/app/invocations/image.py @@ -33,8 +33,8 @@ class ImageOutput(BaseInvocationOutput): # fmt: off type: Literal["image"] = "image" image: ImageField = Field(default=None, description="The output image") - width: Optional[int] = Field(default=None, description="The width of the image in pixels") - height: Optional[int] = Field(default=None, description="The height of the image in pixels") + width: int = Field(description="The width of the image in pixels") + height: int = Field(description="The height of the image in pixels") # fmt: on class Config: From f9384be59b80ddb75f8cd9ddb7f822f92a2cdd51 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 13:49:07 +1000 Subject: [PATCH 07/42] fix(ui): fix init image causing overflow --- .../components/CurrentImagePreview.tsx | 10 ++- .../ImageToImage/InitialImagePreview.tsx | 66 ++++++++----------- 2 files changed, 33 insertions(+), 43 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx index a0fbd7c5d1..18fe166cd8 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx @@ -1,4 +1,4 @@ -import { Box, Flex, Image, Skeleton, useBoolean } from '@chakra-ui/react'; +import { Box, Flex, Image } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; import { useGetUrl } from 'common/util/getUrl'; @@ -50,8 +50,6 @@ const CurrentImagePreview = () => { } = useAppSelector(imagesSelector); const { getUrl } = useGetUrl(); - const [isLoaded, { on, off }] = useBoolean(); - const handleDragStart = useCallback( (e: DragEvent) => { if (!image) { @@ -67,11 +65,11 @@ const CurrentImagePreview = () => { return ( {progressImage && shouldShowProgressInViewer ? ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx index fbb833a14a..757957934e 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx @@ -62,48 +62,40 @@ const InitialImagePreview = () => { sx={{ width: 'full', height: 'full', + position: 'relative', alignItems: 'center', justifyContent: 'center', - position: 'relative', }} onDrop={handleDrop} > - - {initialImage?.url && ( - <> - { - setIsLoaded(true); - }} - fallback={ - - - - } - /> - {isLoaded && } - - )} - {!initialImage?.url && } - + {initialImage?.url && ( + <> + { + setIsLoaded(true); + }} + fallback={ + + + + } + /> + {isLoaded && } + + )} + {!initialImage?.url && } ); }; From 3ffff023b2d63377397b4f6114cd0e209c48f179 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 00:07:36 +1200 Subject: [PATCH 08/42] Add missing key to scheduler_map It was breaking coz the sampler was not being reset. So needs a key on each. Will simplify this later. --- invokeai/app/invocations/latent.py | 20 ++++++++++---------- invokeai/backend/generate.py | 20 ++++++++++---------- invokeai/backend/generator/base.py | 20 ++++++++++---------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index cbd39fdb5f..da0e6c0354 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -54,16 +54,16 @@ class NoiseOutput(BaseInvocationOutput): # TODO: this seems like a hack scheduler_map = dict( - ddim=(diffusers.DDIMScheduler, dict()), - dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), - k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict()), - k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict()), - k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), - k_euler=(diffusers.EulerDiscreteScheduler, dict()), - k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict()), - k_heun=(diffusers.HeunDiscreteScheduler, dict()), - k_lms=(diffusers.LMSDiscreteScheduler, dict()), - plms=(diffusers.PNDMScheduler, dict()), + ddim=(diffusers.DDIMScheduler, dict(cpu_only=False)), + dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict(cpu_only=False)), + k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), + k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_euler=(diffusers.EulerDiscreteScheduler, dict(cpu_only=False)), + k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict(cpu_only=False)), + k_heun=(diffusers.HeunDiscreteScheduler, dict(cpu_only=False)), + k_lms=(diffusers.LMSDiscreteScheduler, dict(cpu_only=False)), + plms=(diffusers.PNDMScheduler, dict(cpu_only=False)), unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) ) diff --git a/invokeai/backend/generate.py b/invokeai/backend/generate.py index 237f609b2f..c64c0e3320 100644 --- a/invokeai/backend/generate.py +++ b/invokeai/backend/generate.py @@ -1049,19 +1049,19 @@ class Generate: # See https://github.com/huggingface/diffusers/issues/277#issuecomment-1371428672 scheduler_map = dict( - ddim=(diffusers.DDIMScheduler, dict()), - dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), - k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict()), - k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict()), + ddim=(diffusers.DDIMScheduler, dict(cpu_only=False)), + dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict(cpu_only=False)), + k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), # DPMSolverMultistepScheduler is technically not `k_` anything, as it is neither # the k-diffusers implementation nor included in EDM (Karras 2022), but we can # provide an alias for compatibility. - k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), - k_euler=(diffusers.EulerDiscreteScheduler, dict()), - k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict()), - k_heun=(diffusers.HeunDiscreteScheduler, dict()), - k_lms=(diffusers.LMSDiscreteScheduler, dict()), - plms=(diffusers.PNDMScheduler, dict()), + k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_euler=(diffusers.EulerDiscreteScheduler, dict(cpu_only=False)), + k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict(cpu_only=False)), + k_heun=(diffusers.HeunDiscreteScheduler, dict(cpu_only=False)), + k_lms=(diffusers.LMSDiscreteScheduler, dict(cpu_only=False)), + plms=(diffusers.PNDMScheduler, dict(cpu_only=False)), unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) ) diff --git a/invokeai/backend/generator/base.py b/invokeai/backend/generator/base.py index f286ad0ad7..33a625de30 100644 --- a/invokeai/backend/generator/base.py +++ b/invokeai/backend/generator/base.py @@ -72,16 +72,16 @@ class InvokeAIGeneratorOutput: # old code that calls Generate will continue to work. class InvokeAIGenerator(metaclass=ABCMeta): scheduler_map = dict( - ddim=(diffusers.DDIMScheduler, dict()), - dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), - k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict()), - k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict()), - k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict()), - k_euler=(diffusers.EulerDiscreteScheduler, dict()), - k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict()), - k_heun=(diffusers.HeunDiscreteScheduler, dict()), - k_lms=(diffusers.LMSDiscreteScheduler, dict()), - plms=(diffusers.PNDMScheduler, dict()), + ddim=(diffusers.DDIMScheduler, dict(cpu_only=False)), + dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict(cpu_only=False)), + k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), + k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_euler=(diffusers.EulerDiscreteScheduler, dict(cpu_only=False)), + k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict(cpu_only=False)), + k_heun=(diffusers.HeunDiscreteScheduler, dict(cpu_only=False)), + k_lms=(diffusers.LMSDiscreteScheduler, dict(cpu_only=False)), + plms=(diffusers.PNDMScheduler, dict(cpu_only=False)), unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) ) From 3493c8119b725ba25e0580be8d972dfdab93129b Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 13:56:51 +1000 Subject: [PATCH 09/42] feat(ui): improve image preview css and fallback --- .../components/CurrentImagePreview.tsx | 16 +++-------- ...eFallback.tsx => ImageFallbackSpinner.tsx} | 6 ++-- .../ImageToImage/InitialImagePreview.tsx | 28 ++++++------------- 3 files changed, 15 insertions(+), 35 deletions(-) rename invokeai/frontend/web/src/features/gallery/components/{CurrentImageFallback.tsx => ImageFallbackSpinner.tsx} (72%) diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx index 18fe166cd8..a86407d5c8 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx @@ -11,7 +11,7 @@ import NextPrevImageButtons from './NextPrevImageButtons'; import CurrentImageHidden from './CurrentImageHidden'; import { DragEvent, memo, useCallback } from 'react'; import { systemSelector } from 'features/system/store/systemSelectors'; -import CurrentImageFallback from './CurrentImageFallback'; +import ImageFallbackSpinner from './ImageFallbackSpinner'; export const imagesSelector = createSelector( [uiSelector, gallerySelector, systemSelector], @@ -90,18 +90,10 @@ const CurrentImagePreview = () => { ) : ( image && ( - ) : ( - - ) - } + fallback={} + onDragStart={handleDragStart} sx={{ objectFit: 'contain', maxWidth: '100%', diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageFallback.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx similarity index 72% rename from invokeai/frontend/web/src/features/gallery/components/CurrentImageFallback.tsx rename to invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx index abcf5d4b18..394ff9db15 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageFallback.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageFallbackSpinner.tsx @@ -1,8 +1,8 @@ import { Flex, Spinner, SpinnerProps } from '@chakra-ui/react'; -type CurrentImageFallbackProps = SpinnerProps; +type ImageFallbackSpinnerProps = SpinnerProps; -const CurrentImageFallback = (props: CurrentImageFallbackProps) => { +const ImageFallbackSpinner = (props: ImageFallbackSpinnerProps) => { const { size = 'xl', ...rest } = props; return ( @@ -21,4 +21,4 @@ const CurrentImageFallback = (props: CurrentImageFallbackProps) => { ); }; -export default CurrentImageFallback; +export default ImageFallbackSpinner; diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx index 757957934e..8c8a52c857 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx @@ -1,17 +1,18 @@ -import { Flex, Image, Spinner } from '@chakra-ui/react'; +import { Flex, Image } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import SelectImagePlaceholder from 'common/components/SelectImagePlaceholder'; import { useGetUrl } from 'common/util/getUrl'; import { clearInitialImage } from 'features/parameters/store/generationSlice'; import { addToast } from 'features/system/store/systemSlice'; -import { DragEvent, useCallback, useState } from 'react'; +import { DragEvent, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { ImageType } from 'services/api'; import ImageToImageOverlay from 'common/components/ImageToImageOverlay'; import { generationSelector } from 'features/parameters/store/generationSelectors'; import { initialImageSelected } from 'features/parameters/store/actions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import ImageFallbackSpinner from 'features/gallery/components/ImageFallbackSpinner'; const selector = createSelector( [generationSelector], @@ -30,8 +31,6 @@ const InitialImagePreview = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); - const [isLoaded, setIsLoaded] = useState(false); - const onError = () => { dispatch( addToast({ @@ -42,13 +41,10 @@ const InitialImagePreview = () => { }) ); dispatch(clearInitialImage()); - setIsLoaded(false); }; const handleDrop = useCallback( (e: DragEvent) => { - setIsLoaded(false); - const name = e.dataTransfer.getData('invokeai/imageName'); const type = e.dataTransfer.getData('invokeai/imageType') as ImageType; @@ -71,6 +67,10 @@ const InitialImagePreview = () => { {initialImage?.url && ( <> } + onError={onError} sx={{ objectFit: 'contain', maxWidth: '100%', @@ -79,20 +79,8 @@ const InitialImagePreview = () => { position: 'absolute', borderRadius: 'base', }} - src={getUrl(initialImage?.url)} - onError={onError} - onLoad={() => { - setIsLoaded(true); - }} - fallback={ - - - - } /> - {isLoaded && } + )} {!initialImage?.url && } From 40d4cabecd5e1644a85bf004e01bd5f46b325a65 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 14:18:35 +1000 Subject: [PATCH 10/42] feat(ui): improve image overlay --- .../components/ImageMetadataOverlay.tsx | 54 +++++++++++++++++++ .../common/components/ImageToImageOverlay.tsx | 37 ------------- .../components/CurrentImagePreview.tsx | 32 ++++++----- .../ImageToImage/InitialImagePreview.tsx | 4 +- 4 files changed, 74 insertions(+), 53 deletions(-) create mode 100644 invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx delete mode 100644 invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx diff --git a/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx b/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx new file mode 100644 index 0000000000..64d5e1beef --- /dev/null +++ b/invokeai/frontend/web/src/common/components/ImageMetadataOverlay.tsx @@ -0,0 +1,54 @@ +import { Badge, Flex } from '@chakra-ui/react'; +import { Image } from 'app/types/invokeai'; +import { isNumber, isString } from 'lodash-es'; +import { useMemo } from 'react'; + +type ImageMetadataOverlayProps = { + image: Image; +}; + +const ImageMetadataOverlay = ({ image }: ImageMetadataOverlayProps) => { + const dimensions = useMemo(() => { + if (!isNumber(image.metadata?.width) || isNumber(!image.metadata?.height)) { + return; + } + + return `${image.metadata?.width} × ${image.metadata?.height}`; + }, [image.metadata]); + + const model = useMemo(() => { + if (!isString(image.metadata?.invokeai?.node?.model)) { + return; + } + + return image.metadata?.invokeai?.node?.model; + }, [image.metadata]); + + return ( + + {dimensions && ( + + {dimensions} + + )} + {model && ( + + {model} + + )} + + ); +}; + +export default ImageMetadataOverlay; diff --git a/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx b/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx deleted file mode 100644 index 9d864f5c9c..0000000000 --- a/invokeai/frontend/web/src/common/components/ImageToImageOverlay.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Badge, Box, Flex } from '@chakra-ui/react'; -import { Image } from 'app/types/invokeai'; - -type ImageToImageOverlayProps = { - image: Image; -}; - -const ImageToImageOverlay = ({ image }: ImageToImageOverlayProps) => { - return ( - - - - {image.metadata?.width} × {image.metadata?.height} - - - - ); -}; - -export default ImageToImageOverlay; diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx index a86407d5c8..b1dbed5a81 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImagePreview.tsx @@ -12,6 +12,7 @@ import CurrentImageHidden from './CurrentImageHidden'; import { DragEvent, memo, useCallback } from 'react'; import { systemSelector } from 'features/system/store/systemSelectors'; import ImageFallbackSpinner from './ImageFallbackSpinner'; +import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay'; export const imagesSelector = createSelector( [uiSelector, gallerySelector, systemSelector], @@ -89,20 +90,23 @@ const CurrentImagePreview = () => { /> ) : ( image && ( - } - onDragStart={handleDragStart} - sx={{ - objectFit: 'contain', - maxWidth: '100%', - maxHeight: '100%', - height: 'auto', - position: 'absolute', - borderRadius: 'base', - }} - /> + <> + } + onDragStart={handleDragStart} + sx={{ + objectFit: 'contain', + maxWidth: '100%', + maxHeight: '100%', + height: 'auto', + position: 'absolute', + borderRadius: 'base', + }} + /> + + ) )} {shouldShowImageDetails && image && 'metadata' in image && ( diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx index 8c8a52c857..3de1e1cebb 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/InitialImagePreview.tsx @@ -8,7 +8,7 @@ import { addToast } from 'features/system/store/systemSlice'; import { DragEvent, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { ImageType } from 'services/api'; -import ImageToImageOverlay from 'common/components/ImageToImageOverlay'; +import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay'; import { generationSelector } from 'features/parameters/store/generationSelectors'; import { initialImageSelected } from 'features/parameters/store/actions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; @@ -80,7 +80,7 @@ const InitialImagePreview = () => { borderRadius: 'base', }} /> - + )} {!initialImage?.url && } From 15c59e606f677e53a5af8154919c47ff5c89c510 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 18:45:06 +1000 Subject: [PATCH 11/42] feat(ui): add spinner to gallery progress images - otherwise you may think you can click it but you cannot --- .../src/features/gallery/components/GalleryProgressImage.tsx | 4 +++- .../web/src/features/gallery/components/HoverableImage.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryProgressImage.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryProgressImage.tsx index b812849c44..a2103eb8e2 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GalleryProgressImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GalleryProgressImage.tsx @@ -1,4 +1,4 @@ -import { Box, Flex, Image } from '@chakra-ui/react'; +import { Flex, Image, Spinner } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; @@ -42,6 +42,7 @@ const GalleryProgressImage = () => { alignItems: 'center', justifyContent: 'center', aspectRatio: '1/1', + position: 'relative', }} > { imageRendering: shouldAntialiasProgressImage ? 'auto' : 'pixelated', }} /> + ); }; diff --git a/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx b/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx index 2e5f166025..35ddefe181 100644 --- a/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/HoverableImage.tsx @@ -278,6 +278,7 @@ const HoverableImage = memo((props: HoverableImageProps) => { h: 'full', transition: 'transform 0.2s ease-out', aspectRatio: '1/1', + cursor: 'pointer', }} > Date: Thu, 11 May 2023 18:49:25 +1000 Subject: [PATCH 12/42] fix(ui): fix results not displaying - fix for commercial product --- .../features/gallery/store/gallerySlice.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 2326295451..81705086b3 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -1,6 +1,7 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; import { Image } from 'app/types/invokeai'; +import { imageReceived, thumbnailReceived } from 'services/thunks/image'; type GalleryImageObjectFitType = 'contain' | 'cover'; @@ -63,6 +64,29 @@ export const gallerySlice = createSlice({ state.shouldUseSingleGalleryColumn = action.payload; }, }, + extraReducers(builder) { + builder.addCase(imageReceived.fulfilled, (state, action) => { + // When we get an updated URL for an image, we need to update the selectedImage in gallery, + // which is currently its own object (instead of a reference to an image in results/uploads) + const { imagePath } = action.payload; + const { imageName } = action.meta.arg; + + if (state.selectedImage?.name === imageName) { + state.selectedImage.url = imagePath; + } + }); + + builder.addCase(thumbnailReceived.fulfilled, (state, action) => { + // When we get an updated URL for an image, we need to update the selectedImage in gallery, + // which is currently its own object (instead of a reference to an image in results/uploads) + const { thumbnailPath } = action.payload; + const { thumbnailName } = action.meta.arg; + + if (state.selectedImage?.name === thumbnailName) { + state.selectedImage.thumbnail = thumbnailPath; + } + }); + }, }); export const { From 7f5f4689ccef870d99666647dde9f67a2c09f7d0 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 19:31:08 +1000 Subject: [PATCH 13/42] fix(ui): clear progress image on cancel --- invokeai/frontend/web/src/features/system/store/systemSlice.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 1aeb2a1939..e9cbd21a15 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -418,6 +418,7 @@ export const systemSlice = createSlice({ state.currentStep = 0; state.totalSteps = 0; state.statusTranslationKey = 'common.statusConnected'; + state.progressImage = null; state.toastQueue.push( makeToast({ title: t('toast.canceled'), status: 'warning' }) From 9a383e456dbcdacc6eb6e79c043b2483d597c776 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 00:40:03 +1200 Subject: [PATCH 14/42] Codesplit SCHEDULER_MAP for reusage --- invokeai/app/invocations/latent.py | 21 +++-------------- invokeai/backend/generate.py | 23 +++---------------- invokeai/backend/generator/base.py | 19 +++------------ .../stable_diffusion/schedulers/__init__.py | 1 + .../stable_diffusion/schedulers/schedulers.py | 17 ++++++++++++++ 5 files changed, 27 insertions(+), 54 deletions(-) create mode 100644 invokeai/backend/stable_diffusion/schedulers/__init__.py create mode 100644 invokeai/backend/stable_diffusion/schedulers/schedulers.py diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 4ce98f97f4..349a13bf3d 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -17,6 +17,7 @@ from ...backend.stable_diffusion.diffusion.shared_invokeai_diffusion import Post from ...backend.image_util.seamless import configure_model_padding from ...backend.prompting.conditioning import get_uc_and_c_and_ec from ...backend.stable_diffusion.diffusers_pipeline import ConditioningData, StableDiffusionGeneratorPipeline, image_resized_to_grid_as_tensor +from ...backend.stable_diffusion.schedulers import SCHEDULER_MAP from .baseinvocation import BaseInvocation, BaseInvocationOutput, InvocationContext, InvocationConfig import numpy as np from ..services.image_storage import ImageType @@ -52,29 +53,13 @@ class NoiseOutput(BaseInvocationOutput): #fmt: on -# TODO: this seems like a hack -scheduler_map = dict( - ddim=(diffusers.DDIMScheduler, dict(cpu_only=False)), - dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict(cpu_only=False)), - k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), - k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_euler=(diffusers.EulerDiscreteScheduler, dict(cpu_only=False)), - k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict(cpu_only=False)), - k_heun=(diffusers.HeunDiscreteScheduler, dict(cpu_only=False)), - k_lms=(diffusers.LMSDiscreteScheduler, dict(cpu_only=False)), - plms=(diffusers.PNDMScheduler, dict(cpu_only=False)), - unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) -) - - SAMPLER_NAME_VALUES = Literal[ - tuple(list(scheduler_map.keys())) + tuple(list(SCHEDULER_MAP.keys())) ] def get_scheduler(scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: - scheduler_class, scheduler_extra_config = scheduler_map.get(scheduler_name, scheduler_map['ddim']) + scheduler_class, scheduler_extra_config = SCHEDULER_MAP.get(scheduler_name, SCHEDULER_MAP['ddim']) scheduler_config = {**model.scheduler.config, **scheduler_extra_config} scheduler = scheduler_class.from_config(scheduler_config) # hack copied over from generate.py diff --git a/invokeai/backend/generate.py b/invokeai/backend/generate.py index c64c0e3320..dc5f9d9ed0 100644 --- a/invokeai/backend/generate.py +++ b/invokeai/backend/generate.py @@ -37,6 +37,7 @@ from .safety_checker import SafetyChecker from .prompting import get_uc_and_c_and_ec from .prompting.conditioning import log_tokenization from .stable_diffusion import HuggingFaceConceptsLibrary +from .stable_diffusion.schedulers import SCHEDULER_MAP from .util import choose_precision, choose_torch_device def fix_func(orig): @@ -1047,26 +1048,8 @@ class Generate: def _set_scheduler(self): default = self.model.scheduler - # See https://github.com/huggingface/diffusers/issues/277#issuecomment-1371428672 - scheduler_map = dict( - ddim=(diffusers.DDIMScheduler, dict(cpu_only=False)), - dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict(cpu_only=False)), - k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), - # DPMSolverMultistepScheduler is technically not `k_` anything, as it is neither - # the k-diffusers implementation nor included in EDM (Karras 2022), but we can - # provide an alias for compatibility. - k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_euler=(diffusers.EulerDiscreteScheduler, dict(cpu_only=False)), - k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict(cpu_only=False)), - k_heun=(diffusers.HeunDiscreteScheduler, dict(cpu_only=False)), - k_lms=(diffusers.LMSDiscreteScheduler, dict(cpu_only=False)), - plms=(diffusers.PNDMScheduler, dict(cpu_only=False)), - unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) - ) - - if self.sampler_name in scheduler_map: - sampler_class, sampler_extra_config = scheduler_map[self.sampler_name] + if self.sampler_name in SCHEDULER_MAP: + sampler_class, sampler_extra_config = SCHEDULER_MAP[self.sampler_name] msg = ( f"Setting Sampler to {self.sampler_name} ({sampler_class.__name__})" ) diff --git a/invokeai/backend/generator/base.py b/invokeai/backend/generator/base.py index 33a625de30..317110b35b 100644 --- a/invokeai/backend/generator/base.py +++ b/invokeai/backend/generator/base.py @@ -31,6 +31,7 @@ from ..util.util import rand_perlin_2d from ..safety_checker import SafetyChecker from ..prompting.conditioning import get_uc_and_c_and_ec from ..stable_diffusion.diffusers_pipeline import StableDiffusionGeneratorPipeline +from ..stable_diffusion.schedulers import SCHEDULER_MAP downsampling = 8 @@ -71,20 +72,6 @@ class InvokeAIGeneratorOutput: # we are interposing a wrapper around the original Generator classes so that # old code that calls Generate will continue to work. class InvokeAIGenerator(metaclass=ABCMeta): - scheduler_map = dict( - ddim=(diffusers.DDIMScheduler, dict(cpu_only=False)), - dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_dpm_2=(diffusers.KDPM2DiscreteScheduler, dict(cpu_only=False)), - k_dpm_2_a=(diffusers.KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), - k_dpmpp_2=(diffusers.DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_euler=(diffusers.EulerDiscreteScheduler, dict(cpu_only=False)), - k_euler_a=(diffusers.EulerAncestralDiscreteScheduler, dict(cpu_only=False)), - k_heun=(diffusers.HeunDiscreteScheduler, dict(cpu_only=False)), - k_lms=(diffusers.LMSDiscreteScheduler, dict(cpu_only=False)), - plms=(diffusers.PNDMScheduler, dict(cpu_only=False)), - unipc=(diffusers.UniPCMultistepScheduler, dict(cpu_only=True)) - ) - def __init__(self, model_info: dict, params: InvokeAIGeneratorBasicParams=InvokeAIGeneratorBasicParams(), @@ -176,13 +163,13 @@ class InvokeAIGenerator(metaclass=ABCMeta): ''' Return list of all the schedulers that we currently handle. ''' - return list(self.scheduler_map.keys()) + return list(SCHEDULER_MAP.keys()) def load_generator(self, model: StableDiffusionGeneratorPipeline, generator_class: Type[Generator]): return generator_class(model, self.params.precision) def get_scheduler(self, scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: - scheduler_class, scheduler_extra_config = self.scheduler_map.get(scheduler_name, self.scheduler_map['ddim']) + scheduler_class, scheduler_extra_config = SCHEDULER_MAP.get(scheduler_name, SCHEDULER_MAP['ddim']) scheduler_config = {**model.scheduler.config, **scheduler_extra_config} scheduler = scheduler_class.from_config(scheduler_config) # hack copied over from generate.py diff --git a/invokeai/backend/stable_diffusion/schedulers/__init__.py b/invokeai/backend/stable_diffusion/schedulers/__init__.py new file mode 100644 index 0000000000..b2df0df231 --- /dev/null +++ b/invokeai/backend/stable_diffusion/schedulers/__init__.py @@ -0,0 +1 @@ +from .schedulers import SCHEDULER_MAP \ No newline at end of file diff --git a/invokeai/backend/stable_diffusion/schedulers/schedulers.py b/invokeai/backend/stable_diffusion/schedulers/schedulers.py new file mode 100644 index 0000000000..00d3675fe5 --- /dev/null +++ b/invokeai/backend/stable_diffusion/schedulers/schedulers.py @@ -0,0 +1,17 @@ +from diffusers import DDIMScheduler, DPMSolverMultistepScheduler, KDPM2DiscreteScheduler, \ + KDPM2AncestralDiscreteScheduler, EulerDiscreteScheduler, EulerAncestralDiscreteScheduler, \ + HeunDiscreteScheduler, LMSDiscreteScheduler, PNDMScheduler, UniPCMultistepScheduler + +SCHEDULER_MAP = dict( + ddim=(DDIMScheduler, dict(cpu_only=False)), + dpmpp_2=(DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_dpm_2=(KDPM2DiscreteScheduler, dict(cpu_only=False)), + k_dpm_2_a=(KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), + k_dpmpp_2=(DPMSolverMultistepScheduler, dict(cpu_only=False)), + k_euler=(EulerDiscreteScheduler, dict(cpu_only=False)), + k_euler_a=(EulerAncestralDiscreteScheduler, dict(cpu_only=False)), + k_heun=(HeunDiscreteScheduler, dict(cpu_only=False)), + k_lms=(LMSDiscreteScheduler, dict(cpu_only=False)), + plms=(PNDMScheduler, dict(cpu_only=False)), + unipc=(UniPCMultistepScheduler, dict(cpu_only=True)) +) From 9af385468d9bb07f674f5bf6235b06d9a6b6cded Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 22:42:13 +1000 Subject: [PATCH 15/42] feat(ui): expand config options now may disable individual SD features eg Noise, Variation, etc - stuff which is not ready for consumption in commercial. --- .../frontend/web/src/app/types/invokeai.ts | 9 +++--- .../components/CurrentImageButtons.tsx | 17 +++++----- .../gallery/components/HoverableImage.tsx | 13 +++++--- .../Parameters/Hires/ParamHiresCollapse.tsx | 7 +++++ .../Parameters/Noise/ParamNoiseCollapse.tsx | 8 +++++ .../Seamless/ParamSeamlessCollapse.tsx | 7 +++++ .../Symmetry/ParamSymmetryCollapse.tsx | 7 +++++ .../Variations/ParamVariationCollapse.tsx | 7 +++++ .../features/system/hooks/useFeatureStatus.ts | 31 +++++++++++++++---- .../src/features/system/store/configSlice.ts | 1 + 10 files changed, 86 insertions(+), 21 deletions(-) diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 4023f7665d..982509a9ec 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -321,11 +321,11 @@ export type AppFeature = /** * A disable-able Stable Diffusion feature */ -export type StableDiffusionFeature = - | 'noiseConfig' - | 'variations' +export type SDFeature = + | 'noise' + | 'variation' | 'symmetry' - | 'tiling' + | 'seamless' | 'hires'; /** @@ -343,6 +343,7 @@ export type AppConfig = { shouldFetchImages: boolean; disabledTabs: InvokeTabName[]; disabledFeatures: AppFeature[]; + disabledSDFeatures: SDFeature[]; canRestoreDeletedImagesFromBin: boolean; sd: { iterations: { diff --git a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx index 2a3d12ce91..e76f3fa41e 100644 --- a/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/CurrentImageButtons.tsx @@ -152,6 +152,7 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { } = useAppSelector(currentImageButtonsSelector); const isLightboxEnabled = useFeatureStatus('lightbox').isFeatureEnabled; + const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled; const isUpscalingEnabled = useFeatureStatus('upscaling').isFeatureEnabled; const isFaceRestoreEnabled = useFeatureStatus('faceRestore').isFeatureEnabled; @@ -429,13 +430,15 @@ const CurrentImageButtons = (props: CurrentImageButtonsProps) => { > {t('parameters.sendToImg2Img')} - } - > - {t('parameters.sendToUnifiedCanvas')} - + {isCanvasEnabled && ( + } + > + {t('parameters.sendToUnifiedCanvas')} + + )} {/* { const toast = useToast(); const { t } = useTranslation(); - const { isFeatureEnabled: isLightboxEnabled } = useFeatureStatus('lightbox'); + + const isLightboxEnabled = useFeatureStatus('lightbox').isFeatureEnabled; + const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled; + const { recallSeed, recallPrompt, recallInitialImage, recallAllParameters } = useParameters(); @@ -250,9 +253,11 @@ const HoverableImage = memo((props: HoverableImageProps) => { > {t('parameters.sendToImg2Img')} - } onClickCapture={handleSendToCanvas}> - {t('parameters.sendToUnifiedCanvas')} - + {isCanvasEnabled && ( + } onClickCapture={handleSendToCanvas}> + {t('parameters.sendToUnifiedCanvas')} + + )} } onClickCapture={onDeleteDialogOpen}> {t('gallery.deleteImage')} diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Hires/ParamHiresCollapse.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Hires/ParamHiresCollapse.tsx index 9c1f3a3f14..b4b077ad6c 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Hires/ParamHiresCollapse.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Hires/ParamHiresCollapse.tsx @@ -6,6 +6,7 @@ import IAICollapse from 'common/components/IAICollapse'; import { memo } from 'react'; import { ParamHiresStrength } from './ParamHiresStrength'; import { setHiresFix } from 'features/parameters/store/postprocessingSlice'; +import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; const ParamHiresCollapse = () => { const { t } = useTranslation(); @@ -13,10 +14,16 @@ const ParamHiresCollapse = () => { (state: RootState) => state.postprocessing.hiresFix ); + const isHiresEnabled = useFeatureStatus('hires').isFeatureEnabled; + const dispatch = useAppDispatch(); const handleToggle = () => dispatch(setHiresFix(!hiresFix)); + if (!isHiresEnabled) { + return null; + } + return ( { const { t } = useTranslation(); + + const isNoiseEnabled = useFeatureStatus('noise').isFeatureEnabled; + const shouldUseNoiseSettings = useAppSelector( (state: RootState) => state.generation.shouldUseNoiseSettings ); @@ -19,6 +23,10 @@ const ParamNoiseCollapse = () => { const handleToggle = () => dispatch(setShouldUseNoiseSettings(!shouldUseNoiseSettings)); + if (!isNoiseEnabled) { + return null; + } + return ( { const { t } = useTranslation(); const { shouldUseSeamless } = useAppSelector(selector); + const isSeamlessEnabled = useFeatureStatus('seamless').isFeatureEnabled; + const dispatch = useAppDispatch(); const handleToggle = () => dispatch(setSeamless(!shouldUseSeamless)); + if (!isSeamlessEnabled) { + return null; + } + return ( { const { t } = useTranslation(); @@ -15,10 +16,16 @@ const ParamSymmetryCollapse = () => { (state: RootState) => state.generation.shouldUseSymmetry ); + const isSymmetryEnabled = useFeatureStatus('symmetry').isFeatureEnabled; + const dispatch = useAppDispatch(); const handleToggle = () => dispatch(setShouldUseSymmetry(!shouldUseSymmetry)); + if (!isSymmetryEnabled) { + return null; + } + return ( { const { t } = useTranslation(); @@ -14,11 +15,17 @@ const ParamVariationCollapse = () => { (state: RootState) => state.generation.shouldGenerateVariations ); + const isVariationEnabled = useFeatureStatus('variation').isFeatureEnabled; + const dispatch = useAppDispatch(); const handleToggle = () => dispatch(setShouldGenerateVariations(!shouldGenerateVariations)); + if (!isVariationEnabled) { + return null; + } + return ( { + const disabledTabs = useAppSelector( + (state: RootState) => state.config.disabledTabs + ); -export const useFeatureStatus = (feature: AppFeature) => { const disabledFeatures = useAppSelector( (state: RootState) => state.config.disabledFeatures ); + const disabledSDFeatures = useAppSelector( + (state: RootState) => state.config.disabledSDFeatures + ); + const isFeatureDisabled = useMemo( - () => disabledFeatures.includes(feature), - [disabledFeatures, feature] + () => + disabledFeatures.includes(feature as AppFeature) || + disabledSDFeatures.includes(feature as SDFeature) || + disabledTabs.includes(feature as InvokeTabName), + [disabledFeatures, disabledSDFeatures, disabledTabs, feature] ); const isFeatureEnabled = useMemo( - () => !disabledFeatures.includes(feature), - [disabledFeatures, feature] + () => + !( + disabledFeatures.includes(feature as AppFeature) || + disabledSDFeatures.includes(feature as SDFeature) || + disabledTabs.includes(feature as InvokeTabName) + ), + [disabledFeatures, disabledSDFeatures, disabledTabs, feature] ); return { isFeatureDisabled, isFeatureEnabled }; diff --git a/invokeai/frontend/web/src/features/system/store/configSlice.ts b/invokeai/frontend/web/src/features/system/store/configSlice.ts index b773692908..7b3a1b1eea 100644 --- a/invokeai/frontend/web/src/features/system/store/configSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/configSlice.ts @@ -8,6 +8,7 @@ export const initialConfigState: AppConfig = { shouldFetchImages: false, disabledTabs: [], disabledFeatures: [], + disabledSDFeatures: [], canRestoreDeletedImagesFromBin: true, sd: { iterations: { From 799cd071747d98c3121bb2d9d2a22903475a0bf7 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 11 May 2023 22:45:53 +1000 Subject: [PATCH 16/42] feat(ui): make core parameters layout consistent --- .../Parameters/ImageToImage/ImageToImageStrength.tsx | 2 +- .../tabs/ImageToImage/ImageToImageTabCoreParameters.tsx | 4 ++-- .../tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageStrength.tsx index b467b15091..eea5ec3c27 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageStrength.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/ImageToImage/ImageToImageStrength.tsx @@ -47,7 +47,7 @@ const ImageToImageStrength = () => { return ( { - - @@ -74,6 +72,8 @@ const ImageToImageTabCoreParameters = () => { + + diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx index cc03ef560d..74949a399d 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/UnifiedCanvas/UnifiedCanvasCoreParameters.tsx @@ -62,8 +62,6 @@ const UnifiedCanvasCoreParameters = () => { - - @@ -72,8 +70,9 @@ const UnifiedCanvasCoreParameters = () => { + + - )} From 95c36445647f5953331fd74eb5620398eb99ffb7 Mon Sep 17 00:00:00 2001 From: Mary Hipp Date: Thu, 11 May 2023 10:09:01 -0400 Subject: [PATCH 17/42] fix it again --- invokeai/frontend/web/src/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/i18n.ts b/invokeai/frontend/web/src/i18n.ts index f28365db20..71d4dfb35f 100644 --- a/invokeai/frontend/web/src/i18n.ts +++ b/invokeai/frontend/web/src/i18n.ts @@ -3,7 +3,7 @@ import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-http-backend'; import { initReactI18next } from 'react-i18next'; -import translationEN from '../dist/locales/en.json'; +import translationEN from '../public/locales/en.json'; import { LOCALSTORAGE_PREFIX } from 'app/store/constants'; if (import.meta.env.MODE === 'package') { From 8a836247c82650a2edae75c8d2248d31a31cf7c5 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 02:23:33 +1200 Subject: [PATCH 18/42] Add DPMPP Single, Euler Karras and DPMPP2 Multi Karras Schedulers --- invokeai/app/invocations/latent.py | 7 +++++- invokeai/backend/args.py | 20 +++++++-------- invokeai/backend/generator/base.py | 7 +++++- .../stable_diffusion/schedulers/schedulers.py | 25 +++++++++++-------- invokeai/backend/web/modules/parameters.py | 20 +++++++-------- invokeai/frontend/web/src/app/constants.ts | 12 +++++---- .../frontend/web/src/app/types/invokeai.ts | 19 ++++++++------ 7 files changed, 64 insertions(+), 46 deletions(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 349a13bf3d..75fa719c07 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -60,8 +60,13 @@ SAMPLER_NAME_VALUES = Literal[ def get_scheduler(scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: scheduler_class, scheduler_extra_config = SCHEDULER_MAP.get(scheduler_name, SCHEDULER_MAP['ddim']) - scheduler_config = {**model.scheduler.config, **scheduler_extra_config} + + scheduler_config = model.scheduler.config + if "_backup" in scheduler_config: + scheduler_config = scheduler_config["_backup"] + scheduler_config = {**scheduler_config, **scheduler_extra_config, "_backup": scheduler_config} scheduler = scheduler_class.from_config(scheduler_config) + # hack copied over from generate.py if not hasattr(scheduler, 'uses_inpainting_model'): scheduler.uses_inpainting_model = lambda: False diff --git a/invokeai/backend/args.py b/invokeai/backend/args.py index 8cbeea10ad..0efaf6fd26 100644 --- a/invokeai/backend/args.py +++ b/invokeai/backend/args.py @@ -108,18 +108,18 @@ APP_VERSION = invokeai.version.__version__ SAMPLER_CHOICES = [ "ddim", - "k_dpm_2_a", - "k_dpm_2", - "k_dpmpp_2_a", - "k_dpmpp_2", - "k_euler_a", - "k_euler", - "k_heun", "k_lms", "plms", - # diffusers: - "pndm", - "unipc" + "k_heun", + "k_euler", + "euler_karras", + "k_euler_a", + "k_dpm_2", + "k_dpm_2_a", + "dpmpp_2s", + "k_dpmpp_2", + "k_dpmpp_2_karras", + "unipc", ] PRECISION_CHOICES = [ diff --git a/invokeai/backend/generator/base.py b/invokeai/backend/generator/base.py index 317110b35b..8f5b1a8395 100644 --- a/invokeai/backend/generator/base.py +++ b/invokeai/backend/generator/base.py @@ -170,8 +170,13 @@ class InvokeAIGenerator(metaclass=ABCMeta): def get_scheduler(self, scheduler_name:str, model: StableDiffusionGeneratorPipeline)->Scheduler: scheduler_class, scheduler_extra_config = SCHEDULER_MAP.get(scheduler_name, SCHEDULER_MAP['ddim']) - scheduler_config = {**model.scheduler.config, **scheduler_extra_config} + + scheduler_config = model.scheduler.config + if "_backup" in scheduler_config: + scheduler_config = scheduler_config["_backup"] + scheduler_config = {**scheduler_config, **scheduler_extra_config, "_backup": scheduler_config} scheduler = scheduler_class.from_config(scheduler_config) + # hack copied over from generate.py if not hasattr(scheduler, 'uses_inpainting_model'): scheduler.uses_inpainting_model = lambda: False diff --git a/invokeai/backend/stable_diffusion/schedulers/schedulers.py b/invokeai/backend/stable_diffusion/schedulers/schedulers.py index 00d3675fe5..98d73ae10b 100644 --- a/invokeai/backend/stable_diffusion/schedulers/schedulers.py +++ b/invokeai/backend/stable_diffusion/schedulers/schedulers.py @@ -1,17 +1,20 @@ from diffusers import DDIMScheduler, DPMSolverMultistepScheduler, KDPM2DiscreteScheduler, \ KDPM2AncestralDiscreteScheduler, EulerDiscreteScheduler, EulerAncestralDiscreteScheduler, \ - HeunDiscreteScheduler, LMSDiscreteScheduler, PNDMScheduler, UniPCMultistepScheduler + HeunDiscreteScheduler, LMSDiscreteScheduler, PNDMScheduler, UniPCMultistepScheduler, \ + DPMSolverSinglestepScheduler SCHEDULER_MAP = dict( - ddim=(DDIMScheduler, dict(cpu_only=False)), - dpmpp_2=(DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_dpm_2=(KDPM2DiscreteScheduler, dict(cpu_only=False)), - k_dpm_2_a=(KDPM2AncestralDiscreteScheduler, dict(cpu_only=False)), - k_dpmpp_2=(DPMSolverMultistepScheduler, dict(cpu_only=False)), - k_euler=(EulerDiscreteScheduler, dict(cpu_only=False)), - k_euler_a=(EulerAncestralDiscreteScheduler, dict(cpu_only=False)), - k_heun=(HeunDiscreteScheduler, dict(cpu_only=False)), - k_lms=(LMSDiscreteScheduler, dict(cpu_only=False)), - plms=(PNDMScheduler, dict(cpu_only=False)), + ddim=(DDIMScheduler, dict()), + k_lms=(LMSDiscreteScheduler, dict()), + plms=(PNDMScheduler, dict()), + k_euler=(EulerDiscreteScheduler, dict(use_karras_sigmas=False)), + euler_karras=(EulerDiscreteScheduler, dict(use_karras_sigmas=True)), + k_euler_a=(EulerAncestralDiscreteScheduler, dict()), + k_dpm_2=(KDPM2DiscreteScheduler, dict()), + k_dpm_2_a=(KDPM2AncestralDiscreteScheduler, dict()), + dpmpp_2s=(DPMSolverSinglestepScheduler, dict()), + k_dpmpp_2=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=False)), + k_dpmpp_2_karras=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=True)), + k_heun=(HeunDiscreteScheduler, dict()), unipc=(UniPCMultistepScheduler, dict(cpu_only=True)) ) diff --git a/invokeai/backend/web/modules/parameters.py b/invokeai/backend/web/modules/parameters.py index 9c06f68097..f25864765b 100644 --- a/invokeai/backend/web/modules/parameters.py +++ b/invokeai/backend/web/modules/parameters.py @@ -4,18 +4,18 @@ from .parse_seed_weights import parse_seed_weights SAMPLER_CHOICES = [ "ddim", - "k_dpm_2_a", - "k_dpm_2", - "k_dpmpp_2_a", - "k_dpmpp_2", - "k_euler_a", - "k_euler", - "k_heun", "k_lms", "plms", - # diffusers: - "pndm", - "unipc" + "k_heun", + "k_euler", + "euler_karras", + "k_euler_a", + "k_dpm_2", + "k_dpm_2_a", + "dpmpp_2s", + "k_dpmpp_2", + "k_dpmpp_2_karras", + "unipc", ] diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index 2d89ffe31e..d9a606435c 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -2,15 +2,17 @@ export const DIFFUSERS_SCHEDULERS: Array = [ 'ddim', - 'plms', 'k_lms', - 'dpmpp_2', + 'plms', + 'k_heun', + 'k_euler', + 'euler_karras', + 'k_euler_a', 'k_dpm_2', 'k_dpm_2_a', + 'dpmpp_2s', 'k_dpmpp_2', - 'k_euler', - 'k_euler_a', - 'k_heun', + 'k_dpmpp_2_karras', 'unipc', ]; diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 4023f7665d..0d8fe4bbdc 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -47,15 +47,18 @@ export type CommonGeneratedImageMetadata = { postprocessing: null | Array; sampler: | 'ddim' - | 'k_dpm_2_a' - | 'k_dpm_2' - | 'k_dpmpp_2_a' - | 'k_dpmpp_2' - | 'k_euler_a' - | 'k_euler' - | 'k_heun' | 'k_lms' - | 'plms'; + | 'plms' + | 'k_heun' + | 'k_euler' + | 'euler_karras' + | 'k_euler_a' + | 'k_dpm_2' + | 'k_dpm_2_a' + | 'dpmpp_2s' + | 'k_dpmpp_2' + | 'k_dpmpp_2_karras' + | 'unipc'; prompt: Prompt; seed: number; variations: SeedWeights; From b928d7a6e68810b91221e7ee8cb731042cade695 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 02:59:43 +1200 Subject: [PATCH 19/42] Change scheduler names to be accurate _a = Ancestral _k = Karras --- invokeai/backend/args.py | 20 +++++++++---------- .../stable_diffusion/schedulers/schedulers.py | 20 +++++++++---------- invokeai/backend/web/modules/parameters.py | 20 +++++++++---------- invokeai/frontend/web/src/app/constants.ts | 20 +++++++++---------- .../frontend/web/src/app/types/invokeai.ts | 20 +++++++++---------- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/invokeai/backend/args.py b/invokeai/backend/args.py index 0efaf6fd26..0925e19edf 100644 --- a/invokeai/backend/args.py +++ b/invokeai/backend/args.py @@ -108,17 +108,17 @@ APP_VERSION = invokeai.version.__version__ SAMPLER_CHOICES = [ "ddim", - "k_lms", - "plms", - "k_heun", - "k_euler", - "euler_karras", - "k_euler_a", - "k_dpm_2", - "k_dpm_2_a", + "lms", + "pndm", + "heun", + "euler", + "euler_k", + "euler_a", + "kdpm_2", + "kdpm_2_a", "dpmpp_2s", - "k_dpmpp_2", - "k_dpmpp_2_karras", + "dpmpp_2m", + "dpmpp_2m_k", "unipc", ] diff --git a/invokeai/backend/stable_diffusion/schedulers/schedulers.py b/invokeai/backend/stable_diffusion/schedulers/schedulers.py index 98d73ae10b..5df6d045c1 100644 --- a/invokeai/backend/stable_diffusion/schedulers/schedulers.py +++ b/invokeai/backend/stable_diffusion/schedulers/schedulers.py @@ -5,16 +5,16 @@ from diffusers import DDIMScheduler, DPMSolverMultistepScheduler, KDPM2DiscreteS SCHEDULER_MAP = dict( ddim=(DDIMScheduler, dict()), - k_lms=(LMSDiscreteScheduler, dict()), - plms=(PNDMScheduler, dict()), - k_euler=(EulerDiscreteScheduler, dict(use_karras_sigmas=False)), - euler_karras=(EulerDiscreteScheduler, dict(use_karras_sigmas=True)), - k_euler_a=(EulerAncestralDiscreteScheduler, dict()), - k_dpm_2=(KDPM2DiscreteScheduler, dict()), - k_dpm_2_a=(KDPM2AncestralDiscreteScheduler, dict()), + lms=(LMSDiscreteScheduler, dict()), + pndm=(PNDMScheduler, dict()), + heun=(HeunDiscreteScheduler, dict()), + euler=(EulerDiscreteScheduler, dict(use_karras_sigmas=False)), + euler_k=(EulerDiscreteScheduler, dict(use_karras_sigmas=True)), + euler_a=(EulerAncestralDiscreteScheduler, dict()), + kdpm_2=(KDPM2DiscreteScheduler, dict()), + kdpm_2_a=(KDPM2AncestralDiscreteScheduler, dict()), dpmpp_2s=(DPMSolverSinglestepScheduler, dict()), - k_dpmpp_2=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=False)), - k_dpmpp_2_karras=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=True)), - k_heun=(HeunDiscreteScheduler, dict()), + dpmpp_2m=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=False)), + dpmpp_2m_k=(DPMSolverMultistepScheduler, dict(use_karras_sigmas=True)), unipc=(UniPCMultistepScheduler, dict(cpu_only=True)) ) diff --git a/invokeai/backend/web/modules/parameters.py b/invokeai/backend/web/modules/parameters.py index f25864765b..22bc2f353e 100644 --- a/invokeai/backend/web/modules/parameters.py +++ b/invokeai/backend/web/modules/parameters.py @@ -4,17 +4,17 @@ from .parse_seed_weights import parse_seed_weights SAMPLER_CHOICES = [ "ddim", - "k_lms", - "plms", - "k_heun", - "k_euler", - "euler_karras", - "k_euler_a", - "k_dpm_2", - "k_dpm_2_a", + "lms", + "pndm", + "heun", + "euler", + "euler_k", + "euler_a", + "kdpm_2", + "kdpm_2_a", "dpmpp_2s", - "k_dpmpp_2", - "k_dpmpp_2_karras", + "dpmpp_2m", + "dpmpp_2m_k", "unipc", ] diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index d9a606435c..ab1c313abe 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -2,17 +2,17 @@ export const DIFFUSERS_SCHEDULERS: Array = [ 'ddim', - 'k_lms', - 'plms', - 'k_heun', - 'k_euler', - 'euler_karras', - 'k_euler_a', - 'k_dpm_2', - 'k_dpm_2_a', + 'lms', + 'pndm', + 'heun', + 'euler', + 'euler_k', + 'euler_a', + 'kdpm_2', + 'kdpm_2_a', 'dpmpp_2s', - 'k_dpmpp_2', - 'k_dpmpp_2_karras', + 'dpmpp_2m', + 'dpmpp_2m_k', 'unipc', ]; diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 0d8fe4bbdc..998bb00d47 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -47,17 +47,17 @@ export type CommonGeneratedImageMetadata = { postprocessing: null | Array; sampler: | 'ddim' - | 'k_lms' - | 'plms' - | 'k_heun' - | 'k_euler' - | 'euler_karras' - | 'k_euler_a' - | 'k_dpm_2' - | 'k_dpm_2_a' + | 'lms' + | 'pndm' + | 'heun' + | 'euler' + | 'euler_k' + | 'euler_a' + | 'kdpm_2' + | 'kdpm_2_a' | 'dpmpp_2s' - | 'k_dpmpp_2' - | 'k_dpmpp_2_karras' + | 'dpmpp_2m' + | 'dpmpp_2m_k' | 'unipc'; prompt: Prompt; seed: number; From 46ca7718d9e69f559bdec6cd0f679db5d2fc7bd9 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 03:10:30 +1200 Subject: [PATCH 20/42] Add DEIS Scheduler --- invokeai/backend/args.py | 1 + invokeai/backend/stable_diffusion/schedulers/schedulers.py | 3 ++- invokeai/backend/web/modules/parameters.py | 1 + invokeai/frontend/web/src/app/constants.ts | 1 + invokeai/frontend/web/src/app/types/invokeai.ts | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/invokeai/backend/args.py b/invokeai/backend/args.py index 0925e19edf..4af9e43f42 100644 --- a/invokeai/backend/args.py +++ b/invokeai/backend/args.py @@ -108,6 +108,7 @@ APP_VERSION = invokeai.version.__version__ SAMPLER_CHOICES = [ "ddim", + "deis", "lms", "pndm", "heun", diff --git a/invokeai/backend/stable_diffusion/schedulers/schedulers.py b/invokeai/backend/stable_diffusion/schedulers/schedulers.py index 5df6d045c1..752e7cdb0c 100644 --- a/invokeai/backend/stable_diffusion/schedulers/schedulers.py +++ b/invokeai/backend/stable_diffusion/schedulers/schedulers.py @@ -1,10 +1,11 @@ from diffusers import DDIMScheduler, DPMSolverMultistepScheduler, KDPM2DiscreteScheduler, \ KDPM2AncestralDiscreteScheduler, EulerDiscreteScheduler, EulerAncestralDiscreteScheduler, \ HeunDiscreteScheduler, LMSDiscreteScheduler, PNDMScheduler, UniPCMultistepScheduler, \ - DPMSolverSinglestepScheduler + DPMSolverSinglestepScheduler, DEISMultistepScheduler SCHEDULER_MAP = dict( ddim=(DDIMScheduler, dict()), + deis=(DEISMultistepScheduler, dict()), lms=(LMSDiscreteScheduler, dict()), pndm=(PNDMScheduler, dict()), heun=(HeunDiscreteScheduler, dict()), diff --git a/invokeai/backend/web/modules/parameters.py b/invokeai/backend/web/modules/parameters.py index 22bc2f353e..940732898d 100644 --- a/invokeai/backend/web/modules/parameters.py +++ b/invokeai/backend/web/modules/parameters.py @@ -4,6 +4,7 @@ from .parse_seed_weights import parse_seed_weights SAMPLER_CHOICES = [ "ddim", + "deis", "lms", "pndm", "heun", diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index ab1c313abe..f0dc48f6d5 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -2,6 +2,7 @@ export const DIFFUSERS_SCHEDULERS: Array = [ 'ddim', + 'deis', 'lms', 'pndm', 'heun', diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 998bb00d47..2ae221b0a8 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -47,6 +47,7 @@ export type CommonGeneratedImageMetadata = { postprocessing: null | Array; sampler: | 'ddim' + | 'deis' | 'lms' | 'pndm' | 'heun' From 4b957edfecbcfa1545c99d6252ecb5aeaa159d80 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 03:18:34 +1200 Subject: [PATCH 21/42] Add DDPM Scheduler --- invokeai/backend/args.py | 1 + invokeai/backend/stable_diffusion/schedulers/schedulers.py | 3 ++- invokeai/backend/web/modules/parameters.py | 1 + invokeai/frontend/web/src/app/constants.ts | 1 + invokeai/frontend/web/src/app/types/invokeai.ts | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/invokeai/backend/args.py b/invokeai/backend/args.py index 4af9e43f42..856272c45b 100644 --- a/invokeai/backend/args.py +++ b/invokeai/backend/args.py @@ -108,6 +108,7 @@ APP_VERSION = invokeai.version.__version__ SAMPLER_CHOICES = [ "ddim", + "ddpm", "deis", "lms", "pndm", diff --git a/invokeai/backend/stable_diffusion/schedulers/schedulers.py b/invokeai/backend/stable_diffusion/schedulers/schedulers.py index 752e7cdb0c..fab28aca8c 100644 --- a/invokeai/backend/stable_diffusion/schedulers/schedulers.py +++ b/invokeai/backend/stable_diffusion/schedulers/schedulers.py @@ -1,10 +1,11 @@ from diffusers import DDIMScheduler, DPMSolverMultistepScheduler, KDPM2DiscreteScheduler, \ KDPM2AncestralDiscreteScheduler, EulerDiscreteScheduler, EulerAncestralDiscreteScheduler, \ HeunDiscreteScheduler, LMSDiscreteScheduler, PNDMScheduler, UniPCMultistepScheduler, \ - DPMSolverSinglestepScheduler, DEISMultistepScheduler + DPMSolverSinglestepScheduler, DEISMultistepScheduler, DDPMScheduler SCHEDULER_MAP = dict( ddim=(DDIMScheduler, dict()), + ddpm=(DDPMScheduler, dict()), deis=(DEISMultistepScheduler, dict()), lms=(LMSDiscreteScheduler, dict()), pndm=(PNDMScheduler, dict()), diff --git a/invokeai/backend/web/modules/parameters.py b/invokeai/backend/web/modules/parameters.py index 940732898d..72211857a3 100644 --- a/invokeai/backend/web/modules/parameters.py +++ b/invokeai/backend/web/modules/parameters.py @@ -4,6 +4,7 @@ from .parse_seed_weights import parse_seed_weights SAMPLER_CHOICES = [ "ddim", + "ddpm", "deis", "lms", "pndm", diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index f0dc48f6d5..a2dc04fd22 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -2,6 +2,7 @@ export const DIFFUSERS_SCHEDULERS: Array = [ 'ddim', + 'ddpm', 'deis', 'lms', 'pndm', diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts index 2ae221b0a8..b79504d8f2 100644 --- a/invokeai/frontend/web/src/app/types/invokeai.ts +++ b/invokeai/frontend/web/src/app/types/invokeai.ts @@ -47,6 +47,7 @@ export type CommonGeneratedImageMetadata = { postprocessing: null | Array; sampler: | 'ddim' + | 'ddpm' | 'deis' | 'lms' | 'pndm' From f7dc171c4fb33525919993cc2cb415ba19051170 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 03:44:20 +1200 Subject: [PATCH 22/42] Rename default schedulers across the app --- invokeai/app/invocations/generate.py | 2 +- invokeai/app/invocations/latent.py | 2 +- invokeai/backend/args.py | 2 +- invokeai/backend/generate.py | 2 +- .../frontend/web/src/features/nodes/examples/iterationGraph.ts | 2 +- .../web/src/features/parameters/store/generationSlice.ts | 2 +- tests/inpainting/original.json | 2 +- tests/nodes/test_png_metadata_service.py | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/invokeai/app/invocations/generate.py b/invokeai/app/invocations/generate.py index 9a29502048..bc72bbe2b3 100644 --- a/invokeai/app/invocations/generate.py +++ b/invokeai/app/invocations/generate.py @@ -52,7 +52,7 @@ class TextToImageInvocation(BaseInvocation, SDImageInvocation): width: int = Field(default=512, multiple_of=8, gt=0, description="The width of the resulting image", ) height: int = Field(default=512, multiple_of=8, gt=0, description="The height of the resulting image", ) cfg_scale: float = Field(default=7.5, ge=1, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", ) - scheduler: SAMPLER_NAME_VALUES = Field(default="k_lms", description="The scheduler to use" ) + scheduler: SAMPLER_NAME_VALUES = Field(default="lms", description="The scheduler to use" ) model: str = Field(default="", description="The model to use (currently ignored)") # fmt: on diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 75fa719c07..60c77b5286 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -140,7 +140,7 @@ class TextToLatentsInvocation(BaseInvocation): noise: Optional[LatentsField] = Field(description="The noise to use") steps: int = Field(default=10, gt=0, description="The number of steps to use to generate the image") cfg_scale: float = Field(default=7.5, gt=0, description="The Classifier-Free Guidance, higher values may result in a result closer to the prompt", ) - scheduler: SAMPLER_NAME_VALUES = Field(default="k_lms", description="The scheduler to use" ) + scheduler: SAMPLER_NAME_VALUES = Field(default="lms", description="The scheduler to use" ) model: str = Field(default="", description="The model to use (currently ignored)") seamless: bool = Field(default=False, description="Whether or not to generate an image that can tile without seams", ) seamless_axes: str = Field(default="", description="The axes to tile the image on, 'x' and/or 'y'") diff --git a/invokeai/backend/args.py b/invokeai/backend/args.py index 856272c45b..db6fbe08df 100644 --- a/invokeai/backend/args.py +++ b/invokeai/backend/args.py @@ -634,7 +634,7 @@ class Args(object): choices=SAMPLER_CHOICES, metavar="SAMPLER_NAME", help=f'Set the default sampler. Supported samplers: {", ".join(SAMPLER_CHOICES)}', - default="k_lms", + default="lms", ) render_group.add_argument( "--log_tokenization", diff --git a/invokeai/backend/generate.py b/invokeai/backend/generate.py index dc5f9d9ed0..3db987bca8 100644 --- a/invokeai/backend/generate.py +++ b/invokeai/backend/generate.py @@ -142,7 +142,7 @@ class Generate: model=None, conf="configs/models.yaml", embedding_path=None, - sampler_name="k_lms", + sampler_name="lms", ddim_eta=0.0, # deterministic full_precision=False, precision="auto", diff --git a/invokeai/frontend/web/src/features/nodes/examples/iterationGraph.ts b/invokeai/frontend/web/src/features/nodes/examples/iterationGraph.ts index 46ee5289b2..3a5b6fb888 100644 --- a/invokeai/frontend/web/src/features/nodes/examples/iterationGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/examples/iterationGraph.ts @@ -20,7 +20,7 @@ export const iterationGraph = { model: '', progress_images: false, prompt: 'dog', - sampler_name: 'k_lms', + sampler_name: 'lms', seamless: false, steps: 11, type: 'txt2img', diff --git a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts index b0adc578a0..5ad0e4973c 100644 --- a/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/generationSlice.ts @@ -51,7 +51,7 @@ export const initialGenerationState: GenerationState = { perlin: 0, prompt: '', negativePrompt: '', - sampler: 'k_lms', + sampler: 'lms', seamBlur: 16, seamSize: 96, seamSteps: 30, diff --git a/tests/inpainting/original.json b/tests/inpainting/original.json index f057f6d0dc..1a9320da03 100644 --- a/tests/inpainting/original.json +++ b/tests/inpainting/original.json @@ -23,7 +23,7 @@ ], "threshold": 0, "postprocessing": null, - "sampler": "k_lms", + "sampler": "lms", "variations": [], "type": "txt2img" } diff --git a/tests/nodes/test_png_metadata_service.py b/tests/nodes/test_png_metadata_service.py index c724074518..975e716fa9 100644 --- a/tests/nodes/test_png_metadata_service.py +++ b/tests/nodes/test_png_metadata_service.py @@ -17,7 +17,7 @@ valid_metadata = { "width": 512, "height": 512, "cfg_scale": 7.5, - "scheduler": "k_lms", + "scheduler": "lms", "model": "stable-diffusion-1.5", }, } From 27dc07d95ac9d090eb02d7fc847e3fd687f5c877 Mon Sep 17 00:00:00 2001 From: Sergey Borisov Date: Thu, 11 May 2023 18:49:27 +0300 Subject: [PATCH 23/42] Set zero eta by default(fix ddim scheduler error) --- invokeai/app/invocations/latent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/app/invocations/latent.py b/invokeai/app/invocations/latent.py index 60c77b5286..825847cf79 100644 --- a/invokeai/app/invocations/latent.py +++ b/invokeai/app/invocations/latent.py @@ -208,7 +208,7 @@ class TextToLatentsInvocation(BaseInvocation): h_symmetry_time_pct=None,#h_symmetry_time_pct, v_symmetry_time_pct=None#v_symmetry_time_pct, ), - ).add_scheduler_args_if_applicable(model.scheduler, eta=None)#ddim_eta) + ).add_scheduler_args_if_applicable(model.scheduler, eta=0.0)#ddim_eta) return conditioning_data From 97127e560e8732ae25347e6b7dc1e6e1cb0c7d3b Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Fri, 12 May 2023 04:51:58 +1200 Subject: [PATCH 24/42] Disable dpmpp_2s in img2img & unifiedCanvas ... until upstream bug is fixed. --- invokeai/frontend/web/src/app/constants.ts | 6 ++++++ .../components/Parameters/Core/ParamSampler.tsx | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/app/constants.ts b/invokeai/frontend/web/src/app/constants.ts index a2dc04fd22..189fbc9dd4 100644 --- a/invokeai/frontend/web/src/app/constants.ts +++ b/invokeai/frontend/web/src/app/constants.ts @@ -18,6 +18,12 @@ export const DIFFUSERS_SCHEDULERS: Array = [ 'unipc', ]; +export const IMG2IMG_DIFFUSERS_SCHEDULERS = DIFFUSERS_SCHEDULERS.filter( + (scheduler) => { + return scheduler !== 'dpmpp_2s'; + } +); + // Valid image widths export const WIDTHS: Array = Array.from(Array(64)).map( (_x, i) => (i + 1) * 64 diff --git a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx index 5a20f54438..9bd22d9abe 100644 --- a/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/Parameters/Core/ParamSampler.tsx @@ -1,8 +1,12 @@ -import { DIFFUSERS_SCHEDULERS } from 'app/constants'; +import { + DIFFUSERS_SCHEDULERS, + IMG2IMG_DIFFUSERS_SCHEDULERS, +} from 'app/constants'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAISelect from 'common/components/IAISelect'; import { setSampler } from 'features/parameters/store/generationSlice'; +import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { ChangeEvent, memo, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; @@ -10,6 +14,9 @@ const ParamSampler = () => { const sampler = useAppSelector( (state: RootState) => state.generation.sampler ); + + const activeTabName = useAppSelector(activeTabNameSelector); + const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -23,7 +30,11 @@ const ParamSampler = () => { label={t('parameters.sampler')} value={sampler} onChange={handleChange} - validValues={DIFFUSERS_SCHEDULERS} + validValues={ + activeTabName === 'img2img' || activeTabName == 'unifiedCanvas' + ? IMG2IMG_DIFFUSERS_SCHEDULERS + : DIFFUSERS_SCHEDULERS + } minWidth={36} /> ); From 6de2f66b50b1be7dd38f93da84de909bf75788ad Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 11:11:59 +1000 Subject: [PATCH 25/42] docs(ui): update ui readme --- invokeai/frontend/web/docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invokeai/frontend/web/docs/README.md b/invokeai/frontend/web/docs/README.md index 787725cdda..323dcc5bc7 100644 --- a/invokeai/frontend/web/docs/README.md +++ b/invokeai/frontend/web/docs/README.md @@ -37,7 +37,7 @@ From `invokeai/frontend/web/` run `yarn install` to get everything set up. Start everything in dev mode: 1. Start the dev server: `yarn dev` -2. Start the InvokeAI UI per usual: `invokeai --web` +2. Start the InvokeAI Nodes backend: `python scripts/invokeai-new.py --web # run from the repo root` 3. Point your browser to the dev server address e.g. ### Production builds From 4caa1f19b2e81a3c54e0aa04abf0df73b17cbaa2 Mon Sep 17 00:00:00 2001 From: Kevin Turner <83819+keturn@users.noreply.github.com> Date: Thu, 11 May 2023 19:06:02 -0700 Subject: [PATCH 26/42] fix(model manager): fix string formatting error on model checksum timer --- invokeai/backend/model_management/model_manager.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/invokeai/backend/model_management/model_manager.py b/invokeai/backend/model_management/model_manager.py index a0a899a319..4f94395a86 100644 --- a/invokeai/backend/model_management/model_manager.py +++ b/invokeai/backend/model_management/model_manager.py @@ -30,7 +30,7 @@ from diffusers import ( UNet2DConditionModel, SchedulerMixin, logging as dlogging, -) +) from huggingface_hub import scan_cache_dir from omegaconf import OmegaConf from omegaconf.dictconfig import DictConfig @@ -68,7 +68,7 @@ class SDModelComponent(Enum): scheduler="scheduler" safety_checker="safety_checker" feature_extractor="feature_extractor" - + DEFAULT_MAX_MODELS = 2 class ModelManager(object): @@ -182,7 +182,7 @@ class ModelManager(object): vae from the model currently in the GPU. """ return self._get_sub_model(model_name, SDModelComponent.vae) - + def get_model_tokenizer(self, model_name: str=None)->CLIPTokenizer: """Given a model name identified in models.yaml, load the model into GPU if necessary and return its assigned CLIPTokenizer. If no @@ -190,12 +190,12 @@ class ModelManager(object): currently in the GPU. """ return self._get_sub_model(model_name, SDModelComponent.tokenizer) - + def get_model_unet(self, model_name: str=None)->UNet2DConditionModel: """Given a model name identified in models.yaml, load the model into GPU if necessary and return its assigned UNet2DConditionModel. If no model name is provided, return the UNet from the model - currently in the GPU. + currently in the GPU. """ return self._get_sub_model(model_name, SDModelComponent.unet) @@ -222,7 +222,7 @@ class ModelManager(object): currently in the GPU. """ return self._get_sub_model(model_name, SDModelComponent.scheduler) - + def _get_sub_model( self, model_name: str=None, @@ -1228,7 +1228,7 @@ class ModelManager(object): sha.update(chunk) hash = sha.hexdigest() toc = time.time() - self.logger.debug(f"sha256 = {hash} ({count} files hashed in", "%4.2fs)" % (toc - tic)) + self.logger.debug(f"sha256 = {hash} ({count} files hashed in {toc - tic:4.2f}s)") with open(hashpath, "w") as f: f.write(hash) return hash From ebec200ba6ac8e295d4c22015f1e6dc23421879a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 13:56:02 +1000 Subject: [PATCH 27/42] Remove unused import --- invokeai/app/invocations/math.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/invokeai/app/invocations/math.py b/invokeai/app/invocations/math.py index b7d2bc5af6..98f87d2dd4 100644 --- a/invokeai/app/invocations/math.py +++ b/invokeai/app/invocations/math.py @@ -5,8 +5,6 @@ from typing import Literal from pydantic import BaseModel, Field import numpy as np -from invokeai.app.util.misc import get_random_seed - from .baseinvocation import BaseInvocation, BaseInvocationOutput, InvocationContext, InvocationConfig @@ -84,4 +82,4 @@ class RandomIntInvocation(BaseInvocation): type: Literal["rand_int"] = "rand_int" #fmt: on def invoke(self, context: InvocationContext) -> IntOutput: - return IntOutput(a=np.random.randint(0, np.iinfo(np.int32).max)) \ No newline at end of file + return IntOutput(a=np.random.randint(0, np.iinfo(np.int32).max)) From 7dc9d18052294751dece6c9768976a637ddfe483 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 11:48:37 +1000 Subject: [PATCH 28/42] fix(ui): do not show intermediates uploads in gallery --- .../middleware/listenerMiddleware/listeners/imageUploaded.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts index 7d578356f4..c32da2e710 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/imageUploaded.ts @@ -6,9 +6,12 @@ import { imageUploaded } from 'services/thunks/image'; export const addImageUploadedListener = () => { startAppListening({ - actionCreator: imageUploaded.fulfilled, + predicate: (action): action is ReturnType => + imageUploaded.fulfilled.match(action) && + action.payload.response.image_type !== 'intermediates', effect: (action, { dispatch, getState }) => { const { response } = action.payload; + const state = getState(); const image = deserializeImageResponse(response); From 65b527eb20914c1ef37e9b91bc09ecc3b7a6f9b4 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 11:50:57 +1000 Subject: [PATCH 29/42] fix(ui): do not show progress images in uploads gallery category --- .../components/ImageGalleryContent.tsx | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index 1426aff43d..b37cd5ca62 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -54,11 +54,7 @@ import { uploadsAdapter } from '../store/uploadsSlice'; import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; import { Virtuoso, VirtuosoGrid } from 'react-virtuoso'; -import ProgressImagePreview from 'features/parameters/components/_ProgressImagePreview'; -import ProgressImage from 'features/parameters/components/ProgressImage'; -import { systemSelector } from 'features/system/store/systemSelectors'; import { Image as ImageType } from 'app/types/invokeai'; -import { ProgressImage as ProgressImageType } from 'services/events/types'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import GalleryProgressImage from './GalleryProgressImage'; @@ -71,13 +67,13 @@ const selector = createSelector( const { results, uploads, system, gallery } = state; const { currentCategory } = gallery; - const tempImages: (ImageType | typeof PROGRESS_IMAGE_PLACEHOLDER)[] = []; - - if (system.progressImage) { - tempImages.push(PROGRESS_IMAGE_PLACEHOLDER); - } - if (currentCategory === 'results') { + const tempImages: (ImageType | typeof PROGRESS_IMAGE_PLACEHOLDER)[] = []; + + if (system.progressImage) { + tempImages.push(PROGRESS_IMAGE_PLACEHOLDER); + } + return { images: tempImages.concat( resultsAdapter.getSelectors().selectAll(results) @@ -88,9 +84,7 @@ const selector = createSelector( } return { - images: tempImages.concat( - uploadsAdapter.getSelectors().selectAll(uploads) - ), + images: uploadsAdapter.getSelectors().selectAll(uploads), isLoading: uploads.isLoading, areMoreImagesAvailable: uploads.page < uploads.pages - 1, }; From e4fb9cb33fdbb89336ade1a821250771aaa6b14e Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 11:51:24 +1000 Subject: [PATCH 30/42] chore(ui): regen api client --- .../frontend/web/src/services/api/index.ts | 2 -- .../api/models/DataURLToImageInvocation.ts | 19 ----------------- .../web/src/services/api/models/Graph.ts | 3 +-- .../api/models/ImageToImageInvocation.ts | 2 +- .../api/models/InfillColorInvocation.ts | 2 +- .../api/models/InfillPatchMatchInvocation.ts | 2 +- .../services/api/models/InpaintInvocation.ts | 2 +- .../api/models/LatentsToLatentsInvocation.ts | 10 ++++++++- .../api/models/TextToImageInvocation.ts | 2 +- .../api/models/TextToLatentsInvocation.ts | 10 ++++++++- .../api/schemas/$DataURLToImageInvocation.ts | 21 ------------------- .../web/src/services/api/schemas/$Graph.ts | 2 -- .../api/schemas/$InfillColorInvocation.ts | 2 +- .../schemas/$InfillPatchMatchInvocation.ts | 2 +- .../schemas/$LatentsToLatentsInvocation.ts | 8 +++++++ .../api/schemas/$TextToLatentsInvocation.ts | 8 +++++++ .../services/api/services/SessionsService.ts | 5 ++--- 17 files changed, 44 insertions(+), 58 deletions(-) delete mode 100644 invokeai/frontend/web/src/services/api/models/DataURLToImageInvocation.ts delete mode 100644 invokeai/frontend/web/src/services/api/schemas/$DataURLToImageInvocation.ts diff --git a/invokeai/frontend/web/src/services/api/index.ts b/invokeai/frontend/web/src/services/api/index.ts index 24eeb458b6..0671808245 100644 --- a/invokeai/frontend/web/src/services/api/index.ts +++ b/invokeai/frontend/web/src/services/api/index.ts @@ -19,7 +19,6 @@ export type { ConditioningField } from './models/ConditioningField'; export type { CreateModelRequest } from './models/CreateModelRequest'; export type { CropImageInvocation } from './models/CropImageInvocation'; export type { CvInpaintInvocation } from './models/CvInpaintInvocation'; -export type { DataURLToImageInvocation } from './models/DataURLToImageInvocation'; export type { DiffusersModelInfo } from './models/DiffusersModelInfo'; export type { DivideInvocation } from './models/DivideInvocation'; export type { Edge } from './models/Edge'; @@ -92,7 +91,6 @@ export { $ConditioningField } from './schemas/$ConditioningField'; export { $CreateModelRequest } from './schemas/$CreateModelRequest'; export { $CropImageInvocation } from './schemas/$CropImageInvocation'; export { $CvInpaintInvocation } from './schemas/$CvInpaintInvocation'; -export { $DataURLToImageInvocation } from './schemas/$DataURLToImageInvocation'; export { $DiffusersModelInfo } from './schemas/$DiffusersModelInfo'; export { $DivideInvocation } from './schemas/$DivideInvocation'; export { $Edge } from './schemas/$Edge'; diff --git a/invokeai/frontend/web/src/services/api/models/DataURLToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/DataURLToImageInvocation.ts deleted file mode 100644 index b1e35d9e0c..0000000000 --- a/invokeai/frontend/web/src/services/api/models/DataURLToImageInvocation.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ - -/** - * Outputs an image from a data URL. - */ -export type DataURLToImageInvocation = { - /** - * The id of this node. Must be unique among all nodes. - */ - id: string; - type?: 'dataURL_image'; - /** - * The b64 data URL - */ - dataURL: string; -}; - diff --git a/invokeai/frontend/web/src/services/api/models/Graph.ts b/invokeai/frontend/web/src/services/api/models/Graph.ts index 9d56276e62..08d993e477 100644 --- a/invokeai/frontend/web/src/services/api/models/Graph.ts +++ b/invokeai/frontend/web/src/services/api/models/Graph.ts @@ -8,7 +8,6 @@ import type { CollectInvocation } from './CollectInvocation'; import type { CompelInvocation } from './CompelInvocation'; import type { CropImageInvocation } from './CropImageInvocation'; import type { CvInpaintInvocation } from './CvInpaintInvocation'; -import type { DataURLToImageInvocation } from './DataURLToImageInvocation'; import type { DivideInvocation } from './DivideInvocation'; import type { Edge } from './Edge'; import type { GraphInvocation } from './GraphInvocation'; @@ -48,7 +47,7 @@ export type Graph = { /** * The nodes in this graph */ - nodes?: Record; + nodes?: Record; /** * The connections between nodes and their fields in this graph */ diff --git a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts index 0dfb893213..09479388a7 100644 --- a/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/ImageToImageInvocation.ts @@ -40,7 +40,7 @@ export type ImageToImageInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'dpmpp_2' | 'k_dpm_2' | 'k_dpm_2_a' | 'k_dpmpp_2' | 'k_euler' | 'k_euler_a' | 'k_heun' | 'k_lms' | 'plms'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts index a0335eab89..157c976e11 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillColorInvocation.ts @@ -6,7 +6,7 @@ import type { ColorField } from './ColorField'; import type { ImageField } from './ImageField'; /** - * Infills transparent areas of an image with a color + * Infills transparent areas of an image with a solid color */ export type InfillColorInvocation = { /** diff --git a/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts b/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts index 6d6c1074a5..a4c18ade5d 100644 --- a/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InfillPatchMatchInvocation.ts @@ -5,7 +5,7 @@ import type { ImageField } from './ImageField'; /** - * Infills transparent areas of an image with tiles of the image + * Infills transparent areas of an image using the PatchMatch algorithm */ export type InfillPatchMatchInvocation = { /** diff --git a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts index c4b125902a..9ef7fcb36e 100644 --- a/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/InpaintInvocation.ts @@ -41,7 +41,7 @@ export type InpaintInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'dpmpp_2' | 'k_dpm_2' | 'k_dpm_2_a' | 'k_dpmpp_2' | 'k_euler' | 'k_euler_a' | 'k_heun' | 'k_lms' | 'plms'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts index 7795ce2b21..425eeaf3d5 100644 --- a/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/LatentsToLatentsInvocation.ts @@ -37,11 +37,19 @@ export type LatentsToLatentsInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'dpmpp_2' | 'k_dpm_2' | 'k_dpm_2_a' | 'k_dpmpp_2' | 'k_euler' | 'k_euler_a' | 'k_heun' | 'k_lms' | 'plms'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ model?: string; + /** + * Whether or not to generate an image that can tile without seams + */ + seamless?: boolean; + /** + * The axes to tile the image on, 'x' and/or 'y' + */ + seamless_axes?: string; /** * The latents to use as a base image */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts index d928515c76..64e072bf4a 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToImageInvocation.ts @@ -38,7 +38,7 @@ export type TextToImageInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'dpmpp_2' | 'k_dpm_2' | 'k_dpm_2_a' | 'k_dpmpp_2' | 'k_euler' | 'k_euler_a' | 'k_heun' | 'k_lms' | 'plms'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ diff --git a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts index a67170d6c8..f939070763 100644 --- a/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/models/TextToLatentsInvocation.ts @@ -37,10 +37,18 @@ export type TextToLatentsInvocation = { /** * The scheduler to use */ - scheduler?: 'ddim' | 'dpmpp_2' | 'k_dpm_2' | 'k_dpm_2_a' | 'k_dpmpp_2' | 'k_euler' | 'k_euler_a' | 'k_heun' | 'k_lms' | 'plms'; + scheduler?: 'ddim' | 'ddpm' | 'deis' | 'lms' | 'pndm' | 'heun' | 'euler' | 'euler_k' | 'euler_a' | 'kdpm_2' | 'kdpm_2_a' | 'dpmpp_2s' | 'dpmpp_2m' | 'dpmpp_2m_k' | 'unipc'; /** * The model to use (currently ignored) */ model?: string; + /** + * Whether or not to generate an image that can tile without seams + */ + seamless?: boolean; + /** + * The axes to tile the image on, 'x' and/or 'y' + */ + seamless_axes?: string; }; diff --git a/invokeai/frontend/web/src/services/api/schemas/$DataURLToImageInvocation.ts b/invokeai/frontend/web/src/services/api/schemas/$DataURLToImageInvocation.ts deleted file mode 100644 index f875cd0a11..0000000000 --- a/invokeai/frontend/web/src/services/api/schemas/$DataURLToImageInvocation.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $DataURLToImageInvocation = { - description: `Outputs an image from a data URL.`, - properties: { - id: { - type: 'string', - description: `The id of this node. Must be unique among all nodes.`, - isRequired: true, - }, - type: { - type: 'Enum', - }, - dataURL: { - type: 'string', - description: `The b64 data URL`, - isRequired: true, - }, - }, -} as const; diff --git a/invokeai/frontend/web/src/services/api/schemas/$Graph.ts b/invokeai/frontend/web/src/services/api/schemas/$Graph.ts index f3d5fe0edd..b526def648 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$Graph.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$Graph.ts @@ -15,8 +15,6 @@ export const $Graph = { type: 'LoadImageInvocation', }, { type: 'ShowImageInvocation', - }, { - type: 'DataURLToImageInvocation', }, { type: 'CropImageInvocation', }, { diff --git a/invokeai/frontend/web/src/services/api/schemas/$InfillColorInvocation.ts b/invokeai/frontend/web/src/services/api/schemas/$InfillColorInvocation.ts index 42af32c9b2..a4f639f280 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$InfillColorInvocation.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$InfillColorInvocation.ts @@ -2,7 +2,7 @@ /* tslint:disable */ /* eslint-disable */ export const $InfillColorInvocation = { - description: `Infills transparent areas of an image with a color`, + description: `Infills transparent areas of an image with a solid color`, properties: { id: { type: 'string', diff --git a/invokeai/frontend/web/src/services/api/schemas/$InfillPatchMatchInvocation.ts b/invokeai/frontend/web/src/services/api/schemas/$InfillPatchMatchInvocation.ts index 0278dafd35..bc62cb829f 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$InfillPatchMatchInvocation.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$InfillPatchMatchInvocation.ts @@ -2,7 +2,7 @@ /* tslint:disable */ /* eslint-disable */ export const $InfillPatchMatchInvocation = { - description: `Infills transparent areas of an image with tiles of the image`, + description: `Infills transparent areas of an image using the PatchMatch algorithm`, properties: { id: { type: 'string', diff --git a/invokeai/frontend/web/src/services/api/schemas/$LatentsToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/schemas/$LatentsToLatentsInvocation.ts index 38df3ad5cc..47f28bed61 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$LatentsToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$LatentsToLatentsInvocation.ts @@ -48,6 +48,14 @@ export const $LatentsToLatentsInvocation = { type: 'string', description: `The model to use (currently ignored)`, }, + seamless: { + type: 'boolean', + description: `Whether or not to generate an image that can tile without seams`, + }, + seamless_axes: { + type: 'string', + description: `The axes to tile the image on, 'x' and/or 'y'`, + }, latents: { type: 'all-of', description: `The latents to use as a base image`, diff --git a/invokeai/frontend/web/src/services/api/schemas/$TextToLatentsInvocation.ts b/invokeai/frontend/web/src/services/api/schemas/$TextToLatentsInvocation.ts index 1080890606..5ff7b44129 100644 --- a/invokeai/frontend/web/src/services/api/schemas/$TextToLatentsInvocation.ts +++ b/invokeai/frontend/web/src/services/api/schemas/$TextToLatentsInvocation.ts @@ -48,5 +48,13 @@ export const $TextToLatentsInvocation = { type: 'string', description: `The model to use (currently ignored)`, }, + seamless: { + type: 'boolean', + description: `Whether or not to generate an image that can tile without seams`, + }, + seamless_axes: { + type: 'string', + description: `The axes to tile the image on, 'x' and/or 'y'`, + }, }, } as const; diff --git a/invokeai/frontend/web/src/services/api/services/SessionsService.ts b/invokeai/frontend/web/src/services/api/services/SessionsService.ts index 694f3822cb..c16e7ecd3d 100644 --- a/invokeai/frontend/web/src/services/api/services/SessionsService.ts +++ b/invokeai/frontend/web/src/services/api/services/SessionsService.ts @@ -7,7 +7,6 @@ import type { CollectInvocation } from '../models/CollectInvocation'; import type { CompelInvocation } from '../models/CompelInvocation'; import type { CropImageInvocation } from '../models/CropImageInvocation'; import type { CvInpaintInvocation } from '../models/CvInpaintInvocation'; -import type { DataURLToImageInvocation } from '../models/DataURLToImageInvocation'; import type { DivideInvocation } from '../models/DivideInvocation'; import type { Edge } from '../models/Edge'; import type { Graph } from '../models/Graph'; @@ -150,7 +149,7 @@ export class SessionsService { * The id of the session */ sessionId: string, - requestBody: (LoadImageInvocation | ShowImageInvocation | DataURLToImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), + requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), }): CancelablePromise { return __request(OpenAPI, { method: 'POST', @@ -187,7 +186,7 @@ export class SessionsService { * The path to the node in the graph */ nodePath: string, - requestBody: (LoadImageInvocation | ShowImageInvocation | DataURLToImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), + requestBody: (LoadImageInvocation | ShowImageInvocation | CropImageInvocation | PasteImageInvocation | MaskFromAlphaInvocation | BlurInvocation | LerpInvocation | InverseLerpInvocation | CompelInvocation | NoiseInvocation | TextToLatentsInvocation | LatentsToImageInvocation | ResizeLatentsInvocation | ScaleLatentsInvocation | ImageToLatentsInvocation | AddInvocation | SubtractInvocation | MultiplyInvocation | DivideInvocation | ParamIntInvocation | CvInpaintInvocation | RangeInvocation | RandomRangeInvocation | UpscaleInvocation | RestoreFaceInvocation | TextToImageInvocation | InfillColorInvocation | InfillTileInvocation | InfillPatchMatchInvocation | GraphInvocation | IterateInvocation | CollectInvocation | LatentsToLatentsInvocation | ImageToImageInvocation | InpaintInvocation), }): CancelablePromise { return __request(OpenAPI, { method: 'PUT', From df5ba75c1425a3f6671bc4d13ccf215b35ecb3da Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 17:43:14 +1000 Subject: [PATCH 31/42] feat(ui): use custom dark mode localStorage key --- invokeai/frontend/web/src/app/components/App.tsx | 9 +-------- .../web/src/app/components/ThemeLocaleProvider.tsx | 14 ++++++++++++-- invokeai/frontend/web/src/theme/theme.ts | 2 ++ 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index b49e44e554..57d507c89f 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -7,7 +7,7 @@ import useToastWatcher from 'features/system/hooks/useToastWatcher'; import FloatingGalleryButton from 'features/ui/components/FloatingGalleryButton'; import FloatingParametersPanelButtons from 'features/ui/components/FloatingParametersPanelButtons'; -import { Box, Flex, Grid, Portal, useColorMode } from '@chakra-ui/react'; +import { Box, Flex, Grid, Portal } from '@chakra-ui/react'; import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants'; import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel'; import Lightbox from 'features/lightbox/components/Lightbox'; @@ -41,15 +41,12 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => { useGlobalHotkeys(); const log = useLogger(); - const currentTheme = useAppSelector((state) => state.ui.currentTheme); - const isLightboxEnabled = useFeatureStatus('lightbox').isFeatureEnabled; const isApplicationReady = useIsApplicationReady(); const [loadingOverridden, setLoadingOverridden] = useState(false); - const { setColorMode } = useColorMode(); const dispatch = useAppDispatch(); useEffect(() => { @@ -57,10 +54,6 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => { dispatch(configChanged(config)); }, [dispatch, config, log]); - useEffect(() => { - setColorMode(['light'].includes(currentTheme) ? 'light' : 'dark'); - }, [setColorMode, currentTheme]); - const handleOverrideClicked = useCallback(() => { setLoadingOverridden(true); }, []); diff --git a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx index f0e2e75240..11f20c1275 100644 --- a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx +++ b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx @@ -1,4 +1,8 @@ -import { ChakraProvider, extendTheme } from '@chakra-ui/react'; +import { + ChakraProvider, + createLocalStorageManager, + extendTheme, +} from '@chakra-ui/react'; import { ReactNode, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { theme as invokeAITheme } from 'theme/theme'; @@ -32,6 +36,8 @@ const THEMES = { ocean: oceanBlueColors, }; +const manager = createLocalStorageManager('@@invokeai-color-mode'); + function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) { const { i18n } = useTranslation(); @@ -51,7 +57,11 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) { document.body.dir = direction; }, [direction]); - return {children}; + return ( + + {children} + + ); } export default ThemeLocaleProvider; diff --git a/invokeai/frontend/web/src/theme/theme.ts b/invokeai/frontend/web/src/theme/theme.ts index c5b127f040..986024aa39 100644 --- a/invokeai/frontend/web/src/theme/theme.ts +++ b/invokeai/frontend/web/src/theme/theme.ts @@ -23,6 +23,8 @@ import { textareaTheme } from './components/textarea'; export const theme: ThemeOverride = { config: { cssVarPrefix: 'invokeai', + initialColorMode: 'dark', + useSystemColorMode: false, }, styles: { global: (_props: StyleFunctionProps) => ({ From da364f34445db8071326fef7ec5a451e6914d69c Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 17:44:48 +1000 Subject: [PATCH 32/42] feat(ui): use variable font reduces package build's CSS by an order of magnitude --- .../frontend/web/src/app/components/InvokeAIUI.tsx | 9 --------- .../web/src/app/components/ThemeLocaleProvider.tsx | 11 ++--------- invokeai/frontend/web/src/theme/theme.ts | 2 +- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx index d9d99de6cf..f0303cfc99 100644 --- a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx +++ b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx @@ -2,15 +2,6 @@ import React, { lazy, memo, PropsWithChildren, useEffect } from 'react'; import { Provider } from 'react-redux'; import { store } from 'app/store/store'; import { OpenAPI } from 'services/api'; -import '@fontsource/inter/100.css'; -import '@fontsource/inter/200.css'; -import '@fontsource/inter/300.css'; -import '@fontsource/inter/400.css'; -import '@fontsource/inter/500.css'; -import '@fontsource/inter/600.css'; -import '@fontsource/inter/700.css'; -import '@fontsource/inter/800.css'; -import '@fontsource/inter/900.css'; import Loading from '../../common/components/Loading/Loading'; import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares'; diff --git a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx index 11f20c1275..6aa38fc15b 100644 --- a/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx +++ b/invokeai/frontend/web/src/app/components/ThemeLocaleProvider.tsx @@ -13,15 +13,8 @@ import { greenTeaThemeColors } from 'theme/colors/greenTea'; import { invokeAIThemeColors } from 'theme/colors/invokeAI'; import { lightThemeColors } from 'theme/colors/lightTheme'; import { oceanBlueColors } from 'theme/colors/oceanBlue'; -import '@fontsource/inter/100.css'; -import '@fontsource/inter/200.css'; -import '@fontsource/inter/300.css'; -import '@fontsource/inter/400.css'; -import '@fontsource/inter/500.css'; -import '@fontsource/inter/600.css'; -import '@fontsource/inter/700.css'; -import '@fontsource/inter/800.css'; -import '@fontsource/inter/900.css'; + +import '@fontsource/inter/variable.css'; import 'overlayscrollbars/overlayscrollbars.css'; import 'theme/css/overlayscrollbars.css'; diff --git a/invokeai/frontend/web/src/theme/theme.ts b/invokeai/frontend/web/src/theme/theme.ts index 986024aa39..90f26387ab 100644 --- a/invokeai/frontend/web/src/theme/theme.ts +++ b/invokeai/frontend/web/src/theme/theme.ts @@ -41,7 +41,7 @@ export const theme: ThemeOverride = { }, direction: 'ltr', fonts: { - body: `'Inter', sans-serif`, + body: `'InterVariable', sans-serif`, }, breakpoints: { base: '0em', // 0px and onwards From 108ce06c623ea6e16d05e652577f4be7b25a5cb7 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 17:45:56 +1000 Subject: [PATCH 33/42] feat(ui): change custom header to be a prop instead of children --- .../frontend/web/src/app/components/App.tsx | 18 ++++++------------ .../web/src/app/components/InvokeAIUI.tsx | 13 ++++++++++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index 57d507c89f..b920698f14 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -11,14 +11,8 @@ import { Box, Flex, Grid, Portal } from '@chakra-ui/react'; import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants'; import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel'; import Lightbox from 'features/lightbox/components/Lightbox'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { - memo, - PropsWithChildren, - useCallback, - useEffect, - useState, -} from 'react'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { memo, ReactNode, useCallback, useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import Loading from 'common/components/Loading/Loading'; import { useIsApplicationReady } from 'features/system/hooks/useIsApplicationReady'; @@ -27,16 +21,16 @@ import { useGlobalHotkeys } from 'common/hooks/useGlobalHotkeys'; import { configChanged } from 'features/system/store/configSlice'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useLogger } from 'app/logging/useLogger'; -import ProgressImagePreview from 'features/parameters/components/_ProgressImagePreview'; import ParametersDrawer from 'features/ui/components/ParametersDrawer'; const DEFAULT_CONFIG = {}; -interface Props extends PropsWithChildren { +interface Props { config?: PartialAppConfig; + headerComponent?: ReactNode; } -const App = ({ config = DEFAULT_CONFIG, children }: Props) => { +const App = ({ config = DEFAULT_CONFIG, headerComponent }: Props) => { useToastWatcher(); useGlobalHotkeys(); const log = useLogger(); @@ -70,7 +64,7 @@ const App = ({ config = DEFAULT_CONFIG, children }: Props) => { w={APP_WIDTH} h={APP_HEIGHT} > - {children || } + {headerComponent || } { +const InvokeAIUI = ({ apiUrl, token, config, headerComponent }: Props) => { useEffect(() => { // configure API client token if (token) { @@ -48,7 +55,7 @@ const InvokeAIUI = ({ apiUrl, token, config, children }: Props) => { }> - {children} + From 14070d674ea1b1458c028c2feecfc67a4b0d88f4 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 17:48:20 +1000 Subject: [PATCH 34/42] build(ui): add style injection plugin when building for package, CSS is all in JS files. when used as a package, it is then injected into the page. bit of a hack to missing CSS in commercial product --- invokeai/frontend/web/config/vite.package.config.ts | 5 ++++- invokeai/frontend/web/tsconfig.json | 8 +------- invokeai/frontend/web/yarn.lock | 5 +++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/invokeai/frontend/web/config/vite.package.config.ts b/invokeai/frontend/web/config/vite.package.config.ts index 5865461b06..f87cce0bc9 100644 --- a/invokeai/frontend/web/config/vite.package.config.ts +++ b/invokeai/frontend/web/config/vite.package.config.ts @@ -5,6 +5,7 @@ import { PluginOption, UserConfig } from 'vite'; import dts from 'vite-plugin-dts'; import eslint from 'vite-plugin-eslint'; import tsconfigPaths from 'vite-tsconfig-paths'; +import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'; export const packageConfig: UserConfig = { base: './', @@ -16,9 +17,10 @@ export const packageConfig: UserConfig = { dts({ insertTypesEntry: true, }), + cssInjectedByJsPlugin(), ], build: { - chunkSizeWarningLimit: 1500, + cssCodeSplit: true, lib: { entry: path.resolve(__dirname, '../src/index.ts'), name: 'InvokeAIUI', @@ -30,6 +32,7 @@ export const packageConfig: UserConfig = { globals: { react: 'React', 'react-dom': 'ReactDOM', + '@emotion/react': 'EmotionReact', }, }, }, diff --git a/invokeai/frontend/web/tsconfig.json b/invokeai/frontend/web/tsconfig.json index 3c777b9318..8276f461eb 100644 --- a/invokeai/frontend/web/tsconfig.json +++ b/invokeai/frontend/web/tsconfig.json @@ -20,13 +20,7 @@ "*": ["./src/*"] } }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - "*.d.ts", - "src/app/store/middleware/listenerMiddleware", - "src/features/nodes/util/edgeBuilders" - ], + "include": ["src/**/*.ts", "src/**/*.tsx", "*.d.ts"], "exclude": ["src/services/fixtures/*", "node_modules", "dist"], "references": [{ "path": "./tsconfig.node.json" }] } diff --git a/invokeai/frontend/web/yarn.lock b/invokeai/frontend/web/yarn.lock index 90bc4055ac..2313cf91c7 100644 --- a/invokeai/frontend/web/yarn.lock +++ b/invokeai/frontend/web/yarn.lock @@ -6673,6 +6673,11 @@ validator@^13.7.0: resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== +vite-plugin-css-injected-by-js@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.1.1.tgz#8324412636cf6fdada1a86f595aa2e78458e5ddb" + integrity sha512-mwrFvEEy0TuH8Ul0cb2HgjmNboQ/JnEFy+kHCWqAJph3ikMOiIuyYVdx0JO4nEIWJyzSnc4TTdmoTulsikvJEg== + vite-plugin-dts@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/vite-plugin-dts/-/vite-plugin-dts-2.3.0.tgz#6ab2edf56f48261bfede03958704bfaee2fca3e4" From 35e0863bdb90138de8999604eb8bdd9ee6fc4f3a Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 17:53:59 +1000 Subject: [PATCH 35/42] fix(ui): fix tab icon sizes --- .../frontend/web/src/features/ui/components/InvokeTabs.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx index d2bd4f6d51..c346723266 100644 --- a/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx +++ b/invokeai/frontend/web/src/features/ui/components/InvokeTabs.tsx @@ -45,12 +45,12 @@ export interface InvokeTabInfo { const tabs: InvokeTabInfo[] = [ { id: 'txt2img', - icon: , + icon: , content: , }, { id: 'img2img', - icon: , + icon: , content: , }, { From 032aa1d59ca95b9320ccb863fcc645d48f0edc07 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 18:50:54 +1000 Subject: [PATCH 36/42] fix(ui): excise most `zIndex`s our stacking contexts are accurate, `zIndex` isn't needed --- .../frontend/web/src/features/system/components/ProgressBar.tsx | 1 - .../web/src/features/ui/components/FloatingGalleryButton.tsx | 1 - .../features/ui/components/FloatingParametersPanelButtons.tsx | 1 - .../ui/components/common/ResizableDrawer/ResizableDrawer.tsx | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx b/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx index 03e78965a3..4584bee644 100644 --- a/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx +++ b/invokeai/frontend/web/src/features/system/components/ProgressBar.tsx @@ -36,7 +36,6 @@ const ProgressBar = () => { aria-label={t('accessibility.invokeProgressBar')} isIndeterminate={isProcessing && !currentStatusHasSteps} height={PROGRESS_BAR_THICKNESS} - zIndex={99} /> ); }; diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx index bcbcc1cefc..c816c9b39e 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingGalleryButton.tsx @@ -44,7 +44,6 @@ const FloatingGalleryButton = () => { pos: 'absolute', top: '50%', transform: 'translate(0, -50%)', - zIndex: 31, p: 0, insetInlineEnd: 0, px: 3, diff --git a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx index 95ac1257c0..4f4995ffa7 100644 --- a/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx +++ b/invokeai/frontend/web/src/features/ui/components/FloatingParametersPanelButtons.tsx @@ -73,7 +73,6 @@ const FloatingParametersPanelButtons = () => { Date: Fri, 12 May 2023 18:55:59 +1000 Subject: [PATCH 37/42] fix(ui): add missing package --- invokeai/frontend/web/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index 561efdd8e9..13f79f4a44 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -145,6 +145,7 @@ "terser": "^5.17.1", "ts-toolbelt": "^9.6.0", "vite": "^4.3.3", + "vite-plugin-css-injected-by-js": "^3.1.1", "vite-plugin-dts": "^2.3.0", "vite-plugin-eslint": "^1.8.1", "vite-tsconfig-paths": "^4.2.0", From 4d6eea7e81aafb5fcd7acd67951f233b884768f2 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 19:35:03 +1000 Subject: [PATCH 38/42] feat(ui): store language in redux --- .../frontend/web/src/app/components/App.tsx | 11 +++- .../system/components/LanguagePicker.tsx | 60 ++++++++++--------- .../features/system/store/systemSelectors.ts | 21 ++++--- .../src/features/system/store/systemSlice.ts | 7 +++ invokeai/frontend/web/src/i18n.ts | 10 ++-- 5 files changed, 64 insertions(+), 45 deletions(-) diff --git a/invokeai/frontend/web/src/app/components/App.tsx b/invokeai/frontend/web/src/app/components/App.tsx index b920698f14..3fbcbc49ea 100644 --- a/invokeai/frontend/web/src/app/components/App.tsx +++ b/invokeai/frontend/web/src/app/components/App.tsx @@ -11,7 +11,7 @@ import { Box, Flex, Grid, Portal } from '@chakra-ui/react'; import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants'; import GalleryDrawer from 'features/gallery/components/ImageGalleryPanel'; import Lightbox from 'features/lightbox/components/Lightbox'; -import { useAppDispatch } from 'app/store/storeHooks'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { memo, ReactNode, useCallback, useEffect, useState } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import Loading from 'common/components/Loading/Loading'; @@ -22,6 +22,8 @@ import { configChanged } from 'features/system/store/configSlice'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { useLogger } from 'app/logging/useLogger'; import ParametersDrawer from 'features/ui/components/ParametersDrawer'; +import { languageSelector } from 'features/system/store/systemSelectors'; +import i18n from 'i18n'; const DEFAULT_CONFIG = {}; @@ -33,6 +35,9 @@ interface Props { const App = ({ config = DEFAULT_CONFIG, headerComponent }: Props) => { useToastWatcher(); useGlobalHotkeys(); + + const language = useAppSelector(languageSelector); + const log = useLogger(); const isLightboxEnabled = useFeatureStatus('lightbox').isFeatureEnabled; @@ -43,6 +48,10 @@ const App = ({ config = DEFAULT_CONFIG, headerComponent }: Props) => { const dispatch = useAppDispatch(); + useEffect(() => { + i18n.changeLanguage(language); + }, [language]); + useEffect(() => { log.info({ namespace: 'App', data: config }, 'Received config'); dispatch(configChanged(config)); diff --git a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx index c69d4f132b..d1608972f0 100644 --- a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx +++ b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx @@ -6,46 +6,50 @@ import IAIIconButton from 'common/components/IAIIconButton'; import IAIPopover from 'common/components/IAIPopover'; import { useTranslation } from 'react-i18next'; import { FaCheck, FaLanguage } from 'react-icons/fa'; +import i18n from 'i18n'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { languageSelector } from '../store/systemSelectors'; +import { languageChanged } from '../store/systemSlice'; + +export const LANGUAGES = { + ar: i18n.t('common.langArabic', { lng: 'ar' }), + nl: i18n.t('common.langDutch', { lng: 'nl' }), + en: i18n.t('common.langEnglish', { lng: 'en' }), + fr: i18n.t('common.langFrench', { lng: 'fr' }), + de: i18n.t('common.langGerman', { lng: 'de' }), + he: i18n.t('common.langHebrew', { lng: 'he' }), + it: i18n.t('common.langItalian', { lng: 'it' }), + ja: i18n.t('common.langJapanese', { lng: 'ja' }), + ko: i18n.t('common.langKorean', { lng: 'ko' }), + pl: i18n.t('common.langPolish', { lng: 'pl' }), + pt_BR: i18n.t('common.langBrPortuguese', { lng: 'pt_BR' }), + pt: i18n.t('common.langPortuguese', { lng: 'pt' }), + ru: i18n.t('common.langRussian', { lng: 'ru' }), + zh_CN: i18n.t('common.langSimplifiedChinese', { lng: 'zh_CN' }), + es: i18n.t('common.langSpanish', { lng: 'es' }), + uk: i18n.t('common.langUkranian', { lng: 'ua' }), +}; export default function LanguagePicker() { - const { t, i18n } = useTranslation(); - const LANGUAGES = { - ar: t('common.langArabic', { lng: 'ar' }), - nl: t('common.langDutch', { lng: 'nl' }), - en: t('common.langEnglish', { lng: 'en' }), - fr: t('common.langFrench', { lng: 'fr' }), - de: t('common.langGerman', { lng: 'de' }), - he: t('common.langHebrew', { lng: 'he' }), - it: t('common.langItalian', { lng: 'it' }), - ja: t('common.langJapanese', { lng: 'ja' }), - ko: t('common.langKorean', { lng: 'ko' }), - pl: t('common.langPolish', { lng: 'pl' }), - pt_BR: t('common.langBrPortuguese', { lng: 'pt_BR' }), - pt: t('common.langPortuguese', { lng: 'pt' }), - ru: t('common.langRussian', { lng: 'ru' }), - zh_CN: t('common.langSimplifiedChinese', { lng: 'zh_CN' }), - es: t('common.langSpanish', { lng: 'es' }), - uk: t('common.langUkranian', { lng: 'ua' }), - }; + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const language = useAppSelector(languageSelector); const renderLanguagePicker = () => { const languagesToRender: ReactNode[] = []; Object.keys(LANGUAGES).forEach((lang) => { + const l = lang as keyof typeof LANGUAGES; languagesToRender.push( - ) : undefined - } - onClick={() => i18n.changeLanguage(lang)} - aria-label={LANGUAGES[lang as keyof typeof LANGUAGES]} + isChecked={language === l} + leftIcon={language === l ? : undefined} + onClick={() => dispatch(languageChanged(l))} + aria-label={LANGUAGES[l]} size="sm" minWidth="200px" > - {LANGUAGES[lang as keyof typeof LANGUAGES]} + {LANGUAGES[l]} ); }); diff --git a/invokeai/frontend/web/src/features/system/store/systemSelectors.ts b/invokeai/frontend/web/src/features/system/store/systemSelectors.ts index 68265aa2dc..d9fd836ece 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSelectors.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSelectors.ts @@ -1,6 +1,7 @@ import { createSelector } from '@reduxjs/toolkit'; import { RootState } from 'app/store/store'; -import { isEqual, reduce, pickBy } from 'lodash-es'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import { reduce, pickBy } from 'lodash-es'; export const systemSelector = (state: RootState) => state.system; @@ -22,11 +23,7 @@ export const activeModelSelector = createSelector( ); return { ...model_list[activeModel], name: activeModel }; }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } + defaultSelectorOptions ); export const diffusersModelsSelector = createSelector( @@ -42,9 +39,11 @@ export const diffusersModelsSelector = createSelector( return diffusersModels; }, - { - memoizeOptions: { - resultEqualityCheck: isEqual, - }, - } + defaultSelectorOptions +); + +export const languageSelector = createSelector( + systemSelector, + (system) => system.language, + defaultSelectorOptions ); diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index e9cbd21a15..5cc6ca3a43 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -24,6 +24,7 @@ import { InvokeLogLevel } from 'app/logging/useLogger'; import { TFuncKey } from 'i18next'; import { t } from 'i18next'; import { userInvoked } from 'app/store/actions'; +import { LANGUAGES } from '../components/LanguagePicker'; export type CancelStrategy = 'immediate' | 'scheduled'; @@ -91,6 +92,7 @@ export interface SystemState { infillMethods: InfillMethod[]; isPersisted: boolean; shouldAntialiasProgressImage: boolean; + language: keyof typeof LANGUAGES; } export const initialSystemState: SystemState = { @@ -125,6 +127,7 @@ export const initialSystemState: SystemState = { canceledSession: '', infillMethods: ['tile', 'patchmatch'], isPersisted: false, + language: 'en', }; export const systemSlice = createSlice({ @@ -272,6 +275,9 @@ export const systemSlice = createSlice({ isPersistedChanged: (state, action: PayloadAction) => { state.isPersisted = action.payload; }, + languageChanged: (state, action: PayloadAction) => { + state.language = action.payload; + }, }, extraReducers(builder) { /** @@ -481,6 +487,7 @@ export const { shouldLogToConsoleChanged, isPersistedChanged, shouldAntialiasProgressImageChanged, + languageChanged, } = systemSlice.actions; export default systemSlice.reducer; diff --git a/invokeai/frontend/web/src/i18n.ts b/invokeai/frontend/web/src/i18n.ts index 71d4dfb35f..68b457eabe 100644 --- a/invokeai/frontend/web/src/i18n.ts +++ b/invokeai/frontend/web/src/i18n.ts @@ -21,11 +21,11 @@ if (import.meta.env.MODE === 'package') { } else { i18n .use(Backend) - .use( - new LanguageDetector(null, { - lookupLocalStorage: `${LOCALSTORAGE_PREFIX}lng`, - }) - ) + // .use( + // new LanguageDetector(null, { + // lookupLocalStorage: `${LOCALSTORAGE_PREFIX}lng`, + // }) + // ) .use(initReactI18next) .init({ fallbackLng: 'en', From 7d582553f21b6b11d86148a778296467c4206e29 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 19:50:34 +1000 Subject: [PATCH 39/42] feat(ui): use chakra menu for language picker --- .../system/components/LanguagePicker.tsx | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx index d1608972f0..e5544c9bb9 100644 --- a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx +++ b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx @@ -1,15 +1,19 @@ -import type { ReactNode } from 'react'; - -import { VStack } from '@chakra-ui/react'; -import IAIButton from 'common/components/IAIButton'; +import { + Menu, + MenuButton, + MenuItemOption, + MenuList, + MenuOptionGroup, + Tooltip, +} from '@chakra-ui/react'; import IAIIconButton from 'common/components/IAIIconButton'; -import IAIPopover from 'common/components/IAIPopover'; import { useTranslation } from 'react-i18next'; -import { FaCheck, FaLanguage } from 'react-icons/fa'; +import { FaLanguage } from 'react-icons/fa'; import i18n from 'i18n'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { languageSelector } from '../store/systemSelectors'; import { languageChanged } from '../store/systemSlice'; +import { map } from 'lodash-es'; export const LANGUAGES = { ar: i18n.t('common.langArabic', { lng: 'ar' }), @@ -35,43 +39,29 @@ export default function LanguagePicker() { const dispatch = useAppDispatch(); const language = useAppSelector(languageSelector); - const renderLanguagePicker = () => { - const languagesToRender: ReactNode[] = []; - Object.keys(LANGUAGES).forEach((lang) => { - const l = lang as keyof typeof LANGUAGES; - languagesToRender.push( - : undefined} - onClick={() => dispatch(languageChanged(l))} - aria-label={LANGUAGES[l]} - size="sm" - minWidth="200px" - > - {LANGUAGES[l]} - - ); - }); - - return languagesToRender; - }; - return ( - + + } - size="sm" variant="link" - data-variant="link" - fontSize={26} + aria-label={t('common.languagePickerLabel')} /> - } - > - {renderLanguagePicker()} - + + + {map(LANGUAGES, (languageName, l: keyof typeof LANGUAGES) => ( + dispatch(languageChanged(l))} + > + {languageName} + + ))} + + + + ); } From eebaa50710bcd79496f06ea8eafae7408c8f3b9c Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 19:52:21 +1000 Subject: [PATCH 40/42] fix(ui): fix language picker tooltip --- invokeai/frontend/web/public/locales/en.json | 2 +- .../system/components/LanguagePicker.tsx | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index dccb77c267..3592e141d0 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -25,7 +25,7 @@ "common": { "hotkeysLabel": "Hotkeys", "themeLabel": "Theme", - "languagePickerLabel": "Language Picker", + "languagePickerLabel": "Language", "reportBugLabel": "Report Bug", "githubLabel": "Github", "discordLabel": "Discord", diff --git a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx index e5544c9bb9..d34ee581ed 100644 --- a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx +++ b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx @@ -40,28 +40,28 @@ export default function LanguagePicker() { const language = useAppSelector(languageSelector); return ( - - + + } variant="link" aria-label={t('common.languagePickerLabel')} /> - - - {map(LANGUAGES, (languageName, l: keyof typeof LANGUAGES) => ( - dispatch(languageChanged(l))} - > - {languageName} - - ))} - - - - + + + + {map(LANGUAGES, (languageName, l: keyof typeof LANGUAGES) => ( + dispatch(languageChanged(l))} + > + {languageName} + + ))} + + + ); } From 78cf70eaad86e457de122d049cd604091fc15563 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 20:04:10 +1000 Subject: [PATCH 41/42] fix(ui): tweak lang picker style --- .../src/features/system/components/LanguagePicker.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx index d34ee581ed..3e4e423c3f 100644 --- a/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx +++ b/invokeai/frontend/web/src/features/system/components/LanguagePicker.tsx @@ -1,4 +1,5 @@ import { + IconButton, Menu, MenuButton, MenuItemOption, @@ -6,14 +7,13 @@ import { MenuOptionGroup, Tooltip, } from '@chakra-ui/react'; -import IAIIconButton from 'common/components/IAIIconButton'; import { useTranslation } from 'react-i18next'; -import { FaLanguage } from 'react-icons/fa'; import i18n from 'i18n'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { languageSelector } from '../store/systemSelectors'; import { languageChanged } from '../store/systemSlice'; import { map } from 'lodash-es'; +import { IoLanguage } from 'react-icons/io5'; export const LANGUAGES = { ar: i18n.t('common.langArabic', { lng: 'ar' }), @@ -43,10 +43,12 @@ export default function LanguagePicker() { } + as={IconButton} + icon={} variant="link" aria-label={t('common.languagePickerLabel')} + fontSize={22} + minWidth={8} /> From 60a565d7deebf2ba1e056e1ed020e8919b8619e5 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Fri, 12 May 2023 20:04:29 +1000 Subject: [PATCH 42/42] feat(ui): use chakra menu for theme changer --- .../system/components/ThemeChanger.tsx | 91 +++++++++---------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/invokeai/frontend/web/src/features/system/components/ThemeChanger.tsx b/invokeai/frontend/web/src/features/system/components/ThemeChanger.tsx index ff825e9bf0..d9426eecf2 100644 --- a/invokeai/frontend/web/src/features/system/components/ThemeChanger.tsx +++ b/invokeai/frontend/web/src/features/system/components/ThemeChanger.tsx @@ -1,13 +1,26 @@ -import { VStack } from '@chakra-ui/react'; +import { + IconButton, + Menu, + MenuButton, + MenuItemOption, + MenuList, + MenuOptionGroup, + Tooltip, +} from '@chakra-ui/react'; import { RootState } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIButton from 'common/components/IAIButton'; -import IAIIconButton from 'common/components/IAIIconButton'; -import IAIPopover from 'common/components/IAIPopover'; import { setCurrentTheme } from 'features/ui/store/uiSlice'; -import type { ReactNode } from 'react'; +import i18n from 'i18n'; +import { map } from 'lodash-es'; import { useTranslation } from 'react-i18next'; -import { FaCheck, FaPalette } from 'react-icons/fa'; +import { FaPalette } from 'react-icons/fa'; + +export const THEMES = { + dark: i18n.t('common.darkTheme'), + light: i18n.t('common.lightTheme'), + green: i18n.t('common.greenTheme'), + ocean: i18n.t('common.oceanTheme'), +}; export default function ThemeChanger() { const { t } = useTranslation(); @@ -17,51 +30,31 @@ export default function ThemeChanger() { (state: RootState) => state.ui.currentTheme ); - const THEMES = { - dark: t('common.darkTheme'), - light: t('common.lightTheme'), - green: t('common.greenTheme'), - ocean: t('common.oceanTheme'), - }; - - const handleChangeTheme = (theme: string) => { - dispatch(setCurrentTheme(theme)); - }; - - const renderThemeOptions = () => { - const themesToRender: ReactNode[] = []; - - Object.keys(THEMES).forEach((theme) => { - themesToRender.push( - : undefined} - size="sm" - onClick={() => handleChangeTheme(theme)} - key={theme} - > - {THEMES[theme as keyof typeof THEMES]} - - ); - }); - - return themesToRender; - }; - return ( - + + } + variant="link" + aria-label={t('common.themeLabel')} + fontSize={20} + minWidth={8} /> - } - > - {renderThemeOptions()} - + + + + {map(THEMES, (themeName, themeKey: keyof typeof THEMES) => ( + dispatch(setCurrentTheme(themeKey))} + > + {themeName} + + ))} + + + ); }