Merge branch 'main' into pr-labeler

This commit is contained in:
Brandon 2024-01-24 15:44:35 -05:00 committed by GitHub
commit e160cbb1e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
59 changed files with 2855 additions and 469 deletions

View File

@ -30,6 +30,7 @@ from invokeai.app.invocations.primitives import ImageField, ImageOutput
from invokeai.app.invocations.util import validate_begin_end_step, validate_weights
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
from invokeai.app.shared.fields import FieldDescriptions
from invokeai.backend.image_util.depth_anything import DepthAnythingDetector
from ...backend.model_management import BaseModelType
from .baseinvocation import (
@ -602,3 +603,33 @@ class ColorMapImageProcessorInvocation(ImageProcessorInvocation):
color_map = cv2.resize(color_map, (width, height), interpolation=cv2.INTER_NEAREST)
color_map = Image.fromarray(color_map)
return color_map
DEPTH_ANYTHING_MODEL_SIZES = Literal["large", "base", "small"]
@invocation(
"depth_anything_image_processor",
title="Depth Anything Processor",
tags=["controlnet", "depth", "depth anything"],
category="controlnet",
version="1.0.0",
)
class DepthAnythingImageProcessorInvocation(ImageProcessorInvocation):
"""Generates a depth map based on the Depth Anything algorithm"""
model_size: DEPTH_ANYTHING_MODEL_SIZES = InputField(
default="small", description="The size of the depth model to use"
)
resolution: int = InputField(default=512, ge=64, multiple_of=64, description=FieldDescriptions.image_res)
offload: bool = InputField(default=False)
def run_processor(self, image):
depth_anything_detector = DepthAnythingDetector()
depth_anything_detector.load_model(model_size=self.model_size)
if image.mode == "RGBA":
image = image.convert("RGB")
processed_image = depth_anything_detector(image=image, resolution=self.resolution, offload=self.offload)
return processed_image

View File

@ -31,6 +31,7 @@ class WorkflowRecordOrderBy(str, Enum, metaclass=MetaEnum):
class WorkflowCategory(str, Enum, metaclass=MetaEnum):
User = "user"
Default = "default"
Project = "project"
class WorkflowMeta(BaseModel):

View File

@ -0,0 +1,109 @@
import pathlib
from typing import Literal, Union
import cv2
import numpy as np
import torch
import torch.nn.functional as F
from einops import repeat
from PIL import Image
from torchvision.transforms import Compose
from invokeai.app.services.config.config_default import InvokeAIAppConfig
from invokeai.backend.image_util.depth_anything.model.dpt import DPT_DINOv2
from invokeai.backend.image_util.depth_anything.utilities.util import NormalizeImage, PrepareForNet, Resize
from invokeai.backend.util.devices import choose_torch_device
from invokeai.backend.util.util import download_with_progress_bar
config = InvokeAIAppConfig.get_config()
DEPTH_ANYTHING_MODELS = {
"large": {
"url": "https://huggingface.co/spaces/LiheYoung/Depth-Anything/resolve/main/checkpoints/depth_anything_vitl14.pth?download=true",
"local": "any/annotators/depth_anything/depth_anything_vitl14.pth",
},
"base": {
"url": "https://huggingface.co/spaces/LiheYoung/Depth-Anything/resolve/main/checkpoints/depth_anything_vitb14.pth?download=true",
"local": "any/annotators/depth_anything/depth_anything_vitb14.pth",
},
"small": {
"url": "https://huggingface.co/spaces/LiheYoung/Depth-Anything/resolve/main/checkpoints/depth_anything_vits14.pth?download=true",
"local": "any/annotators/depth_anything/depth_anything_vits14.pth",
},
}
transform = Compose(
[
Resize(
width=518,
height=518,
resize_target=False,
keep_aspect_ratio=True,
ensure_multiple_of=14,
resize_method="lower_bound",
image_interpolation_method=cv2.INTER_CUBIC,
),
NormalizeImage(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
PrepareForNet(),
]
)
class DepthAnythingDetector:
def __init__(self) -> None:
self.model = None
self.model_size: Union[Literal["large", "base", "small"], None] = None
def load_model(self, model_size=Literal["large", "base", "small"]):
DEPTH_ANYTHING_MODEL_PATH = pathlib.Path(config.models_path / DEPTH_ANYTHING_MODELS[model_size]["local"])
if not DEPTH_ANYTHING_MODEL_PATH.exists():
download_with_progress_bar(DEPTH_ANYTHING_MODELS[model_size]["url"], DEPTH_ANYTHING_MODEL_PATH)
if not self.model or model_size != self.model_size:
del self.model
self.model_size = model_size
match self.model_size:
case "small":
self.model = DPT_DINOv2(encoder="vits", features=64, out_channels=[48, 96, 192, 384])
case "base":
self.model = DPT_DINOv2(encoder="vitb", features=128, out_channels=[96, 192, 384, 768])
case "large":
self.model = DPT_DINOv2(encoder="vitl", features=256, out_channels=[256, 512, 1024, 1024])
case _:
raise TypeError("Not a supported model")
self.model.load_state_dict(torch.load(DEPTH_ANYTHING_MODEL_PATH.as_posix(), map_location="cpu"))
self.model.eval()
self.model.to(choose_torch_device())
return self.model
def to(self, device):
self.model.to(device)
return self
def __call__(self, image, resolution=512, offload=False):
image = np.array(image, dtype=np.uint8)
image = image[:, :, ::-1] / 255.0
image_height, image_width = image.shape[:2]
image = transform({"image": image})["image"]
image = torch.from_numpy(image).unsqueeze(0).to(choose_torch_device())
with torch.no_grad():
depth = self.model(image)
depth = F.interpolate(depth[None], (image_height, image_width), mode="bilinear", align_corners=False)[0, 0]
depth = (depth - depth.min()) / (depth.max() - depth.min()) * 255.0
depth_map = repeat(depth, "h w -> h w 3").cpu().numpy().astype(np.uint8)
depth_map = Image.fromarray(depth_map)
new_height = int(image_height * (resolution / image_width))
depth_map = depth_map.resize((resolution, new_height))
if offload:
del self.model
return depth_map

View File

@ -0,0 +1,145 @@
import torch.nn as nn
def _make_scratch(in_shape, out_shape, groups=1, expand=False):
scratch = nn.Module()
out_shape1 = out_shape
out_shape2 = out_shape
out_shape3 = out_shape
if len(in_shape) >= 4:
out_shape4 = out_shape
if expand:
out_shape1 = out_shape
out_shape2 = out_shape * 2
out_shape3 = out_shape * 4
if len(in_shape) >= 4:
out_shape4 = out_shape * 8
scratch.layer1_rn = nn.Conv2d(
in_shape[0], out_shape1, kernel_size=3, stride=1, padding=1, bias=False, groups=groups
)
scratch.layer2_rn = nn.Conv2d(
in_shape[1], out_shape2, kernel_size=3, stride=1, padding=1, bias=False, groups=groups
)
scratch.layer3_rn = nn.Conv2d(
in_shape[2], out_shape3, kernel_size=3, stride=1, padding=1, bias=False, groups=groups
)
if len(in_shape) >= 4:
scratch.layer4_rn = nn.Conv2d(
in_shape[3], out_shape4, kernel_size=3, stride=1, padding=1, bias=False, groups=groups
)
return scratch
class ResidualConvUnit(nn.Module):
"""Residual convolution module."""
def __init__(self, features, activation, bn):
"""Init.
Args:
features (int): number of features
"""
super().__init__()
self.bn = bn
self.groups = 1
self.conv1 = nn.Conv2d(features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups)
self.conv2 = nn.Conv2d(features, features, kernel_size=3, stride=1, padding=1, bias=True, groups=self.groups)
if self.bn:
self.bn1 = nn.BatchNorm2d(features)
self.bn2 = nn.BatchNorm2d(features)
self.activation = activation
self.skip_add = nn.quantized.FloatFunctional()
def forward(self, x):
"""Forward pass.
Args:
x (tensor): input
Returns:
tensor: output
"""
out = self.activation(x)
out = self.conv1(out)
if self.bn:
out = self.bn1(out)
out = self.activation(out)
out = self.conv2(out)
if self.bn:
out = self.bn2(out)
if self.groups > 1:
out = self.conv_merge(out)
return self.skip_add.add(out, x)
class FeatureFusionBlock(nn.Module):
"""Feature fusion block."""
def __init__(self, features, activation, deconv=False, bn=False, expand=False, align_corners=True, size=None):
"""Init.
Args:
features (int): number of features
"""
super(FeatureFusionBlock, self).__init__()
self.deconv = deconv
self.align_corners = align_corners
self.groups = 1
self.expand = expand
out_features = features
if self.expand:
out_features = features // 2
self.out_conv = nn.Conv2d(features, out_features, kernel_size=1, stride=1, padding=0, bias=True, groups=1)
self.resConfUnit1 = ResidualConvUnit(features, activation, bn)
self.resConfUnit2 = ResidualConvUnit(features, activation, bn)
self.skip_add = nn.quantized.FloatFunctional()
self.size = size
def forward(self, *xs, size=None):
"""Forward pass.
Returns:
tensor: output
"""
output = xs[0]
if len(xs) == 2:
res = self.resConfUnit1(xs[1])
output = self.skip_add.add(output, res)
output = self.resConfUnit2(output)
if (size is None) and (self.size is None):
modifier = {"scale_factor": 2}
elif size is None:
modifier = {"size": self.size}
else:
modifier = {"size": size}
output = nn.functional.interpolate(output, **modifier, mode="bilinear", align_corners=self.align_corners)
output = self.out_conv(output)
return output

View File

@ -0,0 +1,183 @@
from pathlib import Path
import torch
import torch.nn as nn
import torch.nn.functional as F
from .blocks import FeatureFusionBlock, _make_scratch
torchhub_path = Path(__file__).parent.parent / "torchhub"
def _make_fusion_block(features, use_bn, size=None):
return FeatureFusionBlock(
features,
nn.ReLU(False),
deconv=False,
bn=use_bn,
expand=False,
align_corners=True,
size=size,
)
class DPTHead(nn.Module):
def __init__(self, nclass, in_channels, features, out_channels, use_bn=False, use_clstoken=False):
super(DPTHead, self).__init__()
self.nclass = nclass
self.use_clstoken = use_clstoken
self.projects = nn.ModuleList(
[
nn.Conv2d(
in_channels=in_channels,
out_channels=out_channel,
kernel_size=1,
stride=1,
padding=0,
)
for out_channel in out_channels
]
)
self.resize_layers = nn.ModuleList(
[
nn.ConvTranspose2d(
in_channels=out_channels[0], out_channels=out_channels[0], kernel_size=4, stride=4, padding=0
),
nn.ConvTranspose2d(
in_channels=out_channels[1], out_channels=out_channels[1], kernel_size=2, stride=2, padding=0
),
nn.Identity(),
nn.Conv2d(
in_channels=out_channels[3], out_channels=out_channels[3], kernel_size=3, stride=2, padding=1
),
]
)
if use_clstoken:
self.readout_projects = nn.ModuleList()
for _ in range(len(self.projects)):
self.readout_projects.append(nn.Sequential(nn.Linear(2 * in_channels, in_channels), nn.GELU()))
self.scratch = _make_scratch(
out_channels,
features,
groups=1,
expand=False,
)
self.scratch.stem_transpose = None
self.scratch.refinenet1 = _make_fusion_block(features, use_bn)
self.scratch.refinenet2 = _make_fusion_block(features, use_bn)
self.scratch.refinenet3 = _make_fusion_block(features, use_bn)
self.scratch.refinenet4 = _make_fusion_block(features, use_bn)
head_features_1 = features
head_features_2 = 32
if nclass > 1:
self.scratch.output_conv = nn.Sequential(
nn.Conv2d(head_features_1, head_features_1, kernel_size=3, stride=1, padding=1),
nn.ReLU(True),
nn.Conv2d(head_features_1, nclass, kernel_size=1, stride=1, padding=0),
)
else:
self.scratch.output_conv1 = nn.Conv2d(
head_features_1, head_features_1 // 2, kernel_size=3, stride=1, padding=1
)
self.scratch.output_conv2 = nn.Sequential(
nn.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1),
nn.ReLU(True),
nn.Conv2d(head_features_2, 1, kernel_size=1, stride=1, padding=0),
nn.ReLU(True),
nn.Identity(),
)
def forward(self, out_features, patch_h, patch_w):
out = []
for i, x in enumerate(out_features):
if self.use_clstoken:
x, cls_token = x[0], x[1]
readout = cls_token.unsqueeze(1).expand_as(x)
x = self.readout_projects[i](torch.cat((x, readout), -1))
else:
x = x[0]
x = x.permute(0, 2, 1).reshape((x.shape[0], x.shape[-1], patch_h, patch_w))
x = self.projects[i](x)
x = self.resize_layers[i](x)
out.append(x)
layer_1, layer_2, layer_3, layer_4 = out
layer_1_rn = self.scratch.layer1_rn(layer_1)
layer_2_rn = self.scratch.layer2_rn(layer_2)
layer_3_rn = self.scratch.layer3_rn(layer_3)
layer_4_rn = self.scratch.layer4_rn(layer_4)
path_4 = self.scratch.refinenet4(layer_4_rn, size=layer_3_rn.shape[2:])
path_3 = self.scratch.refinenet3(path_4, layer_3_rn, size=layer_2_rn.shape[2:])
path_2 = self.scratch.refinenet2(path_3, layer_2_rn, size=layer_1_rn.shape[2:])
path_1 = self.scratch.refinenet1(path_2, layer_1_rn)
out = self.scratch.output_conv1(path_1)
out = F.interpolate(out, (int(patch_h * 14), int(patch_w * 14)), mode="bilinear", align_corners=True)
out = self.scratch.output_conv2(out)
return out
class DPT_DINOv2(nn.Module):
def __init__(
self,
features,
out_channels,
encoder="vitl",
use_bn=False,
use_clstoken=False,
):
super(DPT_DINOv2, self).__init__()
assert encoder in ["vits", "vitb", "vitl"]
# # in case the Internet connection is not stable, please load the DINOv2 locally
# if use_local:
# self.pretrained = torch.hub.load(
# torchhub_path / "facebookresearch_dinov2_main",
# "dinov2_{:}14".format(encoder),
# source="local",
# pretrained=False,
# )
# else:
# self.pretrained = torch.hub.load(
# "facebookresearch/dinov2",
# "dinov2_{:}14".format(encoder),
# )
self.pretrained = torch.hub.load(
"facebookresearch/dinov2",
"dinov2_{:}14".format(encoder),
)
dim = self.pretrained.blocks[0].attn.qkv.in_features
self.depth_head = DPTHead(1, dim, features, out_channels=out_channels, use_bn=use_bn, use_clstoken=use_clstoken)
def forward(self, x):
h, w = x.shape[-2:]
features = self.pretrained.get_intermediate_layers(x, 4, return_class_token=True)
patch_h, patch_w = h // 14, w // 14
depth = self.depth_head(features, patch_h, patch_w)
depth = F.interpolate(depth, size=(h, w), mode="bilinear", align_corners=True)
depth = F.relu(depth)
return depth.squeeze(1)

View File

@ -0,0 +1,227 @@
import math
import cv2
import numpy as np
import torch
import torch.nn.functional as F
def apply_min_size(sample, size, image_interpolation_method=cv2.INTER_AREA):
"""Rezise the sample to ensure the given size. Keeps aspect ratio.
Args:
sample (dict): sample
size (tuple): image size
Returns:
tuple: new size
"""
shape = list(sample["disparity"].shape)
if shape[0] >= size[0] and shape[1] >= size[1]:
return sample
scale = [0, 0]
scale[0] = size[0] / shape[0]
scale[1] = size[1] / shape[1]
scale = max(scale)
shape[0] = math.ceil(scale * shape[0])
shape[1] = math.ceil(scale * shape[1])
# resize
sample["image"] = cv2.resize(sample["image"], tuple(shape[::-1]), interpolation=image_interpolation_method)
sample["disparity"] = cv2.resize(sample["disparity"], tuple(shape[::-1]), interpolation=cv2.INTER_NEAREST)
sample["mask"] = cv2.resize(
sample["mask"].astype(np.float32),
tuple(shape[::-1]),
interpolation=cv2.INTER_NEAREST,
)
sample["mask"] = sample["mask"].astype(bool)
return tuple(shape)
class Resize(object):
"""Resize sample to given size (width, height)."""
def __init__(
self,
width,
height,
resize_target=True,
keep_aspect_ratio=False,
ensure_multiple_of=1,
resize_method="lower_bound",
image_interpolation_method=cv2.INTER_AREA,
):
"""Init.
Args:
width (int): desired output width
height (int): desired output height
resize_target (bool, optional):
True: Resize the full sample (image, mask, target).
False: Resize image only.
Defaults to True.
keep_aspect_ratio (bool, optional):
True: Keep the aspect ratio of the input sample.
Output sample might not have the given width and height, and
resize behaviour depends on the parameter 'resize_method'.
Defaults to False.
ensure_multiple_of (int, optional):
Output width and height is constrained to be multiple of this parameter.
Defaults to 1.
resize_method (str, optional):
"lower_bound": Output will be at least as large as the given size.
"upper_bound": Output will be at max as large as the given size. (Output size might be smaller
than given size.)
"minimal": Scale as least as possible. (Output size might be smaller than given size.)
Defaults to "lower_bound".
"""
self.__width = width
self.__height = height
self.__resize_target = resize_target
self.__keep_aspect_ratio = keep_aspect_ratio
self.__multiple_of = ensure_multiple_of
self.__resize_method = resize_method
self.__image_interpolation_method = image_interpolation_method
def constrain_to_multiple_of(self, x, min_val=0, max_val=None):
y = (np.round(x / self.__multiple_of) * self.__multiple_of).astype(int)
if max_val is not None and y > max_val:
y = (np.floor(x / self.__multiple_of) * self.__multiple_of).astype(int)
if y < min_val:
y = (np.ceil(x / self.__multiple_of) * self.__multiple_of).astype(int)
return y
def get_size(self, width, height):
# determine new height and width
scale_height = self.__height / height
scale_width = self.__width / width
if self.__keep_aspect_ratio:
if self.__resize_method == "lower_bound":
# scale such that output size is lower bound
if scale_width > scale_height:
# fit width
scale_height = scale_width
else:
# fit height
scale_width = scale_height
elif self.__resize_method == "upper_bound":
# scale such that output size is upper bound
if scale_width < scale_height:
# fit width
scale_height = scale_width
else:
# fit height
scale_width = scale_height
elif self.__resize_method == "minimal":
# scale as least as possbile
if abs(1 - scale_width) < abs(1 - scale_height):
# fit width
scale_height = scale_width
else:
# fit height
scale_width = scale_height
else:
raise ValueError(f"resize_method {self.__resize_method} not implemented")
if self.__resize_method == "lower_bound":
new_height = self.constrain_to_multiple_of(scale_height * height, min_val=self.__height)
new_width = self.constrain_to_multiple_of(scale_width * width, min_val=self.__width)
elif self.__resize_method == "upper_bound":
new_height = self.constrain_to_multiple_of(scale_height * height, max_val=self.__height)
new_width = self.constrain_to_multiple_of(scale_width * width, max_val=self.__width)
elif self.__resize_method == "minimal":
new_height = self.constrain_to_multiple_of(scale_height * height)
new_width = self.constrain_to_multiple_of(scale_width * width)
else:
raise ValueError(f"resize_method {self.__resize_method} not implemented")
return (new_width, new_height)
def __call__(self, sample):
width, height = self.get_size(sample["image"].shape[1], sample["image"].shape[0])
# resize sample
sample["image"] = cv2.resize(
sample["image"],
(width, height),
interpolation=self.__image_interpolation_method,
)
if self.__resize_target:
if "disparity" in sample:
sample["disparity"] = cv2.resize(
sample["disparity"],
(width, height),
interpolation=cv2.INTER_NEAREST,
)
if "depth" in sample:
sample["depth"] = cv2.resize(sample["depth"], (width, height), interpolation=cv2.INTER_NEAREST)
if "semseg_mask" in sample:
# sample["semseg_mask"] = cv2.resize(
# sample["semseg_mask"], (width, height), interpolation=cv2.INTER_NEAREST
# )
sample["semseg_mask"] = F.interpolate(
torch.from_numpy(sample["semseg_mask"]).float()[None, None, ...], (height, width), mode="nearest"
).numpy()[0, 0]
if "mask" in sample:
sample["mask"] = cv2.resize(
sample["mask"].astype(np.float32),
(width, height),
interpolation=cv2.INTER_NEAREST,
)
# sample["mask"] = sample["mask"].astype(bool)
# print(sample['image'].shape, sample['depth'].shape)
return sample
class NormalizeImage(object):
"""Normlize image by given mean and std."""
def __init__(self, mean, std):
self.__mean = mean
self.__std = std
def __call__(self, sample):
sample["image"] = (sample["image"] - self.__mean) / self.__std
return sample
class PrepareForNet(object):
"""Prepare sample for usage as network input."""
def __init__(self):
pass
def __call__(self, sample):
image = np.transpose(sample["image"], (2, 0, 1))
sample["image"] = np.ascontiguousarray(image).astype(np.float32)
if "mask" in sample:
sample["mask"] = sample["mask"].astype(np.float32)
sample["mask"] = np.ascontiguousarray(sample["mask"])
if "depth" in sample:
depth = sample["depth"].astype(np.float32)
sample["depth"] = np.ascontiguousarray(depth)
if "semseg_mask" in sample:
sample["semseg_mask"] = sample["semseg_mask"].astype(np.float32)
sample["semseg_mask"] = np.ascontiguousarray(sample["semseg_mask"])
return sample

View File

@ -6,7 +6,6 @@ import { Provider } from 'react-redux';
import ThemeLocaleProvider from '../src/app/components/ThemeLocaleProvider';
import { $baseUrl } from '../src/app/store/nanostores/baseUrl';
import { createStore } from '../src/app/store/store';
import { Container } from '@chakra-ui/react';
// TODO: Disabled for IDE performance issues with our translation JSON
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore

View File

@ -52,21 +52,12 @@
}
},
"dependencies": {
"@chakra-ui/anatomy": "^2.2.2",
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/layout": "^2.3.1",
"@chakra-ui/portal": "^2.1.0",
"@chakra-ui/react": "^2.8.2",
"@chakra-ui/react-use-size": "^2.1.0",
"@chakra-ui/styled-system": "^2.9.2",
"@chakra-ui/theme-tools": "^2.1.2",
"@dagrejs/graphlib": "^2.1.13",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@fontsource-variable/inter": "^5.0.16",
"@invoke-ai/ui": "0.0.10",
"@invoke-ai/ui": "0.0.13",
"@mantine/form": "6.0.21",
"@nanostores/react": "^0.7.1",
"@reduxjs/toolkit": "2.0.1",

View File

@ -10,30 +10,12 @@ patchedDependencies:
path: patches/reselect@5.0.1.patch
dependencies:
'@chakra-ui/anatomy':
specifier: ^2.2.2
version: 2.2.2
'@chakra-ui/icons':
specifier: ^2.1.1
version: 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/layout':
specifier: ^2.3.1
version: 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/portal':
specifier: ^2.1.0
version: 2.1.0(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/react':
specifier: ^2.8.2
version: 2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/react-use-size':
specifier: ^2.1.0
version: 2.1.0(react@18.2.0)
'@chakra-ui/styled-system':
specifier: ^2.9.2
version: 2.9.2
'@chakra-ui/theme-tools':
specifier: ^2.1.2
version: 2.1.2(@chakra-ui/styled-system@2.9.2)
'@dagrejs/graphlib':
specifier: ^2.1.13
version: 2.1.13
@ -43,18 +25,12 @@ dependencies:
'@dnd-kit/utilities':
specifier: ^3.2.2
version: 3.2.2(react@18.2.0)
'@emotion/react':
specifier: ^11.11.3
version: 11.11.3(@types/react@18.2.48)(react@18.2.0)
'@emotion/styled':
specifier: ^11.11.0
version: 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.48)(react@18.2.0)
'@fontsource-variable/inter':
specifier: ^5.0.16
version: 5.0.16
'@invoke-ai/ui':
specifier: 0.0.10
version: 0.0.10(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0)
specifier: 0.0.13
version: 0.0.13(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.16)(@internationalized/date@3.5.1)(@types/react@18.2.48)(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
'@mantine/form':
specifier: 6.0.21
version: 6.0.21(react@18.2.0)
@ -356,6 +332,92 @@ packages:
'@jridgewell/trace-mapping': 0.3.21
dev: true
/@ark-ui/anatomy@1.3.0(@internationalized/date@3.5.1):
resolution: {integrity: sha512-1yG2MrzUlix6KthjQMCNiHnkXrWwEdFAX6D+HqGJaNu0XvaGul2J+wDNtjsdX+gxiWu1nXXEEOAWlFVYMUf65w==}
dependencies:
'@zag-js/accordion': 0.32.1
'@zag-js/anatomy': 0.32.1
'@zag-js/avatar': 0.32.1
'@zag-js/carousel': 0.32.1
'@zag-js/checkbox': 0.32.1
'@zag-js/color-picker': 0.32.1
'@zag-js/color-utils': 0.32.1
'@zag-js/combobox': 0.32.1
'@zag-js/date-picker': 0.32.1
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.1)
'@zag-js/dialog': 0.32.1
'@zag-js/editable': 0.32.1
'@zag-js/file-upload': 0.32.1
'@zag-js/hover-card': 0.32.1
'@zag-js/menu': 0.32.1
'@zag-js/number-input': 0.32.1
'@zag-js/pagination': 0.32.1
'@zag-js/pin-input': 0.32.1
'@zag-js/popover': 0.32.1
'@zag-js/presence': 0.32.1
'@zag-js/progress': 0.32.1
'@zag-js/radio-group': 0.32.1
'@zag-js/rating-group': 0.32.1
'@zag-js/select': 0.32.1
'@zag-js/slider': 0.32.1
'@zag-js/splitter': 0.32.1
'@zag-js/switch': 0.32.1
'@zag-js/tabs': 0.32.1
'@zag-js/tags-input': 0.32.1
'@zag-js/toast': 0.32.1
'@zag-js/toggle-group': 0.32.1
'@zag-js/tooltip': 0.32.1
transitivePeerDependencies:
- '@internationalized/date'
dev: false
/@ark-ui/react@1.3.0(@internationalized/date@3.5.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-JHjNoIX50+mUCTaEGMjfGQWGGi31pKsV646jZJlR/1xohpYJigzg8BvO97cTsVk8fwtur+cm11gz3Nf7f5QUnA==}
peerDependencies:
react: '>=18.0.0'
react-dom: '>=18.0.0'
dependencies:
'@ark-ui/anatomy': 1.3.0(@internationalized/date@3.5.1)
'@zag-js/accordion': 0.32.1
'@zag-js/avatar': 0.32.1
'@zag-js/carousel': 0.32.1
'@zag-js/checkbox': 0.32.1
'@zag-js/color-picker': 0.32.1
'@zag-js/color-utils': 0.32.1
'@zag-js/combobox': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/date-picker': 0.32.1
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.1)
'@zag-js/dialog': 0.32.1
'@zag-js/editable': 0.32.1
'@zag-js/file-upload': 0.32.1
'@zag-js/hover-card': 0.32.1
'@zag-js/menu': 0.32.1
'@zag-js/number-input': 0.32.1
'@zag-js/pagination': 0.32.1
'@zag-js/pin-input': 0.32.1
'@zag-js/popover': 0.32.1
'@zag-js/presence': 0.32.1
'@zag-js/progress': 0.32.1
'@zag-js/radio-group': 0.32.1
'@zag-js/rating-group': 0.32.1
'@zag-js/react': 0.32.1(react-dom@18.2.0)(react@18.2.0)
'@zag-js/select': 0.32.1
'@zag-js/slider': 0.32.1
'@zag-js/splitter': 0.32.1
'@zag-js/switch': 0.32.1
'@zag-js/tabs': 0.32.1
'@zag-js/tags-input': 0.32.1
'@zag-js/toast': 0.32.1
'@zag-js/toggle-group': 0.32.1
'@zag-js/tooltip': 0.32.1
'@zag-js/types': 0.32.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies:
- '@internationalized/date'
dev: false
/@arthurgeron/eslint-plugin-react-usememo@2.2.3:
resolution: {integrity: sha512-YJG+8hULmhHAxztaANswpa9hWNqEOSvbZcbd6R/JQzyNlEZ49Xh97kqZGuJGZ74rrmULckEO1m3Jh5ctqrGA2A==}
dependencies:
@ -2940,7 +3002,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.6
'@babel/runtime': 7.23.8
'@emotion/babel-plugin': 11.11.0
'@emotion/is-prop-valid': 1.2.1
'@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0)
@ -3629,7 +3691,6 @@ packages:
resolution: {integrity: sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==}
dependencies:
'@floating-ui/utils': 0.2.1
dev: true
/@floating-ui/dom@1.5.3:
resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==}
@ -3643,7 +3704,6 @@ packages:
dependencies:
'@floating-ui/core': 1.5.3
'@floating-ui/utils': 0.2.1
dev: true
/@floating-ui/react-dom@2.0.6(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-IB8aCRFxr8nFkdYZgH+Otd9EVQPJoynxeFRGTB8voPoZMRWo8XjYuCRgpI1btvuKY69XMiLnW+ym7zoBHM90Rw==}
@ -3662,7 +3722,6 @@ packages:
/@floating-ui/utils@0.2.1:
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
dev: true
/@fontsource-variable/inter@5.0.16:
resolution: {integrity: sha512-k+BUNqksTL+AN+o+OV7ILeiE9B5M5X+/jA7LWvCwjbV9ovXTqZyKRhA/x7uYv/ml8WQ0XNLBM7cRFIx4jW0/hg==}
@ -3688,31 +3747,26 @@ packages:
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
dev: true
/@invoke-ai/ui@0.0.10(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0):
resolution: {integrity: sha512-e3cX3g1xap57mkMfjsNznN6V9YS8qUTpSiyjSSr80HEsD3NjXxCoL+Ik6y/Na/KwXgcK00jM6H+xF6ahJFobvw==}
/@internationalized/date@3.5.1:
resolution: {integrity: sha512-LUQIfwU9e+Fmutc/DpRTGXSdgYZLBegi4wygCWDSVmUdLTaMHsQyASDiJtREwanwKuQLq0hY76fCJ9J/9I2xOQ==}
dependencies:
'@swc/helpers': 0.5.3
dev: false
/@internationalized/number@3.5.0:
resolution: {integrity: sha512-ZY1BW8HT9WKYvaubbuqXbbDdHhOUMfE2zHHFJeTppid0S+pc8HtdIxFxaYMsGjCb4UsF+MEJ4n2TfU7iHnUK8w==}
dependencies:
'@swc/helpers': 0.5.3
dev: false
/@invoke-ai/ui@0.0.13(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@fontsource-variable/inter@5.0.16)(@internationalized/date@3.5.1)(@types/react@18.2.48)(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-X4Txij2dMnzPUXTPhorBHezByJQ/ceyHxCM+zZ0gpFsSyXUieOFWjaSu+dAVpghS9y0dxFQGayHvNyX6VsX/PA==}
peerDependencies:
'@chakra-ui/anatomy': ^2.2.2
'@chakra-ui/icons': ^2.1.1
'@chakra-ui/layout': ^2.3.1
'@chakra-ui/portal': ^2.1.0
'@chakra-ui/react': ^2.8.2
'@chakra-ui/styled-system': ^2.9.2
'@chakra-ui/theme-tools': ^2.1.2
'@emotion/react': ^11.11.3
'@emotion/styled': ^11.11.0
'@fontsource-variable/inter': ^5.0.16
'@nanostores/react': ^0.7.1
chakra-react-select: ^4.7.6
framer-motion: ^10.18.0
lodash-es: ^4.17.21
nanostores: ^0.9.5
overlayscrollbars: ^2.4.6
overlayscrollbars-react: ^0.5.3
react: ^18.2.0
react-dom: ^18.2.0
react-i18next: ^14.0.0
react-select: ^5.8.0
dependencies:
'@ark-ui/react': 1.3.0(@internationalized/date@3.5.1)(react-dom@18.2.0)(react@18.2.0)
'@chakra-ui/anatomy': 2.2.2
'@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
'@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
@ -3734,6 +3788,17 @@ packages:
react-dom: 18.2.0(react@18.2.0)
react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
react-select: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
transitivePeerDependencies:
- '@chakra-ui/form-control'
- '@chakra-ui/icon'
- '@chakra-ui/media-query'
- '@chakra-ui/menu'
- '@chakra-ui/spinner'
- '@chakra-ui/system'
- '@internationalized/date'
- '@types/react'
- i18next
- react-native
dev: false
/@isaacs/cliui@8.0.2:
@ -5719,6 +5784,12 @@ packages:
resolution: {integrity: sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==}
dev: true
/@swc/helpers@0.5.3:
resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==}
dependencies:
tslib: 2.6.2
dev: false
/@swc/types@0.1.5:
resolution: {integrity: sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==}
dev: true
@ -6679,20 +6750,567 @@ packages:
tslib: 1.14.1
dev: true
/@zag-js/accordion@0.32.1:
resolution: {integrity: sha512-16beDVpEhXFQsQRMZLmHFruhGphSprJ5XrRu6+OM2U7aTulo1w3ENUd9uI+mIs4oTVO66lYI4Lp+dFcT2UUIYA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/anatomy@0.32.1:
resolution: {integrity: sha512-bR+tfFfkbxwhBzGGjEQG+RUnbeCjMx7tWJxykGnGdVLwAh0wKTQBEfHEOCOQh5qU8RhKUieqemAdvc7oP3Tp4w==}
dev: false
/@zag-js/aria-hidden@0.32.1:
resolution: {integrity: sha512-kznwxvUUHDax8Kd7YNVVCzQcwGARTRaZNOcIkw7MTLE8g/pU+C4pYkwR9iqA7/8imGfjYrZfSsQqZRTb4bkS0g==}
dependencies:
'@zag-js/dom-query': 0.32.1
dev: false
/@zag-js/auto-resize@0.32.1:
resolution: {integrity: sha512-MO6N5gPs2xDKbFgrakn6LDWv1GgN8uhfwpsqchLJX+EaZVvLIz8cXFD+jDv3RjK+5GRWV4mIF+26SXuHRSt9Ug==}
dependencies:
'@zag-js/dom-query': 0.32.1
dev: false
/@zag-js/avatar@0.32.1:
resolution: {integrity: sha512-5P+95pkMX2Na4yljN1etdgYyA+3HPORjWKn0Y3JamkYIAqJwRFO+taEdSm/xcRkuT6aGA3luheUowjt8wZssyA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/mutation-observer': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/carousel@0.32.1:
resolution: {integrity: sha512-S7dUrPtiLr42Fa+S3O18kqKVqSu2yuk67bqGDtppIZSaFOugYHK4feBkZqjKw+eF12NVRRVO2j+A40d3MvxbSA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/checkbox@0.32.1:
resolution: {integrity: sha512-5reRreGyDZ5IlBNd5m1QrYXCehVIl/pmfKMEcAfad5DcgCaHGv5j76eahxbKln/8TEdwz4eWzBrqNtwSkKL5+w==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/collection@0.32.1:
resolution: {integrity: sha512-dAzcVQ/n+xAYoxWB/65/CQinv66RNVuq5ig0fEYszBqP+HjFnOpeGkIrEvP+bFI38hFEViiGtfr6oGAsVByOVQ==}
dev: false
/@zag-js/color-picker@0.32.1:
resolution: {integrity: sha512-ov3FC+c2WBYmEGRXWFVb2jih2Ecejj5JqBjDL9iMLBs2KNY9jnpvtH7WnZbijNY+RMDBj+C/DNI7K2NVaamSIA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/color-utils': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/tabbable': 0.32.1
'@zag-js/text-selection': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/color-utils@0.32.1:
resolution: {integrity: sha512-AzupfOD7oD0mE+H9roTzwnLqtw1wYiJGOQKLPAwdwPQdznJUQD6sMOpxR/6RBuITVTm8Bl12Mr4+7s29LVJruw==}
dependencies:
'@zag-js/numeric-range': 0.32.1
dev: false
/@zag-js/combobox@0.32.1:
resolution: {integrity: sha512-skz2C5UxLD5JoYNP4hcPaQJu2cW7vycKqjDNI9ZtygSkZHOHx+JxpYiACBnr1vqzXatIOuDQm/HUuWW9yOT4eA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/aria-hidden': 0.32.1
'@zag-js/collection': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/mutation-observer': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/core@0.32.1:
resolution: {integrity: sha512-F9F7920/CisoLWALQACIhqbMvemgbv86qBULJ+UEe+a/9XgGwPh9UGn/H/q5EWkNpgEapz2b3pl3ONgKmXsK1A==}
dependencies:
'@zag-js/store': 0.32.1
klona: 2.0.6
dev: false
/@zag-js/date-picker@0.32.1:
resolution: {integrity: sha512-n/hYmF+/R4+NuyfPRzCgeuLT6LJihKSuKzK29STPWy3sC/tBBHiqhNv1/4UKbatHUJXdBW2XF+N8Rw08RffcFQ==}
dependencies:
'@internationalized/date': 3.5.1
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/date-utils': 0.32.1(@internationalized/date@3.5.1)
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/live-region': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/text-selection': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/date-utils@0.32.1(@internationalized/date@3.5.1):
resolution: {integrity: sha512-dbBDRSVr5pRUw3rXndyGuSshZiWqQI5JQO4D2KIFGkXzorj6WzoOpcO910Z7AdM/9cCAMpCjUrka8d8o9BpJBg==}
peerDependencies:
'@internationalized/date': '>=3.0.0'
dependencies:
'@internationalized/date': 3.5.1
dev: false
/@zag-js/dialog@0.32.1:
resolution: {integrity: sha512-czp+qXcdAOM70SrvDo4gBpYZx6gS6HXyrpiptW3+EHa2eiCfc/Z2w+Nu+ZadOTEQGgNWlKlCLW7Ery0i9mMDsw==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/aria-hidden': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/remove-scroll': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
focus-trap: 7.5.4
dev: false
/@zag-js/dismissable@0.32.1:
resolution: {integrity: sha512-UIkG+9Eb5wrus2F2Dy4zqk0pwCV53sdnMYBxk9dpvDzBJHzW+InhVeg3UeKmPL8ELcYlhH/Bap99XCRJvxsXow==}
dependencies:
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/interact-outside': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/dom-event@0.32.1:
resolution: {integrity: sha512-wN6f5Kkf7C/YFN3wbEG3gUockSebyy1fPNL2BuL4C8PIP8vOD14hnHTzZWg5yYfO+veybIAL38r8I46C+bOVBQ==}
dependencies:
'@zag-js/text-selection': 0.32.1
'@zag-js/types': 0.32.1
dev: false
/@zag-js/dom-query@0.16.0:
resolution: {integrity: sha512-Oqhd6+biWyKnhKwFFuZrrf6lxBz2tX2pRQe6grUnYwO6HJ8BcbqZomy2lpOdr+3itlaUqx+Ywj5E5ZZDr/LBfQ==}
dev: false
/@zag-js/dom-query@0.32.1:
resolution: {integrity: sha512-u6hrQHQ0/dcUi6xJn8d2Mu1ClN4KZpPqOKrJFSaxadWjSy+x0qp48WY2CBQ6gZ3j8IwR/XjzU9bu9wY5jJfHgA==}
dev: false
/@zag-js/editable@0.32.1:
resolution: {integrity: sha512-QEGnfp2P9nWVp9vGNWtszspvQcF3KtBRToZrv5/DT30Mpo/uPDKtqijLs0SnB/W60ELzcIRhK4J9taGoK8O8uw==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/interact-outside': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/element-rect@0.32.1:
resolution: {integrity: sha512-tAmxgxU2LsByK8PIs/Cj6cBJ8xZCVXE9RoStxthhuPL7xKYUfZvFGuhHVOHTHd6sDKEqbj6K1ds/TGPuglIh4w==}
dev: false
/@zag-js/element-size@0.10.5:
resolution: {integrity: sha512-uQre5IidULANvVkNOBQ1tfgwTQcGl4hliPSe69Fct1VfYb2Fd0jdAcGzqQgPhfrXFpR62MxLPB7erxJ/ngtL8w==}
dev: false
/@zag-js/element-size@0.32.1:
resolution: {integrity: sha512-ACklufmJQpah2UqwZUlYFaKi6uWfZBeTghtbfYHcDfzRbg2Hni612v8L1JeS4vAgjeDpcdHQpXXR4AZSpGZgNw==}
dev: false
/@zag-js/file-upload@0.32.1:
resolution: {integrity: sha512-cD0NRIDof9Vv2DemmnYe9ZPZxOZ6b8XZl8eq4G0e8+WLYtnRXyEURl8Dw0QJpfdDPQaHnnD4CNxPTQcLgP+9Sg==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/file-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/file-utils@0.32.1:
resolution: {integrity: sha512-0PxTrljW51Lf9OCuYNlZuaLgF0v1NoVRzXa/osZ9HGceQjfo77R5G9u+/TP3u53W2PTxajEZ4eNzTibgpzNXFg==}
dev: false
/@zag-js/focus-visible@0.16.0:
resolution: {integrity: sha512-a7U/HSopvQbrDU4GLerpqiMcHKEkQkNPeDZJWz38cw/6Upunh41GjHetq5TB84hxyCaDzJ6q2nEdNoBQfC0FKA==}
dependencies:
'@zag-js/dom-query': 0.16.0
dev: false
/@zag-js/form-utils@0.32.1:
resolution: {integrity: sha512-OemLBlHCHHm7t8wNcf78FRudRA7FegSgsNEzAjrRTyx+lJztDyHRLaoyI1gCEIg+0Kzl2nMxjOl4MStGsDj8iw==}
dependencies:
'@zag-js/mutation-observer': 0.32.1
dev: false
/@zag-js/hover-card@0.32.1:
resolution: {integrity: sha512-k66YK0z0P4LuK78+jnRoUPxJiM9GA0sbEEz3oPlvcFVXMMwnRTPNIw1OjksfAPI+Nvgg7H6D3A+7HCdRI/oBjw==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/interact-outside@0.32.1:
resolution: {integrity: sha512-8zHuswfTAgfMCaQnp3N4WStvnL32VyxURafb21+mE4neAF/DaKfJHWnJpeUMG1Qh/eXsrMRBxVoX+nBMhHj9bg==}
dependencies:
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/tabbable': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/live-region@0.32.1:
resolution: {integrity: sha512-6/9QMLVZbTRh/G6MoJc/auN8r5vjdY9vUgNT680C2LOa2vnRR5/y0DkIpVgttNh1rSenQ/eLEYxp8hQF1rIYNw==}
dependencies:
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/menu@0.32.1:
resolution: {integrity: sha512-IPsTljVF0N9xTwub1cpGl3GAG5ttAq3h38PdZERREzT3qRgw4v3K/I1TG2vIiDXgJz8UZzUKox6ZYdU7UIAkRA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/mutation-observer': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/rect-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/mutation-observer@0.32.1:
resolution: {integrity: sha512-/hlObxGnhAaYYVnwRJC227md0M3kSE6mO24vkqVGwq2GglS+u4zbVcBBUuWgHdMML+ZjIQrZuVycCBMfVlHq0g==}
dev: false
/@zag-js/number-input@0.32.1:
resolution: {integrity: sha512-atyIOvoMITb4hZtQym7yD6I7grvPW83UeMFO8hCQg3HWwd2zR4+63mouWuyMoWb4QrzVFRVQBaU8OG5xGlknEw==}
dependencies:
'@internationalized/number': 3.5.0
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/mutation-observer': 0.32.1
'@zag-js/number-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/number-utils@0.32.1:
resolution: {integrity: sha512-x/nttU31TtFVTqFBM8e3ZH/0MCc+u15WAfk0rT6ESkoZcdb80rTzZVMokCKCUdpi/JdB1vjEeCLSnj+ig8oAIQ==}
dev: false
/@zag-js/numeric-range@0.32.1:
resolution: {integrity: sha512-1Qe2URTenlrdsWuArlnQ+v5bBH2mHZD3XsK6jYV+C2lgatVzdcoN4GCSNTiF7w+So6J+NTeLMkVHMGCW1Kzx1g==}
dev: false
/@zag-js/pagination@0.32.1:
resolution: {integrity: sha512-lhogzKxJnx5D2Xoni/xm5rkOuy15KWSxqBHVwe8+j5aSNqMy7+aRtEN2F2VQCDVL/v1fdciQvOCA9udm37kZ4w==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/pin-input@0.32.1:
resolution: {integrity: sha512-d18cCXKUr7INL0Xm5KyIoiTRSNsPXfIlIEMl2HrAvM3r70wtEag0PmiDNA5NS2tB4LnnX0XowchGB4HsdFS/ng==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/popover@0.32.1:
resolution: {integrity: sha512-B01if49v3crCjkvtSvIX4CBdT/475nj3DttOObc36s0YOxCEt3UihMITBD5JvIKwEqjZ6oU5t0sLcUYOqQ4f2A==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/aria-hidden': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/remove-scroll': 0.32.1
'@zag-js/tabbable': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
focus-trap: 7.5.4
dev: false
/@zag-js/popper@0.32.1:
resolution: {integrity: sha512-aQgogW1N4VreNACSQhXQoZeXtQQtB//FXUvt1CBnW2DtmZ6YkNnaAfn186Q2lkw2/T0chITRy3eYeviwMmMrqg==}
dependencies:
'@floating-ui/dom': 1.5.4
'@zag-js/dom-query': 0.32.1
'@zag-js/element-rect': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/presence@0.32.1:
resolution: {integrity: sha512-8189QMUf/L1dztAZdurx18ZwPyWlq58Mrd+GdATSaf8JstgrI1ovzVs606inQghWptKHMsH7dUIaV9UkhbSx3Q==}
dependencies:
'@zag-js/core': 0.32.1
'@zag-js/types': 0.32.1
dev: false
/@zag-js/progress@0.32.1:
resolution: {integrity: sha512-ClkQvNYnuIpKfAPUceZXY5E2m/3NnIm21cvHe4gAoJ88YdqEHd5rIRoHP63g8ET8Ct/2KkBRkgR+LrQnGQOomA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/radio-group@0.32.1:
resolution: {integrity: sha512-NvdSjwRF38qIh0oM68jERf71uiwV2JFTrGeQEs3EIqONzULwL6jR2p4P1wm3JJNBAkSYBKZMER5cVUUcqM3kjQ==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/element-rect': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/rating-group@0.32.1:
resolution: {integrity: sha512-RBaFRCw7P00bgTrEjUHT3h/OGRO8XmXKkQYqqhm1tsVbeTsT47iwHoc6XnMEiGBonaJDwN/J0oFasw7GNg5sow==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/react@0.32.1(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-b1SB7hXXv1K6CmXkcy5Y7mb0YRWkyvulyhK8VW5O5hIAPuGxOTx70psmVeZbmVzhjdORCiro9jKx8Ec0LfolFg==}
peerDependencies:
react: '>=18.0.0'
react-dom: '>=18.0.0'
dependencies:
'@zag-js/core': 0.32.1
'@zag-js/store': 0.32.1
'@zag-js/types': 0.32.1
proxy-compare: 2.5.1
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/@zag-js/rect-utils@0.32.1:
resolution: {integrity: sha512-cI07kgldjUZP+SLhXeG9VSl47nrENlC96Fs7jWcTfHj62rhdY8WsBJ0tiTztvwar9m1chwxXZwJowHN+nPIgDQ==}
dev: false
/@zag-js/remove-scroll@0.32.1:
resolution: {integrity: sha512-LyXt2rNMSKb9MKeJRyKTgpk4R7jdA+9kEQTSG5qyA94jo1og7FVgA1W/E+pNkdxDEk1VplL768VU6y7E/L3DHg==}
dependencies:
'@zag-js/dom-query': 0.32.1
dev: false
/@zag-js/select@0.32.1:
resolution: {integrity: sha512-jSzmTKCN1Fk/ZDDWM8TVGOtwgpYUDgyceegjYT+hW1mmEetu4tQcEvAr0557NOzh8akqLvcVFbg/kMj0IriKAA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/collection': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dismissable': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/mutation-observer': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/tabbable': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/slider@0.32.1:
resolution: {integrity: sha512-iZSB3Y8/Maakxem0Ha3rBRa8AyAplhN5K50Bgz+wsv0VEzNNUmK4QgaTWReWd6SfeTRpnC5ftKCcfM2aQrLm6g==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/element-size': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/numeric-range': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/splitter@0.32.1:
resolution: {integrity: sha512-NdHLUXtQAlnz6QpdPwcqZCqYul7LaVqsp0hgtXR2PN4HbH+VAaDfY76pUk6LBerUcykChGZvtM9U0A5FCo1x4A==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/number-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/store@0.32.1:
resolution: {integrity: sha512-hKwzpqAPljw06oOI+eO+Is2udpmY9GsGfmdoqvZVYoK4f5sawpZY9EC/84tbK9QKWUDTbFS+0Ujc254GUThmDA==}
dependencies:
proxy-compare: 2.5.1
dev: false
/@zag-js/switch@0.32.1:
resolution: {integrity: sha512-+5w/AtINA+jpORX1cuUrnyIFXrfjhqV7667EKK/zbPi0Pf1E10+TEihpfFjY6bgms9CSNWZVEb6w2f2C0PNBDA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
'@zag-js/visually-hidden': 0.32.1
dev: false
/@zag-js/tabbable@0.32.1:
resolution: {integrity: sha512-fMXtVgBiX7z3Qmdv+McrfihiSkqsDbNX2nn3e63L7jdy9ZpgnR3jG9BwUZvv7hvzkuOAFhhdKgBYYT+fkBavGg==}
dependencies:
'@zag-js/dom-query': 0.32.1
dev: false
/@zag-js/tabs@0.32.1:
resolution: {integrity: sha512-5l8/k2Pw9Kbfsvvx6HWcVqK7Ns7ca+nyPGLSZtZLMp/Zn2q3xSG32C1U3oDaYtQVIQSiEHdnMjw0C2v+CxGDMA==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/element-rect': 0.32.1
'@zag-js/tabbable': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/tags-input@0.32.1:
resolution: {integrity: sha512-oliLhiMpRNbWFixHF+Oe7hySQBp7NKtL/s8rN5dLT1G1GFRMzuuht/QnmL1h8EoGGpTwaaokMo4zl4uVzHbwyw==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/auto-resize': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/form-utils': 0.32.1
'@zag-js/interact-outside': 0.32.1
'@zag-js/live-region': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/text-selection@0.32.1:
resolution: {integrity: sha512-aK1uswWYF76PFoxGL+3HW/kth9uldFWSW4lOh89NfEcc6Ym7qS5B+P0HKJVM9DuQbihvQX9dyc9PvM7/LJTSRA==}
dependencies:
'@zag-js/dom-query': 0.32.1
dev: false
/@zag-js/toast@0.32.1:
resolution: {integrity: sha512-HrfVzFX7ANS9qOewCr8qOCbgko635bZxYKMv+ojjo4U/TtwkGb43+lVU7/qwZj0z18/OtXBH5YQjFwQZXg5x8g==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/toggle-group@0.32.1:
resolution: {integrity: sha512-MM1XI4J45rRCZiDHcMtZWud0+bWMu6IcMnrbd9oig330YAF3RzcjTlxX93YRY35F04OUMBq5el9qe3qc2vyMuw==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/tooltip@0.32.1:
resolution: {integrity: sha512-+rsmDYTELFBHoYKg5iKShGfRD3H9FJDaZRq915Uc9YnyePMXCnWRgnVp+lk3zI+FDgysQm67SDLRJsR24Iioqg==}
dependencies:
'@zag-js/anatomy': 0.32.1
'@zag-js/core': 0.32.1
'@zag-js/dom-event': 0.32.1
'@zag-js/dom-query': 0.32.1
'@zag-js/popper': 0.32.1
'@zag-js/types': 0.32.1
'@zag-js/utils': 0.32.1
dev: false
/@zag-js/types@0.32.1:
resolution: {integrity: sha512-BLfqb+im4vtXXJqhd2ZUg/4LquEd1qPt9XN56XVjudGDTftN8n3EDpuail7VKxdL59W4jR7wW8lvl4sSgrQKWw==}
dependencies:
csstype: 3.1.3
dev: false
/@zag-js/utils@0.32.1:
resolution: {integrity: sha512-jrcmWYcA3L6TO4fZbPFvpSGEy2Z/mbWt6bPQbmcVgq/BltSS0YxxfPl+eD+S/rZI9aneszwsr04Z5TpladFiVA==}
dev: false
/@zag-js/visually-hidden@0.32.1:
resolution: {integrity: sha512-Vzieo4vNulzY/0zqmVfeYW/LcFJp5xtEoyUgR1FBctH8uBPBRhTIEXxKtoMablW6/vccOVo7zcu0UrR5Vx+eYQ==}
dev: false
/accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@ -9074,6 +9692,12 @@ packages:
tslib: 2.6.2
dev: false
/focus-trap@7.5.4:
resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==}
dependencies:
tabbable: 6.2.0
dev: false
/for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
@ -11307,6 +11931,10 @@ packages:
ipaddr.js: 1.9.1
dev: true
/proxy-compare@2.5.1:
resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==}
dev: false
/proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: true
@ -11435,7 +12063,7 @@ packages:
peerDependencies:
react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.23.7
'@babel/runtime': 7.23.8
react: 18.2.0
dev: false
@ -11530,7 +12158,7 @@ packages:
'@types/react':
optional: true
dependencies:
'@babel/runtime': 7.23.7
'@babel/runtime': 7.23.8
'@types/react': 18.2.48
focus-lock: 1.0.0
prop-types: 15.8.1
@ -12777,6 +13405,10 @@ packages:
resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==}
dev: true
/tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
dev: false
/tapable@2.2.1:
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
engines: {node: '>=6'}

View File

@ -110,7 +110,28 @@
"somethingWentWrong": "Etwas ist schief gelaufen",
"copyError": "$t(gallery.copy) Fehler",
"input": "Eingabe",
"notInstalled": "Nicht $t(common.installed)"
"notInstalled": "Nicht $t(common.installed)",
"advancedOptions": "Erweiterte Einstellungen",
"alpha": "Alpha",
"red": "Rot",
"green": "Grün",
"blue": "Blau",
"delete": "Löschen",
"or": "oder",
"direction": "Richtung",
"free": "Frei",
"save": "Speichern",
"preferencesLabel": "Präferenzen",
"created": "Erstellt",
"prevPage": "Vorherige Seite",
"nextPage": "Nächste Seite",
"unknownError": "Unbekannter Fehler",
"unsaved": "Nicht gespeichert",
"aboutDesc": "Verwenden Sie Invoke für die Arbeit? Dann siehe hier:",
"localSystem": "Lokales System",
"orderBy": "Ordnen nach",
"saveAs": "Speicher als",
"updated": "Aktualisiert"
},
"gallery": {
"generations": "Erzeugungen",
@ -701,7 +722,8 @@
"invokeProgressBar": "Invoke Fortschrittsanzeige",
"mode": "Modus",
"resetUI": "$t(accessibility.reset) von UI",
"createIssue": "Ticket erstellen"
"createIssue": "Ticket erstellen",
"about": "Über"
},
"boards": {
"autoAddBoard": "Automatisches Hinzufügen zum Ordner",
@ -809,7 +831,8 @@
"canny": "Canny",
"hedDescription": "Ganzheitlich verschachtelte Kantenerkennung",
"scribble": "Scribble",
"maxFaces": "Maximal Anzahl Gesichter"
"maxFaces": "Maximal Anzahl Gesichter",
"resizeSimple": "Größe ändern (einfach)"
},
"queue": {
"status": "Status",
@ -999,5 +1022,27 @@
"selectLoRA": "Wählen ein LoRA aus",
"esrganModel": "ESRGAN Modell",
"addLora": "LoRA hinzufügen"
},
"accordions": {
"generation": {
"title": "Erstellung",
"modelTab": "Modell",
"conceptsTab": "Konzepte"
},
"image": {
"title": "Bild"
},
"advanced": {
"title": "Erweitert"
},
"control": {
"title": "Kontrolle",
"controlAdaptersTab": "Kontroll Adapter",
"ipTab": "Bild Beschreibung"
},
"compositing": {
"coherenceTab": "Kohärenzpass",
"infillTab": "Füllung"
}
}
}

View File

@ -224,6 +224,7 @@
"amult": "a_mult",
"autoConfigure": "Auto configure processor",
"balanced": "Balanced",
"base": "Base",
"beginEndStepPercent": "Begin / End Step Percentage",
"bgth": "bg_th",
"canny": "Canny",
@ -237,6 +238,8 @@
"controlMode": "Control Mode",
"crop": "Crop",
"delete": "Delete",
"depthAnything": "Depth Anything",
"depthAnythingDescription": "Depth map generation using the Depth Anything technique",
"depthMidas": "Depth (Midas)",
"depthMidasDescription": "Depth map generation using Midas",
"depthZoe": "Depth (Zoe)",
@ -256,6 +259,7 @@
"colorMapTileSize": "Tile Size",
"importImageFromCanvas": "Import Image From Canvas",
"importMaskFromCanvas": "Import Mask From Canvas",
"large": "Large",
"lineart": "Lineart",
"lineartAnime": "Lineart Anime",
"lineartAnimeDescription": "Anime-style lineart processing",
@ -268,6 +272,7 @@
"minConfidence": "Min Confidence",
"mlsd": "M-LSD",
"mlsdDescription": "Minimalist Line Segment Detector",
"modelSize": "Model Size",
"none": "None",
"noneDescription": "No processing applied",
"normalBae": "Normal BAE",
@ -288,6 +293,7 @@
"selectModel": "Select a model",
"setControlImageDimensions": "Set Control Image Dimensions To W/H",
"showAdvanced": "Show Advanced",
"small": "Small",
"toggleControlNet": "Toggle this ControlNet",
"w": "W",
"weight": "Weight",
@ -600,6 +606,10 @@
"desc": "Send current image to Image to Image",
"title": "Send To Image To Image"
},
"remixImage": {
"desc": "Use all parameters except seed from the current image",
"title": "Remix image"
},
"setParameters": {
"desc": "Use all parameters of the current image",
"title": "Set Parameters"
@ -1216,6 +1226,7 @@
"useCpuNoise": "Use CPU Noise",
"cpuNoise": "CPU Noise",
"gpuNoise": "GPU Noise",
"remixImage": "Remix Image",
"useInitImg": "Use Initial Image",
"usePrompt": "Use Prompt",
"useSeed": "Use Seed",
@ -1697,6 +1708,7 @@
"workflowLibrary": "Library",
"userWorkflows": "My Workflows",
"defaultWorkflows": "Default Workflows",
"projectWorkflows": "Project Workflows",
"openWorkflow": "Open Workflow",
"uploadWorkflow": "Load from File",
"deleteWorkflow": "Delete Workflow",
@ -1709,6 +1721,7 @@
"workflowSaved": "Workflow Saved",
"noRecentWorkflows": "No Recent Workflows",
"noUserWorkflows": "No User Workflows",
"noWorkflows": "No Workflows",
"noSystemWorkflows": "No System Workflows",
"problemLoading": "Problem Loading Workflows",
"loading": "Loading Workflows",

View File

@ -118,7 +118,14 @@
"advancedOptions": "Opzioni avanzate",
"free": "Libero",
"or": "o",
"preferencesLabel": "Preferenze"
"preferencesLabel": "Preferenze",
"red": "Rosso",
"aboutHeading": "Possiedi il tuo potere creativo",
"aboutDesc": "Utilizzi Invoke per lavoro? Guarda qui:",
"localSystem": "Sistema locale",
"green": "Verde",
"blue": "Blu",
"alpha": "Alfa"
},
"gallery": {
"generations": "Generazioni",
@ -521,7 +528,8 @@
"customConfigFileLocation": "Posizione del file di configurazione personalizzato",
"vaePrecision": "Precisione VAE",
"noModelSelected": "Nessun modello selezionato",
"conversionNotSupported": "Conversione non supportata"
"conversionNotSupported": "Conversione non supportata",
"configFile": "File di configurazione"
},
"parameters": {
"images": "Immagini",
@ -660,7 +668,9 @@
"lockAspectRatio": "Blocca proporzioni",
"swapDimensions": "Scambia dimensioni",
"aspect": "Aspetto",
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (potrebbe essere troppo grande)"
"setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (potrebbe essere troppo grande)",
"boxBlur": "Box",
"gaussianBlur": "Gaussian"
},
"settings": {
"models": "Modelli",
@ -794,7 +804,9 @@
"invalidUpload": "Caricamento non valido",
"problemDeletingWorkflow": "Problema durante l'eliminazione del flusso di lavoro",
"workflowDeleted": "Flusso di lavoro eliminato",
"problemRetrievingWorkflow": "Problema nel recupero del flusso di lavoro"
"problemRetrievingWorkflow": "Problema nel recupero del flusso di lavoro",
"resetInitialImage": "Reimposta l'immagine iniziale",
"uploadInitialImage": "Carica l'immagine iniziale"
},
"tooltip": {
"feature": {
@ -899,7 +911,8 @@
"loadMore": "Carica altro",
"mode": "Modalità",
"resetUI": "$t(accessibility.reset) l'Interfaccia Utente",
"createIssue": "Segnala un problema"
"createIssue": "Segnala un problema",
"about": "Informazioni"
},
"ui": {
"hideProgressImages": "Nascondi avanzamento immagini",

View File

@ -36,9 +36,9 @@ export const addModelSelectedListener = () => {
const newModel = result.data;
const { base_model } = newModel;
const newBaseModel = newModel.base_model;
const didBaseModelChange =
state.generation.model?.base_model !== base_model;
state.generation.model?.base_model !== newBaseModel;
if (didBaseModelChange) {
// we may need to reset some incompatible submodels
@ -46,7 +46,7 @@ export const addModelSelectedListener = () => {
// handle incompatible loras
forEach(state.lora.loras, (lora, id) => {
if (lora.base_model !== base_model) {
if (lora.base_model !== newBaseModel) {
dispatch(loraRemoved(id));
modelsCleared += 1;
}
@ -54,14 +54,14 @@ export const addModelSelectedListener = () => {
// handle incompatible vae
const { vae } = state.generation;
if (vae && vae.base_model !== base_model) {
if (vae && vae.base_model !== newBaseModel) {
dispatch(vaeSelected(null));
modelsCleared += 1;
}
// handle incompatible controlnets
selectControlAdapterAll(state.controlAdapters).forEach((ca) => {
if (ca.model?.base_model !== base_model) {
if (ca.model?.base_model !== newBaseModel) {
dispatch(
controlAdapterIsEnabledChanged({ id: ca.id, isEnabled: false })
);

View File

@ -46,14 +46,14 @@ export const addDynamicPromptsListener = () => {
if (cachedPrompts) {
dispatch(promptsChanged(cachedPrompts.prompts));
dispatch(parsingErrorChanged(cachedPrompts.error));
return;
}
if (!getShouldProcessPrompt(state.generation.positivePrompt)) {
if (state.dynamicPrompts.isLoading) {
dispatch(isLoadingChanged(false));
}
dispatch(promptsChanged([state.generation.positivePrompt]));
dispatch(parsingErrorChanged(undefined));
dispatch(isErrorChanged(false));
return;
}
@ -78,7 +78,6 @@ export const addDynamicPromptsListener = () => {
dispatch(promptsChanged(res.prompts));
dispatch(parsingErrorChanged(res.error));
dispatch(isErrorChanged(false));
dispatch(isLoadingChanged(false));
} catch {
dispatch(isErrorChanged(true));
dispatch(isLoadingChanged(false));

View File

@ -1,24 +1,35 @@
import type { ChakraProps } from '@invoke-ai/ui';
import { Box, Flex } from '@invoke-ai/ui';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { getOverlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties, PropsWithChildren } from 'react';
import { memo } from 'react';
import { memo, useMemo } from 'react';
type Props = PropsWithChildren & {
maxHeight?: ChakraProps['maxHeight'];
overflowX?: 'hidden' | 'scroll';
overflowY?: 'hidden' | 'scroll';
};
const styles: CSSProperties = { height: '100%', width: '100%' };
const ScrollableContent = ({ children, maxHeight }: Props) => {
const ScrollableContent = ({
children,
maxHeight,
overflowX = 'hidden',
overflowY = 'scroll',
}: Props) => {
const overlayscrollbarsOptions = useMemo(
() => getOverlayScrollbarsParams(overflowX, overflowY).options,
[overflowX, overflowY]
);
return (
<Flex w="full" h="full" maxHeight={maxHeight} position="relative">
<Box position="absolute" top={0} left={0} right={0} bottom={0}>
<OverlayScrollbarsComponent
defer
style={styles}
options={overlayScrollbarsParams.options}
options={overlayscrollbarsOptions}
>
{children}
</OverlayScrollbarsComponent>

View File

@ -1,3 +1,4 @@
import { cloneDeep, merge } from 'lodash-es';
import { ClickScrollPlugin, OverlayScrollbars } from 'overlayscrollbars';
import type { UseOverlayScrollbarsParams } from 'overlayscrollbars-react';
@ -16,3 +17,12 @@ export const overlayScrollbarsParams: UseOverlayScrollbarsParams = {
overflow: { x: 'hidden' },
},
};
export const getOverlayScrollbarsParams = (
overflowX: 'hidden' | 'scroll' = 'hidden',
overflowY: 'hidden' | 'scroll' = 'scroll'
) => {
const params = cloneDeep(overlayScrollbarsParams);
merge(params, { options: { overflow: { y: overflowY, x: overflowX } } });
return params;
};

View File

@ -1,9 +1,12 @@
import type { FormLabelProps } from '@invoke-ai/ui';
import {
Box,
Button,
ButtonGroup,
Checkbox,
Flex,
FormControl,
FormControlGroup,
FormLabel,
IconButton,
Popover,
@ -33,6 +36,10 @@ import {
PiTrashSimpleFill,
} from 'react-icons/pi';
const formLabelProps: FormLabelProps = {
flexGrow: 1,
};
const IAICanvasMaskOptions = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
@ -124,40 +131,44 @@ const IAICanvasMaskOptions = () => {
<PopoverContent>
<PopoverBody>
<Flex direction="column" gap={2}>
<FormControl>
<FormLabel>{`${t('unifiedCanvas.enableMask')} (H)`}</FormLabel>
<Checkbox
isChecked={isMaskEnabled}
onChange={handleToggleEnableMask}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.preserveMaskedArea')}</FormLabel>
<Checkbox
isChecked={shouldPreserveMaskedArea}
onChange={handleChangePreserveMaskedArea}
/>
</FormControl>
<FormControlGroup formLabelProps={formLabelProps}>
<FormControl>
<FormLabel>{`${t('unifiedCanvas.enableMask')} (H)`}</FormLabel>
<Checkbox
isChecked={isMaskEnabled}
onChange={handleToggleEnableMask}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.preserveMaskedArea')}</FormLabel>
<Checkbox
isChecked={shouldPreserveMaskedArea}
onChange={handleChangePreserveMaskedArea}
/>
</FormControl>
</FormControlGroup>
<Box pt={2} pb={2}>
<IAIColorPicker
color={maskColor}
onChange={handleChangeMaskColor}
/>
</Box>
<Button
size="sm"
leftIcon={<PiFloppyDiskBackFill />}
onClick={handleSaveMask}
>
{t('unifiedCanvas.saveMask')}
</Button>
<Button
size="sm"
leftIcon={<PiTrashSimpleFill />}
onClick={handleClearMask}
>
{t('unifiedCanvas.clearMask')}
</Button>
<ButtonGroup isAttached={false}>
<Button
size="sm"
leftIcon={<PiFloppyDiskBackFill />}
onClick={handleSaveMask}
>
{t('unifiedCanvas.saveMask')}
</Button>
<Button
size="sm"
leftIcon={<PiTrashSimpleFill />}
onClick={handleClearMask}
>
{t('unifiedCanvas.clearMask')}
</Button>
</ButtonGroup>
</Flex>
</PopoverBody>
</PopoverContent>

View File

@ -1,7 +1,9 @@
import type { FormLabelProps } from '@invoke-ai/ui';
import {
Checkbox,
Flex,
FormControl,
FormControlGroup,
FormLabel,
IconButton,
Popover,
@ -28,6 +30,10 @@ import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import { PiGearSixBold } from 'react-icons/pi';
const formLabelProps: FormLabelProps = {
flexGrow: 1,
};
const IAICanvasSettingsButtonPopover = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
@ -122,69 +128,73 @@ const IAICanvasSettingsButtonPopover = () => {
<PopoverContent>
<PopoverBody>
<Flex direction="column" gap={2}>
<FormControl>
<FormLabel>{t('unifiedCanvas.showIntermediates')}</FormLabel>
<Checkbox
isChecked={shouldShowIntermediates}
onChange={handleChangeShouldShowIntermediates}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.showGrid')}</FormLabel>
<Checkbox
isChecked={shouldShowGrid}
onChange={handleChangeShouldShowGrid}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.snapToGrid')}</FormLabel>
<Checkbox
isChecked={shouldSnapToGrid}
onChange={handleChangeShouldSnapToGrid}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.darkenOutsideSelection')}</FormLabel>
<Checkbox
isChecked={shouldDarkenOutsideBoundingBox}
onChange={handleChangeShouldDarkenOutsideBoundingBox}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.autoSaveToGallery')}</FormLabel>
<Checkbox
isChecked={shouldAutoSave}
onChange={handleChangeShouldAutoSave}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.saveBoxRegionOnly')}</FormLabel>
<Checkbox
isChecked={shouldCropToBoundingBoxOnSave}
onChange={handleChangeShouldCropToBoundingBoxOnSave}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.limitStrokesToBox')}</FormLabel>
<Checkbox
isChecked={shouldRestrictStrokesToBox}
onChange={handleChangeShouldRestrictStrokesToBox}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.showCanvasDebugInfo')}</FormLabel>
<Checkbox
isChecked={shouldShowCanvasDebugInfo}
onChange={handleChangeShouldShowCanvasDebugInfo}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.antialiasing')}</FormLabel>
<Checkbox
isChecked={shouldAntialias}
onChange={handleChangeShouldAntialias}
/>
</FormControl>
<FormControlGroup formLabelProps={formLabelProps}>
<FormControl>
<FormLabel>{t('unifiedCanvas.showIntermediates')}</FormLabel>
<Checkbox
isChecked={shouldShowIntermediates}
onChange={handleChangeShouldShowIntermediates}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.showGrid')}</FormLabel>
<Checkbox
isChecked={shouldShowGrid}
onChange={handleChangeShouldShowGrid}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.snapToGrid')}</FormLabel>
<Checkbox
isChecked={shouldSnapToGrid}
onChange={handleChangeShouldSnapToGrid}
/>
</FormControl>
<FormControl>
<FormLabel>
{t('unifiedCanvas.darkenOutsideSelection')}
</FormLabel>
<Checkbox
isChecked={shouldDarkenOutsideBoundingBox}
onChange={handleChangeShouldDarkenOutsideBoundingBox}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.autoSaveToGallery')}</FormLabel>
<Checkbox
isChecked={shouldAutoSave}
onChange={handleChangeShouldAutoSave}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.saveBoxRegionOnly')}</FormLabel>
<Checkbox
isChecked={shouldCropToBoundingBoxOnSave}
onChange={handleChangeShouldCropToBoundingBoxOnSave}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.limitStrokesToBox')}</FormLabel>
<Checkbox
isChecked={shouldRestrictStrokesToBox}
onChange={handleChangeShouldRestrictStrokesToBox}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.showCanvasDebugInfo')}</FormLabel>
<Checkbox
isChecked={shouldShowCanvasDebugInfo}
onChange={handleChangeShouldShowCanvasDebugInfo}
/>
</FormControl>
<FormControl>
<FormLabel>{t('unifiedCanvas.antialiasing')}</FormLabel>
<Checkbox
isChecked={shouldAntialias}
onChange={handleChangeShouldAntialias}
/>
</FormControl>
</FormControlGroup>
<ClearCanvasHistoryButtonModal />
</Flex>
</PopoverBody>

View File

@ -276,9 +276,9 @@ const IAICanvasToolChooserOptions = () => {
</Flex>
<Box w="full" pt={2} pb={2}>
<IAIColorPicker
withNumberInput={true}
color={brushColor}
onChange={handleChangeBrushColor}
withNumberInput
/>
</Box>
</Flex>

View File

@ -1,9 +1,9 @@
import { ChevronUpIcon } from '@chakra-ui/icons';
import {
Box,
Flex,
FormControl,
FormLabel,
Icon,
IconButton,
Switch,
} from '@invoke-ai/ui';
@ -19,7 +19,7 @@ import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import type { ChangeEvent } from 'react';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiCopyBold, PiTrashSimpleBold } from 'react-icons/pi';
import { PiCaretUpBold, PiCopyBold, PiTrashSimpleBold } from 'react-icons/pi';
import { useToggle } from 'react-use';
import ControlAdapterImagePreview from './ControlAdapterImagePreview';
@ -130,7 +130,9 @@ const ControlAdapterConfig = (props: { id: string; number: number }) => {
onClick={toggleIsExpanded}
variant="ghost"
icon={
<ChevronUpIcon
<Icon
boxSize={4}
as={PiCaretUpBold}
transform={isExpanded ? 'rotate(0deg)' : 'rotate(180deg)'}
transitionProperty="common"
transitionDuration="normal"

View File

@ -5,6 +5,7 @@ import { memo } from 'react';
import CannyProcessor from './processors/CannyProcessor';
import ColorMapProcessor from './processors/ColorMapProcessor';
import ContentShuffleProcessor from './processors/ContentShuffleProcessor';
import DepthAnyThingProcessor from './processors/DepthAnyThingProcessor';
import HedProcessor from './processors/HedProcessor';
import LineartAnimeProcessor from './processors/LineartAnimeProcessor';
import LineartProcessor from './processors/LineartProcessor';
@ -48,6 +49,16 @@ const ControlAdapterProcessorComponent = ({ id }: Props) => {
);
}
if (processorNode.type === 'depth_anything_image_processor') {
return (
<DepthAnyThingProcessor
controlNetId={id}
processorNode={processorNode}
isEnabled={isEnabled}
/>
);
}
if (processorNode.type === 'hed_image_processor') {
return (
<HedProcessor

View File

@ -0,0 +1,110 @@
import type { ComboboxOnChange } from '@invoke-ai/ui';
import {
Combobox,
CompositeNumberInput,
CompositeSlider,
FormControl,
FormLabel,
} from '@invoke-ai/ui';
import { useProcessorNodeChanged } from 'features/controlAdapters/components/hooks/useProcessorNodeChanged';
import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
import type {
DepthAnythingModelSize,
RequiredDepthAnythingImageProcessorInvocation,
} from 'features/controlAdapters/store/types';
import { isDepthAnythingModelSize } from 'features/controlAdapters/store/types';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import ProcessorWrapper from './common/ProcessorWrapper';
const DEFAULTS = CONTROLNET_PROCESSORS.midas_depth_image_processor
.default as RequiredDepthAnythingImageProcessorInvocation;
type Props = {
controlNetId: string;
processorNode: RequiredDepthAnythingImageProcessorInvocation;
isEnabled: boolean;
};
const DepthAnythingProcessor = (props: Props) => {
const { controlNetId, processorNode, isEnabled } = props;
const { model_size, resolution } = processorNode;
const processorChanged = useProcessorNodeChanged();
const { t } = useTranslation();
const handleModelSizeChange = useCallback<ComboboxOnChange>(
(v) => {
if (!isDepthAnythingModelSize(v?.value)) {
return;
}
processorChanged(controlNetId, {
model_size: v.value,
});
},
[controlNetId, processorChanged]
);
const options: { label: string; value: DepthAnythingModelSize }[] = useMemo(
() => [
{ label: t('controlnet.small'), value: 'small' },
{ label: t('controlnet.base'), value: 'base' },
{ label: t('controlnet.large'), value: 'large' },
],
[t]
);
const value = useMemo(
() => options.filter((o) => o.value === model_size)[0],
[options, model_size]
);
const handleResolutionChange = useCallback(
(v: number) => {
processorChanged(controlNetId, { resolution: v });
},
[controlNetId, processorChanged]
);
const handleResolutionDefaultChange = useCallback(() => {
processorChanged(controlNetId, { resolution: 512 });
}, [controlNetId, processorChanged]);
return (
<ProcessorWrapper>
<FormControl isDisabled={!isEnabled}>
<FormLabel>{t('controlnet.modelSize')}</FormLabel>
<Combobox
value={value}
defaultInputValue={DEFAULTS.model_size}
options={options}
onChange={handleModelSizeChange}
/>
</FormControl>
<FormControl isDisabled={!isEnabled}>
<FormLabel>{t('controlnet.imageResolution')}</FormLabel>
<CompositeSlider
value={resolution}
onChange={handleResolutionChange}
defaultValue={DEFAULTS.resolution}
min={64}
max={4096}
step={64}
marks
onReset={handleResolutionDefaultChange}
/>
<CompositeNumberInput
value={resolution}
onChange={handleResolutionChange}
defaultValue={DEFAULTS.resolution}
min={64}
max={4096}
step={64}
/>
</FormControl>
</ProcessorWrapper>
);
};
export default memo(DepthAnythingProcessor);

View File

@ -83,6 +83,22 @@ export const CONTROLNET_PROCESSORS: ControlNetProcessorsDict = {
f: 256,
},
},
depth_anything_image_processor: {
type: 'depth_anything_image_processor',
get label() {
return i18n.t('controlnet.depthAnything');
},
get description() {
return i18n.t('controlnet.depthAnythingDescription');
},
default: {
id: 'depth_anything_image_processor',
type: 'depth_anything_image_processor',
model_size: 'small',
resolution: 512,
offload: false,
},
},
hed_image_processor: {
type: 'hed_image_processor',
get label() {
@ -245,7 +261,7 @@ export const CONTROLNET_MODEL_DEFAULT_PROCESSORS: {
} = {
canny: 'canny_image_processor',
mlsd: 'mlsd_image_processor',
depth: 'midas_depth_image_processor',
depth: 'depth_anything_image_processor',
bae: 'normalbae_image_processor',
sketch: 'pidi_image_processor',
scribble: 'lineart_image_processor',

View File

@ -10,6 +10,7 @@ import type {
CannyImageProcessorInvocation,
ColorMapImageProcessorInvocation,
ContentShuffleImageProcessorInvocation,
DepthAnythingImageProcessorInvocation,
HedImageProcessorInvocation,
LineartAnimeImageProcessorInvocation,
LineartImageProcessorInvocation,
@ -31,6 +32,7 @@ export type ControlAdapterProcessorNode =
| CannyImageProcessorInvocation
| ColorMapImageProcessorInvocation
| ContentShuffleImageProcessorInvocation
| DepthAnythingImageProcessorInvocation
| HedImageProcessorInvocation
| LineartAnimeImageProcessorInvocation
| LineartImageProcessorInvocation
@ -73,6 +75,20 @@ export type RequiredContentShuffleImageProcessorInvocation = O.Required<
'type' | 'detect_resolution' | 'image_resolution' | 'w' | 'h' | 'f'
>;
/**
* The DepthAnything processor node, with parameters flagged as required
*/
export type RequiredDepthAnythingImageProcessorInvocation = O.Required<
DepthAnythingImageProcessorInvocation,
'type' | 'model_size' | 'resolution' | 'offload'
>;
export const zDepthAnythingModelSize = z.enum(['large', 'base', 'small']);
export type DepthAnythingModelSize = z.infer<typeof zDepthAnythingModelSize>;
export const isDepthAnythingModelSize = (
v: unknown
): v is DepthAnythingModelSize => zDepthAnythingModelSize.safeParse(v).success;
/**
* The HED processor node, with parameters flagged as required
*/
@ -161,6 +177,7 @@ export type RequiredControlAdapterProcessorNode =
| RequiredCannyImageProcessorInvocation
| RequiredColorMapImageProcessorInvocation
| RequiredContentShuffleImageProcessorInvocation
| RequiredDepthAnythingImageProcessorInvocation
| RequiredHedImageProcessorInvocation
| RequiredLineartAnimeImageProcessorInvocation
| RequiredLineartImageProcessorInvocation
@ -219,6 +236,22 @@ export const isContentShuffleImageProcessorInvocation = (
return false;
};
/**
* Type guard for DepthAnythingImageProcessorInvocation
*/
export const isDepthAnythingImageProcessorInvocation = (
obj: unknown
): obj is DepthAnythingImageProcessorInvocation => {
if (
isObject(obj) &&
'type' in obj &&
obj.type === 'depth_anything_image_processor'
) {
return true;
}
return false;
};
/**
* Type guard for HedImageprocessorInvocation
*/

View File

@ -61,7 +61,12 @@ const ParamDynamicPromptsPreview = () => {
}
return (
<FormControl orientation="vertical" w="full" h="full">
<FormControl
orientation="vertical"
w="full"
h="full"
isInvalid={Boolean(parsingError || isError)}
>
<InformationalPopover feature="dynamicPrompts" inPortal={false}>
<FormLabel>{label}</FormLabel>
</InformationalPopover>

View File

@ -13,7 +13,11 @@ const loadingStyles: SystemStyleObject = {
export const ShowDynamicPromptsPreviewButton = memo(() => {
const { t } = useTranslation();
const isLoading = useAppSelector((s) => s.dynamicPrompts.isLoading);
const isError = useAppSelector((s) =>
Boolean(s.dynamicPrompts.isError || s.dynamicPrompts.parsingError)
);
const { isOpen, onOpen } = useDynamicPromptsModal();
return (
<Tooltip
label={
@ -30,6 +34,7 @@ export const ShowDynamicPromptsPreviewButton = memo(() => {
icon={<BsBracesAsterisk />}
onClick={onOpen}
sx={isLoading ? loadingStyles : undefined}
colorScheme={isError && !isLoading ? 'error' : 'base'}
/>
</Tooltip>
);

View File

@ -47,6 +47,7 @@ export const dynamicPromptsSlice = createSlice({
},
promptsChanged: (state, action: PayloadAction<string[]>) => {
state.prompts = action.payload;
state.isLoading = false;
},
parsingErrorChanged: (
state,

View File

@ -1,3 +1,3 @@
const hasOpenCloseCurlyBracesRegex = /.*\{.*\}.*/;
const hasOpenCloseCurlyBracesRegex = /.*\{[\s\S]*\}.*/;
export const getShouldProcessPrompt = (prompt: string): boolean =>
hasOpenCloseCurlyBracesRegex.test(prompt);

View File

@ -58,7 +58,7 @@ const BoardsSearch = () => {
<IconButton
onClick={clearBoardSearch}
size="sm"
variant="ghost"
variant="link"
aria-label={t('boards.clearSearch')}
icon={<PiXBold />}
/>

View File

@ -32,6 +32,7 @@ import { memo, useCallback } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useTranslation } from 'react-i18next';
import {
PiArrowsCounterClockwiseBold,
PiAsteriskBold,
PiDotsThreeOutlineFill,
PiFlowArrowBold,
@ -129,6 +130,16 @@ const CurrentImageButtons = () => {
useHotkeys('p', handleUsePrompt, [metadata]);
const handleRemixImage = useCallback(() => {
// Recalls all metadata parameters except seed
recallAllParameters({
...metadata,
seed: undefined,
});
}, [metadata, recallAllParameters]);
useHotkeys('r', handleRemixImage, [metadata]);
const handleUseSize = useCallback(() => {
recallWidthAndHeight(metadata?.width, metadata?.height);
}, [metadata?.width, metadata?.height, recallWidthAndHeight]);
@ -231,6 +242,14 @@ const CurrentImageButtons = () => {
onClick={handleLoadWorkflow}
isLoading={getAndLoadEmbeddedWorkflowResult.isLoading}
/>
<IconButton
isLoading={isLoadingMetadata}
icon={<PiArrowsCounterClockwiseBold />}
tooltip={`${t('parameters.remixImage')} (R)`}
aria-label={`${t('parameters.remixImage')} (R)`}
isDisabled={!metadata?.positive_prompt}
onClick={handleRemixImage}
/>
<IconButton
isLoading={isLoadingMetadata}
icon={<PiQuotesBold />}

View File

@ -1,7 +1,7 @@
import { ChevronUpIcon } from '@chakra-ui/icons';
import { Button, Flex, Spacer } from '@invoke-ai/ui';
import { Button, Flex, Icon, Spacer } from '@invoke-ai/ui';
import { useAppSelector } from 'app/store/storeHooks';
import { memo, useMemo } from 'react';
import { PiCaretUpBold } from 'react-icons/pi';
import { useBoardName } from 'services/api/hooks/useBoardName';
type Props = {
@ -36,7 +36,9 @@ const GalleryBoardName = (props: Props) => {
<Spacer />
{formattedBoardName}
<Spacer />
<ChevronUpIcon
<Icon
as={PiCaretUpBold}
boxSize={4}
transform={isOpen ? 'rotate(0deg)' : 'rotate(180deg)'}
transitionProperty="common"
transitionDuration="normal"

View File

@ -1,8 +1,10 @@
import type { FormLabelProps } from '@invoke-ai/ui';
import {
Checkbox,
CompositeSlider,
Flex,
FormControl,
FormControlGroup,
FormLabel,
IconButton,
Popover,
@ -24,6 +26,10 @@ import { RiSettings4Fill } from 'react-icons/ri';
import BoardAutoAddSelect from './Boards/BoardAutoAddSelect';
const formLabelProps: FormLabelProps = {
flexGrow: 1,
};
const GallerySettingsPopover = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
@ -78,20 +84,22 @@ const GallerySettingsPopover = () => {
defaultValue={90}
/>
</FormControl>
<FormControl>
<FormLabel>{t('gallery.autoSwitchNewImages')}</FormLabel>
<Switch
isChecked={shouldAutoSwitch}
onChange={handleChangeAutoSwitch}
/>
</FormControl>
<FormControl>
<FormLabel>{t('gallery.autoAssignBoardOnClick')}</FormLabel>
<Checkbox
isChecked={autoAssignBoardOnClick}
onChange={handleChangeAutoAssignBoardOnClick}
/>
</FormControl>
<FormControlGroup formLabelProps={formLabelProps}>
<FormControl>
<FormLabel>{t('gallery.autoSwitchNewImages')}</FormLabel>
<Switch
isChecked={shouldAutoSwitch}
onChange={handleChangeAutoSwitch}
/>
</FormControl>
<FormControl>
<FormLabel>{t('gallery.autoAssignBoardOnClick')}</FormLabel>
<Checkbox
isChecked={autoAssignBoardOnClick}
onChange={handleChangeAutoAssignBoardOnClick}
/>
</FormControl>
</FormControlGroup>
<BoardAutoAddSelect />
</Flex>
</PopoverBody>

View File

@ -24,6 +24,7 @@ import { memo, useCallback } from 'react';
import { flushSync } from 'react-dom';
import { useTranslation } from 'react-i18next';
import {
PiArrowsCounterClockwiseBold,
PiAsteriskBold,
PiCopyBold,
PiDownloadSimpleBold,
@ -127,6 +128,14 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
recallAllParameters(metadata);
}, [metadata, recallAllParameters]);
const handleRemixImage = useCallback(() => {
// Recalls all metadata parameters except seed
recallAllParameters({
...metadata,
seed: undefined,
});
}, [metadata, recallAllParameters]);
const handleChangeBoard = useCallback(() => {
dispatch(imagesToChangeSelected([imageDTO]));
dispatch(isModalOpenChanged(true));
@ -187,6 +196,19 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
>
{t('nodes.loadWorkflow')}
</MenuItem>
<MenuItem
icon={
isLoadingMetadata ? <SpinnerIcon /> : <PiArrowsCounterClockwiseBold />
}
onClickCapture={handleRemixImage}
isDisabled={
isLoadingMetadata ||
(metadata?.positive_prompt === undefined &&
metadata?.negative_prompt === undefined)
}
>
{t('parameters.remixImage')}
</MenuItem>
<MenuItem
icon={isLoadingMetadata ? <SpinnerIcon /> : <PiQuotesBold />}
onClickCapture={handleRecallPrompt}

View File

@ -1,5 +1,5 @@
import { Box, Flex, IconButton, Tooltip } from '@invoke-ai/ui';
import { overlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { getOverlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
import { isString } from 'lodash-es';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
import type { CSSProperties } from 'react';
@ -15,6 +15,11 @@ type Props = {
withCopy?: boolean;
};
const overlayscrollbarsOptions = getOverlayScrollbarsParams(
'scroll',
'scroll'
).options;
const DataViewer = (props: Props) => {
const { label, data, fileName, withDownload = true, withCopy = true } = props;
const dataString = useMemo(
@ -60,7 +65,7 @@ const DataViewer = (props: Props) => {
<OverlayScrollbarsComponent
defer
style={overlayScrollbarsStyles}
options={overlayScrollbarsParams.options}
options={overlayscrollbarsOptions}
>
<pre>{dataString}</pre>
</OverlayScrollbarsComponent>

View File

@ -1,5 +1,4 @@
import { ExternalLinkIcon } from '@chakra-ui/icons';
import { Flex, IconButton, Link, Text, Tooltip } from '@invoke-ai/ui';
import { ExternalLink, Flex, IconButton, Text, Tooltip } from '@invoke-ai/ui';
import { memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { IoArrowUndoCircleOutline } from 'react-icons/io5';
@ -67,9 +66,7 @@ const ImageMetadataItem = ({
{label}:
</Text>
{isLink ? (
<Link href={value.toString()} isExternal wordBreak="break-all">
{value.toString()} <ExternalLinkIcon mx="2px" />
</Link>
<ExternalLink href={value.toString()} label={value.toString()} />
) : (
<Text overflowY="scroll" wordBreak="break-all">
{value.toString()}

View File

@ -1,13 +1,11 @@
import { ExternalLinkIcon } from '@chakra-ui/icons';
import {
ExternalLink,
Flex,
Link,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
} from '@invoke-ai/ui';
import { IAINoContentFallback } from 'common/components/IAIImageFallback';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
@ -46,13 +44,7 @@ const ImageMetadataViewer = ({ image }: ImageMetadataViewerProps) => {
position="absolute"
overflow="hidden"
>
<Flex gap={2}>
<Text fontWeight="semibold">{t('common.file')}:</Text>
<Link href={image.image_url} isExternal maxW="calc(100% - 3rem)">
{image.image_name}
<ExternalLinkIcon mx="2px" />
</Link>
</Flex>
<ExternalLink href={image.image_url} label={image.image_name} />
<Tabs
variant="line"

View File

@ -122,13 +122,10 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
<FormErrorMessage>{errors.model_name?.message}</FormErrorMessage>
)}
</FormControl>
<FormControl>
<FormLabel>{t('modelManager.baseModel')}</FormLabel>
<BaseModelSelect<DiffusersModelConfig>
control={control}
name="base_model"
/>
</FormControl>
<BaseModelSelect<DiffusersModelConfig>
control={control}
name="base_model"
/>
<FormControl isInvalid={Boolean(errors.path)}>
<FormLabel>{t('modelManager.modelLocation')}</FormLabel>
<Input
@ -137,7 +134,7 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
value.trim().length > 0 || 'Must provide a path',
onBlur,
})}
/>{' '}
/>
{errors.path?.message && (
<FormErrorMessage>{errors.path?.message}</FormErrorMessage>
)}

View File

@ -109,28 +109,28 @@ const InputField = ({ nodeId, fieldName }: Props) => {
return (
<InputFieldWrapper shouldDim={shouldDim}>
<FieldContextMenu nodeId={nodeId} fieldName={fieldName} kind="input">
{(ref) => (
<FormControl
ref={ref}
isInvalid={isMissingInput}
isDisabled={isConnected}
orientation="vertical"
px={2}
>
<Flex flexDir="column" w="full" gap={1}>
<FormControl
isInvalid={isMissingInput}
isDisabled={isConnected}
orientation="vertical"
px={2}
>
<Flex flexDir="column" w="full" gap={1}>
<FieldContextMenu nodeId={nodeId} fieldName={fieldName} kind="input">
{(ref) => (
<EditableFieldTitle
ref={ref}
nodeId={nodeId}
fieldName={fieldName}
kind="input"
isMissingInput={isMissingInput}
withTooltip
/>
<InputFieldRenderer nodeId={nodeId} fieldName={fieldName} />
</Flex>
</FormControl>
)}
</FieldContextMenu>
)}
</FieldContextMenu>
<InputFieldRenderer nodeId={nodeId} fieldName={fieldName} />
</Flex>
</FormControl>
{fieldTemplate.input !== 'direct' && (
<FieldHandle

View File

@ -1,8 +1,8 @@
import { ChevronUpIcon } from '@chakra-ui/icons';
import { IconButton } from '@invoke-ai/ui';
import { Icon, IconButton } from '@invoke-ai/ui';
import { useAppDispatch } from 'app/store/storeHooks';
import { nodeIsOpenChanged } from 'features/nodes/store/nodesSlice';
import { memo, useCallback } from 'react';
import { PiCaretUpBold } from 'react-icons/pi';
import { useUpdateNodeInternals } from 'reactflow';
interface Props {
@ -29,7 +29,8 @@ const NodeCollapseButton = ({ nodeId, isOpen }: Props) => {
h={8}
variant="link"
icon={
<ChevronUpIcon
<Icon
as={PiCaretUpBold}
transform={isOpen ? 'rotate(0deg)' : 'rotate(180deg)'}
transitionProperty="common"
transitionDuration="normal"

View File

@ -65,7 +65,7 @@ export const zModelType = z.enum([
'controlnet',
'embedding',
]);
export const zModelName = z.string().trim().min(1);
export const zModelName = z.string().min(3);
export const zModelIdentifier = z.object({
model_name: zModelName,
base_model: zBaseModel,

View File

@ -15,7 +15,7 @@ export type XYPosition = z.infer<typeof zXYPosition>;
export const zDimension = z.number().gt(0).nullish();
export type Dimension = z.infer<typeof zDimension>;
export const zWorkflowCategory = z.enum(['user', 'default']);
export const zWorkflowCategory = z.enum(['user', 'default', 'project']);
export type WorkflowCategory = z.infer<typeof zWorkflowCategory>;
// #endregion

View File

@ -34,7 +34,7 @@ import {
modelSelected,
} from 'features/parameters/store/actions';
import {
heightChanged,
heightRecalled,
selectGenerationSlice,
setCfgRescaleMultiplier,
setCfgScale,
@ -45,8 +45,9 @@ import {
setSeed,
setSteps,
vaeSelected,
widthChanged,
widthRecalled,
} from 'features/parameters/store/generationSlice';
import type { ParameterModel } from 'features/parameters/types/parameterSchemas';
import {
isParameterCFGRescaleMultiplier,
isParameterCFGScale,
@ -372,7 +373,7 @@ export const useRecallParameters = () => {
parameterNotSetToast();
return;
}
dispatch(widthChanged(width));
dispatch(widthRecalled(width));
parameterSetToast();
},
[dispatch, parameterSetToast, parameterNotSetToast]
@ -387,7 +388,7 @@ export const useRecallParameters = () => {
parameterNotSetToast();
return;
}
dispatch(heightChanged(height));
dispatch(heightRecalled(height));
parameterSetToast();
},
[dispatch, parameterSetToast, parameterNotSetToast]
@ -406,8 +407,8 @@ export const useRecallParameters = () => {
allParameterNotSetToast();
return;
}
dispatch(heightChanged(height));
dispatch(widthChanged(width));
dispatch(heightRecalled(height));
dispatch(widthRecalled(width));
allParameterSetToast();
},
[dispatch, allParameterSetToast, allParameterNotSetToast]
@ -480,7 +481,7 @@ export const useRecallParameters = () => {
const { data: loraModels } = useGetLoRAModelsQuery(undefined);
const prepareLoRAMetadataItem = useCallback(
(loraMetadataItem: LoRAMetadataItem) => {
(loraMetadataItem: LoRAMetadataItem, newModel?: ParameterModel) => {
if (!isParameterLoRAModel(loraMetadataItem.lora)) {
return { lora: null, error: 'Invalid LoRA model' };
}
@ -499,7 +500,7 @@ export const useRecallParameters = () => {
}
const isCompatibleBaseModel =
matchingLoRA?.base_model === model?.base_model;
matchingLoRA?.base_model === (newModel ?? model)?.base_model;
if (!isCompatibleBaseModel) {
return {
@ -510,7 +511,7 @@ export const useRecallParameters = () => {
return { lora: matchingLoRA, error: null };
},
[loraModels, model?.base_model]
[loraModels, model]
);
const recallLoRA = useCallback(
@ -538,7 +539,10 @@ export const useRecallParameters = () => {
const { data: controlNetModels } = useGetControlNetModelsQuery(undefined);
const prepareControlNetMetadataItem = useCallback(
(controlnetMetadataItem: ControlNetMetadataItem) => {
(
controlnetMetadataItem: ControlNetMetadataItem,
newModel?: ParameterModel
) => {
if (!isParameterControlNetModel(controlnetMetadataItem.control_model)) {
return { controlnet: null, error: 'Invalid ControlNet model' };
}
@ -565,7 +569,7 @@ export const useRecallParameters = () => {
}
const isCompatibleBaseModel =
matchingControlNetModel?.base_model === model?.base_model;
matchingControlNetModel?.base_model === (newModel ?? model)?.base_model;
if (!isCompatibleBaseModel) {
return {
@ -600,7 +604,7 @@ export const useRecallParameters = () => {
return { controlnet, error: null };
},
[controlNetModels, model?.base_model]
[controlNetModels, model]
);
const recallControlNet = useCallback(
@ -631,7 +635,10 @@ export const useRecallParameters = () => {
const { data: t2iAdapterModels } = useGetT2IAdapterModelsQuery(undefined);
const prepareT2IAdapterMetadataItem = useCallback(
(t2iAdapterMetadataItem: T2IAdapterMetadataItem) => {
(
t2iAdapterMetadataItem: T2IAdapterMetadataItem,
newModel?: ParameterModel
) => {
if (
!isParameterControlNetModel(t2iAdapterMetadataItem.t2i_adapter_model)
) {
@ -659,7 +666,7 @@ export const useRecallParameters = () => {
}
const isCompatibleBaseModel =
matchingT2IAdapterModel?.base_model === model?.base_model;
matchingT2IAdapterModel?.base_model === (newModel ?? model)?.base_model;
if (!isCompatibleBaseModel) {
return {
@ -690,7 +697,7 @@ export const useRecallParameters = () => {
return { t2iAdapter, error: null };
},
[model?.base_model, t2iAdapterModels]
[model, t2iAdapterModels]
);
const recallT2IAdapter = useCallback(
@ -721,7 +728,10 @@ export const useRecallParameters = () => {
const { data: ipAdapterModels } = useGetIPAdapterModelsQuery(undefined);
const prepareIPAdapterMetadataItem = useCallback(
(ipAdapterMetadataItem: IPAdapterMetadataItem) => {
(
ipAdapterMetadataItem: IPAdapterMetadataItem,
newModel?: ParameterModel
) => {
if (!isParameterIPAdapterModel(ipAdapterMetadataItem?.ip_adapter_model)) {
return { ipAdapter: null, error: 'Invalid IP Adapter model' };
}
@ -746,7 +756,7 @@ export const useRecallParameters = () => {
}
const isCompatibleBaseModel =
matchingIPAdapterModel?.base_model === model?.base_model;
matchingIPAdapterModel?.base_model === (newModel ?? model)?.base_model;
if (!isCompatibleBaseModel) {
return {
@ -768,7 +778,7 @@ export const useRecallParameters = () => {
return { ipAdapter, error: null };
},
[ipAdapterModels, model?.base_model]
[ipAdapterModels, model]
);
const recallIPAdapter = useCallback(
@ -840,6 +850,13 @@ export const useRecallParameters = () => {
t2iAdapters,
} = metadata;
let newModel: ParameterModel | undefined = undefined;
if (isParameterModel(model)) {
newModel = model;
dispatch(modelSelected(model));
}
if (isParameterCFGScale(cfg_scale)) {
dispatch(setCfgScale(cfg_scale));
}
@ -848,10 +865,6 @@ export const useRecallParameters = () => {
dispatch(setCfgRescaleMultiplier(cfg_rescale_multiplier));
}
if (isParameterModel(model)) {
dispatch(modelSelected(model));
}
if (isParameterPositivePrompt(positive_prompt)) {
dispatch(setPositivePrompt(positive_prompt));
}
@ -880,11 +893,11 @@ export const useRecallParameters = () => {
}
if (isParameterWidth(width)) {
dispatch(widthChanged(width));
dispatch(widthRecalled(width));
}
if (isParameterHeight(height)) {
dispatch(heightChanged(height));
dispatch(heightRecalled(height));
}
if (isParameterStrength(strength)) {
@ -953,7 +966,7 @@ export const useRecallParameters = () => {
dispatch(lorasCleared());
loras?.forEach((lora) => {
const result = prepareLoRAMetadataItem(lora);
const result = prepareLoRAMetadataItem(lora, newModel);
if (result.lora) {
dispatch(loraRecalled({ ...result.lora, weight: lora.weight }));
}
@ -961,21 +974,21 @@ export const useRecallParameters = () => {
dispatch(controlAdaptersReset());
controlnets?.forEach((controlnet) => {
const result = prepareControlNetMetadataItem(controlnet);
const result = prepareControlNetMetadataItem(controlnet, newModel);
if (result.controlnet) {
dispatch(controlAdapterRecalled(result.controlnet));
}
});
ipAdapters?.forEach((ipAdapter) => {
const result = prepareIPAdapterMetadataItem(ipAdapter);
const result = prepareIPAdapterMetadataItem(ipAdapter, newModel);
if (result.ipAdapter) {
dispatch(controlAdapterRecalled(result.ipAdapter));
}
});
t2iAdapters?.forEach((t2iAdapter) => {
const result = prepareT2IAdapterMetadataItem(t2iAdapter);
const result = prepareT2IAdapterMetadataItem(t2iAdapter, newModel);
if (result.t2iAdapter) {
dispatch(controlAdapterRecalled(result.t2iAdapter));
}

View File

@ -175,8 +175,15 @@ export const generationSlice = createSlice({
}
// Clamp ClipSkip Based On Selected Model
const { maxClip } = CLIP_SKIP_MAP[newModel.base_model];
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
// TODO(psyche): remove this special handling when https://github.com/invoke-ai/InvokeAI/issues/4583 is resolved
// WIP PR here: https://github.com/invoke-ai/InvokeAI/pull/4624
if (newModel.base_model === 'sdxl') {
// We don't support clip skip for SDXL yet - it's not in the graphs
state.clipSkip = 0;
} else {
const { maxClip } = CLIP_SKIP_MAP[newModel.base_model];
state.clipSkip = clamp(state.clipSkip, 0, maxClip);
}
if (action.meta.previousModel?.base_model === newModel.base_model) {
// The base model hasn't changed, we don't need to optimize the size
@ -223,6 +230,18 @@ export const generationSlice = createSlice({
heightChanged: (state, action: PayloadAction<number>) => {
state.height = action.payload;
},
widthRecalled: (state, action: PayloadAction<number>) => {
state.width = action.payload;
state.aspectRatio.value = action.payload / state.height;
state.aspectRatio.id = 'Free';
state.aspectRatio.isLocked = false;
},
heightRecalled: (state, action: PayloadAction<number>) => {
state.height = action.payload;
state.aspectRatio.value = state.width / action.payload;
state.aspectRatio.id = 'Free';
state.aspectRatio.isLocked = false;
},
aspectRatioChanged: (state, action: PayloadAction<AspectRatioState>) => {
state.aspectRatio = action.payload;
},
@ -299,6 +318,8 @@ export const {
aspectRatioChanged,
widthChanged,
heightChanged,
widthRecalled,
heightRecalled,
} = generationSlice.actions;
export const { selectOptimalDimension } = generationSlice.selectors;

View File

@ -6,7 +6,6 @@ import {
Spinner,
Text,
} from '@invoke-ai/ui';
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
import DataViewer from 'features/gallery/components/ImageMetadataViewer/DataViewer';
import { useCancelBatch } from 'features/queue/hooks/useCancelBatch';
import { useCancelQueueItem } from 'features/queue/hooks/useCancelQueueItem';
@ -127,9 +126,7 @@ const QueueItemComponent = ({ queueItemDTO }: Props) => {
justifyContent="center"
>
{queueItem ? (
<ScrollableContent>
<DataViewer label="Queue Item" data={queueItem} />
</ScrollableContent>
<DataViewer label="Queue Item" data={queueItem} />
) : (
<Spinner opacity={0.5} />
)}

View File

@ -1,23 +1,26 @@
import { useDisclosure } from '@invoke-ai/ui';
import { useAppDispatch } from 'app/store/storeHooks';
import { expanderToggled } from 'features/settingsAccordions/store/actions';
import { useCallback } from 'react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { expanderStateChanged, selectUiSlice } from 'features/ui/store/uiSlice';
import { useCallback, useMemo } from 'react';
type UseExpanderToggleArg = {
defaultIsOpen: boolean;
id?: string;
id: string;
};
export const useExpanderToggle = (arg: UseExpanderToggleArg) => {
const dispatch = useAppDispatch();
const { isOpen, onToggle: _onToggle } = useDisclosure({
defaultIsOpen: arg.defaultIsOpen,
});
const selectIsOpen = useMemo(
() =>
createSelector(
selectUiSlice,
(ui) => ui.expanders[arg.id] ?? arg.defaultIsOpen
),
[arg]
);
const isOpen = useAppSelector(selectIsOpen);
const onToggle = useCallback(() => {
if (arg.id) {
dispatch(expanderToggled({ id: arg.id, isOpen }));
}
_onToggle();
}, [_onToggle, dispatch, arg.id, isOpen]);
dispatch(expanderStateChanged({ id: arg.id, isOpen: !isOpen }));
}, [dispatch, arg.id, isOpen]);
return { isOpen, onToggle };
};

View File

@ -1,25 +1,31 @@
import { useDisclosure } from '@invoke-ai/ui';
import { useAppDispatch } from 'app/store/storeHooks';
import { standaloneAccordionToggled } from 'features/settingsAccordions/store/actions';
import { useCallback } from 'react';
import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import {
accordionStateChanged,
selectUiSlice,
} from 'features/ui/store/uiSlice';
import { useCallback, useMemo } from 'react';
type UseStandaloneAccordionToggleArg = {
defaultIsOpen: boolean;
id?: string;
id: string;
};
export const useStandaloneAccordionToggle = (
arg: UseStandaloneAccordionToggleArg
) => {
const dispatch = useAppDispatch();
const { isOpen, onToggle: _onToggle } = useDisclosure({
defaultIsOpen: arg.defaultIsOpen,
});
const selectIsOpen = useMemo(
() =>
createSelector(
selectUiSlice,
(ui) => ui.accordions[arg.id] ?? arg.defaultIsOpen
),
[arg]
);
const isOpen = useAppSelector(selectIsOpen);
const onToggle = useCallback(() => {
if (arg.id) {
dispatch(standaloneAccordionToggled({ id: arg.id, isOpen }));
}
_onToggle();
}, [_onToggle, arg.id, dispatch, isOpen]);
dispatch(accordionStateChanged({ id: arg.id, isOpen: !isOpen }));
}, [arg.id, dispatch, isOpen]);
return { isOpen, onToggle };
};

View File

@ -1,10 +0,0 @@
import { createAction } from '@reduxjs/toolkit';
export const expanderToggled = createAction<{ id: string; isOpen: boolean }>(
'parameters/expanderToggled'
);
export const standaloneAccordionToggled = createAction<{
id: string;
isOpen: boolean;
}>('parameters/standaloneAccordionToggled');

View File

@ -1,11 +1,10 @@
import { ExternalLinkIcon } from '@chakra-ui/icons';
import {
ExternalLink,
Flex,
Grid,
GridItem,
Heading,
Image,
Link,
Modal,
ModalBody,
ModalCloseButton,
@ -103,23 +102,21 @@ const AboutModal = ({ children }: AboutModalProps) => {
{appVersion && <Text>{`v${appVersion?.version}`}</Text>}
<Grid templateColumns="repeat(2, 1fr)" gap="3">
<GridItem>
<Link fontSize="sm" href={githubLink} isExternal>
{t('common.githubLabel')}
<ExternalLinkIcon mx="2px" />
</Link>
<ExternalLink
href={githubLink}
label={t('common.githubLabel')}
/>
</GridItem>
<GridItem>
<Link fontSize="sm" href={discordLink} isExternal>
{t('common.discordLabel')}
<ExternalLinkIcon mx="2px" />
</Link>
<ExternalLink
href={discordLink}
label={t('common.discordLabel')}
/>
</GridItem>
</Grid>
<Heading fontSize="large">{t('common.aboutHeading')}</Heading>
<Text fontSize="sm">{t('common.aboutDesc')}</Text>
<Link isExternal href={websiteLink} fontSize="sm">
{websiteLink} <ExternalLinkIcon mx="2px" />
</Link>
<ExternalLink href={websiteLink} label={websiteLink} />
</Flex>
</GridItem>
</Grid>

View File

@ -1,4 +1,3 @@
import { CloseIcon } from '@chakra-ui/icons';
import {
Divider,
Flex,
@ -30,6 +29,7 @@ import {
useState,
} from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
import HotkeyListItem from './HotkeyListItem';
@ -103,7 +103,8 @@ const HotkeysModal = ({ children }: HotkeysModalProps) => {
size="sm"
variant="ghost"
aria-label={t('hotkeys.clearSearch')}
icon={<CloseIcon boxSize={3} />}
boxSize={4}
icon={<PiXBold />}
/>
</InputRightElement>
)}

View File

@ -81,6 +81,11 @@ export const useHotkeyData = (): HotkeyGroup[] => {
() => ({
title: t('hotkeys.generalHotkeys'),
hotkeyListItems: [
{
title: t('hotkeys.remixImage.title'),
desc: t('hotkeys.remixImage.desc'),
hotkeys: [['R']],
},
{
title: t('hotkeys.setPrompt.title'),
desc: t('hotkeys.setPrompt.desc'),

View File

@ -1,8 +1,8 @@
import { SpinnerIcon } from '@chakra-ui/icons';
import type { SystemStyleObject } from '@invoke-ai/ui';
import {
ButtonGroup,
Flex,
Icon,
IconButton,
Portal,
spinAnimation,
@ -14,7 +14,7 @@ import { useQueueBack } from 'features/queue/hooks/useQueueBack';
import type { UsePanelReturn } from 'features/ui/hooks/usePanel';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiSlidersHorizontalBold } from 'react-icons/pi';
import { PiCircleNotchBold, PiSlidersHorizontalBold } from 'react-icons/pi';
import { RiSparklingFill } from 'react-icons/ri';
import { useGetQueueStatusQuery } from 'services/api/endpoints/queue';
@ -35,7 +35,7 @@ const FloatingSidePanelButtons = (props: Props) => {
const queueButtonIcon = useMemo(
() =>
!isDisabled && queueStatus?.processor.is_processing ? (
<SpinnerIcon animation={spinAnimation} />
<Icon boxSize={6} as={PiCircleNotchBold} animation={spinAnimation} />
) : (
<RiSparklingFill size="16px" />
),

View File

@ -10,10 +10,10 @@ export const initialUIState: UIState = {
_version: 1,
activeTab: 'txt2img',
shouldShowImageDetails: false,
shouldShowExistingModelsInSearch: false,
shouldHidePreview: false,
shouldShowProgressInViewer: true,
panels: {},
accordions: {},
expanders: {},
};
export const uiSlice = createSlice({
@ -26,15 +26,6 @@ export const uiSlice = createSlice({
setShouldShowImageDetails: (state, action: PayloadAction<boolean>) => {
state.shouldShowImageDetails = action.payload;
},
setShouldHidePreview: (state, action: PayloadAction<boolean>) => {
state.shouldHidePreview = action.payload;
},
setShouldShowExistingModelsInSearch: (
state,
action: PayloadAction<boolean>
) => {
state.shouldShowExistingModelsInSearch = action.payload;
},
setShouldShowProgressInViewer: (state, action: PayloadAction<boolean>) => {
state.shouldShowProgressInViewer = action.payload;
},
@ -44,6 +35,20 @@ export const uiSlice = createSlice({
) => {
state.panels[action.payload.name] = action.payload.value;
},
accordionStateChanged: (
state,
action: PayloadAction<{ id: string; isOpen: boolean }>
) => {
const { id, isOpen } = action.payload;
state.accordions[id] = isOpen;
},
expanderStateChanged: (
state,
action: PayloadAction<{ id: string; isOpen: boolean }>
) => {
const { id, isOpen } = action.payload;
state.expanders[id] = isOpen;
},
},
extraReducers(builder) {
builder.addCase(initialImageChanged, (state) => {
@ -55,10 +60,10 @@ export const uiSlice = createSlice({
export const {
setActiveTab,
setShouldShowImageDetails,
setShouldShowExistingModelsInSearch,
setShouldHidePreview,
setShouldShowProgressInViewer,
panelsChanged,
accordionStateChanged,
expanderStateChanged,
} = uiSlice.actions;
export default uiSlice.reducer;

View File

@ -1,11 +1,32 @@
import type { InvokeTabName } from './tabMap';
export interface UIState {
/**
* Slice schema version.
*/
_version: 1;
/**
* The currently active tab.
*/
activeTab: InvokeTabName;
/**
* Whether or not to show image details, e.g. metadata, workflow, etc.
*/
shouldShowImageDetails: boolean;
shouldShowExistingModelsInSearch: boolean;
shouldHidePreview: boolean;
/**
* Whether or not to show progress in the viewer.
*/
shouldShowProgressInViewer: boolean;
/**
* The react-resizable-panels state. The shape is managed by react-resizable-panels.
*/
panels: Record<string, string>;
/**
* The state of accordions. The key is the id of the accordion, and the value is a boolean representing the open state.
*/
accordions: Record<string, boolean>;
/**
* The state of expanders. The key is the id of the expander, and the value is a boolean representing the open state.
*/
expanders: Record<string, boolean>;
}

View File

@ -1,4 +1,3 @@
import { CloseIcon } from '@chakra-ui/icons';
import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui';
import {
Button,
@ -14,6 +13,8 @@ import {
InputRightElement,
Spacer,
} from '@invoke-ai/ui';
import { useStore } from '@nanostores/react';
import { $projectId } from 'app/store/nanostores/projectId';
import {
IAINoContentFallback,
IAINoContentFallbackWithSpinner,
@ -25,6 +26,7 @@ import WorkflowLibraryPagination from 'features/workflowLibrary/components/Workf
import type { ChangeEvent, KeyboardEvent } from 'react';
import { memo, useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { PiXBold } from 'react-icons/pi';
import { useListWorkflowsQuery } from 'services/api/endpoints/workflows';
import type {
SQLiteDirection,
@ -62,6 +64,7 @@ const WorkflowLibraryList = () => {
const [order_by, setOrderBy] = useState<WorkflowRecordOrderBy>('opened_at');
const [direction, setDirection] = useState<SQLiteDirection>('ASC');
const [debouncedQuery] = useDebounce(query, 500);
const projectId = useStore($projectId);
const queryArg = useMemo<Parameters<typeof useListWorkflowsQuery>[0]>(() => {
if (category === 'user') {
@ -142,13 +145,8 @@ const WorkflowLibraryList = () => {
[]
);
const handleSetUserCategory = useCallback(() => {
setCategory('user');
setPage(0);
}, []);
const handleSetDefaultCategory = useCallback(() => {
setCategory('default');
const handleSetCategory = useCallback((category: WorkflowCategory) => {
setCategory(category);
setPage(0);
}, []);
@ -158,21 +156,31 @@ const WorkflowLibraryList = () => {
<ButtonGroup>
<Button
variant={category === 'user' ? undefined : 'ghost'}
onClick={handleSetUserCategory}
onClick={handleSetCategory.bind(null, 'user')}
isChecked={category === 'user'}
>
{t('workflows.userWorkflows')}
</Button>
<Button
variant={category === 'default' ? undefined : 'ghost'}
onClick={handleSetDefaultCategory}
isChecked={category === 'default'}
>
{t('workflows.defaultWorkflows')}
</Button>
{projectId ? (
<Button
variant={category === 'project' ? undefined : 'ghost'}
onClick={handleSetCategory.bind(null, 'project')}
isChecked={category === 'project'}
>
{t('workflows.projectWorkflows')}
</Button>
) : (
<Button
variant={category === 'default' ? undefined : 'ghost'}
onClick={handleSetCategory.bind(null, 'default')}
isChecked={category === 'default'}
>
{t('workflows.defaultWorkflows')}
</Button>
)}
</ButtonGroup>
<Spacer />
{category === 'user' && (
{category !== 'default' && (
<>
<FormControl isDisabled={isFetching} w={64} minW={56}>
<FormLabel>{t('common.orderBy')}</FormLabel>
@ -202,14 +210,13 @@ const WorkflowLibraryList = () => {
minW={64}
/>
{query.trim().length && (
<InputRightElement>
<InputRightElement h="full" pe={2}>
<IconButton
onClick={resetFilterText}
size="xs"
variant="ghost"
size="sm"
variant="link"
aria-label={t('workflows.clearWorkflowSearchFilter')}
opacity={0.5}
icon={<CloseIcon boxSize={2} />}
icon={<PiXBold />}
/>
</InputRightElement>
)}
@ -229,7 +236,7 @@ const WorkflowLibraryList = () => {
</Flex>
</ScrollableContent>
) : (
<IAINoContentFallback label={t('workflows.noUserWorkflows')} />
<IAINoContentFallback label={t('workflows.noWorkflows')} />
)}
<Divider />
{data && (

View File

@ -52,7 +52,7 @@ const WorkflowLibraryListItem = ({ workflowDTO }: Props) => {
{workflowDTO.name || t('workflows.unnamedWorkflow')}
</Heading>
<Spacer />
{workflowDTO.category === 'user' && (
{workflowDTO.category !== 'default' && (
<Text
fontSize="sm"
variant="subtext"
@ -81,7 +81,7 @@ const WorkflowLibraryListItem = ({ workflowDTO }: Props) => {
</Text>
)}
<Spacer />
{workflowDTO.category === 'user' && (
{workflowDTO.category !== 'default' && (
<Text
fontSize="sm"
variant="subtext"
@ -104,7 +104,7 @@ const WorkflowLibraryListItem = ({ workflowDTO }: Props) => {
>
{t('common.load')}
</Button>
{workflowDTO.category === 'user' && (
{workflowDTO.category !== 'default' && (
<Button
flexShrink={0}
colorScheme="error"

File diff suppressed because one or more lines are too long

View File

@ -164,6 +164,8 @@ export type ColorMapImageProcessorInvocation =
s['ColorMapImageProcessorInvocation'];
export type ContentShuffleImageProcessorInvocation =
s['ContentShuffleImageProcessorInvocation'];
export type DepthAnythingImageProcessorInvocation =
s['DepthAnythingImageProcessorInvocation'];
export type HedImageProcessorInvocation = s['HedImageProcessorInvocation'];
export type LineartAnimeImageProcessorInvocation =
s['LineartAnimeImageProcessorInvocation'];

View File

@ -1 +1 @@
__version__ = "3.6.0"
__version__ = "3.6.1"

View File

@ -33,12 +33,12 @@ classifiers = [
]
dependencies = [
# Core generation dependencies, pinned for reproducible builds.
"accelerate==0.25.0",
"accelerate==0.26.1",
"basicsr==1.4.2",
"clip_anytorch==2.5.2", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip",
"compel==2.0.2",
"controlnet-aux==0.0.7",
"diffusers[torch]==0.25.0",
"diffusers[torch]==0.25.1",
"invisible-watermark==0.2.0", # needed to install SDXL base and refiner using their repo_ids
"mediapipe==0.10.7", # needed for "mediapipeface" controlnet model
"numpy==1.26.3", # >1.24.0 is needed to use the 'strict' argument to np.testing.assert_array_equal()
@ -52,7 +52,7 @@ dependencies = [
"torchmetrics==0.11.4",
"torchsde==0.2.6",
"torchvision==0.16.2",
"transformers==4.36.2",
"transformers==4.37.0",
# Core application dependencies, pinned for reproducible builds.
"fastapi-events==0.10.0",