mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
merge
This commit is contained in:
commit
31f63028fd
@ -165,13 +165,13 @@ class ImageProcessorInvocation(BaseInvocation, WithMetadata, WithBoard):
|
|||||||
title="Canny Processor",
|
title="Canny Processor",
|
||||||
tags=["controlnet", "canny"],
|
tags=["controlnet", "canny"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.3.2",
|
version="1.3.3",
|
||||||
)
|
)
|
||||||
class CannyImageProcessorInvocation(ImageProcessorInvocation):
|
class CannyImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Canny edge detection for ControlNet"""
|
"""Canny edge detection for ControlNet"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
low_threshold: int = InputField(
|
low_threshold: int = InputField(
|
||||||
default=100, ge=0, le=255, description="The low threshold of the Canny pixel gradient (0-255)"
|
default=100, ge=0, le=255, description="The low threshold of the Canny pixel gradient (0-255)"
|
||||||
)
|
)
|
||||||
@ -199,13 +199,13 @@ class CannyImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="HED (softedge) Processor",
|
title="HED (softedge) Processor",
|
||||||
tags=["controlnet", "hed", "softedge"],
|
tags=["controlnet", "hed", "softedge"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class HedImageProcessorInvocation(ImageProcessorInvocation):
|
class HedImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies HED edge detection to image"""
|
"""Applies HED edge detection to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
# safe not supported in controlnet_aux v0.0.3
|
# safe not supported in controlnet_aux v0.0.3
|
||||||
# safe: bool = InputField(default=False, description=FieldDescriptions.safe_mode)
|
# safe: bool = InputField(default=False, description=FieldDescriptions.safe_mode)
|
||||||
scribble: bool = InputField(default=False, description=FieldDescriptions.scribble_mode)
|
scribble: bool = InputField(default=False, description=FieldDescriptions.scribble_mode)
|
||||||
@ -228,13 +228,13 @@ class HedImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Lineart Processor",
|
title="Lineart Processor",
|
||||||
tags=["controlnet", "lineart"],
|
tags=["controlnet", "lineart"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class LineartImageProcessorInvocation(ImageProcessorInvocation):
|
class LineartImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies line art processing to image"""
|
"""Applies line art processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
coarse: bool = InputField(default=False, description="Whether to use coarse mode")
|
coarse: bool = InputField(default=False, description="Whether to use coarse mode")
|
||||||
|
|
||||||
def run_processor(self, image: Image.Image) -> Image.Image:
|
def run_processor(self, image: Image.Image) -> Image.Image:
|
||||||
@ -250,13 +250,13 @@ class LineartImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Lineart Anime Processor",
|
title="Lineart Anime Processor",
|
||||||
tags=["controlnet", "lineart", "anime"],
|
tags=["controlnet", "lineart", "anime"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
|
class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies line art anime processing to image"""
|
"""Applies line art anime processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image: Image.Image) -> Image.Image:
|
def run_processor(self, image: Image.Image) -> Image.Image:
|
||||||
processor = LineartAnimeProcessor()
|
processor = LineartAnimeProcessor()
|
||||||
@ -273,15 +273,15 @@ class LineartAnimeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Midas Depth Processor",
|
title="Midas Depth Processor",
|
||||||
tags=["controlnet", "midas"],
|
tags=["controlnet", "midas"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.3",
|
version="1.2.4",
|
||||||
)
|
)
|
||||||
class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
|
class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies Midas depth processing to image"""
|
"""Applies Midas depth processing to image"""
|
||||||
|
|
||||||
a_mult: float = InputField(default=2.0, ge=0, description="Midas parameter `a_mult` (a = a_mult * PI)")
|
a_mult: float = InputField(default=2.0, ge=0, description="Midas parameter `a_mult` (a = a_mult * PI)")
|
||||||
bg_th: float = InputField(default=0.1, ge=0, description="Midas parameter `bg_th`")
|
bg_th: float = InputField(default=0.1, ge=0, description="Midas parameter `bg_th`")
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
# depth_and_normal not supported in controlnet_aux v0.0.3
|
# depth_and_normal not supported in controlnet_aux v0.0.3
|
||||||
# depth_and_normal: bool = InputField(default=False, description="whether to use depth and normal mode")
|
# depth_and_normal: bool = InputField(default=False, description="whether to use depth and normal mode")
|
||||||
|
|
||||||
@ -304,13 +304,13 @@ class MidasDepthImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Normal BAE Processor",
|
title="Normal BAE Processor",
|
||||||
tags=["controlnet"],
|
tags=["controlnet"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
|
class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies NormalBae processing to image"""
|
"""Applies NormalBae processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image):
|
def run_processor(self, image):
|
||||||
normalbae_processor = NormalBaeDetector.from_pretrained("lllyasviel/Annotators")
|
normalbae_processor = NormalBaeDetector.from_pretrained("lllyasviel/Annotators")
|
||||||
@ -321,13 +321,13 @@ class NormalbaeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"mlsd_image_processor", title="MLSD Processor", tags=["controlnet", "mlsd"], category="controlnet", version="1.2.2"
|
"mlsd_image_processor", title="MLSD Processor", tags=["controlnet", "mlsd"], category="controlnet", version="1.2.3"
|
||||||
)
|
)
|
||||||
class MlsdImageProcessorInvocation(ImageProcessorInvocation):
|
class MlsdImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies MLSD processing to image"""
|
"""Applies MLSD processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
thr_v: float = InputField(default=0.1, ge=0, description="MLSD parameter `thr_v`")
|
thr_v: float = InputField(default=0.1, ge=0, description="MLSD parameter `thr_v`")
|
||||||
thr_d: float = InputField(default=0.1, ge=0, description="MLSD parameter `thr_d`")
|
thr_d: float = InputField(default=0.1, ge=0, description="MLSD parameter `thr_d`")
|
||||||
|
|
||||||
@ -344,13 +344,13 @@ class MlsdImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
|
|
||||||
|
|
||||||
@invocation(
|
@invocation(
|
||||||
"pidi_image_processor", title="PIDI Processor", tags=["controlnet", "pidi"], category="controlnet", version="1.2.2"
|
"pidi_image_processor", title="PIDI Processor", tags=["controlnet", "pidi"], category="controlnet", version="1.2.3"
|
||||||
)
|
)
|
||||||
class PidiImageProcessorInvocation(ImageProcessorInvocation):
|
class PidiImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies PIDI processing to image"""
|
"""Applies PIDI processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
safe: bool = InputField(default=False, description=FieldDescriptions.safe_mode)
|
safe: bool = InputField(default=False, description=FieldDescriptions.safe_mode)
|
||||||
scribble: bool = InputField(default=False, description=FieldDescriptions.scribble_mode)
|
scribble: bool = InputField(default=False, description=FieldDescriptions.scribble_mode)
|
||||||
|
|
||||||
@ -371,13 +371,13 @@ class PidiImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Content Shuffle Processor",
|
title="Content Shuffle Processor",
|
||||||
tags=["controlnet", "contentshuffle"],
|
tags=["controlnet", "contentshuffle"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
|
class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies content shuffle processing to image"""
|
"""Applies content shuffle processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
h: int = InputField(default=512, ge=0, description="Content shuffle `h` parameter")
|
h: int = InputField(default=512, ge=0, description="Content shuffle `h` parameter")
|
||||||
w: int = InputField(default=512, ge=0, description="Content shuffle `w` parameter")
|
w: int = InputField(default=512, ge=0, description="Content shuffle `w` parameter")
|
||||||
f: int = InputField(default=256, ge=0, description="Content shuffle `f` parameter")
|
f: int = InputField(default=256, ge=0, description="Content shuffle `f` parameter")
|
||||||
@ -401,7 +401,7 @@ class ContentShuffleImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Zoe (Depth) Processor",
|
title="Zoe (Depth) Processor",
|
||||||
tags=["controlnet", "zoe", "depth"],
|
tags=["controlnet", "zoe", "depth"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
|
class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies Zoe depth processing to image"""
|
"""Applies Zoe depth processing to image"""
|
||||||
@ -417,15 +417,15 @@ class ZoeDepthImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Mediapipe Face Processor",
|
title="Mediapipe Face Processor",
|
||||||
tags=["controlnet", "mediapipe", "face"],
|
tags=["controlnet", "mediapipe", "face"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.3",
|
version="1.2.4",
|
||||||
)
|
)
|
||||||
class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
|
class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies mediapipe face processing to image"""
|
"""Applies mediapipe face processing to image"""
|
||||||
|
|
||||||
max_faces: int = InputField(default=1, ge=1, description="Maximum number of faces to detect")
|
max_faces: int = InputField(default=1, ge=1, description="Maximum number of faces to detect")
|
||||||
min_confidence: float = InputField(default=0.5, ge=0, le=1, description="Minimum confidence for face detection")
|
min_confidence: float = InputField(default=0.5, ge=0, le=1, description="Minimum confidence for face detection")
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image):
|
def run_processor(self, image):
|
||||||
mediapipe_face_processor = MediapipeFaceDetector()
|
mediapipe_face_processor = MediapipeFaceDetector()
|
||||||
@ -444,7 +444,7 @@ class MediapipeFaceProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Leres (Depth) Processor",
|
title="Leres (Depth) Processor",
|
||||||
tags=["controlnet", "leres", "depth"],
|
tags=["controlnet", "leres", "depth"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies leres processing to image"""
|
"""Applies leres processing to image"""
|
||||||
@ -452,8 +452,8 @@ class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
thr_a: float = InputField(default=0, description="Leres parameter `thr_a`")
|
thr_a: float = InputField(default=0, description="Leres parameter `thr_a`")
|
||||||
thr_b: float = InputField(default=0, description="Leres parameter `thr_b`")
|
thr_b: float = InputField(default=0, description="Leres parameter `thr_b`")
|
||||||
boost: bool = InputField(default=False, description="Whether to use boost mode")
|
boost: bool = InputField(default=False, description="Whether to use boost mode")
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image):
|
def run_processor(self, image):
|
||||||
leres_processor = LeresDetector.from_pretrained("lllyasviel/Annotators")
|
leres_processor = LeresDetector.from_pretrained("lllyasviel/Annotators")
|
||||||
@ -473,7 +473,7 @@ class LeresImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Tile Resample Processor",
|
title="Tile Resample Processor",
|
||||||
tags=["controlnet", "tile"],
|
tags=["controlnet", "tile"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class TileResamplerProcessorInvocation(ImageProcessorInvocation):
|
class TileResamplerProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Tile resampler processor"""
|
"""Tile resampler processor"""
|
||||||
@ -513,13 +513,13 @@ class TileResamplerProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Segment Anything Processor",
|
title="Segment Anything Processor",
|
||||||
tags=["controlnet", "segmentanything"],
|
tags=["controlnet", "segmentanything"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.3",
|
version="1.2.4",
|
||||||
)
|
)
|
||||||
class SegmentAnythingProcessorInvocation(ImageProcessorInvocation):
|
class SegmentAnythingProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Applies segment anything processing to image"""
|
"""Applies segment anything processing to image"""
|
||||||
|
|
||||||
detect_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.detect_res)
|
detect_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.detect_res)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image):
|
def run_processor(self, image):
|
||||||
# segment_anything_processor = SamDetector.from_pretrained("ybelkada/segment-anything", subfolder="checkpoints")
|
# segment_anything_processor = SamDetector.from_pretrained("ybelkada/segment-anything", subfolder="checkpoints")
|
||||||
@ -560,12 +560,12 @@ class SamDetectorReproducibleColors(SamDetector):
|
|||||||
title="Color Map Processor",
|
title="Color Map Processor",
|
||||||
tags=["controlnet"],
|
tags=["controlnet"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.2.2",
|
version="1.2.3",
|
||||||
)
|
)
|
||||||
class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
|
class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Generates a color map from the provided image"""
|
"""Generates a color map from the provided image"""
|
||||||
|
|
||||||
color_map_tile_size: int = InputField(default=64, ge=0, description=FieldDescriptions.tile_size)
|
color_map_tile_size: int = InputField(default=64, ge=1, description=FieldDescriptions.tile_size)
|
||||||
|
|
||||||
def run_processor(self, image: Image.Image):
|
def run_processor(self, image: Image.Image):
|
||||||
np_image = np.array(image, dtype=np.uint8)
|
np_image = np.array(image, dtype=np.uint8)
|
||||||
@ -592,7 +592,7 @@ DEPTH_ANYTHING_MODEL_SIZES = Literal["large", "base", "small"]
|
|||||||
title="Depth Anything Processor",
|
title="Depth Anything Processor",
|
||||||
tags=["controlnet", "depth", "depth anything"],
|
tags=["controlnet", "depth", "depth anything"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.1",
|
version="1.1.2",
|
||||||
)
|
)
|
||||||
class DepthAnythingImageProcessorInvocation(ImageProcessorInvocation):
|
class DepthAnythingImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Generates a depth map based on the Depth Anything algorithm"""
|
"""Generates a depth map based on the Depth Anything algorithm"""
|
||||||
@ -600,7 +600,7 @@ class DepthAnythingImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
model_size: DEPTH_ANYTHING_MODEL_SIZES = InputField(
|
model_size: DEPTH_ANYTHING_MODEL_SIZES = InputField(
|
||||||
default="small", description="The size of the depth model to use"
|
default="small", description="The size of the depth model to use"
|
||||||
)
|
)
|
||||||
resolution: int = InputField(default=512, ge=64, multiple_of=64, description=FieldDescriptions.image_res)
|
resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image: Image.Image):
|
def run_processor(self, image: Image.Image):
|
||||||
depth_anything_detector = DepthAnythingDetector()
|
depth_anything_detector = DepthAnythingDetector()
|
||||||
@ -615,7 +615,7 @@ class DepthAnythingImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="DW Openpose Image Processor",
|
title="DW Openpose Image Processor",
|
||||||
tags=["controlnet", "dwpose", "openpose"],
|
tags=["controlnet", "dwpose", "openpose"],
|
||||||
category="controlnet",
|
category="controlnet",
|
||||||
version="1.1.0",
|
version="1.1.1",
|
||||||
)
|
)
|
||||||
class DWOpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
class DWOpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
"""Generates an openpose pose from an image using DWPose"""
|
"""Generates an openpose pose from an image using DWPose"""
|
||||||
@ -623,7 +623,7 @@ class DWOpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
draw_body: bool = InputField(default=True)
|
draw_body: bool = InputField(default=True)
|
||||||
draw_face: bool = InputField(default=False)
|
draw_face: bool = InputField(default=False)
|
||||||
draw_hands: bool = InputField(default=False)
|
draw_hands: bool = InputField(default=False)
|
||||||
image_resolution: int = InputField(default=512, ge=0, description=FieldDescriptions.image_res)
|
image_resolution: int = InputField(default=512, ge=1, description=FieldDescriptions.image_res)
|
||||||
|
|
||||||
def run_processor(self, image: Image.Image):
|
def run_processor(self, image: Image.Image):
|
||||||
dw_openpose = DWOpenposeDetector()
|
dw_openpose = DWOpenposeDetector()
|
||||||
@ -642,15 +642,15 @@ class DWOpenposeImageProcessorInvocation(ImageProcessorInvocation):
|
|||||||
title="Heuristic Resize",
|
title="Heuristic Resize",
|
||||||
tags=["image, controlnet"],
|
tags=["image, controlnet"],
|
||||||
category="image",
|
category="image",
|
||||||
version="1.0.0",
|
version="1.0.1",
|
||||||
classification=Classification.Prototype,
|
classification=Classification.Prototype,
|
||||||
)
|
)
|
||||||
class HeuristicResizeInvocation(BaseInvocation):
|
class HeuristicResizeInvocation(BaseInvocation):
|
||||||
"""Resize an image using a heuristic method. Preserves edge maps."""
|
"""Resize an image using a heuristic method. Preserves edge maps."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to resize")
|
image: ImageField = InputField(description="The image to resize")
|
||||||
width: int = InputField(default=512, gt=0, description="The width to resize to (px)")
|
width: int = InputField(default=512, ge=1, description="The width to resize to (px)")
|
||||||
height: int = InputField(default=512, gt=0, description="The height to resize to (px)")
|
height: int = InputField(default=512, ge=1, description="The height to resize to (px)")
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.images.get_pil(self.image.image_name, "RGB")
|
image = context.images.get_pil(self.image.image_name, "RGB")
|
||||||
|
@ -3,7 +3,7 @@ import inspect
|
|||||||
import math
|
import math
|
||||||
from contextlib import ExitStack
|
from contextlib import ExitStack
|
||||||
from functools import singledispatchmethod
|
from functools import singledispatchmethod
|
||||||
from typing import Any, Iterator, List, Literal, Optional, Tuple, Union
|
from typing import Any, Dict, Iterator, List, Literal, Optional, Tuple, Union
|
||||||
|
|
||||||
import einops
|
import einops
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -11,7 +11,6 @@ import numpy.typing as npt
|
|||||||
import torch
|
import torch
|
||||||
import torchvision
|
import torchvision
|
||||||
import torchvision.transforms as T
|
import torchvision.transforms as T
|
||||||
from diffusers import AutoencoderKL, AutoencoderTiny
|
|
||||||
from diffusers.configuration_utils import ConfigMixin
|
from diffusers.configuration_utils import ConfigMixin
|
||||||
from diffusers.image_processor import VaeImageProcessor
|
from diffusers.image_processor import VaeImageProcessor
|
||||||
from diffusers.models.adapter import T2IAdapter
|
from diffusers.models.adapter import T2IAdapter
|
||||||
@ -21,9 +20,12 @@ from diffusers.models.attention_processor import (
|
|||||||
LoRAXFormersAttnProcessor,
|
LoRAXFormersAttnProcessor,
|
||||||
XFormersAttnProcessor,
|
XFormersAttnProcessor,
|
||||||
)
|
)
|
||||||
|
from diffusers.models.autoencoders.autoencoder_kl import AutoencoderKL
|
||||||
|
from diffusers.models.autoencoders.autoencoder_tiny import AutoencoderTiny
|
||||||
from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
|
from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
|
||||||
from diffusers.schedulers import DPMSolverSDEScheduler
|
from diffusers.schedulers.scheduling_dpmsolver_sde import DPMSolverSDEScheduler
|
||||||
from diffusers.schedulers import SchedulerMixin as Scheduler
|
from diffusers.schedulers.scheduling_tcd import TCDScheduler
|
||||||
|
from diffusers.schedulers.scheduling_utils import SchedulerMixin as Scheduler
|
||||||
from PIL import Image, ImageFilter
|
from PIL import Image, ImageFilter
|
||||||
from pydantic import field_validator
|
from pydantic import field_validator
|
||||||
from torchvision.transforms.functional import resize as tv_resize
|
from torchvision.transforms.functional import resize as tv_resize
|
||||||
@ -521,9 +523,10 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if is_sdxl:
|
if is_sdxl:
|
||||||
return SDXLConditioningInfo(
|
return (
|
||||||
embeds=text_embedding, pooled_embeds=pooled_embedding, add_time_ids=add_time_ids
|
SDXLConditioningInfo(embeds=text_embedding, pooled_embeds=pooled_embedding, add_time_ids=add_time_ids),
|
||||||
), regions
|
regions,
|
||||||
|
)
|
||||||
return BasicConditioningInfo(embeds=text_embedding), regions
|
return BasicConditioningInfo(embeds=text_embedding), regions
|
||||||
|
|
||||||
def get_conditioning_data(
|
def get_conditioning_data(
|
||||||
@ -825,7 +828,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
denoising_start: float,
|
denoising_start: float,
|
||||||
denoising_end: float,
|
denoising_end: float,
|
||||||
seed: int,
|
seed: int,
|
||||||
) -> Tuple[int, List[int], int]:
|
) -> Tuple[int, List[int], int, Dict[str, Any]]:
|
||||||
assert isinstance(scheduler, ConfigMixin)
|
assert isinstance(scheduler, ConfigMixin)
|
||||||
if scheduler.config.get("cpu_only", False):
|
if scheduler.config.get("cpu_only", False):
|
||||||
scheduler.set_timesteps(steps, device="cpu")
|
scheduler.set_timesteps(steps, device="cpu")
|
||||||
@ -853,13 +856,15 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
timesteps = timesteps[t_start_idx : t_start_idx + t_end_idx]
|
timesteps = timesteps[t_start_idx : t_start_idx + t_end_idx]
|
||||||
num_inference_steps = len(timesteps) // scheduler.order
|
num_inference_steps = len(timesteps) // scheduler.order
|
||||||
|
|
||||||
scheduler_step_kwargs = {}
|
scheduler_step_kwargs: Dict[str, Any] = {}
|
||||||
scheduler_step_signature = inspect.signature(scheduler.step)
|
scheduler_step_signature = inspect.signature(scheduler.step)
|
||||||
if "generator" in scheduler_step_signature.parameters:
|
if "generator" in scheduler_step_signature.parameters:
|
||||||
# At some point, someone decided that schedulers that accept a generator should use the original seed with
|
# At some point, someone decided that schedulers that accept a generator should use the original seed with
|
||||||
# all bits flipped. I don't know the original rationale for this, but now we must keep it like this for
|
# all bits flipped. I don't know the original rationale for this, but now we must keep it like this for
|
||||||
# reproducibility.
|
# reproducibility.
|
||||||
scheduler_step_kwargs = {"generator": torch.Generator(device=device).manual_seed(seed ^ 0xFFFFFFFF)}
|
scheduler_step_kwargs.update({"generator": torch.Generator(device=device).manual_seed(seed ^ 0xFFFFFFFF)})
|
||||||
|
if isinstance(scheduler, TCDScheduler):
|
||||||
|
scheduler_step_kwargs.update({"eta": 1.0})
|
||||||
|
|
||||||
return num_inference_steps, timesteps, init_timestep, scheduler_step_kwargs
|
return num_inference_steps, timesteps, init_timestep, scheduler_step_kwargs
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ from diffusers import (
|
|||||||
LCMScheduler,
|
LCMScheduler,
|
||||||
LMSDiscreteScheduler,
|
LMSDiscreteScheduler,
|
||||||
PNDMScheduler,
|
PNDMScheduler,
|
||||||
|
TCDScheduler,
|
||||||
UniPCMultistepScheduler,
|
UniPCMultistepScheduler,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,4 +41,5 @@ SCHEDULER_MAP = {
|
|||||||
"dpmpp_sde_k": (DPMSolverSDEScheduler, {"use_karras_sigmas": True, "noise_sampler_seed": 0}),
|
"dpmpp_sde_k": (DPMSolverSDEScheduler, {"use_karras_sigmas": True, "noise_sampler_seed": 0}),
|
||||||
"unipc": (UniPCMultistepScheduler, {"cpu_only": True}),
|
"unipc": (UniPCMultistepScheduler, {"cpu_only": True}),
|
||||||
"lcm": (LCMScheduler, {}),
|
"lcm": (LCMScheduler, {}),
|
||||||
|
"tcd": (TCDScheduler, {}),
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"typegen": "node scripts/typegen.js",
|
"typegen": "node scripts/typegen.js",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint:knip": "knip",
|
"lint:knip": "knip",
|
||||||
"lint:dpdm": "dpdm --no-warning --no-tree --transform --exit-code circular:0 src/main.tsx",
|
"lint:dpdm": "dpdm --no-warning --no-tree --transform --exit-code circular:1 src/main.tsx",
|
||||||
"lint:eslint": "eslint --max-warnings=0 .",
|
"lint:eslint": "eslint --max-warnings=0 .",
|
||||||
"lint:prettier": "prettier --check .",
|
"lint:prettier": "prettier --check .",
|
||||||
"lint:tsc": "tsc --noEmit",
|
"lint:tsc": "tsc --noEmit",
|
||||||
@ -58,7 +58,7 @@
|
|||||||
"@dnd-kit/sortable": "^8.0.0",
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@fontsource-variable/inter": "^5.0.17",
|
"@fontsource-variable/inter": "^5.0.17",
|
||||||
"@invoke-ai/ui-library": "^0.0.21",
|
"@invoke-ai/ui-library": "^0.0.25",
|
||||||
"@nanostores/react": "^0.7.2",
|
"@nanostores/react": "^0.7.2",
|
||||||
"@reduxjs/toolkit": "2.2.2",
|
"@reduxjs/toolkit": "2.2.2",
|
||||||
"@roarr/browser-log-writer": "^1.3.0",
|
"@roarr/browser-log-writer": "^1.3.0",
|
||||||
|
@ -7,7 +7,7 @@ settings:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@chakra-ui/react':
|
'@chakra-ui/react':
|
||||||
specifier: ^2.8.2
|
specifier: ^2.8.2
|
||||||
version: 2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.59)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0)
|
version: 2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@chakra-ui/react-use-size':
|
'@chakra-ui/react-use-size':
|
||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
version: 2.1.0(react@18.2.0)
|
version: 2.1.0(react@18.2.0)
|
||||||
@ -30,8 +30,8 @@ dependencies:
|
|||||||
specifier: ^5.0.17
|
specifier: ^5.0.17
|
||||||
version: 5.0.17
|
version: 5.0.17
|
||||||
'@invoke-ai/ui-library':
|
'@invoke-ai/ui-library':
|
||||||
specifier: ^0.0.21
|
specifier: ^0.0.25
|
||||||
version: 0.0.21(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.17)(@internationalized/date@3.5.2)(@types/react@18.2.73)(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0)
|
version: 0.0.25(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.17)(@internationalized/date@3.5.3)(@types/react@18.2.73)(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@nanostores/react':
|
'@nanostores/react':
|
||||||
specifier: ^0.7.2
|
specifier: ^0.7.2
|
||||||
version: 0.7.2(nanostores@0.10.0)(react@18.2.0)
|
version: 0.7.2(nanostores@0.10.0)(react@18.2.0)
|
||||||
@ -306,7 +306,7 @@ packages:
|
|||||||
'@jridgewell/trace-mapping': 0.3.25
|
'@jridgewell/trace-mapping': 0.3.25
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@ark-ui/anatomy@1.3.0(@internationalized/date@3.5.2):
|
/@ark-ui/anatomy@1.3.0(@internationalized/date@3.5.3):
|
||||||
resolution: {integrity: sha512-1yG2MrzUlix6KthjQMCNiHnkXrWwEdFAX6D+HqGJaNu0XvaGul2J+wDNtjsdX+gxiWu1nXXEEOAWlFVYMUf65w==}
|
resolution: {integrity: sha512-1yG2MrzUlix6KthjQMCNiHnkXrWwEdFAX6D+HqGJaNu0XvaGul2J+wDNtjsdX+gxiWu1nXXEEOAWlFVYMUf65w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@zag-js/accordion': 0.32.1
|
'@zag-js/accordion': 0.32.1
|
||||||
@ -318,7 +318,7 @@ packages:
|
|||||||
'@zag-js/color-utils': 0.32.1
|
'@zag-js/color-utils': 0.32.1
|
||||||
'@zag-js/combobox': 0.32.1
|
'@zag-js/combobox': 0.32.1
|
||||||
'@zag-js/date-picker': 0.32.1
|
'@zag-js/date-picker': 0.32.1
|
||||||
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.2)
|
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.3)
|
||||||
'@zag-js/dialog': 0.32.1
|
'@zag-js/dialog': 0.32.1
|
||||||
'@zag-js/editable': 0.32.1
|
'@zag-js/editable': 0.32.1
|
||||||
'@zag-js/file-upload': 0.32.1
|
'@zag-js/file-upload': 0.32.1
|
||||||
@ -345,13 +345,13 @@ packages:
|
|||||||
- '@internationalized/date'
|
- '@internationalized/date'
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@ark-ui/react@1.3.0(@internationalized/date@3.5.2)(react-dom@18.2.0)(react@18.2.0):
|
/@ark-ui/react@1.3.0(@internationalized/date@3.5.3)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-JHjNoIX50+mUCTaEGMjfGQWGGi31pKsV646jZJlR/1xohpYJigzg8BvO97cTsVk8fwtur+cm11gz3Nf7f5QUnA==}
|
resolution: {integrity: sha512-JHjNoIX50+mUCTaEGMjfGQWGGi31pKsV646jZJlR/1xohpYJigzg8BvO97cTsVk8fwtur+cm11gz3Nf7f5QUnA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=18.0.0'
|
react: '>=18.0.0'
|
||||||
react-dom: '>=18.0.0'
|
react-dom: '>=18.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ark-ui/anatomy': 1.3.0(@internationalized/date@3.5.2)
|
'@ark-ui/anatomy': 1.3.0(@internationalized/date@3.5.3)
|
||||||
'@zag-js/accordion': 0.32.1
|
'@zag-js/accordion': 0.32.1
|
||||||
'@zag-js/avatar': 0.32.1
|
'@zag-js/avatar': 0.32.1
|
||||||
'@zag-js/carousel': 0.32.1
|
'@zag-js/carousel': 0.32.1
|
||||||
@ -361,7 +361,7 @@ packages:
|
|||||||
'@zag-js/combobox': 0.32.1
|
'@zag-js/combobox': 0.32.1
|
||||||
'@zag-js/core': 0.32.1
|
'@zag-js/core': 0.32.1
|
||||||
'@zag-js/date-picker': 0.32.1
|
'@zag-js/date-picker': 0.32.1
|
||||||
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.2)
|
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.3)
|
||||||
'@zag-js/dialog': 0.32.1
|
'@zag-js/dialog': 0.32.1
|
||||||
'@zag-js/editable': 0.32.1
|
'@zag-js/editable': 0.32.1
|
||||||
'@zag-js/file-upload': 0.32.1
|
'@zag-js/file-upload': 0.32.1
|
||||||
@ -1681,7 +1681,7 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0):
|
/@chakra-ui/accordion@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==}
|
resolution: {integrity: sha512-FSXRm8iClFyU+gVaXisOSEw0/4Q+qZbFRiuhIAkVU6Boj0FxAMrlo9a8AV5TuF77rgaHytCdHk0Ng+cyUijrag==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@chakra-ui/system': '>=2.0.0'
|
'@chakra-ui/system': '>=2.0.0'
|
||||||
@ -1694,9 +1694,9 @@ packages:
|
|||||||
'@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.6)(react@18.2.0)
|
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0)
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -1848,16 +1848,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/css-reset@2.3.0(@emotion/react@11.11.3)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==}
|
|
||||||
peerDependencies:
|
|
||||||
'@emotion/react': '>=10.0.35'
|
|
||||||
react: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@emotion/react': 11.11.3(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/css-reset@2.3.0(@emotion/react@11.11.4)(react@18.2.0):
|
/@chakra-ui/css-reset@2.3.0(@emotion/react@11.11.4)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==}
|
resolution: {integrity: sha512-cQwwBy5O0jzvl0K7PLTLgp8ijqLPKyuEMiDXwYzl95seD3AoeuoCLyzZcJtVqaUZ573PiBdAbY/IlZcwDOItWg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1905,18 +1895,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==}
|
resolution: {integrity: sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/focus-lock@2.1.0(@types/react@18.2.59)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==}
|
|
||||||
peerDependencies:
|
|
||||||
react: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/dom-utils': 2.1.0
|
|
||||||
react: 18.2.0
|
|
||||||
react-focus-lock: 2.11.1(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@types/react'
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/focus-lock@2.1.0(@types/react@18.2.73)(react@18.2.0):
|
/@chakra-ui/focus-lock@2.1.0(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==}
|
resolution: {integrity: sha512-EmGx4PhWGjm4dpjRqM4Aa+rCWBxP+Rq8Uc/nAVnD4YVqkEhBkrPTpui2lnjsuxqNaZ24fIAZ10cF1hlpemte/w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1924,7 +1902,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@chakra-ui/dom-utils': 2.1.0
|
'@chakra-ui/dom-utils': 2.1.0
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-focus-lock: 2.11.2(@types/react@18.2.73)(react@18.2.0)
|
react-focus-lock: 2.11.1(@types/react@18.2.73)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
dev: false
|
dev: false
|
||||||
@ -2100,59 +2078,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/menu@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-lJS7XEObzJxsOwWQh7yfG4H8FzFPRP5hVPN/CL+JzytEINCSBvsCDHrYPQGp7jzpCi8vnTqQQGQe0f8dwnXd2g==}
|
|
||||||
peerDependencies:
|
|
||||||
'@chakra-ui/system': '>=2.0.0'
|
|
||||||
framer-motion: '>=4.0.0'
|
|
||||||
react: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/clickable': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/descendant': 3.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/lazy-utils': 2.0.5
|
|
||||||
'@chakra-ui/popper': 3.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-children-utils': 2.0.6(react@18.2.0)
|
|
||||||
'@chakra-ui/react-context': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-animation-state': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-controllable-state': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-disclosure': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-focus-effect': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-outside-click': 2.2.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.59)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==}
|
|
||||||
peerDependencies:
|
|
||||||
'@chakra-ui/system': '>=2.0.0'
|
|
||||||
framer-motion: '>=4.0.0'
|
|
||||||
react: '>=18'
|
|
||||||
react-dom: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/react-context': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/react-types': 2.0.7(react@18.2.0)
|
|
||||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
aria-hidden: 1.2.3
|
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
react-remove-scroll: 2.5.7(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@types/react'
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0):
|
/@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==}
|
resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2170,11 +2095,37 @@ packages:
|
|||||||
'@chakra-ui/shared-utils': 2.0.5
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
'@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.2.0)
|
'@chakra-ui/transition': 2.1.0(framer-motion@10.18.0)(react@18.2.0)
|
||||||
aria-hidden: 1.2.4
|
aria-hidden: 1.2.3
|
||||||
framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
|
framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
react-remove-scroll: 2.5.9(@types/react@18.2.73)(react@18.2.0)
|
react-remove-scroll: 2.5.7(@types/react@18.2.73)(react@18.2.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/react'
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@chakra-ui/modal@2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-TQv1ZaiJMZN+rR9DK0snx/OPwmtaGH1HbZtlYt4W4s6CzyK541fxLRTjIXfEzIGpvNW+b6VFuFjbcR78p4DEoQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@chakra-ui/system': '>=2.0.0'
|
||||||
|
framer-motion: '>=4.0.0'
|
||||||
|
react: '>=18'
|
||||||
|
react-dom: '>=18'
|
||||||
|
dependencies:
|
||||||
|
'@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.73)(react@18.2.0)
|
||||||
|
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/react-context': 2.1.0(react@18.2.0)
|
||||||
|
'@chakra-ui/react-types': 2.0.7(react@18.2.0)
|
||||||
|
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
||||||
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0)
|
||||||
|
aria-hidden: 1.2.3
|
||||||
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
react-remove-scroll: 2.5.7(@types/react@18.2.73)(react@18.2.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
dev: false
|
dev: false
|
||||||
@ -2248,7 +2199,7 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0):
|
/@chakra-ui/popover@2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==}
|
resolution: {integrity: sha512-K+2ai2dD0ljvJnlrzesCDT9mNzLifE3noGKZ3QwLqd/K34Ym1W/0aL1ERSynrcG78NKoXS54SdEzkhCZ4Gn/Zg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@chakra-ui/system': '>=2.0.0'
|
'@chakra-ui/system': '>=2.0.0'
|
||||||
@ -2266,8 +2217,8 @@ packages:
|
|||||||
'@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-focus-on-pointer-down': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -2305,25 +2256,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/provider@2.4.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==}
|
|
||||||
peerDependencies:
|
|
||||||
'@emotion/react': ^11.0.0
|
|
||||||
'@emotion/styled': ^11.0.0
|
|
||||||
react: '>=18'
|
|
||||||
react-dom: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.3)(react@18.2.0)
|
|
||||||
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/react-env': 3.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/utils': 2.0.15
|
|
||||||
'@emotion/react': 11.11.3(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
'@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/provider@2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0):
|
/@chakra-ui/provider@2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==}
|
resolution: {integrity: sha512-w0Tef5ZCJK1mlJorcSjItCSbyvVuqpvyWdxZiVQmE6fvSJR83wZof42ux0+sfWD+I7rHSfj+f9nzhNaEWClysw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2554,77 +2486,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/react@2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.59)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==}
|
|
||||||
peerDependencies:
|
|
||||||
'@emotion/react': ^11.0.0
|
|
||||||
'@emotion/styled': ^11.0.0
|
|
||||||
framer-motion: '>=4.0.0'
|
|
||||||
react: '>=18'
|
|
||||||
react-dom: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
'@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/counter': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.3)(react@18.2.0)
|
|
||||||
'@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
'@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/hooks': 2.2.1(react@18.2.0)
|
|
||||||
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/live-region': 2.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
'@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.59)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
'@chakra-ui/popper': 3.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/provider': 2.4.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/react-env': 3.1.0(react@18.2.0)
|
|
||||||
'@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/styled-system': 2.9.2
|
|
||||||
'@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2)
|
|
||||||
'@chakra-ui/theme-utils': 2.0.21
|
|
||||||
'@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.6)(react@18.2.0)
|
|
||||||
'@chakra-ui/utils': 2.0.15
|
|
||||||
'@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
|
||||||
'@emotion/react': 11.11.3(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
'@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@types/react'
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0):
|
/@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==}
|
resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2696,6 +2557,77 @@ packages:
|
|||||||
- '@types/react'
|
- '@types/react'
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@chakra-ui/react@2.8.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-Hn0moyxxyCDKuR9ywYpqgX8dvjqwu9ArwpIb9wHNYjnODETjLwazgNIliCVBRcJvysGRiV51U2/JtJVrpeCjUQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@emotion/react': ^11.0.0
|
||||||
|
'@emotion/styled': ^11.0.0
|
||||||
|
framer-motion: '>=4.0.0'
|
||||||
|
react: '>=18'
|
||||||
|
react-dom: '>=18'
|
||||||
|
dependencies:
|
||||||
|
'@chakra-ui/accordion': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0)
|
||||||
|
'@chakra-ui/alert': 2.2.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/avatar': 2.3.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/breadcrumb': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/button': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/card': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/close-button': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/control-box': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/counter': 2.1.0(react@18.2.0)
|
||||||
|
'@chakra-ui/css-reset': 2.3.0(@emotion/react@11.11.4)(react@18.2.0)
|
||||||
|
'@chakra-ui/editable': 3.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/focus-lock': 2.1.0(@types/react@18.2.73)(react@18.2.0)
|
||||||
|
'@chakra-ui/form-control': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/hooks': 2.2.1(react@18.2.0)
|
||||||
|
'@chakra-ui/icon': 3.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/image': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/live-region': 2.1.0(react@18.2.0)
|
||||||
|
'@chakra-ui/media-query': 3.3.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/menu': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0)
|
||||||
|
'@chakra-ui/modal': 2.3.1(@chakra-ui/system@2.6.2)(@types/react@18.2.73)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/number-input': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/pin-input': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/popover': 2.2.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0)
|
||||||
|
'@chakra-ui/popper': 3.1.0(react@18.2.0)
|
||||||
|
'@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/progress': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/provider': 2.4.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/radio': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/react-env': 3.1.0(react@18.2.0)
|
||||||
|
'@chakra-ui/select': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/skeleton': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/skip-nav': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/slider': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/spinner': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/stat': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/stepper': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/styled-system': 2.9.2
|
||||||
|
'@chakra-ui/switch': 2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0)
|
||||||
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/table': 2.1.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/tabs': 3.0.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/tag': 3.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/textarea': 2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2)
|
||||||
|
'@chakra-ui/theme-utils': 2.0.21
|
||||||
|
'@chakra-ui/toast': 7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/tooltip': 2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
'@chakra-ui/transition': 2.1.0(framer-motion@11.0.22)(react@18.2.0)
|
||||||
|
'@chakra-ui/utils': 2.0.15
|
||||||
|
'@chakra-ui/visually-hidden': 2.2.0(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
|
'@emotion/react': 11.11.4(@types/react@18.2.73)(react@18.2.0)
|
||||||
|
'@emotion/styled': 11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0)
|
||||||
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/react'
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/select@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0):
|
/@chakra-ui/select@2.1.2(@chakra-ui/system@2.6.2)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==}
|
resolution: {integrity: sha512-ZwCb7LqKCVLJhru3DXvKXpZ7Pbu1TDZ7N0PdQ0Zj1oyVLJyrpef1u9HR5u0amOpqcH++Ugt0f5JSmirjNlctjA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2814,7 +2746,7 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react@18.2.0):
|
/@chakra-ui/switch@2.1.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==}
|
resolution: {integrity: sha512-pgmi/CC+E1v31FcnQhsSGjJnOE2OcND4cKPyTE+0F+bmGm48Q/b5UmKD9Y+CmZsrt/7V3h8KNczowupfuBfIHA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@chakra-ui/system': '>=2.0.0'
|
'@chakra-ui/system': '>=2.0.0'
|
||||||
@ -2823,30 +2755,11 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
'@chakra-ui/checkbox': 2.3.2(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/system@2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==}
|
|
||||||
peerDependencies:
|
|
||||||
'@emotion/react': ^11.0.0
|
|
||||||
'@emotion/styled': ^11.0.0
|
|
||||||
react: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/color-mode': 2.2.0(react@18.2.0)
|
|
||||||
'@chakra-ui/object-utils': 2.1.0
|
|
||||||
'@chakra-ui/react-utils': 2.0.12(react@18.2.0)
|
|
||||||
'@chakra-ui/styled-system': 2.9.2
|
|
||||||
'@chakra-ui/theme-utils': 2.0.21
|
|
||||||
'@chakra-ui/utils': 2.0.15
|
|
||||||
'@emotion/react': 11.11.3(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
'@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
react-fast-compare: 3.2.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/system@2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0):
|
/@chakra-ui/system@2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==}
|
resolution: {integrity: sha512-EGtpoEjLrUu4W1fHD+a62XR+hzC5YfsWm+6lO0Kybcga3yYEij9beegO0jZgug27V+Rf7vns95VPVP6mFd/DEQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2975,7 +2888,7 @@ packages:
|
|||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0):
|
/@chakra-ui/toast@7.0.2(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==}
|
resolution: {integrity: sha512-yvRP8jFKRs/YnkuE41BVTq9nB2v/KDRmje9u6dgDmE5+1bFt3bwjdf9gVbif4u5Ve7F7BGk5E093ARRVtvLvXA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@chakra-ui/system': 2.6.2
|
'@chakra-ui/system': 2.6.2
|
||||||
@ -2991,9 +2904,9 @@ packages:
|
|||||||
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-update-effect': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
'@chakra-ui/styled-system': 2.9.2
|
'@chakra-ui/styled-system': 2.9.2
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
'@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2)
|
'@chakra-ui/theme': 3.3.1(@chakra-ui/styled-system@2.9.2)
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
@ -3020,7 +2933,7 @@ packages:
|
|||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.6)(react-dom@18.2.0)(react@18.2.0):
|
/@chakra-ui/tooltip@2.3.1(@chakra-ui/system@2.6.2)(framer-motion@11.0.22)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==}
|
resolution: {integrity: sha512-Rh39GBn/bL4kZpuEMPPRwYNnccRCL+w9OqamWHIB3Qboxs6h8cOyXfIdGxjo72lvhu1QI/a4KFqkM3St+WfC0A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@chakra-ui/system': '>=2.0.0'
|
'@chakra-ui/system': '>=2.0.0'
|
||||||
@ -3036,8 +2949,8 @@ packages:
|
|||||||
'@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-event-listener': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
'@chakra-ui/react-use-merge-refs': 2.1.0(react@18.2.0)
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
'@chakra-ui/shared-utils': 2.0.5
|
||||||
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(react@18.2.0)
|
'@chakra-ui/system': 2.6.2(@emotion/react@11.11.4)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
framer-motion: 11.0.22(react-dom@18.2.0)(react@18.2.0)
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
react-dom: 18.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
@ -3064,17 +2977,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@chakra-ui/transition@2.1.0(framer-motion@11.0.6)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-orkT6T/Dt+/+kVwJNy7zwJ+U2xAZ3EU7M3XCs45RBvUnZDr/u9vdmaM/3D/rOpmQJWgQBwKPJleUXrYWUagEDQ==}
|
|
||||||
peerDependencies:
|
|
||||||
framer-motion: '>=4.0.0'
|
|
||||||
react: '>=18'
|
|
||||||
dependencies:
|
|
||||||
'@chakra-ui/shared-utils': 2.0.5
|
|
||||||
framer-motion: 11.0.6(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@chakra-ui/utils@2.0.15:
|
/@chakra-ui/utils@2.0.15:
|
||||||
resolution: {integrity: sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==}
|
resolution: {integrity: sha512-El4+jL0WSaYYs+rJbuYFDbjmfCcfGDmRY95GO4xwzit6YAPZBLcR65rOEwLps+XWluZTy1xdMrusg/hW0c1aAA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3198,12 +3100,6 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@emotion/is-prop-valid@1.2.1:
|
|
||||||
resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==}
|
|
||||||
dependencies:
|
|
||||||
'@emotion/memoize': 0.8.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/is-prop-valid@1.2.2:
|
/@emotion/is-prop-valid@1.2.2:
|
||||||
resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==}
|
resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3220,27 +3116,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
|
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@emotion/react@11.11.3(@types/react@18.2.59)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': '*'
|
|
||||||
react: '>=16.8.0'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.9
|
|
||||||
'@emotion/babel-plugin': 11.11.0
|
|
||||||
'@emotion/cache': 11.11.0
|
|
||||||
'@emotion/serialize': 1.1.3
|
|
||||||
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
|
|
||||||
'@emotion/utils': 1.2.1
|
|
||||||
'@emotion/weak-memoize': 0.3.1
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/react@11.11.4(@types/react@18.2.73)(react@18.2.0):
|
/@emotion/react@11.11.4(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==}
|
resolution: {integrity: sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3276,27 +3151,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
|
resolution: {integrity: sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@emotion/styled@11.11.0(@emotion/react@11.11.3)(@types/react@18.2.59)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
|
|
||||||
peerDependencies:
|
|
||||||
'@emotion/react': ^11.0.0-rc.0
|
|
||||||
'@types/react': '*'
|
|
||||||
react: '>=16.8.0'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.9
|
|
||||||
'@emotion/babel-plugin': 11.11.0
|
|
||||||
'@emotion/is-prop-valid': 1.2.1
|
|
||||||
'@emotion/react': 11.11.3(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
'@emotion/serialize': 1.1.3
|
|
||||||
'@emotion/use-insertion-effect-with-fallbacks': 1.0.1(react@18.2.0)
|
|
||||||
'@emotion/utils': 1.2.1
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/styled@11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0):
|
/@emotion/styled@11.11.0(@emotion/react@11.11.4)(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
|
resolution: {integrity: sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3663,16 +3517,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@internationalized/date@3.5.2:
|
/@internationalized/date@3.5.3:
|
||||||
resolution: {integrity: sha512-vo1yOMUt2hzp63IutEaTUxROdvQg1qlMRsbCvbay2AK2Gai7wIgCyK5weEX3nHkiLgo4qCXHijFNC/ILhlRpOQ==}
|
resolution: {integrity: sha512-X9bi8NAEHAjD8yzmPYT2pdJsbe+tYSEBAfowtlxJVJdZR3aK8Vg7ZUT1Fm5M47KLzp/M1p1VwAaeSma3RT7biw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@swc/helpers': 0.5.7
|
'@swc/helpers': 0.5.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@internationalized/number@3.5.1:
|
/@internationalized/number@3.5.1:
|
||||||
resolution: {integrity: sha512-N0fPU/nz15SwR9IbfJ5xaS9Ss/O5h1sVXMZf43vc9mxEG48ovglvvzBjF53aHlq20uoR6c+88CrIXipU/LSzwg==}
|
resolution: {integrity: sha512-N0fPU/nz15SwR9IbfJ5xaS9Ss/O5h1sVXMZf43vc9mxEG48ovglvvzBjF53aHlq20uoR6c+88CrIXipU/LSzwg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@swc/helpers': 0.5.7
|
'@swc/helpers': 0.5.11
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@invoke-ai/eslint-config-react@0.0.14(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.3):
|
/@invoke-ai/eslint-config-react@0.0.14(eslint@8.57.0)(prettier@3.2.5)(typescript@5.4.3):
|
||||||
@ -3709,14 +3563,14 @@ packages:
|
|||||||
prettier: 3.2.5
|
prettier: 3.2.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@invoke-ai/ui-library@0.0.21(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.17)(@internationalized/date@3.5.2)(@types/react@18.2.73)(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0):
|
/@invoke-ai/ui-library@0.0.25(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.17)(@internationalized/date@3.5.3)(@types/react@18.2.73)(i18next@23.10.1)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-tCvgkBPDt0gNq+8IcR03e/Mw7R8Mb/SMXTqx3FEIxlTQEo93A/D38dKXeDCzTdx4sQ+sknfB+JLBbHs6sg5hhQ==}
|
resolution: {integrity: sha512-Fmjdlu62NXHgairYXGjcuCrxPEAl1G6Q6ban8g3excF6pDDdBeS7CmSNCyEDMxnSIOZrQlI04OhaMB17Imi9Uw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@fontsource-variable/inter': ^5.0.16
|
'@fontsource-variable/inter': ^5.0.16
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ark-ui/react': 1.3.0(@internationalized/date@3.5.2)(react-dom@18.2.0)(react@18.2.0)
|
'@ark-ui/react': 1.3.0(@internationalized/date@3.5.3)(react-dom@18.2.0)(react@18.2.0)
|
||||||
'@chakra-ui/anatomy': 2.2.2
|
'@chakra-ui/anatomy': 2.2.2
|
||||||
'@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
'@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
|
||||||
@ -5381,8 +5235,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@swc/helpers@0.5.7:
|
/@swc/helpers@0.5.11:
|
||||||
resolution: {integrity: sha512-BVvNZhx362+l2tSwSuyEUV4h7+jk9raNdoTSdLfwTshXJSaGmYKluGRJznziCI3KX02Z19DdsQrdfrpXAU3Hfg==}
|
resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
@ -5844,10 +5698,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==}
|
resolution: {integrity: sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/prop-types@15.7.11:
|
|
||||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/prop-types@15.7.12:
|
/@types/prop-types@15.7.12:
|
||||||
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
|
||||||
|
|
||||||
@ -5877,14 +5727,6 @@ packages:
|
|||||||
'@types/react': 18.2.73
|
'@types/react': 18.2.73
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/react@18.2.59:
|
|
||||||
resolution: {integrity: sha512-DE+F6BYEC8VtajY85Qr7mmhTd/79rJKIHCg99MU9SWPB4xvLb6D1za2vYflgZfmPqQVEr6UqJTnLXEwzpVPuOg==}
|
|
||||||
dependencies:
|
|
||||||
'@types/prop-types': 15.7.11
|
|
||||||
'@types/scheduler': 0.16.8
|
|
||||||
csstype: 3.1.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/react@18.2.73:
|
/@types/react@18.2.73:
|
||||||
resolution: {integrity: sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==}
|
resolution: {integrity: sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -5895,10 +5737,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==}
|
resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/scheduler@0.16.8:
|
|
||||||
resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@types/semver@7.5.8:
|
/@types/semver@7.5.8:
|
||||||
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
|
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -6405,10 +6243,10 @@ packages:
|
|||||||
/@zag-js/date-picker@0.32.1:
|
/@zag-js/date-picker@0.32.1:
|
||||||
resolution: {integrity: sha512-n/hYmF+/R4+NuyfPRzCgeuLT6LJihKSuKzK29STPWy3sC/tBBHiqhNv1/4UKbatHUJXdBW2XF+N8Rw08RffcFQ==}
|
resolution: {integrity: sha512-n/hYmF+/R4+NuyfPRzCgeuLT6LJihKSuKzK29STPWy3sC/tBBHiqhNv1/4UKbatHUJXdBW2XF+N8Rw08RffcFQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@internationalized/date': 3.5.2
|
'@internationalized/date': 3.5.3
|
||||||
'@zag-js/anatomy': 0.32.1
|
'@zag-js/anatomy': 0.32.1
|
||||||
'@zag-js/core': 0.32.1
|
'@zag-js/core': 0.32.1
|
||||||
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.2)
|
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.3)
|
||||||
'@zag-js/dismissable': 0.32.1
|
'@zag-js/dismissable': 0.32.1
|
||||||
'@zag-js/dom-event': 0.32.1
|
'@zag-js/dom-event': 0.32.1
|
||||||
'@zag-js/dom-query': 0.32.1
|
'@zag-js/dom-query': 0.32.1
|
||||||
@ -6420,12 +6258,12 @@ packages:
|
|||||||
'@zag-js/utils': 0.32.1
|
'@zag-js/utils': 0.32.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@zag-js/date-utils@0.32.1(@internationalized/date@3.5.2):
|
/@zag-js/date-utils@0.32.1(@internationalized/date@3.5.3):
|
||||||
resolution: {integrity: sha512-dbBDRSVr5pRUw3rXndyGuSshZiWqQI5JQO4D2KIFGkXzorj6WzoOpcO910Z7AdM/9cCAMpCjUrka8d8o9BpJBg==}
|
resolution: {integrity: sha512-dbBDRSVr5pRUw3rXndyGuSshZiWqQI5JQO4D2KIFGkXzorj6WzoOpcO910Z7AdM/9cCAMpCjUrka8d8o9BpJBg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@internationalized/date': '>=3.0.0'
|
'@internationalized/date': '>=3.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@internationalized/date': 3.5.2
|
'@internationalized/date': 3.5.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@zag-js/dialog@0.32.1:
|
/@zag-js/dialog@0.32.1:
|
||||||
@ -6999,13 +6837,6 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/aria-hidden@1.2.4:
|
|
||||||
resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/aria-query@5.1.3:
|
/aria-query@5.1.3:
|
||||||
resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
|
resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -9026,13 +8857,6 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/focus-lock@1.3.4:
|
|
||||||
resolution: {integrity: sha512-Gv0N3mvej3pD+HWkNryrF8sExzEHqhQ6OSFxD4DPxm9n5HGCaHme98ZMBZroNEAJcsdtHxk+skvThGKyUeoEGA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
dependencies:
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/focus-trap@7.5.4:
|
/focus-trap@7.5.4:
|
||||||
resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
|
resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -9095,24 +8919,6 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/framer-motion@11.0.6(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-BpO3mWF8UwxzO3Ca5AmSkrg14QYTeJa9vKgoLOoBdBdTPj0e81i1dMwnX6EQJXRieUx20uiDBXq8bA6y7N6b8Q==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^18.0.0
|
|
||||||
react-dom: ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
react:
|
|
||||||
optional: true
|
|
||||||
react-dom:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
tslib: 2.6.2
|
|
||||||
optionalDependencies:
|
|
||||||
'@emotion/is-prop-valid': 0.8.8
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/framesync@6.1.2:
|
/framesync@6.1.2:
|
||||||
resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==}
|
resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -11485,7 +11291,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
|
resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-focus-lock@2.11.1(@types/react@18.2.59)(react@18.2.0):
|
/react-focus-lock@2.11.1(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-IXLwnTBrLTlKTpASZXqqXJ8oymWrgAlOfuuDYN4XCuN1YJ72dwX198UCaF1QqGUk5C3QOnlMik//n3ufcfe8Ig==}
|
resolution: {integrity: sha512-IXLwnTBrLTlKTpASZXqqXJ8oymWrgAlOfuuDYN4XCuN1YJ72dwX198UCaF1QqGUk5C3QOnlMik//n3ufcfe8Ig==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
@ -11495,31 +11301,12 @@ packages:
|
|||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.9
|
'@babel/runtime': 7.23.9
|
||||||
'@types/react': 18.2.59
|
'@types/react': 18.2.73
|
||||||
focus-lock: 1.3.3
|
focus-lock: 1.3.3
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-clientside-effect: 1.2.6(react@18.2.0)
|
react-clientside-effect: 1.2.6(react@18.2.0)
|
||||||
use-callback-ref: 1.3.1(@types/react@18.2.59)(react@18.2.0)
|
use-callback-ref: 1.3.1(@types/react@18.2.73)(react@18.2.0)
|
||||||
use-sidecar: 1.1.2(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-focus-lock@2.11.2(@types/react@18.2.73)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-DDTbEiov0+RthESPVSTIdAWPPKic+op3sCcP+icbMRobvQNt7LuAlJ3KoarqQv5sCgKArru3kXmlmFTa27/CdQ==}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.24.1
|
|
||||||
'@types/react': 18.2.73
|
|
||||||
focus-lock: 1.3.4
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-clientside-effect: 1.2.6(react@18.2.0)
|
|
||||||
use-callback-ref: 1.3.2(@types/react@18.2.73)(react@18.2.0)
|
|
||||||
use-sidecar: 1.1.2(@types/react@18.2.73)(react@18.2.0)
|
use-sidecar: 1.1.2(@types/react@18.2.73)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -11634,25 +11421,9 @@ packages:
|
|||||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-remove-scroll-bar@2.3.5(@types/react@18.2.59)(react@18.2.0):
|
/react-remove-scroll-bar@2.3.5(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==}
|
resolution: {integrity: sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
react: 18.2.0
|
|
||||||
react-style-singleton: 2.2.1(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-remove-scroll-bar@2.3.6(@types/react@18.2.73)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
@ -11666,28 +11437,9 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-remove-scroll@2.5.7(@types/react@18.2.59)(react@18.2.0):
|
/react-remove-scroll@2.5.7(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==}
|
resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
react: 18.2.0
|
|
||||||
react-remove-scroll-bar: 2.3.5(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
react-style-singleton: 2.2.1(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
tslib: 2.6.2
|
|
||||||
use-callback-ref: 1.3.1(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
use-sidecar: 1.1.2(@types/react@18.2.59)(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-remove-scroll@2.5.9(@types/react@18.2.73)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-bvHCLBrFfM2OgcrpPY2YW84sPdS2o2HKWJUf1xGyGLnSoEnOTOBpahIarjRuYtN0ryahCeP242yf+5TrBX/pZA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
@ -11697,10 +11449,10 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': 18.2.73
|
'@types/react': 18.2.73
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-remove-scroll-bar: 2.3.6(@types/react@18.2.73)(react@18.2.0)
|
react-remove-scroll-bar: 2.3.5(@types/react@18.2.73)(react@18.2.0)
|
||||||
react-style-singleton: 2.2.1(@types/react@18.2.73)(react@18.2.0)
|
react-style-singleton: 2.2.1(@types/react@18.2.73)(react@18.2.0)
|
||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
use-callback-ref: 1.3.2(@types/react@18.2.73)(react@18.2.0)
|
use-callback-ref: 1.3.1(@types/react@18.2.73)(react@18.2.0)
|
||||||
use-sidecar: 1.1.2(@types/react@18.2.73)(react@18.2.0)
|
use-sidecar: 1.1.2(@types/react@18.2.73)(react@18.2.0)
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -11756,23 +11508,6 @@ packages:
|
|||||||
- '@types/react'
|
- '@types/react'
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-style-singleton@2.2.1(@types/react@18.2.59)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
get-nonce: 1.0.1
|
|
||||||
invariant: 2.2.4
|
|
||||||
react: 18.2.0
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-style-singleton@2.2.1(@types/react@18.2.73)(react@18.2.0):
|
/react-style-singleton@2.2.1(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -13288,24 +13023,9 @@ packages:
|
|||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/use-callback-ref@1.3.1(@types/react@18.2.59)(react@18.2.0):
|
/use-callback-ref@1.3.1(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==}
|
resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
react: 18.2.0
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/use-callback-ref@1.3.2(@types/react@18.2.73)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
@ -13358,22 +13078,6 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/use-sidecar@1.1.2(@types/react@18.2.59)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
|
||||||
engines: {node: '>=10'}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.59
|
|
||||||
detect-node-es: 1.1.0
|
|
||||||
react: 18.2.0
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/use-sidecar@1.1.2(@types/react@18.2.73)(react@18.2.0):
|
/use-sidecar@1.1.2(@types/react@18.2.73)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -88,11 +88,13 @@
|
|||||||
"negativePrompt": "Negative Prompt",
|
"negativePrompt": "Negative Prompt",
|
||||||
"discordLabel": "Discord",
|
"discordLabel": "Discord",
|
||||||
"dontAskMeAgain": "Don't ask me again",
|
"dontAskMeAgain": "Don't ask me again",
|
||||||
|
"editor": "Editor",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"file": "File",
|
"file": "File",
|
||||||
"folder": "Folder",
|
"folder": "Folder",
|
||||||
"format": "format",
|
"format": "format",
|
||||||
"githubLabel": "Github",
|
"githubLabel": "Github",
|
||||||
|
"goTo": "Go to",
|
||||||
"hotkeysLabel": "Hotkeys",
|
"hotkeysLabel": "Hotkeys",
|
||||||
"imageFailedToLoad": "Unable to Load Image",
|
"imageFailedToLoad": "Unable to Load Image",
|
||||||
"img2img": "Image To Image",
|
"img2img": "Image To Image",
|
||||||
@ -140,7 +142,8 @@
|
|||||||
"blue": "Blue",
|
"blue": "Blue",
|
||||||
"alpha": "Alpha",
|
"alpha": "Alpha",
|
||||||
"selected": "Selected",
|
"selected": "Selected",
|
||||||
"viewer": "Viewer"
|
"viewer": "Viewer",
|
||||||
|
"tab": "Tab"
|
||||||
},
|
},
|
||||||
"controlnet": {
|
"controlnet": {
|
||||||
"controlAdapter_one": "Control Adapter",
|
"controlAdapter_one": "Control Adapter",
|
||||||
@ -225,7 +228,7 @@
|
|||||||
"composition": "Composition Only",
|
"composition": "Composition Only",
|
||||||
"safe": "Safe",
|
"safe": "Safe",
|
||||||
"saveControlImage": "Save Control Image",
|
"saveControlImage": "Save Control Image",
|
||||||
"scribble": "scribble",
|
"scribble": "Scribble",
|
||||||
"selectModel": "Select a model",
|
"selectModel": "Select a model",
|
||||||
"selectCLIPVisionModel": "Select a CLIP Vision model",
|
"selectCLIPVisionModel": "Select a CLIP Vision model",
|
||||||
"setControlImageDimensions": "Copy size to W/H (optimize for model)",
|
"setControlImageDimensions": "Copy size to W/H (optimize for model)",
|
||||||
@ -361,7 +364,8 @@
|
|||||||
"bulkDownloadRequestFailed": "Problem Preparing Download",
|
"bulkDownloadRequestFailed": "Problem Preparing Download",
|
||||||
"bulkDownloadFailed": "Download Failed",
|
"bulkDownloadFailed": "Download Failed",
|
||||||
"problemDeletingImages": "Problem Deleting Images",
|
"problemDeletingImages": "Problem Deleting Images",
|
||||||
"problemDeletingImagesDesc": "One or more images could not be deleted"
|
"problemDeletingImagesDesc": "One or more images could not be deleted",
|
||||||
|
"switchTo": "Switch to {{ tab }} (Z)"
|
||||||
},
|
},
|
||||||
"hotkeys": {
|
"hotkeys": {
|
||||||
"searchHotkeys": "Search Hotkeys",
|
"searchHotkeys": "Search Hotkeys",
|
||||||
@ -584,6 +588,14 @@
|
|||||||
"upscale": {
|
"upscale": {
|
||||||
"desc": "Upscale the current image",
|
"desc": "Upscale the current image",
|
||||||
"title": "Upscale"
|
"title": "Upscale"
|
||||||
|
},
|
||||||
|
"backToEditor": {
|
||||||
|
"desc": "Closes the Image Viewer and shows the Editor View (Text to Image tab only)",
|
||||||
|
"title": "Back to Editor"
|
||||||
|
},
|
||||||
|
"openImageViewer": {
|
||||||
|
"desc": "Opens the Image Viewer (Text to Image tab only)",
|
||||||
|
"title": "Open Image Viewer"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
@ -917,6 +929,7 @@
|
|||||||
"missingInputForField": "{{nodeLabel}} -> {{fieldLabel}} missing input",
|
"missingInputForField": "{{nodeLabel}} -> {{fieldLabel}} missing input",
|
||||||
"missingNodeTemplate": "Missing node template",
|
"missingNodeTemplate": "Missing node template",
|
||||||
"noControlImageForControlAdapter": "Control Adapter #{{number}} has no control image",
|
"noControlImageForControlAdapter": "Control Adapter #{{number}} has no control image",
|
||||||
|
"imageNotProcessedForControlAdapter": "Control Adapter #{{number}}'s image is not processed",
|
||||||
"noInitialImageSelected": "No initial image selected",
|
"noInitialImageSelected": "No initial image selected",
|
||||||
"noModelForControlAdapter": "Control Adapter #{{number}} has no model selected.",
|
"noModelForControlAdapter": "Control Adapter #{{number}} has no model selected.",
|
||||||
"incompatibleBaseModelForControlAdapter": "Control Adapter #{{number}} model is incompatible with main model.",
|
"incompatibleBaseModelForControlAdapter": "Control Adapter #{{number}} model is incompatible with main model.",
|
||||||
@ -1542,6 +1555,25 @@
|
|||||||
"globalControlAdapterLayer": "Global $t(controlnet.controlAdapter_one) $t(unifiedCanvas.layer)",
|
"globalControlAdapterLayer": "Global $t(controlnet.controlAdapter_one) $t(unifiedCanvas.layer)",
|
||||||
"globalIPAdapter": "Global $t(common.ipAdapter)",
|
"globalIPAdapter": "Global $t(common.ipAdapter)",
|
||||||
"globalIPAdapterLayer": "Global $t(common.ipAdapter) $t(unifiedCanvas.layer)",
|
"globalIPAdapterLayer": "Global $t(common.ipAdapter) $t(unifiedCanvas.layer)",
|
||||||
"opacityFilter": "Opacity Filter"
|
"globalInitialImage": "Global Initial Image",
|
||||||
|
"globalInitialImageLayer": "$t(controlLayers.globalInitialImage) $t(unifiedCanvas.layer)",
|
||||||
|
"opacityFilter": "Opacity Filter",
|
||||||
|
"clearProcessor": "Clear Processor",
|
||||||
|
"resetProcessor": "Reset Processor to Defaults",
|
||||||
|
"noLayersAdded": "No Layers Added"
|
||||||
|
},
|
||||||
|
"ui": {
|
||||||
|
"tabs": {
|
||||||
|
"generation": "Generation",
|
||||||
|
"generationTab": "$t(ui.tabs.generation) $t(common.tab)",
|
||||||
|
"canvas": "Canvas",
|
||||||
|
"canvasTab": "$t(ui.tabs.canvas) $t(common.tab)",
|
||||||
|
"workflows": "Workflows",
|
||||||
|
"workflowsTab": "$t(ui.tabs.workflows) $t(common.tab)",
|
||||||
|
"models": "Models",
|
||||||
|
"modelsTab": "$t(ui.tabs.models) $t(common.tab)",
|
||||||
|
"queue": "Queue",
|
||||||
|
"queueTab": "$t(ui.tabs.queue) $t(common.tab)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ export type LoggerNamespace =
|
|||||||
| 'models'
|
| 'models'
|
||||||
| 'config'
|
| 'config'
|
||||||
| 'canvas'
|
| 'canvas'
|
||||||
| 'txt2img'
|
| 'generation'
|
||||||
| 'img2img'
|
|
||||||
| 'nodes'
|
| 'nodes'
|
||||||
| 'system'
|
| 'system'
|
||||||
| 'socketio'
|
| 'socketio'
|
||||||
|
@ -16,7 +16,7 @@ import { addCanvasMaskSavedToGalleryListener } from 'app/store/middleware/listen
|
|||||||
import { addCanvasMaskToControlNetListener } from 'app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet';
|
import { addCanvasMaskToControlNetListener } from 'app/store/middleware/listenerMiddleware/listeners/canvasMaskToControlNet';
|
||||||
import { addCanvasMergedListener } from 'app/store/middleware/listenerMiddleware/listeners/canvasMerged';
|
import { addCanvasMergedListener } from 'app/store/middleware/listenerMiddleware/listeners/canvasMerged';
|
||||||
import { addCanvasSavedToGalleryListener } from 'app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery';
|
import { addCanvasSavedToGalleryListener } from 'app/store/middleware/listenerMiddleware/listeners/canvasSavedToGallery';
|
||||||
import { addControlLayersToControlAdapterBridge } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
import { addControlAdapterPreprocessor } from 'app/store/middleware/listenerMiddleware/listeners/controlAdapterPreprocessor';
|
||||||
import { addControlNetAutoProcessListener } from 'app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess';
|
import { addControlNetAutoProcessListener } from 'app/store/middleware/listenerMiddleware/listeners/controlNetAutoProcess';
|
||||||
import { addControlNetImageProcessedListener } from 'app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed';
|
import { addControlNetImageProcessedListener } from 'app/store/middleware/listenerMiddleware/listeners/controlNetImageProcessed';
|
||||||
import { addEnqueueRequestedCanvasListener } from 'app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas';
|
import { addEnqueueRequestedCanvasListener } from 'app/store/middleware/listenerMiddleware/listeners/enqueueRequestedCanvas';
|
||||||
@ -32,7 +32,6 @@ import { addImagesStarredListener } from 'app/store/middleware/listenerMiddlewar
|
|||||||
import { addImagesUnstarredListener } from 'app/store/middleware/listenerMiddleware/listeners/imagesUnstarred';
|
import { addImagesUnstarredListener } from 'app/store/middleware/listenerMiddleware/listeners/imagesUnstarred';
|
||||||
import { addImageToDeleteSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected';
|
import { addImageToDeleteSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/imageToDeleteSelected';
|
||||||
import { addImageUploadedFulfilledListener } from 'app/store/middleware/listenerMiddleware/listeners/imageUploaded';
|
import { addImageUploadedFulfilledListener } from 'app/store/middleware/listenerMiddleware/listeners/imageUploaded';
|
||||||
import { addInitialImageSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/initialImageSelected';
|
|
||||||
import { addModelSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelSelected';
|
import { addModelSelectedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelSelected';
|
||||||
import { addModelsLoadedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelsLoaded';
|
import { addModelsLoadedListener } from 'app/store/middleware/listenerMiddleware/listeners/modelsLoaded';
|
||||||
import { addDynamicPromptsListener } from 'app/store/middleware/listenerMiddleware/listeners/promptChanged';
|
import { addDynamicPromptsListener } from 'app/store/middleware/listenerMiddleware/listeners/promptChanged';
|
||||||
@ -73,9 +72,6 @@ const startAppListening = listenerMiddleware.startListening as AppStartListening
|
|||||||
// Image uploaded
|
// Image uploaded
|
||||||
addImageUploadedFulfilledListener(startAppListening);
|
addImageUploadedFulfilledListener(startAppListening);
|
||||||
|
|
||||||
// Image selected
|
|
||||||
addInitialImageSelectedListener(startAppListening);
|
|
||||||
|
|
||||||
// Image deleted
|
// Image deleted
|
||||||
addRequestedSingleImageDeletionListener(startAppListening);
|
addRequestedSingleImageDeletionListener(startAppListening);
|
||||||
addDeleteBoardAndImagesFulfilledListener(startAppListening);
|
addDeleteBoardAndImagesFulfilledListener(startAppListening);
|
||||||
@ -158,5 +154,4 @@ addUpscaleRequestedListener(startAppListening);
|
|||||||
addDynamicPromptsListener(startAppListening);
|
addDynamicPromptsListener(startAppListening);
|
||||||
|
|
||||||
addSetDefaultSettingsListener(startAppListening);
|
addSetDefaultSettingsListener(startAppListening);
|
||||||
|
addControlAdapterPreprocessor(startAppListening);
|
||||||
addControlLayersToControlAdapterBridge(startAppListening);
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
||||||
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
import { controlAdaptersReset } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
|
import { allLayersDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||||
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppStartListening) => {
|
export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppStartListening) => {
|
||||||
@ -14,19 +14,14 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
|
|||||||
|
|
||||||
// Remove all deleted images from the UI
|
// Remove all deleted images from the UI
|
||||||
|
|
||||||
let wasInitialImageReset = false;
|
|
||||||
let wasCanvasReset = false;
|
let wasCanvasReset = false;
|
||||||
let wasNodeEditorReset = false;
|
let wasNodeEditorReset = false;
|
||||||
let wereControlAdaptersReset = false;
|
let wereControlAdaptersReset = false;
|
||||||
|
let wereControlLayersReset = false;
|
||||||
|
|
||||||
const { generation, canvas, nodes, controlAdapters } = getState();
|
const { canvas, nodes, controlAdapters, controlLayers } = getState();
|
||||||
deleted_images.forEach((image_name) => {
|
deleted_images.forEach((image_name) => {
|
||||||
const imageUsage = getImageUsage(generation, canvas, nodes, controlAdapters, image_name);
|
const imageUsage = getImageUsage(canvas, nodes, controlAdapters, controlLayers.present, image_name);
|
||||||
|
|
||||||
if (imageUsage.isInitialImage && !wasInitialImageReset) {
|
|
||||||
dispatch(clearInitialImage());
|
|
||||||
wasInitialImageReset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageUsage.isCanvasImage && !wasCanvasReset) {
|
if (imageUsage.isCanvasImage && !wasCanvasReset) {
|
||||||
dispatch(resetCanvas());
|
dispatch(resetCanvas());
|
||||||
@ -42,6 +37,11 @@ export const addDeleteBoardAndImagesFulfilledListener = (startAppListening: AppS
|
|||||||
dispatch(controlAdaptersReset());
|
dispatch(controlAdaptersReset());
|
||||||
wereControlAdaptersReset = true;
|
wereControlAdaptersReset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (imageUsage.isControlLayerImage && !wereControlLayersReset) {
|
||||||
|
dispatch(allLayersDeleted());
|
||||||
|
wereControlLayersReset = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -48,10 +48,12 @@ export const addCanvasImageToControlNetListener = (startAppListening: AppStartLi
|
|||||||
})
|
})
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
const { image_name } = imageDTO;
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
controlAdapterImageChanged({
|
controlAdapterImageChanged({
|
||||||
id,
|
id,
|
||||||
controlImage: imageDTO,
|
controlImage: image_name,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -58,10 +58,12 @@ export const addCanvasMaskToControlNetListener = (startAppListening: AppStartLis
|
|||||||
})
|
})
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
|
const { image_name } = imageDTO;
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
controlAdapterImageChanged({
|
controlAdapterImageChanged({
|
||||||
id,
|
id,
|
||||||
controlImage: imageDTO,
|
controlImage: image_name,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,156 @@
|
|||||||
|
import { isAnyOf } from '@reduxjs/toolkit';
|
||||||
|
import { logger } from 'app/logging/logger';
|
||||||
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
|
import { parseify } from 'common/util/serialize';
|
||||||
|
import {
|
||||||
|
caLayerImageChanged,
|
||||||
|
caLayerIsProcessingImageChanged,
|
||||||
|
caLayerModelChanged,
|
||||||
|
caLayerProcessedImageChanged,
|
||||||
|
caLayerProcessorConfigChanged,
|
||||||
|
isControlAdapterLayer,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { isImageOutput } from 'features/nodes/types/common';
|
||||||
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
|
import type { BatchConfig, ImageDTO } from 'services/api/types';
|
||||||
|
import { socketInvocationComplete } from 'services/events/actions';
|
||||||
|
|
||||||
|
const matcher = isAnyOf(caLayerImageChanged, caLayerProcessorConfigChanged, caLayerModelChanged);
|
||||||
|
|
||||||
|
const DEBOUNCE_MS = 300;
|
||||||
|
const log = logger('session');
|
||||||
|
|
||||||
|
export const addControlAdapterPreprocessor = (startAppListening: AppStartListening) => {
|
||||||
|
startAppListening({
|
||||||
|
matcher,
|
||||||
|
effect: async (action, { dispatch, getState, getOriginalState, cancelActiveListeners, delay, take }) => {
|
||||||
|
const { layerId } = action.payload;
|
||||||
|
const precheckLayerOriginal = getOriginalState()
|
||||||
|
.controlLayers.present.layers.filter(isControlAdapterLayer)
|
||||||
|
.find((l) => l.id === layerId);
|
||||||
|
const precheckLayer = getState()
|
||||||
|
.controlLayers.present.layers.filter(isControlAdapterLayer)
|
||||||
|
.find((l) => l.id === layerId);
|
||||||
|
|
||||||
|
// Conditions to bail
|
||||||
|
const layerDoesNotExist = !precheckLayer;
|
||||||
|
const layerHasNoImage = !precheckLayer?.controlAdapter.image;
|
||||||
|
const layerHasNoProcessorConfig = !precheckLayer?.controlAdapter.processorConfig;
|
||||||
|
const layerIsAlreadyProcessingImage = precheckLayer?.controlAdapter.isProcessingImage;
|
||||||
|
const areImageAndProcessorUnchanged =
|
||||||
|
isEqual(precheckLayer?.controlAdapter.image, precheckLayerOriginal?.controlAdapter.image) &&
|
||||||
|
isEqual(precheckLayer?.controlAdapter.processorConfig, precheckLayerOriginal?.controlAdapter.processorConfig);
|
||||||
|
|
||||||
|
if (
|
||||||
|
layerDoesNotExist ||
|
||||||
|
layerHasNoImage ||
|
||||||
|
layerHasNoProcessorConfig ||
|
||||||
|
areImageAndProcessorUnchanged ||
|
||||||
|
layerIsAlreadyProcessingImage
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel any in-progress instances of this listener
|
||||||
|
cancelActiveListeners();
|
||||||
|
log.trace('Control Layer CA auto-process triggered');
|
||||||
|
|
||||||
|
// Delay before starting actual work
|
||||||
|
await delay(DEBOUNCE_MS);
|
||||||
|
dispatch(caLayerIsProcessingImageChanged({ layerId, isProcessingImage: true }));
|
||||||
|
|
||||||
|
// Double-check that we are still eligible for processing
|
||||||
|
const state = getState();
|
||||||
|
const layer = state.controlLayers.present.layers.filter(isControlAdapterLayer).find((l) => l.id === layerId);
|
||||||
|
const image = layer?.controlAdapter.image;
|
||||||
|
const config = layer?.controlAdapter.processorConfig;
|
||||||
|
|
||||||
|
// If we have no image or there is no processor config, bail
|
||||||
|
if (!layer || !image || !config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error: TS isn't able to narrow the typing of buildNode and `config` will error...
|
||||||
|
const processorNode = CA_PROCESSOR_DATA[config.type].buildNode(image, config);
|
||||||
|
const enqueueBatchArg: BatchConfig = {
|
||||||
|
prepend: true,
|
||||||
|
batch: {
|
||||||
|
graph: {
|
||||||
|
nodes: {
|
||||||
|
[processorNode.id]: { ...processorNode, is_intermediate: true },
|
||||||
|
},
|
||||||
|
edges: [],
|
||||||
|
},
|
||||||
|
runs: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const req = dispatch(
|
||||||
|
queueApi.endpoints.enqueueBatch.initiate(enqueueBatchArg, {
|
||||||
|
fixedCacheKey: 'enqueueBatch',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const enqueueResult = await req.unwrap();
|
||||||
|
req.reset();
|
||||||
|
log.debug({ enqueueResult: parseify(enqueueResult) }, t('queue.graphQueued'));
|
||||||
|
|
||||||
|
const [invocationCompleteAction] = await take(
|
||||||
|
(action): action is ReturnType<typeof socketInvocationComplete> =>
|
||||||
|
socketInvocationComplete.match(action) &&
|
||||||
|
action.payload.data.queue_batch_id === enqueueResult.batch.batch_id &&
|
||||||
|
action.payload.data.source_node_id === processorNode.id
|
||||||
|
);
|
||||||
|
|
||||||
|
// We still have to check the output type
|
||||||
|
if (isImageOutput(invocationCompleteAction.payload.data.result)) {
|
||||||
|
const { image_name } = invocationCompleteAction.payload.data.result.image;
|
||||||
|
|
||||||
|
// Wait for the ImageDTO to be received
|
||||||
|
const [{ payload }] = await take(
|
||||||
|
(action) =>
|
||||||
|
imagesApi.endpoints.getImageDTO.matchFulfilled(action) && action.payload.image_name === image_name
|
||||||
|
);
|
||||||
|
|
||||||
|
const imageDTO = payload as ImageDTO;
|
||||||
|
|
||||||
|
log.debug({ layerId, imageDTO }, 'ControlNet image processed');
|
||||||
|
|
||||||
|
// Update the processed image in the store
|
||||||
|
dispatch(
|
||||||
|
caLayerProcessedImageChanged({
|
||||||
|
layerId,
|
||||||
|
imageDTO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
dispatch(caLayerIsProcessingImageChanged({ layerId, isProcessingImage: false }));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
log.error({ enqueueBatchArg: parseify(enqueueBatchArg) }, t('queue.graphFailedToQueue'));
|
||||||
|
dispatch(caLayerIsProcessingImageChanged({ layerId, isProcessingImage: false }));
|
||||||
|
|
||||||
|
if (error instanceof Object) {
|
||||||
|
if ('data' in error && 'status' in error) {
|
||||||
|
if (error.status === 403) {
|
||||||
|
dispatch(caLayerImageChanged({ layerId, imageDTO: null }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
title: t('queue.graphFailedToQueue'),
|
||||||
|
status: 'error',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@ -1,144 +0,0 @@
|
|||||||
import { createAction } from '@reduxjs/toolkit';
|
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
|
||||||
import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
|
|
||||||
import { controlAdapterAdded, controlAdapterRemoved } from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import type { ControlNetConfig, IPAdapterConfig } from 'features/controlAdapters/store/types';
|
|
||||||
import { isControlAdapterProcessorType } from 'features/controlAdapters/store/types';
|
|
||||||
import {
|
|
||||||
controlAdapterLayerAdded,
|
|
||||||
ipAdapterLayerAdded,
|
|
||||||
layerDeleted,
|
|
||||||
maskLayerIPAdapterAdded,
|
|
||||||
maskLayerIPAdapterDeleted,
|
|
||||||
regionalGuidanceLayerAdded,
|
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
|
||||||
import { modelConfigsAdapterSelectors, modelsApi } from 'services/api/endpoints/models';
|
|
||||||
import { isControlNetModelConfig, isIPAdapterModelConfig } from 'services/api/types';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
export const guidanceLayerAdded = createAction<Layer['type']>('controlLayers/guidanceLayerAdded');
|
|
||||||
export const guidanceLayerDeleted = createAction<string>('controlLayers/guidanceLayerDeleted');
|
|
||||||
export const allLayersDeleted = createAction('controlLayers/allLayersDeleted');
|
|
||||||
export const guidanceLayerIPAdapterAdded = createAction<string>('controlLayers/guidanceLayerIPAdapterAdded');
|
|
||||||
export const guidanceLayerIPAdapterDeleted = createAction<{ layerId: string; ipAdapterId: string }>(
|
|
||||||
'controlLayers/guidanceLayerIPAdapterDeleted'
|
|
||||||
);
|
|
||||||
|
|
||||||
export const addControlLayersToControlAdapterBridge = (startAppListening: AppStartListening) => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: guidanceLayerAdded,
|
|
||||||
effect: (action, { dispatch, getState }) => {
|
|
||||||
const type = action.payload;
|
|
||||||
const layerId = uuidv4();
|
|
||||||
if (type === 'regional_guidance_layer') {
|
|
||||||
dispatch(regionalGuidanceLayerAdded({ layerId }));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = getState();
|
|
||||||
const baseModel = state.generation.model?.base;
|
|
||||||
const modelConfigs = modelsApi.endpoints.getModelConfigs.select(undefined)(state).data;
|
|
||||||
|
|
||||||
if (type === 'ip_adapter_layer') {
|
|
||||||
const ipAdapterId = uuidv4();
|
|
||||||
const overrides: Partial<IPAdapterConfig> = {
|
|
||||||
id: ipAdapterId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find and select the first matching model
|
|
||||||
if (modelConfigs) {
|
|
||||||
const models = modelConfigsAdapterSelectors.selectAll(modelConfigs).filter(isIPAdapterModelConfig);
|
|
||||||
overrides.model = models.find((m) => m.base === baseModel) ?? null;
|
|
||||||
}
|
|
||||||
dispatch(controlAdapterAdded({ type: 'ip_adapter', overrides }));
|
|
||||||
dispatch(ipAdapterLayerAdded({ layerId, ipAdapterId }));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'control_adapter_layer') {
|
|
||||||
const controlNetId = uuidv4();
|
|
||||||
const overrides: Partial<ControlNetConfig> = {
|
|
||||||
id: controlNetId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find and select the first matching model
|
|
||||||
if (modelConfigs) {
|
|
||||||
const models = modelConfigsAdapterSelectors.selectAll(modelConfigs).filter(isControlNetModelConfig);
|
|
||||||
const model = models.find((m) => m.base === baseModel) ?? null;
|
|
||||||
overrides.model = model;
|
|
||||||
const defaultPreprocessor = model?.default_settings?.preprocessor;
|
|
||||||
overrides.processorType = isControlAdapterProcessorType(defaultPreprocessor) ? defaultPreprocessor : 'none';
|
|
||||||
overrides.processorNode = CONTROLNET_PROCESSORS[overrides.processorType].buildDefaults(baseModel);
|
|
||||||
}
|
|
||||||
dispatch(controlAdapterAdded({ type: 'controlnet', overrides }));
|
|
||||||
dispatch(controlAdapterLayerAdded({ layerId, controlNetId }));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: guidanceLayerDeleted,
|
|
||||||
effect: (action, { getState, dispatch }) => {
|
|
||||||
const layerId = action.payload;
|
|
||||||
const state = getState();
|
|
||||||
const layer = state.controlLayers.present.layers.find((l) => l.id === layerId);
|
|
||||||
assert(layer, `Layer ${layerId} not found`);
|
|
||||||
|
|
||||||
if (layer.type === 'ip_adapter_layer') {
|
|
||||||
dispatch(controlAdapterRemoved({ id: layer.ipAdapterId }));
|
|
||||||
} else if (layer.type === 'control_adapter_layer') {
|
|
||||||
dispatch(controlAdapterRemoved({ id: layer.controlNetId }));
|
|
||||||
} else if (layer.type === 'regional_guidance_layer') {
|
|
||||||
for (const ipAdapterId of layer.ipAdapterIds) {
|
|
||||||
dispatch(controlAdapterRemoved({ id: ipAdapterId }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dispatch(layerDeleted(layerId));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: allLayersDeleted,
|
|
||||||
effect: (action, { dispatch, getOriginalState }) => {
|
|
||||||
const state = getOriginalState();
|
|
||||||
for (const layer of state.controlLayers.present.layers) {
|
|
||||||
dispatch(guidanceLayerDeleted(layer.id));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: guidanceLayerIPAdapterAdded,
|
|
||||||
effect: (action, { dispatch, getState }) => {
|
|
||||||
const layerId = action.payload;
|
|
||||||
const ipAdapterId = uuidv4();
|
|
||||||
const overrides: Partial<IPAdapterConfig> = {
|
|
||||||
id: ipAdapterId,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find and select the first matching model
|
|
||||||
const state = getState();
|
|
||||||
const baseModel = state.generation.model?.base;
|
|
||||||
const modelConfigs = modelsApi.endpoints.getModelConfigs.select(undefined)(state).data;
|
|
||||||
if (modelConfigs) {
|
|
||||||
const models = modelConfigsAdapterSelectors.selectAll(modelConfigs).filter(isIPAdapterModelConfig);
|
|
||||||
overrides.model = models.find((m) => m.base === baseModel) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(controlAdapterAdded({ type: 'ip_adapter', overrides }));
|
|
||||||
dispatch(maskLayerIPAdapterAdded({ layerId, ipAdapterId }));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: guidanceLayerIPAdapterDeleted,
|
|
||||||
effect: (action, { dispatch }) => {
|
|
||||||
const { layerId, ipAdapterId } = action.payload;
|
|
||||||
dispatch(controlAdapterRemoved({ id: ipAdapterId }));
|
|
||||||
dispatch(maskLayerIPAdapterDeleted({ layerId, ipAdapterId }));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -12,7 +12,6 @@ import {
|
|||||||
selectControlAdapterById,
|
selectControlAdapterById,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||||
import { isEqual } from 'lodash-es';
|
|
||||||
|
|
||||||
type AnyControlAdapterParamChangeAction =
|
type AnyControlAdapterParamChangeAction =
|
||||||
| ReturnType<typeof controlAdapterProcessorParamsChanged>
|
| ReturnType<typeof controlAdapterProcessorParamsChanged>
|
||||||
@ -53,11 +52,6 @@ const predicate: AnyListenerPredicate<RootState> = (action, state, prevState) =>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevCA.controlImage === ca.controlImage && isEqual(prevCA.processorNode, ca.processorNode)) {
|
|
||||||
// Don't re-process if the processor hasn't changed
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isProcessorSelected = processorType !== 'none';
|
const isProcessorSelected = processorType !== 'none';
|
||||||
|
|
||||||
const hasControlImage = Boolean(controlImage);
|
const hasControlImage = Boolean(controlImage);
|
||||||
|
@ -91,7 +91,7 @@ export const addControlNetImageProcessedListener = (startAppListening: AppStartL
|
|||||||
dispatch(
|
dispatch(
|
||||||
controlAdapterProcessedImageChanged({
|
controlAdapterProcessedImageChanged({
|
||||||
id,
|
id,
|
||||||
processedControlImage,
|
processedControlImage: processedControlImage.image_name,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import type { ImageDTO } from 'services/api/types';
|
|||||||
export const addEnqueueRequestedCanvasListener = (startAppListening: AppStartListening) => {
|
export const addEnqueueRequestedCanvasListener = (startAppListening: AppStartListening) => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
||||||
enqueueRequested.match(action) && action.payload.tabName === 'unifiedCanvas',
|
enqueueRequested.match(action) && action.payload.tabName === 'canvas',
|
||||||
effect: async (action, { getState, dispatch }) => {
|
effect: async (action, { getState, dispatch }) => {
|
||||||
const log = logger('queue');
|
const log = logger('queue');
|
||||||
const { prepend } = action.payload;
|
const { prepend } = action.payload;
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import { enqueueRequested } from 'app/store/actions';
|
import { enqueueRequested } from 'app/store/actions';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
|
import { buildGenerationTabGraph } from 'features/nodes/util/graph/buildGenerationTabGraph';
|
||||||
|
import { buildGenerationTabSDXLGraph } from 'features/nodes/util/graph/buildGenerationTabSDXLGraph';
|
||||||
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
||||||
import { buildLinearImageToImageGraph } from 'features/nodes/util/graph/buildLinearImageToImageGraph';
|
|
||||||
import { buildLinearSDXLImageToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLImageToImageGraph';
|
|
||||||
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLTextToImageGraph';
|
|
||||||
import { buildLinearTextToImageGraph } from 'features/nodes/util/graph/buildLinearTextToImageGraph';
|
|
||||||
import { queueApi } from 'services/api/endpoints/queue';
|
import { queueApi } from 'services/api/endpoints/queue';
|
||||||
|
|
||||||
export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) => {
|
export const addEnqueueRequestedLinear = (startAppListening: AppStartListening) => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
||||||
enqueueRequested.match(action) && (action.payload.tabName === 'txt2img' || action.payload.tabName === 'img2img'),
|
enqueueRequested.match(action) && action.payload.tabName === 'generation',
|
||||||
effect: async (action, { getState, dispatch }) => {
|
effect: async (action, { getState, dispatch }) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const model = state.generation.model;
|
const model = state.generation.model;
|
||||||
@ -19,17 +17,9 @@ export const addEnqueueRequestedLinear = (startAppListening: AppStartListening)
|
|||||||
let graph;
|
let graph;
|
||||||
|
|
||||||
if (model && model.base === 'sdxl') {
|
if (model && model.base === 'sdxl') {
|
||||||
if (action.payload.tabName === 'txt2img') {
|
graph = await buildGenerationTabSDXLGraph(state);
|
||||||
graph = await buildLinearSDXLTextToImageGraph(state);
|
|
||||||
} else {
|
|
||||||
graph = await buildLinearSDXLImageToImageGraph(state);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (action.payload.tabName === 'txt2img') {
|
graph = await buildGenerationTabGraph(state);
|
||||||
graph = await buildLinearTextToImageGraph(state);
|
|
||||||
} else {
|
|
||||||
graph = await buildLinearImageToImageGraph(state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchConfig = prepareLinearUIBatch(state, graph, prepend);
|
const batchConfig = prepareLinearUIBatch(state, graph, prepend);
|
||||||
|
@ -8,7 +8,7 @@ import type { BatchConfig } from 'services/api/types';
|
|||||||
export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) => {
|
export const addEnqueueRequestedNodes = (startAppListening: AppStartListening) => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
predicate: (action): action is ReturnType<typeof enqueueRequested> =>
|
||||||
enqueueRequested.match(action) && action.payload.tabName === 'nodes',
|
enqueueRequested.match(action) && action.payload.tabName === 'workflows',
|
||||||
effect: async (action, { getState, dispatch }) => {
|
effect: async (action, { getState, dispatch }) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const { nodes, edges } = state.nodes;
|
const { nodes, edges } = state.nodes;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
|
import type { AppDispatch, RootState } from 'app/store/store';
|
||||||
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
import { resetCanvas } from 'features/canvas/store/canvasSlice';
|
||||||
import {
|
import {
|
||||||
controlAdapterImageChanged,
|
controlAdapterImageChanged,
|
||||||
@ -7,6 +8,13 @@ import {
|
|||||||
selectControlAdapterAll,
|
selectControlAdapterAll,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||||
|
import {
|
||||||
|
isControlAdapterLayer,
|
||||||
|
isInitialImageLayer,
|
||||||
|
isIPAdapterLayer,
|
||||||
|
isRegionalGuidanceLayer,
|
||||||
|
layerDeleted,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
import { imageDeletionConfirmed } from 'features/deleteImageModal/store/actions';
|
||||||
import { isModalOpenChanged } from 'features/deleteImageModal/store/slice';
|
import { isModalOpenChanged } from 'features/deleteImageModal/store/slice';
|
||||||
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
|
import { selectListImagesQueryArgs } from 'features/gallery/store/gallerySelectors';
|
||||||
@ -14,12 +22,82 @@ import { imageSelected } from 'features/gallery/store/gallerySlice';
|
|||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { isImageFieldInputInstance } from 'features/nodes/types/field';
|
import { isImageFieldInputInstance } from 'features/nodes/types/field';
|
||||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
|
||||||
import { clamp, forEach } from 'lodash-es';
|
import { clamp, forEach } from 'lodash-es';
|
||||||
import { api } from 'services/api';
|
import { api } from 'services/api';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
import type { ImageDTO } from 'services/api/types';
|
||||||
import { imagesSelectors } from 'services/api/util';
|
import { imagesSelectors } from 'services/api/util';
|
||||||
|
|
||||||
|
const deleteNodesImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||||
|
state.nodes.nodes.forEach((node) => {
|
||||||
|
if (!isInvocationNode(node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
forEach(node.data.inputs, (input) => {
|
||||||
|
if (isImageFieldInputInstance(input) && input.value?.image_name === imageDTO.image_name) {
|
||||||
|
dispatch(
|
||||||
|
fieldImageValueChanged({
|
||||||
|
nodeId: node.data.id,
|
||||||
|
fieldName: input.name,
|
||||||
|
value: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteControlAdapterImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||||
|
forEach(selectControlAdapterAll(state.controlAdapters), (ca) => {
|
||||||
|
if (
|
||||||
|
ca.controlImage === imageDTO.image_name ||
|
||||||
|
(isControlNetOrT2IAdapter(ca) && ca.processedControlImage === imageDTO.image_name)
|
||||||
|
) {
|
||||||
|
dispatch(
|
||||||
|
controlAdapterImageChanged({
|
||||||
|
id: ca.id,
|
||||||
|
controlImage: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
dispatch(
|
||||||
|
controlAdapterProcessedImageChanged({
|
||||||
|
id: ca.id,
|
||||||
|
processedControlImage: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteControlLayerImages = (state: RootState, dispatch: AppDispatch, imageDTO: ImageDTO) => {
|
||||||
|
state.controlLayers.present.layers.forEach((l) => {
|
||||||
|
if (isRegionalGuidanceLayer(l)) {
|
||||||
|
if (l.ipAdapters.some((ipa) => ipa.image?.imageName === imageDTO.image_name)) {
|
||||||
|
dispatch(layerDeleted(l.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isControlAdapterLayer(l)) {
|
||||||
|
if (
|
||||||
|
l.controlAdapter.image?.imageName === imageDTO.image_name ||
|
||||||
|
l.controlAdapter.processedImage?.imageName === imageDTO.image_name
|
||||||
|
) {
|
||||||
|
dispatch(layerDeleted(l.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isIPAdapterLayer(l)) {
|
||||||
|
if (l.ipAdapter.image?.imageName === imageDTO.image_name) {
|
||||||
|
dispatch(layerDeleted(l.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isInitialImageLayer(l)) {
|
||||||
|
if (l.image?.imageName === imageDTO.image_name) {
|
||||||
|
dispatch(layerDeleted(l.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const addRequestedSingleImageDeletionListener = (startAppListening: AppStartListening) => {
|
export const addRequestedSingleImageDeletionListener = (startAppListening: AppStartListening) => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
actionCreator: imageDeletionConfirmed,
|
actionCreator: imageDeletionConfirmed,
|
||||||
@ -73,50 +151,9 @@ export const addRequestedSingleImageDeletionListener = (startAppListening: AppSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageDTOs.forEach((imageDTO) => {
|
imageDTOs.forEach((imageDTO) => {
|
||||||
// reset init image if we deleted it
|
deleteControlAdapterImages(state, dispatch, imageDTO);
|
||||||
if (getState().generation.initialImage?.imageName === imageDTO.image_name) {
|
deleteNodesImages(state, dispatch, imageDTO);
|
||||||
dispatch(clearInitialImage());
|
deleteControlLayerImages(state, dispatch, imageDTO);
|
||||||
}
|
|
||||||
|
|
||||||
// reset control adapters that use the deleted images
|
|
||||||
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
|
|
||||||
if (
|
|
||||||
ca.controlImage === imageDTO.image_name ||
|
|
||||||
(isControlNetOrT2IAdapter(ca) && ca.processedControlImage === imageDTO.image_name)
|
|
||||||
) {
|
|
||||||
dispatch(
|
|
||||||
controlAdapterImageChanged({
|
|
||||||
id: ca.id,
|
|
||||||
controlImage: null,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
controlAdapterProcessedImageChanged({
|
|
||||||
id: ca.id,
|
|
||||||
processedControlImage: null,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// reset nodes that use the deleted images
|
|
||||||
getState().nodes.nodes.forEach((node) => {
|
|
||||||
if (!isInvocationNode(node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
forEach(node.data.inputs, (input) => {
|
|
||||||
if (isImageFieldInputInstance(input) && input.value?.image_name === imageDTO.image_name) {
|
|
||||||
dispatch(
|
|
||||||
fieldImageValueChanged({
|
|
||||||
nodeId: node.data.id,
|
|
||||||
fieldName: input.name,
|
|
||||||
value: undefined,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete from server
|
// Delete from server
|
||||||
@ -168,50 +205,9 @@ export const addRequestedSingleImageDeletionListener = (startAppListening: AppSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageDTOs.forEach((imageDTO) => {
|
imageDTOs.forEach((imageDTO) => {
|
||||||
// reset init image if we deleted it
|
deleteControlAdapterImages(state, dispatch, imageDTO);
|
||||||
if (getState().generation.initialImage?.imageName === imageDTO.image_name) {
|
deleteNodesImages(state, dispatch, imageDTO);
|
||||||
dispatch(clearInitialImage());
|
deleteControlLayerImages(state, dispatch, imageDTO);
|
||||||
}
|
|
||||||
|
|
||||||
// reset control adapters that use the deleted images
|
|
||||||
forEach(selectControlAdapterAll(getState().controlAdapters), (ca) => {
|
|
||||||
if (
|
|
||||||
ca.controlImage === imageDTO.image_name ||
|
|
||||||
(isControlNetOrT2IAdapter(ca) && ca.processedControlImage === imageDTO.image_name)
|
|
||||||
) {
|
|
||||||
dispatch(
|
|
||||||
controlAdapterImageChanged({
|
|
||||||
id: ca.id,
|
|
||||||
controlImage: null,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
dispatch(
|
|
||||||
controlAdapterProcessedImageChanged({
|
|
||||||
id: ca.id,
|
|
||||||
processedControlImage: null,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// reset nodes that use the deleted images
|
|
||||||
getState().nodes.nodes.forEach((node) => {
|
|
||||||
if (!isInvocationNode(node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
forEach(node.data.inputs, (input) => {
|
|
||||||
if (isImageFieldInputInstance(input) && input.value?.image_name === imageDTO.image_name) {
|
|
||||||
dispatch(
|
|
||||||
fieldImageValueChanged({
|
|
||||||
nodeId: node.data.id,
|
|
||||||
fieldName: input.name,
|
|
||||||
value: undefined,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -7,10 +7,16 @@ import {
|
|||||||
controlAdapterImageChanged,
|
controlAdapterImageChanged,
|
||||||
controlAdapterIsEnabledChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
|
import {
|
||||||
|
caLayerImageChanged,
|
||||||
|
iiLayerImageChanged,
|
||||||
|
ipaLayerImageChanged,
|
||||||
|
rgLayerIPAdapterImageChanged,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { initialImageChanged, selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { imagesApi } from 'services/api/endpoints/images';
|
import { imagesApi } from 'services/api/endpoints/images';
|
||||||
|
|
||||||
export const dndDropped = createAction<{
|
export const dndDropped = createAction<{
|
||||||
@ -47,18 +53,6 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Image dropped on initial image
|
|
||||||
*/
|
|
||||||
if (
|
|
||||||
overData.actionType === 'SET_INITIAL_IMAGE' &&
|
|
||||||
activeData.payloadType === 'IMAGE_DTO' &&
|
|
||||||
activeData.payload.imageDTO
|
|
||||||
) {
|
|
||||||
dispatch(initialImageChanged(activeData.payload.imageDTO));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image dropped on ControlNet
|
* Image dropped on ControlNet
|
||||||
*/
|
*/
|
||||||
@ -71,7 +65,7 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
dispatch(
|
dispatch(
|
||||||
controlAdapterImageChanged({
|
controlAdapterImageChanged({
|
||||||
id,
|
id,
|
||||||
controlImage: activeData.payload.imageDTO,
|
controlImage: activeData.payload.imageDTO.image_name,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -83,6 +77,79 @@ export const addImageDroppedListener = (startAppListening: AppStartListening) =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image dropped on Control Adapter Layer
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
overData.actionType === 'SET_CA_LAYER_IMAGE' &&
|
||||||
|
activeData.payloadType === 'IMAGE_DTO' &&
|
||||||
|
activeData.payload.imageDTO
|
||||||
|
) {
|
||||||
|
const { layerId } = overData.context;
|
||||||
|
dispatch(
|
||||||
|
caLayerImageChanged({
|
||||||
|
layerId,
|
||||||
|
imageDTO: activeData.payload.imageDTO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image dropped on IP Adapter Layer
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
overData.actionType === 'SET_IPA_LAYER_IMAGE' &&
|
||||||
|
activeData.payloadType === 'IMAGE_DTO' &&
|
||||||
|
activeData.payload.imageDTO
|
||||||
|
) {
|
||||||
|
const { layerId } = overData.context;
|
||||||
|
dispatch(
|
||||||
|
ipaLayerImageChanged({
|
||||||
|
layerId,
|
||||||
|
imageDTO: activeData.payload.imageDTO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image dropped on RG Layer IP Adapter
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
overData.actionType === 'SET_RG_LAYER_IP_ADAPTER_IMAGE' &&
|
||||||
|
activeData.payloadType === 'IMAGE_DTO' &&
|
||||||
|
activeData.payload.imageDTO
|
||||||
|
) {
|
||||||
|
const { layerId, ipAdapterId } = overData.context;
|
||||||
|
dispatch(
|
||||||
|
rgLayerIPAdapterImageChanged({
|
||||||
|
layerId,
|
||||||
|
ipAdapterId,
|
||||||
|
imageDTO: activeData.payload.imageDTO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Image dropped on II Layer Image
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
overData.actionType === 'SET_II_LAYER_IMAGE' &&
|
||||||
|
activeData.payloadType === 'IMAGE_DTO' &&
|
||||||
|
activeData.payload.imageDTO
|
||||||
|
) {
|
||||||
|
const { layerId } = overData.context;
|
||||||
|
dispatch(
|
||||||
|
iiLayerImageChanged({
|
||||||
|
layerId,
|
||||||
|
imageDTO: activeData.payload.imageDTO,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image dropped on Canvas
|
* Image dropped on Canvas
|
||||||
*/
|
*/
|
||||||
|
@ -14,7 +14,6 @@ export const addImageToDeleteSelectedListener = (startAppListening: AppStartList
|
|||||||
|
|
||||||
const isImageInUse =
|
const isImageInUse =
|
||||||
imagesUsage.some((i) => i.isCanvasImage) ||
|
imagesUsage.some((i) => i.isCanvasImage) ||
|
||||||
imagesUsage.some((i) => i.isInitialImage) ||
|
|
||||||
imagesUsage.some((i) => i.isControlImage) ||
|
imagesUsage.some((i) => i.isControlImage) ||
|
||||||
imagesUsage.some((i) => i.isNodesImage);
|
imagesUsage.some((i) => i.isNodesImage);
|
||||||
|
|
||||||
|
@ -6,8 +6,14 @@ import {
|
|||||||
controlAdapterImageChanged,
|
controlAdapterImageChanged,
|
||||||
controlAdapterIsEnabledChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
|
import {
|
||||||
|
caLayerImageChanged,
|
||||||
|
iiLayerImageChanged,
|
||||||
|
ipaLayerImageChanged,
|
||||||
|
rgLayerIPAdapterImageChanged,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
import { initialImageChanged, selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
import { addToast } from 'features/system/store/systemSlice';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { omit } from 'lodash-es';
|
import { omit } from 'lodash-es';
|
||||||
@ -96,7 +102,7 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
dispatch(
|
dispatch(
|
||||||
controlAdapterImageChanged({
|
controlAdapterImageChanged({
|
||||||
id,
|
id,
|
||||||
controlImage: imageDTO,
|
controlImage: imageDTO.image_name,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -108,15 +114,48 @@ export const addImageUploadedFulfilledListener = (startAppListening: AppStartLis
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_INITIAL_IMAGE') {
|
if (postUploadAction?.type === 'SET_CA_LAYER_IMAGE') {
|
||||||
dispatch(initialImageChanged(imageDTO));
|
const { layerId } = postUploadAction;
|
||||||
|
dispatch(caLayerImageChanged({ layerId, imageDTO }));
|
||||||
dispatch(
|
dispatch(
|
||||||
addToast({
|
addToast({
|
||||||
...DEFAULT_UPLOADED_TOAST,
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
description: t('toast.setInitialImage'),
|
description: t('toast.setControlImage'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'SET_IPA_LAYER_IMAGE') {
|
||||||
|
const { layerId } = postUploadAction;
|
||||||
|
dispatch(ipaLayerImageChanged({ layerId, imageDTO }));
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
|
description: t('toast.setControlImage'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'SET_RG_LAYER_IP_ADAPTER_IMAGE') {
|
||||||
|
const { layerId, ipAdapterId } = postUploadAction;
|
||||||
|
dispatch(rgLayerIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
|
description: t('toast.setControlImage'),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postUploadAction?.type === 'SET_II_LAYER_IMAGE') {
|
||||||
|
const { layerId } = postUploadAction;
|
||||||
|
dispatch(iiLayerImageChanged({ layerId, imageDTO }));
|
||||||
|
dispatch(
|
||||||
|
addToast({
|
||||||
|
...DEFAULT_UPLOADED_TOAST,
|
||||||
|
description: t('toast.setControlImage'),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postUploadAction?.type === 'SET_NODES_IMAGE') {
|
if (postUploadAction?.type === 'SET_NODES_IMAGE') {
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
|
||||||
import { initialImageSelected } from 'features/parameters/store/actions';
|
|
||||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
|
||||||
import { addToast } from 'features/system/store/systemSlice';
|
|
||||||
import { makeToast } from 'features/system/util/makeToast';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
|
|
||||||
export const addInitialImageSelectedListener = (startAppListening: AppStartListening) => {
|
|
||||||
startAppListening({
|
|
||||||
actionCreator: initialImageSelected,
|
|
||||||
effect: (action, { dispatch }) => {
|
|
||||||
if (!action.payload) {
|
|
||||||
dispatch(addToast(makeToast({ title: t('toast.imageNotLoadedDesc'), status: 'error' })));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(initialImageChanged(action.payload));
|
|
||||||
dispatch(addToast(makeToast(t('toast.sentToImageToImage'))));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,7 +1,7 @@
|
|||||||
import { logger } from 'app/logging/logger';
|
import { logger } from 'app/logging/logger';
|
||||||
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
import type { AppStartListening } from 'app/store/middleware/listenerMiddleware';
|
||||||
import {
|
import {
|
||||||
controlAdapterModelChanged,
|
controlAdapterIsEnabledChanged,
|
||||||
selectControlAdapterAll,
|
selectControlAdapterAll,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { loraRemoved } from 'features/lora/store/loraSlice';
|
import { loraRemoved } from 'features/lora/store/loraSlice';
|
||||||
@ -54,7 +54,7 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
|
|||||||
// handle incompatible controlnets
|
// handle incompatible controlnets
|
||||||
selectControlAdapterAll(state.controlAdapters).forEach((ca) => {
|
selectControlAdapterAll(state.controlAdapters).forEach((ca) => {
|
||||||
if (ca.model?.base !== newBaseModel) {
|
if (ca.model?.base !== newBaseModel) {
|
||||||
dispatch(controlAdapterModelChanged({ id: ca.id, modelConfig: null }));
|
dispatch(controlAdapterIsEnabledChanged({ id: ca.id, isEnabled: false }));
|
||||||
modelsCleared += 1;
|
modelsCleared += 1;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -96,16 +96,16 @@ export const addSetDefaultSettingsListener = (startAppListening: AppStartListeni
|
|||||||
dispatch(setScheduler(scheduler));
|
dispatch(setScheduler(scheduler));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const setSizeOptions = { updateAspectRatio: true, clamp: true };
|
||||||
if (width) {
|
if (width) {
|
||||||
if (isParameterWidth(width)) {
|
if (isParameterWidth(width)) {
|
||||||
dispatch(widthChanged({ width, updateAspectRatio: true }));
|
dispatch(widthChanged({ width, ...setSizeOptions }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height) {
|
if (height) {
|
||||||
if (isParameterHeight(height)) {
|
if (isParameterHeight(height)) {
|
||||||
dispatch(heightChanged({ height, updateAspectRatio: true }));
|
dispatch(heightChanged({ height, ...setSizeOptions }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,14 +17,10 @@ const accept: Accept = {
|
|||||||
const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (activeTabName) => {
|
const selectPostUploadAction = createMemoizedSelector(activeTabNameSelector, (activeTabName) => {
|
||||||
let postUploadAction: PostUploadAction = { type: 'TOAST' };
|
let postUploadAction: PostUploadAction = { type: 'TOAST' };
|
||||||
|
|
||||||
if (activeTabName === 'unifiedCanvas') {
|
if (activeTabName === 'canvas') {
|
||||||
postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' };
|
postUploadAction = { type: 'SET_CANVAS_INITIAL_IMAGE' };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'img2img') {
|
|
||||||
postUploadAction = { type: 'SET_INITIAL_IMAGE' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return postUploadAction;
|
return postUploadAction;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ export const useGlobalHotkeys = () => {
|
|||||||
useHotkeys(
|
useHotkeys(
|
||||||
'1',
|
'1',
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('txt2img'));
|
dispatch(setActiveTab('generation'));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
@ -75,7 +75,7 @@ export const useGlobalHotkeys = () => {
|
|||||||
useHotkeys(
|
useHotkeys(
|
||||||
'2',
|
'2',
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('img2img'));
|
dispatch(setActiveTab('canvas'));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
@ -83,31 +83,23 @@ export const useGlobalHotkeys = () => {
|
|||||||
useHotkeys(
|
useHotkeys(
|
||||||
'3',
|
'3',
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('unifiedCanvas'));
|
dispatch(setActiveTab('workflows'));
|
||||||
},
|
},
|
||||||
[dispatch]
|
[dispatch]
|
||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
'4',
|
'4',
|
||||||
() => {
|
|
||||||
dispatch(setActiveTab('nodes'));
|
|
||||||
},
|
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
useHotkeys(
|
|
||||||
'5',
|
|
||||||
() => {
|
() => {
|
||||||
if (isModelManagerEnabled) {
|
if (isModelManagerEnabled) {
|
||||||
dispatch(setActiveTab('modelManager'));
|
dispatch(setActiveTab('models'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[dispatch, isModelManagerEnabled]
|
[dispatch, isModelManagerEnabled]
|
||||||
);
|
);
|
||||||
|
|
||||||
useHotkeys(
|
useHotkeys(
|
||||||
isModelManagerEnabled ? '6' : '5',
|
isModelManagerEnabled ? '5' : '4',
|
||||||
() => {
|
() => {
|
||||||
dispatch(setActiveTab('queue'));
|
dispatch(setActiveTab('queue'));
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,7 @@ const selector = createMemoizedSelector(
|
|||||||
activeTabNameSelector,
|
activeTabNameSelector,
|
||||||
],
|
],
|
||||||
(controlAdapters, generation, system, nodes, dynamicPrompts, controlLayers, activeTabName) => {
|
(controlAdapters, generation, system, nodes, dynamicPrompts, controlLayers, activeTabName) => {
|
||||||
const { initialImage, model } = generation;
|
const { model } = generation;
|
||||||
const { positivePrompt } = controlLayers.present;
|
const { positivePrompt } = controlLayers.present;
|
||||||
|
|
||||||
const { isConnected } = system;
|
const { isConnected } = system;
|
||||||
@ -40,11 +40,7 @@ const selector = createMemoizedSelector(
|
|||||||
reasons.push(i18n.t('parameters.invoke.systemDisconnected'));
|
reasons.push(i18n.t('parameters.invoke.systemDisconnected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'img2img' && !initialImage) {
|
if (activeTabName === 'workflows') {
|
||||||
reasons.push(i18n.t('parameters.invoke.noInitialImageSelected'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTabName === 'nodes') {
|
|
||||||
if (nodes.shouldValidateGraph) {
|
if (nodes.shouldValidateGraph) {
|
||||||
if (!nodes.nodes.length) {
|
if (!nodes.nodes.length) {
|
||||||
reasons.push(i18n.t('parameters.invoke.noNodesInGraph'));
|
reasons.push(i18n.t('parameters.invoke.noNodesInGraph'));
|
||||||
@ -97,71 +93,93 @@ const selector = createMemoizedSelector(
|
|||||||
reasons.push(i18n.t('parameters.invoke.noModelSelected'));
|
reasons.push(i18n.t('parameters.invoke.noModelSelected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
let enabledControlAdapters = selectControlAdapterAll(controlAdapters).filter((ca) => ca.isEnabled);
|
if (activeTabName === 'generation') {
|
||||||
|
// Handling for generation tab
|
||||||
if (activeTabName === 'txt2img') {
|
controlLayers.present.layers
|
||||||
// Special handling for control layers on txt2img
|
|
||||||
const enabledControlLayersAdapterIds = controlLayers.present.layers
|
|
||||||
.filter((l) => l.isEnabled)
|
.filter((l) => l.isEnabled)
|
||||||
.flatMap((layer) => {
|
.flatMap((l) => {
|
||||||
if (layer.type === 'regional_guidance_layer') {
|
if (l.type === 'control_adapter_layer') {
|
||||||
return layer.ipAdapterIds;
|
return l.controlAdapter;
|
||||||
|
} else if (l.type === 'ip_adapter_layer') {
|
||||||
|
return l.ipAdapter;
|
||||||
|
} else if (l.type === 'regional_guidance_layer') {
|
||||||
|
return l.ipAdapters;
|
||||||
}
|
}
|
||||||
if (layer.type === 'control_adapter_layer') {
|
return [];
|
||||||
return [layer.controlNetId];
|
})
|
||||||
|
.forEach((ca, i) => {
|
||||||
|
const hasNoModel = !ca.model;
|
||||||
|
const mismatchedModelBase = ca.model?.base !== model?.base;
|
||||||
|
const hasNoImage = !ca.image;
|
||||||
|
const imageNotProcessed =
|
||||||
|
(ca.type === 'controlnet' || ca.type === 't2i_adapter') && !ca.processedImage && ca.processorConfig;
|
||||||
|
|
||||||
|
if (hasNoModel) {
|
||||||
|
reasons.push(
|
||||||
|
i18n.t('parameters.invoke.noModelForControlAdapter', {
|
||||||
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (layer.type === 'ip_adapter_layer') {
|
if (mismatchedModelBase) {
|
||||||
return [layer.ipAdapterId];
|
// This should never happen, just a sanity check
|
||||||
|
reasons.push(
|
||||||
|
i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', {
|
||||||
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (hasNoImage) {
|
||||||
|
reasons.push(
|
||||||
|
i18n.t('parameters.invoke.noControlImageForControlAdapter', {
|
||||||
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (imageNotProcessed) {
|
||||||
|
reasons.push(
|
||||||
|
i18n.t('parameters.invoke.imageNotProcessedForControlAdapter', {
|
||||||
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
enabledControlAdapters = enabledControlAdapters.filter((ca) => enabledControlLayersAdapterIds.includes(ca.id));
|
|
||||||
} else {
|
} else {
|
||||||
const allControlLayerAdapterIds = controlLayers.present.layers.flatMap((layer) => {
|
// Handling for all other tabs
|
||||||
if (layer.type === 'regional_guidance_layer') {
|
selectControlAdapterAll(controlAdapters)
|
||||||
return layer.ipAdapterIds;
|
.filter((ca) => ca.isEnabled)
|
||||||
}
|
.forEach((ca, i) => {
|
||||||
if (layer.type === 'control_adapter_layer') {
|
if (!ca.isEnabled) {
|
||||||
return [layer.controlNetId];
|
return;
|
||||||
}
|
}
|
||||||
if (layer.type === 'ip_adapter_layer') {
|
|
||||||
return [layer.ipAdapterId];
|
if (!ca.model) {
|
||||||
}
|
reasons.push(
|
||||||
});
|
i18n.t('parameters.invoke.noModelForControlAdapter', {
|
||||||
enabledControlAdapters = enabledControlAdapters.filter((ca) => !allControlLayerAdapterIds.includes(ca.id));
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (ca.model.base !== model?.base) {
|
||||||
|
// This should never happen, just a sanity check
|
||||||
|
reasons.push(
|
||||||
|
i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', {
|
||||||
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!ca.controlImage ||
|
||||||
|
(isControlNetOrT2IAdapter(ca) && !ca.processedControlImage && ca.processorType !== 'none')
|
||||||
|
) {
|
||||||
|
reasons.push(
|
||||||
|
i18n.t('parameters.invoke.noControlImageForControlAdapter', {
|
||||||
|
number: i + 1,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
enabledControlAdapters.forEach((ca, i) => {
|
|
||||||
if (!ca.isEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ca.model) {
|
|
||||||
reasons.push(
|
|
||||||
i18n.t('parameters.invoke.noModelForControlAdapter', {
|
|
||||||
number: i + 1,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (ca.model.base !== model?.base) {
|
|
||||||
// This should never happen, just a sanity check
|
|
||||||
reasons.push(
|
|
||||||
i18n.t('parameters.invoke.incompatibleBaseModelForControlAdapter', {
|
|
||||||
number: i + 1,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!ca.controlImage ||
|
|
||||||
(isControlNetOrT2IAdapter(ca) && !ca.processedControlImage && ca.processorType !== 'none')
|
|
||||||
) {
|
|
||||||
reasons.push(
|
|
||||||
i18n.t('parameters.invoke.noControlImageForControlAdapter', {
|
|
||||||
number: i + 1,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { isReady: !reasons.length, reasons };
|
return { isReady: !reasons.length, reasons };
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
} from 'features/canvas/store/canvasSlice';
|
} from 'features/canvas/store/canvasSlice';
|
||||||
import type { CanvasLayer } from 'features/canvas/store/canvasTypes';
|
import type { CanvasLayer } from 'features/canvas/store/canvasTypes';
|
||||||
import { LAYER_NAMES_DICT } from 'features/canvas/store/canvasTypes';
|
import { LAYER_NAMES_DICT } from 'features/canvas/store/canvasTypes';
|
||||||
|
import { ViewerButton } from 'features/gallery/components/ImageViewer/ViewerButton';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -219,97 +220,107 @@ const IAICanvasToolbar = () => {
|
|||||||
const value = useMemo(() => LAYER_NAMES_DICT.filter((o) => o.value === layer)[0], [layer]);
|
const value = useMemo(() => LAYER_NAMES_DICT.filter((o) => o.value === layer)[0], [layer]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex alignItems="center" gap={2} flexWrap="wrap">
|
<Flex w="full" gap={2} alignItems="center">
|
||||||
<Tooltip label={`${t('unifiedCanvas.layer')} (Q)`}>
|
<Flex flex={1} justifyContent="center">
|
||||||
<FormControl isDisabled={isStaging} w="5rem">
|
<Flex gap={2} marginInlineEnd="auto" />
|
||||||
<Combobox value={value} options={LAYER_NAMES_DICT} onChange={handleChangeLayer} />
|
</Flex>
|
||||||
</FormControl>
|
<Flex flex={1} gap={2} justifyContent="center">
|
||||||
</Tooltip>
|
<Tooltip label={`${t('unifiedCanvas.layer')} (Q)`}>
|
||||||
|
<FormControl isDisabled={isStaging} w="5rem">
|
||||||
|
<Combobox value={value} options={LAYER_NAMES_DICT} onChange={handleChangeLayer} />
|
||||||
|
</FormControl>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<IAICanvasMaskOptions />
|
<IAICanvasMaskOptions />
|
||||||
<IAICanvasToolChooserOptions />
|
<IAICanvasToolChooserOptions />
|
||||||
|
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<IconButton
|
|
||||||
aria-label={`${t('unifiedCanvas.move')} (V)`}
|
|
||||||
tooltip={`${t('unifiedCanvas.move')} (V)`}
|
|
||||||
icon={<PiHandGrabbingBold />}
|
|
||||||
isChecked={tool === 'move' || isStaging}
|
|
||||||
onClick={handleSelectMoveTool}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
aria-label={`${shouldShowBoundingBox ? t('unifiedCanvas.hideBoundingBox') : t('unifiedCanvas.showBoundingBox')} (Shift + H)`}
|
|
||||||
tooltip={`${shouldShowBoundingBox ? t('unifiedCanvas.hideBoundingBox') : t('unifiedCanvas.showBoundingBox')} (Shift + H)`}
|
|
||||||
icon={shouldShowBoundingBox ? <PiEyeBold /> : <PiEyeSlashBold />}
|
|
||||||
onClick={handleSetShouldShowBoundingBox}
|
|
||||||
isDisabled={isStaging}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
aria-label={`${t('unifiedCanvas.resetView')} (R)`}
|
|
||||||
tooltip={`${t('unifiedCanvas.resetView')} (R)`}
|
|
||||||
icon={<PiCrosshairSimpleBold />}
|
|
||||||
onClick={handleClickResetCanvasView}
|
|
||||||
/>
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
<ButtonGroup>
|
|
||||||
<IconButton
|
|
||||||
aria-label={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
|
|
||||||
tooltip={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
|
|
||||||
icon={<PiStackBold />}
|
|
||||||
onClick={handleMergeVisible}
|
|
||||||
isDisabled={isStaging}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
aria-label={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
|
||||||
tooltip={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
|
||||||
icon={<PiFloppyDiskBold />}
|
|
||||||
onClick={handleSaveToGallery}
|
|
||||||
isDisabled={isStaging}
|
|
||||||
/>
|
|
||||||
{isClipboardAPIAvailable && (
|
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
|
aria-label={`${t('unifiedCanvas.move')} (V)`}
|
||||||
tooltip={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
|
tooltip={`${t('unifiedCanvas.move')} (V)`}
|
||||||
icon={<PiCopyBold />}
|
icon={<PiHandGrabbingBold />}
|
||||||
onClick={handleCopyImageToClipboard}
|
isChecked={tool === 'move' || isStaging}
|
||||||
|
onClick={handleSelectMoveTool}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
aria-label={`${shouldShowBoundingBox ? t('unifiedCanvas.hideBoundingBox') : t('unifiedCanvas.showBoundingBox')} (Shift + H)`}
|
||||||
|
tooltip={`${shouldShowBoundingBox ? t('unifiedCanvas.hideBoundingBox') : t('unifiedCanvas.showBoundingBox')} (Shift + H)`}
|
||||||
|
icon={shouldShowBoundingBox ? <PiEyeBold /> : <PiEyeSlashBold />}
|
||||||
|
onClick={handleSetShouldShowBoundingBox}
|
||||||
isDisabled={isStaging}
|
isDisabled={isStaging}
|
||||||
/>
|
/>
|
||||||
)}
|
<IconButton
|
||||||
<IconButton
|
aria-label={`${t('unifiedCanvas.resetView')} (R)`}
|
||||||
aria-label={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
|
tooltip={`${t('unifiedCanvas.resetView')} (R)`}
|
||||||
tooltip={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
|
icon={<PiCrosshairSimpleBold />}
|
||||||
icon={<PiDownloadSimpleBold />}
|
onClick={handleClickResetCanvasView}
|
||||||
onClick={handleDownloadAsImage}
|
/>
|
||||||
isDisabled={isStaging}
|
</ButtonGroup>
|
||||||
/>
|
|
||||||
</ButtonGroup>
|
|
||||||
<ButtonGroup>
|
|
||||||
<IAICanvasUndoButton />
|
|
||||||
<IAICanvasRedoButton />
|
|
||||||
</ButtonGroup>
|
|
||||||
|
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label={`${t('common.upload')}`}
|
aria-label={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
|
||||||
tooltip={`${t('common.upload')}`}
|
tooltip={`${t('unifiedCanvas.mergeVisible')} (Shift+M)`}
|
||||||
icon={<PiUploadSimpleBold />}
|
icon={<PiStackBold />}
|
||||||
isDisabled={isStaging}
|
onClick={handleMergeVisible}
|
||||||
{...getUploadButtonProps()}
|
isDisabled={isStaging}
|
||||||
/>
|
/>
|
||||||
<input {...getUploadInputProps()} />
|
<IconButton
|
||||||
<IconButton
|
aria-label={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
||||||
aria-label={`${t('unifiedCanvas.clearCanvas')}`}
|
tooltip={`${t('unifiedCanvas.saveToGallery')} (Shift+S)`}
|
||||||
tooltip={`${t('unifiedCanvas.clearCanvas')}`}
|
icon={<PiFloppyDiskBold />}
|
||||||
icon={<PiTrashSimpleBold />}
|
onClick={handleSaveToGallery}
|
||||||
onClick={handleResetCanvas}
|
isDisabled={isStaging}
|
||||||
colorScheme="error"
|
/>
|
||||||
isDisabled={isStaging}
|
{isClipboardAPIAvailable && (
|
||||||
/>
|
<IconButton
|
||||||
</ButtonGroup>
|
aria-label={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
|
||||||
<ButtonGroup>
|
tooltip={`${t('unifiedCanvas.copyToClipboard')} (Cmd/Ctrl+C)`}
|
||||||
<IAICanvasSettingsButtonPopover />
|
icon={<PiCopyBold />}
|
||||||
</ButtonGroup>
|
onClick={handleCopyImageToClipboard}
|
||||||
|
isDisabled={isStaging}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<IconButton
|
||||||
|
aria-label={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
|
||||||
|
tooltip={`${t('unifiedCanvas.downloadAsImage')} (Shift+D)`}
|
||||||
|
icon={<PiDownloadSimpleBold />}
|
||||||
|
onClick={handleDownloadAsImage}
|
||||||
|
isDisabled={isStaging}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
<ButtonGroup>
|
||||||
|
<IAICanvasUndoButton />
|
||||||
|
<IAICanvasRedoButton />
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
|
<ButtonGroup>
|
||||||
|
<IconButton
|
||||||
|
aria-label={`${t('common.upload')}`}
|
||||||
|
tooltip={`${t('common.upload')}`}
|
||||||
|
icon={<PiUploadSimpleBold />}
|
||||||
|
isDisabled={isStaging}
|
||||||
|
{...getUploadButtonProps()}
|
||||||
|
/>
|
||||||
|
<input {...getUploadInputProps()} />
|
||||||
|
<IconButton
|
||||||
|
aria-label={`${t('unifiedCanvas.clearCanvas')}`}
|
||||||
|
tooltip={`${t('unifiedCanvas.clearCanvas')}`}
|
||||||
|
icon={<PiTrashSimpleBold />}
|
||||||
|
onClick={handleResetCanvas}
|
||||||
|
colorScheme="error"
|
||||||
|
isDisabled={isStaging}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
<ButtonGroup>
|
||||||
|
<IAICanvasSettingsButtonPopover />
|
||||||
|
</ButtonGroup>
|
||||||
|
</Flex>
|
||||||
|
<Flex flex={1} justifyContent="center">
|
||||||
|
<Flex gap={2} marginInlineStart="auto">
|
||||||
|
<ViewerButton />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -75,7 +75,7 @@ const useInpaintingCanvasHotkeys = () => {
|
|||||||
|
|
||||||
const onKeyDown = useCallback(
|
const onKeyDown = useCallback(
|
||||||
(e: KeyboardEvent) => {
|
(e: KeyboardEvent) => {
|
||||||
if (e.repeat || e.key !== ' ' || isInteractiveTarget(e.target) || activeTabName !== 'unifiedCanvas') {
|
if (e.repeat || e.key !== ' ' || isInteractiveTarget(e.target) || activeTabName !== 'canvas') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($toolStash.get() || $tool.get() === 'move') {
|
if ($toolStash.get() || $tool.get() === 'move') {
|
||||||
@ -90,7 +90,7 @@ const useInpaintingCanvasHotkeys = () => {
|
|||||||
);
|
);
|
||||||
const onKeyUp = useCallback(
|
const onKeyUp = useCallback(
|
||||||
(e: KeyboardEvent) => {
|
(e: KeyboardEvent) => {
|
||||||
if (e.repeat || e.key !== ' ' || isInteractiveTarget(e.target) || activeTabName !== 'unifiedCanvas') {
|
if (e.repeat || e.key !== ' ' || isInteractiveTarget(e.target) || activeTabName !== 'canvas') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!$toolStash.get() || $tool.get() !== 'move') {
|
if (!$toolStash.get() || $tool.get() !== 'move') {
|
||||||
|
@ -8,6 +8,7 @@ import calculateScale from 'features/canvas/util/calculateScale';
|
|||||||
import { STAGE_PADDING_PERCENTAGE } from 'features/canvas/util/constants';
|
import { STAGE_PADDING_PERCENTAGE } from 'features/canvas/util/constants';
|
||||||
import floorCoordinates from 'features/canvas/util/floorCoordinates';
|
import floorCoordinates from 'features/canvas/util/floorCoordinates';
|
||||||
import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundingBoxDimensions';
|
import getScaledBoundingBoxDimensions from 'features/canvas/util/getScaledBoundingBoxDimensions';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
import { initialAspectRatioState } from 'features/parameters/components/ImageSize/constants';
|
||||||
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
import type { AspectRatioState } from 'features/parameters/components/ImageSize/types';
|
||||||
import { modelChanged } from 'features/parameters/store/generationSlice';
|
import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||||
@ -588,8 +589,9 @@ export const canvasSlice = createSlice({
|
|||||||
},
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(modelChanged, (state, action) => {
|
builder.addCase(modelChanged, (state, action) => {
|
||||||
if (action.meta.previousModel?.base === action.payload?.base) {
|
const newModel = action.payload;
|
||||||
// The base model hasn't changed, we don't need to optimize the size
|
if (!newModel || action.meta.previousModel?.base === newModel.base) {
|
||||||
|
// Model was cleared or the base didn't change
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const optimalDimension = getOptimalDimension(action.payload);
|
const optimalDimension = getOptimalDimension(action.payload);
|
||||||
@ -597,14 +599,8 @@ export const canvasSlice = createSlice({
|
|||||||
if (getIsSizeOptimal(width, height, optimalDimension)) {
|
if (getIsSizeOptimal(width, height, optimalDimension)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setBoundingBoxDimensionsReducer(
|
const newSize = calculateNewSize(state.aspectRatio.value, optimalDimension * optimalDimension);
|
||||||
state,
|
setBoundingBoxDimensionsReducer(state, newSize, optimalDimension);
|
||||||
{
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
},
|
|
||||||
optimalDimension
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addCase(socketQueueItemStatusChanged, (state, action) => {
|
builder.addCase(socketQueueItemStatusChanged, (state, action) => {
|
||||||
|
@ -76,7 +76,7 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
|
|||||||
<Box minW={0} w="full" transitionProperty="common" transitionDuration="0.1s">
|
<Box minW={0} w="full" transitionProperty="common" transitionDuration="0.1s">
|
||||||
<ParamControlAdapterModel id={id} />
|
<ParamControlAdapterModel id={id} />
|
||||||
</Box>
|
</Box>
|
||||||
{activeTabName === 'unifiedCanvas' && <ControlNetCanvasImageImports id={id} />}
|
{activeTabName === 'canvas' && <ControlNetCanvasImageImports id={id} />}
|
||||||
<IconButton
|
<IconButton
|
||||||
size="sm"
|
size="sm"
|
||||||
tooltip={t('controlnet.duplicate')}
|
tooltip={t('controlnet.duplicate')}
|
||||||
@ -113,7 +113,7 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
|
|||||||
<Flex w="full" flexDir="column" gap={4}>
|
<Flex w="full" flexDir="column" gap={4}>
|
||||||
<Flex gap={8} w="full" alignItems="center">
|
<Flex gap={8} w="full" alignItems="center">
|
||||||
<Flex flexDir="column" gap={4} h={controlAdapterType === 'ip_adapter' ? 40 : 32} w="full">
|
<Flex flexDir="column" gap={4} h={controlAdapterType === 'ip_adapter' ? 40 : 32} w="full">
|
||||||
{controlAdapterType === 'ip_adapter' && <ParamControlAdapterIPMethod id={id} />}
|
<ParamControlAdapterIPMethod id={id} />
|
||||||
<ParamControlAdapterWeight id={id} />
|
<ParamControlAdapterWeight id={id} />
|
||||||
<ParamControlAdapterBeginEnd id={id} />
|
<ParamControlAdapterBeginEnd id={id} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -93,15 +93,16 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTabName === 'unifiedCanvas') {
|
if (activeTabName === 'canvas') {
|
||||||
dispatch(setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension));
|
dispatch(setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension));
|
||||||
} else {
|
} else {
|
||||||
|
const options = { updateAspectRatio: true, clamp: true };
|
||||||
const { width, height } = calculateNewSize(
|
const { width, height } = calculateNewSize(
|
||||||
controlImage.width / controlImage.height,
|
controlImage.width / controlImage.height,
|
||||||
optimalDimension * optimalDimension
|
optimalDimension * optimalDimension
|
||||||
);
|
);
|
||||||
dispatch(widthChanged({ width, updateAspectRatio: true }));
|
dispatch(widthChanged({ width, ...options }));
|
||||||
dispatch(heightChanged({ height, updateAspectRatio: true }));
|
dispatch(heightChanged({ height, ...options }));
|
||||||
}
|
}
|
||||||
}, [controlImage, activeTabName, dispatch, optimalDimension]);
|
}, [controlImage, activeTabName, dispatch, optimalDimension]);
|
||||||
|
|
||||||
|
@ -46,9 +46,13 @@ const ParamControlAdapterIPMethod = ({ id }: Props) => {
|
|||||||
|
|
||||||
const value = useMemo(() => options.find((o) => o.value === method), [options, method]);
|
const value = useMemo(() => options.find((o) => o.value === method), [options, method]);
|
||||||
|
|
||||||
|
if (!method) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<InformationalPopover feature="ipAdapterMethod">
|
<InformationalPopover feature="controlNetResizeMode">
|
||||||
<FormLabel>{t('controlnet.ipAdapterMethod')}</FormLabel>
|
<FormLabel>{t('controlnet.ipAdapterMethod')}</FormLabel>
|
||||||
</InformationalPopover>
|
</InformationalPopover>
|
||||||
<Combobox value={value} options={options} isDisabled={!isEnabled} onChange={handleIPMethodChanged} />
|
<Combobox value={value} options={options} isDisabled={!isEnabled} onChange={handleIPMethodChanged} />
|
||||||
|
@ -102,9 +102,13 @@ const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={4}>
|
<Flex sx={{ gap: 2 }}>
|
||||||
<Tooltip label={selectedModel?.description}>
|
<Tooltip label={selectedModel?.description}>
|
||||||
<FormControl isDisabled={!isEnabled} isInvalid={!value || mainModel?.base !== modelConfig?.base} w="full">
|
<FormControl
|
||||||
|
isDisabled={!isEnabled}
|
||||||
|
isInvalid={!value || mainModel?.base !== modelConfig?.base}
|
||||||
|
sx={{ width: '100%' }}
|
||||||
|
>
|
||||||
<Combobox
|
<Combobox
|
||||||
options={options}
|
options={options}
|
||||||
placeholder={t('controlnet.selectModel')}
|
placeholder={t('controlnet.selectModel')}
|
||||||
@ -118,8 +122,7 @@ const ParamControlAdapterModel = ({ id }: ParamControlAdapterModelProps) => {
|
|||||||
<FormControl
|
<FormControl
|
||||||
isDisabled={!isEnabled}
|
isDisabled={!isEnabled}
|
||||||
isInvalid={!value || mainModel?.base !== modelConfig?.base}
|
isInvalid={!value || mainModel?.base !== modelConfig?.base}
|
||||||
width="max-content"
|
sx={{ width: 'max-content', minWidth: 28 }}
|
||||||
minWidth={28}
|
|
||||||
>
|
>
|
||||||
<Combobox
|
<Combobox
|
||||||
options={clipVisionOptions}
|
options={clipVisionOptions}
|
||||||
|
@ -5,15 +5,15 @@ import {
|
|||||||
selectControlAdaptersSlice,
|
selectControlAdaptersSlice,
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
export const useControlAdapterIPMethod = (id: string) => {
|
export const useControlAdapterIPMethod = (id: string) => {
|
||||||
const selector = useMemo(
|
const selector = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
createMemoizedSelector(selectControlAdaptersSlice, (controlAdapters) => {
|
||||||
const ca = selectControlAdapterById(controlAdapters, id);
|
const cn = selectControlAdapterById(controlAdapters, id);
|
||||||
assert(ca?.type === 'ip_adapter');
|
if (cn && cn?.type === 'ip_adapter') {
|
||||||
return ca.method;
|
return cn.method;
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
[id]
|
[id]
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { PayloadAction, Update } from '@reduxjs/toolkit';
|
import type { PayloadAction, Update } from '@reduxjs/toolkit';
|
||||||
import { createEntityAdapter, createSlice, isAnyOf } from '@reduxjs/toolkit';
|
import { createEntityAdapter, createSlice } from '@reduxjs/toolkit';
|
||||||
import { getSelectorsOptions } from 'app/store/createMemoizedSelector';
|
import { getSelectorsOptions } from 'app/store/createMemoizedSelector';
|
||||||
import type { PersistConfig, RootState } from 'app/store/store';
|
import type { PersistConfig, RootState } from 'app/store/store';
|
||||||
import { deepClone } from 'common/util/deepClone';
|
import { deepClone } from 'common/util/deepClone';
|
||||||
@ -7,7 +7,7 @@ import { buildControlAdapter } from 'features/controlAdapters/util/buildControlA
|
|||||||
import { buildControlAdapterProcessor } from 'features/controlAdapters/util/buildControlAdapterProcessor';
|
import { buildControlAdapterProcessor } from 'features/controlAdapters/util/buildControlAdapterProcessor';
|
||||||
import { zModelIdentifierField } from 'features/nodes/types/common';
|
import { zModelIdentifierField } from 'features/nodes/types/common';
|
||||||
import { merge, uniq } from 'lodash-es';
|
import { merge, uniq } from 'lodash-es';
|
||||||
import type { ControlNetModelConfig, ImageDTO, IPAdapterModelConfig, T2IAdapterModelConfig } from 'services/api/types';
|
import type { ControlNetModelConfig, IPAdapterModelConfig, T2IAdapterModelConfig } from 'services/api/types';
|
||||||
import { socketInvocationError } from 'services/events/actions';
|
import { socketInvocationError } from 'services/events/actions';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
@ -134,46 +134,23 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
const { id, isEnabled } = action.payload;
|
const { id, isEnabled } = action.payload;
|
||||||
caAdapter.updateOne(state, { id, changes: { isEnabled } });
|
caAdapter.updateOne(state, { id, changes: { isEnabled } });
|
||||||
},
|
},
|
||||||
controlAdapterImageChanged: (state, action: PayloadAction<{ id: string; controlImage: ImageDTO | null }>) => {
|
controlAdapterImageChanged: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{
|
||||||
|
id: string;
|
||||||
|
controlImage: string | null;
|
||||||
|
}>
|
||||||
|
) => {
|
||||||
const { id, controlImage } = action.payload;
|
const { id, controlImage } = action.payload;
|
||||||
const ca = selectControlAdapterById(state, id);
|
const ca = selectControlAdapterById(state, id);
|
||||||
if (!ca) {
|
if (!ca) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isControlNetOrT2IAdapter(ca)) {
|
caAdapter.updateOne(state, {
|
||||||
if (controlImage) {
|
id,
|
||||||
const { image_name, width, height } = controlImage;
|
changes: { controlImage, processedControlImage: null },
|
||||||
const processorNode = deepClone(ca.processorNode);
|
});
|
||||||
const minDim = Math.min(controlImage.width, controlImage.height);
|
|
||||||
if ('detect_resolution' in processorNode) {
|
|
||||||
processorNode.detect_resolution = minDim;
|
|
||||||
}
|
|
||||||
if ('image_resolution' in processorNode) {
|
|
||||||
processorNode.image_resolution = minDim;
|
|
||||||
}
|
|
||||||
if ('resolution' in processorNode) {
|
|
||||||
processorNode.resolution = minDim;
|
|
||||||
}
|
|
||||||
caAdapter.updateOne(state, {
|
|
||||||
id,
|
|
||||||
changes: {
|
|
||||||
processorNode,
|
|
||||||
controlImage: image_name,
|
|
||||||
controlImageDimensions: { width, height },
|
|
||||||
processedControlImage: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
caAdapter.updateOne(state, {
|
|
||||||
id,
|
|
||||||
changes: { controlImage: null, controlImageDimensions: null, processedControlImage: null },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ip adapter
|
|
||||||
caAdapter.updateOne(state, { id, changes: { controlImage: controlImage?.image_name ?? null } });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controlImage !== null && isControlNetOrT2IAdapter(ca) && ca.processorType !== 'none') {
|
if (controlImage !== null && isControlNetOrT2IAdapter(ca) && ca.processorType !== 'none') {
|
||||||
state.pendingControlImages.push(id);
|
state.pendingControlImages.push(id);
|
||||||
@ -183,7 +160,7 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
state,
|
state,
|
||||||
action: PayloadAction<{
|
action: PayloadAction<{
|
||||||
id: string;
|
id: string;
|
||||||
processedControlImage: ImageDTO | null;
|
processedControlImage: string | null;
|
||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
const { id, processedControlImage } = action.payload;
|
const { id, processedControlImage } = action.payload;
|
||||||
@ -196,24 +173,12 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (processedControlImage) {
|
caAdapter.updateOne(state, {
|
||||||
const { image_name, width, height } = processedControlImage;
|
id,
|
||||||
caAdapter.updateOne(state, {
|
changes: {
|
||||||
id,
|
processedControlImage,
|
||||||
changes: {
|
},
|
||||||
processedControlImage: image_name,
|
});
|
||||||
processedControlImageDimensions: { width, height },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
caAdapter.updateOne(state, {
|
|
||||||
id,
|
|
||||||
changes: {
|
|
||||||
processedControlImage: null,
|
|
||||||
processedControlImageDimensions: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
state.pendingControlImages = state.pendingControlImages.filter((pendingId) => pendingId !== id);
|
state.pendingControlImages = state.pendingControlImages.filter((pendingId) => pendingId !== id);
|
||||||
},
|
},
|
||||||
@ -227,7 +192,7 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
state,
|
state,
|
||||||
action: PayloadAction<{
|
action: PayloadAction<{
|
||||||
id: string;
|
id: string;
|
||||||
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | IPAdapterModelConfig | null;
|
modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | IPAdapterModelConfig;
|
||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
const { id, modelConfig } = action.payload;
|
const { id, modelConfig } = action.payload;
|
||||||
@ -236,11 +201,6 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelConfig === null) {
|
|
||||||
caAdapter.updateOne(state, { id, changes: { model: null } });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const model = zModelIdentifierField.parse(modelConfig);
|
const model = zModelIdentifierField.parse(modelConfig);
|
||||||
|
|
||||||
if (!isControlNetOrT2IAdapter(cn)) {
|
if (!isControlNetOrT2IAdapter(cn)) {
|
||||||
@ -248,36 +208,22 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const update: Update<ControlNetConfig | T2IAdapterConfig, string> = {
|
||||||
|
id,
|
||||||
|
changes: { model, shouldAutoConfig: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
update.changes.processedControlImage = null;
|
||||||
|
|
||||||
if (modelConfig.type === 'ip_adapter') {
|
if (modelConfig.type === 'ip_adapter') {
|
||||||
// should never happen...
|
// should never happen...
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We always update the model
|
|
||||||
const update: Update<ControlNetConfig | T2IAdapterConfig, string> = { id, changes: { model } };
|
|
||||||
|
|
||||||
// Build the default processor for this model
|
|
||||||
const processor = buildControlAdapterProcessor(modelConfig);
|
const processor = buildControlAdapterProcessor(modelConfig);
|
||||||
if (processor.processorType !== cn.processorNode.type) {
|
update.changes.processorType = processor.processorType;
|
||||||
// If the processor type has changed, update the processor node
|
update.changes.processorNode = processor.processorNode;
|
||||||
update.changes.shouldAutoConfig = true;
|
|
||||||
update.changes.processedControlImage = null;
|
|
||||||
update.changes.processorType = processor.processorType;
|
|
||||||
update.changes.processorNode = processor.processorNode;
|
|
||||||
|
|
||||||
if (cn.controlImageDimensions) {
|
|
||||||
const minDim = Math.min(cn.controlImageDimensions.width, cn.controlImageDimensions.height);
|
|
||||||
if ('detect_resolution' in update.changes.processorNode) {
|
|
||||||
update.changes.processorNode.detect_resolution = minDim;
|
|
||||||
}
|
|
||||||
if ('image_resolution' in update.changes.processorNode) {
|
|
||||||
update.changes.processorNode.image_resolution = minDim;
|
|
||||||
}
|
|
||||||
if ('resolution' in update.changes.processorNode) {
|
|
||||||
update.changes.processorNode.resolution = minDim;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
caAdapter.updateOne(state, update);
|
caAdapter.updateOne(state, update);
|
||||||
},
|
},
|
||||||
controlAdapterWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
controlAdapterWeightChanged: (state, action: PayloadAction<{ id: string; weight: number }>) => {
|
||||||
@ -394,23 +340,8 @@ export const controlAdaptersSlice = createSlice({
|
|||||||
|
|
||||||
if (update.changes.shouldAutoConfig && modelConfig) {
|
if (update.changes.shouldAutoConfig && modelConfig) {
|
||||||
const processor = buildControlAdapterProcessor(modelConfig);
|
const processor = buildControlAdapterProcessor(modelConfig);
|
||||||
if (processor.processorType !== cn.processorNode.type) {
|
update.changes.processorType = processor.processorType;
|
||||||
update.changes.processorType = processor.processorType;
|
update.changes.processorNode = processor.processorNode;
|
||||||
update.changes.processorNode = processor.processorNode;
|
|
||||||
// Copy image resolution settings, urgh
|
|
||||||
if (cn.controlImageDimensions) {
|
|
||||||
const minDim = Math.min(cn.controlImageDimensions.width, cn.controlImageDimensions.height);
|
|
||||||
if ('detect_resolution' in update.changes.processorNode) {
|
|
||||||
update.changes.processorNode.detect_resolution = minDim;
|
|
||||||
}
|
|
||||||
if ('image_resolution' in update.changes.processorNode) {
|
|
||||||
update.changes.processorNode.image_resolution = minDim;
|
|
||||||
}
|
|
||||||
if ('resolution' in update.changes.processorNode) {
|
|
||||||
update.changes.processorNode.resolution = minDim;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
caAdapter.updateOne(state, update);
|
caAdapter.updateOne(state, update);
|
||||||
@ -481,8 +412,6 @@ export const {
|
|||||||
t2iAdaptersReset,
|
t2iAdaptersReset,
|
||||||
} = controlAdaptersSlice.actions;
|
} = controlAdaptersSlice.actions;
|
||||||
|
|
||||||
export const isAnyControlAdapterAdded = isAnyOf(controlAdapterAdded, controlAdapterRecalled);
|
|
||||||
|
|
||||||
export const selectControlAdaptersSlice = (state: RootState) => state.controlAdapters;
|
export const selectControlAdaptersSlice = (state: RootState) => state.controlAdapters;
|
||||||
|
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
|
||||||
|
@ -225,9 +225,7 @@ export type ControlNetConfig = {
|
|||||||
controlMode: ControlMode;
|
controlMode: ControlMode;
|
||||||
resizeMode: ResizeMode;
|
resizeMode: ResizeMode;
|
||||||
controlImage: string | null;
|
controlImage: string | null;
|
||||||
controlImageDimensions: { width: number; height: number } | null;
|
|
||||||
processedControlImage: string | null;
|
processedControlImage: string | null;
|
||||||
processedControlImageDimensions: { width: number; height: number } | null;
|
|
||||||
processorType: ControlAdapterProcessorType;
|
processorType: ControlAdapterProcessorType;
|
||||||
processorNode: RequiredControlAdapterProcessorNode;
|
processorNode: RequiredControlAdapterProcessorNode;
|
||||||
shouldAutoConfig: boolean;
|
shouldAutoConfig: boolean;
|
||||||
@ -243,9 +241,7 @@ export type T2IAdapterConfig = {
|
|||||||
endStepPct: number;
|
endStepPct: number;
|
||||||
resizeMode: ResizeMode;
|
resizeMode: ResizeMode;
|
||||||
controlImage: string | null;
|
controlImage: string | null;
|
||||||
controlImageDimensions: { width: number; height: number } | null;
|
|
||||||
processedControlImage: string | null;
|
processedControlImage: string | null;
|
||||||
processedControlImageDimensions: { width: number; height: number } | null;
|
|
||||||
processorType: ControlAdapterProcessorType;
|
processorType: ControlAdapterProcessorType;
|
||||||
processorNode: RequiredControlAdapterProcessorNode;
|
processorNode: RequiredControlAdapterProcessorNode;
|
||||||
shouldAutoConfig: boolean;
|
shouldAutoConfig: boolean;
|
||||||
|
@ -20,9 +20,7 @@ export const initialControlNet: Omit<ControlNetConfig, 'id'> = {
|
|||||||
controlMode: 'balanced',
|
controlMode: 'balanced',
|
||||||
resizeMode: 'just_resize',
|
resizeMode: 'just_resize',
|
||||||
controlImage: null,
|
controlImage: null,
|
||||||
controlImageDimensions: null,
|
|
||||||
processedControlImage: null,
|
processedControlImage: null,
|
||||||
processedControlImageDimensions: null,
|
|
||||||
processorType: 'canny_image_processor',
|
processorType: 'canny_image_processor',
|
||||||
processorNode: CONTROLNET_PROCESSORS.canny_image_processor.buildDefaults() as RequiredCannyImageProcessorInvocation,
|
processorNode: CONTROLNET_PROCESSORS.canny_image_processor.buildDefaults() as RequiredCannyImageProcessorInvocation,
|
||||||
shouldAutoConfig: true,
|
shouldAutoConfig: true,
|
||||||
@ -37,9 +35,7 @@ export const initialT2IAdapter: Omit<T2IAdapterConfig, 'id'> = {
|
|||||||
endStepPct: 1,
|
endStepPct: 1,
|
||||||
resizeMode: 'just_resize',
|
resizeMode: 'just_resize',
|
||||||
controlImage: null,
|
controlImage: null,
|
||||||
controlImageDimensions: null,
|
|
||||||
processedControlImage: null,
|
processedControlImage: null,
|
||||||
processedControlImageDimensions: null,
|
|
||||||
processorType: 'canny_image_processor',
|
processorType: 'canny_image_processor',
|
||||||
processorNode: CONTROLNET_PROCESSORS.canny_image_processor.buildDefaults() as RequiredCannyImageProcessorInvocation,
|
processorNode: CONTROLNET_PROCESSORS.canny_image_processor.buildDefaults() as RequiredCannyImageProcessorInvocation,
|
||||||
shouldAutoConfig: true,
|
shouldAutoConfig: true,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
import { Button, Menu, MenuButton, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||||
import { guidanceLayerAdded } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import { useAddCALayer, useAddIILayer, useAddIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
|
import { rgLayerAdded } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiPlusBold } from 'react-icons/pi';
|
import { PiPlusBold } from 'react-icons/pi';
|
||||||
@ -8,14 +9,11 @@ import { PiPlusBold } from 'react-icons/pi';
|
|||||||
export const AddLayerButton = memo(() => {
|
export const AddLayerButton = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const addRegionalGuidanceLayer = useCallback(() => {
|
const [addCALayer, isAddCALayerDisabled] = useAddCALayer();
|
||||||
dispatch(guidanceLayerAdded('regional_guidance_layer'));
|
const [addIPALayer, isAddIPALayerDisabled] = useAddIPALayer();
|
||||||
}, [dispatch]);
|
const [addIILayer, isAddIILayerDisabled] = useAddIILayer();
|
||||||
const addControlAdapterLayer = useCallback(() => {
|
const addRGLayer = useCallback(() => {
|
||||||
dispatch(guidanceLayerAdded('control_adapter_layer'));
|
dispatch(rgLayerAdded());
|
||||||
}, [dispatch]);
|
|
||||||
const addIPAdapterLayer = useCallback(() => {
|
|
||||||
dispatch(guidanceLayerAdded('ip_adapter_layer'));
|
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -24,15 +22,18 @@ export const AddLayerButton = memo(() => {
|
|||||||
{t('controlLayers.addLayer')}
|
{t('controlLayers.addLayer')}
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList>
|
<MenuList>
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addRegionalGuidanceLayer}>
|
<MenuItem icon={<PiPlusBold />} onClick={addRGLayer}>
|
||||||
{t('controlLayers.regionalGuidanceLayer')}
|
{t('controlLayers.regionalGuidanceLayer')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addControlAdapterLayer}>
|
<MenuItem icon={<PiPlusBold />} onClick={addCALayer} isDisabled={isAddCALayerDisabled}>
|
||||||
{t('controlLayers.globalControlAdapterLayer')}
|
{t('controlLayers.globalControlAdapterLayer')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem icon={<PiPlusBold />} onClick={addIPAdapterLayer}>
|
<MenuItem icon={<PiPlusBold />} onClick={addIPALayer} isDisabled={isAddIPALayerDisabled}>
|
||||||
{t('controlLayers.globalIPAdapterLayer')}
|
{t('controlLayers.globalIPAdapterLayer')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
<MenuItem icon={<PiPlusBold />} onClick={addIILayer} isDisabled={isAddIILayerDisabled}>
|
||||||
|
{t('controlLayers.globalInitialImageLayer')}
|
||||||
|
</MenuItem>
|
||||||
</MenuList>
|
</MenuList>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Button, Flex } from '@invoke-ai/ui-library';
|
import { Button, Flex } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { guidanceLayerIPAdapterAdded } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useAddIPAdapterToIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
import {
|
import {
|
||||||
isRegionalGuidanceLayer,
|
isRegionalGuidanceLayer,
|
||||||
maskLayerNegativePromptChanged,
|
rgLayerNegativePromptChanged,
|
||||||
maskLayerPositivePromptChanged,
|
rgLayerPositivePromptChanged,
|
||||||
selectControlLayersSlice,
|
selectControlLayersSlice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
@ -19,6 +19,7 @@ type AddPromptButtonProps = {
|
|||||||
export const AddPromptButtons = ({ layerId }: AddPromptButtonProps) => {
|
export const AddPromptButtons = ({ layerId }: AddPromptButtonProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToIPALayer(layerId);
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
||||||
@ -33,13 +34,10 @@ export const AddPromptButtons = ({ layerId }: AddPromptButtonProps) => {
|
|||||||
);
|
);
|
||||||
const validActions = useAppSelector(selectValidActions);
|
const validActions = useAppSelector(selectValidActions);
|
||||||
const addPositivePrompt = useCallback(() => {
|
const addPositivePrompt = useCallback(() => {
|
||||||
dispatch(maskLayerPositivePromptChanged({ layerId, prompt: '' }));
|
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
const addNegativePrompt = useCallback(() => {
|
const addNegativePrompt = useCallback(() => {
|
||||||
dispatch(maskLayerNegativePromptChanged({ layerId, prompt: '' }));
|
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
|
||||||
const addIPAdapter = useCallback(() => {
|
|
||||||
dispatch(guidanceLayerIPAdapterAdded(layerId));
|
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -62,7 +60,13 @@ export const AddPromptButtons = ({ layerId }: AddPromptButtonProps) => {
|
|||||||
>
|
>
|
||||||
{t('common.negativePrompt')}
|
{t('common.negativePrompt')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="sm" variant="ghost" leftIcon={<PiPlusBold />} onClick={addIPAdapter}>
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
leftIcon={<PiPlusBold />}
|
||||||
|
onClick={addIPAdapter}
|
||||||
|
isDisabled={isAddIPAdapterDisabled}
|
||||||
|
>
|
||||||
{t('common.ipAdapter')}
|
{t('common.ipAdapter')}
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { CALayerControlAdapterWrapper } from 'features/controlLayers/components/CALayer/CALayerControlAdapterWrapper';
|
||||||
|
import { LayerDeleteButton } from 'features/controlLayers/components/LayerCommon/LayerDeleteButton';
|
||||||
|
import { LayerMenu } from 'features/controlLayers/components/LayerCommon/LayerMenu';
|
||||||
|
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
||||||
|
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
||||||
|
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
||||||
|
import { layerSelected, selectCALayerOrThrow } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
|
||||||
|
import CALayerOpacity from './CALayerOpacity';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CALayer = memo(({ layerId }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const isSelected = useAppSelector((s) => selectCALayerOrThrow(s.controlLayers.present, layerId).isSelected);
|
||||||
|
const onClick = useCallback(() => {
|
||||||
|
// Must be capture so that the layer is selected before deleting/resetting/etc
|
||||||
|
dispatch(layerSelected(layerId));
|
||||||
|
}, [dispatch, layerId]);
|
||||||
|
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayerWrapper onClick={onClick} borderColor={isSelected ? 'base.400' : 'base.800'}>
|
||||||
|
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
||||||
|
<LayerVisibilityToggle layerId={layerId} />
|
||||||
|
<LayerTitle type="control_adapter_layer" />
|
||||||
|
<Spacer />
|
||||||
|
<CALayerOpacity layerId={layerId} />
|
||||||
|
<LayerMenu layerId={layerId} />
|
||||||
|
<LayerDeleteButton layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
{isOpen && (
|
||||||
|
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
||||||
|
<CALayerControlAdapterWrapper layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</LayerWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CALayer.displayName = 'CALayer';
|
@ -0,0 +1,121 @@
|
|||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { ControlAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/ControlAdapter';
|
||||||
|
import {
|
||||||
|
caLayerControlModeChanged,
|
||||||
|
caLayerImageChanged,
|
||||||
|
caLayerModelChanged,
|
||||||
|
caLayerProcessorConfigChanged,
|
||||||
|
caOrIPALayerBeginEndStepPctChanged,
|
||||||
|
caOrIPALayerWeightChanged,
|
||||||
|
selectCALayerOrThrow,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { ControlModeV2, ProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { CALayerImageDropData } from 'features/dnd/types';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import type {
|
||||||
|
CALayerImagePostUploadAction,
|
||||||
|
ControlNetModelConfig,
|
||||||
|
ImageDTO,
|
||||||
|
T2IAdapterModelConfig,
|
||||||
|
} from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CALayerControlAdapterWrapper = memo(({ layerId }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const controlAdapter = useAppSelector((s) => selectCALayerOrThrow(s.controlLayers.present, layerId).controlAdapter);
|
||||||
|
|
||||||
|
const onChangeBeginEndStepPct = useCallback(
|
||||||
|
(beginEndStepPct: [number, number]) => {
|
||||||
|
dispatch(
|
||||||
|
caOrIPALayerBeginEndStepPctChanged({
|
||||||
|
layerId,
|
||||||
|
beginEndStepPct,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeControlMode = useCallback(
|
||||||
|
(controlMode: ControlModeV2) => {
|
||||||
|
dispatch(
|
||||||
|
caLayerControlModeChanged({
|
||||||
|
layerId,
|
||||||
|
controlMode,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeWeight = useCallback(
|
||||||
|
(weight: number) => {
|
||||||
|
dispatch(caOrIPALayerWeightChanged({ layerId, weight }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeProcessorConfig = useCallback(
|
||||||
|
(processorConfig: ProcessorConfig | null) => {
|
||||||
|
dispatch(caLayerProcessorConfigChanged({ layerId, processorConfig }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeModel = useCallback(
|
||||||
|
(modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => {
|
||||||
|
dispatch(
|
||||||
|
caLayerModelChanged({
|
||||||
|
layerId,
|
||||||
|
modelConfig,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeImage = useCallback(
|
||||||
|
(imageDTO: ImageDTO | null) => {
|
||||||
|
dispatch(caLayerImageChanged({ layerId, imageDTO }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const droppableData = useMemo<CALayerImageDropData>(
|
||||||
|
() => ({
|
||||||
|
actionType: 'SET_CA_LAYER_IMAGE',
|
||||||
|
context: {
|
||||||
|
layerId,
|
||||||
|
},
|
||||||
|
id: layerId,
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const postUploadAction = useMemo<CALayerImagePostUploadAction>(
|
||||||
|
() => ({
|
||||||
|
layerId,
|
||||||
|
type: 'SET_CA_LAYER_IMAGE',
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ControlAdapter
|
||||||
|
controlAdapter={controlAdapter}
|
||||||
|
onChangeBeginEndStepPct={onChangeBeginEndStepPct}
|
||||||
|
onChangeControlMode={onChangeControlMode}
|
||||||
|
onChangeWeight={onChangeWeight}
|
||||||
|
onChangeProcessorConfig={onChangeProcessorConfig}
|
||||||
|
onChangeModel={onChangeModel}
|
||||||
|
onChangeImage={onChangeImage}
|
||||||
|
droppableData={droppableData}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
CALayerControlAdapterWrapper.displayName = 'CALayerControlAdapterWrapper';
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { useLayerOpacity } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useLayerOpacity } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { isFilterEnabledChanged, layerOpacityChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { caLayerIsFilterEnabledChanged, caLayerOpacityChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -34,13 +34,13 @@ const CALayerOpacity = ({ layerId }: Props) => {
|
|||||||
const { opacity, isFilterEnabled } = useLayerOpacity(layerId);
|
const { opacity, isFilterEnabled } = useLayerOpacity(layerId);
|
||||||
const onChangeOpacity = useCallback(
|
const onChangeOpacity = useCallback(
|
||||||
(v: number) => {
|
(v: number) => {
|
||||||
dispatch(layerOpacityChanged({ layerId, opacity: v / 100 }));
|
dispatch(caLayerOpacityChanged({ layerId, opacity: v / 100 }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
const onChangeFilter = useCallback(
|
const onChangeFilter = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
dispatch(isFilterEnabledChanged({ layerId, isFilterEnabled: e.target.checked }));
|
dispatch(caLayerIsFilterEnabledChanged({ layerId, isFilterEnabled: e.target.checked }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
||||||
@ -55,7 +55,7 @@ const CALayerOpacity = ({ layerId }: Props) => {
|
|||||||
onDoubleClick={stopPropagation}
|
onDoubleClick={stopPropagation}
|
||||||
/>
|
/>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent>
|
<PopoverContent onDoubleClick={stopPropagation}>
|
||||||
<PopoverArrow />
|
<PopoverArrow />
|
||||||
<PopoverBody>
|
<PopoverBody>
|
||||||
<Flex direction="column" gap={2}>
|
<Flex direction="column" gap={2}>
|
@ -1,71 +0,0 @@
|
|||||||
import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import CALayerOpacity from 'features/controlLayers/components/CALayerOpacity';
|
|
||||||
import ControlAdapterLayerConfig from 'features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig';
|
|
||||||
import { LayerDeleteButton } from 'features/controlLayers/components/LayerDeleteButton';
|
|
||||||
import { LayerMenu } from 'features/controlLayers/components/LayerMenu';
|
|
||||||
import { LayerTitle } from 'features/controlLayers/components/LayerTitle';
|
|
||||||
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerVisibilityToggle';
|
|
||||||
import {
|
|
||||||
isControlAdapterLayer,
|
|
||||||
layerSelected,
|
|
||||||
selectControlLayersSlice,
|
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
layerId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CALayerListItem = memo(({ layerId }: Props) => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const selector = useMemo(
|
|
||||||
() =>
|
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
|
||||||
assert(isControlAdapterLayer(layer), `Layer ${layerId} not found or not a ControlNet layer`);
|
|
||||||
return {
|
|
||||||
controlNetId: layer.controlNetId,
|
|
||||||
isSelected: layerId === controlLayers.present.selectedLayerId,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
[layerId]
|
|
||||||
);
|
|
||||||
const { controlNetId, isSelected } = useAppSelector(selector);
|
|
||||||
const onClickCapture = useCallback(() => {
|
|
||||||
// Must be capture so that the layer is selected before deleting/resetting/etc
|
|
||||||
dispatch(layerSelected(layerId));
|
|
||||||
}, [dispatch, layerId]);
|
|
||||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
gap={2}
|
|
||||||
onClickCapture={onClickCapture}
|
|
||||||
bg={isSelected ? 'base.400' : 'base.800'}
|
|
||||||
px={2}
|
|
||||||
borderRadius="base"
|
|
||||||
py="1px"
|
|
||||||
>
|
|
||||||
<Flex flexDir="column" w="full" bg="base.850" borderRadius="base">
|
|
||||||
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
|
||||||
<LayerVisibilityToggle layerId={layerId} />
|
|
||||||
<LayerTitle type="control_adapter_layer" />
|
|
||||||
<Spacer />
|
|
||||||
<CALayerOpacity layerId={layerId} />
|
|
||||||
<LayerMenu layerId={layerId} />
|
|
||||||
<LayerDeleteButton layerId={layerId} />
|
|
||||||
</Flex>
|
|
||||||
{isOpen && (
|
|
||||||
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
|
||||||
<ControlAdapterLayerConfig id={controlNetId} />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
CALayerListItem.displayName = 'CALayerListItem';
|
|
@ -0,0 +1,117 @@
|
|||||||
|
import { Box, Divider, Flex, Icon, IconButton } from '@invoke-ai/ui-library';
|
||||||
|
import { ControlAdapterModelCombobox } from 'features/controlLayers/components/ControlAndIPAdapter/ControlAdapterModelCombobox';
|
||||||
|
import type {
|
||||||
|
ControlModeV2,
|
||||||
|
ControlNetConfigV2,
|
||||||
|
ProcessorConfig,
|
||||||
|
T2IAdapterConfigV2,
|
||||||
|
} from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { TypesafeDroppableData } from 'features/dnd/types';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiCaretUpBold } from 'react-icons/pi';
|
||||||
|
import { useToggle } from 'react-use';
|
||||||
|
import type { ControlNetModelConfig, ImageDTO, PostUploadAction, T2IAdapterModelConfig } from 'services/api/types';
|
||||||
|
|
||||||
|
import { ControlAdapterBeginEndStepPct } from './ControlAdapterBeginEndStepPct';
|
||||||
|
import { ControlAdapterControlModeSelect } from './ControlAdapterControlModeSelect';
|
||||||
|
import { ControlAdapterImagePreview } from './ControlAdapterImagePreview';
|
||||||
|
import { ControlAdapterProcessorConfig } from './ControlAdapterProcessorConfig';
|
||||||
|
import { ControlAdapterProcessorTypeSelect } from './ControlAdapterProcessorTypeSelect';
|
||||||
|
import { ControlAdapterWeight } from './ControlAdapterWeight';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
controlAdapter: ControlNetConfigV2 | T2IAdapterConfigV2;
|
||||||
|
onChangeBeginEndStepPct: (beginEndStepPct: [number, number]) => void;
|
||||||
|
onChangeControlMode: (controlMode: ControlModeV2) => void;
|
||||||
|
onChangeWeight: (weight: number) => void;
|
||||||
|
onChangeProcessorConfig: (processorConfig: ProcessorConfig | null) => void;
|
||||||
|
onChangeModel: (modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => void;
|
||||||
|
onChangeImage: (imageDTO: ImageDTO | null) => void;
|
||||||
|
droppableData: TypesafeDroppableData;
|
||||||
|
postUploadAction: PostUploadAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ControlAdapter = memo(
|
||||||
|
({
|
||||||
|
controlAdapter,
|
||||||
|
onChangeBeginEndStepPct,
|
||||||
|
onChangeControlMode,
|
||||||
|
onChangeWeight,
|
||||||
|
onChangeProcessorConfig,
|
||||||
|
onChangeModel,
|
||||||
|
onChangeImage,
|
||||||
|
droppableData,
|
||||||
|
postUploadAction,
|
||||||
|
}: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" gap={3} position="relative" w="full">
|
||||||
|
<Flex gap={3} alignItems="center" w="full">
|
||||||
|
<Box minW={0} w="full" transitionProperty="common" transitionDuration="0.1s">
|
||||||
|
<ControlAdapterModelCombobox modelKey={controlAdapter.model?.key ?? null} onChange={onChangeModel} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size="sm"
|
||||||
|
tooltip={isExpanded ? t('controlnet.hideAdvanced') : t('controlnet.showAdvanced')}
|
||||||
|
aria-label={isExpanded ? t('controlnet.hideAdvanced') : t('controlnet.showAdvanced')}
|
||||||
|
onClick={toggleIsExpanded}
|
||||||
|
variant="ghost"
|
||||||
|
icon={
|
||||||
|
<Icon
|
||||||
|
boxSize={4}
|
||||||
|
as={PiCaretUpBold}
|
||||||
|
transform={isExpanded ? 'rotate(0deg)' : 'rotate(180deg)'}
|
||||||
|
transitionProperty="common"
|
||||||
|
transitionDuration="normal"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex gap={3} w="full">
|
||||||
|
<Flex flexDir="column" gap={3} w="full" h="full">
|
||||||
|
{controlAdapter.type === 'controlnet' && (
|
||||||
|
<ControlAdapterControlModeSelect
|
||||||
|
controlMode={controlAdapter.controlMode}
|
||||||
|
onChange={onChangeControlMode}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ControlAdapterWeight weight={controlAdapter.weight} onChange={onChangeWeight} />
|
||||||
|
<ControlAdapterBeginEndStepPct
|
||||||
|
beginEndStepPct={controlAdapter.beginEndStepPct}
|
||||||
|
onChange={onChangeBeginEndStepPct}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems="center" justifyContent="center" h={36} w={36} aspectRatio="1/1">
|
||||||
|
<ControlAdapterImagePreview
|
||||||
|
controlAdapter={controlAdapter}
|
||||||
|
onChangeImage={onChangeImage}
|
||||||
|
droppableData={droppableData}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
{isExpanded && (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
<Flex flexDir="column" gap={3} w="full">
|
||||||
|
<ControlAdapterProcessorTypeSelect
|
||||||
|
config={controlAdapter.processorConfig}
|
||||||
|
onChange={onChangeProcessorConfig}
|
||||||
|
/>
|
||||||
|
<ControlAdapterProcessorConfig
|
||||||
|
config={controlAdapter.processorConfig}
|
||||||
|
onChange={onChangeProcessorConfig}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ControlAdapter.displayName = 'ControlAdapter';
|
@ -0,0 +1,43 @@
|
|||||||
|
import { CompositeRangeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
beginEndStepPct: [number, number];
|
||||||
|
onChange: (beginEndStepPct: [number, number]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatPct = (v: number) => `${Math.round(v * 100)}%`;
|
||||||
|
const ariaLabel = ['Begin Step %', 'End Step %'];
|
||||||
|
|
||||||
|
export const ControlAdapterBeginEndStepPct = memo(({ beginEndStepPct, onChange }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const onReset = useCallback(() => {
|
||||||
|
onChange([0, 1]);
|
||||||
|
}, [onChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl orientation="horizontal">
|
||||||
|
<InformationalPopover feature="controlNetBeginEnd">
|
||||||
|
<FormLabel m={0}>{t('controlnet.beginEndStepPercentShort')}</FormLabel>
|
||||||
|
</InformationalPopover>
|
||||||
|
<CompositeRangeSlider
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
value={beginEndStepPct}
|
||||||
|
onChange={onChange}
|
||||||
|
onReset={onReset}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.05}
|
||||||
|
fineStep={0.01}
|
||||||
|
minStepsBetweenThumbs={1}
|
||||||
|
formatValue={formatPct}
|
||||||
|
marks
|
||||||
|
withThumbTooltip
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ControlAdapterBeginEndStepPct.displayName = 'ControlAdapterBeginEndStepPct';
|
@ -1,24 +1,19 @@
|
|||||||
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||||
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
|
||||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||||
import { useControlAdapterControlMode } from 'features/controlAdapters/hooks/useControlAdapterControlMode';
|
import type { ControlModeV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
|
import { isControlModeV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
import { controlAdapterControlModeChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import type { ControlMode } from 'features/controlAdapters/store/types';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
id: string;
|
controlMode: ControlModeV2;
|
||||||
|
onChange: (controlMode: ControlModeV2) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParamControlAdapterControlMode = ({ id }: Props) => {
|
export const ControlAdapterControlModeSelect = memo(({ controlMode, onChange }: Props) => {
|
||||||
const isEnabled = useControlAdapterIsEnabled(id);
|
|
||||||
const controlMode = useControlAdapterControlMode(id);
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const CONTROL_MODE_DATA = useMemo(
|
const CONTROL_MODE_DATA = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{ label: t('controlnet.balanced'), value: 'balanced' },
|
{ label: t('controlnet.balanced'), value: 'balanced' },
|
||||||
@ -31,17 +26,10 @@ const ParamControlAdapterControlMode = ({ id }: Props) => {
|
|||||||
|
|
||||||
const handleControlModeChange = useCallback<ComboboxOnChange>(
|
const handleControlModeChange = useCallback<ComboboxOnChange>(
|
||||||
(v) => {
|
(v) => {
|
||||||
if (!v) {
|
assert(isControlModeV2(v?.value));
|
||||||
return;
|
onChange(v.value);
|
||||||
}
|
|
||||||
dispatch(
|
|
||||||
controlAdapterControlModeChanged({
|
|
||||||
id,
|
|
||||||
controlMode: v.value as ControlMode,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[id, dispatch]
|
[onChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
@ -54,13 +42,19 @@ const ParamControlAdapterControlMode = ({ id }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl isDisabled={!isEnabled}>
|
<FormControl>
|
||||||
<InformationalPopover feature="controlNetControlMode">
|
<InformationalPopover feature="controlNetControlMode">
|
||||||
<FormLabel m={0}>{t('controlnet.control')}</FormLabel>
|
<FormLabel m={0}>{t('controlnet.control')}</FormLabel>
|
||||||
</InformationalPopover>
|
</InformationalPopover>
|
||||||
<Combobox value={value} options={CONTROL_MODE_DATA} onChange={handleControlModeChange} />
|
<Combobox
|
||||||
|
value={value}
|
||||||
|
options={CONTROL_MODE_DATA}
|
||||||
|
onChange={handleControlModeChange}
|
||||||
|
isClearable={false}
|
||||||
|
isSearchable={false}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default memo(ParamControlAdapterControlMode);
|
ControlAdapterControlModeSelect.displayName = 'ControlAdapterControlModeSelect';
|
@ -0,0 +1,217 @@
|
|||||||
|
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||||
|
import { Box, Flex, Spinner, useShiftModifier } from '@invoke-ai/ui-library';
|
||||||
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
|
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||||
|
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { ControlNetConfigV2, T2IAdapterConfigV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiArrowCounterClockwiseBold, PiFloppyDiskBold, PiRulerBold } from 'react-icons/pi';
|
||||||
|
import {
|
||||||
|
useAddImageToBoardMutation,
|
||||||
|
useChangeImageIsIntermediateMutation,
|
||||||
|
useGetImageDTOQuery,
|
||||||
|
useRemoveImageFromBoardMutation,
|
||||||
|
} from 'services/api/endpoints/images';
|
||||||
|
import type { ImageDTO, PostUploadAction } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
controlAdapter: ControlNetConfigV2 | T2IAdapterConfigV2;
|
||||||
|
onChangeImage: (imageDTO: ImageDTO | null) => void;
|
||||||
|
droppableData: TypesafeDroppableData;
|
||||||
|
postUploadAction: PostUploadAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ControlAdapterImagePreview = memo(
|
||||||
|
({ controlAdapter, onChangeImage, droppableData, postUploadAction }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
||||||
|
const isConnected = useAppSelector((s) => s.system.isConnected);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
|
const shift = useShiftModifier();
|
||||||
|
|
||||||
|
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
||||||
|
|
||||||
|
const { currentData: controlImage, isError: isErrorControlImage } = useGetImageDTOQuery(
|
||||||
|
controlAdapter.image?.imageName ?? skipToken
|
||||||
|
);
|
||||||
|
const { currentData: processedControlImage, isError: isErrorProcessedControlImage } = useGetImageDTOQuery(
|
||||||
|
controlAdapter.processedImage?.imageName ?? skipToken
|
||||||
|
);
|
||||||
|
|
||||||
|
const [changeIsIntermediate] = useChangeImageIsIntermediateMutation();
|
||||||
|
const [addToBoard] = useAddImageToBoardMutation();
|
||||||
|
const [removeFromBoard] = useRemoveImageFromBoardMutation();
|
||||||
|
const handleResetControlImage = useCallback(() => {
|
||||||
|
onChangeImage(null);
|
||||||
|
}, [onChangeImage]);
|
||||||
|
|
||||||
|
const handleSaveControlImage = useCallback(async () => {
|
||||||
|
if (!processedControlImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await changeIsIntermediate({
|
||||||
|
imageDTO: processedControlImage,
|
||||||
|
is_intermediate: false,
|
||||||
|
}).unwrap();
|
||||||
|
|
||||||
|
if (autoAddBoardId !== 'none') {
|
||||||
|
addToBoard({
|
||||||
|
imageDTO: processedControlImage,
|
||||||
|
board_id: autoAddBoardId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
removeFromBoard({ imageDTO: processedControlImage });
|
||||||
|
}
|
||||||
|
}, [processedControlImage, changeIsIntermediate, autoAddBoardId, addToBoard, removeFromBoard]);
|
||||||
|
|
||||||
|
const handleSetControlImageToDimensions = useCallback(() => {
|
||||||
|
if (!controlImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTabName === 'canvas') {
|
||||||
|
dispatch(
|
||||||
|
setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const options = { updateAspectRatio: true, clamp: true };
|
||||||
|
|
||||||
|
if (shift) {
|
||||||
|
const { width, height } = controlImage;
|
||||||
|
dispatch(widthChanged({ width, ...options }));
|
||||||
|
dispatch(heightChanged({ height, ...options }));
|
||||||
|
} else {
|
||||||
|
const { width, height } = calculateNewSize(
|
||||||
|
controlImage.width / controlImage.height,
|
||||||
|
optimalDimension * optimalDimension
|
||||||
|
);
|
||||||
|
dispatch(widthChanged({ width, ...options }));
|
||||||
|
dispatch(heightChanged({ height, ...options }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [controlImage, activeTabName, dispatch, optimalDimension, shift]);
|
||||||
|
|
||||||
|
const handleMouseEnter = useCallback(() => {
|
||||||
|
setIsMouseOverImage(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleMouseLeave = useCallback(() => {
|
||||||
|
setIsMouseOverImage(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const draggableData = useMemo<ImageDraggableData | undefined>(() => {
|
||||||
|
if (controlImage) {
|
||||||
|
return {
|
||||||
|
id: controlAdapter.id,
|
||||||
|
payloadType: 'IMAGE_DTO',
|
||||||
|
payload: { imageDTO: controlImage },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [controlImage, controlAdapter.id]);
|
||||||
|
|
||||||
|
const shouldShowProcessedImage =
|
||||||
|
controlImage &&
|
||||||
|
processedControlImage &&
|
||||||
|
!isMouseOverImage &&
|
||||||
|
!controlAdapter.isProcessingImage &&
|
||||||
|
controlAdapter.processorConfig !== null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isConnected && (isErrorControlImage || isErrorProcessedControlImage)) {
|
||||||
|
handleResetControlImage();
|
||||||
|
}
|
||||||
|
}, [handleResetControlImage, isConnected, isErrorControlImage, isErrorProcessedControlImage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
onMouseEnter={handleMouseEnter}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
position="relative"
|
||||||
|
w="full"
|
||||||
|
h={36}
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
>
|
||||||
|
<IAIDndImage
|
||||||
|
draggableData={draggableData}
|
||||||
|
droppableData={droppableData}
|
||||||
|
imageDTO={controlImage}
|
||||||
|
isDropDisabled={shouldShowProcessedImage}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
insetInlineStart={0}
|
||||||
|
w="full"
|
||||||
|
h="full"
|
||||||
|
opacity={shouldShowProcessedImage ? 1 : 0}
|
||||||
|
transitionProperty="common"
|
||||||
|
transitionDuration="normal"
|
||||||
|
pointerEvents="none"
|
||||||
|
>
|
||||||
|
<IAIDndImage
|
||||||
|
draggableData={draggableData}
|
||||||
|
droppableData={droppableData}
|
||||||
|
imageDTO={processedControlImage}
|
||||||
|
isUploadDisabled={true}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={handleResetControlImage}
|
||||||
|
icon={controlImage ? <PiArrowCounterClockwiseBold size={16} /> : undefined}
|
||||||
|
tooltip={t('controlnet.resetControlImage')}
|
||||||
|
/>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={handleSaveControlImage}
|
||||||
|
icon={controlImage ? <PiFloppyDiskBold size={16} /> : undefined}
|
||||||
|
tooltip={t('controlnet.saveControlImage')}
|
||||||
|
styleOverrides={saveControlImageStyleOverrides}
|
||||||
|
/>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={handleSetControlImageToDimensions}
|
||||||
|
icon={controlImage ? <PiRulerBold size={16} /> : undefined}
|
||||||
|
tooltip={shift ? t('controlnet.setControlImageDimensionsForce') : t('controlnet.setControlImageDimensions')}
|
||||||
|
styleOverrides={setControlImageDimensionsStyleOverrides}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
|
||||||
|
{controlAdapter.isProcessingImage && (
|
||||||
|
<Flex
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
insetInlineStart={0}
|
||||||
|
w="full"
|
||||||
|
h="full"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
opacity={0.8}
|
||||||
|
borderRadius="base"
|
||||||
|
bg="base.900"
|
||||||
|
>
|
||||||
|
<Spinner size="xl" color="base.400" />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
ControlAdapterImagePreview.displayName = 'ControlAdapterImagePreview';
|
||||||
|
|
||||||
|
const saveControlImageStyleOverrides: SystemStyleObject = { mt: 6 };
|
||||||
|
const setControlImageDimensionsStyleOverrides: SystemStyleObject = { mt: 12 };
|
@ -0,0 +1,62 @@
|
|||||||
|
import { Combobox, FormControl, Tooltip } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useControlNetAndT2IAdapterModels } from 'services/api/hooks/modelsByType';
|
||||||
|
import type { AnyModelConfig, ControlNetModelConfig, T2IAdapterModelConfig } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
modelKey: string | null;
|
||||||
|
onChange: (modelConfig: ControlNetModelConfig | T2IAdapterModelConfig) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ControlAdapterModelCombobox = memo(({ modelKey, onChange: onChangeModel }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const currentBaseModel = useAppSelector((s) => s.generation.model?.base);
|
||||||
|
const [modelConfigs, { isLoading }] = useControlNetAndT2IAdapterModels();
|
||||||
|
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);
|
||||||
|
|
||||||
|
const _onChange = useCallback(
|
||||||
|
(modelConfig: ControlNetModelConfig | T2IAdapterModelConfig | null) => {
|
||||||
|
if (!modelConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChangeModel(modelConfig);
|
||||||
|
},
|
||||||
|
[onChangeModel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getIsDisabled = useCallback(
|
||||||
|
(model: AnyModelConfig): boolean => {
|
||||||
|
const isCompatible = currentBaseModel === model.base;
|
||||||
|
const hasMainModel = Boolean(currentBaseModel);
|
||||||
|
return !hasMainModel || !isCompatible;
|
||||||
|
},
|
||||||
|
[currentBaseModel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { options, value, onChange, noOptionsMessage } = useGroupedModelCombobox({
|
||||||
|
modelConfigs,
|
||||||
|
onChange: _onChange,
|
||||||
|
selectedModel,
|
||||||
|
getIsDisabled,
|
||||||
|
isLoading,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip label={selectedModel?.description}>
|
||||||
|
<FormControl isInvalid={!value || currentBaseModel !== selectedModel?.base} w="full">
|
||||||
|
<Combobox
|
||||||
|
options={options}
|
||||||
|
placeholder={t('controlnet.selectModel')}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
noOptionsMessage={noOptionsMessage}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ControlAdapterModelCombobox.displayName = 'ControlAdapterModelCombobox';
|
@ -0,0 +1,85 @@
|
|||||||
|
import type { ProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
import { CannyProcessor } from './processors/CannyProcessor';
|
||||||
|
import { ColorMapProcessor } from './processors/ColorMapProcessor';
|
||||||
|
import { ContentShuffleProcessor } from './processors/ContentShuffleProcessor';
|
||||||
|
import { DepthAnythingProcessor } from './processors/DepthAnythingProcessor';
|
||||||
|
import { DWOpenposeProcessor } from './processors/DWOpenposeProcessor';
|
||||||
|
import { HedProcessor } from './processors/HedProcessor';
|
||||||
|
import { LineartProcessor } from './processors/LineartProcessor';
|
||||||
|
import { MediapipeFaceProcessor } from './processors/MediapipeFaceProcessor';
|
||||||
|
import { MidasDepthProcessor } from './processors/MidasDepthProcessor';
|
||||||
|
import { MlsdImageProcessor } from './processors/MlsdImageProcessor';
|
||||||
|
import { PidiProcessor } from './processors/PidiProcessor';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
config: ProcessorConfig | null;
|
||||||
|
onChange: (config: ProcessorConfig | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ControlAdapterProcessorConfig = memo(({ config, onChange }: Props) => {
|
||||||
|
if (!config) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'canny_image_processor') {
|
||||||
|
return <CannyProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'color_map_image_processor') {
|
||||||
|
return <ColorMapProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'depth_anything_image_processor') {
|
||||||
|
return <DepthAnythingProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'hed_image_processor') {
|
||||||
|
return <HedProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'lineart_image_processor') {
|
||||||
|
return <LineartProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'content_shuffle_image_processor') {
|
||||||
|
return <ContentShuffleProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'lineart_anime_image_processor') {
|
||||||
|
// No configurable options for this processor
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'mediapipe_face_processor') {
|
||||||
|
return <MediapipeFaceProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'midas_depth_image_processor') {
|
||||||
|
return <MidasDepthProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'mlsd_image_processor') {
|
||||||
|
return <MlsdImageProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'normalbae_image_processor') {
|
||||||
|
// No configurable options for this processor
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'dw_openpose_image_processor') {
|
||||||
|
return <DWOpenposeProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'pidi_image_processor') {
|
||||||
|
return <PidiProcessor onChange={onChange} config={config} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.type === 'zoe_depth_image_processor') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ControlAdapterProcessorConfig.displayName = 'ControlAdapterProcessorConfig';
|
@ -0,0 +1,70 @@
|
|||||||
|
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||||
|
import { Combobox, Flex, FormControl, FormLabel, IconButton } from '@invoke-ai/ui-library';
|
||||||
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||||
|
import type { ProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { CA_PROCESSOR_DATA, isProcessorTypeV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { configSelector } from 'features/system/store/configSelectors';
|
||||||
|
import { includes, map } from 'lodash-es';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiXBold } from 'react-icons/pi';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
config: ProcessorConfig | null;
|
||||||
|
onChange: (config: ProcessorConfig | null) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectDisabledProcessors = createMemoizedSelector(
|
||||||
|
configSelector,
|
||||||
|
(config) => config.sd.disabledControlNetProcessors
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ControlAdapterProcessorTypeSelect = memo(({ config, onChange }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const disabledProcessors = useAppSelector(selectDisabledProcessors);
|
||||||
|
const options = useMemo(() => {
|
||||||
|
return map(CA_PROCESSOR_DATA, ({ labelTKey }, type) => ({ value: type, label: t(labelTKey) })).filter(
|
||||||
|
(o) => !includes(disabledProcessors, o.value)
|
||||||
|
);
|
||||||
|
}, [disabledProcessors, t]);
|
||||||
|
|
||||||
|
const _onChange = useCallback<ComboboxOnChange>(
|
||||||
|
(v) => {
|
||||||
|
if (!v) {
|
||||||
|
onChange(null);
|
||||||
|
} else {
|
||||||
|
assert(isProcessorTypeV2(v.value));
|
||||||
|
onChange(CA_PROCESSOR_DATA[v.value].buildDefaults());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onChange]
|
||||||
|
);
|
||||||
|
const clearProcessor = useCallback(() => {
|
||||||
|
onChange(null);
|
||||||
|
}, [onChange]);
|
||||||
|
const value = useMemo(() => options.find((o) => o.value === config?.type) ?? null, [options, config?.type]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap={2}>
|
||||||
|
<FormControl>
|
||||||
|
<InformationalPopover feature="controlNetProcessor">
|
||||||
|
<FormLabel m={0}>{t('controlnet.processor')}</FormLabel>
|
||||||
|
</InformationalPopover>
|
||||||
|
<Combobox value={value} options={options} onChange={_onChange} isSearchable={false} isClearable={false} />
|
||||||
|
</FormControl>
|
||||||
|
<IconButton
|
||||||
|
aria-label={t('controlLayers.clearProcessor')}
|
||||||
|
onClick={clearProcessor}
|
||||||
|
isDisabled={!config}
|
||||||
|
icon={<PiXBold />}
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ControlAdapterProcessorTypeSelect.displayName = 'ControlAdapterProcessorTypeSelect';
|
@ -1,24 +1,19 @@
|
|||||||
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||||
import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
|
import { memo } from 'react';
|
||||||
import { useControlAdapterWeight } from 'features/controlAdapters/hooks/useControlAdapterWeight';
|
|
||||||
import { controlAdapterWeightChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import { isNil } from 'lodash-es';
|
|
||||||
import { memo, useCallback } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
type ParamControlAdapterWeightProps = {
|
type Props = {
|
||||||
id: string;
|
weight: number;
|
||||||
|
onChange: (weight: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatValue = (v: number) => v.toFixed(2);
|
const formatValue = (v: number) => v.toFixed(2);
|
||||||
|
const marks = [0, 1, 2];
|
||||||
|
|
||||||
const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => {
|
export const ControlAdapterWeight = memo(({ weight, onChange }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const isEnabled = useControlAdapterIsEnabled(id);
|
|
||||||
const weight = useControlAdapterWeight(id);
|
|
||||||
const initial = useAppSelector((s) => s.config.sd.ca.weight.initial);
|
const initial = useAppSelector((s) => s.config.sd.ca.weight.initial);
|
||||||
const sliderMin = useAppSelector((s) => s.config.sd.ca.weight.sliderMin);
|
const sliderMin = useAppSelector((s) => s.config.sd.ca.weight.sliderMin);
|
||||||
const sliderMax = useAppSelector((s) => s.config.sd.ca.weight.sliderMax);
|
const sliderMax = useAppSelector((s) => s.config.sd.ca.weight.sliderMax);
|
||||||
@ -27,20 +22,8 @@ const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => {
|
|||||||
const coarseStep = useAppSelector((s) => s.config.sd.ca.weight.coarseStep);
|
const coarseStep = useAppSelector((s) => s.config.sd.ca.weight.coarseStep);
|
||||||
const fineStep = useAppSelector((s) => s.config.sd.ca.weight.fineStep);
|
const fineStep = useAppSelector((s) => s.config.sd.ca.weight.fineStep);
|
||||||
|
|
||||||
const onChange = useCallback(
|
|
||||||
(weight: number) => {
|
|
||||||
dispatch(controlAdapterWeightChanged({ id, weight }));
|
|
||||||
},
|
|
||||||
[dispatch, id]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isNil(weight)) {
|
|
||||||
// should never happen
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl isDisabled={!isEnabled} orientation="horizontal">
|
<FormControl orientation="horizontal">
|
||||||
<InformationalPopover feature="controlNetWeight">
|
<InformationalPopover feature="controlNetWeight">
|
||||||
<FormLabel m={0}>{t('controlnet.weight')}</FormLabel>
|
<FormLabel m={0}>{t('controlnet.weight')}</FormLabel>
|
||||||
</InformationalPopover>
|
</InformationalPopover>
|
||||||
@ -67,8 +50,6 @@ const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => {
|
|||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default memo(ParamControlAdapterWeight);
|
ControlAdapterWeight.displayName = 'ControlAdapterWeight';
|
||||||
|
|
||||||
const marks = [0, 1, 2];
|
|
@ -0,0 +1,72 @@
|
|||||||
|
import { Box, Flex } from '@invoke-ai/ui-library';
|
||||||
|
import { ControlAdapterBeginEndStepPct } from 'features/controlLayers/components/ControlAndIPAdapter/ControlAdapterBeginEndStepPct';
|
||||||
|
import { ControlAdapterWeight } from 'features/controlLayers/components/ControlAndIPAdapter/ControlAdapterWeight';
|
||||||
|
import { IPAdapterImagePreview } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapterImagePreview';
|
||||||
|
import { IPAdapterMethod } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapterMethod';
|
||||||
|
import { IPAdapterModelSelect } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapterModelSelect';
|
||||||
|
import type { CLIPVisionModelV2, IPAdapterConfigV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { TypesafeDroppableData } from 'features/dnd/types';
|
||||||
|
import { memo } from 'react';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig, PostUploadAction } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
ipAdapter: IPAdapterConfigV2;
|
||||||
|
onChangeBeginEndStepPct: (beginEndStepPct: [number, number]) => void;
|
||||||
|
onChangeWeight: (weight: number) => void;
|
||||||
|
onChangeIPMethod: (method: IPMethodV2) => void;
|
||||||
|
onChangeModel: (modelConfig: IPAdapterModelConfig) => void;
|
||||||
|
onChangeCLIPVisionModel: (clipVisionModel: CLIPVisionModelV2) => void;
|
||||||
|
onChangeImage: (imageDTO: ImageDTO | null) => void;
|
||||||
|
droppableData: TypesafeDroppableData;
|
||||||
|
postUploadAction: PostUploadAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IPAdapter = memo(
|
||||||
|
({
|
||||||
|
ipAdapter,
|
||||||
|
onChangeBeginEndStepPct,
|
||||||
|
onChangeWeight,
|
||||||
|
onChangeIPMethod,
|
||||||
|
onChangeModel,
|
||||||
|
onChangeCLIPVisionModel,
|
||||||
|
onChangeImage,
|
||||||
|
droppableData,
|
||||||
|
postUploadAction,
|
||||||
|
}: Props) => {
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" gap={4} position="relative" w="full">
|
||||||
|
<Flex gap={3} alignItems="center" w="full">
|
||||||
|
<Box minW={0} w="full" transitionProperty="common" transitionDuration="0.1s">
|
||||||
|
<IPAdapterModelSelect
|
||||||
|
modelKey={ipAdapter.model?.key ?? null}
|
||||||
|
onChangeModel={onChangeModel}
|
||||||
|
clipVisionModel={ipAdapter.clipVisionModel}
|
||||||
|
onChangeCLIPVisionModel={onChangeCLIPVisionModel}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
<Flex gap={4} w="full" alignItems="center">
|
||||||
|
<Flex flexDir="column" gap={3} w="full">
|
||||||
|
<IPAdapterMethod method={ipAdapter.method} onChange={onChangeIPMethod} />
|
||||||
|
<ControlAdapterWeight weight={ipAdapter.weight} onChange={onChangeWeight} />
|
||||||
|
<ControlAdapterBeginEndStepPct
|
||||||
|
beginEndStepPct={ipAdapter.beginEndStepPct}
|
||||||
|
onChange={onChangeBeginEndStepPct}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<Flex alignItems="center" justifyContent="center" h={36} w={36} aspectRatio="1/1">
|
||||||
|
<IPAdapterImagePreview
|
||||||
|
image={ipAdapter.image}
|
||||||
|
onChangeImage={onChangeImage}
|
||||||
|
ipAdapterId={ipAdapter.id}
|
||||||
|
droppableData={droppableData}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
IPAdapter.displayName = 'IPAdapter';
|
@ -0,0 +1,115 @@
|
|||||||
|
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||||
|
import { Flex, useShiftModifier } from '@invoke-ai/ui-library';
|
||||||
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
|
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||||
|
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { ImageWithDims } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiArrowCounterClockwiseBold, PiRulerBold } from 'react-icons/pi';
|
||||||
|
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||||
|
import type { ImageDTO, PostUploadAction } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
image: ImageWithDims | null;
|
||||||
|
onChangeImage: (imageDTO: ImageDTO | null) => void;
|
||||||
|
ipAdapterId: string; // required for the dnd/upload interactions
|
||||||
|
droppableData: TypesafeDroppableData;
|
||||||
|
postUploadAction: PostUploadAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IPAdapterImagePreview = memo(
|
||||||
|
({ image, onChangeImage, ipAdapterId, droppableData, postUploadAction }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const isConnected = useAppSelector((s) => s.system.isConnected);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
|
const shift = useShiftModifier();
|
||||||
|
|
||||||
|
const { currentData: controlImage, isError: isErrorControlImage } = useGetImageDTOQuery(
|
||||||
|
image?.imageName ?? skipToken
|
||||||
|
);
|
||||||
|
const handleResetControlImage = useCallback(() => {
|
||||||
|
onChangeImage(null);
|
||||||
|
}, [onChangeImage]);
|
||||||
|
|
||||||
|
const handleSetControlImageToDimensions = useCallback(() => {
|
||||||
|
if (!controlImage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTabName === 'canvas') {
|
||||||
|
dispatch(
|
||||||
|
setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const options = { updateAspectRatio: true, clamp: true };
|
||||||
|
if (shift) {
|
||||||
|
const { width, height } = controlImage;
|
||||||
|
dispatch(widthChanged({ width, ...options }));
|
||||||
|
dispatch(heightChanged({ height, ...options }));
|
||||||
|
} else {
|
||||||
|
const { width, height } = calculateNewSize(
|
||||||
|
controlImage.width / controlImage.height,
|
||||||
|
optimalDimension * optimalDimension
|
||||||
|
);
|
||||||
|
dispatch(widthChanged({ width, ...options }));
|
||||||
|
dispatch(heightChanged({ height, ...options }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [controlImage, activeTabName, dispatch, optimalDimension, shift]);
|
||||||
|
|
||||||
|
const draggableData = useMemo<ImageDraggableData | undefined>(() => {
|
||||||
|
if (controlImage) {
|
||||||
|
return {
|
||||||
|
id: ipAdapterId,
|
||||||
|
payloadType: 'IMAGE_DTO',
|
||||||
|
payload: { imageDTO: controlImage },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [controlImage, ipAdapterId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isConnected && isErrorControlImage) {
|
||||||
|
handleResetControlImage();
|
||||||
|
}
|
||||||
|
}, [handleResetControlImage, isConnected, isErrorControlImage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex position="relative" w="full" h={36} alignItems="center" justifyContent="center">
|
||||||
|
<IAIDndImage
|
||||||
|
draggableData={draggableData}
|
||||||
|
droppableData={droppableData}
|
||||||
|
imageDTO={controlImage}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={handleResetControlImage}
|
||||||
|
icon={controlImage ? <PiArrowCounterClockwiseBold size={16} /> : undefined}
|
||||||
|
tooltip={t('controlnet.resetControlImage')}
|
||||||
|
/>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={handleSetControlImageToDimensions}
|
||||||
|
icon={controlImage ? <PiRulerBold size={16} /> : undefined}
|
||||||
|
tooltip={shift ? t('controlnet.setControlImageDimensionsForce') : t('controlnet.setControlImageDimensions')}
|
||||||
|
styleOverrides={setControlImageDimensionsStyleOverrides}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
IPAdapterImagePreview.displayName = 'IPAdapterImagePreview';
|
||||||
|
|
||||||
|
const setControlImageDimensionsStyleOverrides: SystemStyleObject = { mt: 6 };
|
@ -0,0 +1,44 @@
|
|||||||
|
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||||
|
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
|
||||||
|
import type { IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { isIPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
method: IPMethodV2;
|
||||||
|
onChange: (method: IPMethodV2) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IPAdapterMethod = memo(({ method, onChange }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const options: { label: string; value: IPMethodV2 }[] = useMemo(
|
||||||
|
() => [
|
||||||
|
{ label: t('controlnet.full'), value: 'full' },
|
||||||
|
{ label: `${t('controlnet.style')} (${t('common.beta')})`, value: 'style' },
|
||||||
|
{ label: `${t('controlnet.composition')} (${t('common.beta')})`, value: 'composition' },
|
||||||
|
],
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
const _onChange = useCallback<ComboboxOnChange>(
|
||||||
|
(v) => {
|
||||||
|
assert(isIPMethodV2(v?.value));
|
||||||
|
onChange(v.value);
|
||||||
|
},
|
||||||
|
[onChange]
|
||||||
|
);
|
||||||
|
const value = useMemo(() => options.find((o) => o.value === method), [options, method]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl>
|
||||||
|
<InformationalPopover feature="ipAdapterMethod">
|
||||||
|
<FormLabel>{t('controlnet.ipAdapterMethod')}</FormLabel>
|
||||||
|
</InformationalPopover>
|
||||||
|
<Combobox value={value} options={options} onChange={_onChange} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
IPAdapterMethod.displayName = 'IPAdapterMethod';
|
@ -0,0 +1,100 @@
|
|||||||
|
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||||
|
import { Combobox, Flex, FormControl, Tooltip } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
|
||||||
|
import type { CLIPVisionModelV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { isCLIPVisionModelV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useIPAdapterModels } from 'services/api/hooks/modelsByType';
|
||||||
|
import type { AnyModelConfig, IPAdapterModelConfig } from 'services/api/types';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
const CLIP_VISION_OPTIONS = [
|
||||||
|
{ label: 'ViT-H', value: 'ViT-H' },
|
||||||
|
{ label: 'ViT-G', value: 'ViT-G' },
|
||||||
|
];
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
modelKey: string | null;
|
||||||
|
onChangeModel: (modelConfig: IPAdapterModelConfig) => void;
|
||||||
|
clipVisionModel: CLIPVisionModelV2;
|
||||||
|
onChangeCLIPVisionModel: (clipVisionModel: CLIPVisionModelV2) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IPAdapterModelSelect = memo(
|
||||||
|
({ modelKey, onChangeModel, clipVisionModel, onChangeCLIPVisionModel }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const currentBaseModel = useAppSelector((s) => s.generation.model?.base);
|
||||||
|
const [modelConfigs, { isLoading }] = useIPAdapterModels();
|
||||||
|
const selectedModel = useMemo(() => modelConfigs.find((m) => m.key === modelKey), [modelConfigs, modelKey]);
|
||||||
|
|
||||||
|
const _onChangeModel = useCallback(
|
||||||
|
(modelConfig: IPAdapterModelConfig | null) => {
|
||||||
|
if (!modelConfig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChangeModel(modelConfig);
|
||||||
|
},
|
||||||
|
[onChangeModel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const _onChangeCLIPVisionModel = useCallback<ComboboxOnChange>(
|
||||||
|
(v) => {
|
||||||
|
assert(isCLIPVisionModelV2(v?.value));
|
||||||
|
onChangeCLIPVisionModel(v.value);
|
||||||
|
},
|
||||||
|
[onChangeCLIPVisionModel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getIsDisabled = useCallback(
|
||||||
|
(model: AnyModelConfig): boolean => {
|
||||||
|
const isCompatible = currentBaseModel === model.base;
|
||||||
|
const hasMainModel = Boolean(currentBaseModel);
|
||||||
|
return !hasMainModel || !isCompatible;
|
||||||
|
},
|
||||||
|
[currentBaseModel]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { options, value, onChange, noOptionsMessage } = useGroupedModelCombobox({
|
||||||
|
modelConfigs,
|
||||||
|
onChange: _onChangeModel,
|
||||||
|
selectedModel,
|
||||||
|
getIsDisabled,
|
||||||
|
isLoading,
|
||||||
|
});
|
||||||
|
|
||||||
|
const clipVisionModelValue = useMemo(
|
||||||
|
() => CLIP_VISION_OPTIONS.find((o) => o.value === clipVisionModel),
|
||||||
|
[clipVisionModel]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex gap={4}>
|
||||||
|
<Tooltip label={selectedModel?.description}>
|
||||||
|
<FormControl isInvalid={!value || currentBaseModel !== selectedModel?.base} w="full">
|
||||||
|
<Combobox
|
||||||
|
options={options}
|
||||||
|
placeholder={t('controlnet.selectModel')}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
noOptionsMessage={noOptionsMessage}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Tooltip>
|
||||||
|
{selectedModel?.format === 'checkpoint' && (
|
||||||
|
<FormControl isInvalid={!value || currentBaseModel !== selectedModel?.base} width="max-content" minWidth={28}>
|
||||||
|
<Combobox
|
||||||
|
options={CLIP_VISION_OPTIONS}
|
||||||
|
placeholder={t('controlnet.selectCLIPVisionModel')}
|
||||||
|
value={clipVisionModelValue}
|
||||||
|
onChange={_onChangeCLIPVisionModel}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
IPAdapterModelSelect.displayName = 'IPAdapterModelSelect';
|
@ -0,0 +1,67 @@
|
|||||||
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import { CA_PROCESSOR_DATA, type CannyProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<CannyProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['canny_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const CannyProcessor = ({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const handleLowThresholdChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, low_threshold: v });
|
||||||
|
},
|
||||||
|
[onChange, config]
|
||||||
|
);
|
||||||
|
const handleHighThresholdChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, high_threshold: v });
|
||||||
|
},
|
||||||
|
[onChange, config]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.lowThreshold')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.low_threshold}
|
||||||
|
onChange={handleLowThresholdChanged}
|
||||||
|
defaultValue={DEFAULTS.low_threshold}
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.low_threshold}
|
||||||
|
onChange={handleLowThresholdChanged}
|
||||||
|
defaultValue={DEFAULTS.low_threshold}
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.highThreshold')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.high_threshold}
|
||||||
|
onChange={handleHighThresholdChanged}
|
||||||
|
defaultValue={DEFAULTS.high_threshold}
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.high_threshold}
|
||||||
|
onChange={handleHighThresholdChanged}
|
||||||
|
defaultValue={DEFAULTS.high_threshold}
|
||||||
|
min={0}
|
||||||
|
max={255}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CannyProcessor.displayName = 'CannyProcessor';
|
@ -0,0 +1,47 @@
|
|||||||
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import { CA_PROCESSOR_DATA, type ColorMapProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<ColorMapProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['color_map_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const ColorMapProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const handleColorMapTileSizeChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, color_map_tile_size: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.colorMapTileSize')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.color_map_tile_size}
|
||||||
|
defaultValue={DEFAULTS.color_map_tile_size}
|
||||||
|
onChange={handleColorMapTileSizeChanged}
|
||||||
|
min={1}
|
||||||
|
max={256}
|
||||||
|
step={1}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.color_map_tile_size}
|
||||||
|
defaultValue={DEFAULTS.color_map_tile_size}
|
||||||
|
onChange={handleColorMapTileSizeChanged}
|
||||||
|
min={1}
|
||||||
|
max={4096}
|
||||||
|
step={1}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ColorMapProcessor.displayName = 'ColorMapProcessor';
|
@ -0,0 +1,79 @@
|
|||||||
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { ContentShuffleProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<ContentShuffleProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['content_shuffle_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const ContentShuffleProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleWChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, w: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleHChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, h: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, f: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.w')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.w}
|
||||||
|
defaultValue={DEFAULTS.w}
|
||||||
|
onChange={handleWChanged}
|
||||||
|
min={0}
|
||||||
|
max={4096}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput value={config.w} defaultValue={DEFAULTS.w} onChange={handleWChanged} min={0} max={4096} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.h')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.h}
|
||||||
|
defaultValue={DEFAULTS.h}
|
||||||
|
onChange={handleHChanged}
|
||||||
|
min={0}
|
||||||
|
max={4096}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput value={config.h} defaultValue={DEFAULTS.h} onChange={handleHChanged} min={0} max={4096} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.f')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.f}
|
||||||
|
defaultValue={DEFAULTS.f}
|
||||||
|
onChange={handleFChanged}
|
||||||
|
min={0}
|
||||||
|
max={4096}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput value={config.f} defaultValue={DEFAULTS.f} onChange={handleFChanged} min={0} max={4096} />
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentShuffleProcessor.displayName = 'ContentShuffleProcessor';
|
@ -0,0 +1,62 @@
|
|||||||
|
import { Flex, FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { DWOpenposeProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<DWOpenposeProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['dw_openpose_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const DWOpenposeProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleDrawBodyChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, draw_body: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDrawFaceChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, draw_face: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDrawHandsChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, draw_hands: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<Flex sx={{ flexDir: 'row', gap: 6 }}>
|
||||||
|
<FormControl w="max-content">
|
||||||
|
<FormLabel m={0}>{t('controlnet.body')}</FormLabel>
|
||||||
|
<Switch defaultChecked={DEFAULTS.draw_body} isChecked={config.draw_body} onChange={handleDrawBodyChanged} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl w="max-content">
|
||||||
|
<FormLabel m={0}>{t('controlnet.face')}</FormLabel>
|
||||||
|
<Switch defaultChecked={DEFAULTS.draw_face} isChecked={config.draw_face} onChange={handleDrawFaceChanged} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl w="max-content">
|
||||||
|
<FormLabel m={0}>{t('controlnet.hands')}</FormLabel>
|
||||||
|
<Switch
|
||||||
|
defaultChecked={DEFAULTS.draw_hands}
|
||||||
|
isChecked={config.draw_hands}
|
||||||
|
onChange={handleDrawHandsChanged}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Flex>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DWOpenposeProcessor.displayName = 'DWOpenposeProcessor';
|
@ -0,0 +1,52 @@
|
|||||||
|
import type { ComboboxOnChange } from '@invoke-ai/ui-library';
|
||||||
|
import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { DepthAnythingModelSize, DepthAnythingProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { CA_PROCESSOR_DATA, isDepthAnythingModelSize } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<DepthAnythingProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['depth_anything_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const DepthAnythingProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const handleModelSizeChange = useCallback<ComboboxOnChange>(
|
||||||
|
(v) => {
|
||||||
|
if (!isDepthAnythingModelSize(v?.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onChange({ ...config, model_size: v.value });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const options: { label: string; value: DepthAnythingModelSize }[] = useMemo(
|
||||||
|
() => [
|
||||||
|
{ label: t('controlnet.small'), value: 'small' },
|
||||||
|
{ label: t('controlnet.base'), value: 'base' },
|
||||||
|
{ label: t('controlnet.large'), value: 'large' },
|
||||||
|
],
|
||||||
|
[t]
|
||||||
|
);
|
||||||
|
|
||||||
|
const value = useMemo(() => options.filter((o) => o.value === config.model_size)[0], [options, config.model_size]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.modelSize')}</FormLabel>
|
||||||
|
<Combobox
|
||||||
|
value={value}
|
||||||
|
defaultInputValue={DEFAULTS.model_size}
|
||||||
|
options={options}
|
||||||
|
onChange={handleModelSizeChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
DepthAnythingProcessor.displayName = 'DepthAnythingProcessor';
|
@ -0,0 +1,32 @@
|
|||||||
|
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { HedProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<HedProcessorConfig>;
|
||||||
|
|
||||||
|
export const HedProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleScribbleChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, scribble: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.scribble')}</FormLabel>
|
||||||
|
<Switch isChecked={config.scribble} onChange={handleScribbleChanged} />
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
HedProcessor.displayName = 'HedProcessor';
|
@ -0,0 +1,32 @@
|
|||||||
|
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { LineartProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<LineartProcessorConfig>;
|
||||||
|
|
||||||
|
export const LineartProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleCoarseChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, coarse: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.coarse')}</FormLabel>
|
||||||
|
<Switch isChecked={config.coarse} onChange={handleCoarseChanged} />
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
LineartProcessor.displayName = 'LineartProcessor';
|
@ -0,0 +1,73 @@
|
|||||||
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import { CA_PROCESSOR_DATA, type MediapipeFaceProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<MediapipeFaceProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['mediapipe_face_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const MediapipeFaceProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleMaxFacesChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, max_faces: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleMinConfidenceChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, min_confidence: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.maxFaces')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.max_faces}
|
||||||
|
onChange={handleMaxFacesChanged}
|
||||||
|
defaultValue={DEFAULTS.max_faces}
|
||||||
|
min={1}
|
||||||
|
max={20}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.max_faces}
|
||||||
|
onChange={handleMaxFacesChanged}
|
||||||
|
defaultValue={DEFAULTS.max_faces}
|
||||||
|
min={1}
|
||||||
|
max={20}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.minConfidence')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.min_confidence}
|
||||||
|
onChange={handleMinConfidenceChanged}
|
||||||
|
defaultValue={DEFAULTS.min_confidence}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.min_confidence}
|
||||||
|
onChange={handleMinConfidenceChanged}
|
||||||
|
defaultValue={DEFAULTS.min_confidence}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
MediapipeFaceProcessor.displayName = 'MediapipeFaceProcessor';
|
@ -0,0 +1,76 @@
|
|||||||
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { MidasDepthProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<MidasDepthProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['midas_depth_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const MidasDepthProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleAMultChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, a_mult: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleBgThChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, bg_th: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.amult')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.a_mult}
|
||||||
|
onChange={handleAMultChanged}
|
||||||
|
defaultValue={DEFAULTS.a_mult}
|
||||||
|
min={0}
|
||||||
|
max={20}
|
||||||
|
step={0.01}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.a_mult}
|
||||||
|
onChange={handleAMultChanged}
|
||||||
|
defaultValue={DEFAULTS.a_mult}
|
||||||
|
min={0}
|
||||||
|
max={20}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.bgth')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.bg_th}
|
||||||
|
onChange={handleBgThChanged}
|
||||||
|
defaultValue={DEFAULTS.bg_th}
|
||||||
|
min={0}
|
||||||
|
max={20}
|
||||||
|
step={0.01}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.bg_th}
|
||||||
|
onChange={handleBgThChanged}
|
||||||
|
defaultValue={DEFAULTS.bg_th}
|
||||||
|
min={0}
|
||||||
|
max={20}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
MidasDepthProcessor.displayName = 'MidasDepthProcessor';
|
@ -0,0 +1,76 @@
|
|||||||
|
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { MlsdProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { CA_PROCESSOR_DATA } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<MlsdProcessorConfig>;
|
||||||
|
const DEFAULTS = CA_PROCESSOR_DATA['mlsd_image_processor'].buildDefaults();
|
||||||
|
|
||||||
|
export const MlsdImageProcessor = memo(({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleThrDChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, thr_d: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleThrVChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
onChange({ ...config, thr_v: v });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.w')} </FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.thr_d}
|
||||||
|
onChange={handleThrDChanged}
|
||||||
|
defaultValue={DEFAULTS.thr_d}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.thr_d}
|
||||||
|
onChange={handleThrDChanged}
|
||||||
|
defaultValue={DEFAULTS.thr_d}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.h')} </FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
value={config.thr_v}
|
||||||
|
onChange={handleThrVChanged}
|
||||||
|
defaultValue={DEFAULTS.thr_v}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
marks
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
value={config.thr_v}
|
||||||
|
onChange={handleThrVChanged}
|
||||||
|
defaultValue={DEFAULTS.thr_v}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.01}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
MlsdImageProcessor.displayName = 'MlsdImageProcessor';
|
@ -0,0 +1,43 @@
|
|||||||
|
import { FormControl, FormLabel, Switch } from '@invoke-ai/ui-library';
|
||||||
|
import type { ProcessorComponentProps } from 'features/controlLayers/components/ControlAndIPAdapter/processors/types';
|
||||||
|
import type { PidiProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ChangeEvent } from 'react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import ProcessorWrapper from './ProcessorWrapper';
|
||||||
|
|
||||||
|
type Props = ProcessorComponentProps<PidiProcessorConfig>;
|
||||||
|
|
||||||
|
export const PidiProcessor = ({ onChange, config }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleScribbleChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, scribble: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSafeChanged = useCallback(
|
||||||
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onChange({ ...config, safe: e.target.checked });
|
||||||
|
},
|
||||||
|
[config, onChange]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.scribble')}</FormLabel>
|
||||||
|
<Switch isChecked={config.scribble} onChange={handleScribbleChanged} />
|
||||||
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel m={0}>{t('controlnet.safe')}</FormLabel>
|
||||||
|
<Switch isChecked={config.safe} onChange={handleSafeChanged} />
|
||||||
|
</FormControl>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
PidiProcessor.displayName = 'PidiProcessor';
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Flex } from '@invoke-ai/ui-library';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
type Props = PropsWithChildren;
|
||||||
|
|
||||||
|
const ProcessorWrapper = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" gap={3}>
|
||||||
|
{props.children}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ProcessorWrapper);
|
@ -0,0 +1,6 @@
|
|||||||
|
import type { ProcessorConfig } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
|
||||||
|
export type ProcessorComponentProps<T extends ProcessorConfig> = {
|
||||||
|
onChange: (config: T) => void;
|
||||||
|
config: T;
|
||||||
|
};
|
@ -2,16 +2,19 @@
|
|||||||
import { Flex } from '@invoke-ai/ui-library';
|
import { Flex } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||||
import { AddLayerButton } from 'features/controlLayers/components/AddLayerButton';
|
import { AddLayerButton } from 'features/controlLayers/components/AddLayerButton';
|
||||||
import { CALayerListItem } from 'features/controlLayers/components/CALayerListItem';
|
import { CALayer } from 'features/controlLayers/components/CALayer/CALayer';
|
||||||
import { DeleteAllLayersButton } from 'features/controlLayers/components/DeleteAllLayersButton';
|
import { DeleteAllLayersButton } from 'features/controlLayers/components/DeleteAllLayersButton';
|
||||||
import { IPLayerListItem } from 'features/controlLayers/components/IPLayerListItem';
|
import { IILayer } from 'features/controlLayers/components/IILayer/IILayer';
|
||||||
import { RGLayerListItem } from 'features/controlLayers/components/RGLayerListItem';
|
import { IPALayer } from 'features/controlLayers/components/IPALayer/IPALayer';
|
||||||
|
import { RGLayer } from 'features/controlLayers/components/RGLayer/RGLayer';
|
||||||
import { isRenderableLayer, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
import { isRenderableLayer, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { Layer } from 'features/controlLayers/store/types';
|
import type { Layer } from 'features/controlLayers/store/types';
|
||||||
import { partition } from 'lodash-es';
|
import { partition } from 'lodash-es';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const selectLayerIdTypePairs = createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
const selectLayerIdTypePairs = createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
||||||
const [renderableLayers, ipAdapterLayers] = partition(controlLayers.present.layers, isRenderableLayer);
|
const [renderableLayers, ipAdapterLayers] = partition(controlLayers.present.layers, isRenderableLayer);
|
||||||
@ -19,20 +22,24 @@ const selectLayerIdTypePairs = createMemoizedSelector(selectControlLayersSlice,
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ControlLayersPanelContent = memo(() => {
|
export const ControlLayersPanelContent = memo(() => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const layerIdTypePairs = useAppSelector(selectLayerIdTypePairs);
|
const layerIdTypePairs = useAppSelector(selectLayerIdTypePairs);
|
||||||
return (
|
return (
|
||||||
<Flex flexDir="column" gap={4} w="full" h="full">
|
<Flex flexDir="column" gap={2} w="full" h="full">
|
||||||
<Flex justifyContent="space-around">
|
<Flex justifyContent="space-around">
|
||||||
<AddLayerButton />
|
<AddLayerButton />
|
||||||
<DeleteAllLayersButton />
|
<DeleteAllLayersButton />
|
||||||
</Flex>
|
</Flex>
|
||||||
<ScrollableContent>
|
{layerIdTypePairs.length > 0 && (
|
||||||
<Flex flexDir="column" gap={4}>
|
<ScrollableContent>
|
||||||
{layerIdTypePairs.map(({ id, type }) => (
|
<Flex flexDir="column" gap={2}>
|
||||||
<LayerWrapper key={id} id={id} type={type} />
|
{layerIdTypePairs.map(({ id, type }) => (
|
||||||
))}
|
<LayerWrapper key={id} id={id} type={type} />
|
||||||
</Flex>
|
))}
|
||||||
</ScrollableContent>
|
</Flex>
|
||||||
|
</ScrollableContent>
|
||||||
|
)}
|
||||||
|
{layerIdTypePairs.length === 0 && <IAINoContentFallback icon={null} label={t('controlLayers.noLayersAdded')} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -46,13 +53,16 @@ type LayerWrapperProps = {
|
|||||||
|
|
||||||
const LayerWrapper = memo(({ id, type }: LayerWrapperProps) => {
|
const LayerWrapper = memo(({ id, type }: LayerWrapperProps) => {
|
||||||
if (type === 'regional_guidance_layer') {
|
if (type === 'regional_guidance_layer') {
|
||||||
return <RGLayerListItem key={id} layerId={id} />;
|
return <RGLayer key={id} layerId={id} />;
|
||||||
}
|
}
|
||||||
if (type === 'control_adapter_layer') {
|
if (type === 'control_adapter_layer') {
|
||||||
return <CALayerListItem key={id} layerId={id} />;
|
return <CALayer key={id} layerId={id} />;
|
||||||
}
|
}
|
||||||
if (type === 'ip_adapter_layer') {
|
if (type === 'ip_adapter_layer') {
|
||||||
return <IPLayerListItem key={id} layerId={id} />;
|
return <IPALayer key={id} layerId={id} />;
|
||||||
|
}
|
||||||
|
if (type === 'initial_image_layer') {
|
||||||
|
return <IILayer key={id} layerId={id} />;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,15 +4,26 @@ import { BrushSize } from 'features/controlLayers/components/BrushSize';
|
|||||||
import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover';
|
import ControlLayersSettingsPopover from 'features/controlLayers/components/ControlLayersSettingsPopover';
|
||||||
import { ToolChooser } from 'features/controlLayers/components/ToolChooser';
|
import { ToolChooser } from 'features/controlLayers/components/ToolChooser';
|
||||||
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
import { UndoRedoButtonGroup } from 'features/controlLayers/components/UndoRedoButtonGroup';
|
||||||
|
import { ViewerButton } from 'features/gallery/components/ImageViewer/ViewerButton';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
export const ControlLayersToolbar = memo(() => {
|
export const ControlLayersToolbar = memo(() => {
|
||||||
return (
|
return (
|
||||||
<Flex gap={4}>
|
<Flex w="full" gap={2}>
|
||||||
<BrushSize />
|
<Flex flex={1} justifyContent="center">
|
||||||
<ToolChooser />
|
<Flex gap={2} marginInlineEnd="auto" />
|
||||||
<UndoRedoButtonGroup />
|
</Flex>
|
||||||
<ControlLayersSettingsPopover />
|
<Flex flex={1} gap={2} justifyContent="center">
|
||||||
|
<BrushSize />
|
||||||
|
<ToolChooser />
|
||||||
|
<UndoRedoButtonGroup />
|
||||||
|
<ControlLayersSettingsPopover />
|
||||||
|
</Flex>
|
||||||
|
<Flex flex={1} justifyContent="center">
|
||||||
|
<Flex gap={2} marginInlineStart="auto">
|
||||||
|
<ViewerButton />
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Button } from '@invoke-ai/ui-library';
|
import { Button } from '@invoke-ai/ui-library';
|
||||||
import { allLayersDeleted } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { allLayersDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
@ -8,12 +8,19 @@ import { PiTrashSimpleBold } from 'react-icons/pi';
|
|||||||
export const DeleteAllLayersButton = memo(() => {
|
export const DeleteAllLayersButton = memo(() => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
const isDisabled = useAppSelector((s) => s.controlLayers.present.layers.length === 0);
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
dispatch(allLayersDeleted());
|
dispatch(allLayersDeleted());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button onClick={onClick} leftIcon={<PiTrashSimpleBold />} variant="ghost" colorScheme="error">
|
<Button
|
||||||
|
onClick={onClick}
|
||||||
|
leftIcon={<PiTrashSimpleBold />}
|
||||||
|
variant="ghost"
|
||||||
|
colorScheme="error"
|
||||||
|
isDisabled={isDisabled}
|
||||||
|
>
|
||||||
{t('controlLayers.deleteAll')}
|
{t('controlLayers.deleteAll')}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import IILayerOpacity from 'features/controlLayers/components/IILayer/IILayerOpacity';
|
||||||
|
import { InitialImagePreview } from 'features/controlLayers/components/IILayer/InitialImagePreview';
|
||||||
|
import { LayerDeleteButton } from 'features/controlLayers/components/LayerCommon/LayerDeleteButton';
|
||||||
|
import { LayerMenu } from 'features/controlLayers/components/LayerCommon/LayerMenu';
|
||||||
|
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
||||||
|
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
||||||
|
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
||||||
|
import {
|
||||||
|
iiLayerImageChanged,
|
||||||
|
layerSelected,
|
||||||
|
selectIILayerOrThrow,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { IILayerImageDropData } from 'features/dnd/types';
|
||||||
|
import ImageToImageStrength from 'features/parameters/components/ImageToImage/ImageToImageStrength';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import type { IILayerImagePostUploadAction, ImageDTO } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IILayer = memo(({ layerId }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const layer = useAppSelector((s) => selectIILayerOrThrow(s.controlLayers.present, layerId));
|
||||||
|
const onClick = useCallback(() => {
|
||||||
|
dispatch(layerSelected(layerId));
|
||||||
|
}, [dispatch, layerId]);
|
||||||
|
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||||
|
|
||||||
|
const onChangeImage = useCallback(
|
||||||
|
(imageDTO: ImageDTO | null) => {
|
||||||
|
dispatch(iiLayerImageChanged({ layerId, imageDTO }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const droppableData = useMemo<IILayerImageDropData>(
|
||||||
|
() => ({
|
||||||
|
actionType: 'SET_II_LAYER_IMAGE',
|
||||||
|
context: {
|
||||||
|
layerId,
|
||||||
|
},
|
||||||
|
id: layerId,
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const postUploadAction = useMemo<IILayerImagePostUploadAction>(
|
||||||
|
() => ({
|
||||||
|
layerId,
|
||||||
|
type: 'SET_II_LAYER_IMAGE',
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayerWrapper onClick={onClick} borderColor={layer.isSelected ? 'base.400' : 'base.800'}>
|
||||||
|
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
||||||
|
<LayerVisibilityToggle layerId={layerId} />
|
||||||
|
<LayerTitle type="initial_image_layer" />
|
||||||
|
<Spacer />
|
||||||
|
<IILayerOpacity layerId={layerId} />
|
||||||
|
<LayerMenu layerId={layerId} />
|
||||||
|
<LayerDeleteButton layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
{isOpen && (
|
||||||
|
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
||||||
|
<ImageToImageStrength />
|
||||||
|
<InitialImagePreview
|
||||||
|
image={layer.image}
|
||||||
|
onChangeImage={onChangeImage}
|
||||||
|
droppableData={droppableData}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</LayerWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
IILayer.displayName = 'IILayer';
|
@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
CompositeNumberInput,
|
||||||
|
CompositeSlider,
|
||||||
|
Flex,
|
||||||
|
FormControl,
|
||||||
|
FormLabel,
|
||||||
|
IconButton,
|
||||||
|
Popover,
|
||||||
|
PopoverArrow,
|
||||||
|
PopoverBody,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@invoke-ai/ui-library';
|
||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
|
import {
|
||||||
|
iiLayerOpacityChanged,
|
||||||
|
isInitialImageLayer,
|
||||||
|
selectControlLayersSlice,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiDropHalfFill } from 'react-icons/pi';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const marks = [0, 25, 50, 75, 100];
|
||||||
|
const formatPct = (v: number | string) => `${v} %`;
|
||||||
|
|
||||||
|
const IILayerOpacity = ({ layerId }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const selectOpacity = useMemo(
|
||||||
|
() =>
|
||||||
|
createSelector(selectControlLayersSlice, (controlLayers) => {
|
||||||
|
const layer = controlLayers.present.layers.filter(isInitialImageLayer).find((l) => l.id === layerId);
|
||||||
|
assert(layer, `Layer ${layerId} not found`);
|
||||||
|
return Math.round(layer.opacity * 100);
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
const opacity = useAppSelector(selectOpacity);
|
||||||
|
const onChangeOpacity = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
dispatch(iiLayerOpacityChanged({ layerId, opacity: v / 100 }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Popover isLazy>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<IconButton
|
||||||
|
aria-label={t('controlLayers.opacity')}
|
||||||
|
size="sm"
|
||||||
|
icon={<PiDropHalfFill size={16} />}
|
||||||
|
variant="ghost"
|
||||||
|
onDoubleClick={stopPropagation}
|
||||||
|
/>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent onDoubleClick={stopPropagation}>
|
||||||
|
<PopoverArrow />
|
||||||
|
<PopoverBody>
|
||||||
|
<Flex direction="column" gap={2}>
|
||||||
|
<FormControl orientation="horizontal">
|
||||||
|
<FormLabel m={0}>{t('controlLayers.opacity')}</FormLabel>
|
||||||
|
<CompositeSlider
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
value={opacity}
|
||||||
|
defaultValue={100}
|
||||||
|
onChange={onChangeOpacity}
|
||||||
|
marks={marks}
|
||||||
|
w={48}
|
||||||
|
/>
|
||||||
|
<CompositeNumberInput
|
||||||
|
min={0}
|
||||||
|
max={100}
|
||||||
|
step={1}
|
||||||
|
value={opacity}
|
||||||
|
defaultValue={100}
|
||||||
|
onChange={onChangeOpacity}
|
||||||
|
w={24}
|
||||||
|
format={formatPct}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</Flex>
|
||||||
|
</PopoverBody>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(IILayerOpacity);
|
@ -0,0 +1,109 @@
|
|||||||
|
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
||||||
|
import { Flex, useShiftModifier } from '@invoke-ai/ui-library';
|
||||||
|
import { skipToken } from '@reduxjs/toolkit/query';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import IAIDndImage from 'common/components/IAIDndImage';
|
||||||
|
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||||
|
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||||
|
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { ImageWithDims } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { ImageDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
||||||
|
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
||||||
|
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
||||||
|
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
||||||
|
import { memo, useCallback, useEffect, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { PiArrowCounterClockwiseBold, PiRulerBold } from 'react-icons/pi';
|
||||||
|
import { useGetImageDTOQuery } from 'services/api/endpoints/images';
|
||||||
|
import type { ImageDTO, PostUploadAction } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
image: ImageWithDims | null;
|
||||||
|
onChangeImage: (imageDTO: ImageDTO | null) => void;
|
||||||
|
droppableData: TypesafeDroppableData;
|
||||||
|
postUploadAction: PostUploadAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InitialImagePreview = memo(({ image, onChangeImage, droppableData, postUploadAction }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const isConnected = useAppSelector((s) => s.system.isConnected);
|
||||||
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
const optimalDimension = useAppSelector(selectOptimalDimension);
|
||||||
|
const shift = useShiftModifier();
|
||||||
|
|
||||||
|
const { currentData: imageDTO, isError: isErrorControlImage } = useGetImageDTOQuery(image?.imageName ?? skipToken);
|
||||||
|
|
||||||
|
const onReset = useCallback(() => {
|
||||||
|
onChangeImage(null);
|
||||||
|
}, [onChangeImage]);
|
||||||
|
|
||||||
|
const onUseSize = useCallback(() => {
|
||||||
|
if (!imageDTO) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeTabName === 'canvas') {
|
||||||
|
dispatch(setBoundingBoxDimensions({ width: imageDTO.width, height: imageDTO.height }, optimalDimension));
|
||||||
|
} else {
|
||||||
|
const options = { updateAspectRatio: true, clamp: true };
|
||||||
|
if (shift) {
|
||||||
|
const { width, height } = imageDTO;
|
||||||
|
dispatch(widthChanged({ width, ...options }));
|
||||||
|
dispatch(heightChanged({ height, ...options }));
|
||||||
|
} else {
|
||||||
|
const { width, height } = calculateNewSize(
|
||||||
|
imageDTO.width / imageDTO.height,
|
||||||
|
optimalDimension * optimalDimension
|
||||||
|
);
|
||||||
|
dispatch(widthChanged({ width, ...options }));
|
||||||
|
dispatch(heightChanged({ height, ...options }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [imageDTO, activeTabName, dispatch, optimalDimension, shift]);
|
||||||
|
|
||||||
|
const draggableData = useMemo<ImageDraggableData | undefined>(() => {
|
||||||
|
if (imageDTO) {
|
||||||
|
return {
|
||||||
|
id: 'initial_image_layer',
|
||||||
|
payloadType: 'IMAGE_DTO',
|
||||||
|
payload: { imageDTO: imageDTO },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [imageDTO]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isConnected && isErrorControlImage) {
|
||||||
|
onReset();
|
||||||
|
}
|
||||||
|
}, [onReset, isConnected, isErrorControlImage]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex position="relative" w="full" h={36} alignItems="center" justifyContent="center">
|
||||||
|
<IAIDndImage
|
||||||
|
draggableData={draggableData}
|
||||||
|
droppableData={droppableData}
|
||||||
|
imageDTO={imageDTO}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={onReset}
|
||||||
|
icon={imageDTO ? <PiArrowCounterClockwiseBold size={16} /> : undefined}
|
||||||
|
tooltip={t('controlnet.resetControlImage')}
|
||||||
|
/>
|
||||||
|
<IAIDndImageIcon
|
||||||
|
onClick={onUseSize}
|
||||||
|
icon={imageDTO ? <PiRulerBold size={16} /> : undefined}
|
||||||
|
tooltip={shift ? t('controlnet.setControlImageDimensionsForce') : t('controlnet.setControlImageDimensions')}
|
||||||
|
styleOverrides={useSizeStyleOverrides}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
InitialImagePreview.displayName = 'InitialImagePreview';
|
||||||
|
|
||||||
|
const useSizeStyleOverrides: SystemStyleObject = { mt: 6 };
|
@ -0,0 +1,32 @@
|
|||||||
|
import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
||||||
|
import { IPALayerIPAdapterWrapper } from 'features/controlLayers/components/IPALayer/IPALayerIPAdapterWrapper';
|
||||||
|
import { LayerDeleteButton } from 'features/controlLayers/components/LayerCommon/LayerDeleteButton';
|
||||||
|
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
||||||
|
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
||||||
|
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IPALayer = memo(({ layerId }: Props) => {
|
||||||
|
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||||
|
return (
|
||||||
|
<LayerWrapper borderColor="base.800">
|
||||||
|
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
||||||
|
<LayerVisibilityToggle layerId={layerId} />
|
||||||
|
<LayerTitle type="ip_adapter_layer" />
|
||||||
|
<Spacer />
|
||||||
|
<LayerDeleteButton layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
{isOpen && (
|
||||||
|
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
||||||
|
<IPALayerIPAdapterWrapper layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</LayerWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
IPALayer.displayName = 'IPALayer';
|
@ -0,0 +1,106 @@
|
|||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { IPAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapter';
|
||||||
|
import {
|
||||||
|
caOrIPALayerBeginEndStepPctChanged,
|
||||||
|
caOrIPALayerWeightChanged,
|
||||||
|
ipaLayerCLIPVisionModelChanged,
|
||||||
|
ipaLayerImageChanged,
|
||||||
|
ipaLayerMethodChanged,
|
||||||
|
ipaLayerModelChanged,
|
||||||
|
selectIPALayerOrThrow,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { IPALayerImageDropData } from 'features/dnd/types';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig, IPALayerImagePostUploadAction } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IPALayerIPAdapterWrapper = memo(({ layerId }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const ipAdapter = useAppSelector((s) => selectIPALayerOrThrow(s.controlLayers.present, layerId).ipAdapter);
|
||||||
|
|
||||||
|
const onChangeBeginEndStepPct = useCallback(
|
||||||
|
(beginEndStepPct: [number, number]) => {
|
||||||
|
dispatch(
|
||||||
|
caOrIPALayerBeginEndStepPctChanged({
|
||||||
|
layerId,
|
||||||
|
beginEndStepPct,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeWeight = useCallback(
|
||||||
|
(weight: number) => {
|
||||||
|
dispatch(caOrIPALayerWeightChanged({ layerId, weight }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeIPMethod = useCallback(
|
||||||
|
(method: IPMethodV2) => {
|
||||||
|
dispatch(ipaLayerMethodChanged({ layerId, method }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeModel = useCallback(
|
||||||
|
(modelConfig: IPAdapterModelConfig) => {
|
||||||
|
dispatch(ipaLayerModelChanged({ layerId, modelConfig }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeCLIPVisionModel = useCallback(
|
||||||
|
(clipVisionModel: CLIPVisionModelV2) => {
|
||||||
|
dispatch(ipaLayerCLIPVisionModelChanged({ layerId, clipVisionModel }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeImage = useCallback(
|
||||||
|
(imageDTO: ImageDTO | null) => {
|
||||||
|
dispatch(ipaLayerImageChanged({ layerId, imageDTO }));
|
||||||
|
},
|
||||||
|
[dispatch, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const droppableData = useMemo<IPALayerImageDropData>(
|
||||||
|
() => ({
|
||||||
|
actionType: 'SET_IPA_LAYER_IMAGE',
|
||||||
|
context: {
|
||||||
|
layerId,
|
||||||
|
},
|
||||||
|
id: layerId,
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const postUploadAction = useMemo<IPALayerImagePostUploadAction>(
|
||||||
|
() => ({
|
||||||
|
type: 'SET_IPA_LAYER_IMAGE',
|
||||||
|
layerId,
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IPAdapter
|
||||||
|
ipAdapter={ipAdapter}
|
||||||
|
onChangeBeginEndStepPct={onChangeBeginEndStepPct}
|
||||||
|
onChangeWeight={onChangeWeight}
|
||||||
|
onChangeIPMethod={onChangeIPMethod}
|
||||||
|
onChangeModel={onChangeModel}
|
||||||
|
onChangeCLIPVisionModel={onChangeCLIPVisionModel}
|
||||||
|
onChangeImage={onChangeImage}
|
||||||
|
droppableData={droppableData}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
IPALayerIPAdapterWrapper.displayName = 'IPALayerIPAdapterWrapper';
|
@ -1,47 +0,0 @@
|
|||||||
import { Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import ControlAdapterLayerConfig from 'features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig';
|
|
||||||
import { LayerDeleteButton } from 'features/controlLayers/components/LayerDeleteButton';
|
|
||||||
import { LayerTitle } from 'features/controlLayers/components/LayerTitle';
|
|
||||||
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerVisibilityToggle';
|
|
||||||
import { isIPAdapterLayer, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { memo, useMemo } from 'react';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
layerId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const IPLayerListItem = memo(({ layerId }: Props) => {
|
|
||||||
const selector = useMemo(
|
|
||||||
() =>
|
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
|
||||||
assert(isIPAdapterLayer(layer), `Layer ${layerId} not found or not an IP Adapter layer`);
|
|
||||||
return layer.ipAdapterId;
|
|
||||||
}),
|
|
||||||
[layerId]
|
|
||||||
);
|
|
||||||
const ipAdapterId = useAppSelector(selector);
|
|
||||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
|
||||||
return (
|
|
||||||
<Flex gap={2} bg="base.800" borderRadius="base" p="1px" px={2}>
|
|
||||||
<Flex flexDir="column" w="full" bg="base.850" borderRadius="base">
|
|
||||||
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
|
||||||
<LayerVisibilityToggle layerId={layerId} />
|
|
||||||
<LayerTitle type="ip_adapter_layer" />
|
|
||||||
<Spacer />
|
|
||||||
<LayerDeleteButton layerId={layerId} />
|
|
||||||
</Flex>
|
|
||||||
{isOpen && (
|
|
||||||
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
|
||||||
<ControlAdapterLayerConfig id={ipAdapterId} />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
IPLayerListItem.displayName = 'IPLayerListItem';
|
|
@ -1,7 +1,7 @@
|
|||||||
import { IconButton } from '@invoke-ai/ui-library';
|
import { IconButton } from '@invoke-ai/ui-library';
|
||||||
import { guidanceLayerDeleted } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
|
import { layerDeleted } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
@ -12,7 +12,7 @@ export const LayerDeleteButton = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const deleteLayer = useCallback(() => {
|
const deleteLayer = useCallback(() => {
|
||||||
dispatch(guidanceLayerDeleted(layerId));
|
dispatch(layerDeleted(layerId));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
return (
|
return (
|
||||||
<IconButton
|
<IconButton
|
@ -1,8 +1,8 @@
|
|||||||
import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
import { IconButton, Menu, MenuButton, MenuDivider, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { LayerMenuArrangeActions } from 'features/controlLayers/components/LayerMenuArrangeActions';
|
import { LayerMenuArrangeActions } from 'features/controlLayers/components/LayerCommon/LayerMenuArrangeActions';
|
||||||
import { LayerMenuRGActions } from 'features/controlLayers/components/LayerMenuRGActions';
|
import { LayerMenuRGActions } from 'features/controlLayers/components/LayerCommon/LayerMenuRGActions';
|
||||||
import { useLayerType } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useLayerType } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { layerDeleted, layerReset } from 'features/controlLayers/store/controlLayersSlice';
|
import { layerDeleted, layerReset } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
@ -37,7 +37,9 @@ export const LayerMenu = memo(({ layerId }: Props) => {
|
|||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{(layerType === 'regional_guidance_layer' || layerType === 'control_adapter_layer') && (
|
{(layerType === 'regional_guidance_layer' ||
|
||||||
|
layerType === 'control_adapter_layer' ||
|
||||||
|
layerType === 'initial_image_layer') && (
|
||||||
<>
|
<>
|
||||||
<LayerMenuArrangeActions layerId={layerId} />
|
<LayerMenuArrangeActions layerId={layerId} />
|
||||||
<MenuDivider />
|
<MenuDivider />
|
@ -1,11 +1,11 @@
|
|||||||
import { MenuItem } from '@invoke-ai/ui-library';
|
import { MenuItem } from '@invoke-ai/ui-library';
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
import { guidanceLayerIPAdapterAdded } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { useAddIPAdapterToIPALayer } from 'features/controlLayers/hooks/addLayerHooks';
|
||||||
import {
|
import {
|
||||||
isRegionalGuidanceLayer,
|
isRegionalGuidanceLayer,
|
||||||
maskLayerNegativePromptChanged,
|
rgLayerNegativePromptChanged,
|
||||||
maskLayerPositivePromptChanged,
|
rgLayerPositivePromptChanged,
|
||||||
selectControlLayersSlice,
|
selectControlLayersSlice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -18,6 +18,7 @@ type Props = { layerId: string };
|
|||||||
export const LayerMenuRGActions = memo(({ layerId }: Props) => {
|
export const LayerMenuRGActions = memo(({ layerId }: Props) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const [addIPAdapter, isAddIPAdapterDisabled] = useAddIPAdapterToIPALayer(layerId);
|
||||||
const selectValidActions = useMemo(
|
const selectValidActions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
||||||
@ -32,13 +33,10 @@ export const LayerMenuRGActions = memo(({ layerId }: Props) => {
|
|||||||
);
|
);
|
||||||
const validActions = useAppSelector(selectValidActions);
|
const validActions = useAppSelector(selectValidActions);
|
||||||
const addPositivePrompt = useCallback(() => {
|
const addPositivePrompt = useCallback(() => {
|
||||||
dispatch(maskLayerPositivePromptChanged({ layerId, prompt: '' }));
|
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
const addNegativePrompt = useCallback(() => {
|
const addNegativePrompt = useCallback(() => {
|
||||||
dispatch(maskLayerNegativePromptChanged({ layerId, prompt: '' }));
|
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: '' }));
|
||||||
}, [dispatch, layerId]);
|
|
||||||
const addIPAdapter = useCallback(() => {
|
|
||||||
dispatch(guidanceLayerIPAdapterAdded(layerId));
|
|
||||||
}, [dispatch, layerId]);
|
}, [dispatch, layerId]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -48,7 +46,7 @@ export const LayerMenuRGActions = memo(({ layerId }: Props) => {
|
|||||||
<MenuItem onClick={addNegativePrompt} isDisabled={!validActions.canAddNegativePrompt} icon={<PiPlusBold />}>
|
<MenuItem onClick={addNegativePrompt} isDisabled={!validActions.canAddNegativePrompt} icon={<PiPlusBold />}>
|
||||||
{t('controlLayers.addNegativePrompt')}
|
{t('controlLayers.addNegativePrompt')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={addIPAdapter} icon={<PiPlusBold />}>
|
<MenuItem onClick={addIPAdapter} icon={<PiPlusBold />} isDisabled={isAddIPAdapterDisabled}>
|
||||||
{t('controlLayers.addIPAdapter')}
|
{t('controlLayers.addIPAdapter')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</>
|
</>
|
@ -16,6 +16,8 @@ export const LayerTitle = memo(({ type }: Props) => {
|
|||||||
return t('controlLayers.globalControlAdapter');
|
return t('controlLayers.globalControlAdapter');
|
||||||
} else if (type === 'ip_adapter_layer') {
|
} else if (type === 'ip_adapter_layer') {
|
||||||
return t('controlLayers.globalIPAdapter');
|
return t('controlLayers.globalIPAdapter');
|
||||||
|
} else if (type === 'initial_image_layer') {
|
||||||
|
return t('controlLayers.globalInitialImage');
|
||||||
}
|
}
|
||||||
}, [t, type]);
|
}, [t, type]);
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
import type { ChakraProps } from '@invoke-ai/ui-library';
|
||||||
|
import { Flex } from '@invoke-ai/ui-library';
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
type Props = PropsWithChildren<{
|
||||||
|
onClick?: () => void;
|
||||||
|
borderColor: ChakraProps['bg'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export const LayerWrapper = memo(({ onClick, borderColor, children }: Props) => {
|
||||||
|
return (
|
||||||
|
<Flex gap={2} onClick={onClick} bg={borderColor} px={2} borderRadius="base" py="1px">
|
||||||
|
<Flex flexDir="column" w="full" bg="base.850" borderRadius="base">
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
LayerWrapper.displayName = 'LayerWrapper';
|
@ -0,0 +1,83 @@
|
|||||||
|
import { Badge, Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
||||||
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
||||||
|
import { AddPromptButtons } from 'features/controlLayers/components/AddPromptButtons';
|
||||||
|
import { LayerDeleteButton } from 'features/controlLayers/components/LayerCommon/LayerDeleteButton';
|
||||||
|
import { LayerMenu } from 'features/controlLayers/components/LayerCommon/LayerMenu';
|
||||||
|
import { LayerTitle } from 'features/controlLayers/components/LayerCommon/LayerTitle';
|
||||||
|
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerCommon/LayerVisibilityToggle';
|
||||||
|
import { LayerWrapper } from 'features/controlLayers/components/LayerCommon/LayerWrapper';
|
||||||
|
import {
|
||||||
|
isRegionalGuidanceLayer,
|
||||||
|
layerSelected,
|
||||||
|
selectControlLayersSlice,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
import { RGLayerColorPicker } from './RGLayerColorPicker';
|
||||||
|
import { RGLayerIPAdapterList } from './RGLayerIPAdapterList';
|
||||||
|
import { RGLayerNegativePrompt } from './RGLayerNegativePrompt';
|
||||||
|
import { RGLayerPositivePrompt } from './RGLayerPositivePrompt';
|
||||||
|
import RGLayerSettingsPopover from './RGLayerSettingsPopover';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RGLayer = memo(({ layerId }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const selector = useMemo(
|
||||||
|
() =>
|
||||||
|
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
||||||
|
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
||||||
|
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
||||||
|
return {
|
||||||
|
color: rgbColorToString(layer.previewColor),
|
||||||
|
hasPositivePrompt: layer.positivePrompt !== null,
|
||||||
|
hasNegativePrompt: layer.negativePrompt !== null,
|
||||||
|
hasIPAdapters: layer.ipAdapters.length > 0,
|
||||||
|
isSelected: layerId === controlLayers.present.selectedLayerId,
|
||||||
|
autoNegative: layer.autoNegative,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
const { autoNegative, color, hasPositivePrompt, hasNegativePrompt, hasIPAdapters, isSelected } =
|
||||||
|
useAppSelector(selector);
|
||||||
|
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
||||||
|
const onClick = useCallback(() => {
|
||||||
|
dispatch(layerSelected(layerId));
|
||||||
|
}, [dispatch, layerId]);
|
||||||
|
return (
|
||||||
|
<LayerWrapper onClick={onClick} borderColor={isSelected ? color : 'base.800'}>
|
||||||
|
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
||||||
|
<LayerVisibilityToggle layerId={layerId} />
|
||||||
|
<LayerTitle type="regional_guidance_layer" />
|
||||||
|
<Spacer />
|
||||||
|
{autoNegative === 'invert' && (
|
||||||
|
<Badge color="base.300" bg="transparent" borderWidth={1} userSelect="none">
|
||||||
|
{t('controlLayers.autoNegative')}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
<RGLayerColorPicker layerId={layerId} />
|
||||||
|
<RGLayerSettingsPopover layerId={layerId} />
|
||||||
|
<LayerMenu layerId={layerId} />
|
||||||
|
<LayerDeleteButton layerId={layerId} />
|
||||||
|
</Flex>
|
||||||
|
{isOpen && (
|
||||||
|
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
||||||
|
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && <AddPromptButtons layerId={layerId} />}
|
||||||
|
{hasPositivePrompt && <RGLayerPositivePrompt layerId={layerId} />}
|
||||||
|
{hasNegativePrompt && <RGLayerNegativePrompt layerId={layerId} />}
|
||||||
|
{hasIPAdapters && <RGLayerIPAdapterList layerId={layerId} />}
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</LayerWrapper>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RGLayer.displayName = 'RGLayer';
|
@ -3,7 +3,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
isRegionalGuidanceLayer,
|
isRegionalGuidanceLayer,
|
||||||
maskLayerAutoNegativeChanged,
|
rgLayerAutoNegativeChanged,
|
||||||
selectControlLayersSlice,
|
selectControlLayersSlice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import type { ChangeEvent } from 'react';
|
import type { ChangeEvent } from 'react';
|
||||||
@ -35,7 +35,7 @@ export const RGLayerAutoNegativeCheckbox = memo(({ layerId }: Props) => {
|
|||||||
const autoNegative = useAutoNegative(layerId);
|
const autoNegative = useAutoNegative(layerId);
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
dispatch(maskLayerAutoNegativeChanged({ layerId, autoNegative: e.target.checked ? 'invert' : 'off' }));
|
dispatch(rgLayerAutoNegativeChanged({ layerId, autoNegative: e.target.checked ? 'invert' : 'off' }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
@ -6,7 +6,7 @@ import { stopPropagation } from 'common/util/stopPropagation';
|
|||||||
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
||||||
import {
|
import {
|
||||||
isRegionalGuidanceLayer,
|
isRegionalGuidanceLayer,
|
||||||
maskLayerPreviewColorChanged,
|
rgLayerPreviewColorChanged,
|
||||||
selectControlLayersSlice,
|
selectControlLayersSlice,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
@ -33,7 +33,7 @@ export const RGLayerColorPicker = memo(({ layerId }: Props) => {
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const onColorChange = useCallback(
|
const onColorChange = useCallback(
|
||||||
(color: RgbColor) => {
|
(color: RgbColor) => {
|
||||||
dispatch(maskLayerPreviewColorChanged({ layerId, color }));
|
dispatch(rgLayerPreviewColorChanged({ layerId, color }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
@ -0,0 +1,45 @@
|
|||||||
|
import { Divider, Flex } from '@invoke-ai/ui-library';
|
||||||
|
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||||
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { RGLayerIPAdapterWrapper } from 'features/controlLayers/components/RGLayer/RGLayerIPAdapterWrapper';
|
||||||
|
import { isRegionalGuidanceLayer, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import { memo, useMemo } from 'react';
|
||||||
|
import { assert } from 'tsafe';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RGLayerIPAdapterList = memo(({ layerId }: Props) => {
|
||||||
|
const selectIPAdapterIds = useMemo(
|
||||||
|
() =>
|
||||||
|
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
||||||
|
const layer = controlLayers.present.layers.filter(isRegionalGuidanceLayer).find((l) => l.id === layerId);
|
||||||
|
assert(layer, `Layer ${layerId} not found`);
|
||||||
|
return layer.ipAdapters;
|
||||||
|
}),
|
||||||
|
[layerId]
|
||||||
|
);
|
||||||
|
const ipAdapters = useAppSelector(selectIPAdapterIds);
|
||||||
|
|
||||||
|
if (ipAdapters.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ipAdapters.map(({ id }, index) => (
|
||||||
|
<Flex flexDir="column" key={id}>
|
||||||
|
{index > 0 && (
|
||||||
|
<Flex pb={3}>
|
||||||
|
<Divider />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
<RGLayerIPAdapterWrapper layerId={layerId} ipAdapterId={id} ipAdapterNumber={index + 1} />
|
||||||
|
</Flex>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RGLayerIPAdapterList.displayName = 'RGLayerIPAdapterList';
|
@ -0,0 +1,131 @@
|
|||||||
|
import { Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
|
||||||
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { IPAdapter } from 'features/controlLayers/components/ControlAndIPAdapter/IPAdapter';
|
||||||
|
import {
|
||||||
|
rgLayerIPAdapterBeginEndStepPctChanged,
|
||||||
|
rgLayerIPAdapterCLIPVisionModelChanged,
|
||||||
|
rgLayerIPAdapterDeleted,
|
||||||
|
rgLayerIPAdapterImageChanged,
|
||||||
|
rgLayerIPAdapterMethodChanged,
|
||||||
|
rgLayerIPAdapterModelChanged,
|
||||||
|
rgLayerIPAdapterWeightChanged,
|
||||||
|
selectRGLayerIPAdapterOrThrow,
|
||||||
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
|
import type { CLIPVisionModelV2, IPMethodV2 } from 'features/controlLayers/util/controlAdapters';
|
||||||
|
import type { RGLayerIPAdapterImageDropData } from 'features/dnd/types';
|
||||||
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
|
import { PiTrashSimpleBold } from 'react-icons/pi';
|
||||||
|
import type { ImageDTO, IPAdapterModelConfig, RGLayerIPAdapterImagePostUploadAction } from 'services/api/types';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
layerId: string;
|
||||||
|
ipAdapterId: string;
|
||||||
|
ipAdapterNumber: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RGLayerIPAdapterWrapper = memo(({ layerId, ipAdapterId, ipAdapterNumber }: Props) => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const onDeleteIPAdapter = useCallback(() => {
|
||||||
|
dispatch(rgLayerIPAdapterDeleted({ layerId, ipAdapterId }));
|
||||||
|
}, [dispatch, ipAdapterId, layerId]);
|
||||||
|
const ipAdapter = useAppSelector((s) => selectRGLayerIPAdapterOrThrow(s.controlLayers.present, layerId, ipAdapterId));
|
||||||
|
|
||||||
|
const onChangeBeginEndStepPct = useCallback(
|
||||||
|
(beginEndStepPct: [number, number]) => {
|
||||||
|
dispatch(
|
||||||
|
rgLayerIPAdapterBeginEndStepPctChanged({
|
||||||
|
layerId,
|
||||||
|
ipAdapterId,
|
||||||
|
beginEndStepPct,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch, ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeWeight = useCallback(
|
||||||
|
(weight: number) => {
|
||||||
|
dispatch(rgLayerIPAdapterWeightChanged({ layerId, ipAdapterId, weight }));
|
||||||
|
},
|
||||||
|
[dispatch, ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeIPMethod = useCallback(
|
||||||
|
(method: IPMethodV2) => {
|
||||||
|
dispatch(rgLayerIPAdapterMethodChanged({ layerId, ipAdapterId, method }));
|
||||||
|
},
|
||||||
|
[dispatch, ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeModel = useCallback(
|
||||||
|
(modelConfig: IPAdapterModelConfig) => {
|
||||||
|
dispatch(rgLayerIPAdapterModelChanged({ layerId, ipAdapterId, modelConfig }));
|
||||||
|
},
|
||||||
|
[dispatch, ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeCLIPVisionModel = useCallback(
|
||||||
|
(clipVisionModel: CLIPVisionModelV2) => {
|
||||||
|
dispatch(rgLayerIPAdapterCLIPVisionModelChanged({ layerId, ipAdapterId, clipVisionModel }));
|
||||||
|
},
|
||||||
|
[dispatch, ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeImage = useCallback(
|
||||||
|
(imageDTO: ImageDTO | null) => {
|
||||||
|
dispatch(rgLayerIPAdapterImageChanged({ layerId, ipAdapterId, imageDTO }));
|
||||||
|
},
|
||||||
|
[dispatch, ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const droppableData = useMemo<RGLayerIPAdapterImageDropData>(
|
||||||
|
() => ({
|
||||||
|
actionType: 'SET_RG_LAYER_IP_ADAPTER_IMAGE',
|
||||||
|
context: {
|
||||||
|
layerId,
|
||||||
|
ipAdapterId,
|
||||||
|
},
|
||||||
|
id: layerId,
|
||||||
|
}),
|
||||||
|
[ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const postUploadAction = useMemo<RGLayerIPAdapterImagePostUploadAction>(
|
||||||
|
() => ({
|
||||||
|
type: 'SET_RG_LAYER_IP_ADAPTER_IMAGE',
|
||||||
|
layerId,
|
||||||
|
ipAdapterId,
|
||||||
|
}),
|
||||||
|
[ipAdapterId, layerId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex flexDir="column" gap={3}>
|
||||||
|
<Flex alignItems="center" gap={3}>
|
||||||
|
<Text fontWeight="semibold" color="base.400">{`IP Adapter ${ipAdapterNumber}`}</Text>
|
||||||
|
<Spacer />
|
||||||
|
<IconButton
|
||||||
|
size="sm"
|
||||||
|
icon={<PiTrashSimpleBold />}
|
||||||
|
aria-label="Delete IP Adapter"
|
||||||
|
onClick={onDeleteIPAdapter}
|
||||||
|
variant="ghost"
|
||||||
|
colorScheme="error"
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<IPAdapter
|
||||||
|
ipAdapter={ipAdapter}
|
||||||
|
onChangeBeginEndStepPct={onChangeBeginEndStepPct}
|
||||||
|
onChangeWeight={onChangeWeight}
|
||||||
|
onChangeIPMethod={onChangeIPMethod}
|
||||||
|
onChangeModel={onChangeModel}
|
||||||
|
onChangeCLIPVisionModel={onChangeCLIPVisionModel}
|
||||||
|
onChangeImage={onChangeImage}
|
||||||
|
droppableData={droppableData}
|
||||||
|
postUploadAction={postUploadAction}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
RGLayerIPAdapterWrapper.displayName = 'RGLayerIPAdapterWrapper';
|
@ -1,8 +1,8 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayerPromptDeleteButton';
|
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayer/RGLayerPromptDeleteButton';
|
||||||
import { useLayerNegativePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useLayerNegativePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { maskLayerNegativePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { rgLayerNegativePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||||
@ -21,7 +21,7 @@ export const RGLayerNegativePrompt = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const _onChange = useCallback(
|
const _onChange = useCallback(
|
||||||
(v: string) => {
|
(v: string) => {
|
||||||
dispatch(maskLayerNegativePromptChanged({ layerId, prompt: v }));
|
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: v }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
@ -1,8 +1,8 @@
|
|||||||
import { Box, Textarea } from '@invoke-ai/ui-library';
|
import { Box, Textarea } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayerPromptDeleteButton';
|
import { RGLayerPromptDeleteButton } from 'features/controlLayers/components/RGLayer/RGLayerPromptDeleteButton';
|
||||||
import { useLayerPositivePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
import { useLayerPositivePrompt } from 'features/controlLayers/hooks/layerStateHooks';
|
||||||
import { maskLayerPositivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
import { rgLayerPositivePromptChanged } from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
import { PromptOverlayButtonWrapper } from 'features/parameters/components/Prompts/PromptOverlayButtonWrapper';
|
||||||
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
import { AddPromptTriggerButton } from 'features/prompt/AddPromptTriggerButton';
|
||||||
import { PromptPopover } from 'features/prompt/PromptPopover';
|
import { PromptPopover } from 'features/prompt/PromptPopover';
|
||||||
@ -21,7 +21,7 @@ export const RGLayerPositivePrompt = memo(({ layerId }: Props) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const _onChange = useCallback(
|
const _onChange = useCallback(
|
||||||
(v: string) => {
|
(v: string) => {
|
||||||
dispatch(maskLayerPositivePromptChanged({ layerId, prompt: v }));
|
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: v }));
|
||||||
},
|
},
|
||||||
[dispatch, layerId]
|
[dispatch, layerId]
|
||||||
);
|
);
|
@ -1,8 +1,8 @@
|
|||||||
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
import { IconButton, Tooltip } from '@invoke-ai/ui-library';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import {
|
import {
|
||||||
maskLayerNegativePromptChanged,
|
rgLayerNegativePromptChanged,
|
||||||
maskLayerPositivePromptChanged,
|
rgLayerPositivePromptChanged,
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
} from 'features/controlLayers/store/controlLayersSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -18,9 +18,9 @@ export const RGLayerPromptDeleteButton = memo(({ layerId, polarity }: Props) =>
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
if (polarity === 'positive') {
|
if (polarity === 'positive') {
|
||||||
dispatch(maskLayerPositivePromptChanged({ layerId, prompt: null }));
|
dispatch(rgLayerPositivePromptChanged({ layerId, prompt: null }));
|
||||||
} else {
|
} else {
|
||||||
dispatch(maskLayerNegativePromptChanged({ layerId, prompt: null }));
|
dispatch(rgLayerNegativePromptChanged({ layerId, prompt: null }));
|
||||||
}
|
}
|
||||||
}, [dispatch, layerId, polarity]);
|
}, [dispatch, layerId, polarity]);
|
||||||
return (
|
return (
|
@ -10,7 +10,7 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@invoke-ai/ui-library';
|
} from '@invoke-ai/ui-library';
|
||||||
import { stopPropagation } from 'common/util/stopPropagation';
|
import { stopPropagation } from 'common/util/stopPropagation';
|
||||||
import { RGLayerAutoNegativeCheckbox } from 'features/controlLayers/components/RGLayerAutoNegativeCheckbox';
|
import { RGLayerAutoNegativeCheckbox } from 'features/controlLayers/components/RGLayer/RGLayerAutoNegativeCheckbox';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PiGearSixBold } from 'react-icons/pi';
|
import { PiGearSixBold } from 'react-icons/pi';
|
@ -1,80 +0,0 @@
|
|||||||
import { Divider, Flex, IconButton, Spacer, Text } from '@invoke-ai/ui-library';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { guidanceLayerIPAdapterDeleted } from 'app/store/middleware/listenerMiddleware/listeners/controlLayersToControlAdapterBridge';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import ControlAdapterLayerConfig from 'features/controlLayers/components/controlAdapterOverrides/ControlAdapterLayerConfig';
|
|
||||||
import { isRegionalGuidanceLayer, selectControlLayersSlice } from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { PiTrashSimpleBold } from 'react-icons/pi';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
layerId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RGLayerIPAdapterList = memo(({ layerId }: Props) => {
|
|
||||||
const selectIPAdapterIds = useMemo(
|
|
||||||
() =>
|
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
|
||||||
const layer = controlLayers.present.layers.filter(isRegionalGuidanceLayer).find((l) => l.id === layerId);
|
|
||||||
assert(layer, `Layer ${layerId} not found`);
|
|
||||||
return layer.ipAdapterIds;
|
|
||||||
}),
|
|
||||||
[layerId]
|
|
||||||
);
|
|
||||||
const ipAdapterIds = useAppSelector(selectIPAdapterIds);
|
|
||||||
|
|
||||||
if (ipAdapterIds.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{ipAdapterIds.map((id, index) => (
|
|
||||||
<Flex flexDir="column" key={id}>
|
|
||||||
{index > 0 && (
|
|
||||||
<Flex pb={3}>
|
|
||||||
<Divider />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
<RGLayerIPAdapterListItem layerId={layerId} ipAdapterId={id} ipAdapterNumber={index + 1} />
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
RGLayerIPAdapterList.displayName = 'RGLayerIPAdapterList';
|
|
||||||
|
|
||||||
type IPAdapterListItemProps = {
|
|
||||||
layerId: string;
|
|
||||||
ipAdapterId: string;
|
|
||||||
ipAdapterNumber: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const RGLayerIPAdapterListItem = memo(({ layerId, ipAdapterId, ipAdapterNumber }: IPAdapterListItemProps) => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const onDeleteIPAdapter = useCallback(() => {
|
|
||||||
dispatch(guidanceLayerIPAdapterDeleted({ layerId, ipAdapterId }));
|
|
||||||
}, [dispatch, ipAdapterId, layerId]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex flexDir="column" gap={3}>
|
|
||||||
<Flex alignItems="center" gap={3}>
|
|
||||||
<Text fontWeight="semibold" color="base.400">{`IP Adapter ${ipAdapterNumber}`}</Text>
|
|
||||||
<Spacer />
|
|
||||||
<IconButton
|
|
||||||
size="sm"
|
|
||||||
icon={<PiTrashSimpleBold />}
|
|
||||||
aria-label="Delete IP Adapter"
|
|
||||||
onClick={onDeleteIPAdapter}
|
|
||||||
variant="ghost"
|
|
||||||
colorScheme="error"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<ControlAdapterLayerConfig id={ipAdapterId} />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
RGLayerIPAdapterListItem.displayName = 'RGLayerIPAdapterListItem';
|
|
@ -1,84 +0,0 @@
|
|||||||
import { Badge, Flex, Spacer, useDisclosure } from '@invoke-ai/ui-library';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import { rgbColorToString } from 'features/canvas/util/colorToString';
|
|
||||||
import { LayerDeleteButton } from 'features/controlLayers/components/LayerDeleteButton';
|
|
||||||
import { LayerMenu } from 'features/controlLayers/components/LayerMenu';
|
|
||||||
import { LayerTitle } from 'features/controlLayers/components/LayerTitle';
|
|
||||||
import { LayerVisibilityToggle } from 'features/controlLayers/components/LayerVisibilityToggle';
|
|
||||||
import { RGLayerColorPicker } from 'features/controlLayers/components/RGLayerColorPicker';
|
|
||||||
import { RGLayerIPAdapterList } from 'features/controlLayers/components/RGLayerIPAdapterList';
|
|
||||||
import { RGLayerNegativePrompt } from 'features/controlLayers/components/RGLayerNegativePrompt';
|
|
||||||
import { RGLayerPositivePrompt } from 'features/controlLayers/components/RGLayerPositivePrompt';
|
|
||||||
import RGLayerSettingsPopover from 'features/controlLayers/components/RGLayerSettingsPopover';
|
|
||||||
import {
|
|
||||||
isRegionalGuidanceLayer,
|
|
||||||
layerSelected,
|
|
||||||
selectControlLayersSlice,
|
|
||||||
} from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import { memo, useCallback, useMemo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { assert } from 'tsafe';
|
|
||||||
|
|
||||||
import { AddPromptButtons } from './AddPromptButtons';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
layerId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RGLayerListItem = memo(({ layerId }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const selector = useMemo(
|
|
||||||
() =>
|
|
||||||
createMemoizedSelector(selectControlLayersSlice, (controlLayers) => {
|
|
||||||
const layer = controlLayers.present.layers.find((l) => l.id === layerId);
|
|
||||||
assert(isRegionalGuidanceLayer(layer), `Layer ${layerId} not found or not an RP layer`);
|
|
||||||
return {
|
|
||||||
color: rgbColorToString(layer.previewColor),
|
|
||||||
hasPositivePrompt: layer.positivePrompt !== null,
|
|
||||||
hasNegativePrompt: layer.negativePrompt !== null,
|
|
||||||
hasIPAdapters: layer.ipAdapterIds.length > 0,
|
|
||||||
isSelected: layerId === controlLayers.present.selectedLayerId,
|
|
||||||
autoNegative: layer.autoNegative,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
[layerId]
|
|
||||||
);
|
|
||||||
const { autoNegative, color, hasPositivePrompt, hasNegativePrompt, hasIPAdapters, isSelected } =
|
|
||||||
useAppSelector(selector);
|
|
||||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen: true });
|
|
||||||
const onClick = useCallback(() => {
|
|
||||||
dispatch(layerSelected(layerId));
|
|
||||||
}, [dispatch, layerId]);
|
|
||||||
return (
|
|
||||||
<Flex gap={2} onClick={onClick} bg={isSelected ? color : 'base.800'} px={2} borderRadius="base" py="1px">
|
|
||||||
<Flex flexDir="column" w="full" bg="base.850" borderRadius="base">
|
|
||||||
<Flex gap={3} alignItems="center" p={3} cursor="pointer" onDoubleClick={onToggle}>
|
|
||||||
<LayerVisibilityToggle layerId={layerId} />
|
|
||||||
<LayerTitle type="regional_guidance_layer" />
|
|
||||||
<Spacer />
|
|
||||||
{autoNegative === 'invert' && (
|
|
||||||
<Badge color="base.300" bg="transparent" borderWidth={1} userSelect="none">
|
|
||||||
{t('controlLayers.autoNegative')}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
<RGLayerColorPicker layerId={layerId} />
|
|
||||||
<RGLayerSettingsPopover layerId={layerId} />
|
|
||||||
<LayerMenu layerId={layerId} />
|
|
||||||
<LayerDeleteButton layerId={layerId} />
|
|
||||||
</Flex>
|
|
||||||
{isOpen && (
|
|
||||||
<Flex flexDir="column" gap={3} px={3} pb={3}>
|
|
||||||
{!hasPositivePrompt && !hasNegativePrompt && !hasIPAdapters && <AddPromptButtons layerId={layerId} />}
|
|
||||||
{hasPositivePrompt && <RGLayerPositivePrompt layerId={layerId} />}
|
|
||||||
{hasNegativePrompt && <RGLayerNegativePrompt layerId={layerId} />}
|
|
||||||
{hasIPAdapters && <RGLayerIPAdapterList layerId={layerId} />}
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
RGLayerListItem.displayName = 'RGLayerListItem';
|
|
@ -7,7 +7,6 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { useMouseEvents } from 'features/controlLayers/hooks/mouseEventHooks';
|
import { useMouseEvents } from 'features/controlLayers/hooks/mouseEventHooks';
|
||||||
import {
|
import {
|
||||||
$cursorPosition,
|
$cursorPosition,
|
||||||
$isMouseOver,
|
|
||||||
$lastMouseDownPos,
|
$lastMouseDownPos,
|
||||||
$tool,
|
$tool,
|
||||||
isRegionalGuidanceLayer,
|
isRegionalGuidanceLayer,
|
||||||
@ -48,10 +47,9 @@ const useStageRenderer = (
|
|||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const state = useAppSelector((s) => s.controlLayers.present);
|
const state = useAppSelector((s) => s.controlLayers.present);
|
||||||
const tool = useStore($tool);
|
const tool = useStore($tool);
|
||||||
const { onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave, onMouseWheel } = useMouseEvents();
|
const mouseEventHandlers = useMouseEvents();
|
||||||
const cursorPosition = useStore($cursorPosition);
|
const cursorPosition = useStore($cursorPosition);
|
||||||
const lastMouseDownPos = useStore($lastMouseDownPos);
|
const lastMouseDownPos = useStore($lastMouseDownPos);
|
||||||
const isMouseOver = useStore($isMouseOver);
|
|
||||||
const selectedLayerIdColor = useAppSelector(selectSelectedLayerColor);
|
const selectedLayerIdColor = useAppSelector(selectSelectedLayerColor);
|
||||||
const selectedLayerType = useAppSelector(selectSelectedLayerType);
|
const selectedLayerType = useAppSelector(selectSelectedLayerType);
|
||||||
const layerIds = useMemo(() => state.layers.map((l) => l.id), [state.layers]);
|
const layerIds = useMemo(() => state.layers.map((l) => l.id), [state.layers]);
|
||||||
@ -90,23 +88,21 @@ const useStageRenderer = (
|
|||||||
if (asPreview) {
|
if (asPreview) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stage.on('mousedown', onMouseDown);
|
stage.on('mousedown', mouseEventHandlers.onMouseDown);
|
||||||
stage.on('mouseup', onMouseUp);
|
stage.on('mouseup', mouseEventHandlers.onMouseUp);
|
||||||
stage.on('mousemove', onMouseMove);
|
stage.on('mousemove', mouseEventHandlers.onMouseMove);
|
||||||
stage.on('mouseenter', onMouseEnter);
|
stage.on('mouseleave', mouseEventHandlers.onMouseLeave);
|
||||||
stage.on('mouseleave', onMouseLeave);
|
stage.on('wheel', mouseEventHandlers.onMouseWheel);
|
||||||
stage.on('wheel', onMouseWheel);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
log.trace('Cleaning up stage listeners');
|
log.trace('Cleaning up stage listeners');
|
||||||
stage.off('mousedown', onMouseDown);
|
stage.off('mousedown', mouseEventHandlers.onMouseDown);
|
||||||
stage.off('mouseup', onMouseUp);
|
stage.off('mouseup', mouseEventHandlers.onMouseUp);
|
||||||
stage.off('mousemove', onMouseMove);
|
stage.off('mousemove', mouseEventHandlers.onMouseMove);
|
||||||
stage.off('mouseenter', onMouseEnter);
|
stage.off('mouseleave', mouseEventHandlers.onMouseLeave);
|
||||||
stage.off('mouseleave', onMouseLeave);
|
stage.off('wheel', mouseEventHandlers.onMouseWheel);
|
||||||
stage.off('wheel', onMouseWheel);
|
|
||||||
};
|
};
|
||||||
}, [stage, asPreview, onMouseDown, onMouseUp, onMouseMove, onMouseEnter, onMouseLeave, onMouseWheel]);
|
}, [stage, asPreview, mouseEventHandlers]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
log.trace('Updating stage dimensions');
|
log.trace('Updating stage dimensions');
|
||||||
@ -147,7 +143,6 @@ const useStageRenderer = (
|
|||||||
state.globalMaskLayerOpacity,
|
state.globalMaskLayerOpacity,
|
||||||
cursorPosition,
|
cursorPosition,
|
||||||
lastMouseDownPos,
|
lastMouseDownPos,
|
||||||
isMouseOver,
|
|
||||||
state.brushSize
|
state.brushSize
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
@ -159,7 +154,6 @@ const useStageRenderer = (
|
|||||||
state.globalMaskLayerOpacity,
|
state.globalMaskLayerOpacity,
|
||||||
cursorPosition,
|
cursorPosition,
|
||||||
lastMouseDownPos,
|
lastMouseDownPos,
|
||||||
isMouseOver,
|
|
||||||
state.brushSize,
|
state.brushSize,
|
||||||
renderers,
|
renderers,
|
||||||
]);
|
]);
|
||||||
|
@ -1,237 +0,0 @@
|
|||||||
import type { SystemStyleObject } from '@invoke-ai/ui-library';
|
|
||||||
import { Box, Flex, Spinner, useShiftModifier } from '@invoke-ai/ui-library';
|
|
||||||
import { skipToken } from '@reduxjs/toolkit/query';
|
|
||||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|
||||||
import IAIDndImage from 'common/components/IAIDndImage';
|
|
||||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
|
||||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
|
||||||
import { useControlAdapterControlImage } from 'features/controlAdapters/hooks/useControlAdapterControlImage';
|
|
||||||
import { useControlAdapterProcessedControlImage } from 'features/controlAdapters/hooks/useControlAdapterProcessedControlImage';
|
|
||||||
import { useControlAdapterProcessorType } from 'features/controlAdapters/hooks/useControlAdapterProcessorType';
|
|
||||||
import {
|
|
||||||
controlAdapterImageChanged,
|
|
||||||
selectControlAdaptersSlice,
|
|
||||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
|
||||||
import { heightChanged, widthChanged } from 'features/controlLayers/store/controlLayersSlice';
|
|
||||||
import type { TypesafeDraggableData, TypesafeDroppableData } from 'features/dnd/types';
|
|
||||||
import { calculateNewSize } from 'features/parameters/components/ImageSize/calculateNewSize';
|
|
||||||
import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
|
|
||||||
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
|
|
||||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { PiArrowCounterClockwiseBold, PiFloppyDiskBold, PiRulerBold } from 'react-icons/pi';
|
|
||||||
import {
|
|
||||||
useAddImageToBoardMutation,
|
|
||||||
useChangeImageIsIntermediateMutation,
|
|
||||||
useGetImageDTOQuery,
|
|
||||||
useRemoveImageFromBoardMutation,
|
|
||||||
} from 'services/api/endpoints/images';
|
|
||||||
import type { PostUploadAction } from 'services/api/types';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
id: string;
|
|
||||||
isSmall?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectPendingControlImages = createMemoizedSelector(
|
|
||||||
selectControlAdaptersSlice,
|
|
||||||
(controlAdapters) => controlAdapters.pendingControlImages
|
|
||||||
);
|
|
||||||
|
|
||||||
const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const controlImageName = useControlAdapterControlImage(id);
|
|
||||||
const processedControlImageName = useControlAdapterProcessedControlImage(id);
|
|
||||||
const processorType = useControlAdapterProcessorType(id);
|
|
||||||
const autoAddBoardId = useAppSelector((s) => s.gallery.autoAddBoardId);
|
|
||||||
const isConnected = useAppSelector((s) => s.system.isConnected);
|
|
||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
|
||||||
const optimalDimension = useAppSelector(selectOptimalDimension);
|
|
||||||
const pendingControlImages = useAppSelector(selectPendingControlImages);
|
|
||||||
const shift = useShiftModifier();
|
|
||||||
|
|
||||||
const [isMouseOverImage, setIsMouseOverImage] = useState(false);
|
|
||||||
|
|
||||||
const { currentData: controlImage, isError: isErrorControlImage } = useGetImageDTOQuery(
|
|
||||||
controlImageName ?? skipToken
|
|
||||||
);
|
|
||||||
|
|
||||||
const { currentData: processedControlImage, isError: isErrorProcessedControlImage } = useGetImageDTOQuery(
|
|
||||||
processedControlImageName ?? skipToken
|
|
||||||
);
|
|
||||||
|
|
||||||
const [changeIsIntermediate] = useChangeImageIsIntermediateMutation();
|
|
||||||
const [addToBoard] = useAddImageToBoardMutation();
|
|
||||||
const [removeFromBoard] = useRemoveImageFromBoardMutation();
|
|
||||||
const handleResetControlImage = useCallback(() => {
|
|
||||||
dispatch(controlAdapterImageChanged({ id, controlImage: null }));
|
|
||||||
}, [id, dispatch]);
|
|
||||||
|
|
||||||
const handleSaveControlImage = useCallback(async () => {
|
|
||||||
if (!processedControlImage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await changeIsIntermediate({
|
|
||||||
imageDTO: processedControlImage,
|
|
||||||
is_intermediate: false,
|
|
||||||
}).unwrap();
|
|
||||||
|
|
||||||
if (autoAddBoardId !== 'none') {
|
|
||||||
addToBoard({
|
|
||||||
imageDTO: processedControlImage,
|
|
||||||
board_id: autoAddBoardId,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
removeFromBoard({ imageDTO: processedControlImage });
|
|
||||||
}
|
|
||||||
}, [processedControlImage, changeIsIntermediate, autoAddBoardId, addToBoard, removeFromBoard]);
|
|
||||||
|
|
||||||
const handleSetControlImageToDimensions = useCallback(() => {
|
|
||||||
if (!controlImage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeTabName === 'unifiedCanvas') {
|
|
||||||
dispatch(setBoundingBoxDimensions({ width: controlImage.width, height: controlImage.height }, optimalDimension));
|
|
||||||
} else {
|
|
||||||
if (shift) {
|
|
||||||
const { width, height } = controlImage;
|
|
||||||
dispatch(widthChanged({ width, updateAspectRatio: true }));
|
|
||||||
dispatch(heightChanged({ height, updateAspectRatio: true }));
|
|
||||||
} else {
|
|
||||||
const { width, height } = calculateNewSize(
|
|
||||||
controlImage.width / controlImage.height,
|
|
||||||
optimalDimension * optimalDimension
|
|
||||||
);
|
|
||||||
dispatch(widthChanged({ width, updateAspectRatio: true }));
|
|
||||||
dispatch(heightChanged({ height, updateAspectRatio: true }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [controlImage, activeTabName, dispatch, optimalDimension, shift]);
|
|
||||||
|
|
||||||
const handleMouseEnter = useCallback(() => {
|
|
||||||
setIsMouseOverImage(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleMouseLeave = useCallback(() => {
|
|
||||||
setIsMouseOverImage(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const draggableData = useMemo<TypesafeDraggableData | undefined>(() => {
|
|
||||||
if (controlImage) {
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
payloadType: 'IMAGE_DTO',
|
|
||||||
payload: { imageDTO: controlImage },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}, [controlImage, id]);
|
|
||||||
|
|
||||||
const droppableData = useMemo<TypesafeDroppableData | undefined>(
|
|
||||||
() => ({
|
|
||||||
id,
|
|
||||||
actionType: 'SET_CONTROL_ADAPTER_IMAGE',
|
|
||||||
context: { id },
|
|
||||||
}),
|
|
||||||
[id]
|
|
||||||
);
|
|
||||||
|
|
||||||
const postUploadAction = useMemo<PostUploadAction>(() => ({ type: 'SET_CONTROL_ADAPTER_IMAGE', id }), [id]);
|
|
||||||
|
|
||||||
const shouldShowProcessedImage =
|
|
||||||
controlImage &&
|
|
||||||
processedControlImage &&
|
|
||||||
!isMouseOverImage &&
|
|
||||||
!pendingControlImages.includes(id) &&
|
|
||||||
processorType !== 'none';
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isConnected && (isErrorControlImage || isErrorProcessedControlImage)) {
|
|
||||||
handleResetControlImage();
|
|
||||||
}
|
|
||||||
}, [handleResetControlImage, isConnected, isErrorControlImage, isErrorProcessedControlImage]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
onMouseEnter={handleMouseEnter}
|
|
||||||
onMouseLeave={handleMouseLeave}
|
|
||||||
position="relative"
|
|
||||||
w="full"
|
|
||||||
h={isSmall ? 36 : 366} // magic no touch
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
>
|
|
||||||
<IAIDndImage
|
|
||||||
draggableData={draggableData}
|
|
||||||
droppableData={droppableData}
|
|
||||||
imageDTO={controlImage}
|
|
||||||
isDropDisabled={shouldShowProcessedImage}
|
|
||||||
postUploadAction={postUploadAction}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
top={0}
|
|
||||||
insetInlineStart={0}
|
|
||||||
w="full"
|
|
||||||
h="full"
|
|
||||||
opacity={shouldShowProcessedImage ? 1 : 0}
|
|
||||||
transitionProperty="common"
|
|
||||||
transitionDuration="normal"
|
|
||||||
pointerEvents="none"
|
|
||||||
>
|
|
||||||
<IAIDndImage
|
|
||||||
draggableData={draggableData}
|
|
||||||
droppableData={droppableData}
|
|
||||||
imageDTO={processedControlImage}
|
|
||||||
isUploadDisabled={true}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<>
|
|
||||||
<IAIDndImageIcon
|
|
||||||
onClick={handleResetControlImage}
|
|
||||||
icon={controlImage ? <PiArrowCounterClockwiseBold size={16} /> : undefined}
|
|
||||||
tooltip={t('controlnet.resetControlImage')}
|
|
||||||
/>
|
|
||||||
<IAIDndImageIcon
|
|
||||||
onClick={handleSaveControlImage}
|
|
||||||
icon={controlImage ? <PiFloppyDiskBold size={16} /> : undefined}
|
|
||||||
tooltip={t('controlnet.saveControlImage')}
|
|
||||||
styleOverrides={saveControlImageStyleOverrides}
|
|
||||||
/>
|
|
||||||
<IAIDndImageIcon
|
|
||||||
onClick={handleSetControlImageToDimensions}
|
|
||||||
icon={controlImage ? <PiRulerBold size={16} /> : undefined}
|
|
||||||
tooltip={shift ? t('controlnet.setControlImageDimensionsForce') : t('controlnet.setControlImageDimensions')}
|
|
||||||
styleOverrides={setControlImageDimensionsStyleOverrides}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
|
|
||||||
{pendingControlImages.includes(id) && (
|
|
||||||
<Flex
|
|
||||||
position="absolute"
|
|
||||||
top={0}
|
|
||||||
insetInlineStart={0}
|
|
||||||
w="full"
|
|
||||||
h="full"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
opacity={0.8}
|
|
||||||
borderRadius="base"
|
|
||||||
bg="base.900"
|
|
||||||
>
|
|
||||||
<Spinner size="xl" color="base.400" />
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ControlAdapterImagePreview);
|
|
||||||
|
|
||||||
const saveControlImageStyleOverrides: SystemStyleObject = { mt: 6 };
|
|
||||||
const setControlImageDimensionsStyleOverrides: SystemStyleObject = { mt: 12 };
|
|
@ -1,72 +0,0 @@
|
|||||||
import { Box, Flex, Icon, IconButton } from '@invoke-ai/ui-library';
|
|
||||||
import ControlAdapterProcessorComponent from 'features/controlAdapters/components/ControlAdapterProcessorComponent';
|
|
||||||
import ControlAdapterShouldAutoConfig from 'features/controlAdapters/components/ControlAdapterShouldAutoConfig';
|
|
||||||
import ParamControlAdapterIPMethod from 'features/controlAdapters/components/parameters/ParamControlAdapterIPMethod';
|
|
||||||
import ParamControlAdapterProcessorSelect from 'features/controlAdapters/components/parameters/ParamControlAdapterProcessorSelect';
|
|
||||||
import { useControlAdapterType } from 'features/controlAdapters/hooks/useControlAdapterType';
|
|
||||||
import { memo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { PiCaretUpBold } from 'react-icons/pi';
|
|
||||||
import { useToggle } from 'react-use';
|
|
||||||
|
|
||||||
import ControlAdapterImagePreview from './ControlAdapterImagePreview';
|
|
||||||
import { ParamControlAdapterBeginEnd } from './ParamControlAdapterBeginEnd';
|
|
||||||
import ParamControlAdapterControlMode from './ParamControlAdapterControlMode';
|
|
||||||
import ParamControlAdapterModel from './ParamControlAdapterModel';
|
|
||||||
import ParamControlAdapterWeight from './ParamControlAdapterWeight';
|
|
||||||
|
|
||||||
const ControlAdapterLayerConfig = (props: { id: string }) => {
|
|
||||||
const { id } = props;
|
|
||||||
const controlAdapterType = useControlAdapterType(id);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex flexDir="column" gap={4} position="relative" w="full">
|
|
||||||
<Flex gap={3} alignItems="center" w="full">
|
|
||||||
<Box minW={0} w="full" transitionProperty="common" transitionDuration="0.1s">
|
|
||||||
<ParamControlAdapterModel id={id} />{' '}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{controlAdapterType !== 'ip_adapter' && (
|
|
||||||
<IconButton
|
|
||||||
size="sm"
|
|
||||||
tooltip={isExpanded ? t('controlnet.hideAdvanced') : t('controlnet.showAdvanced')}
|
|
||||||
aria-label={isExpanded ? t('controlnet.hideAdvanced') : t('controlnet.showAdvanced')}
|
|
||||||
onClick={toggleIsExpanded}
|
|
||||||
variant="ghost"
|
|
||||||
icon={
|
|
||||||
<Icon
|
|
||||||
boxSize={4}
|
|
||||||
as={PiCaretUpBold}
|
|
||||||
transform={isExpanded ? 'rotate(0deg)' : 'rotate(180deg)'}
|
|
||||||
transitionProperty="common"
|
|
||||||
transitionDuration="normal"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
<Flex gap={4} w="full" alignItems="center">
|
|
||||||
<Flex flexDir="column" gap={3} w="full">
|
|
||||||
{controlAdapterType === 'ip_adapter' && <ParamControlAdapterIPMethod id={id} />}
|
|
||||||
{controlAdapterType === 'controlnet' && <ParamControlAdapterControlMode id={id} />}
|
|
||||||
<ParamControlAdapterWeight id={id} />
|
|
||||||
<ParamControlAdapterBeginEnd id={id} />
|
|
||||||
</Flex>
|
|
||||||
<Flex alignItems="center" justifyContent="center" h={36} w={36} aspectRatio="1/1">
|
|
||||||
<ControlAdapterImagePreview id={id} isSmall />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
{isExpanded && (
|
|
||||||
<>
|
|
||||||
<ControlAdapterShouldAutoConfig id={id} />
|
|
||||||
<ParamControlAdapterProcessorSelect id={id} />
|
|
||||||
<ControlAdapterProcessorComponent id={id} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ControlAdapterLayerConfig);
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user