mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' into feat/ui/allow-number-to-string
This commit is contained in:
commit
4c9344b0ee
@ -88,6 +88,9 @@ class FieldDescriptions:
|
|||||||
num_1 = "The first number"
|
num_1 = "The first number"
|
||||||
num_2 = "The second number"
|
num_2 = "The second number"
|
||||||
mask = "The mask to use for the operation"
|
mask = "The mask to use for the operation"
|
||||||
|
board = "The board to save the image to"
|
||||||
|
image = "The image to process"
|
||||||
|
tile_size = "Tile size"
|
||||||
|
|
||||||
|
|
||||||
class Input(str, Enum):
|
class Input(str, Enum):
|
||||||
@ -173,6 +176,7 @@ class UIType(str, Enum):
|
|||||||
WorkflowField = "WorkflowField"
|
WorkflowField = "WorkflowField"
|
||||||
IsIntermediate = "IsIntermediate"
|
IsIntermediate = "IsIntermediate"
|
||||||
MetadataField = "MetadataField"
|
MetadataField = "MetadataField"
|
||||||
|
BoardField = "BoardField"
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
|
||||||
@ -656,6 +660,8 @@ def invocation(
|
|||||||
:param Optional[str] title: Adds a title to the invocation. Use if the auto-generated title isn't quite right. Defaults to None.
|
:param Optional[str] title: Adds a title to the invocation. Use if the auto-generated title isn't quite right. Defaults to None.
|
||||||
:param Optional[list[str]] tags: Adds tags to the invocation. Invocations may be searched for by their tags. Defaults to None.
|
:param Optional[list[str]] tags: Adds tags to the invocation. Invocations may be searched for by their tags. Defaults to None.
|
||||||
:param Optional[str] category: Adds a category to the invocation. Used to group the invocations in the UI. Defaults to None.
|
:param Optional[str] category: Adds a category to the invocation. Used to group the invocations in the UI. Defaults to None.
|
||||||
|
:param Optional[str] version: Adds a version to the invocation. Must be a valid semver string. Defaults to None.
|
||||||
|
:param Optional[bool] use_cache: Whether or not to use the invocation cache. Defaults to True. The user may override this in the workflow editor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper(cls: Type[GenericBaseInvocation]) -> Type[GenericBaseInvocation]:
|
def wrapper(cls: Type[GenericBaseInvocation]) -> Type[GenericBaseInvocation]:
|
||||||
|
@ -559,3 +559,33 @@ class SamDetectorReproducibleColors(SamDetector):
|
|||||||
img[:, :] = ann_color
|
img[:, :] = ann_color
|
||||||
final_img.paste(Image.fromarray(img, mode="RGB"), (0, 0), Image.fromarray(np.uint8(m * 255)))
|
final_img.paste(Image.fromarray(img, mode="RGB"), (0, 0), Image.fromarray(np.uint8(m * 255)))
|
||||||
return np.array(final_img, dtype=np.uint8)
|
return np.array(final_img, dtype=np.uint8)
|
||||||
|
|
||||||
|
|
||||||
|
@invocation(
|
||||||
|
"color_map_image_processor",
|
||||||
|
title="Color Map Processor",
|
||||||
|
tags=["controlnet"],
|
||||||
|
category="controlnet",
|
||||||
|
version="1.0.0",
|
||||||
|
)
|
||||||
|
class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
|
||||||
|
"""Generates a color map from the provided image"""
|
||||||
|
|
||||||
|
color_map_tile_size: int = InputField(default=64, ge=0, description=FieldDescriptions.tile_size)
|
||||||
|
|
||||||
|
def run_processor(self, image: Image.Image):
|
||||||
|
image = image.convert("RGB")
|
||||||
|
image = np.array(image, dtype=np.uint8)
|
||||||
|
height, width = image.shape[:2]
|
||||||
|
|
||||||
|
width_tile_size = min(self.color_map_tile_size, width)
|
||||||
|
height_tile_size = min(self.color_map_tile_size, height)
|
||||||
|
|
||||||
|
color_map = cv2.resize(
|
||||||
|
image,
|
||||||
|
(width // width_tile_size, height // height_tile_size),
|
||||||
|
interpolation=cv2.INTER_CUBIC,
|
||||||
|
)
|
||||||
|
color_map = cv2.resize(color_map, (width, height), interpolation=cv2.INTER_NEAREST)
|
||||||
|
color_map = Image.fromarray(color_map)
|
||||||
|
return color_map
|
||||||
|
@ -8,12 +8,12 @@ import numpy
|
|||||||
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
||||||
|
|
||||||
from invokeai.app.invocations.metadata import CoreMetadata
|
from invokeai.app.invocations.metadata import CoreMetadata
|
||||||
from invokeai.app.invocations.primitives import ColorField, ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import BoardField, ColorField, ImageField, ImageOutput
|
||||||
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
||||||
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
||||||
|
|
||||||
from ..models.image import ImageCategory, ResourceOrigin
|
from ..models.image import ImageCategory, ResourceOrigin
|
||||||
from .baseinvocation import BaseInvocation, FieldDescriptions, InputField, InvocationContext, invocation
|
from .baseinvocation import BaseInvocation, FieldDescriptions, Input, InputField, InvocationContext, invocation
|
||||||
|
|
||||||
|
|
||||||
@invocation("show_image", title="Show Image", tags=["image"], category="image", version="1.0.0")
|
@invocation("show_image", title="Show Image", tags=["image"], category="image", version="1.0.0")
|
||||||
@ -972,13 +972,14 @@ class ImageChannelMultiplyInvocation(BaseInvocation):
|
|||||||
title="Save Image",
|
title="Save Image",
|
||||||
tags=["primitives", "image"],
|
tags=["primitives", "image"],
|
||||||
category="primitives",
|
category="primitives",
|
||||||
version="1.0.0",
|
version="1.0.1",
|
||||||
use_cache=False,
|
use_cache=False,
|
||||||
)
|
)
|
||||||
class SaveImageInvocation(BaseInvocation):
|
class SaveImageInvocation(BaseInvocation):
|
||||||
"""Saves an image. Unlike an image primitive, this invocation stores a copy of the image."""
|
"""Saves an image. Unlike an image primitive, this invocation stores a copy of the image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to load")
|
image: ImageField = InputField(description=FieldDescriptions.image)
|
||||||
|
board: Optional[BoardField] = InputField(default=None, description=FieldDescriptions.board, input=Input.Direct)
|
||||||
metadata: CoreMetadata = InputField(
|
metadata: CoreMetadata = InputField(
|
||||||
default=None,
|
default=None,
|
||||||
description=FieldDescriptions.core_metadata,
|
description=FieldDescriptions.core_metadata,
|
||||||
@ -992,6 +993,7 @@ class SaveImageInvocation(BaseInvocation):
|
|||||||
image=image,
|
image=image,
|
||||||
image_origin=ResourceOrigin.INTERNAL,
|
image_origin=ResourceOrigin.INTERNAL,
|
||||||
image_category=ImageCategory.GENERAL,
|
image_category=ImageCategory.GENERAL,
|
||||||
|
board_id=self.board.board_id if self.board else None,
|
||||||
node_id=self.id,
|
node_id=self.id,
|
||||||
session_id=context.graph_execution_state_id,
|
session_id=context.graph_execution_state_id,
|
||||||
is_intermediate=self.is_intermediate,
|
is_intermediate=self.is_intermediate,
|
||||||
|
@ -226,6 +226,12 @@ class ImageField(BaseModel):
|
|||||||
image_name: str = Field(description="The name of the image")
|
image_name: str = Field(description="The name of the image")
|
||||||
|
|
||||||
|
|
||||||
|
class BoardField(BaseModel):
|
||||||
|
"""A board primitive field"""
|
||||||
|
|
||||||
|
board_id: str = Field(description="The id of the board")
|
||||||
|
|
||||||
|
|
||||||
@invocation_output("image_output")
|
@invocation_output("image_output")
|
||||||
class ImageOutput(BaseInvocationOutput):
|
class ImageOutput(BaseInvocationOutput):
|
||||||
"""Base class for nodes that output a single image"""
|
"""Base class for nodes that output a single image"""
|
||||||
|
@ -8,97 +8,97 @@ from invokeai.app.services.invoker import Invoker
|
|||||||
|
|
||||||
|
|
||||||
class MemoryInvocationCache(InvocationCacheBase):
|
class MemoryInvocationCache(InvocationCacheBase):
|
||||||
__cache: dict[Union[int, str], tuple[BaseInvocationOutput, str]]
|
_cache: dict[Union[int, str], tuple[BaseInvocationOutput, str]]
|
||||||
__max_cache_size: int
|
_max_cache_size: int
|
||||||
__disabled: bool
|
_disabled: bool
|
||||||
__hits: int
|
_hits: int
|
||||||
__misses: int
|
_misses: int
|
||||||
__cache_ids: Queue
|
_cache_ids: Queue
|
||||||
__invoker: Invoker
|
_invoker: Invoker
|
||||||
|
|
||||||
def __init__(self, max_cache_size: int = 0) -> None:
|
def __init__(self, max_cache_size: int = 0) -> None:
|
||||||
self.__cache = dict()
|
self._cache = dict()
|
||||||
self.__max_cache_size = max_cache_size
|
self._max_cache_size = max_cache_size
|
||||||
self.__disabled = False
|
self._disabled = False
|
||||||
self.__hits = 0
|
self._hits = 0
|
||||||
self.__misses = 0
|
self._misses = 0
|
||||||
self.__cache_ids = Queue()
|
self._cache_ids = Queue()
|
||||||
|
|
||||||
def start(self, invoker: Invoker) -> None:
|
def start(self, invoker: Invoker) -> None:
|
||||||
self.__invoker = invoker
|
self._invoker = invoker
|
||||||
if self.__max_cache_size == 0:
|
if self._max_cache_size == 0:
|
||||||
return
|
return
|
||||||
self.__invoker.services.images.on_deleted(self._delete_by_match)
|
self._invoker.services.images.on_deleted(self._delete_by_match)
|
||||||
self.__invoker.services.latents.on_deleted(self._delete_by_match)
|
self._invoker.services.latents.on_deleted(self._delete_by_match)
|
||||||
|
|
||||||
def get(self, key: Union[int, str]) -> Optional[BaseInvocationOutput]:
|
def get(self, key: Union[int, str]) -> Optional[BaseInvocationOutput]:
|
||||||
if self.__max_cache_size == 0 or self.__disabled:
|
if self._max_cache_size == 0 or self._disabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
item = self.__cache.get(key, None)
|
item = self._cache.get(key, None)
|
||||||
if item is not None:
|
if item is not None:
|
||||||
self.__hits += 1
|
self._hits += 1
|
||||||
return item[0]
|
return item[0]
|
||||||
self.__misses += 1
|
self._misses += 1
|
||||||
|
|
||||||
def save(self, key: Union[int, str], invocation_output: BaseInvocationOutput) -> None:
|
def save(self, key: Union[int, str], invocation_output: BaseInvocationOutput) -> None:
|
||||||
if self.__max_cache_size == 0 or self.__disabled:
|
if self._max_cache_size == 0 or self._disabled:
|
||||||
return
|
return
|
||||||
|
|
||||||
if key not in self.__cache:
|
if key not in self._cache:
|
||||||
self.__cache[key] = (invocation_output, invocation_output.json())
|
self._cache[key] = (invocation_output, invocation_output.json())
|
||||||
self.__cache_ids.put(key)
|
self._cache_ids.put(key)
|
||||||
if self.__cache_ids.qsize() > self.__max_cache_size:
|
if self._cache_ids.qsize() > self._max_cache_size:
|
||||||
try:
|
try:
|
||||||
self.__cache.pop(self.__cache_ids.get())
|
self._cache.pop(self._cache_ids.get())
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# this means the cache_ids are somehow out of sync w/ the cache
|
# this means the cache_ids are somehow out of sync w/ the cache
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def delete(self, key: Union[int, str]) -> None:
|
def delete(self, key: Union[int, str]) -> None:
|
||||||
if self.__max_cache_size == 0 or self.__disabled:
|
if self._max_cache_size == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
if key in self.__cache:
|
if key in self._cache:
|
||||||
del self.__cache[key]
|
del self._cache[key]
|
||||||
|
|
||||||
def clear(self, *args, **kwargs) -> None:
|
def clear(self, *args, **kwargs) -> None:
|
||||||
if self.__max_cache_size == 0 or self.__disabled:
|
if self._max_cache_size == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__cache.clear()
|
self._cache.clear()
|
||||||
self.__cache_ids = Queue()
|
self._cache_ids = Queue()
|
||||||
self.__misses = 0
|
self._misses = 0
|
||||||
self.__hits = 0
|
self._hits = 0
|
||||||
|
|
||||||
def create_key(self, invocation: BaseInvocation) -> int:
|
def create_key(self, invocation: BaseInvocation) -> int:
|
||||||
return hash(invocation.json(exclude={"id"}))
|
return hash(invocation.json(exclude={"id"}))
|
||||||
|
|
||||||
def disable(self) -> None:
|
def disable(self) -> None:
|
||||||
if self.__max_cache_size == 0:
|
if self._max_cache_size == 0:
|
||||||
return
|
return
|
||||||
self.__disabled = True
|
self._disabled = True
|
||||||
|
|
||||||
def enable(self) -> None:
|
def enable(self) -> None:
|
||||||
if self.__max_cache_size == 0:
|
if self._max_cache_size == 0:
|
||||||
return
|
return
|
||||||
self.__disabled = False
|
self._disabled = False
|
||||||
|
|
||||||
def get_status(self) -> InvocationCacheStatus:
|
def get_status(self) -> InvocationCacheStatus:
|
||||||
return InvocationCacheStatus(
|
return InvocationCacheStatus(
|
||||||
hits=self.__hits,
|
hits=self._hits,
|
||||||
misses=self.__misses,
|
misses=self._misses,
|
||||||
enabled=not self.__disabled and self.__max_cache_size > 0,
|
enabled=not self._disabled and self._max_cache_size > 0,
|
||||||
size=len(self.__cache),
|
size=len(self._cache),
|
||||||
max_size=self.__max_cache_size,
|
max_size=self._max_cache_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _delete_by_match(self, to_match: str) -> None:
|
def _delete_by_match(self, to_match: str) -> None:
|
||||||
if self.__max_cache_size == 0 or self.__disabled:
|
if self._max_cache_size == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
keys_to_delete = set()
|
keys_to_delete = set()
|
||||||
for key, value_tuple in self.__cache.items():
|
for key, value_tuple in self._cache.items():
|
||||||
if to_match in value_tuple[1]:
|
if to_match in value_tuple[1]:
|
||||||
keys_to_delete.add(key)
|
keys_to_delete.add(key)
|
||||||
|
|
||||||
@ -108,4 +108,4 @@ class MemoryInvocationCache(InvocationCacheBase):
|
|||||||
for key in keys_to_delete:
|
for key in keys_to_delete:
|
||||||
self.delete(key)
|
self.delete(key)
|
||||||
|
|
||||||
self.__invoker.services.logger.debug(f"Deleted {len(keys_to_delete)} cached invocation outputs for {to_match}")
|
self._invoker.services.logger.debug(f"Deleted {len(keys_to_delete)} cached invocation outputs for {to_match}")
|
||||||
|
@ -81,6 +81,7 @@
|
|||||||
"load": "Load",
|
"load": "Load",
|
||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
"loadingInvokeAI": "Loading Invoke AI",
|
"loadingInvokeAI": "Loading Invoke AI",
|
||||||
|
"learnMore": "Learn More",
|
||||||
"modelManager": "Model Manager",
|
"modelManager": "Model Manager",
|
||||||
"nodeEditor": "Node Editor",
|
"nodeEditor": "Node Editor",
|
||||||
"nodes": "Workflow Editor",
|
"nodes": "Workflow Editor",
|
||||||
@ -135,6 +136,8 @@
|
|||||||
"bgth": "bg_th",
|
"bgth": "bg_th",
|
||||||
"canny": "Canny",
|
"canny": "Canny",
|
||||||
"cannyDescription": "Canny edge detection",
|
"cannyDescription": "Canny edge detection",
|
||||||
|
"colorMap": "Color",
|
||||||
|
"colorMapDescription": "Generates a color map from the image",
|
||||||
"coarse": "Coarse",
|
"coarse": "Coarse",
|
||||||
"contentShuffle": "Content Shuffle",
|
"contentShuffle": "Content Shuffle",
|
||||||
"contentShuffleDescription": "Shuffles the content in an image",
|
"contentShuffleDescription": "Shuffles the content in an image",
|
||||||
@ -158,6 +161,7 @@
|
|||||||
"hideAdvanced": "Hide Advanced",
|
"hideAdvanced": "Hide Advanced",
|
||||||
"highThreshold": "High Threshold",
|
"highThreshold": "High Threshold",
|
||||||
"imageResolution": "Image Resolution",
|
"imageResolution": "Image Resolution",
|
||||||
|
"colorMapTileSize": "Tile Size",
|
||||||
"importImageFromCanvas": "Import Image From Canvas",
|
"importImageFromCanvas": "Import Image From Canvas",
|
||||||
"importMaskFromCanvas": "Import Mask From Canvas",
|
"importMaskFromCanvas": "Import Mask From Canvas",
|
||||||
"incompatibleBaseModel": "Incompatible base model:",
|
"incompatibleBaseModel": "Incompatible base model:",
|
||||||
@ -701,6 +705,8 @@
|
|||||||
"addNodeToolTip": "Add Node (Shift+A, Space)",
|
"addNodeToolTip": "Add Node (Shift+A, Space)",
|
||||||
"animatedEdges": "Animated Edges",
|
"animatedEdges": "Animated Edges",
|
||||||
"animatedEdgesHelp": "Animate selected edges and edges connected to selected nodes",
|
"animatedEdgesHelp": "Animate selected edges and edges connected to selected nodes",
|
||||||
|
"boardField": "Board",
|
||||||
|
"boardFieldDescription": "A gallery board",
|
||||||
"boolean": "Booleans",
|
"boolean": "Booleans",
|
||||||
"booleanCollection": "Boolean Collection",
|
"booleanCollection": "Boolean Collection",
|
||||||
"booleanCollectionDescription": "A collection of booleans.",
|
"booleanCollectionDescription": "A collection of booleans.",
|
||||||
@ -888,7 +894,7 @@
|
|||||||
"zoomOutNodes": "Zoom Out"
|
"zoomOutNodes": "Zoom Out"
|
||||||
},
|
},
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"aspectRatio": "Ratio",
|
"aspectRatio": "Aspect Ratio",
|
||||||
"boundingBoxHeader": "Bounding Box",
|
"boundingBoxHeader": "Bounding Box",
|
||||||
"boundingBoxHeight": "Bounding Box Height",
|
"boundingBoxHeight": "Bounding Box Height",
|
||||||
"boundingBoxWidth": "Bounding Box Width",
|
"boundingBoxWidth": "Bounding Box Width",
|
||||||
@ -1020,8 +1026,8 @@
|
|||||||
"label": "Seed Behaviour",
|
"label": "Seed Behaviour",
|
||||||
"perIterationLabel": "Seed per Iteration",
|
"perIterationLabel": "Seed per Iteration",
|
||||||
"perIterationDesc": "Use a different seed for each iteration",
|
"perIterationDesc": "Use a different seed for each iteration",
|
||||||
"perPromptLabel": "Seed per Prompt",
|
"perPromptLabel": "Seed per Image",
|
||||||
"perPromptDesc": "Use a different seed for each prompt"
|
"perPromptDesc": "Use a different seed for each image"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sdxl": {
|
"sdxl": {
|
||||||
@ -1173,131 +1179,205 @@
|
|||||||
"popovers": {
|
"popovers": {
|
||||||
"clipSkip": {
|
"clipSkip": {
|
||||||
"heading": "CLIP Skip",
|
"heading": "CLIP Skip",
|
||||||
"paragraph": "Choose how many layers of the CLIP model to skip. Certain models are better suited to be used with CLIP Skip."
|
"paragraphs": [
|
||||||
},
|
"Choose how many layers of the CLIP model to skip.",
|
||||||
"compositingBlur": {
|
"Some models work better with certain CLIP Skip settings.",
|
||||||
"heading": "Blur",
|
"A higher value typically results in a less detailed image."
|
||||||
"paragraph": "The blur radius of the mask."
|
]
|
||||||
},
|
|
||||||
"compositingBlurMethod": {
|
|
||||||
"heading": "Blur Method",
|
|
||||||
"paragraph": "The method of blur applied to the masked area."
|
|
||||||
},
|
|
||||||
"compositingCoherencePass": {
|
|
||||||
"heading": "Coherence Pass",
|
|
||||||
"paragraph": "Composite the Inpainted/Outpainted images."
|
|
||||||
},
|
|
||||||
"compositingCoherenceMode": {
|
|
||||||
"heading": "Mode",
|
|
||||||
"paragraph": "The mode of the Coherence Pass."
|
|
||||||
},
|
|
||||||
"compositingCoherenceSteps": {
|
|
||||||
"heading": "Steps",
|
|
||||||
"paragraph": "Number of steps in the Coherence Pass. Similar to Denoising Steps."
|
|
||||||
},
|
|
||||||
"compositingStrength": {
|
|
||||||
"heading": "Strength",
|
|
||||||
"paragraph": "Amount of noise added for the Coherence Pass. Similar to Denoising Strength."
|
|
||||||
},
|
|
||||||
"compositingMaskAdjustments": {
|
|
||||||
"heading": "Mask Adjustments",
|
|
||||||
"paragraph": "Adjust the mask."
|
|
||||||
},
|
|
||||||
"controlNetBeginEnd": {
|
|
||||||
"heading": "Begin / End Step Percentage",
|
|
||||||
"paragraph": "Which parts of the denoising process will have the ControlNet applied. ControlNets applied at the start of the process guide composition, and ControlNets applied at the end guide details."
|
|
||||||
},
|
|
||||||
"controlNetControlMode": {
|
|
||||||
"heading": "Control Mode",
|
|
||||||
"paragraph": "Lends more weight to either the prompt or ControlNet."
|
|
||||||
},
|
|
||||||
"controlNetResizeMode": {
|
|
||||||
"heading": "Resize Mode",
|
|
||||||
"paragraph": "How the ControlNet image will be fit to the image generation Ratio"
|
|
||||||
},
|
|
||||||
"controlNetToggle": {
|
|
||||||
"heading": "Enable ControlNet",
|
|
||||||
"paragraph": "ControlNets provide guidance to the generation process, helping create images with controlled composition, structure, or style, depending on the model selected."
|
|
||||||
},
|
|
||||||
"controlNetWeight": {
|
|
||||||
"heading": "Weight",
|
|
||||||
"paragraph": "How strongly the ControlNet will impact the generated image."
|
|
||||||
},
|
|
||||||
"dynamicPromptsToggle": {
|
|
||||||
"heading": "Enable Dynamic Prompts",
|
|
||||||
"paragraph": "Dynamic prompts allow multiple options within a prompt. Dynamic prompts can be used by: {option1|option2|option3}. Combinations of prompts will be randomly generated until the “Images” number has been reached."
|
|
||||||
},
|
|
||||||
"dynamicPromptsCombinatorial": {
|
|
||||||
"heading": "Combinatorial Generation",
|
|
||||||
"paragraph": "Generate an image for every possible combination of Dynamic Prompts until the Max Prompts is reached."
|
|
||||||
},
|
|
||||||
"infillMethod": {
|
|
||||||
"heading": "Infill Method",
|
|
||||||
"paragraph": "Method to infill the selected area."
|
|
||||||
},
|
|
||||||
"lora": {
|
|
||||||
"heading": "LoRA Weight",
|
|
||||||
"paragraph": "Weight of the LoRA. Higher weight will lead to larger impacts on the final image."
|
|
||||||
},
|
|
||||||
"noiseEnable": {
|
|
||||||
"heading": "Enable Noise Settings",
|
|
||||||
"paragraph": "Advanced control over noise generation."
|
|
||||||
},
|
|
||||||
"noiseUseCPU": {
|
|
||||||
"heading": "Use CPU Noise",
|
|
||||||
"paragraph": "Uses the CPU to generate random noise."
|
|
||||||
},
|
|
||||||
"paramCFGScale": {
|
|
||||||
"heading": "CFG Scale",
|
|
||||||
"paragraph": "Controls how much your prompt influences the generation process."
|
|
||||||
},
|
|
||||||
"paramDenoisingStrength": {
|
|
||||||
"heading": "Denoising Strength",
|
|
||||||
"paragraph": "How much noise is added to the input image. 0 will result in an identical image, while 1 will result in a completely new image."
|
|
||||||
},
|
|
||||||
"paramIterations": {
|
|
||||||
"heading": "Iterations",
|
|
||||||
"paragraph": "The number of images to generate. If Dynamic Prompts is enabled, each of the prompts will be generated this many times."
|
|
||||||
},
|
|
||||||
"paramModel": {
|
|
||||||
"heading": "Model",
|
|
||||||
"paragraph": "Model used for the denoising steps. Different models are trained to specialize in producing different aesthetic results and content."
|
|
||||||
},
|
},
|
||||||
"paramNegativeConditioning": {
|
"paramNegativeConditioning": {
|
||||||
"heading": "Negative Prompt",
|
"heading": "Negative Prompt",
|
||||||
"paragraph": "The generation process avoids the concepts in the negative prompt. Use this to exclude qualities or objects from the output. Supports Compel syntax and embeddings."
|
"paragraphs": [
|
||||||
|
"The generation process avoids the concepts in the negative prompt. Use this to exclude qualities or objects from the output.",
|
||||||
|
"Supports Compel syntax and embeddings."
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"paramPositiveConditioning": {
|
"paramPositiveConditioning": {
|
||||||
"heading": "Positive Prompt",
|
"heading": "Positive Prompt",
|
||||||
"paragraph": "Guides the generation process. You may use any words or phrases. Supports Compel and Dynamic Prompts syntaxes and embeddings."
|
"paragraphs": [
|
||||||
},
|
"Guides the generation process. You may use any words or phrases.",
|
||||||
"paramRatio": {
|
"Compel and Dynamic Prompts syntaxes and embeddings."
|
||||||
"heading": "Ratio",
|
]
|
||||||
"paragraph": "The ratio of the dimensions of the image generated. An image size (in number of pixels) equivalent to 512x512 is recommended for SD1.5 models and a size equivalent to 1024x1024 is recommended for SDXL models."
|
|
||||||
},
|
},
|
||||||
"paramScheduler": {
|
"paramScheduler": {
|
||||||
"heading": "Scheduler",
|
"heading": "Scheduler",
|
||||||
"paragraph": "Scheduler defines how to iteratively add noise to an image or how to update a sample based on a model's output."
|
"paragraphs": [
|
||||||
|
"Scheduler defines how to iteratively add noise to an image or how to update a sample based on a model's output."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compositingBlur": {
|
||||||
|
"heading": "Blur",
|
||||||
|
"paragraphs": ["The blur radius of the mask."]
|
||||||
|
},
|
||||||
|
"compositingBlurMethod": {
|
||||||
|
"heading": "Blur Method",
|
||||||
|
"paragraphs": ["The method of blur applied to the masked area."]
|
||||||
|
},
|
||||||
|
"compositingCoherencePass": {
|
||||||
|
"heading": "Coherence Pass",
|
||||||
|
"paragraphs": [
|
||||||
|
"A second round of denoising helps to composite the Inpainted/Outpainted image."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compositingCoherenceMode": {
|
||||||
|
"heading": "Mode",
|
||||||
|
"paragraphs": ["The mode of the Coherence Pass."]
|
||||||
|
},
|
||||||
|
"compositingCoherenceSteps": {
|
||||||
|
"heading": "Steps",
|
||||||
|
"paragraphs": [
|
||||||
|
"Number of denoising steps used in the Coherence Pass.",
|
||||||
|
"Same as the main Steps parameter."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compositingStrength": {
|
||||||
|
"heading": "Strength",
|
||||||
|
"paragraphs": [
|
||||||
|
"Denoising strength for the Coherence Pass.",
|
||||||
|
"Same as the Image to Image Denoising Strength parameter."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"compositingMaskAdjustments": {
|
||||||
|
"heading": "Mask Adjustments",
|
||||||
|
"paragraphs": ["Adjust the mask."]
|
||||||
|
},
|
||||||
|
"controlNetBeginEnd": {
|
||||||
|
"heading": "Begin / End Step Percentage",
|
||||||
|
"paragraphs": [
|
||||||
|
"Which steps of the denoising process will have the ControlNet applied.",
|
||||||
|
"ControlNets applied at the beginning of the process guide composition, and ControlNets applied at the end guide details."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"controlNetControlMode": {
|
||||||
|
"heading": "Control Mode",
|
||||||
|
"paragraphs": [
|
||||||
|
"Lends more weight to either the prompt or ControlNet."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"controlNetResizeMode": {
|
||||||
|
"heading": "Resize Mode",
|
||||||
|
"paragraphs": [
|
||||||
|
"How the ControlNet image will be fit to the image output size."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"controlNet": {
|
||||||
|
"heading": "ControlNet",
|
||||||
|
"paragraphs": [
|
||||||
|
"ControlNets provide guidance to the generation process, helping create images with controlled composition, structure, or style, depending on the model selected."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"controlNetWeight": {
|
||||||
|
"heading": "Weight",
|
||||||
|
"paragraphs": [
|
||||||
|
"How strongly the ControlNet will impact the generated image."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dynamicPrompts": {
|
||||||
|
"heading": "Dynamic Prompts",
|
||||||
|
"paragraphs": [
|
||||||
|
"Dynamic Prompts parses a single prompt into many.",
|
||||||
|
"The basic syntax is \"a {red|green|blue} ball\". This will produce three prompts: \"a red ball\", \"a green ball\" and \"a blue ball\".",
|
||||||
|
"You can use the syntax as many times as you like in a single prompt, but be sure to keep the number of prompts generated in check with the Max Prompts setting."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dynamicPromptsMaxPrompts": {
|
||||||
|
"heading": "Max Prompts",
|
||||||
|
"paragraphs": [
|
||||||
|
"Limits the number of prompts that can be generated by Dynamic Prompts."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dynamicPromptsSeedBehaviour": {
|
||||||
|
"heading": "Seed Behaviour",
|
||||||
|
"paragraphs": [
|
||||||
|
"Controls how the seed is used when generating prompts.",
|
||||||
|
"Per Iteration will use a unique seed for each iteration. Use this to explore prompt variations on a single seed.",
|
||||||
|
"For example, if you have 5 prompts, each image will use the same seed.",
|
||||||
|
"Per Image will use a unique seed for each image. This provides more variation."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"infillMethod": {
|
||||||
|
"heading": "Infill Method",
|
||||||
|
"paragraphs": ["Method to infill the selected area."]
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"heading": "LoRA Weight",
|
||||||
|
"paragraphs": [
|
||||||
|
"Higher LoRA weight will lead to larger impacts on the final image."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"noiseUseCPU": {
|
||||||
|
"heading": "Use CPU Noise",
|
||||||
|
"paragraphs": [
|
||||||
|
"Controls whether noise is generated on the CPU or GPU.",
|
||||||
|
"With CPU Noise enabled, a particular seed will produce the same image on any machine.",
|
||||||
|
"There is no performance impact to enabling CPU Noise."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"paramCFGScale": {
|
||||||
|
"heading": "CFG Scale",
|
||||||
|
"paragraphs": [
|
||||||
|
"Controls how much your prompt influences the generation process."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"paramDenoisingStrength": {
|
||||||
|
"heading": "Denoising Strength",
|
||||||
|
"paragraphs": [
|
||||||
|
"How much noise is added to the input image.",
|
||||||
|
"0 will result in an identical image, while 1 will result in a completely new image."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"paramIterations": {
|
||||||
|
"heading": "Iterations",
|
||||||
|
"paragraphs": [
|
||||||
|
"The number of images to generate.",
|
||||||
|
"If Dynamic Prompts is enabled, each of the prompts will be generated this many times."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"paramModel": {
|
||||||
|
"heading": "Model",
|
||||||
|
"paragraphs": [
|
||||||
|
"Model used for the denoising steps.",
|
||||||
|
"Different models are typically trained to specialize in producing particular aesthetic results and content."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"paramRatio": {
|
||||||
|
"heading": "Aspect Ratio",
|
||||||
|
"paragraphs": [
|
||||||
|
"The aspect ratio of the dimensions of the image generated.",
|
||||||
|
"An image size (in number of pixels) equivalent to 512x512 is recommended for SD1.5 models and a size equivalent to 1024x1024 is recommended for SDXL models."
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"paramSeed": {
|
"paramSeed": {
|
||||||
"heading": "Seed",
|
"heading": "Seed",
|
||||||
"paragraph": "Controls the starting noise used for generation. Disable “Random Seed” to produce identical results with the same generation settings."
|
"paragraphs": [
|
||||||
|
"Controls the starting noise used for generation.",
|
||||||
|
"Disable “Random Seed” to produce identical results with the same generation settings."
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"paramSteps": {
|
"paramSteps": {
|
||||||
"heading": "Steps",
|
"heading": "Steps",
|
||||||
"paragraph": "Number of steps that will be performed in each generation. Higher step counts will typically create better images but will require more generation time."
|
"paragraphs": [
|
||||||
|
"Number of steps that will be performed in each generation.",
|
||||||
|
"Higher step counts will typically create better images but will require more generation time."
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"paramVAE": {
|
"paramVAE": {
|
||||||
"heading": "VAE",
|
"heading": "VAE",
|
||||||
"paragraph": "Model used for translating AI output into the final image."
|
"paragraphs": [
|
||||||
|
"Model used for translating AI output into the final image."
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"paramVAEPrecision": {
|
"paramVAEPrecision": {
|
||||||
"heading": "VAE Precision",
|
"heading": "VAE Precision",
|
||||||
"paragraph": "The precision used during VAE encoding and decoding. Fp16/Half precision is more efficient, at the expense of minor image variations."
|
"paragraphs": [
|
||||||
|
"The precision used during VAE encoding and decoding. FP16/half precision is more efficient, at the expense of minor image variations."
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"scaleBeforeProcessing": {
|
"scaleBeforeProcessing": {
|
||||||
"heading": "Scale Before Processing",
|
"heading": "Scale Before Processing",
|
||||||
"paragraph": "Scales the selected area to the size best suited for the model before the image generation process."
|
"paragraphs": [
|
||||||
|
"Scales the selected area to the size best suited for the model before the image generation process."
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ui": {
|
"ui": {
|
||||||
|
@ -17,7 +17,8 @@ import {
|
|||||||
} from 'services/events/actions';
|
} from 'services/events/actions';
|
||||||
import { startAppListening } from '../..';
|
import { startAppListening } from '../..';
|
||||||
|
|
||||||
const nodeDenylist = ['load_image'];
|
// These nodes output an image, but do not actually *save* an image, so we don't want to handle the gallery logic on them
|
||||||
|
const nodeDenylist = ['load_image', 'image'];
|
||||||
|
|
||||||
export const addInvocationCompleteEventListener = () => {
|
export const addInvocationCompleteEventListener = () => {
|
||||||
startAppListening({
|
startAppListening({
|
||||||
@ -37,6 +38,7 @@ export const addInvocationCompleteEventListener = () => {
|
|||||||
const { image_name } = result.image;
|
const { image_name } = result.image;
|
||||||
const { canvas, gallery } = getState();
|
const { canvas, gallery } = getState();
|
||||||
|
|
||||||
|
// This populates the `getImageDTO` cache
|
||||||
const imageDTO = await dispatch(
|
const imageDTO = await dispatch(
|
||||||
imagesApi.endpoints.getImageDTO.initiate(image_name)
|
imagesApi.endpoints.getImageDTO.initiate(image_name)
|
||||||
).unwrap();
|
).unwrap();
|
||||||
@ -52,54 +54,36 @@ export const addInvocationCompleteEventListener = () => {
|
|||||||
if (!imageDTO.is_intermediate) {
|
if (!imageDTO.is_intermediate) {
|
||||||
/**
|
/**
|
||||||
* Cache updates for when an image result is received
|
* Cache updates for when an image result is received
|
||||||
* - *add* to getImageDTO
|
* - add it to the no_board/images
|
||||||
* - IF `autoAddBoardId` is set:
|
|
||||||
* - THEN add it to the board_id/images
|
|
||||||
* - ELSE (`autoAddBoardId` is not set):
|
|
||||||
* - THEN add it to the no_board/images
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { autoAddBoardId } = gallery;
|
dispatch(
|
||||||
if (autoAddBoardId && autoAddBoardId !== 'none') {
|
imagesApi.util.updateQueryData(
|
||||||
dispatch(
|
'listImages',
|
||||||
imagesApi.endpoints.addImageToBoard.initiate({
|
{
|
||||||
board_id: autoAddBoardId,
|
board_id: imageDTO.board_id ?? 'none',
|
||||||
imageDTO,
|
categories: IMAGE_CATEGORIES,
|
||||||
})
|
},
|
||||||
);
|
(draft) => {
|
||||||
} else {
|
imagesAdapter.addOne(draft, imageDTO);
|
||||||
dispatch(
|
}
|
||||||
imagesApi.util.updateQueryData(
|
)
|
||||||
'listImages',
|
);
|
||||||
{
|
|
||||||
board_id: 'none',
|
|
||||||
categories: IMAGE_CATEGORIES,
|
|
||||||
},
|
|
||||||
(draft) => {
|
|
||||||
imagesAdapter.addOne(draft, imageDTO);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
imagesApi.util.invalidateTags([
|
imagesApi.util.invalidateTags([
|
||||||
{ type: 'BoardImagesTotal', id: autoAddBoardId },
|
{ type: 'BoardImagesTotal', id: imageDTO.board_id },
|
||||||
{ type: 'BoardAssetsTotal', id: autoAddBoardId },
|
{ type: 'BoardAssetsTotal', id: imageDTO.board_id },
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
const { selectedBoardId, shouldAutoSwitch } = gallery;
|
const { shouldAutoSwitch } = gallery;
|
||||||
|
|
||||||
// If auto-switch is enabled, select the new image
|
// If auto-switch is enabled, select the new image
|
||||||
if (shouldAutoSwitch) {
|
if (shouldAutoSwitch) {
|
||||||
// if auto-add is enabled, switch the board as the image comes in
|
// if auto-add is enabled, switch the board as the image comes in
|
||||||
if (autoAddBoardId && autoAddBoardId !== selectedBoardId) {
|
dispatch(galleryViewChanged('images'));
|
||||||
dispatch(boardIdSelected(autoAddBoardId));
|
dispatch(boardIdSelected(imageDTO.board_id ?? 'none'));
|
||||||
dispatch(galleryViewChanged('images'));
|
|
||||||
} else if (!autoAddBoardId) {
|
|
||||||
dispatch(galleryViewChanged('images'));
|
|
||||||
}
|
|
||||||
dispatch(imageSelected(imageDTO));
|
dispatch(imageSelected(imageDTO));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,14 @@ export const addUpscaleRequestedListener = () => {
|
|||||||
const log = logger('session');
|
const log = logger('session');
|
||||||
|
|
||||||
const { image_name } = action.payload;
|
const { image_name } = action.payload;
|
||||||
const { esrganModelName } = getState().postprocessing;
|
const state = getState();
|
||||||
|
const { esrganModelName } = state.postprocessing;
|
||||||
|
const { autoAddBoardId } = state.gallery;
|
||||||
|
|
||||||
const graph = buildAdHocUpscaleGraph({
|
const graph = buildAdHocUpscaleGraph({
|
||||||
image_name,
|
image_name,
|
||||||
esrganModelName,
|
esrganModelName,
|
||||||
|
autoAddBoardId,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
Divider,
|
|
||||||
Flex,
|
|
||||||
Heading,
|
|
||||||
Image,
|
|
||||||
Popover,
|
|
||||||
PopoverArrow,
|
|
||||||
PopoverBody,
|
|
||||||
PopoverCloseButton,
|
|
||||||
PopoverContent,
|
|
||||||
PopoverProps,
|
|
||||||
PopoverTrigger,
|
|
||||||
Portal,
|
|
||||||
Text,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { ReactNode, memo } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { useAppSelector } from '../../app/store/storeHooks';
|
|
||||||
|
|
||||||
const OPEN_DELAY = 1500;
|
|
||||||
|
|
||||||
type Props = Omit<PopoverProps, 'children'> & {
|
|
||||||
details: string;
|
|
||||||
children: ReactNode;
|
|
||||||
image?: string;
|
|
||||||
buttonLabel?: string;
|
|
||||||
buttonHref?: string;
|
|
||||||
placement?: PopoverProps['placement'];
|
|
||||||
};
|
|
||||||
|
|
||||||
const IAIInformationalPopover = ({
|
|
||||||
details,
|
|
||||||
image,
|
|
||||||
buttonLabel,
|
|
||||||
buttonHref,
|
|
||||||
children,
|
|
||||||
placement,
|
|
||||||
}: Props) => {
|
|
||||||
const shouldEnableInformationalPopovers = useAppSelector(
|
|
||||||
(state) => state.system.shouldEnableInformationalPopovers
|
|
||||||
);
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const heading = t(`popovers.${details}.heading`);
|
|
||||||
const paragraph = t(`popovers.${details}.paragraph`);
|
|
||||||
|
|
||||||
if (!shouldEnableInformationalPopovers) {
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Popover
|
|
||||||
placement={placement || 'top'}
|
|
||||||
closeOnBlur={false}
|
|
||||||
trigger="hover"
|
|
||||||
variant="informational"
|
|
||||||
openDelay={OPEN_DELAY}
|
|
||||||
>
|
|
||||||
<PopoverTrigger>
|
|
||||||
<Box w="full">{children}</Box>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<Portal>
|
|
||||||
<PopoverContent>
|
|
||||||
<PopoverArrow />
|
|
||||||
<PopoverCloseButton />
|
|
||||||
|
|
||||||
<PopoverBody>
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
gap: 3,
|
|
||||||
flexDirection: 'column',
|
|
||||||
width: '100%',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{image && (
|
|
||||||
<Image
|
|
||||||
sx={{
|
|
||||||
objectFit: 'contain',
|
|
||||||
maxW: '60%',
|
|
||||||
maxH: '60%',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
}}
|
|
||||||
src={image}
|
|
||||||
alt="Optional Image"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Flex
|
|
||||||
sx={{
|
|
||||||
gap: 3,
|
|
||||||
flexDirection: 'column',
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{heading && (
|
|
||||||
<>
|
|
||||||
<Heading size="sm">{heading}</Heading>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Text>{paragraph}</Text>
|
|
||||||
{buttonLabel && (
|
|
||||||
<Flex justifyContent="flex-end">
|
|
||||||
<Button
|
|
||||||
onClick={() => window.open(buttonHref)}
|
|
||||||
size="sm"
|
|
||||||
variant="invokeAIOutline"
|
|
||||||
>
|
|
||||||
{buttonLabel}
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</PopoverBody>
|
|
||||||
</PopoverContent>
|
|
||||||
</Portal>
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(IAIInformationalPopover);
|
|
@ -0,0 +1,155 @@
|
|||||||
|
import {
|
||||||
|
Box,
|
||||||
|
BoxProps,
|
||||||
|
Button,
|
||||||
|
Divider,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
Image,
|
||||||
|
Popover,
|
||||||
|
PopoverBody,
|
||||||
|
PopoverCloseButton,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverProps,
|
||||||
|
PopoverTrigger,
|
||||||
|
Portal,
|
||||||
|
Text,
|
||||||
|
forwardRef,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { merge, omit } from 'lodash-es';
|
||||||
|
import { PropsWithChildren, memo, useCallback, useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { FaExternalLinkAlt } from 'react-icons/fa';
|
||||||
|
import { useAppSelector } from '../../../app/store/storeHooks';
|
||||||
|
import {
|
||||||
|
Feature,
|
||||||
|
OPEN_DELAY,
|
||||||
|
POPOVER_DATA,
|
||||||
|
POPPER_MODIFIERS,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
type Props = PropsWithChildren & {
|
||||||
|
feature: Feature;
|
||||||
|
wrapperProps?: BoxProps;
|
||||||
|
popoverProps?: PopoverProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const IAIInformationalPopover = forwardRef(
|
||||||
|
({ feature, children, wrapperProps, ...rest }: Props, ref) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const shouldEnableInformationalPopovers = useAppSelector(
|
||||||
|
(state) => state.system.shouldEnableInformationalPopovers
|
||||||
|
);
|
||||||
|
|
||||||
|
const data = useMemo(() => POPOVER_DATA[feature], [feature]);
|
||||||
|
|
||||||
|
const popoverProps = useMemo(
|
||||||
|
() => merge(omit(data, ['image', 'href', 'buttonLabel']), rest),
|
||||||
|
[data, rest]
|
||||||
|
);
|
||||||
|
|
||||||
|
const heading = useMemo<string | undefined>(
|
||||||
|
() => t(`popovers.${feature}.heading`),
|
||||||
|
[feature, t]
|
||||||
|
);
|
||||||
|
|
||||||
|
const paragraphs = useMemo<string[]>(
|
||||||
|
() =>
|
||||||
|
t(`popovers.${feature}.paragraphs`, {
|
||||||
|
returnObjects: true,
|
||||||
|
}) ?? [],
|
||||||
|
[feature, t]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
if (!data?.href) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open(data.href);
|
||||||
|
}, [data?.href]);
|
||||||
|
|
||||||
|
if (!shouldEnableInformationalPopovers) {
|
||||||
|
return (
|
||||||
|
<Box ref={ref} w="full" {...wrapperProps}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
isLazy
|
||||||
|
closeOnBlur={false}
|
||||||
|
trigger="hover"
|
||||||
|
variant="informational"
|
||||||
|
openDelay={OPEN_DELAY}
|
||||||
|
modifiers={POPPER_MODIFIERS}
|
||||||
|
placement="top"
|
||||||
|
{...popoverProps}
|
||||||
|
>
|
||||||
|
<PopoverTrigger>
|
||||||
|
<Box ref={ref} w="full" {...wrapperProps}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<Portal>
|
||||||
|
<PopoverContent w={96}>
|
||||||
|
<PopoverCloseButton />
|
||||||
|
<PopoverBody>
|
||||||
|
<Flex
|
||||||
|
sx={{
|
||||||
|
gap: 2,
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{heading && (
|
||||||
|
<>
|
||||||
|
<Heading size="sm">{heading}</Heading>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{data?.image && (
|
||||||
|
<>
|
||||||
|
<Image
|
||||||
|
sx={{
|
||||||
|
objectFit: 'contain',
|
||||||
|
maxW: '60%',
|
||||||
|
maxH: '60%',
|
||||||
|
backgroundColor: 'white',
|
||||||
|
}}
|
||||||
|
src={data.image}
|
||||||
|
alt="Optional Image"
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{paragraphs.map((p) => (
|
||||||
|
<Text key={p}>{p}</Text>
|
||||||
|
))}
|
||||||
|
{data?.href && (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
<Button
|
||||||
|
pt={1}
|
||||||
|
onClick={handleClick}
|
||||||
|
leftIcon={<FaExternalLinkAlt />}
|
||||||
|
alignSelf="flex-end"
|
||||||
|
variant="link"
|
||||||
|
>
|
||||||
|
{t('common.learnMore') ?? heading}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</PopoverBody>
|
||||||
|
</PopoverContent>
|
||||||
|
</Portal>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
IAIInformationalPopover.displayName = 'IAIInformationalPopover';
|
||||||
|
|
||||||
|
export default memo(IAIInformationalPopover);
|
@ -0,0 +1,98 @@
|
|||||||
|
import { PopoverProps } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
export type Feature =
|
||||||
|
| 'clipSkip'
|
||||||
|
| 'paramNegativeConditioning'
|
||||||
|
| 'paramPositiveConditioning'
|
||||||
|
| 'paramScheduler'
|
||||||
|
| 'compositingBlur'
|
||||||
|
| 'compositingBlurMethod'
|
||||||
|
| 'compositingCoherencePass'
|
||||||
|
| 'compositingCoherenceMode'
|
||||||
|
| 'compositingCoherenceSteps'
|
||||||
|
| 'compositingStrength'
|
||||||
|
| 'compositingMaskAdjustments'
|
||||||
|
| 'controlNetBeginEnd'
|
||||||
|
| 'controlNetControlMode'
|
||||||
|
| 'controlNetResizeMode'
|
||||||
|
| 'controlNet'
|
||||||
|
| 'controlNetWeight'
|
||||||
|
| 'dynamicPrompts'
|
||||||
|
| 'dynamicPromptsMaxPrompts'
|
||||||
|
| 'dynamicPromptsSeedBehaviour'
|
||||||
|
| 'infillMethod'
|
||||||
|
| 'lora'
|
||||||
|
| 'noiseUseCPU'
|
||||||
|
| 'paramCFGScale'
|
||||||
|
| 'paramDenoisingStrength'
|
||||||
|
| 'paramIterations'
|
||||||
|
| 'paramModel'
|
||||||
|
| 'paramRatio'
|
||||||
|
| 'paramSeed'
|
||||||
|
| 'paramSteps'
|
||||||
|
| 'paramVAE'
|
||||||
|
| 'paramVAEPrecision'
|
||||||
|
| 'scaleBeforeProcessing';
|
||||||
|
|
||||||
|
export type PopoverData = PopoverProps & {
|
||||||
|
image?: string;
|
||||||
|
href?: string;
|
||||||
|
buttonLabel?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const POPOVER_DATA: { [key in Feature]?: PopoverData } = {
|
||||||
|
paramNegativeConditioning: {
|
||||||
|
placement: 'right',
|
||||||
|
},
|
||||||
|
controlNet: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000105880',
|
||||||
|
},
|
||||||
|
lora: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000159072',
|
||||||
|
},
|
||||||
|
compositingCoherenceMode: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000158838',
|
||||||
|
},
|
||||||
|
infillMethod: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000158841',
|
||||||
|
},
|
||||||
|
scaleBeforeProcessing: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000158841',
|
||||||
|
},
|
||||||
|
paramIterations: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000159073',
|
||||||
|
},
|
||||||
|
paramPositiveConditioning: {
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000096606-tips-on-crafting-prompts',
|
||||||
|
placement: 'right',
|
||||||
|
},
|
||||||
|
paramScheduler: {
|
||||||
|
placement: 'right',
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000159073',
|
||||||
|
},
|
||||||
|
paramModel: {
|
||||||
|
placement: 'right',
|
||||||
|
href: 'https://support.invoke.ai/support/solutions/articles/151000096601-what-is-a-model-which-should-i-use-',
|
||||||
|
},
|
||||||
|
paramRatio: {
|
||||||
|
gutter: 16,
|
||||||
|
},
|
||||||
|
controlNetControlMode: {
|
||||||
|
placement: 'right',
|
||||||
|
},
|
||||||
|
controlNetResizeMode: {
|
||||||
|
placement: 'right',
|
||||||
|
},
|
||||||
|
paramVAE: {
|
||||||
|
placement: 'right',
|
||||||
|
},
|
||||||
|
paramVAEPrecision: {
|
||||||
|
placement: 'right',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const OPEN_DELAY = 1000; // in milliseconds
|
||||||
|
|
||||||
|
export const POPPER_MODIFIERS: PopoverProps['modifiers'] = [
|
||||||
|
{ name: 'preventOverflow', options: { padding: 10 } },
|
||||||
|
];
|
@ -44,23 +44,19 @@ const IAIMantineMultiSelect = forwardRef((props: IAIMultiSelectProps, ref) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltip} placement="top" hasArrow isOpen={true}>
|
<Tooltip label={tooltip} placement="top" hasArrow isOpen={true}>
|
||||||
<MultiSelect
|
<FormControl ref={ref} isDisabled={disabled}>
|
||||||
label={
|
{label && <FormLabel>{label}</FormLabel>}
|
||||||
label ? (
|
<MultiSelect
|
||||||
<FormControl ref={ref} isDisabled={disabled}>
|
ref={inputRef}
|
||||||
<FormLabel>{label}</FormLabel>
|
disabled={disabled}
|
||||||
</FormControl>
|
onKeyDown={handleKeyDown}
|
||||||
) : undefined
|
onKeyUp={handleKeyUp}
|
||||||
}
|
searchable={searchable}
|
||||||
ref={inputRef}
|
maxDropdownHeight={300}
|
||||||
disabled={disabled}
|
styles={styles}
|
||||||
onKeyDown={handleKeyDown}
|
{...rest}
|
||||||
onKeyUp={handleKeyUp}
|
/>
|
||||||
searchable={searchable}
|
</FormControl>
|
||||||
maxDropdownHeight={300}
|
|
||||||
styles={styles}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -70,26 +70,23 @@ const IAIMantineSearchableSelect = forwardRef((props: IAISelectProps, ref) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltip} placement="top" hasArrow>
|
<Tooltip label={tooltip} placement="top" hasArrow>
|
||||||
<Select
|
<FormControl ref={ref} isDisabled={disabled}>
|
||||||
ref={inputRef}
|
{label && <FormLabel>{label}</FormLabel>}
|
||||||
label={
|
<Select
|
||||||
label ? (
|
ref={inputRef}
|
||||||
<FormControl ref={ref} isDisabled={disabled}>
|
withinPortal
|
||||||
<FormLabel>{label}</FormLabel>
|
disabled={disabled}
|
||||||
</FormControl>
|
searchValue={searchValue}
|
||||||
) : undefined
|
onSearchChange={setSearchValue}
|
||||||
}
|
onChange={handleChange}
|
||||||
disabled={disabled}
|
onKeyDown={handleKeyDown}
|
||||||
searchValue={searchValue}
|
onKeyUp={handleKeyUp}
|
||||||
onSearchChange={setSearchValue}
|
searchable={searchable}
|
||||||
onChange={handleChange}
|
maxDropdownHeight={300}
|
||||||
onKeyDown={handleKeyDown}
|
styles={styles}
|
||||||
onKeyUp={handleKeyUp}
|
{...rest}
|
||||||
searchable={searchable}
|
/>
|
||||||
maxDropdownHeight={300}
|
</FormControl>
|
||||||
styles={styles}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -22,19 +22,10 @@ const IAIMantineSelect = forwardRef((props: IAISelectProps, ref) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label={tooltip} placement="top" hasArrow>
|
<Tooltip label={tooltip} placement="top" hasArrow>
|
||||||
<Select
|
<FormControl ref={ref} isRequired={required} isDisabled={disabled}>
|
||||||
label={
|
<FormLabel>{label}</FormLabel>
|
||||||
label ? (
|
<Select disabled={disabled} ref={inputRef} styles={styles} {...rest} />
|
||||||
<FormControl ref={ref} isRequired={required} isDisabled={disabled}>
|
</FormControl>
|
||||||
<FormLabel>{label}</FormLabel>
|
|
||||||
</FormControl>
|
|
||||||
) : undefined
|
|
||||||
}
|
|
||||||
disabled={disabled}
|
|
||||||
ref={inputRef}
|
|
||||||
styles={styles}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { ControlNetConfig } from '../store/controlNetSlice';
|
import { ControlNetConfig } from '../store/controlNetSlice';
|
||||||
import CannyProcessor from './processors/CannyProcessor';
|
import CannyProcessor from './processors/CannyProcessor';
|
||||||
|
import ColorMapProcessor from './processors/ColorMapProcessor';
|
||||||
import ContentShuffleProcessor from './processors/ContentShuffleProcessor';
|
import ContentShuffleProcessor from './processors/ContentShuffleProcessor';
|
||||||
import HedProcessor from './processors/HedProcessor';
|
import HedProcessor from './processors/HedProcessor';
|
||||||
import LineartAnimeProcessor from './processors/LineartAnimeProcessor';
|
import LineartAnimeProcessor from './processors/LineartAnimeProcessor';
|
||||||
@ -30,6 +31,16 @@ const ControlNetProcessorComponent = (props: ControlNetProcessorProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (processorNode.type === 'color_map_image_processor') {
|
||||||
|
return (
|
||||||
|
<ColorMapProcessor
|
||||||
|
controlNetId={controlNetId}
|
||||||
|
processorNode={processorNode}
|
||||||
|
isEnabled={isEnabled}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (processorNode.type === 'hed_image_processor') {
|
if (processorNode.type === 'hed_image_processor') {
|
||||||
return (
|
return (
|
||||||
<HedProcessor
|
<HedProcessor
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import {
|
import {
|
||||||
ControlNetConfig,
|
ControlNetConfig,
|
||||||
controlNetBeginStepPctChanged,
|
controlNetBeginStepPctChanged,
|
||||||
@ -50,7 +50,7 @@ const ParamControlNetBeginEnd = (props: Props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="controlNetBeginEnd">
|
<IAIInformationalPopover feature="controlNetBeginEnd">
|
||||||
<FormControl isDisabled={!isEnabled}>
|
<FormControl isDisabled={!isEnabled}>
|
||||||
<FormLabel>{t('controlnet.beginEndStepPercent')}</FormLabel>
|
<FormLabel>{t('controlnet.beginEndStepPercent')}</FormLabel>
|
||||||
<HStack w="100%" gap={2} alignItems="center">
|
<HStack w="100%" gap={2} alignItems="center">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import {
|
import {
|
||||||
ControlModes,
|
ControlModes,
|
||||||
@ -35,7 +35,7 @@ export default function ParamControlNetControlMode(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="controlNetControlMode">
|
<IAIInformationalPopover feature="controlNetControlMode">
|
||||||
<IAIMantineSelect
|
<IAIMantineSelect
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
label={t('controlnet.controlMode')}
|
label={t('controlnet.controlMode')}
|
||||||
|
@ -3,7 +3,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { isControlNetEnabledToggled } from 'features/controlNet/store/controlNetSlice';
|
import { isControlNetEnabledToggled } from 'features/controlNet/store/controlNetSlice';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
@ -28,7 +28,7 @@ const ParamControlNetFeatureToggle = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box width="100%">
|
<Box width="100%">
|
||||||
<IAIInformationalPopover details="controlNetToggle">
|
<IAIInformationalPopover feature="controlNet">
|
||||||
<IAISwitch
|
<IAISwitch
|
||||||
label="Enable ControlNet"
|
label="Enable ControlNet"
|
||||||
isChecked={isEnabled}
|
isChecked={isEnabled}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import {
|
import {
|
||||||
ControlNetConfig,
|
ControlNetConfig,
|
||||||
@ -34,7 +34,7 @@ export default function ParamControlNetResizeMode(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="controlNetResizeMode">
|
<IAIInformationalPopover feature="controlNetResizeMode">
|
||||||
<IAIMantineSelect
|
<IAIMantineSelect
|
||||||
disabled={!isEnabled}
|
disabled={!isEnabled}
|
||||||
label={t('controlnet.resizeMode')}
|
label={t('controlnet.resizeMode')}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useAppDispatch } from 'app/store/storeHooks';
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import {
|
import {
|
||||||
ControlNetConfig,
|
ControlNetConfig,
|
||||||
@ -24,7 +24,7 @@ const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="controlNetWeight">
|
<IAIInformationalPopover feature="controlNetWeight">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
isDisabled={!isEnabled}
|
isDisabled={!isEnabled}
|
||||||
label={t('controlnet.weight')}
|
label={t('controlnet.weight')}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import IAISlider from 'common/components/IAISlider';
|
||||||
|
import { CONTROLNET_PROCESSORS } from 'features/controlNet/store/constants';
|
||||||
|
import { RequiredColorMapImageProcessorInvocation } from 'features/controlNet/store/types';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useProcessorNodeChanged } from '../hooks/useProcessorNodeChanged';
|
||||||
|
import ProcessorWrapper from './common/ProcessorWrapper';
|
||||||
|
|
||||||
|
const DEFAULTS = CONTROLNET_PROCESSORS.color_map_image_processor
|
||||||
|
.default as RequiredColorMapImageProcessorInvocation;
|
||||||
|
|
||||||
|
type ColorMapProcessorProps = {
|
||||||
|
controlNetId: string;
|
||||||
|
processorNode: RequiredColorMapImageProcessorInvocation;
|
||||||
|
isEnabled: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ColorMapProcessor = (props: ColorMapProcessorProps) => {
|
||||||
|
const { controlNetId, processorNode, isEnabled } = props;
|
||||||
|
const { color_map_tile_size } = processorNode;
|
||||||
|
const processorChanged = useProcessorNodeChanged();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleColorMapTileSizeChanged = useCallback(
|
||||||
|
(v: number) => {
|
||||||
|
processorChanged(controlNetId, { color_map_tile_size: v });
|
||||||
|
},
|
||||||
|
[controlNetId, processorChanged]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleColorMapTileSizeReset = useCallback(() => {
|
||||||
|
processorChanged(controlNetId, {
|
||||||
|
color_map_tile_size: DEFAULTS.color_map_tile_size,
|
||||||
|
});
|
||||||
|
}, [controlNetId, processorChanged]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProcessorWrapper>
|
||||||
|
<IAISlider
|
||||||
|
isDisabled={!isEnabled}
|
||||||
|
label={t('controlnet.colorMapTileSize')}
|
||||||
|
value={color_map_tile_size}
|
||||||
|
onChange={handleColorMapTileSizeChanged}
|
||||||
|
handleReset={handleColorMapTileSizeReset}
|
||||||
|
withReset
|
||||||
|
min={1}
|
||||||
|
max={256}
|
||||||
|
step={1}
|
||||||
|
withInput
|
||||||
|
withSliderMarks
|
||||||
|
sliderNumberInputProps={{
|
||||||
|
max: 4096,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ProcessorWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(ColorMapProcessor);
|
@ -4,5 +4,9 @@ import { PropsWithChildren } from 'react';
|
|||||||
type Props = PropsWithChildren;
|
type Props = PropsWithChildren;
|
||||||
|
|
||||||
export default function ProcessorWrapper(props: Props) {
|
export default function ProcessorWrapper(props: Props) {
|
||||||
return <Flex sx={{ flexDirection: 'column', gap: 2 }}>{props.children}</Flex>;
|
return (
|
||||||
|
<Flex sx={{ flexDirection: 'column', gap: 2, pb: 2 }}>
|
||||||
|
{props.children}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import i18n from 'i18next';
|
||||||
import {
|
import {
|
||||||
ControlNetProcessorType,
|
ControlNetProcessorType,
|
||||||
RequiredControlNetProcessorNode,
|
RequiredControlNetProcessorNode,
|
||||||
} from './types';
|
} from './types';
|
||||||
import i18n from 'i18next';
|
|
||||||
|
|
||||||
type ControlNetProcessorsDict = Record<
|
type ControlNetProcessorsDict = Record<
|
||||||
ControlNetProcessorType,
|
ControlNetProcessorType,
|
||||||
@ -50,6 +50,20 @@ export const CONTROLNET_PROCESSORS: ControlNetProcessorsDict = {
|
|||||||
high_threshold: 200,
|
high_threshold: 200,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
color_map_image_processor: {
|
||||||
|
type: 'color_map_image_processor',
|
||||||
|
get label() {
|
||||||
|
return i18n.t('controlnet.colorMap');
|
||||||
|
},
|
||||||
|
get description() {
|
||||||
|
return i18n.t('controlnet.colorMapDescription');
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
id: 'color_map_image_processor',
|
||||||
|
type: 'color_map_image_processor',
|
||||||
|
color_map_tile_size: 64,
|
||||||
|
},
|
||||||
|
},
|
||||||
content_shuffle_image_processor: {
|
content_shuffle_image_processor: {
|
||||||
type: 'content_shuffle_image_processor',
|
type: 'content_shuffle_image_processor',
|
||||||
get label() {
|
get label() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { isObject } from 'lodash-es';
|
import { isObject } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
CannyImageProcessorInvocation,
|
CannyImageProcessorInvocation,
|
||||||
|
ColorMapImageProcessorInvocation,
|
||||||
ContentShuffleImageProcessorInvocation,
|
ContentShuffleImageProcessorInvocation,
|
||||||
HedImageProcessorInvocation,
|
HedImageProcessorInvocation,
|
||||||
LineartAnimeImageProcessorInvocation,
|
LineartAnimeImageProcessorInvocation,
|
||||||
@ -20,6 +21,7 @@ import { O } from 'ts-toolbelt';
|
|||||||
*/
|
*/
|
||||||
export type ControlNetProcessorNode =
|
export type ControlNetProcessorNode =
|
||||||
| CannyImageProcessorInvocation
|
| CannyImageProcessorInvocation
|
||||||
|
| ColorMapImageProcessorInvocation
|
||||||
| ContentShuffleImageProcessorInvocation
|
| ContentShuffleImageProcessorInvocation
|
||||||
| HedImageProcessorInvocation
|
| HedImageProcessorInvocation
|
||||||
| LineartAnimeImageProcessorInvocation
|
| LineartAnimeImageProcessorInvocation
|
||||||
@ -47,6 +49,14 @@ export type RequiredCannyImageProcessorInvocation = O.Required<
|
|||||||
'type' | 'low_threshold' | 'high_threshold'
|
'type' | 'low_threshold' | 'high_threshold'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Color Map processor node, with parameters flagged as required
|
||||||
|
*/
|
||||||
|
export type RequiredColorMapImageProcessorInvocation = O.Required<
|
||||||
|
ColorMapImageProcessorInvocation,
|
||||||
|
'type' | 'color_map_tile_size'
|
||||||
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ContentShuffle processor node, with parameters flagged as required
|
* The ContentShuffle processor node, with parameters flagged as required
|
||||||
*/
|
*/
|
||||||
@ -140,6 +150,7 @@ export type RequiredZoeDepthImageProcessorInvocation = O.Required<
|
|||||||
*/
|
*/
|
||||||
export type RequiredControlNetProcessorNode = O.Required<
|
export type RequiredControlNetProcessorNode = O.Required<
|
||||||
| RequiredCannyImageProcessorInvocation
|
| RequiredCannyImageProcessorInvocation
|
||||||
|
| RequiredColorMapImageProcessorInvocation
|
||||||
| RequiredContentShuffleImageProcessorInvocation
|
| RequiredContentShuffleImageProcessorInvocation
|
||||||
| RequiredHedImageProcessorInvocation
|
| RequiredHedImageProcessorInvocation
|
||||||
| RequiredLineartAnimeImageProcessorInvocation
|
| RequiredLineartAnimeImageProcessorInvocation
|
||||||
@ -166,6 +177,22 @@ export const isCannyImageProcessorInvocation = (
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type guard for ColorMapImageProcessorInvocation
|
||||||
|
*/
|
||||||
|
export const isColorMapImageProcessorInvocation = (
|
||||||
|
obj: unknown
|
||||||
|
): obj is ColorMapImageProcessorInvocation => {
|
||||||
|
if (
|
||||||
|
isObject(obj) &&
|
||||||
|
'type' in obj &&
|
||||||
|
obj.type === 'color_map_image_processor'
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type guard for ContentShuffleImageProcessorInvocation
|
* Type guard for ContentShuffleImageProcessorInvocation
|
||||||
*/
|
*/
|
||||||
|
@ -43,8 +43,8 @@ const ParamDynamicPromptsCollapse = () => {
|
|||||||
activeLabel={activeLabel}
|
activeLabel={activeLabel}
|
||||||
>
|
>
|
||||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||||
<ParamDynamicPromptsSeedBehaviour />
|
|
||||||
<ParamDynamicPromptsPreview />
|
<ParamDynamicPromptsPreview />
|
||||||
|
<ParamDynamicPromptsSeedBehaviour />
|
||||||
<ParamDynamicPromptsMaxPrompts />
|
<ParamDynamicPromptsMaxPrompts />
|
||||||
</Flex>
|
</Flex>
|
||||||
</IAICollapse>
|
</IAICollapse>
|
||||||
|
@ -4,9 +4,8 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
|||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { combinatorialToggled } from '../store/dynamicPromptsSlice';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import { combinatorialToggled } from '../store/dynamicPromptsSlice';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
stateSelector,
|
stateSelector,
|
||||||
@ -28,13 +27,11 @@ const ParamDynamicPromptsCombinatorial = () => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="dynamicPromptsCombinatorial">
|
<IAISwitch
|
||||||
<IAISwitch
|
label={t('dynamicPrompts.combinatorial')}
|
||||||
label={t('dynamicPrompts.combinatorial')}
|
isChecked={combinatorial}
|
||||||
isChecked={combinatorial}
|
onChange={handleChange}
|
||||||
onChange={handleChange}
|
/>
|
||||||
/>
|
|
||||||
</IAIInformationalPopover>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
maxPromptsReset,
|
maxPromptsReset,
|
||||||
} from '../store/dynamicPromptsSlice';
|
} from '../store/dynamicPromptsSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
stateSelector,
|
stateSelector,
|
||||||
@ -46,19 +47,21 @@ const ParamDynamicPromptsMaxPrompts = () => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAISlider
|
<IAIInformationalPopover feature="dynamicPromptsMaxPrompts">
|
||||||
label={t('dynamicPrompts.maxPrompts')}
|
<IAISlider
|
||||||
isDisabled={isDisabled}
|
label={t('dynamicPrompts.maxPrompts')}
|
||||||
min={min}
|
isDisabled={isDisabled}
|
||||||
max={sliderMax}
|
min={min}
|
||||||
value={maxPrompts}
|
max={sliderMax}
|
||||||
onChange={handleChange}
|
value={maxPrompts}
|
||||||
sliderNumberInputProps={{ max: inputMax }}
|
onChange={handleChange}
|
||||||
withSliderMarks
|
sliderNumberInputProps={{ max: inputMax }}
|
||||||
withInput
|
withSliderMarks
|
||||||
withReset
|
withInput
|
||||||
handleReset={handleReset}
|
withReset
|
||||||
/>
|
handleReset={handleReset}
|
||||||
|
/>
|
||||||
|
</IAIInformationalPopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import { stateSelector } from 'app/store/store';
|
|||||||
import { useAppSelector } from 'app/store/storeHooks';
|
import { useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||||
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
|
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { FaCircleExclamation } from 'react-icons/fa6';
|
import { FaCircleExclamation } from 'react-icons/fa6';
|
||||||
@ -42,58 +43,73 @@ const ParamDynamicPromptsPreview = () => {
|
|||||||
|
|
||||||
if (isError) {
|
if (isError) {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<IAIInformationalPopover feature="dynamicPrompts">
|
||||||
w="full"
|
<Flex
|
||||||
h="full"
|
w="full"
|
||||||
layerStyle="second"
|
h="full"
|
||||||
alignItems="center"
|
layerStyle="second"
|
||||||
justifyContent="center"
|
alignItems="center"
|
||||||
p={8}
|
justifyContent="center"
|
||||||
>
|
p={8}
|
||||||
<IAINoContentFallback
|
>
|
||||||
icon={FaCircleExclamation}
|
<IAINoContentFallback
|
||||||
label="Problem generating prompts"
|
icon={FaCircleExclamation}
|
||||||
/>
|
label="Problem generating prompts"
|
||||||
</Flex>
|
/>
|
||||||
|
</Flex>
|
||||||
|
</IAIInformationalPopover>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormControl isInvalid={Boolean(parsingError)}>
|
<IAIInformationalPopover feature="dynamicPrompts">
|
||||||
<FormLabel whiteSpace="nowrap" overflow="hidden" textOverflow="ellipsis">
|
<FormControl isInvalid={Boolean(parsingError)}>
|
||||||
Prompts Preview ({prompts.length}){parsingError && ` - ${parsingError}`}
|
<FormLabel
|
||||||
</FormLabel>
|
whiteSpace="nowrap"
|
||||||
<Flex h={64} pos="relative" layerStyle="third" borderRadius="base" p={2}>
|
overflow="hidden"
|
||||||
<ScrollableContent>
|
textOverflow="ellipsis"
|
||||||
<OrderedList stylePosition="inside" ms={0}>
|
>
|
||||||
{prompts.map((prompt, i) => (
|
Prompts Preview ({prompts.length})
|
||||||
<ListItem
|
{parsingError && ` - ${parsingError}`}
|
||||||
fontSize="sm"
|
</FormLabel>
|
||||||
key={`${prompt}.${i}`}
|
<Flex
|
||||||
sx={listItemStyles}
|
h={64}
|
||||||
>
|
pos="relative"
|
||||||
<Text as="span">{prompt}</Text>
|
layerStyle="third"
|
||||||
</ListItem>
|
borderRadius="base"
|
||||||
))}
|
p={2}
|
||||||
</OrderedList>
|
>
|
||||||
</ScrollableContent>
|
<ScrollableContent>
|
||||||
{isLoading && (
|
<OrderedList stylePosition="inside" ms={0}>
|
||||||
<Flex
|
{prompts.map((prompt, i) => (
|
||||||
pos="absolute"
|
<ListItem
|
||||||
w="full"
|
fontSize="sm"
|
||||||
h="full"
|
key={`${prompt}.${i}`}
|
||||||
top={0}
|
sx={listItemStyles}
|
||||||
insetInlineStart={0}
|
>
|
||||||
layerStyle="second"
|
<Text as="span">{prompt}</Text>
|
||||||
opacity={0.7}
|
</ListItem>
|
||||||
alignItems="center"
|
))}
|
||||||
justifyContent="center"
|
</OrderedList>
|
||||||
>
|
</ScrollableContent>
|
||||||
<Spinner />
|
{isLoading && (
|
||||||
</Flex>
|
<Flex
|
||||||
)}
|
pos="absolute"
|
||||||
</Flex>
|
w="full"
|
||||||
</FormControl>
|
h="full"
|
||||||
|
top={0}
|
||||||
|
insetInlineStart={0}
|
||||||
|
layerStyle="second"
|
||||||
|
opacity={0.7}
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
>
|
||||||
|
<Spinner />
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
</FormControl>
|
||||||
|
</IAIInformationalPopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
seedBehaviourChanged,
|
seedBehaviourChanged,
|
||||||
} from '../store/dynamicPromptsSlice';
|
} from '../store/dynamicPromptsSlice';
|
||||||
import IAIMantineSelectItemWithDescription from 'common/components/IAIMantineSelectItemWithDescription';
|
import IAIMantineSelectItemWithDescription from 'common/components/IAIMantineSelectItemWithDescription';
|
||||||
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
label: string;
|
label: string;
|
||||||
@ -47,13 +48,15 @@ const ParamDynamicPromptsSeedBehaviour = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIMantineSelect
|
<IAIInformationalPopover feature="dynamicPromptsSeedBehaviour">
|
||||||
label={t('dynamicPrompts.seedBehaviour.label')}
|
<IAIMantineSelect
|
||||||
value={seedBehaviour}
|
label={t('dynamicPrompts.seedBehaviour.label')}
|
||||||
data={data}
|
value={seedBehaviour}
|
||||||
itemComponent={IAIMantineSelectItemWithDescription}
|
data={data}
|
||||||
onChange={handleChange}
|
itemComponent={IAIMantineSelectItemWithDescription}
|
||||||
/>
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</IAIInformationalPopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
loraWeightChanged,
|
loraWeightChanged,
|
||||||
loraWeightReset,
|
loraWeightReset,
|
||||||
} from '../store/loraSlice';
|
} from '../store/loraSlice';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
lora: LoRA;
|
lora: LoRA;
|
||||||
@ -36,7 +36,7 @@ const ParamLora = (props: Props) => {
|
|||||||
}, [dispatch, lora.id]);
|
}, [dispatch, lora.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="lora">
|
<IAIInformationalPopover feature="lora">
|
||||||
<Flex sx={{ gap: 2.5, alignItems: 'flex-end' }}>
|
<Flex sx={{ gap: 2.5, alignItems: 'flex-end' }}>
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={lora.model_name}
|
label={lora.model_name}
|
||||||
|
@ -25,8 +25,8 @@ const InvocationNodeFooter = ({ nodeId }: Props) => {
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{hasImageOutput && <EmbedWorkflowCheckbox nodeId={nodeId} />}
|
|
||||||
<UseCacheCheckbox nodeId={nodeId} />
|
<UseCacheCheckbox nodeId={nodeId} />
|
||||||
|
{hasImageOutput && <EmbedWorkflowCheckbox nodeId={nodeId} />}
|
||||||
{hasImageOutput && <SaveToGalleryCheckbox nodeId={nodeId} />}
|
{hasImageOutput && <SaveToGalleryCheckbox nodeId={nodeId} />}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
|
@ -16,6 +16,7 @@ import SchedulerInputField from './inputs/SchedulerInputField';
|
|||||||
import StringInputField from './inputs/StringInputField';
|
import StringInputField from './inputs/StringInputField';
|
||||||
import VaeModelInputField from './inputs/VaeModelInputField';
|
import VaeModelInputField from './inputs/VaeModelInputField';
|
||||||
import IPAdapterModelInputField from './inputs/IPAdapterModelInputField';
|
import IPAdapterModelInputField from './inputs/IPAdapterModelInputField';
|
||||||
|
import BoardInputField from './inputs/BoardInputField';
|
||||||
|
|
||||||
type InputFieldProps = {
|
type InputFieldProps = {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
@ -99,6 +100,16 @@ const InputFieldRenderer = ({ nodeId, fieldName }: InputFieldProps) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field?.type === 'BoardField' && fieldTemplate?.type === 'BoardField') {
|
||||||
|
return (
|
||||||
|
<BoardInputField
|
||||||
|
nodeId={nodeId}
|
||||||
|
field={field}
|
||||||
|
fieldTemplate={fieldTemplate}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
field?.type === 'MainModelField' &&
|
field?.type === 'MainModelField' &&
|
||||||
fieldTemplate?.type === 'MainModelField'
|
fieldTemplate?.type === 'MainModelField'
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
import { SelectItem } from '@mantine/core';
|
||||||
|
import { useAppDispatch } from 'app/store/storeHooks';
|
||||||
|
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
|
||||||
|
import { fieldBoardValueChanged } from 'features/nodes/store/nodesSlice';
|
||||||
|
import {
|
||||||
|
BoardInputFieldTemplate,
|
||||||
|
BoardInputFieldValue,
|
||||||
|
FieldComponentProps,
|
||||||
|
} from 'features/nodes/types/types';
|
||||||
|
import { memo, useCallback } from 'react';
|
||||||
|
import { useListAllBoardsQuery } from 'services/api/endpoints/boards';
|
||||||
|
|
||||||
|
const BoardInputFieldComponent = (
|
||||||
|
props: FieldComponentProps<BoardInputFieldValue, BoardInputFieldTemplate>
|
||||||
|
) => {
|
||||||
|
const { nodeId, field } = props;
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const { data, hasBoards } = useListAllBoardsQuery(undefined, {
|
||||||
|
selectFromResult: ({ data }) => {
|
||||||
|
const boards: SelectItem[] = [
|
||||||
|
{
|
||||||
|
label: 'None',
|
||||||
|
value: 'none',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
data?.forEach(({ board_id, board_name }) => {
|
||||||
|
boards.push({
|
||||||
|
label: board_name,
|
||||||
|
value: board_id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
data: boards,
|
||||||
|
hasBoards: boards.length > 1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(v: string | null) => {
|
||||||
|
dispatch(
|
||||||
|
fieldBoardValueChanged({
|
||||||
|
nodeId,
|
||||||
|
fieldName: field.name,
|
||||||
|
value: v && v !== 'none' ? { board_id: v } : undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[dispatch, field.name, nodeId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IAIMantineSearchableSelect
|
||||||
|
className="nowheel nodrag"
|
||||||
|
value={field.value?.board_id ?? 'none'}
|
||||||
|
data={data}
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled={!hasBoards}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(BoardInputFieldComponent);
|
@ -65,11 +65,6 @@ const SchedulerInputField = (
|
|||||||
return (
|
return (
|
||||||
<IAIMantineSearchableSelect
|
<IAIMantineSearchableSelect
|
||||||
className="nowheel nodrag"
|
className="nowheel nodrag"
|
||||||
sx={{
|
|
||||||
'.mantine-Select-dropdown': {
|
|
||||||
width: '14rem !important',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
value={field.value}
|
value={field.value}
|
||||||
data={data}
|
data={data}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
@ -143,7 +143,7 @@ export const useBuildNodeData = () => {
|
|||||||
notes: '',
|
notes: '',
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
embedWorkflow: false,
|
embedWorkflow: false,
|
||||||
isIntermediate: true,
|
isIntermediate: type === 'save_image' ? false : true,
|
||||||
inputs,
|
inputs,
|
||||||
outputs,
|
outputs,
|
||||||
useCache: template.useCache,
|
useCache: template.useCache,
|
||||||
|
@ -17,8 +17,12 @@ export const useHasImageOutput = (nodeId: string) => {
|
|||||||
if (!isInvocationNode(node)) {
|
if (!isInvocationNode(node)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return some(node.data.outputs, (output) =>
|
return some(
|
||||||
IMAGE_FIELDS.includes(output.type)
|
node.data.outputs,
|
||||||
|
(output) =>
|
||||||
|
IMAGE_FIELDS.includes(output.type) &&
|
||||||
|
// the image primitive node does not actually save the image, do not show the image-saving checkboxes
|
||||||
|
node.data.type !== 'image'
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
defaultSelectorOptions
|
defaultSelectorOptions
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { DRAG_HANDLE_CLASSNAME } from '../types/constants';
|
import { DRAG_HANDLE_CLASSNAME } from '../types/constants';
|
||||||
import {
|
import {
|
||||||
|
BoardInputFieldValue,
|
||||||
BooleanInputFieldValue,
|
BooleanInputFieldValue,
|
||||||
ColorInputFieldValue,
|
ColorInputFieldValue,
|
||||||
ControlNetModelInputFieldValue,
|
ControlNetModelInputFieldValue,
|
||||||
@ -494,6 +495,12 @@ const nodesSlice = createSlice({
|
|||||||
) => {
|
) => {
|
||||||
fieldValueReducer(state, action);
|
fieldValueReducer(state, action);
|
||||||
},
|
},
|
||||||
|
fieldBoardValueChanged: (
|
||||||
|
state,
|
||||||
|
action: FieldValueAction<BoardInputFieldValue>
|
||||||
|
) => {
|
||||||
|
fieldValueReducer(state, action);
|
||||||
|
},
|
||||||
fieldImageValueChanged: (
|
fieldImageValueChanged: (
|
||||||
state,
|
state,
|
||||||
action: FieldValueAction<ImageInputFieldValue>
|
action: FieldValueAction<ImageInputFieldValue>
|
||||||
@ -871,7 +878,7 @@ const nodesSlice = createSlice({
|
|||||||
builder.addCase(appSocketQueueItemStatusChanged, (state, action) => {
|
builder.addCase(appSocketQueueItemStatusChanged, (state, action) => {
|
||||||
if (['in_progress'].includes(action.payload.data.status)) {
|
if (['in_progress'].includes(action.payload.data.status)) {
|
||||||
forEach(state.nodeExecutionStates, (nes) => {
|
forEach(state.nodeExecutionStates, (nes) => {
|
||||||
nes.status = NodeStatus.IN_PROGRESS;
|
nes.status = NodeStatus.PENDING;
|
||||||
nes.error = null;
|
nes.error = null;
|
||||||
nes.progress = null;
|
nes.progress = null;
|
||||||
nes.progressImage = null;
|
nes.progressImage = null;
|
||||||
@ -897,6 +904,7 @@ export const {
|
|||||||
imageCollectionFieldValueChanged,
|
imageCollectionFieldValueChanged,
|
||||||
fieldStringValueChanged,
|
fieldStringValueChanged,
|
||||||
fieldNumberValueChanged,
|
fieldNumberValueChanged,
|
||||||
|
fieldBoardValueChanged,
|
||||||
fieldBooleanValueChanged,
|
fieldBooleanValueChanged,
|
||||||
fieldImageValueChanged,
|
fieldImageValueChanged,
|
||||||
fieldColorValueChanged,
|
fieldColorValueChanged,
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { FieldType, FieldUIConfig } from './types';
|
import {
|
||||||
|
FieldType,
|
||||||
|
FieldTypeMap,
|
||||||
|
FieldTypeMapWithNumber,
|
||||||
|
FieldUIConfig,
|
||||||
|
} from './types';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
|
|
||||||
export const HANDLE_TOOLTIP_OPEN_DELAY = 500;
|
export const HANDLE_TOOLTIP_OPEN_DELAY = 500;
|
||||||
@ -28,7 +33,7 @@ export const COLLECTION_TYPES: FieldType[] = [
|
|||||||
'ColorCollection',
|
'ColorCollection',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const POLYMORPHIC_TYPES = [
|
export const POLYMORPHIC_TYPES: FieldType[] = [
|
||||||
'IntegerPolymorphic',
|
'IntegerPolymorphic',
|
||||||
'BooleanPolymorphic',
|
'BooleanPolymorphic',
|
||||||
'FloatPolymorphic',
|
'FloatPolymorphic',
|
||||||
@ -40,7 +45,7 @@ export const POLYMORPHIC_TYPES = [
|
|||||||
'ColorPolymorphic',
|
'ColorPolymorphic',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const MODEL_TYPES = [
|
export const MODEL_TYPES: FieldType[] = [
|
||||||
'IPAdapterModelField',
|
'IPAdapterModelField',
|
||||||
'ControlNetModelField',
|
'ControlNetModelField',
|
||||||
'LoRAModelField',
|
'LoRAModelField',
|
||||||
@ -54,7 +59,7 @@ export const MODEL_TYPES = [
|
|||||||
'ClipField',
|
'ClipField',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const COLLECTION_MAP = {
|
export const COLLECTION_MAP: FieldTypeMapWithNumber = {
|
||||||
integer: 'IntegerCollection',
|
integer: 'IntegerCollection',
|
||||||
boolean: 'BooleanCollection',
|
boolean: 'BooleanCollection',
|
||||||
number: 'FloatCollection',
|
number: 'FloatCollection',
|
||||||
@ -71,7 +76,7 @@ export const isCollectionItemType = (
|
|||||||
): itemType is keyof typeof COLLECTION_MAP =>
|
): itemType is keyof typeof COLLECTION_MAP =>
|
||||||
Boolean(itemType && itemType in COLLECTION_MAP);
|
Boolean(itemType && itemType in COLLECTION_MAP);
|
||||||
|
|
||||||
export const SINGLE_TO_POLYMORPHIC_MAP = {
|
export const SINGLE_TO_POLYMORPHIC_MAP: FieldTypeMapWithNumber = {
|
||||||
integer: 'IntegerPolymorphic',
|
integer: 'IntegerPolymorphic',
|
||||||
boolean: 'BooleanPolymorphic',
|
boolean: 'BooleanPolymorphic',
|
||||||
number: 'FloatPolymorphic',
|
number: 'FloatPolymorphic',
|
||||||
@ -84,7 +89,7 @@ export const SINGLE_TO_POLYMORPHIC_MAP = {
|
|||||||
ColorField: 'ColorPolymorphic',
|
ColorField: 'ColorPolymorphic',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const POLYMORPHIC_TO_SINGLE_MAP = {
|
export const POLYMORPHIC_TO_SINGLE_MAP: FieldTypeMap = {
|
||||||
IntegerPolymorphic: 'integer',
|
IntegerPolymorphic: 'integer',
|
||||||
BooleanPolymorphic: 'boolean',
|
BooleanPolymorphic: 'boolean',
|
||||||
FloatPolymorphic: 'float',
|
FloatPolymorphic: 'float',
|
||||||
@ -96,7 +101,7 @@ export const POLYMORPHIC_TO_SINGLE_MAP = {
|
|||||||
ColorPolymorphic: 'ColorField',
|
ColorPolymorphic: 'ColorField',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TYPES_WITH_INPUT_COMPONENTS = [
|
export const TYPES_WITH_INPUT_COMPONENTS: FieldType[] = [
|
||||||
'string',
|
'string',
|
||||||
'StringPolymorphic',
|
'StringPolymorphic',
|
||||||
'boolean',
|
'boolean',
|
||||||
@ -117,6 +122,7 @@ export const TYPES_WITH_INPUT_COMPONENTS = [
|
|||||||
'SDXLMainModelField',
|
'SDXLMainModelField',
|
||||||
'Scheduler',
|
'Scheduler',
|
||||||
'IPAdapterModelField',
|
'IPAdapterModelField',
|
||||||
|
'BoardField',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const isPolymorphicItemType = (
|
export const isPolymorphicItemType = (
|
||||||
@ -240,6 +246,11 @@ export const FIELDS: Record<FieldType, FieldUIConfig> = {
|
|||||||
description: t('nodes.imageFieldDescription'),
|
description: t('nodes.imageFieldDescription'),
|
||||||
title: t('nodes.imageField'),
|
title: t('nodes.imageField'),
|
||||||
},
|
},
|
||||||
|
BoardField: {
|
||||||
|
color: 'purple.500',
|
||||||
|
description: t('nodes.imageFieldDescription'),
|
||||||
|
title: t('nodes.imageField'),
|
||||||
|
},
|
||||||
ImagePolymorphic: {
|
ImagePolymorphic: {
|
||||||
color: 'purple.500',
|
color: 'purple.500',
|
||||||
description: t('nodes.imagePolymorphicDescription'),
|
description: t('nodes.imagePolymorphicDescription'),
|
||||||
|
@ -72,6 +72,7 @@ export type FieldUIConfig = {
|
|||||||
|
|
||||||
// TODO: Get this from the OpenAPI schema? may be tricky...
|
// TODO: Get this from the OpenAPI schema? may be tricky...
|
||||||
export const zFieldType = z.enum([
|
export const zFieldType = z.enum([
|
||||||
|
'BoardField',
|
||||||
'boolean',
|
'boolean',
|
||||||
'BooleanCollection',
|
'BooleanCollection',
|
||||||
'BooleanPolymorphic',
|
'BooleanPolymorphic',
|
||||||
@ -119,6 +120,10 @@ export const zFieldType = z.enum([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export type FieldType = z.infer<typeof zFieldType>;
|
export type FieldType = z.infer<typeof zFieldType>;
|
||||||
|
export type FieldTypeMap = { [key in FieldType]?: FieldType };
|
||||||
|
export type FieldTypeMapWithNumber = {
|
||||||
|
[key in FieldType | 'number']?: FieldType;
|
||||||
|
};
|
||||||
|
|
||||||
export const zReservedFieldType = z.enum([
|
export const zReservedFieldType = z.enum([
|
||||||
'WorkflowField',
|
'WorkflowField',
|
||||||
@ -187,6 +192,11 @@ export const zImageField = z.object({
|
|||||||
});
|
});
|
||||||
export type ImageField = z.infer<typeof zImageField>;
|
export type ImageField = z.infer<typeof zImageField>;
|
||||||
|
|
||||||
|
export const zBoardField = z.object({
|
||||||
|
board_id: z.string().trim().min(1),
|
||||||
|
});
|
||||||
|
export type BoardField = z.infer<typeof zBoardField>;
|
||||||
|
|
||||||
export const zLatentsField = z.object({
|
export const zLatentsField = z.object({
|
||||||
latents_name: z.string().trim().min(1),
|
latents_name: z.string().trim().min(1),
|
||||||
seed: z.number().int().optional(),
|
seed: z.number().int().optional(),
|
||||||
@ -494,6 +504,12 @@ export const zImageInputFieldValue = zInputFieldValueBase.extend({
|
|||||||
});
|
});
|
||||||
export type ImageInputFieldValue = z.infer<typeof zImageInputFieldValue>;
|
export type ImageInputFieldValue = z.infer<typeof zImageInputFieldValue>;
|
||||||
|
|
||||||
|
export const zBoardInputFieldValue = zInputFieldValueBase.extend({
|
||||||
|
type: z.literal('BoardField'),
|
||||||
|
value: zBoardField.optional(),
|
||||||
|
});
|
||||||
|
export type BoardInputFieldValue = z.infer<typeof zBoardInputFieldValue>;
|
||||||
|
|
||||||
export const zImagePolymorphicInputFieldValue = zInputFieldValueBase.extend({
|
export const zImagePolymorphicInputFieldValue = zInputFieldValueBase.extend({
|
||||||
type: z.literal('ImagePolymorphic'),
|
type: z.literal('ImagePolymorphic'),
|
||||||
value: zImageField.optional(),
|
value: zImageField.optional(),
|
||||||
@ -630,6 +646,7 @@ export type SchedulerInputFieldValue = z.infer<
|
|||||||
>;
|
>;
|
||||||
|
|
||||||
export const zInputFieldValue = z.discriminatedUnion('type', [
|
export const zInputFieldValue = z.discriminatedUnion('type', [
|
||||||
|
zBoardInputFieldValue,
|
||||||
zBooleanCollectionInputFieldValue,
|
zBooleanCollectionInputFieldValue,
|
||||||
zBooleanInputFieldValue,
|
zBooleanInputFieldValue,
|
||||||
zBooleanPolymorphicInputFieldValue,
|
zBooleanPolymorphicInputFieldValue,
|
||||||
@ -770,6 +787,11 @@ export type BooleanPolymorphicInputFieldTemplate = Omit<
|
|||||||
type: 'BooleanPolymorphic';
|
type: 'BooleanPolymorphic';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BoardInputFieldTemplate = InputFieldTemplateBase & {
|
||||||
|
default: BoardField;
|
||||||
|
type: 'BoardField';
|
||||||
|
};
|
||||||
|
|
||||||
export type ImageInputFieldTemplate = InputFieldTemplateBase & {
|
export type ImageInputFieldTemplate = InputFieldTemplateBase & {
|
||||||
default: ImageField;
|
default: ImageField;
|
||||||
type: 'ImageField';
|
type: 'ImageField';
|
||||||
@ -952,6 +974,7 @@ export type WorkflowInputFieldTemplate = InputFieldTemplateBase & {
|
|||||||
* maximum length, pattern to match, etc).
|
* maximum length, pattern to match, etc).
|
||||||
*/
|
*/
|
||||||
export type InputFieldTemplate =
|
export type InputFieldTemplate =
|
||||||
|
| BoardInputFieldTemplate
|
||||||
| BooleanCollectionInputFieldTemplate
|
| BooleanCollectionInputFieldTemplate
|
||||||
| BooleanPolymorphicInputFieldTemplate
|
| BooleanPolymorphicInputFieldTemplate
|
||||||
| BooleanInputFieldTemplate
|
| BooleanInputFieldTemplate
|
||||||
|
@ -62,6 +62,8 @@ import {
|
|||||||
ConditioningField,
|
ConditioningField,
|
||||||
IPAdapterInputFieldTemplate,
|
IPAdapterInputFieldTemplate,
|
||||||
IPAdapterModelInputFieldTemplate,
|
IPAdapterModelInputFieldTemplate,
|
||||||
|
BoardInputFieldTemplate,
|
||||||
|
InputFieldTemplate,
|
||||||
} from '../types/types';
|
} from '../types/types';
|
||||||
import { ControlField } from 'services/api/types';
|
import { ControlField } from 'services/api/types';
|
||||||
|
|
||||||
@ -450,6 +452,19 @@ const buildIPAdapterModelInputFieldTemplate = ({
|
|||||||
return template;
|
return template;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const buildBoardInputFieldTemplate = ({
|
||||||
|
schemaObject,
|
||||||
|
baseField,
|
||||||
|
}: BuildInputFieldArg): BoardInputFieldTemplate => {
|
||||||
|
const template: BoardInputFieldTemplate = {
|
||||||
|
...baseField,
|
||||||
|
type: 'BoardField',
|
||||||
|
default: schemaObject.default ?? undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return template;
|
||||||
|
};
|
||||||
|
|
||||||
const buildImageInputFieldTemplate = ({
|
const buildImageInputFieldTemplate = ({
|
||||||
schemaObject,
|
schemaObject,
|
||||||
baseField,
|
baseField,
|
||||||
@ -851,7 +866,10 @@ export const getFieldType = (
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TEMPLATE_BUILDER_MAP = {
|
const TEMPLATE_BUILDER_MAP: {
|
||||||
|
[key in FieldType]?: (arg: BuildInputFieldArg) => InputFieldTemplate;
|
||||||
|
} = {
|
||||||
|
BoardField: buildBoardInputFieldTemplate,
|
||||||
boolean: buildBooleanInputFieldTemplate,
|
boolean: buildBooleanInputFieldTemplate,
|
||||||
BooleanCollection: buildBooleanCollectionInputFieldTemplate,
|
BooleanCollection: buildBooleanCollectionInputFieldTemplate,
|
||||||
BooleanPolymorphic: buildBooleanPolymorphicInputFieldTemplate,
|
BooleanPolymorphic: buildBooleanPolymorphicInputFieldTemplate,
|
||||||
@ -937,7 +955,13 @@ export const buildInputFieldTemplate = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TEMPLATE_BUILDER_MAP[fieldType]({
|
const builder = TEMPLATE_BUILDER_MAP[fieldType];
|
||||||
|
|
||||||
|
if (!builder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder({
|
||||||
schemaObject: fieldSchema,
|
schemaObject: fieldSchema,
|
||||||
baseField,
|
baseField,
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import { InputFieldTemplate, InputFieldValue } from '../types/types';
|
import { FieldType, InputFieldTemplate, InputFieldValue } from '../types/types';
|
||||||
|
|
||||||
const FIELD_VALUE_FALLBACK_MAP = {
|
const FIELD_VALUE_FALLBACK_MAP: {
|
||||||
|
[key in FieldType]: InputFieldValue['value'];
|
||||||
|
} = {
|
||||||
enum: '',
|
enum: '',
|
||||||
|
BoardField: undefined,
|
||||||
boolean: false,
|
boolean: false,
|
||||||
BooleanCollection: [],
|
BooleanCollection: [],
|
||||||
BooleanPolymorphic: false,
|
BooleanPolymorphic: false,
|
||||||
|
@ -24,12 +24,14 @@ export const addSaveImageNode = (
|
|||||||
const activeTabName = activeTabNameSelector(state);
|
const activeTabName = activeTabNameSelector(state);
|
||||||
const is_intermediate =
|
const is_intermediate =
|
||||||
activeTabName === 'unifiedCanvas' ? !state.canvas.shouldAutoSave : false;
|
activeTabName === 'unifiedCanvas' ? !state.canvas.shouldAutoSave : false;
|
||||||
|
const { autoAddBoardId } = state.gallery;
|
||||||
|
|
||||||
const saveImageNode: SaveImageInvocation = {
|
const saveImageNode: SaveImageInvocation = {
|
||||||
id: SAVE_IMAGE,
|
id: SAVE_IMAGE,
|
||||||
type: 'save_image',
|
type: 'save_image',
|
||||||
is_intermediate,
|
is_intermediate,
|
||||||
use_cache: false,
|
use_cache: false,
|
||||||
|
board: autoAddBoardId === 'none' ? undefined : { board_id: autoAddBoardId },
|
||||||
};
|
};
|
||||||
|
|
||||||
graph.nodes[SAVE_IMAGE] = saveImageNode;
|
graph.nodes[SAVE_IMAGE] = saveImageNode;
|
||||||
|
@ -6,15 +6,18 @@ import {
|
|||||||
SaveImageInvocation,
|
SaveImageInvocation,
|
||||||
} from 'services/api/types';
|
} from 'services/api/types';
|
||||||
import { REALESRGAN as ESRGAN, SAVE_IMAGE } from './constants';
|
import { REALESRGAN as ESRGAN, SAVE_IMAGE } from './constants';
|
||||||
|
import { BoardId } from 'features/gallery/store/types';
|
||||||
|
|
||||||
type Arg = {
|
type Arg = {
|
||||||
image_name: string;
|
image_name: string;
|
||||||
esrganModelName: ESRGANModelName;
|
esrganModelName: ESRGANModelName;
|
||||||
|
autoAddBoardId: BoardId;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const buildAdHocUpscaleGraph = ({
|
export const buildAdHocUpscaleGraph = ({
|
||||||
image_name,
|
image_name,
|
||||||
esrganModelName,
|
esrganModelName,
|
||||||
|
autoAddBoardId,
|
||||||
}: Arg): Graph => {
|
}: Arg): Graph => {
|
||||||
const realesrganNode: ESRGANInvocation = {
|
const realesrganNode: ESRGANInvocation = {
|
||||||
id: ESRGAN,
|
id: ESRGAN,
|
||||||
@ -28,6 +31,8 @@ export const buildAdHocUpscaleGraph = ({
|
|||||||
id: SAVE_IMAGE,
|
id: SAVE_IMAGE,
|
||||||
type: 'save_image',
|
type: 'save_image',
|
||||||
use_cache: false,
|
use_cache: false,
|
||||||
|
is_intermediate: false,
|
||||||
|
board: autoAddBoardId === 'none' ? undefined : { board_id: autoAddBoardId },
|
||||||
};
|
};
|
||||||
|
|
||||||
const graph: NonNullableGraph = {
|
const graph: NonNullableGraph = {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { setClipSkip } from 'features/parameters/store/generationSlice';
|
import { setClipSkip } from 'features/parameters/store/generationSlice';
|
||||||
import { clipSkipMap } from 'features/parameters/types/constants';
|
import { clipSkipMap } from 'features/parameters/types/constants';
|
||||||
@ -47,7 +47,7 @@ export default function ParamClipSkip() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="clipSkip">
|
<IAIInformationalPopover feature="clipSkip" placement="top">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.clipSkip')}
|
label={t('parameters.clipSkip')}
|
||||||
aria-label={t('parameters.clipSkip')}
|
aria-label={t('parameters.clipSkip')}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { Box, Flex, Spacer, Text } from '@chakra-ui/react';
|
import { Flex, FormControl, FormLabel, Spacer } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { flipBoundingBoxAxes } from 'features/canvas/store/canvasSlice';
|
import { flipBoundingBoxAxes } from 'features/canvas/store/canvasSlice';
|
||||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
@ -18,7 +19,6 @@ import ParamAspectRatio, {
|
|||||||
} from '../../Core/ParamAspectRatio';
|
} from '../../Core/ParamAspectRatio';
|
||||||
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
|
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
|
||||||
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
|
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
|
||||||
|
|
||||||
const sizeOptsSelector = createSelector(
|
const sizeOptsSelector = createSelector(
|
||||||
[generationSelector, canvasSelector],
|
[generationSelector, canvasSelector],
|
||||||
@ -93,42 +93,29 @@ export default function ParamBoundingBoxSize() {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex alignItems="center" gap={2}>
|
<IAIInformationalPopover feature="paramRatio">
|
||||||
<Box width="full">
|
<FormControl as={Flex} flexDir="row" alignItems="center" gap={2}>
|
||||||
<IAIInformationalPopover details="paramRatio">
|
<FormLabel>{t('parameters.aspectRatio')}</FormLabel>
|
||||||
<Text
|
<Spacer />
|
||||||
sx={{
|
<ParamAspectRatio />
|
||||||
fontSize: 'sm',
|
<IAIIconButton
|
||||||
width: 'full',
|
tooltip={t('ui.swapSizes')}
|
||||||
color: 'base.700',
|
aria-label={t('ui.swapSizes')}
|
||||||
_dark: {
|
size="sm"
|
||||||
color: 'base.300',
|
icon={<MdOutlineSwapVert />}
|
||||||
},
|
fontSize={20}
|
||||||
}}
|
onClick={handleToggleSize}
|
||||||
>
|
/>
|
||||||
{t('parameters.aspectRatio')}
|
<IAIIconButton
|
||||||
</Text>
|
tooltip={t('ui.lockRatio')}
|
||||||
</IAIInformationalPopover>
|
aria-label={t('ui.lockRatio')}
|
||||||
</Box>
|
size="sm"
|
||||||
<Spacer />
|
icon={<FaLock />}
|
||||||
<ParamAspectRatio />
|
isChecked={shouldLockAspectRatio}
|
||||||
<IAIIconButton
|
onClick={handleLockRatio}
|
||||||
tooltip={t('ui.swapSizes')}
|
/>
|
||||||
aria-label={t('ui.swapSizes')}
|
</FormControl>
|
||||||
size="sm"
|
</IAIInformationalPopover>
|
||||||
icon={<MdOutlineSwapVert />}
|
|
||||||
fontSize={20}
|
|
||||||
onClick={handleToggleSize}
|
|
||||||
/>
|
|
||||||
<IAIIconButton
|
|
||||||
tooltip={t('ui.lockRatio')}
|
|
||||||
aria-label={t('ui.lockRatio')}
|
|
||||||
size="sm"
|
|
||||||
icon={<FaLock />}
|
|
||||||
isChecked={shouldLockAspectRatio}
|
|
||||||
onClick={handleLockRatio}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<ParamBoundingBoxWidth />
|
<ParamBoundingBoxWidth />
|
||||||
<ParamBoundingBoxHeight />
|
<ParamBoundingBoxHeight />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { RootState } from 'app/store/store';
|
import type { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import { IAISelectDataType } from 'common/components/IAIMantineSearchableSelect';
|
import { IAISelectDataType } from 'common/components/IAIMantineSearchableSelect';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import { setCanvasCoherenceMode } from 'features/parameters/store/generationSlice';
|
import { setCanvasCoherenceMode } from 'features/parameters/store/generationSlice';
|
||||||
@ -31,7 +31,7 @@ const ParamCanvasCoherenceMode = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="compositingCoherenceMode">
|
<IAIInformationalPopover feature="compositingCoherenceMode">
|
||||||
<IAIMantineSelect
|
<IAIMantineSelect
|
||||||
label={t('parameters.coherenceMode')}
|
label={t('parameters.coherenceMode')}
|
||||||
data={coherenceModeSelectData}
|
data={coherenceModeSelectData}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { RootState } from 'app/store/store';
|
import type { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { setCanvasCoherenceSteps } from 'features/parameters/store/generationSlice';
|
import { setCanvasCoherenceSteps } from 'features/parameters/store/generationSlice';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@ -14,7 +14,7 @@ const ParamCanvasCoherenceSteps = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="compositingCoherenceSteps">
|
<IAIInformationalPopover feature="compositingCoherenceSteps">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.coherenceSteps')}
|
label={t('parameters.coherenceSteps')}
|
||||||
min={1}
|
min={1}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { RootState } from 'app/store/store';
|
import type { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { setCanvasCoherenceStrength } from 'features/parameters/store/generationSlice';
|
import { setCanvasCoherenceStrength } from 'features/parameters/store/generationSlice';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
@ -14,7 +14,7 @@ const ParamCanvasCoherenceStrength = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="compositingStrength">
|
<IAIInformationalPopover feature="compositingStrength">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.coherenceStrength')}
|
label={t('parameters.coherenceStrength')}
|
||||||
min={0}
|
min={0}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { RootState } from 'app/store/store';
|
import type { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { setMaskBlur } from 'features/parameters/store/generationSlice';
|
import { setMaskBlur } from 'features/parameters/store/generationSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -13,7 +13,7 @@ export default function ParamMaskBlur() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="compositingBlur">
|
<IAIInformationalPopover feature="compositingBlur">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.maskBlur')}
|
label={t('parameters.maskBlur')}
|
||||||
min={0}
|
min={0}
|
||||||
|
@ -2,7 +2,7 @@ import { SelectItem } from '@mantine/core';
|
|||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import { setMaskBlurMethod } from 'features/parameters/store/generationSlice';
|
import { setMaskBlurMethod } from 'features/parameters/store/generationSlice';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -29,7 +29,7 @@ export default function ParamMaskBlurMethod() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="compositingBlurMethod">
|
<IAIInformationalPopover feature="compositingBlurMethod">
|
||||||
<IAIMantineSelect
|
<IAIMantineSelect
|
||||||
value={maskBlurMethod}
|
value={maskBlurMethod}
|
||||||
onChange={handleMaskBlurMethodChange}
|
onChange={handleMaskBlurMethodChange}
|
||||||
|
@ -15,19 +15,13 @@ const ParamCompositingSettingsCollapse = () => {
|
|||||||
return (
|
return (
|
||||||
<IAICollapse label={t('parameters.compositingSettingsHeader')}>
|
<IAICollapse label={t('parameters.compositingSettingsHeader')}>
|
||||||
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
|
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
|
||||||
<SubParametersWrapper
|
<SubParametersWrapper label={t('parameters.coherencePassHeader')}>
|
||||||
label={t('parameters.coherencePassHeader')}
|
|
||||||
headerInfoPopover="compositingCoherencePass"
|
|
||||||
>
|
|
||||||
<ParamCanvasCoherenceMode />
|
<ParamCanvasCoherenceMode />
|
||||||
<ParamCanvasCoherenceSteps />
|
<ParamCanvasCoherenceSteps />
|
||||||
<ParamCanvasCoherenceStrength />
|
<ParamCanvasCoherenceStrength />
|
||||||
</SubParametersWrapper>
|
</SubParametersWrapper>
|
||||||
<Divider />
|
<Divider />
|
||||||
<SubParametersWrapper
|
<SubParametersWrapper label={t('parameters.maskAdjustmentsHeader')}>
|
||||||
label={t('parameters.maskAdjustmentsHeader')}
|
|
||||||
headerInfoPopover="compositingMaskAdjustments"
|
|
||||||
>
|
|
||||||
<ParamMaskBlur />
|
<ParamMaskBlur />
|
||||||
<ParamMaskBlurMethod />
|
<ParamMaskBlurMethod />
|
||||||
</SubParametersWrapper>
|
</SubParametersWrapper>
|
||||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import { setInfillMethod } from 'features/parameters/store/generationSlice';
|
import { setInfillMethod } from 'features/parameters/store/generationSlice';
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ const ParamInfillMethod = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="infillMethod">
|
<IAIInformationalPopover feature="infillMethod">
|
||||||
<IAIMantineSelect
|
<IAIMantineSelect
|
||||||
disabled={infill_methods?.length === 0}
|
disabled={infill_methods?.length === 0}
|
||||||
placeholder={isLoading ? 'Loading...' : undefined}
|
placeholder={isLoading ? 'Loading...' : undefined}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
|
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
|
||||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||||
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
|
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
|
||||||
@ -36,7 +36,7 @@ const ParamScaleBeforeProcessing = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="scaleBeforeProcessing">
|
<IAIInformationalPopover feature="scaleBeforeProcessing">
|
||||||
<IAIMantineSearchableSelect
|
<IAIMantineSearchableSelect
|
||||||
label={t('parameters.scaleBeforeProcessing')}
|
label={t('parameters.scaleBeforeProcessing')}
|
||||||
data={BOUNDING_BOX_SCALES_DICT}
|
data={BOUNDING_BOX_SCALES_DICT}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ButtonGroup, Flex } from '@chakra-ui/react';
|
import { ButtonGroup } from '@chakra-ui/react';
|
||||||
import { RootState } from 'app/store/store';
|
import { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIButton from 'common/components/IAIButton';
|
import IAIButton from 'common/components/IAIButton';
|
||||||
@ -29,25 +29,23 @@ export default function ParamAspectRatio() {
|
|||||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap={2} flexGrow={1}>
|
<ButtonGroup isAttached>
|
||||||
<ButtonGroup isAttached>
|
{aspectRatios.map((ratio) => (
|
||||||
{aspectRatios.map((ratio) => (
|
<IAIButton
|
||||||
<IAIButton
|
key={ratio.name}
|
||||||
key={ratio.name}
|
size="sm"
|
||||||
size="sm"
|
isChecked={aspectRatio === ratio.value}
|
||||||
isChecked={aspectRatio === ratio.value}
|
isDisabled={
|
||||||
isDisabled={
|
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
}
|
||||||
}
|
onClick={() => {
|
||||||
onClick={() => {
|
dispatch(setAspectRatio(ratio.value));
|
||||||
dispatch(setAspectRatio(ratio.value));
|
dispatch(setShouldLockAspectRatio(false));
|
||||||
dispatch(setShouldLockAspectRatio(false));
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{ratio.name}
|
||||||
{ratio.name}
|
</IAIButton>
|
||||||
</IAIButton>
|
))}
|
||||||
))}
|
</ButtonGroup>
|
||||||
</ButtonGroup>
|
|
||||||
</Flex>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
import IAINumberInput from 'common/components/IAINumberInput';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { setCfgScale } from 'features/parameters/store/generationSlice';
|
import { setCfgScale } from 'features/parameters/store/generationSlice';
|
||||||
@ -54,7 +54,7 @@ const ParamCFGScale = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return shouldUseSliders ? (
|
return shouldUseSliders ? (
|
||||||
<IAIInformationalPopover details="paramCFGScale">
|
<IAIInformationalPopover feature="paramCFGScale">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.cfgScale')}
|
label={t('parameters.cfgScale')}
|
||||||
step={shift ? 0.1 : 0.5}
|
step={shift ? 0.1 : 0.5}
|
||||||
@ -71,7 +71,7 @@ const ParamCFGScale = () => {
|
|||||||
/>
|
/>
|
||||||
</IAIInformationalPopover>
|
</IAIInformationalPopover>
|
||||||
) : (
|
) : (
|
||||||
<IAIInformationalPopover details="paramCFGScale">
|
<IAIInformationalPopover feature="paramCFGScale">
|
||||||
<IAINumberInput
|
<IAINumberInput
|
||||||
label={t('parameters.cfgScale')}
|
label={t('parameters.cfgScale')}
|
||||||
step={0.5}
|
step={0.5}
|
||||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
import IAINumberInput from 'common/components/IAINumberInput';
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
import { setIterations } from 'features/parameters/store/generationSlice';
|
import { setIterations } from 'features/parameters/store/generationSlice';
|
||||||
@ -61,7 +61,7 @@ const ParamIterations = ({ asSlider }: Props) => {
|
|||||||
}, [dispatch, initial]);
|
}, [dispatch, initial]);
|
||||||
|
|
||||||
return asSlider || shouldUseSliders ? (
|
return asSlider || shouldUseSliders ? (
|
||||||
<IAIInformationalPopover details="paramIterations">
|
<IAIInformationalPopover feature="paramIterations">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.iterations')}
|
label={t('parameters.iterations')}
|
||||||
step={step}
|
step={step}
|
||||||
@ -77,7 +77,7 @@ const ParamIterations = ({ asSlider }: Props) => {
|
|||||||
/>
|
/>
|
||||||
</IAIInformationalPopover>
|
</IAIInformationalPopover>
|
||||||
) : (
|
) : (
|
||||||
<IAIInformationalPopover details="paramIterations">
|
<IAIInformationalPopover feature="paramIterations">
|
||||||
<IAINumberInput
|
<IAINumberInput
|
||||||
label={t('parameters.iterations')}
|
label={t('parameters.iterations')}
|
||||||
step={step}
|
step={step}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Box, FormControl, useDisclosure } from '@chakra-ui/react';
|
import { Box, FormControl, useDisclosure } from '@chakra-ui/react';
|
||||||
import type { RootState } from 'app/store/store';
|
import type { RootState } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAITextarea from 'common/components/IAITextarea';
|
import IAITextarea from 'common/components/IAITextarea';
|
||||||
import AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
|
import AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
|
||||||
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
|
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
|
||||||
@ -76,15 +76,15 @@ const ParamNegativeConditioning = () => {
|
|||||||
const isEmbeddingEnabled = useFeatureStatus('embedding').isFeatureEnabled;
|
const isEmbeddingEnabled = useFeatureStatus('embedding').isFeatureEnabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover
|
<FormControl>
|
||||||
placement="right"
|
<ParamEmbeddingPopover
|
||||||
details="paramNegativeConditioning"
|
isOpen={isOpen}
|
||||||
>
|
onClose={onClose}
|
||||||
<FormControl>
|
onSelect={handleSelectEmbedding}
|
||||||
<ParamEmbeddingPopover
|
>
|
||||||
isOpen={isOpen}
|
<IAIInformationalPopover
|
||||||
onClose={onClose}
|
feature="paramNegativeConditioning"
|
||||||
onSelect={handleSelectEmbedding}
|
placement="right"
|
||||||
>
|
>
|
||||||
<IAITextarea
|
<IAITextarea
|
||||||
id="negativePrompt"
|
id="negativePrompt"
|
||||||
@ -98,20 +98,20 @@ const ParamNegativeConditioning = () => {
|
|||||||
minH={16}
|
minH={16}
|
||||||
{...(isEmbeddingEnabled && { onKeyDown: handleKeyDown })}
|
{...(isEmbeddingEnabled && { onKeyDown: handleKeyDown })}
|
||||||
/>
|
/>
|
||||||
</ParamEmbeddingPopover>
|
</IAIInformationalPopover>
|
||||||
{!isOpen && isEmbeddingEnabled && (
|
</ParamEmbeddingPopover>
|
||||||
<Box
|
{!isOpen && isEmbeddingEnabled && (
|
||||||
sx={{
|
<Box
|
||||||
position: 'absolute',
|
sx={{
|
||||||
top: 0,
|
position: 'absolute',
|
||||||
insetInlineEnd: 0,
|
top: 0,
|
||||||
}}
|
insetInlineEnd: 0,
|
||||||
>
|
}}
|
||||||
<AddEmbeddingButton onClick={onOpen} />
|
>
|
||||||
</Box>
|
<AddEmbeddingButton onClick={onOpen} />
|
||||||
)}
|
</Box>
|
||||||
</FormControl>
|
)}
|
||||||
</IAIInformationalPopover>
|
</FormControl>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import { Box, FormControl, useDisclosure } from '@chakra-ui/react';
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAITextarea from 'common/components/IAITextarea';
|
import IAITextarea from 'common/components/IAITextarea';
|
||||||
import AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
|
import AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
|
||||||
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
|
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
|
||||||
@ -12,7 +13,6 @@ import { flushSync } from 'react-dom';
|
|||||||
import { useHotkeys } from 'react-hotkeys-hook';
|
import { useHotkeys } from 'react-hotkeys-hook';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
|
||||||
import IAIInformationalPopover from '../../../../../common/components/IAIInformationalPopover';
|
|
||||||
|
|
||||||
const promptInputSelector = createSelector(
|
const promptInputSelector = createSelector(
|
||||||
[stateSelector],
|
[stateSelector],
|
||||||
@ -104,15 +104,15 @@ const ParamPositiveConditioning = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box position="relative">
|
<Box position="relative">
|
||||||
<IAIInformationalPopover
|
<FormControl>
|
||||||
placement="right"
|
<ParamEmbeddingPopover
|
||||||
details="paramPositiveConditioning"
|
isOpen={isOpen}
|
||||||
>
|
onClose={onClose}
|
||||||
<FormControl>
|
onSelect={handleSelectEmbedding}
|
||||||
<ParamEmbeddingPopover
|
>
|
||||||
isOpen={isOpen}
|
<IAIInformationalPopover
|
||||||
onClose={onClose}
|
feature="paramPositiveConditioning"
|
||||||
onSelect={handleSelectEmbedding}
|
placement="right"
|
||||||
>
|
>
|
||||||
<IAITextarea
|
<IAITextarea
|
||||||
id="prompt"
|
id="prompt"
|
||||||
@ -125,9 +125,9 @@ const ParamPositiveConditioning = () => {
|
|||||||
resize="vertical"
|
resize="vertical"
|
||||||
minH={32}
|
minH={32}
|
||||||
/>
|
/>
|
||||||
</ParamEmbeddingPopover>
|
</IAIInformationalPopover>
|
||||||
</FormControl>
|
</ParamEmbeddingPopover>
|
||||||
</IAIInformationalPopover>
|
</FormControl>
|
||||||
{!isOpen && isEmbeddingEnabled && (
|
{!isOpen && isEmbeddingEnabled && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
|
import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect';
|
||||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
import { setScheduler } from 'features/parameters/store/generationSlice';
|
import { setScheduler } from 'features/parameters/store/generationSlice';
|
||||||
@ -52,7 +52,7 @@ const ParamScheduler = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="paramScheduler">
|
<IAIInformationalPopover feature="paramScheduler">
|
||||||
<IAIMantineSearchableSelect
|
<IAIMantineSearchableSelect
|
||||||
label={t('parameters.scheduler')}
|
label={t('parameters.scheduler')}
|
||||||
value={scheduler}
|
value={scheduler}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { Box, Flex, Spacer, Text } from '@chakra-ui/react';
|
import { Flex, FormControl, FormLabel, Spacer } from '@chakra-ui/react';
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIIconButton from 'common/components/IAIIconButton';
|
import IAIIconButton from 'common/components/IAIIconButton';
|
||||||
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||||
import {
|
import {
|
||||||
setAspectRatio,
|
setAspectRatio,
|
||||||
@ -16,8 +18,6 @@ import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
|
|||||||
import ParamAspectRatio, { mappedAspectRatios } from './ParamAspectRatio';
|
import ParamAspectRatio, { mappedAspectRatios } from './ParamAspectRatio';
|
||||||
import ParamHeight from './ParamHeight';
|
import ParamHeight from './ParamHeight';
|
||||||
import ParamWidth from './ParamWidth';
|
import ParamWidth from './ParamWidth';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
|
||||||
|
|
||||||
const sizeOptsSelector = createSelector(
|
const sizeOptsSelector = createSelector(
|
||||||
[generationSelector, activeTabNameSelector],
|
[generationSelector, activeTabNameSelector],
|
||||||
@ -83,47 +83,35 @@ export default function ParamSize() {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex alignItems="center" gap={2}>
|
<IAIInformationalPopover feature="paramRatio">
|
||||||
<Box width="full">
|
<FormControl as={Flex} flexDir="row" alignItems="center" gap={2}>
|
||||||
<IAIInformationalPopover details="paramRatio">
|
<FormLabel>{t('parameters.aspectRatio')}</FormLabel>
|
||||||
<Text
|
<Spacer />
|
||||||
sx={{
|
<ParamAspectRatio />
|
||||||
fontSize: 'sm',
|
<IAIIconButton
|
||||||
color: 'base.700',
|
tooltip={t('ui.swapSizes')}
|
||||||
_dark: {
|
aria-label={t('ui.swapSizes')}
|
||||||
color: 'base.300',
|
size="sm"
|
||||||
},
|
icon={<MdOutlineSwapVert />}
|
||||||
}}
|
fontSize={20}
|
||||||
>
|
isDisabled={
|
||||||
{t('parameters.aspectRatio')}
|
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||||
</Text>
|
}
|
||||||
</IAIInformationalPopover>
|
onClick={handleToggleSize}
|
||||||
</Box>
|
/>
|
||||||
<Spacer />
|
<IAIIconButton
|
||||||
<ParamAspectRatio />
|
tooltip={t('ui.lockRatio')}
|
||||||
<IAIIconButton
|
aria-label={t('ui.lockRatio')}
|
||||||
tooltip={t('ui.swapSizes')}
|
size="sm"
|
||||||
aria-label={t('ui.swapSizes')}
|
icon={<FaLock />}
|
||||||
size="sm"
|
isChecked={shouldLockAspectRatio}
|
||||||
icon={<MdOutlineSwapVert />}
|
isDisabled={
|
||||||
fontSize={20}
|
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||||
isDisabled={
|
}
|
||||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
onClick={handleLockRatio}
|
||||||
}
|
/>
|
||||||
onClick={handleToggleSize}
|
</FormControl>
|
||||||
/>
|
</IAIInformationalPopover>
|
||||||
<IAIIconButton
|
|
||||||
tooltip={t('ui.lockRatio')}
|
|
||||||
aria-label={t('ui.lockRatio')}
|
|
||||||
size="sm"
|
|
||||||
icon={<FaLock />}
|
|
||||||
isChecked={shouldLockAspectRatio}
|
|
||||||
isDisabled={
|
|
||||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
|
||||||
}
|
|
||||||
onClick={handleLockRatio}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
<Flex gap={2} alignItems="center">
|
<Flex gap={2} alignItems="center">
|
||||||
<Flex gap={2} flexDirection="column" width="full">
|
<Flex gap={2} flexDirection="column" width="full">
|
||||||
<ParamWidth
|
<ParamWidth
|
||||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAINumberInput from 'common/components/IAINumberInput';
|
import IAINumberInput from 'common/components/IAINumberInput';
|
||||||
|
|
||||||
import IAISlider from 'common/components/IAISlider';
|
import IAISlider from 'common/components/IAISlider';
|
||||||
@ -57,7 +57,7 @@ const ParamSteps = () => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return shouldUseSliders ? (
|
return shouldUseSliders ? (
|
||||||
<IAIInformationalPopover details="paramSteps">
|
<IAIInformationalPopover feature="paramSteps">
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('parameters.steps')}
|
label={t('parameters.steps')}
|
||||||
min={min}
|
min={min}
|
||||||
@ -73,7 +73,7 @@ const ParamSteps = () => {
|
|||||||
/>
|
/>
|
||||||
</IAIInformationalPopover>
|
</IAIInformationalPopover>
|
||||||
) : (
|
) : (
|
||||||
<IAIInformationalPopover details="paramSteps">
|
<IAIInformationalPopover feature="paramSteps">
|
||||||
<IAINumberInput
|
<IAINumberInput
|
||||||
label={t('parameters.steps')}
|
label={t('parameters.steps')}
|
||||||
min={min}
|
min={min}
|
||||||
|
@ -7,7 +7,7 @@ import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import SubParametersWrapper from '../SubParametersWrapper';
|
import SubParametersWrapper from '../SubParametersWrapper';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[stateSelector],
|
[stateSelector],
|
||||||
@ -46,8 +46,8 @@ const ImageToImageStrength = () => {
|
|||||||
}, [dispatch, initial]);
|
}, [dispatch, initial]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubParametersWrapper>
|
<IAIInformationalPopover feature="paramDenoisingStrength">
|
||||||
<IAIInformationalPopover details="paramDenoisingStrength">
|
<SubParametersWrapper>
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={`${t('parameters.denoisingStrength')}`}
|
label={`${t('parameters.denoisingStrength')}`}
|
||||||
step={step}
|
step={step}
|
||||||
@ -62,8 +62,8 @@ const ImageToImageStrength = () => {
|
|||||||
withReset
|
withReset
|
||||||
sliderNumberInputProps={{ max: inputMax }}
|
sliderNumberInputProps={{ max: inputMax }}
|
||||||
/>
|
/>
|
||||||
</IAIInformationalPopover>
|
</SubParametersWrapper>
|
||||||
</SubParametersWrapper>
|
</IAIInformationalPopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
useGetOnnxModelsQuery,
|
useGetOnnxModelsQuery,
|
||||||
} from 'services/api/endpoints/models';
|
} from 'services/api/endpoints/models';
|
||||||
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
|
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
stateSelector,
|
stateSelector,
|
||||||
@ -120,7 +120,7 @@ const ParamMainModelSelect = () => {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Flex w="100%" alignItems="center" gap={3}>
|
<Flex w="100%" alignItems="center" gap={3}>
|
||||||
<IAIInformationalPopover details="paramModel" placement="bottom">
|
<IAIInformationalPopover feature="paramModel">
|
||||||
<IAIMantineSearchableSelect
|
<IAIMantineSearchableSelect
|
||||||
tooltip={selectedModel?.description}
|
tooltip={selectedModel?.description}
|
||||||
label={t('modelManager.model')}
|
label={t('modelManager.model')}
|
||||||
@ -136,7 +136,7 @@ const ParamMainModelSelect = () => {
|
|||||||
/>
|
/>
|
||||||
</IAIInformationalPopover>
|
</IAIInformationalPopover>
|
||||||
{isSyncModelEnabled && (
|
{isSyncModelEnabled && (
|
||||||
<Box mt={7}>
|
<Box mt={6}>
|
||||||
<SyncModelsButton iconMode />
|
<SyncModelsButton iconMode />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAISwitch from 'common/components/IAISwitch';
|
import IAISwitch from 'common/components/IAISwitch';
|
||||||
import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
|
import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
|
||||||
import { ChangeEvent, useCallback } from 'react';
|
import { ChangeEvent, useCallback } from 'react';
|
||||||
@ -20,7 +20,7 @@ export const ParamCpuNoiseToggle = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="noiseUseCPU">
|
<IAIInformationalPopover feature="noiseUseCPU">
|
||||||
<IAISwitch
|
<IAISwitch
|
||||||
label={t('parameters.useCpuNoise')}
|
label={t('parameters.useCpuNoise')}
|
||||||
isChecked={shouldUseCpuNoise}
|
isChecked={shouldUseCpuNoise}
|
||||||
|
@ -3,11 +3,11 @@ import { memo } from 'react';
|
|||||||
import ParamSeed from './ParamSeed';
|
import ParamSeed from './ParamSeed';
|
||||||
import ParamSeedShuffle from './ParamSeedShuffle';
|
import ParamSeedShuffle from './ParamSeedShuffle';
|
||||||
import ParamSeedRandomize from './ParamSeedRandomize';
|
import ParamSeedRandomize from './ParamSeedRandomize';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
const ParamSeedFull = () => {
|
const ParamSeedFull = () => {
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="paramSeed">
|
<IAIInformationalPopover feature="paramSeed">
|
||||||
<Flex sx={{ gap: 3, alignItems: 'flex-end' }}>
|
<Flex sx={{ gap: 3, alignItems: 'flex-end' }}>
|
||||||
<ParamSeed />
|
<ParamSeed />
|
||||||
<ParamSeedShuffle />
|
<ParamSeedShuffle />
|
||||||
|
@ -1,40 +1,28 @@
|
|||||||
import { Flex, Text } from '@chakra-ui/react';
|
import { Flex, Text, forwardRef } from '@chakra-ui/react';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
|
||||||
import { ReactNode, memo } from 'react';
|
import { ReactNode, memo } from 'react';
|
||||||
|
|
||||||
type SubParameterWrapperProps = {
|
type SubParameterWrapperProps = {
|
||||||
children: ReactNode | ReactNode[];
|
children: ReactNode;
|
||||||
label?: string;
|
label?: string;
|
||||||
headerInfoPopover?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubParametersWrapper = (props: SubParameterWrapperProps) => (
|
const SubParametersWrapper = forwardRef(
|
||||||
<Flex
|
(props: SubParameterWrapperProps, ref) => (
|
||||||
sx={{
|
<Flex
|
||||||
flexDir: 'column',
|
ref={ref}
|
||||||
gap: 2,
|
sx={{
|
||||||
bg: 'base.100',
|
flexDir: 'column',
|
||||||
px: 4,
|
gap: 2,
|
||||||
pt: 2,
|
bg: 'base.100',
|
||||||
pb: 4,
|
px: 4,
|
||||||
borderRadius: 'base',
|
pt: 2,
|
||||||
_dark: {
|
pb: 4,
|
||||||
bg: 'base.750',
|
borderRadius: 'base',
|
||||||
},
|
_dark: {
|
||||||
}}
|
bg: 'base.750',
|
||||||
>
|
},
|
||||||
{props.headerInfoPopover && props.label && (
|
}}
|
||||||
<IAIInformationalPopover details={props.headerInfoPopover}>
|
>
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="bold"
|
|
||||||
sx={{ color: 'base.600', _dark: { color: 'base.300' } }}
|
|
||||||
>
|
|
||||||
{props.label}
|
|
||||||
</Text>
|
|
||||||
</IAIInformationalPopover>
|
|
||||||
)}
|
|
||||||
{!props.headerInfoPopover && props.label && (
|
|
||||||
<Text
|
<Text
|
||||||
fontSize="sm"
|
fontSize="sm"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
@ -42,9 +30,9 @@ const SubParametersWrapper = (props: SubParameterWrapperProps) => (
|
|||||||
>
|
>
|
||||||
{props.label}
|
{props.label}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
{props.children}
|
||||||
{props.children}
|
</Flex>
|
||||||
</Flex>
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
SubParametersWrapper.displayName = 'SubSettingsWrapper';
|
SubParametersWrapper.displayName = 'SubSettingsWrapper';
|
||||||
|
@ -15,7 +15,7 @@ import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectI
|
|||||||
import { vaeSelected } from 'features/parameters/store/generationSlice';
|
import { vaeSelected } from 'features/parameters/store/generationSlice';
|
||||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||||
import { modelIdToVAEModelParam } from 'features/parameters/util/modelIdToVAEModelParam';
|
import { modelIdToVAEModelParam } from 'features/parameters/util/modelIdToVAEModelParam';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
stateSelector,
|
stateSelector,
|
||||||
@ -94,7 +94,7 @@ const ParamVAEModelSelect = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="paramVAE">
|
<IAIInformationalPopover feature="paramVAE">
|
||||||
<IAIMantineSearchableSelect
|
<IAIMantineSearchableSelect
|
||||||
itemComponent={IAIMantineSelectItemWithTooltip}
|
itemComponent={IAIMantineSelectItemWithTooltip}
|
||||||
tooltip={selectedVaeModel?.description}
|
tooltip={selectedVaeModel?.description}
|
||||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
|||||||
import { stateSelector } from 'app/store/store';
|
import { stateSelector } from 'app/store/store';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||||
import { vaePrecisionChanged } from 'features/parameters/store/generationSlice';
|
import { vaePrecisionChanged } from 'features/parameters/store/generationSlice';
|
||||||
import { PrecisionParam } from 'features/parameters/types/parameterSchemas';
|
import { PrecisionParam } from 'features/parameters/types/parameterSchemas';
|
||||||
@ -35,7 +35,7 @@ const ParamVAEModelSelect = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IAIInformationalPopover details="paramVAEPrecision">
|
<IAIInformationalPopover feature="paramVAEPrecision">
|
||||||
<IAIMantineSelect
|
<IAIMantineSelect
|
||||||
label="VAE Precision"
|
label="VAE Precision"
|
||||||
value={vaePrecision}
|
value={vaePrecision}
|
||||||
|
@ -7,7 +7,7 @@ import SubParametersWrapper from 'features/parameters/components/Parameters/SubP
|
|||||||
import { memo, useCallback } from 'react';
|
import { memo, useCallback } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { setSDXLImg2ImgDenoisingStrength } from '../store/sdxlSlice';
|
import { setSDXLImg2ImgDenoisingStrength } from '../store/sdxlSlice';
|
||||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||||
|
|
||||||
const selector = createSelector(
|
const selector = createSelector(
|
||||||
[stateSelector],
|
[stateSelector],
|
||||||
@ -36,8 +36,8 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubParametersWrapper>
|
<IAIInformationalPopover feature="paramDenoisingStrength">
|
||||||
<IAIInformationalPopover details="paramDenoisingStrength">
|
<SubParametersWrapper>
|
||||||
<IAISlider
|
<IAISlider
|
||||||
label={t('sdxl.denoisingStrength')}
|
label={t('sdxl.denoisingStrength')}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
@ -51,8 +51,8 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
|
|||||||
withSliderMarks
|
withSliderMarks
|
||||||
withReset
|
withReset
|
||||||
/>
|
/>
|
||||||
</IAIInformationalPopover>
|
</SubParametersWrapper>
|
||||||
</SubParametersWrapper>
|
</IAIInformationalPopover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
155
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
155
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
File diff suppressed because one or more lines are too long
@ -152,6 +152,8 @@ export type SaveImageInvocation = s['SaveImageInvocation'];
|
|||||||
export type ControlNetInvocation = s['ControlNetInvocation'];
|
export type ControlNetInvocation = s['ControlNetInvocation'];
|
||||||
export type IPAdapterInvocation = s['IPAdapterInvocation'];
|
export type IPAdapterInvocation = s['IPAdapterInvocation'];
|
||||||
export type CannyImageProcessorInvocation = s['CannyImageProcessorInvocation'];
|
export type CannyImageProcessorInvocation = s['CannyImageProcessorInvocation'];
|
||||||
|
export type ColorMapImageProcessorInvocation =
|
||||||
|
s['ColorMapImageProcessorInvocation'];
|
||||||
export type ContentShuffleImageProcessorInvocation =
|
export type ContentShuffleImageProcessorInvocation =
|
||||||
s['ContentShuffleImageProcessorInvocation'];
|
s['ContentShuffleImageProcessorInvocation'];
|
||||||
export type HedImageProcessorInvocation = s['HedImageProcessorInvocation'];
|
export type HedImageProcessorInvocation = s['HedImageProcessorInvocation'];
|
||||||
|
@ -31,14 +31,14 @@ const invokeAIContent = defineStyle((props) => {
|
|||||||
|
|
||||||
const informationalContent = defineStyle((props) => {
|
const informationalContent = defineStyle((props) => {
|
||||||
return {
|
return {
|
||||||
[$arrowBg.variable]: mode('colors.base.100', 'colors.base.600')(props),
|
[$arrowBg.variable]: mode('colors.base.100', 'colors.base.700')(props),
|
||||||
[$popperBg.variable]: mode('colors.base.100', 'colors.base.600')(props),
|
[$popperBg.variable]: mode('colors.base.100', 'colors.base.700')(props),
|
||||||
[$arrowShadowColor.variable]: mode(
|
[$arrowShadowColor.variable]: mode(
|
||||||
'colors.base.400',
|
'colors.base.400',
|
||||||
'colors.base.400'
|
'colors.base.400'
|
||||||
)(props),
|
)(props),
|
||||||
p: 4,
|
p: 4,
|
||||||
bg: mode('base.100', 'base.600')(props),
|
bg: mode('base.100', 'base.700')(props),
|
||||||
border: 'none',
|
border: 'none',
|
||||||
shadow: 'dark-lg',
|
shadow: 'dark-lg',
|
||||||
};
|
};
|
||||||
@ -46,6 +46,7 @@ const informationalContent = defineStyle((props) => {
|
|||||||
|
|
||||||
const invokeAI = definePartsStyle((props) => ({
|
const invokeAI = definePartsStyle((props) => ({
|
||||||
content: invokeAIContent(props),
|
content: invokeAIContent(props),
|
||||||
|
body: { padding: 0 },
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const informational = definePartsStyle((props) => ({
|
const informational = definePartsStyle((props) => ({
|
||||||
|
@ -76,6 +76,7 @@ export const theme: ThemeOverride = {
|
|||||||
direction: 'ltr',
|
direction: 'ltr',
|
||||||
fonts: {
|
fonts: {
|
||||||
body: `'Inter Variable', sans-serif`,
|
body: `'Inter Variable', sans-serif`,
|
||||||
|
heading: `'Inter Variable', sans-serif`,
|
||||||
},
|
},
|
||||||
shadows: {
|
shadows: {
|
||||||
light: {
|
light: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user