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_2 = "The second number"
|
||||
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):
|
||||
@ -173,6 +176,7 @@ class UIType(str, Enum):
|
||||
WorkflowField = "WorkflowField"
|
||||
IsIntermediate = "IsIntermediate"
|
||||
MetadataField = "MetadataField"
|
||||
BoardField = "BoardField"
|
||||
# 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[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] 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]:
|
||||
|
@ -559,3 +559,33 @@ class SamDetectorReproducibleColors(SamDetector):
|
||||
img[:, :] = ann_color
|
||||
final_img.paste(Image.fromarray(img, mode="RGB"), (0, 0), Image.fromarray(np.uint8(m * 255)))
|
||||
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 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.safety_checker import SafetyChecker
|
||||
|
||||
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")
|
||||
@ -972,13 +972,14 @@ class ImageChannelMultiplyInvocation(BaseInvocation):
|
||||
title="Save Image",
|
||||
tags=["primitives", "image"],
|
||||
category="primitives",
|
||||
version="1.0.0",
|
||||
version="1.0.1",
|
||||
use_cache=False,
|
||||
)
|
||||
class SaveImageInvocation(BaseInvocation):
|
||||
"""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(
|
||||
default=None,
|
||||
description=FieldDescriptions.core_metadata,
|
||||
@ -992,6 +993,7 @@ class SaveImageInvocation(BaseInvocation):
|
||||
image=image,
|
||||
image_origin=ResourceOrigin.INTERNAL,
|
||||
image_category=ImageCategory.GENERAL,
|
||||
board_id=self.board.board_id if self.board else None,
|
||||
node_id=self.id,
|
||||
session_id=context.graph_execution_state_id,
|
||||
is_intermediate=self.is_intermediate,
|
||||
|
@ -226,6 +226,12 @@ class ImageField(BaseModel):
|
||||
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")
|
||||
class ImageOutput(BaseInvocationOutput):
|
||||
"""Base class for nodes that output a single image"""
|
||||
|
@ -8,97 +8,97 @@ from invokeai.app.services.invoker import Invoker
|
||||
|
||||
|
||||
class MemoryInvocationCache(InvocationCacheBase):
|
||||
__cache: dict[Union[int, str], tuple[BaseInvocationOutput, str]]
|
||||
__max_cache_size: int
|
||||
__disabled: bool
|
||||
__hits: int
|
||||
__misses: int
|
||||
__cache_ids: Queue
|
||||
__invoker: Invoker
|
||||
_cache: dict[Union[int, str], tuple[BaseInvocationOutput, str]]
|
||||
_max_cache_size: int
|
||||
_disabled: bool
|
||||
_hits: int
|
||||
_misses: int
|
||||
_cache_ids: Queue
|
||||
_invoker: Invoker
|
||||
|
||||
def __init__(self, max_cache_size: int = 0) -> None:
|
||||
self.__cache = dict()
|
||||
self.__max_cache_size = max_cache_size
|
||||
self.__disabled = False
|
||||
self.__hits = 0
|
||||
self.__misses = 0
|
||||
self.__cache_ids = Queue()
|
||||
self._cache = dict()
|
||||
self._max_cache_size = max_cache_size
|
||||
self._disabled = False
|
||||
self._hits = 0
|
||||
self._misses = 0
|
||||
self._cache_ids = Queue()
|
||||
|
||||
def start(self, invoker: Invoker) -> None:
|
||||
self.__invoker = invoker
|
||||
if self.__max_cache_size == 0:
|
||||
self._invoker = invoker
|
||||
if self._max_cache_size == 0:
|
||||
return
|
||||
self.__invoker.services.images.on_deleted(self._delete_by_match)
|
||||
self.__invoker.services.latents.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)
|
||||
|
||||
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
|
||||
|
||||
item = self.__cache.get(key, None)
|
||||
item = self._cache.get(key, None)
|
||||
if item is not None:
|
||||
self.__hits += 1
|
||||
self._hits += 1
|
||||
return item[0]
|
||||
self.__misses += 1
|
||||
self._misses += 1
|
||||
|
||||
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
|
||||
|
||||
if key not in self.__cache:
|
||||
self.__cache[key] = (invocation_output, invocation_output.json())
|
||||
self.__cache_ids.put(key)
|
||||
if self.__cache_ids.qsize() > self.__max_cache_size:
|
||||
if key not in self._cache:
|
||||
self._cache[key] = (invocation_output, invocation_output.json())
|
||||
self._cache_ids.put(key)
|
||||
if self._cache_ids.qsize() > self._max_cache_size:
|
||||
try:
|
||||
self.__cache.pop(self.__cache_ids.get())
|
||||
self._cache.pop(self._cache_ids.get())
|
||||
except KeyError:
|
||||
# this means the cache_ids are somehow out of sync w/ the cache
|
||||
pass
|
||||
|
||||
def delete(self, key: Union[int, str]) -> None:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
if self._max_cache_size == 0:
|
||||
return
|
||||
|
||||
if key in self.__cache:
|
||||
del self.__cache[key]
|
||||
if key in self._cache:
|
||||
del self._cache[key]
|
||||
|
||||
def clear(self, *args, **kwargs) -> None:
|
||||
if self.__max_cache_size == 0 or self.__disabled:
|
||||
if self._max_cache_size == 0:
|
||||
return
|
||||
|
||||
self.__cache.clear()
|
||||
self.__cache_ids = Queue()
|
||||
self.__misses = 0
|
||||
self.__hits = 0
|
||||
self._cache.clear()
|
||||
self._cache_ids = Queue()
|
||||
self._misses = 0
|
||||
self._hits = 0
|
||||
|
||||
def create_key(self, invocation: BaseInvocation) -> int:
|
||||
return hash(invocation.json(exclude={"id"}))
|
||||
|
||||
def disable(self) -> None:
|
||||
if self.__max_cache_size == 0:
|
||||
if self._max_cache_size == 0:
|
||||
return
|
||||
self.__disabled = True
|
||||
self._disabled = True
|
||||
|
||||
def enable(self) -> None:
|
||||
if self.__max_cache_size == 0:
|
||||
if self._max_cache_size == 0:
|
||||
return
|
||||
self.__disabled = False
|
||||
self._disabled = False
|
||||
|
||||
def get_status(self) -> InvocationCacheStatus:
|
||||
return InvocationCacheStatus(
|
||||
hits=self.__hits,
|
||||
misses=self.__misses,
|
||||
enabled=not self.__disabled and self.__max_cache_size > 0,
|
||||
size=len(self.__cache),
|
||||
max_size=self.__max_cache_size,
|
||||
hits=self._hits,
|
||||
misses=self._misses,
|
||||
enabled=not self._disabled and self._max_cache_size > 0,
|
||||
size=len(self._cache),
|
||||
max_size=self._max_cache_size,
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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]:
|
||||
keys_to_delete.add(key)
|
||||
|
||||
@ -108,4 +108,4 @@ class MemoryInvocationCache(InvocationCacheBase):
|
||||
for key in keys_to_delete:
|
||||
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",
|
||||
"loading": "Loading",
|
||||
"loadingInvokeAI": "Loading Invoke AI",
|
||||
"learnMore": "Learn More",
|
||||
"modelManager": "Model Manager",
|
||||
"nodeEditor": "Node Editor",
|
||||
"nodes": "Workflow Editor",
|
||||
@ -135,6 +136,8 @@
|
||||
"bgth": "bg_th",
|
||||
"canny": "Canny",
|
||||
"cannyDescription": "Canny edge detection",
|
||||
"colorMap": "Color",
|
||||
"colorMapDescription": "Generates a color map from the image",
|
||||
"coarse": "Coarse",
|
||||
"contentShuffle": "Content Shuffle",
|
||||
"contentShuffleDescription": "Shuffles the content in an image",
|
||||
@ -158,6 +161,7 @@
|
||||
"hideAdvanced": "Hide Advanced",
|
||||
"highThreshold": "High Threshold",
|
||||
"imageResolution": "Image Resolution",
|
||||
"colorMapTileSize": "Tile Size",
|
||||
"importImageFromCanvas": "Import Image From Canvas",
|
||||
"importMaskFromCanvas": "Import Mask From Canvas",
|
||||
"incompatibleBaseModel": "Incompatible base model:",
|
||||
@ -701,6 +705,8 @@
|
||||
"addNodeToolTip": "Add Node (Shift+A, Space)",
|
||||
"animatedEdges": "Animated Edges",
|
||||
"animatedEdgesHelp": "Animate selected edges and edges connected to selected nodes",
|
||||
"boardField": "Board",
|
||||
"boardFieldDescription": "A gallery board",
|
||||
"boolean": "Booleans",
|
||||
"booleanCollection": "Boolean Collection",
|
||||
"booleanCollectionDescription": "A collection of booleans.",
|
||||
@ -888,7 +894,7 @@
|
||||
"zoomOutNodes": "Zoom Out"
|
||||
},
|
||||
"parameters": {
|
||||
"aspectRatio": "Ratio",
|
||||
"aspectRatio": "Aspect Ratio",
|
||||
"boundingBoxHeader": "Bounding Box",
|
||||
"boundingBoxHeight": "Bounding Box Height",
|
||||
"boundingBoxWidth": "Bounding Box Width",
|
||||
@ -1020,8 +1026,8 @@
|
||||
"label": "Seed Behaviour",
|
||||
"perIterationLabel": "Seed per Iteration",
|
||||
"perIterationDesc": "Use a different seed for each iteration",
|
||||
"perPromptLabel": "Seed per Prompt",
|
||||
"perPromptDesc": "Use a different seed for each prompt"
|
||||
"perPromptLabel": "Seed per Image",
|
||||
"perPromptDesc": "Use a different seed for each image"
|
||||
}
|
||||
},
|
||||
"sdxl": {
|
||||
@ -1173,131 +1179,205 @@
|
||||
"popovers": {
|
||||
"clipSkip": {
|
||||
"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."
|
||||
},
|
||||
"compositingBlur": {
|
||||
"heading": "Blur",
|
||||
"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."
|
||||
"paragraphs": [
|
||||
"Choose how many layers of the CLIP model to skip.",
|
||||
"Some models work better with certain CLIP Skip settings.",
|
||||
"A higher value typically results in a less detailed image."
|
||||
]
|
||||
},
|
||||
"paramNegativeConditioning": {
|
||||
"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": {
|
||||
"heading": "Positive Prompt",
|
||||
"paragraph": "Guides the generation process. You may use any words or phrases. Supports Compel and Dynamic Prompts syntaxes and embeddings."
|
||||
},
|
||||
"paramRatio": {
|
||||
"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."
|
||||
"paragraphs": [
|
||||
"Guides the generation process. You may use any words or phrases.",
|
||||
"Compel and Dynamic Prompts syntaxes and embeddings."
|
||||
]
|
||||
},
|
||||
"paramScheduler": {
|
||||
"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": {
|
||||
"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": {
|
||||
"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": {
|
||||
"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": {
|
||||
"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": {
|
||||
"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": {
|
||||
|
@ -17,7 +17,8 @@ import {
|
||||
} from 'services/events/actions';
|
||||
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 = () => {
|
||||
startAppListening({
|
||||
@ -37,6 +38,7 @@ export const addInvocationCompleteEventListener = () => {
|
||||
const { image_name } = result.image;
|
||||
const { canvas, gallery } = getState();
|
||||
|
||||
// This populates the `getImageDTO` cache
|
||||
const imageDTO = await dispatch(
|
||||
imagesApi.endpoints.getImageDTO.initiate(image_name)
|
||||
).unwrap();
|
||||
@ -52,54 +54,36 @@ export const addInvocationCompleteEventListener = () => {
|
||||
if (!imageDTO.is_intermediate) {
|
||||
/**
|
||||
* Cache updates for when an image result is received
|
||||
* - *add* to getImageDTO
|
||||
* - 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
|
||||
* - add it to the no_board/images
|
||||
*/
|
||||
|
||||
const { autoAddBoardId } = gallery;
|
||||
if (autoAddBoardId && autoAddBoardId !== 'none') {
|
||||
dispatch(
|
||||
imagesApi.endpoints.addImageToBoard.initiate({
|
||||
board_id: autoAddBoardId,
|
||||
imageDTO,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
dispatch(
|
||||
imagesApi.util.updateQueryData(
|
||||
'listImages',
|
||||
{
|
||||
board_id: 'none',
|
||||
categories: IMAGE_CATEGORIES,
|
||||
},
|
||||
(draft) => {
|
||||
imagesAdapter.addOne(draft, imageDTO);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
dispatch(
|
||||
imagesApi.util.updateQueryData(
|
||||
'listImages',
|
||||
{
|
||||
board_id: imageDTO.board_id ?? 'none',
|
||||
categories: IMAGE_CATEGORIES,
|
||||
},
|
||||
(draft) => {
|
||||
imagesAdapter.addOne(draft, imageDTO);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
dispatch(
|
||||
imagesApi.util.invalidateTags([
|
||||
{ type: 'BoardImagesTotal', id: autoAddBoardId },
|
||||
{ type: 'BoardAssetsTotal', id: autoAddBoardId },
|
||||
{ type: 'BoardImagesTotal', id: imageDTO.board_id },
|
||||
{ type: 'BoardAssetsTotal', id: imageDTO.board_id },
|
||||
])
|
||||
);
|
||||
|
||||
const { selectedBoardId, shouldAutoSwitch } = gallery;
|
||||
const { shouldAutoSwitch } = gallery;
|
||||
|
||||
// If auto-switch is enabled, select the new image
|
||||
if (shouldAutoSwitch) {
|
||||
// if auto-add is enabled, switch the board as the image comes in
|
||||
if (autoAddBoardId && autoAddBoardId !== selectedBoardId) {
|
||||
dispatch(boardIdSelected(autoAddBoardId));
|
||||
dispatch(galleryViewChanged('images'));
|
||||
} else if (!autoAddBoardId) {
|
||||
dispatch(galleryViewChanged('images'));
|
||||
}
|
||||
dispatch(galleryViewChanged('images'));
|
||||
dispatch(boardIdSelected(imageDTO.board_id ?? 'none'));
|
||||
dispatch(imageSelected(imageDTO));
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,14 @@ export const addUpscaleRequestedListener = () => {
|
||||
const log = logger('session');
|
||||
|
||||
const { image_name } = action.payload;
|
||||
const { esrganModelName } = getState().postprocessing;
|
||||
const state = getState();
|
||||
const { esrganModelName } = state.postprocessing;
|
||||
const { autoAddBoardId } = state.gallery;
|
||||
|
||||
const graph = buildAdHocUpscaleGraph({
|
||||
image_name,
|
||||
esrganModelName,
|
||||
autoAddBoardId,
|
||||
});
|
||||
|
||||
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 (
|
||||
<Tooltip label={tooltip} placement="top" hasArrow isOpen={true}>
|
||||
<MultiSelect
|
||||
label={
|
||||
label ? (
|
||||
<FormControl ref={ref} isDisabled={disabled}>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
</FormControl>
|
||||
) : undefined
|
||||
}
|
||||
ref={inputRef}
|
||||
disabled={disabled}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
searchable={searchable}
|
||||
maxDropdownHeight={300}
|
||||
styles={styles}
|
||||
{...rest}
|
||||
/>
|
||||
<FormControl ref={ref} isDisabled={disabled}>
|
||||
{label && <FormLabel>{label}</FormLabel>}
|
||||
<MultiSelect
|
||||
ref={inputRef}
|
||||
disabled={disabled}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
searchable={searchable}
|
||||
maxDropdownHeight={300}
|
||||
styles={styles}
|
||||
{...rest}
|
||||
/>
|
||||
</FormControl>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
@ -70,26 +70,23 @@ const IAIMantineSearchableSelect = forwardRef((props: IAISelectProps, ref) => {
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} placement="top" hasArrow>
|
||||
<Select
|
||||
ref={inputRef}
|
||||
label={
|
||||
label ? (
|
||||
<FormControl ref={ref} isDisabled={disabled}>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
</FormControl>
|
||||
) : undefined
|
||||
}
|
||||
disabled={disabled}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
searchable={searchable}
|
||||
maxDropdownHeight={300}
|
||||
styles={styles}
|
||||
{...rest}
|
||||
/>
|
||||
<FormControl ref={ref} isDisabled={disabled}>
|
||||
{label && <FormLabel>{label}</FormLabel>}
|
||||
<Select
|
||||
ref={inputRef}
|
||||
withinPortal
|
||||
disabled={disabled}
|
||||
searchValue={searchValue}
|
||||
onSearchChange={setSearchValue}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
onKeyUp={handleKeyUp}
|
||||
searchable={searchable}
|
||||
maxDropdownHeight={300}
|
||||
styles={styles}
|
||||
{...rest}
|
||||
/>
|
||||
</FormControl>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
@ -22,19 +22,10 @@ const IAIMantineSelect = forwardRef((props: IAISelectProps, ref) => {
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} placement="top" hasArrow>
|
||||
<Select
|
||||
label={
|
||||
label ? (
|
||||
<FormControl ref={ref} isRequired={required} isDisabled={disabled}>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
</FormControl>
|
||||
) : undefined
|
||||
}
|
||||
disabled={disabled}
|
||||
ref={inputRef}
|
||||
styles={styles}
|
||||
{...rest}
|
||||
/>
|
||||
<FormControl ref={ref} isRequired={required} isDisabled={disabled}>
|
||||
<FormLabel>{label}</FormLabel>
|
||||
<Select disabled={disabled} ref={inputRef} styles={styles} {...rest} />
|
||||
</FormControl>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
import { ControlNetConfig } from '../store/controlNetSlice';
|
||||
import CannyProcessor from './processors/CannyProcessor';
|
||||
import ColorMapProcessor from './processors/ColorMapProcessor';
|
||||
import ContentShuffleProcessor from './processors/ContentShuffleProcessor';
|
||||
import HedProcessor from './processors/HedProcessor';
|
||||
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') {
|
||||
return (
|
||||
<HedProcessor
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
Tooltip,
|
||||
} from '@chakra-ui/react';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import {
|
||||
ControlNetConfig,
|
||||
controlNetBeginStepPctChanged,
|
||||
@ -50,7 +50,7 @@ const ParamControlNetBeginEnd = (props: Props) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="controlNetBeginEnd">
|
||||
<IAIInformationalPopover feature="controlNetBeginEnd">
|
||||
<FormControl isDisabled={!isEnabled}>
|
||||
<FormLabel>{t('controlnet.beginEndStepPercent')}</FormLabel>
|
||||
<HStack w="100%" gap={2} alignItems="center">
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
ControlModes,
|
||||
@ -35,7 +35,7 @@ export default function ParamControlNetControlMode(
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="controlNetControlMode">
|
||||
<IAIInformationalPopover feature="controlNetControlMode">
|
||||
<IAIMantineSelect
|
||||
disabled={!isEnabled}
|
||||
label={t('controlnet.controlMode')}
|
||||
|
@ -3,7 +3,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { isControlNetEnabledToggled } from 'features/controlNet/store/controlNetSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
@ -28,7 +28,7 @@ const ParamControlNetFeatureToggle = () => {
|
||||
|
||||
return (
|
||||
<Box width="100%">
|
||||
<IAIInformationalPopover details="controlNetToggle">
|
||||
<IAIInformationalPopover feature="controlNet">
|
||||
<IAISwitch
|
||||
label="Enable ControlNet"
|
||||
isChecked={isEnabled}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
ControlNetConfig,
|
||||
@ -34,7 +34,7 @@ export default function ParamControlNetResizeMode(
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="controlNetResizeMode">
|
||||
<IAIInformationalPopover feature="controlNetResizeMode">
|
||||
<IAIMantineSelect
|
||||
disabled={!isEnabled}
|
||||
label={t('controlnet.resizeMode')}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 {
|
||||
ControlNetConfig,
|
||||
@ -24,7 +24,7 @@ const ParamControlNetWeight = (props: ParamControlNetWeightProps) => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="controlNetWeight">
|
||||
<IAIInformationalPopover feature="controlNetWeight">
|
||||
<IAISlider
|
||||
isDisabled={!isEnabled}
|
||||
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;
|
||||
|
||||
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 {
|
||||
ControlNetProcessorType,
|
||||
RequiredControlNetProcessorNode,
|
||||
} from './types';
|
||||
import i18n from 'i18next';
|
||||
|
||||
type ControlNetProcessorsDict = Record<
|
||||
ControlNetProcessorType,
|
||||
@ -50,6 +50,20 @@ export const CONTROLNET_PROCESSORS: ControlNetProcessorsDict = {
|
||||
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: {
|
||||
type: 'content_shuffle_image_processor',
|
||||
get label() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { isObject } from 'lodash-es';
|
||||
import {
|
||||
CannyImageProcessorInvocation,
|
||||
ColorMapImageProcessorInvocation,
|
||||
ContentShuffleImageProcessorInvocation,
|
||||
HedImageProcessorInvocation,
|
||||
LineartAnimeImageProcessorInvocation,
|
||||
@ -20,6 +21,7 @@ import { O } from 'ts-toolbelt';
|
||||
*/
|
||||
export type ControlNetProcessorNode =
|
||||
| CannyImageProcessorInvocation
|
||||
| ColorMapImageProcessorInvocation
|
||||
| ContentShuffleImageProcessorInvocation
|
||||
| HedImageProcessorInvocation
|
||||
| LineartAnimeImageProcessorInvocation
|
||||
@ -47,6 +49,14 @@ export type RequiredCannyImageProcessorInvocation = O.Required<
|
||||
'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
|
||||
*/
|
||||
@ -140,6 +150,7 @@ export type RequiredZoeDepthImageProcessorInvocation = O.Required<
|
||||
*/
|
||||
export type RequiredControlNetProcessorNode = O.Required<
|
||||
| RequiredCannyImageProcessorInvocation
|
||||
| RequiredColorMapImageProcessorInvocation
|
||||
| RequiredContentShuffleImageProcessorInvocation
|
||||
| RequiredHedImageProcessorInvocation
|
||||
| RequiredLineartAnimeImageProcessorInvocation
|
||||
@ -166,6 +177,22 @@ export const isCannyImageProcessorInvocation = (
|
||||
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
|
||||
*/
|
||||
|
@ -43,8 +43,8 @@ const ParamDynamicPromptsCollapse = () => {
|
||||
activeLabel={activeLabel}
|
||||
>
|
||||
<Flex sx={{ gap: 2, flexDir: 'column' }}>
|
||||
<ParamDynamicPromptsSeedBehaviour />
|
||||
<ParamDynamicPromptsPreview />
|
||||
<ParamDynamicPromptsSeedBehaviour />
|
||||
<ParamDynamicPromptsMaxPrompts />
|
||||
</Flex>
|
||||
</IAICollapse>
|
||||
|
@ -4,9 +4,8 @@ import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAISwitch from 'common/components/IAISwitch';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { combinatorialToggled } from '../store/dynamicPromptsSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import { combinatorialToggled } from '../store/dynamicPromptsSlice';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
@ -28,13 +27,11 @@ const ParamDynamicPromptsCombinatorial = () => {
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="dynamicPromptsCombinatorial">
|
||||
<IAISwitch
|
||||
label={t('dynamicPrompts.combinatorial')}
|
||||
isChecked={combinatorial}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
<IAISwitch
|
||||
label={t('dynamicPrompts.combinatorial')}
|
||||
isChecked={combinatorial}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
maxPromptsReset,
|
||||
} from '../store/dynamicPromptsSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
@ -46,19 +47,21 @@ const ParamDynamicPromptsMaxPrompts = () => {
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<IAISlider
|
||||
label={t('dynamicPrompts.maxPrompts')}
|
||||
isDisabled={isDisabled}
|
||||
min={min}
|
||||
max={sliderMax}
|
||||
value={maxPrompts}
|
||||
onChange={handleChange}
|
||||
sliderNumberInputProps={{ max: inputMax }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
withReset
|
||||
handleReset={handleReset}
|
||||
/>
|
||||
<IAIInformationalPopover feature="dynamicPromptsMaxPrompts">
|
||||
<IAISlider
|
||||
label={t('dynamicPrompts.maxPrompts')}
|
||||
isDisabled={isDisabled}
|
||||
min={min}
|
||||
max={sliderMax}
|
||||
value={maxPrompts}
|
||||
onChange={handleChange}
|
||||
sliderNumberInputProps={{ max: inputMax }}
|
||||
withSliderMarks
|
||||
withInput
|
||||
withReset
|
||||
handleReset={handleReset}
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@ import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import ScrollableContent from 'features/nodes/components/sidePanel/ScrollableContent';
|
||||
import { memo } from 'react';
|
||||
import { FaCircleExclamation } from 'react-icons/fa6';
|
||||
@ -42,58 +43,73 @@ const ParamDynamicPromptsPreview = () => {
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<Flex
|
||||
w="full"
|
||||
h="full"
|
||||
layerStyle="second"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
p={8}
|
||||
>
|
||||
<IAINoContentFallback
|
||||
icon={FaCircleExclamation}
|
||||
label="Problem generating prompts"
|
||||
/>
|
||||
</Flex>
|
||||
<IAIInformationalPopover feature="dynamicPrompts">
|
||||
<Flex
|
||||
w="full"
|
||||
h="full"
|
||||
layerStyle="second"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
p={8}
|
||||
>
|
||||
<IAINoContentFallback
|
||||
icon={FaCircleExclamation}
|
||||
label="Problem generating prompts"
|
||||
/>
|
||||
</Flex>
|
||||
</IAIInformationalPopover>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl isInvalid={Boolean(parsingError)}>
|
||||
<FormLabel whiteSpace="nowrap" overflow="hidden" textOverflow="ellipsis">
|
||||
Prompts Preview ({prompts.length}){parsingError && ` - ${parsingError}`}
|
||||
</FormLabel>
|
||||
<Flex h={64} pos="relative" layerStyle="third" borderRadius="base" p={2}>
|
||||
<ScrollableContent>
|
||||
<OrderedList stylePosition="inside" ms={0}>
|
||||
{prompts.map((prompt, i) => (
|
||||
<ListItem
|
||||
fontSize="sm"
|
||||
key={`${prompt}.${i}`}
|
||||
sx={listItemStyles}
|
||||
>
|
||||
<Text as="span">{prompt}</Text>
|
||||
</ListItem>
|
||||
))}
|
||||
</OrderedList>
|
||||
</ScrollableContent>
|
||||
{isLoading && (
|
||||
<Flex
|
||||
pos="absolute"
|
||||
w="full"
|
||||
h="full"
|
||||
top={0}
|
||||
insetInlineStart={0}
|
||||
layerStyle="second"
|
||||
opacity={0.7}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Spinner />
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
</FormControl>
|
||||
<IAIInformationalPopover feature="dynamicPrompts">
|
||||
<FormControl isInvalid={Boolean(parsingError)}>
|
||||
<FormLabel
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
>
|
||||
Prompts Preview ({prompts.length})
|
||||
{parsingError && ` - ${parsingError}`}
|
||||
</FormLabel>
|
||||
<Flex
|
||||
h={64}
|
||||
pos="relative"
|
||||
layerStyle="third"
|
||||
borderRadius="base"
|
||||
p={2}
|
||||
>
|
||||
<ScrollableContent>
|
||||
<OrderedList stylePosition="inside" ms={0}>
|
||||
{prompts.map((prompt, i) => (
|
||||
<ListItem
|
||||
fontSize="sm"
|
||||
key={`${prompt}.${i}`}
|
||||
sx={listItemStyles}
|
||||
>
|
||||
<Text as="span">{prompt}</Text>
|
||||
</ListItem>
|
||||
))}
|
||||
</OrderedList>
|
||||
</ScrollableContent>
|
||||
{isLoading && (
|
||||
<Flex
|
||||
pos="absolute"
|
||||
w="full"
|
||||
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,
|
||||
} from '../store/dynamicPromptsSlice';
|
||||
import IAIMantineSelectItemWithDescription from 'common/components/IAIMantineSelectItemWithDescription';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
type Item = {
|
||||
label: string;
|
||||
@ -47,13 +48,15 @@ const ParamDynamicPromptsSeedBehaviour = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIMantineSelect
|
||||
label={t('dynamicPrompts.seedBehaviour.label')}
|
||||
value={seedBehaviour}
|
||||
data={data}
|
||||
itemComponent={IAIMantineSelectItemWithDescription}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<IAIInformationalPopover feature="dynamicPromptsSeedBehaviour">
|
||||
<IAIMantineSelect
|
||||
label={t('dynamicPrompts.seedBehaviour.label')}
|
||||
value={seedBehaviour}
|
||||
data={data}
|
||||
itemComponent={IAIMantineSelectItemWithDescription}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
loraWeightChanged,
|
||||
loraWeightReset,
|
||||
} from '../store/loraSlice';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
type Props = {
|
||||
lora: LoRA;
|
||||
@ -36,7 +36,7 @@ const ParamLora = (props: Props) => {
|
||||
}, [dispatch, lora.id]);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="lora">
|
||||
<IAIInformationalPopover feature="lora">
|
||||
<Flex sx={{ gap: 2.5, alignItems: 'flex-end' }}>
|
||||
<IAISlider
|
||||
label={lora.model_name}
|
||||
|
@ -25,8 +25,8 @@ const InvocationNodeFooter = ({ nodeId }: Props) => {
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
{hasImageOutput && <EmbedWorkflowCheckbox nodeId={nodeId} />}
|
||||
<UseCacheCheckbox nodeId={nodeId} />
|
||||
{hasImageOutput && <EmbedWorkflowCheckbox nodeId={nodeId} />}
|
||||
{hasImageOutput && <SaveToGalleryCheckbox nodeId={nodeId} />}
|
||||
</Flex>
|
||||
);
|
||||
|
@ -16,6 +16,7 @@ import SchedulerInputField from './inputs/SchedulerInputField';
|
||||
import StringInputField from './inputs/StringInputField';
|
||||
import VaeModelInputField from './inputs/VaeModelInputField';
|
||||
import IPAdapterModelInputField from './inputs/IPAdapterModelInputField';
|
||||
import BoardInputField from './inputs/BoardInputField';
|
||||
|
||||
type InputFieldProps = {
|
||||
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 (
|
||||
field?.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 (
|
||||
<IAIMantineSearchableSelect
|
||||
className="nowheel nodrag"
|
||||
sx={{
|
||||
'.mantine-Select-dropdown': {
|
||||
width: '14rem !important',
|
||||
},
|
||||
}}
|
||||
value={field.value}
|
||||
data={data}
|
||||
onChange={handleChange}
|
||||
|
@ -143,7 +143,7 @@ export const useBuildNodeData = () => {
|
||||
notes: '',
|
||||
isOpen: true,
|
||||
embedWorkflow: false,
|
||||
isIntermediate: true,
|
||||
isIntermediate: type === 'save_image' ? false : true,
|
||||
inputs,
|
||||
outputs,
|
||||
useCache: template.useCache,
|
||||
|
@ -17,8 +17,12 @@ export const useHasImageOutput = (nodeId: string) => {
|
||||
if (!isInvocationNode(node)) {
|
||||
return false;
|
||||
}
|
||||
return some(node.data.outputs, (output) =>
|
||||
IMAGE_FIELDS.includes(output.type)
|
||||
return some(
|
||||
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
|
||||
|
@ -30,6 +30,7 @@ import {
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { DRAG_HANDLE_CLASSNAME } from '../types/constants';
|
||||
import {
|
||||
BoardInputFieldValue,
|
||||
BooleanInputFieldValue,
|
||||
ColorInputFieldValue,
|
||||
ControlNetModelInputFieldValue,
|
||||
@ -494,6 +495,12 @@ const nodesSlice = createSlice({
|
||||
) => {
|
||||
fieldValueReducer(state, action);
|
||||
},
|
||||
fieldBoardValueChanged: (
|
||||
state,
|
||||
action: FieldValueAction<BoardInputFieldValue>
|
||||
) => {
|
||||
fieldValueReducer(state, action);
|
||||
},
|
||||
fieldImageValueChanged: (
|
||||
state,
|
||||
action: FieldValueAction<ImageInputFieldValue>
|
||||
@ -871,7 +878,7 @@ const nodesSlice = createSlice({
|
||||
builder.addCase(appSocketQueueItemStatusChanged, (state, action) => {
|
||||
if (['in_progress'].includes(action.payload.data.status)) {
|
||||
forEach(state.nodeExecutionStates, (nes) => {
|
||||
nes.status = NodeStatus.IN_PROGRESS;
|
||||
nes.status = NodeStatus.PENDING;
|
||||
nes.error = null;
|
||||
nes.progress = null;
|
||||
nes.progressImage = null;
|
||||
@ -897,6 +904,7 @@ export const {
|
||||
imageCollectionFieldValueChanged,
|
||||
fieldStringValueChanged,
|
||||
fieldNumberValueChanged,
|
||||
fieldBoardValueChanged,
|
||||
fieldBooleanValueChanged,
|
||||
fieldImageValueChanged,
|
||||
fieldColorValueChanged,
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { FieldType, FieldUIConfig } from './types';
|
||||
import {
|
||||
FieldType,
|
||||
FieldTypeMap,
|
||||
FieldTypeMapWithNumber,
|
||||
FieldUIConfig,
|
||||
} from './types';
|
||||
import { t } from 'i18next';
|
||||
|
||||
export const HANDLE_TOOLTIP_OPEN_DELAY = 500;
|
||||
@ -28,7 +33,7 @@ export const COLLECTION_TYPES: FieldType[] = [
|
||||
'ColorCollection',
|
||||
];
|
||||
|
||||
export const POLYMORPHIC_TYPES = [
|
||||
export const POLYMORPHIC_TYPES: FieldType[] = [
|
||||
'IntegerPolymorphic',
|
||||
'BooleanPolymorphic',
|
||||
'FloatPolymorphic',
|
||||
@ -40,7 +45,7 @@ export const POLYMORPHIC_TYPES = [
|
||||
'ColorPolymorphic',
|
||||
];
|
||||
|
||||
export const MODEL_TYPES = [
|
||||
export const MODEL_TYPES: FieldType[] = [
|
||||
'IPAdapterModelField',
|
||||
'ControlNetModelField',
|
||||
'LoRAModelField',
|
||||
@ -54,7 +59,7 @@ export const MODEL_TYPES = [
|
||||
'ClipField',
|
||||
];
|
||||
|
||||
export const COLLECTION_MAP = {
|
||||
export const COLLECTION_MAP: FieldTypeMapWithNumber = {
|
||||
integer: 'IntegerCollection',
|
||||
boolean: 'BooleanCollection',
|
||||
number: 'FloatCollection',
|
||||
@ -71,7 +76,7 @@ export const isCollectionItemType = (
|
||||
): itemType is keyof typeof COLLECTION_MAP =>
|
||||
Boolean(itemType && itemType in COLLECTION_MAP);
|
||||
|
||||
export const SINGLE_TO_POLYMORPHIC_MAP = {
|
||||
export const SINGLE_TO_POLYMORPHIC_MAP: FieldTypeMapWithNumber = {
|
||||
integer: 'IntegerPolymorphic',
|
||||
boolean: 'BooleanPolymorphic',
|
||||
number: 'FloatPolymorphic',
|
||||
@ -84,7 +89,7 @@ export const SINGLE_TO_POLYMORPHIC_MAP = {
|
||||
ColorField: 'ColorPolymorphic',
|
||||
};
|
||||
|
||||
export const POLYMORPHIC_TO_SINGLE_MAP = {
|
||||
export const POLYMORPHIC_TO_SINGLE_MAP: FieldTypeMap = {
|
||||
IntegerPolymorphic: 'integer',
|
||||
BooleanPolymorphic: 'boolean',
|
||||
FloatPolymorphic: 'float',
|
||||
@ -96,7 +101,7 @@ export const POLYMORPHIC_TO_SINGLE_MAP = {
|
||||
ColorPolymorphic: 'ColorField',
|
||||
};
|
||||
|
||||
export const TYPES_WITH_INPUT_COMPONENTS = [
|
||||
export const TYPES_WITH_INPUT_COMPONENTS: FieldType[] = [
|
||||
'string',
|
||||
'StringPolymorphic',
|
||||
'boolean',
|
||||
@ -117,6 +122,7 @@ export const TYPES_WITH_INPUT_COMPONENTS = [
|
||||
'SDXLMainModelField',
|
||||
'Scheduler',
|
||||
'IPAdapterModelField',
|
||||
'BoardField',
|
||||
];
|
||||
|
||||
export const isPolymorphicItemType = (
|
||||
@ -240,6 +246,11 @@ export const FIELDS: Record<FieldType, FieldUIConfig> = {
|
||||
description: t('nodes.imageFieldDescription'),
|
||||
title: t('nodes.imageField'),
|
||||
},
|
||||
BoardField: {
|
||||
color: 'purple.500',
|
||||
description: t('nodes.imageFieldDescription'),
|
||||
title: t('nodes.imageField'),
|
||||
},
|
||||
ImagePolymorphic: {
|
||||
color: 'purple.500',
|
||||
description: t('nodes.imagePolymorphicDescription'),
|
||||
|
@ -72,6 +72,7 @@ export type FieldUIConfig = {
|
||||
|
||||
// TODO: Get this from the OpenAPI schema? may be tricky...
|
||||
export const zFieldType = z.enum([
|
||||
'BoardField',
|
||||
'boolean',
|
||||
'BooleanCollection',
|
||||
'BooleanPolymorphic',
|
||||
@ -119,6 +120,10 @@ export const zFieldType = z.enum([
|
||||
]);
|
||||
|
||||
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([
|
||||
'WorkflowField',
|
||||
@ -187,6 +192,11 @@ export const zImageField = z.object({
|
||||
});
|
||||
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({
|
||||
latents_name: z.string().trim().min(1),
|
||||
seed: z.number().int().optional(),
|
||||
@ -494,6 +504,12 @@ export const zImageInputFieldValue = zInputFieldValueBase.extend({
|
||||
});
|
||||
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({
|
||||
type: z.literal('ImagePolymorphic'),
|
||||
value: zImageField.optional(),
|
||||
@ -630,6 +646,7 @@ export type SchedulerInputFieldValue = z.infer<
|
||||
>;
|
||||
|
||||
export const zInputFieldValue = z.discriminatedUnion('type', [
|
||||
zBoardInputFieldValue,
|
||||
zBooleanCollectionInputFieldValue,
|
||||
zBooleanInputFieldValue,
|
||||
zBooleanPolymorphicInputFieldValue,
|
||||
@ -770,6 +787,11 @@ export type BooleanPolymorphicInputFieldTemplate = Omit<
|
||||
type: 'BooleanPolymorphic';
|
||||
};
|
||||
|
||||
export type BoardInputFieldTemplate = InputFieldTemplateBase & {
|
||||
default: BoardField;
|
||||
type: 'BoardField';
|
||||
};
|
||||
|
||||
export type ImageInputFieldTemplate = InputFieldTemplateBase & {
|
||||
default: ImageField;
|
||||
type: 'ImageField';
|
||||
@ -952,6 +974,7 @@ export type WorkflowInputFieldTemplate = InputFieldTemplateBase & {
|
||||
* maximum length, pattern to match, etc).
|
||||
*/
|
||||
export type InputFieldTemplate =
|
||||
| BoardInputFieldTemplate
|
||||
| BooleanCollectionInputFieldTemplate
|
||||
| BooleanPolymorphicInputFieldTemplate
|
||||
| BooleanInputFieldTemplate
|
||||
|
@ -62,6 +62,8 @@ import {
|
||||
ConditioningField,
|
||||
IPAdapterInputFieldTemplate,
|
||||
IPAdapterModelInputFieldTemplate,
|
||||
BoardInputFieldTemplate,
|
||||
InputFieldTemplate,
|
||||
} from '../types/types';
|
||||
import { ControlField } from 'services/api/types';
|
||||
|
||||
@ -450,6 +452,19 @@ const buildIPAdapterModelInputFieldTemplate = ({
|
||||
return template;
|
||||
};
|
||||
|
||||
const buildBoardInputFieldTemplate = ({
|
||||
schemaObject,
|
||||
baseField,
|
||||
}: BuildInputFieldArg): BoardInputFieldTemplate => {
|
||||
const template: BoardInputFieldTemplate = {
|
||||
...baseField,
|
||||
type: 'BoardField',
|
||||
default: schemaObject.default ?? undefined,
|
||||
};
|
||||
|
||||
return template;
|
||||
};
|
||||
|
||||
const buildImageInputFieldTemplate = ({
|
||||
schemaObject,
|
||||
baseField,
|
||||
@ -851,7 +866,10 @@ export const getFieldType = (
|
||||
return;
|
||||
};
|
||||
|
||||
const TEMPLATE_BUILDER_MAP = {
|
||||
const TEMPLATE_BUILDER_MAP: {
|
||||
[key in FieldType]?: (arg: BuildInputFieldArg) => InputFieldTemplate;
|
||||
} = {
|
||||
BoardField: buildBoardInputFieldTemplate,
|
||||
boolean: buildBooleanInputFieldTemplate,
|
||||
BooleanCollection: buildBooleanCollectionInputFieldTemplate,
|
||||
BooleanPolymorphic: buildBooleanPolymorphicInputFieldTemplate,
|
||||
@ -937,7 +955,13 @@ export const buildInputFieldTemplate = (
|
||||
return;
|
||||
}
|
||||
|
||||
return TEMPLATE_BUILDER_MAP[fieldType]({
|
||||
const builder = TEMPLATE_BUILDER_MAP[fieldType];
|
||||
|
||||
if (!builder) {
|
||||
return;
|
||||
}
|
||||
|
||||
return builder({
|
||||
schemaObject: fieldSchema,
|
||||
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: '',
|
||||
BoardField: undefined,
|
||||
boolean: false,
|
||||
BooleanCollection: [],
|
||||
BooleanPolymorphic: false,
|
||||
|
@ -24,12 +24,14 @@ export const addSaveImageNode = (
|
||||
const activeTabName = activeTabNameSelector(state);
|
||||
const is_intermediate =
|
||||
activeTabName === 'unifiedCanvas' ? !state.canvas.shouldAutoSave : false;
|
||||
const { autoAddBoardId } = state.gallery;
|
||||
|
||||
const saveImageNode: SaveImageInvocation = {
|
||||
id: SAVE_IMAGE,
|
||||
type: 'save_image',
|
||||
is_intermediate,
|
||||
use_cache: false,
|
||||
board: autoAddBoardId === 'none' ? undefined : { board_id: autoAddBoardId },
|
||||
};
|
||||
|
||||
graph.nodes[SAVE_IMAGE] = saveImageNode;
|
||||
|
@ -6,15 +6,18 @@ import {
|
||||
SaveImageInvocation,
|
||||
} from 'services/api/types';
|
||||
import { REALESRGAN as ESRGAN, SAVE_IMAGE } from './constants';
|
||||
import { BoardId } from 'features/gallery/store/types';
|
||||
|
||||
type Arg = {
|
||||
image_name: string;
|
||||
esrganModelName: ESRGANModelName;
|
||||
autoAddBoardId: BoardId;
|
||||
};
|
||||
|
||||
export const buildAdHocUpscaleGraph = ({
|
||||
image_name,
|
||||
esrganModelName,
|
||||
autoAddBoardId,
|
||||
}: Arg): Graph => {
|
||||
const realesrganNode: ESRGANInvocation = {
|
||||
id: ESRGAN,
|
||||
@ -28,6 +31,8 @@ export const buildAdHocUpscaleGraph = ({
|
||||
id: SAVE_IMAGE,
|
||||
type: 'save_image',
|
||||
use_cache: false,
|
||||
is_intermediate: false,
|
||||
board: autoAddBoardId === 'none' ? undefined : { board_id: autoAddBoardId },
|
||||
};
|
||||
|
||||
const graph: NonNullableGraph = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { RootState } from 'app/store/store';
|
||||
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 { setClipSkip } from 'features/parameters/store/generationSlice';
|
||||
import { clipSkipMap } from 'features/parameters/types/constants';
|
||||
@ -47,7 +47,7 @@ export default function ParamClipSkip() {
|
||||
}
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="clipSkip">
|
||||
<IAIInformationalPopover feature="clipSkip" placement="top">
|
||||
<IAISlider
|
||||
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { flipBoundingBoxAxes } from 'features/canvas/store/canvasSlice';
|
||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||
@ -18,7 +19,6 @@ import ParamAspectRatio, {
|
||||
} from '../../Core/ParamAspectRatio';
|
||||
import ParamBoundingBoxHeight from './ParamBoundingBoxHeight';
|
||||
import ParamBoundingBoxWidth from './ParamBoundingBoxWidth';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
|
||||
const sizeOptsSelector = createSelector(
|
||||
[generationSelector, canvasSelector],
|
||||
@ -93,42 +93,29 @@ export default function ParamBoundingBoxSize() {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" gap={2}>
|
||||
<Box width="full">
|
||||
<IAIInformationalPopover details="paramRatio">
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
width: 'full',
|
||||
color: 'base.700',
|
||||
_dark: {
|
||||
color: 'base.300',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('parameters.aspectRatio')}
|
||||
</Text>
|
||||
</IAIInformationalPopover>
|
||||
</Box>
|
||||
<Spacer />
|
||||
<ParamAspectRatio />
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.swapSizes')}
|
||||
aria-label={t('ui.swapSizes')}
|
||||
size="sm"
|
||||
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>
|
||||
<IAIInformationalPopover feature="paramRatio">
|
||||
<FormControl as={Flex} flexDir="row" alignItems="center" gap={2}>
|
||||
<FormLabel>{t('parameters.aspectRatio')}</FormLabel>
|
||||
<Spacer />
|
||||
<ParamAspectRatio />
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.swapSizes')}
|
||||
aria-label={t('ui.swapSizes')}
|
||||
size="sm"
|
||||
icon={<MdOutlineSwapVert />}
|
||||
fontSize={20}
|
||||
onClick={handleToggleSize}
|
||||
/>
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.lockRatio')}
|
||||
aria-label={t('ui.lockRatio')}
|
||||
size="sm"
|
||||
icon={<FaLock />}
|
||||
isChecked={shouldLockAspectRatio}
|
||||
onClick={handleLockRatio}
|
||||
/>
|
||||
</FormControl>
|
||||
</IAIInformationalPopover>
|
||||
<ParamBoundingBoxWidth />
|
||||
<ParamBoundingBoxHeight />
|
||||
</Flex>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
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 IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { setCanvasCoherenceMode } from 'features/parameters/store/generationSlice';
|
||||
@ -31,7 +31,7 @@ const ParamCanvasCoherenceMode = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="compositingCoherenceMode">
|
||||
<IAIInformationalPopover feature="compositingCoherenceMode">
|
||||
<IAIMantineSelect
|
||||
label={t('parameters.coherenceMode')}
|
||||
data={coherenceModeSelectData}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
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 { setCanvasCoherenceSteps } from 'features/parameters/store/generationSlice';
|
||||
import { memo } from 'react';
|
||||
@ -14,7 +14,7 @@ const ParamCanvasCoherenceSteps = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="compositingCoherenceSteps">
|
||||
<IAIInformationalPopover feature="compositingCoherenceSteps">
|
||||
<IAISlider
|
||||
label={t('parameters.coherenceSteps')}
|
||||
min={1}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
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 { setCanvasCoherenceStrength } from 'features/parameters/store/generationSlice';
|
||||
import { memo } from 'react';
|
||||
@ -14,7 +14,7 @@ const ParamCanvasCoherenceStrength = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="compositingStrength">
|
||||
<IAIInformationalPopover feature="compositingStrength">
|
||||
<IAISlider
|
||||
label={t('parameters.coherenceStrength')}
|
||||
min={0}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
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 { setMaskBlur } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -13,7 +13,7 @@ export default function ParamMaskBlur() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="compositingBlur">
|
||||
<IAIInformationalPopover feature="compositingBlur">
|
||||
<IAISlider
|
||||
label={t('parameters.maskBlur')}
|
||||
min={0}
|
||||
|
@ -2,7 +2,7 @@ import { SelectItem } from '@mantine/core';
|
||||
import { RootState } from 'app/store/store';
|
||||
|
||||
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 { setMaskBlurMethod } from 'features/parameters/store/generationSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -29,7 +29,7 @@ export default function ParamMaskBlurMethod() {
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="compositingBlurMethod">
|
||||
<IAIInformationalPopover feature="compositingBlurMethod">
|
||||
<IAIMantineSelect
|
||||
value={maskBlurMethod}
|
||||
onChange={handleMaskBlurMethodChange}
|
||||
|
@ -15,19 +15,13 @@ const ParamCompositingSettingsCollapse = () => {
|
||||
return (
|
||||
<IAICollapse label={t('parameters.compositingSettingsHeader')}>
|
||||
<Flex sx={{ flexDirection: 'column', gap: 2 }}>
|
||||
<SubParametersWrapper
|
||||
label={t('parameters.coherencePassHeader')}
|
||||
headerInfoPopover="compositingCoherencePass"
|
||||
>
|
||||
<SubParametersWrapper label={t('parameters.coherencePassHeader')}>
|
||||
<ParamCanvasCoherenceMode />
|
||||
<ParamCanvasCoherenceSteps />
|
||||
<ParamCanvasCoherenceStrength />
|
||||
</SubParametersWrapper>
|
||||
<Divider />
|
||||
<SubParametersWrapper
|
||||
label={t('parameters.maskAdjustmentsHeader')}
|
||||
headerInfoPopover="compositingMaskAdjustments"
|
||||
>
|
||||
<SubParametersWrapper label={t('parameters.maskAdjustmentsHeader')}>
|
||||
<ParamMaskBlur />
|
||||
<ParamMaskBlurMethod />
|
||||
</SubParametersWrapper>
|
||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { setInfillMethod } from 'features/parameters/store/generationSlice';
|
||||
|
||||
@ -40,7 +40,7 @@ const ParamInfillMethod = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="infillMethod">
|
||||
<IAIInformationalPopover feature="infillMethod">
|
||||
<IAIMantineSelect
|
||||
disabled={infill_methods?.length === 0}
|
||||
placeholder={isLoading ? 'Loading...' : undefined}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { canvasSelector } from 'features/canvas/store/canvasSelectors';
|
||||
import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
|
||||
@ -36,7 +36,7 @@ const ParamScaleBeforeProcessing = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="scaleBeforeProcessing">
|
||||
<IAIInformationalPopover feature="scaleBeforeProcessing">
|
||||
<IAIMantineSearchableSelect
|
||||
label={t('parameters.scaleBeforeProcessing')}
|
||||
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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
@ -29,25 +29,23 @@ export default function ParamAspectRatio() {
|
||||
const activeTabName = useAppSelector(activeTabNameSelector);
|
||||
|
||||
return (
|
||||
<Flex gap={2} flexGrow={1}>
|
||||
<ButtonGroup isAttached>
|
||||
{aspectRatios.map((ratio) => (
|
||||
<IAIButton
|
||||
key={ratio.name}
|
||||
size="sm"
|
||||
isChecked={aspectRatio === ratio.value}
|
||||
isDisabled={
|
||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||
}
|
||||
onClick={() => {
|
||||
dispatch(setAspectRatio(ratio.value));
|
||||
dispatch(setShouldLockAspectRatio(false));
|
||||
}}
|
||||
>
|
||||
{ratio.name}
|
||||
</IAIButton>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
<ButtonGroup isAttached>
|
||||
{aspectRatios.map((ratio) => (
|
||||
<IAIButton
|
||||
key={ratio.name}
|
||||
size="sm"
|
||||
isChecked={aspectRatio === ratio.value}
|
||||
isDisabled={
|
||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||
}
|
||||
onClick={() => {
|
||||
dispatch(setAspectRatio(ratio.value));
|
||||
dispatch(setShouldLockAspectRatio(false));
|
||||
}}
|
||||
>
|
||||
{ratio.name}
|
||||
</IAIButton>
|
||||
))}
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setCfgScale } from 'features/parameters/store/generationSlice';
|
||||
@ -54,7 +54,7 @@ const ParamCFGScale = () => {
|
||||
);
|
||||
|
||||
return shouldUseSliders ? (
|
||||
<IAIInformationalPopover details="paramCFGScale">
|
||||
<IAIInformationalPopover feature="paramCFGScale">
|
||||
<IAISlider
|
||||
label={t('parameters.cfgScale')}
|
||||
step={shift ? 0.1 : 0.5}
|
||||
@ -71,7 +71,7 @@ const ParamCFGScale = () => {
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
) : (
|
||||
<IAIInformationalPopover details="paramCFGScale">
|
||||
<IAIInformationalPopover feature="paramCFGScale">
|
||||
<IAINumberInput
|
||||
label={t('parameters.cfgScale')}
|
||||
step={0.5}
|
||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
import { setIterations } from 'features/parameters/store/generationSlice';
|
||||
@ -61,7 +61,7 @@ const ParamIterations = ({ asSlider }: Props) => {
|
||||
}, [dispatch, initial]);
|
||||
|
||||
return asSlider || shouldUseSliders ? (
|
||||
<IAIInformationalPopover details="paramIterations">
|
||||
<IAIInformationalPopover feature="paramIterations">
|
||||
<IAISlider
|
||||
label={t('parameters.iterations')}
|
||||
step={step}
|
||||
@ -77,7 +77,7 @@ const ParamIterations = ({ asSlider }: Props) => {
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
) : (
|
||||
<IAIInformationalPopover details="paramIterations">
|
||||
<IAIInformationalPopover feature="paramIterations">
|
||||
<IAINumberInput
|
||||
label={t('parameters.iterations')}
|
||||
step={step}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Box, FormControl, useDisclosure } from '@chakra-ui/react';
|
||||
import type { RootState } from 'app/store/store';
|
||||
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 AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
|
||||
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
|
||||
@ -76,15 +76,15 @@ const ParamNegativeConditioning = () => {
|
||||
const isEmbeddingEnabled = useFeatureStatus('embedding').isFeatureEnabled;
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover
|
||||
placement="right"
|
||||
details="paramNegativeConditioning"
|
||||
>
|
||||
<FormControl>
|
||||
<ParamEmbeddingPopover
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onSelect={handleSelectEmbedding}
|
||||
<FormControl>
|
||||
<ParamEmbeddingPopover
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onSelect={handleSelectEmbedding}
|
||||
>
|
||||
<IAIInformationalPopover
|
||||
feature="paramNegativeConditioning"
|
||||
placement="right"
|
||||
>
|
||||
<IAITextarea
|
||||
id="negativePrompt"
|
||||
@ -98,20 +98,20 @@ const ParamNegativeConditioning = () => {
|
||||
minH={16}
|
||||
{...(isEmbeddingEnabled && { onKeyDown: handleKeyDown })}
|
||||
/>
|
||||
</ParamEmbeddingPopover>
|
||||
{!isOpen && isEmbeddingEnabled && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
}}
|
||||
>
|
||||
<AddEmbeddingButton onClick={onOpen} />
|
||||
</Box>
|
||||
)}
|
||||
</FormControl>
|
||||
</IAIInformationalPopover>
|
||||
</IAIInformationalPopover>
|
||||
</ParamEmbeddingPopover>
|
||||
{!isOpen && isEmbeddingEnabled && (
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
insetInlineEnd: 0,
|
||||
}}
|
||||
>
|
||||
<AddEmbeddingButton onClick={onOpen} />
|
||||
</Box>
|
||||
)}
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@ import { Box, FormControl, useDisclosure } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAITextarea from 'common/components/IAITextarea';
|
||||
import AddEmbeddingButton from 'features/embedding/components/AddEmbeddingButton';
|
||||
import ParamEmbeddingPopover from 'features/embedding/components/ParamEmbeddingPopover';
|
||||
@ -12,7 +13,6 @@ import { flushSync } from 'react-dom';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
|
||||
import IAIInformationalPopover from '../../../../../common/components/IAIInformationalPopover';
|
||||
|
||||
const promptInputSelector = createSelector(
|
||||
[stateSelector],
|
||||
@ -104,15 +104,15 @@ const ParamPositiveConditioning = () => {
|
||||
|
||||
return (
|
||||
<Box position="relative">
|
||||
<IAIInformationalPopover
|
||||
placement="right"
|
||||
details="paramPositiveConditioning"
|
||||
>
|
||||
<FormControl>
|
||||
<ParamEmbeddingPopover
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onSelect={handleSelectEmbedding}
|
||||
<FormControl>
|
||||
<ParamEmbeddingPopover
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
onSelect={handleSelectEmbedding}
|
||||
>
|
||||
<IAIInformationalPopover
|
||||
feature="paramPositiveConditioning"
|
||||
placement="right"
|
||||
>
|
||||
<IAITextarea
|
||||
id="prompt"
|
||||
@ -125,9 +125,9 @@ const ParamPositiveConditioning = () => {
|
||||
resize="vertical"
|
||||
minH={32}
|
||||
/>
|
||||
</ParamEmbeddingPopover>
|
||||
</FormControl>
|
||||
</IAIInformationalPopover>
|
||||
</IAIInformationalPopover>
|
||||
</ParamEmbeddingPopover>
|
||||
</FormControl>
|
||||
{!isOpen && isEmbeddingEnabled && (
|
||||
<Box
|
||||
sx={{
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
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 { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||
import { setScheduler } from 'features/parameters/store/generationSlice';
|
||||
@ -52,7 +52,7 @@ const ParamScheduler = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="paramScheduler">
|
||||
<IAIInformationalPopover feature="paramScheduler">
|
||||
<IAIMantineSearchableSelect
|
||||
label={t('parameters.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 { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIIconButton from 'common/components/IAIIconButton';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import { generationSelector } from 'features/parameters/store/generationSelectors';
|
||||
import {
|
||||
setAspectRatio,
|
||||
@ -16,8 +18,6 @@ import { activeTabNameSelector } from '../../../../ui/store/uiSelectors';
|
||||
import ParamAspectRatio, { mappedAspectRatios } from './ParamAspectRatio';
|
||||
import ParamHeight from './ParamHeight';
|
||||
import ParamWidth from './ParamWidth';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
|
||||
const sizeOptsSelector = createSelector(
|
||||
[generationSelector, activeTabNameSelector],
|
||||
@ -83,47 +83,35 @@ export default function ParamSize() {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Flex alignItems="center" gap={2}>
|
||||
<Box width="full">
|
||||
<IAIInformationalPopover details="paramRatio">
|
||||
<Text
|
||||
sx={{
|
||||
fontSize: 'sm',
|
||||
color: 'base.700',
|
||||
_dark: {
|
||||
color: 'base.300',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('parameters.aspectRatio')}
|
||||
</Text>
|
||||
</IAIInformationalPopover>
|
||||
</Box>
|
||||
<Spacer />
|
||||
<ParamAspectRatio />
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.swapSizes')}
|
||||
aria-label={t('ui.swapSizes')}
|
||||
size="sm"
|
||||
icon={<MdOutlineSwapVert />}
|
||||
fontSize={20}
|
||||
isDisabled={
|
||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||
}
|
||||
onClick={handleToggleSize}
|
||||
/>
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.lockRatio')}
|
||||
aria-label={t('ui.lockRatio')}
|
||||
size="sm"
|
||||
icon={<FaLock />}
|
||||
isChecked={shouldLockAspectRatio}
|
||||
isDisabled={
|
||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||
}
|
||||
onClick={handleLockRatio}
|
||||
/>
|
||||
</Flex>
|
||||
<IAIInformationalPopover feature="paramRatio">
|
||||
<FormControl as={Flex} flexDir="row" alignItems="center" gap={2}>
|
||||
<FormLabel>{t('parameters.aspectRatio')}</FormLabel>
|
||||
<Spacer />
|
||||
<ParamAspectRatio />
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.swapSizes')}
|
||||
aria-label={t('ui.swapSizes')}
|
||||
size="sm"
|
||||
icon={<MdOutlineSwapVert />}
|
||||
fontSize={20}
|
||||
isDisabled={
|
||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||
}
|
||||
onClick={handleToggleSize}
|
||||
/>
|
||||
<IAIIconButton
|
||||
tooltip={t('ui.lockRatio')}
|
||||
aria-label={t('ui.lockRatio')}
|
||||
size="sm"
|
||||
icon={<FaLock />}
|
||||
isChecked={shouldLockAspectRatio}
|
||||
isDisabled={
|
||||
activeTabName === 'img2img' ? !shouldFitToWidthHeight : false
|
||||
}
|
||||
onClick={handleLockRatio}
|
||||
/>
|
||||
</FormControl>
|
||||
</IAIInformationalPopover>
|
||||
<Flex gap={2} alignItems="center">
|
||||
<Flex gap={2} flexDirection="column" width="full">
|
||||
<ParamWidth
|
||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAINumberInput from 'common/components/IAINumberInput';
|
||||
|
||||
import IAISlider from 'common/components/IAISlider';
|
||||
@ -57,7 +57,7 @@ const ParamSteps = () => {
|
||||
}, [dispatch]);
|
||||
|
||||
return shouldUseSliders ? (
|
||||
<IAIInformationalPopover details="paramSteps">
|
||||
<IAIInformationalPopover feature="paramSteps">
|
||||
<IAISlider
|
||||
label={t('parameters.steps')}
|
||||
min={min}
|
||||
@ -73,7 +73,7 @@ const ParamSteps = () => {
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
) : (
|
||||
<IAIInformationalPopover details="paramSteps">
|
||||
<IAIInformationalPopover feature="paramSteps">
|
||||
<IAINumberInput
|
||||
label={t('parameters.steps')}
|
||||
min={min}
|
||||
|
@ -7,7 +7,7 @@ import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SubParametersWrapper from '../SubParametersWrapper';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
const selector = createSelector(
|
||||
[stateSelector],
|
||||
@ -46,8 +46,8 @@ const ImageToImageStrength = () => {
|
||||
}, [dispatch, initial]);
|
||||
|
||||
return (
|
||||
<SubParametersWrapper>
|
||||
<IAIInformationalPopover details="paramDenoisingStrength">
|
||||
<IAIInformationalPopover feature="paramDenoisingStrength">
|
||||
<SubParametersWrapper>
|
||||
<IAISlider
|
||||
label={`${t('parameters.denoisingStrength')}`}
|
||||
step={step}
|
||||
@ -62,8 +62,8 @@ const ImageToImageStrength = () => {
|
||||
withReset
|
||||
sliderNumberInputProps={{ max: inputMax }}
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
</SubParametersWrapper>
|
||||
</SubParametersWrapper>
|
||||
</IAIInformationalPopover>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -21,7 +21,7 @@ import {
|
||||
useGetOnnxModelsQuery,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { useFeatureStatus } from '../../../../system/hooks/useFeatureStatus';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
@ -120,7 +120,7 @@ const ParamMainModelSelect = () => {
|
||||
/>
|
||||
) : (
|
||||
<Flex w="100%" alignItems="center" gap={3}>
|
||||
<IAIInformationalPopover details="paramModel" placement="bottom">
|
||||
<IAIInformationalPopover feature="paramModel">
|
||||
<IAIMantineSearchableSelect
|
||||
tooltip={selectedModel?.description}
|
||||
label={t('modelManager.model')}
|
||||
@ -136,7 +136,7 @@ const ParamMainModelSelect = () => {
|
||||
/>
|
||||
</IAIInformationalPopover>
|
||||
{isSyncModelEnabled && (
|
||||
<Box mt={7}>
|
||||
<Box mt={6}>
|
||||
<SyncModelsButton iconMode />
|
||||
</Box>
|
||||
)}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
|
||||
import { ChangeEvent, useCallback } from 'react';
|
||||
@ -20,7 +20,7 @@ export const ParamCpuNoiseToggle = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="noiseUseCPU">
|
||||
<IAIInformationalPopover feature="noiseUseCPU">
|
||||
<IAISwitch
|
||||
label={t('parameters.useCpuNoise')}
|
||||
isChecked={shouldUseCpuNoise}
|
||||
|
@ -3,11 +3,11 @@ import { memo } from 'react';
|
||||
import ParamSeed from './ParamSeed';
|
||||
import ParamSeedShuffle from './ParamSeedShuffle';
|
||||
import ParamSeedRandomize from './ParamSeedRandomize';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
const ParamSeedFull = () => {
|
||||
return (
|
||||
<IAIInformationalPopover details="paramSeed">
|
||||
<IAIInformationalPopover feature="paramSeed">
|
||||
<Flex sx={{ gap: 3, alignItems: 'flex-end' }}>
|
||||
<ParamSeed />
|
||||
<ParamSeedShuffle />
|
||||
|
@ -1,40 +1,28 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import { Flex, Text, forwardRef } from '@chakra-ui/react';
|
||||
import { ReactNode, memo } from 'react';
|
||||
|
||||
type SubParameterWrapperProps = {
|
||||
children: ReactNode | ReactNode[];
|
||||
children: ReactNode;
|
||||
label?: string;
|
||||
headerInfoPopover?: string;
|
||||
};
|
||||
|
||||
const SubParametersWrapper = (props: SubParameterWrapperProps) => (
|
||||
<Flex
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
bg: 'base.100',
|
||||
px: 4,
|
||||
pt: 2,
|
||||
pb: 4,
|
||||
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 && (
|
||||
const SubParametersWrapper = forwardRef(
|
||||
(props: SubParameterWrapperProps, ref) => (
|
||||
<Flex
|
||||
ref={ref}
|
||||
sx={{
|
||||
flexDir: 'column',
|
||||
gap: 2,
|
||||
bg: 'base.100',
|
||||
px: 4,
|
||||
pt: 2,
|
||||
pb: 4,
|
||||
borderRadius: 'base',
|
||||
_dark: {
|
||||
bg: 'base.750',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
fontSize="sm"
|
||||
fontWeight="bold"
|
||||
@ -42,9 +30,9 @@ const SubParametersWrapper = (props: SubParameterWrapperProps) => (
|
||||
>
|
||||
{props.label}
|
||||
</Text>
|
||||
)}
|
||||
{props.children}
|
||||
</Flex>
|
||||
{props.children}
|
||||
</Flex>
|
||||
)
|
||||
);
|
||||
|
||||
SubParametersWrapper.displayName = 'SubSettingsWrapper';
|
||||
|
@ -15,7 +15,7 @@ import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectI
|
||||
import { vaeSelected } from 'features/parameters/store/generationSlice';
|
||||
import { MODEL_TYPE_MAP } from 'features/parameters/types/constants';
|
||||
import { modelIdToVAEModelParam } from 'features/parameters/util/modelIdToVAEModelParam';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
const selector = createSelector(
|
||||
stateSelector,
|
||||
@ -94,7 +94,7 @@ const ParamVAEModelSelect = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="paramVAE">
|
||||
<IAIInformationalPopover feature="paramVAE">
|
||||
<IAIMantineSearchableSelect
|
||||
itemComponent={IAIMantineSelectItemWithTooltip}
|
||||
tooltip={selectedVaeModel?.description}
|
||||
|
@ -2,7 +2,7 @@ import { createSelector } from '@reduxjs/toolkit';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
import IAIMantineSelect from 'common/components/IAIMantineSelect';
|
||||
import { vaePrecisionChanged } from 'features/parameters/store/generationSlice';
|
||||
import { PrecisionParam } from 'features/parameters/types/parameterSchemas';
|
||||
@ -35,7 +35,7 @@ const ParamVAEModelSelect = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<IAIInformationalPopover details="paramVAEPrecision">
|
||||
<IAIInformationalPopover feature="paramVAEPrecision">
|
||||
<IAIMantineSelect
|
||||
label="VAE Precision"
|
||||
value={vaePrecision}
|
||||
|
@ -7,7 +7,7 @@ import SubParametersWrapper from 'features/parameters/components/Parameters/SubP
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { setSDXLImg2ImgDenoisingStrength } from '../store/sdxlSlice';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover';
|
||||
import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
|
||||
|
||||
const selector = createSelector(
|
||||
[stateSelector],
|
||||
@ -36,8 +36,8 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<SubParametersWrapper>
|
||||
<IAIInformationalPopover details="paramDenoisingStrength">
|
||||
<IAIInformationalPopover feature="paramDenoisingStrength">
|
||||
<SubParametersWrapper>
|
||||
<IAISlider
|
||||
label={t('sdxl.denoisingStrength')}
|
||||
step={0.01}
|
||||
@ -51,8 +51,8 @@ const ParamSDXLImg2ImgDenoisingStrength = () => {
|
||||
withSliderMarks
|
||||
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 IPAdapterInvocation = s['IPAdapterInvocation'];
|
||||
export type CannyImageProcessorInvocation = s['CannyImageProcessorInvocation'];
|
||||
export type ColorMapImageProcessorInvocation =
|
||||
s['ColorMapImageProcessorInvocation'];
|
||||
export type ContentShuffleImageProcessorInvocation =
|
||||
s['ContentShuffleImageProcessorInvocation'];
|
||||
export type HedImageProcessorInvocation = s['HedImageProcessorInvocation'];
|
||||
|
@ -31,14 +31,14 @@ const invokeAIContent = defineStyle((props) => {
|
||||
|
||||
const informationalContent = defineStyle((props) => {
|
||||
return {
|
||||
[$arrowBg.variable]: mode('colors.base.100', 'colors.base.600')(props),
|
||||
[$popperBg.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.700')(props),
|
||||
[$arrowShadowColor.variable]: mode(
|
||||
'colors.base.400',
|
||||
'colors.base.400'
|
||||
)(props),
|
||||
p: 4,
|
||||
bg: mode('base.100', 'base.600')(props),
|
||||
bg: mode('base.100', 'base.700')(props),
|
||||
border: 'none',
|
||||
shadow: 'dark-lg',
|
||||
};
|
||||
@ -46,6 +46,7 @@ const informationalContent = defineStyle((props) => {
|
||||
|
||||
const invokeAI = definePartsStyle((props) => ({
|
||||
content: invokeAIContent(props),
|
||||
body: { padding: 0 },
|
||||
}));
|
||||
|
||||
const informational = definePartsStyle((props) => ({
|
||||
|
@ -76,6 +76,7 @@ export const theme: ThemeOverride = {
|
||||
direction: 'ltr',
|
||||
fonts: {
|
||||
body: `'Inter Variable', sans-serif`,
|
||||
heading: `'Inter Variable', sans-serif`,
|
||||
},
|
||||
shadows: {
|
||||
light: {
|
||||
|
Loading…
Reference in New Issue
Block a user