Merge branch 'main' into bugfix/convert-v2-models

This commit is contained in:
Lincoln Stein 2023-02-16 23:00:58 -05:00 committed by GitHub
commit 159ce2ea08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
119 changed files with 1965 additions and 1087 deletions

View File

@ -5,8 +5,17 @@ on:
- 'main' - 'main'
- 'update/ci/docker/*' - 'update/ci/docker/*'
- 'update/docker/*' - 'update/docker/*'
paths:
- 'pyproject.toml'
- 'ldm/**'
- 'invokeai/backend/**'
- 'invokeai/configs/**'
- 'invokeai/frontend/dist/**'
- 'docker/Dockerfile'
tags: tags:
- 'v*.*.*' - 'v*.*.*'
workflow_dispatch:
jobs: jobs:
docker: docker:

View File

@ -0,0 +1,67 @@
name: Test invoke.py pip
on:
pull_request:
paths-ignore:
- 'pyproject.toml'
- 'ldm/**'
- 'invokeai/backend/**'
- 'invokeai/configs/**'
- 'invokeai/frontend/dist/**'
merge_group:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
matrix:
if: github.event.pull_request.draft == false
strategy:
matrix:
python-version:
# - '3.9'
- '3.10'
pytorch:
# - linux-cuda-11_6
- linux-cuda-11_7
- linux-rocm-5_2
- linux-cpu
- macos-default
- windows-cpu
# - windows-cuda-11_6
# - windows-cuda-11_7
include:
# - pytorch: linux-cuda-11_6
# os: ubuntu-22.04
# extra-index-url: 'https://download.pytorch.org/whl/cu116'
# github-env: $GITHUB_ENV
- pytorch: linux-cuda-11_7
os: ubuntu-22.04
github-env: $GITHUB_ENV
- pytorch: linux-rocm-5_2
os: ubuntu-22.04
extra-index-url: 'https://download.pytorch.org/whl/rocm5.2'
github-env: $GITHUB_ENV
- pytorch: linux-cpu
os: ubuntu-22.04
extra-index-url: 'https://download.pytorch.org/whl/cpu'
github-env: $GITHUB_ENV
- pytorch: macos-default
os: macOS-12
github-env: $GITHUB_ENV
- pytorch: windows-cpu
os: windows-2022
github-env: $env:GITHUB_ENV
# - pytorch: windows-cuda-11_6
# os: windows-2022
# extra-index-url: 'https://download.pytorch.org/whl/cu116'
# github-env: $env:GITHUB_ENV
# - pytorch: windows-cuda-11_7
# os: windows-2022
# extra-index-url: 'https://download.pytorch.org/whl/cu117'
# github-env: $env:GITHUB_ENV
name: ${{ matrix.pytorch }} on ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
steps:
- run: 'echo "No build required"'

View File

@ -3,7 +3,19 @@ on:
push: push:
branches: branches:
- 'main' - 'main'
paths:
- 'pyproject.toml'
- 'ldm/**'
- 'invokeai/backend/**'
- 'invokeai/configs/**'
- 'invokeai/frontend/dist/**'
pull_request: pull_request:
paths:
- 'pyproject.toml'
- 'ldm/**'
- 'invokeai/backend/**'
- 'invokeai/configs/**'
- 'invokeai/frontend/dist/**'
types: types:
- 'ready_for_review' - 'ready_for_review'
- 'opened' - 'opened'

View File

@ -13,7 +13,7 @@
[![github open issues badge]][github open issues link] [![github open prs badge]][github open prs link] [![github open issues badge]][github open issues link] [![github open prs badge]][github open prs link]
[CI checks on main badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/main?label=CI%20status%20on%20main&cache=900&icon=github [CI checks on main badge]: https://flat.badgen.net/github/checks/invoke-ai/InvokeAI/main?label=CI%20status%20on%20main&cache=900&icon=github
[CI checks on main link]: https://github.com/invoke-ai/InvokeAI/actions/workflows/test-invoke-conda.yml [CI checks on main link]:https://github.com/invoke-ai/InvokeAI/actions?query=branch%3Amain
[discord badge]: https://flat.badgen.net/discord/members/ZmtBAhwWhy?icon=discord [discord badge]: https://flat.badgen.net/discord/members/ZmtBAhwWhy?icon=discord
[discord link]: https://discord.gg/ZmtBAhwWhy [discord link]: https://discord.gg/ZmtBAhwWhy
[github forks badge]: https://flat.badgen.net/github/forks/invoke-ai/InvokeAI?icon=github [github forks badge]: https://flat.badgen.net/github/forks/invoke-ai/InvokeAI?icon=github
@ -33,7 +33,7 @@
InvokeAI is a leading creative engine built to empower professionals and enthusiasts alike. Generate and create stunning visual media using the latest AI-driven technologies. InvokeAI offers an industry leading Web Interface, interactive Command Line Interface, and also serves as the foundation for multiple commercial products. InvokeAI is a leading creative engine built to empower professionals and enthusiasts alike. Generate and create stunning visual media using the latest AI-driven technologies. InvokeAI offers an industry leading Web Interface, interactive Command Line Interface, and also serves as the foundation for multiple commercial products.
**Quick links**: [[How to Install](#installation)] [<a href="https://discord.gg/ZmtBAhwWhy">Discord Server</a>] [<a href="https://invoke-ai.github.io/InvokeAI/">Documentation and Tutorials</a>] [<a href="https://github.com/invoke-ai/InvokeAI/">Code and Downloads</a>] [<a href="https://github.com/invoke-ai/InvokeAI/issues">Bug Reports</a>] [<a href="https://github.com/invoke-ai/InvokeAI/discussions">Discussion, Ideas & Q&A</a>] **Quick links**: [[How to Install](https://invoke-ai.github.io/InvokeAI/#installation)] [<a href="https://discord.gg/ZmtBAhwWhy">Discord Server</a>] [<a href="https://invoke-ai.github.io/InvokeAI/">Documentation and Tutorials</a>] [<a href="https://github.com/invoke-ai/InvokeAI/">Code and Downloads</a>] [<a href="https://github.com/invoke-ai/InvokeAI/issues">Bug Reports</a>] [<a href="https://github.com/invoke-ai/InvokeAI/discussions">Discussion, Ideas & Q&A</a>]
_Note: InvokeAI is rapidly evolving. Please use the _Note: InvokeAI is rapidly evolving. Please use the
[Issues](https://github.com/invoke-ai/InvokeAI/issues) tab to report bugs and make feature [Issues](https://github.com/invoke-ai/InvokeAI/issues) tab to report bugs and make feature

View File

@ -147,7 +147,7 @@ echo ***** Installed invoke launcher script ******
rd /s /q binary_installer installer_files rd /s /q binary_installer installer_files
@rem preload the models @rem preload the models
call .venv\Scripts\python scripts\configure_invokeai.py call .venv\Scripts\python ldm\invoke\config\invokeai_configure.py
set err_msg=----- model download clone failed ----- set err_msg=----- model download clone failed -----
if %errorlevel% neq 0 goto err_exit if %errorlevel% neq 0 goto err_exit
deactivate deactivate

View File

@ -12,7 +12,7 @@ LABEL org.opencontainers.image.authors="mauwii@outlook.de"
RUN rm -f /etc/apt/apt.conf.d/docker-clean \ RUN rm -f /etc/apt/apt.conf.d/docker-clean \
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' >/etc/apt/apt.conf.d/keep-cache
# Install necesarry packages # Install necessary packages
RUN \ RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \
@ -78,7 +78,7 @@ RUN python3 -c "from patchmatch import patch_match"
##################### #####################
FROM python-base AS runtime FROM python-base AS runtime
# Create a new User # Create a new user
ARG UNAME=appuser ARG UNAME=appuser
RUN useradd \ RUN useradd \
--no-log-init \ --no-log-init \

View File

@ -36,7 +36,7 @@ echo -e "Container Image:\t${CONTAINER_IMAGE}\n"
if [[ -n "$(docker volume ls -f name="${VOLUMENAME}" -q)" ]]; then if [[ -n "$(docker volume ls -f name="${VOLUMENAME}" -q)" ]]; then
echo -e "Volume already exists\n" echo -e "Volume already exists\n"
else else
echo -n "createing docker volume " echo -n "creating docker volume "
docker volume create "${VOLUMENAME}" docker volume create "${VOLUMENAME}"
fi fi

View File

@ -28,7 +28,7 @@ from ldm.invoke.args import Args, APP_ID, APP_VERSION, calculate_init_img_hash
from ldm.invoke.conditioning import get_tokens_for_prompt, get_prompt_structure from ldm.invoke.conditioning import get_tokens_for_prompt, get_prompt_structure
from ldm.invoke.generator.diffusers_pipeline import PipelineIntermediateState from ldm.invoke.generator.diffusers_pipeline import PipelineIntermediateState
from ldm.invoke.generator.inpaint import infill_methods from ldm.invoke.generator.inpaint import infill_methods
from ldm.invoke.globals import Globals from ldm.invoke.globals import Globals, global_converted_ckpts_dir
from ldm.invoke.pngwriter import PngWriter, retrieve_metadata from ldm.invoke.pngwriter import PngWriter, retrieve_metadata
from ldm.invoke.prompt_parser import split_weighted_subprompts, Blend from ldm.invoke.prompt_parser import split_weighted_subprompts, Blend
@ -43,7 +43,8 @@ if not os.path.isabs(args.outdir):
# normalize the config directory relative to root # normalize the config directory relative to root
if not os.path.isabs(opt.conf): if not os.path.isabs(opt.conf):
opt.conf = os.path.normpath(os.path.join(Globals.root,opt.conf)) opt.conf = os.path.normpath(os.path.join(Globals.root, opt.conf))
class InvokeAIWebServer: class InvokeAIWebServer:
def __init__(self, generate: Generate, gfpgan, codeformer, esrgan) -> None: def __init__(self, generate: Generate, gfpgan, codeformer, esrgan) -> None:
@ -189,7 +190,8 @@ class InvokeAIWebServer:
(width, height) = pil_image.size (width, height) = pil_image.size
thumbnail_path = save_thumbnail( thumbnail_path = save_thumbnail(
pil_image, os.path.basename(file_path), self.thumbnail_image_path pil_image, os.path.basename(
file_path), self.thumbnail_image_path
) )
response = { response = {
@ -264,14 +266,16 @@ class InvokeAIWebServer:
# location for "finished" images # location for "finished" images
self.result_path = args.outdir self.result_path = args.outdir
# temporary path for intermediates # temporary path for intermediates
self.intermediate_path = os.path.join(self.result_path, "intermediates/") self.intermediate_path = os.path.join(
self.result_path, "intermediates/")
# path for user-uploaded init images and masks # path for user-uploaded init images and masks
self.init_image_path = os.path.join(self.result_path, "init-images/") self.init_image_path = os.path.join(self.result_path, "init-images/")
self.mask_image_path = os.path.join(self.result_path, "mask-images/") self.mask_image_path = os.path.join(self.result_path, "mask-images/")
# path for temp images e.g. gallery generations which are not committed # path for temp images e.g. gallery generations which are not committed
self.temp_image_path = os.path.join(self.result_path, "temp-images/") self.temp_image_path = os.path.join(self.result_path, "temp-images/")
# path for thumbnail images # path for thumbnail images
self.thumbnail_image_path = os.path.join(self.result_path, "thumbnails/") self.thumbnail_image_path = os.path.join(
self.result_path, "thumbnails/")
# txt log # txt log
self.log_path = os.path.join(self.result_path, "invoke_log.txt") self.log_path = os.path.join(self.result_path, "invoke_log.txt")
# make all output paths # make all output paths
@ -290,7 +294,7 @@ class InvokeAIWebServer:
def load_socketio_listeners(self, socketio): def load_socketio_listeners(self, socketio):
@socketio.on("requestSystemConfig") @socketio.on("requestSystemConfig")
def handle_request_capabilities(): def handle_request_capabilities():
print(f">> System config requested") print(">> System config requested")
config = self.get_system_config() config = self.get_system_config()
config["model_list"] = self.generate.model_manager.list_models() config["model_list"] = self.generate.model_manager.list_models()
config["infill_methods"] = infill_methods() config["infill_methods"] = infill_methods()
@ -301,14 +305,16 @@ class InvokeAIWebServer:
try: try:
if not search_folder: if not search_folder:
socketio.emit( socketio.emit(
"foundModels", "foundModels",
{'search_folder': None, 'found_models': None}, {'search_folder': None, 'found_models': None},
) )
else: else:
search_folder, found_models = self.generate.model_manager.search_models(search_folder) search_folder, found_models = self.generate.model_manager.search_models(
search_folder)
socketio.emit( socketio.emit(
"foundModels", "foundModels",
{'search_folder': search_folder, 'found_models': found_models}, {'search_folder': search_folder,
'found_models': found_models},
) )
except Exception as e: except Exception as e:
self.socketio.emit("error", {"message": (str(e))}) self.socketio.emit("error", {"message": (str(e))})
@ -393,6 +399,67 @@ class InvokeAIWebServer:
traceback.print_exc() traceback.print_exc()
print("\n") print("\n")
@socketio.on('convertToDiffusers')
def convert_to_diffusers(model_to_convert: dict):
try:
if (model_info := self.generate.model_manager.model_info(model_name=model_to_convert['model_name'])):
if 'weights' in model_info:
ckpt_path = Path(model_info['weights'])
original_config_file = Path(model_info['config'])
model_name = model_to_convert['model_name']
model_description = model_info['description']
else:
self.socketio.emit(
"error", {"message": "Model is not a valid checkpoint file"})
else:
self.socketio.emit(
"error", {"message": "Could not retrieve model info."})
if not ckpt_path.is_absolute():
ckpt_path = Path(Globals.root, ckpt_path)
if original_config_file and not original_config_file.is_absolute():
original_config_file = Path(
Globals.root, original_config_file)
diffusers_path = Path(
ckpt_path.parent.absolute(),
f'{model_name}_diffusers'
)
if model_to_convert['save_location'] == 'root':
diffusers_path = Path(global_converted_ckpts_dir(), f'{model_name}_diffusers')
if model_to_convert['save_location'] == 'custom' and model_to_convert['custom_location'] is not None:
diffusers_path = Path(model_to_convert['custom_location'], f'{model_name}_diffusers')
if diffusers_path.exists():
shutil.rmtree(diffusers_path)
self.generate.model_manager.convert_and_import(
ckpt_path,
diffusers_path,
model_name=model_name,
model_description=model_description,
vae=None,
original_config_file=original_config_file,
commit_to_conf=opt.conf,
)
new_model_list = self.generate.model_manager.list_models()
socketio.emit(
"modelConverted",
{"new_model_name": model_name,
"model_list": new_model_list, 'update': True},
)
print(f">> Model Converted: {model_name}")
except Exception as e:
self.socketio.emit("error", {"message": (str(e))})
print("\n")
traceback.print_exc()
print("\n")
@socketio.on("requestEmptyTempFolder") @socketio.on("requestEmptyTempFolder")
def empty_temp_folder(): def empty_temp_folder():
try: try:
@ -406,7 +473,8 @@ class InvokeAIWebServer:
) )
os.remove(thumbnail_path) os.remove(thumbnail_path)
except Exception as e: except Exception as e:
socketio.emit("error", {"message": f"Unable to delete {f}: {str(e)}"}) socketio.emit(
"error", {"message": f"Unable to delete {f}: {str(e)}"})
pass pass
socketio.emit("tempFolderEmptied") socketio.emit("tempFolderEmptied")
@ -421,7 +489,8 @@ class InvokeAIWebServer:
def save_temp_image_to_gallery(url): def save_temp_image_to_gallery(url):
try: try:
image_path = self.get_image_path_from_url(url) image_path = self.get_image_path_from_url(url)
new_path = os.path.join(self.result_path, os.path.basename(image_path)) new_path = os.path.join(
self.result_path, os.path.basename(image_path))
shutil.copy2(image_path, new_path) shutil.copy2(image_path, new_path)
if os.path.splitext(new_path)[1] == ".png": if os.path.splitext(new_path)[1] == ".png":
@ -434,7 +503,8 @@ class InvokeAIWebServer:
(width, height) = pil_image.size (width, height) = pil_image.size
thumbnail_path = save_thumbnail( thumbnail_path = save_thumbnail(
pil_image, os.path.basename(new_path), self.thumbnail_image_path pil_image, os.path.basename(
new_path), self.thumbnail_image_path
) )
image_array = [ image_array = [
@ -497,7 +567,8 @@ class InvokeAIWebServer:
(width, height) = pil_image.size (width, height) = pil_image.size
thumbnail_path = save_thumbnail( thumbnail_path = save_thumbnail(
pil_image, os.path.basename(path), self.thumbnail_image_path pil_image, os.path.basename(
path), self.thumbnail_image_path
) )
image_array.append( image_array.append(
@ -515,7 +586,8 @@ class InvokeAIWebServer:
} }
) )
except Exception as e: except Exception as e:
socketio.emit("error", {"message": f"Unable to load {path}: {str(e)}"}) socketio.emit(
"error", {"message": f"Unable to load {path}: {str(e)}"})
pass pass
socketio.emit( socketio.emit(
@ -569,7 +641,8 @@ class InvokeAIWebServer:
(width, height) = pil_image.size (width, height) = pil_image.size
thumbnail_path = save_thumbnail( thumbnail_path = save_thumbnail(
pil_image, os.path.basename(path), self.thumbnail_image_path pil_image, os.path.basename(
path), self.thumbnail_image_path
) )
image_array.append( image_array.append(
@ -588,7 +661,8 @@ class InvokeAIWebServer:
) )
except Exception as e: except Exception as e:
print(f">> Unable to load {path}") print(f">> Unable to load {path}")
socketio.emit("error", {"message": f"Unable to load {path}: {str(e)}"}) socketio.emit(
"error", {"message": f"Unable to load {path}: {str(e)}"})
pass pass
socketio.emit( socketio.emit(
@ -626,7 +700,8 @@ class InvokeAIWebServer:
printable_parameters["init_mask"][:64] + "..." printable_parameters["init_mask"][:64] + "..."
) )
print(f'\n>> Image Generation Parameters:\n\n{printable_parameters}\n') print(
f'\n>> Image Generation Parameters:\n\n{printable_parameters}\n')
print(f'>> ESRGAN Parameters: {esrgan_parameters}') print(f'>> ESRGAN Parameters: {esrgan_parameters}')
print(f'>> Facetool Parameters: {facetool_parameters}') print(f'>> Facetool Parameters: {facetool_parameters}')
@ -662,16 +737,18 @@ class InvokeAIWebServer:
try: try:
seed = original_image["metadata"]["image"]["seed"] seed = original_image["metadata"]["image"]["seed"]
except (KeyError) as e: except KeyError:
seed = "unknown_seed" seed = "unknown_seed"
pass pass
if postprocessing_parameters["type"] == "esrgan": if postprocessing_parameters["type"] == "esrgan":
progress.set_current_status("common:statusUpscalingESRGAN") progress.set_current_status("common:statusUpscalingESRGAN")
elif postprocessing_parameters["type"] == "gfpgan": elif postprocessing_parameters["type"] == "gfpgan":
progress.set_current_status("common:statusRestoringFacesGFPGAN") progress.set_current_status(
"common:statusRestoringFacesGFPGAN")
elif postprocessing_parameters["type"] == "codeformer": elif postprocessing_parameters["type"] == "codeformer":
progress.set_current_status("common:statusRestoringFacesCodeFormer") progress.set_current_status(
"common:statusRestoringFacesCodeFormer")
socketio.emit("progressUpdate", progress.to_formatted_dict()) socketio.emit("progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
@ -760,7 +837,7 @@ class InvokeAIWebServer:
@socketio.on("cancel") @socketio.on("cancel")
def handle_cancel(): def handle_cancel():
print(f">> Cancel processing requested") print(">> Cancel processing requested")
self.canceled.set() self.canceled.set()
# TODO: I think this needs a safety mechanism. # TODO: I think this needs a safety mechanism.
@ -842,12 +919,10 @@ class InvokeAIWebServer:
So we need to convert each into a PIL Image. So we need to convert each into a PIL Image.
""" """
truncated_outpaint_image_b64 = generation_parameters["init_img"][:64]
truncated_outpaint_mask_b64 = generation_parameters["init_mask"][:64]
init_img_url = generation_parameters["init_img"] init_img_url = generation_parameters["init_img"]
original_bounding_box = generation_parameters["bounding_box"].copy() original_bounding_box = generation_parameters["bounding_box"].copy(
)
initial_image = dataURL_to_image( initial_image = dataURL_to_image(
generation_parameters["init_img"] generation_parameters["init_img"]
@ -924,7 +999,8 @@ class InvokeAIWebServer:
elif generation_parameters["generation_mode"] == "img2img": elif generation_parameters["generation_mode"] == "img2img":
init_img_url = generation_parameters["init_img"] init_img_url = generation_parameters["init_img"]
init_img_path = self.get_image_path_from_url(init_img_url) init_img_path = self.get_image_path_from_url(init_img_url)
generation_parameters["init_img"] = Image.open(init_img_path).convert('RGB') generation_parameters["init_img"] = Image.open(
init_img_path).convert('RGB')
def image_progress(sample, step): def image_progress(sample, step):
if self.canceled.is_set(): if self.canceled.is_set():
@ -983,9 +1059,9 @@ class InvokeAIWebServer:
}, },
) )
if generation_parameters["progress_latents"]: if generation_parameters["progress_latents"]:
image = self.generate.sample_to_lowres_estimated_image(sample) image = self.generate.sample_to_lowres_estimated_image(
sample)
(width, height) = image.size (width, height) = image.size
width *= 8 width *= 8
height *= 8 height *= 8
@ -1004,7 +1080,8 @@ class InvokeAIWebServer:
}, },
) )
self.socketio.emit("progressUpdate", progress.to_formatted_dict()) self.socketio.emit(
"progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
def image_done(image, seed, first_seed, attention_maps_image=None): def image_done(image, seed, first_seed, attention_maps_image=None):
@ -1016,7 +1093,6 @@ class InvokeAIWebServer:
nonlocal facetool_parameters nonlocal facetool_parameters
nonlocal progress nonlocal progress
step_index = 1
nonlocal prior_variations nonlocal prior_variations
""" """
@ -1032,7 +1108,8 @@ class InvokeAIWebServer:
progress.set_current_status("common:statusGenerationComplete") progress.set_current_status("common:statusGenerationComplete")
self.socketio.emit("progressUpdate", progress.to_formatted_dict()) self.socketio.emit(
"progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
all_parameters = generation_parameters all_parameters = generation_parameters
@ -1043,7 +1120,8 @@ class InvokeAIWebServer:
and all_parameters["variation_amount"] > 0 and all_parameters["variation_amount"] > 0
): ):
first_seed = first_seed or seed first_seed = first_seed or seed
this_variation = [[seed, all_parameters["variation_amount"]]] this_variation = [
[seed, all_parameters["variation_amount"]]]
all_parameters["with_variations"] = ( all_parameters["with_variations"] = (
prior_variations + this_variation prior_variations + this_variation
) )
@ -1059,7 +1137,8 @@ class InvokeAIWebServer:
if esrgan_parameters: if esrgan_parameters:
progress.set_current_status("common:statusUpscaling") progress.set_current_status("common:statusUpscaling")
progress.set_current_status_has_steps(False) progress.set_current_status_has_steps(False)
self.socketio.emit("progressUpdate", progress.to_formatted_dict()) self.socketio.emit(
"progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
image = self.esrgan.process( image = self.esrgan.process(
@ -1082,12 +1161,15 @@ class InvokeAIWebServer:
if facetool_parameters: if facetool_parameters:
if facetool_parameters["type"] == "gfpgan": if facetool_parameters["type"] == "gfpgan":
progress.set_current_status("common:statusRestoringFacesGFPGAN") progress.set_current_status(
"common:statusRestoringFacesGFPGAN")
elif facetool_parameters["type"] == "codeformer": elif facetool_parameters["type"] == "codeformer":
progress.set_current_status("common:statusRestoringFacesCodeFormer") progress.set_current_status(
"common:statusRestoringFacesCodeFormer")
progress.set_current_status_has_steps(False) progress.set_current_status_has_steps(False)
self.socketio.emit("progressUpdate", progress.to_formatted_dict()) self.socketio.emit(
"progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
if facetool_parameters["type"] == "gfpgan": if facetool_parameters["type"] == "gfpgan":
@ -1117,7 +1199,8 @@ class InvokeAIWebServer:
all_parameters["facetool_type"] = facetool_parameters["type"] all_parameters["facetool_type"] = facetool_parameters["type"]
progress.set_current_status("common:statusSavingImage") progress.set_current_status("common:statusSavingImage")
self.socketio.emit("progressUpdate", progress.to_formatted_dict()) self.socketio.emit(
"progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
# restore the stashed URLS and discard the paths, we are about to send the result to client # restore the stashed URLS and discard the paths, we are about to send the result to client
@ -1128,12 +1211,14 @@ class InvokeAIWebServer:
) )
if "init_mask" in all_parameters: if "init_mask" in all_parameters:
all_parameters["init_mask"] = "" # TODO: store the mask in metadata # TODO: store the mask in metadata
all_parameters["init_mask"] = ""
if generation_parameters["generation_mode"] == "unifiedCanvas": if generation_parameters["generation_mode"] == "unifiedCanvas":
all_parameters["bounding_box"] = original_bounding_box all_parameters["bounding_box"] = original_bounding_box
metadata = self.parameters_to_generated_image_metadata(all_parameters) metadata = self.parameters_to_generated_image_metadata(
all_parameters)
command = parameters_to_command(all_parameters) command = parameters_to_command(all_parameters)
@ -1163,15 +1248,18 @@ class InvokeAIWebServer:
if progress.total_iterations > progress.current_iteration: if progress.total_iterations > progress.current_iteration:
progress.set_current_step(1) progress.set_current_step(1)
progress.set_current_status("common:statusIterationComplete") progress.set_current_status(
"common:statusIterationComplete")
progress.set_current_status_has_steps(False) progress.set_current_status_has_steps(False)
else: else:
progress.mark_complete() progress.mark_complete()
self.socketio.emit("progressUpdate", progress.to_formatted_dict()) self.socketio.emit(
"progressUpdate", progress.to_formatted_dict())
eventlet.sleep(0) eventlet.sleep(0)
parsed_prompt, _ = get_prompt_structure(generation_parameters["prompt"]) parsed_prompt, _ = get_prompt_structure(
generation_parameters["prompt"])
tokens = None if type(parsed_prompt) is Blend else \ tokens = None if type(parsed_prompt) is Blend else \
get_tokens_for_prompt(self.generate.model, parsed_prompt) get_tokens_for_prompt(self.generate.model, parsed_prompt)
attention_maps_image_base64_url = None if attention_maps_image is None \ attention_maps_image_base64_url = None if attention_maps_image is None \
@ -1345,7 +1433,8 @@ class InvokeAIWebServer:
self, parameters, original_image_path self, parameters, original_image_path
): ):
try: try:
current_metadata = retrieve_metadata(original_image_path)["sd-metadata"] current_metadata = retrieve_metadata(
original_image_path)["sd-metadata"]
postprocessing_metadata = {} postprocessing_metadata = {}
""" """
@ -1385,7 +1474,8 @@ class InvokeAIWebServer:
postprocessing_metadata postprocessing_metadata
) )
else: else:
current_metadata["image"]["postprocessing"] = [postprocessing_metadata] current_metadata["image"]["postprocessing"] = [
postprocessing_metadata]
return current_metadata return current_metadata
@ -1424,7 +1514,7 @@ class InvokeAIWebServer:
if step_index: if step_index:
filename += f".{step_index}" filename += f".{step_index}"
if postprocessing: if postprocessing:
filename += f".postprocessed" filename += ".postprocessed"
filename += ".png" filename += ".png"
@ -1497,7 +1587,8 @@ class InvokeAIWebServer:
) )
elif "thumbnails" in url: elif "thumbnails" in url:
return os.path.abspath( return os.path.abspath(
os.path.join(self.thumbnail_image_path, os.path.basename(url)) os.path.join(self.thumbnail_image_path,
os.path.basename(url))
) )
else: else:
return os.path.abspath( return os.path.abspath(
@ -1666,10 +1757,12 @@ def dataURL_to_image(dataURL: str) -> ImageType:
) )
return image return image
""" """
Converts an image into a base64 image dataURL. Converts an image into a base64 image dataURL.
""" """
def image_to_dataURL(image: ImageType) -> str: def image_to_dataURL(image: ImageType) -> str:
buffered = io.BytesIO() buffered = io.BytesIO()
image.save(buffered, format="PNG") image.save(buffered, format="PNG")
@ -1679,7 +1772,6 @@ def image_to_dataURL(image: ImageType) -> str:
return image_base64 return image_base64
""" """
Converts a base64 image dataURL into bytes. Converts a base64 image dataURL into bytes.
The dataURL is split on the first commma. The dataURL is split on the first commma.

View File

@ -3,3 +3,4 @@ dist/
node_modules/ node_modules/
patches/ patches/
public/ public/
stats.html

View File

@ -3,3 +3,4 @@ dist/
node_modules/ node_modules/
patches/ patches/
public/ public/
stats.html

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>InvokeAI - A Stable Diffusion Toolkit</title> <title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="shortcut icon" type="icon" href="./assets/favicon-0d253ced.ico" /> <link rel="shortcut icon" type="icon" href="./assets/favicon-0d253ced.ico" />
<script type="module" crossorigin src="./assets/index-a93d4500.js"></script> <script type="module" crossorigin src="./assets/index-12bd70ca.js"></script>
<link rel="stylesheet" href="./assets/index-fecb6dd4.css"> <link rel="stylesheet" href="./assets/index-c1af841f.css">
</head> </head>
<body> <body>

View File

@ -58,5 +58,7 @@
"statusUpscaling": "Upscaling", "statusUpscaling": "Upscaling",
"statusUpscalingESRGAN": "Upscaling (ESRGAN)", "statusUpscalingESRGAN": "Upscaling (ESRGAN)",
"statusLoadingModel": "Loading Model", "statusLoadingModel": "Loading Model",
"statusModelChanged": "Model Changed" "statusModelChanged": "Model Changed",
"statusConvertingModel": "Converting Model",
"statusModelConverted": "Model Converted"
} }

View File

@ -63,5 +63,23 @@
"formMessageDiffusersModelLocation": "Diffusers Model Location", "formMessageDiffusersModelLocation": "Diffusers Model Location",
"formMessageDiffusersModelLocationDesc": "Please enter at least one.", "formMessageDiffusersModelLocationDesc": "Please enter at least one.",
"formMessageDiffusersVAELocation": "VAE Location", "formMessageDiffusersVAELocation": "VAE Location",
"formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above." "formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above.",
"convert": "Convert",
"convertToDiffusers": "Convert To Diffusers",
"convertToDiffusersHelpText1": "This model will be converted to the 🧨 Diffusers format.",
"convertToDiffusersHelpText2": "This process will replace your Model Manager entry with the Diffusers version of the same model.",
"convertToDiffusersHelpText3": "Your checkpoint file on the disk will NOT be deleted or modified in anyway. You can add your checkpoint to the Model Manager again if you want to.",
"convertToDiffusersHelpText4": "This is a one time process only. It might take around 30s-60s depending on the specifications of your computer.",
"convertToDiffusersHelpText5": "Please make sure you have enough disk space. Models generally vary between 4GB-7GB in size.",
"convertToDiffusersHelpText6": "Do you wish to convert this model?",
"v1": "v1",
"v2": "v2",
"inpainting": "v1 Inpainting",
"customConfig": "Custom Config",
"pathToCustomConfig": "Path To Custom Config",
"statusConverting": "Converting",
"sameFolder": "Same Folder",
"invokeRoot": "Invoke Models",
"custom": "Custom",
"customSaveLocation": "Custom Save Location"
} }

View File

@ -63,5 +63,25 @@
"formMessageDiffusersModelLocation": "Diffusers Model Location", "formMessageDiffusersModelLocation": "Diffusers Model Location",
"formMessageDiffusersModelLocationDesc": "Please enter at least one.", "formMessageDiffusersModelLocationDesc": "Please enter at least one.",
"formMessageDiffusersVAELocation": "VAE Location", "formMessageDiffusersVAELocation": "VAE Location",
"formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above." "formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above.",
"convert": "Convert",
"convertToDiffusers": "Convert To Diffusers",
"convertToDiffusersHelpText1": "This model will be converted to the 🧨 Diffusers format.",
"convertToDiffusersHelpText2": "This process will replace your Model Manager entry with the Diffusers version of the same model.",
"convertToDiffusersHelpText3": "Your checkpoint file on the disk will NOT be deleted or modified in anyway. You can add your checkpoint to the Model Manager again if you want to.",
"convertToDiffusersHelpText4": "This is a one time process only. It might take around 30s-60s depending on the specifications of your computer.",
"convertToDiffusersHelpText5": "Please make sure you have enough disk space. Models generally vary between 4GB-7GB in size.",
"convertToDiffusersHelpText6": "Do you wish to convert this model?",
"convertToDiffusersSaveLocation": "Save Location",
"v1": "v1",
"v2": "v2",
"inpainting": "v1 Inpainting",
"customConfig": "Custom Config",
"pathToCustomConfig": "Path To Custom Config",
"statusConverting": "Converting",
"modelConverted": "Model Converted",
"sameFolder": "Same folder",
"invokeRoot": "InvokeAI folder",
"custom": "Custom",
"customSaveLocation": "Custom Save Location"
} }

View File

@ -57,6 +57,6 @@
"useInitImg": "Use Initial Image", "useInitImg": "Use Initial Image",
"info": "Info", "info": "Info",
"deleteImage": "Delete Image", "deleteImage": "Delete Image",
"initialImage": "Inital Image", "initialImage": "Initial Image",
"showOptionsPanel": "Show Options Panel" "showOptionsPanel": "Show Options Panel"
} }

View File

@ -60,6 +60,6 @@
"useInitImg": "Use Initial Image", "useInitImg": "Use Initial Image",
"info": "Info", "info": "Info",
"deleteImage": "Delete Image", "deleteImage": "Delete Image",
"initialImage": "Inital Image", "initialImage": "Initial Image",
"showOptionsPanel": "Show Options Panel" "showOptionsPanel": "Show Options Panel"
} }

View File

@ -56,7 +56,7 @@
"useInitImg": "Use Initial Image", "useInitImg": "Use Initial Image",
"info": "情報", "info": "情報",
"deleteImage": "画像を削除", "deleteImage": "画像を削除",
"initialImage": "Inital Image", "initialImage": "Initial Image",
"showOptionsPanel": "オプションパネルを表示" "showOptionsPanel": "オプションパネルを表示"
} }

View File

@ -58,5 +58,7 @@
"statusUpscaling": "Upscaling", "statusUpscaling": "Upscaling",
"statusUpscalingESRGAN": "Upscaling (ESRGAN)", "statusUpscalingESRGAN": "Upscaling (ESRGAN)",
"statusLoadingModel": "Loading Model", "statusLoadingModel": "Loading Model",
"statusModelChanged": "Model Changed" "statusModelChanged": "Model Changed",
"statusConvertingModel": "Converting Model",
"statusModelConverted": "Model Converted"
} }

View File

@ -63,5 +63,23 @@
"formMessageDiffusersModelLocation": "Diffusers Model Location", "formMessageDiffusersModelLocation": "Diffusers Model Location",
"formMessageDiffusersModelLocationDesc": "Please enter at least one.", "formMessageDiffusersModelLocationDesc": "Please enter at least one.",
"formMessageDiffusersVAELocation": "VAE Location", "formMessageDiffusersVAELocation": "VAE Location",
"formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above." "formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above.",
"convert": "Convert",
"convertToDiffusers": "Convert To Diffusers",
"convertToDiffusersHelpText1": "This model will be converted to the 🧨 Diffusers format.",
"convertToDiffusersHelpText2": "This process will replace your Model Manager entry with the Diffusers version of the same model.",
"convertToDiffusersHelpText3": "Your checkpoint file on the disk will NOT be deleted or modified in anyway. You can add your checkpoint to the Model Manager again if you want to.",
"convertToDiffusersHelpText4": "This is a one time process only. It might take around 30s-60s depending on the specifications of your computer.",
"convertToDiffusersHelpText5": "Please make sure you have enough disk space. Models generally vary between 4GB-7GB in size.",
"convertToDiffusersHelpText6": "Do you wish to convert this model?",
"v1": "v1",
"v2": "v2",
"inpainting": "v1 Inpainting",
"customConfig": "Custom Config",
"pathToCustomConfig": "Path To Custom Config",
"statusConverting": "Converting",
"sameFolder": "Same Folder",
"invokeRoot": "Invoke Models",
"custom": "Custom",
"customSaveLocation": "Custom Save Location"
} }

View File

@ -63,5 +63,25 @@
"formMessageDiffusersModelLocation": "Diffusers Model Location", "formMessageDiffusersModelLocation": "Diffusers Model Location",
"formMessageDiffusersModelLocationDesc": "Please enter at least one.", "formMessageDiffusersModelLocationDesc": "Please enter at least one.",
"formMessageDiffusersVAELocation": "VAE Location", "formMessageDiffusersVAELocation": "VAE Location",
"formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above." "formMessageDiffusersVAELocationDesc": "If not provided, InvokeAI will look for the VAE file inside the model location given above.",
"convert": "Convert",
"convertToDiffusers": "Convert To Diffusers",
"convertToDiffusersHelpText1": "This model will be converted to the 🧨 Diffusers format.",
"convertToDiffusersHelpText2": "This process will replace your Model Manager entry with the Diffusers version of the same model.",
"convertToDiffusersHelpText3": "Your checkpoint file on the disk will NOT be deleted or modified in anyway. You can add your checkpoint to the Model Manager again if you want to.",
"convertToDiffusersHelpText4": "This is a one time process only. It might take around 30s-60s depending on the specifications of your computer.",
"convertToDiffusersHelpText5": "Please make sure you have enough disk space. Models generally vary between 4GB-7GB in size.",
"convertToDiffusersHelpText6": "Do you wish to convert this model?",
"convertToDiffusersSaveLocation": "Save Location",
"v1": "v1",
"v2": "v2",
"inpainting": "v1 Inpainting",
"customConfig": "Custom Config",
"pathToCustomConfig": "Path To Custom Config",
"statusConverting": "Converting",
"modelConverted": "Model Converted",
"sameFolder": "Same folder",
"invokeRoot": "InvokeAI folder",
"custom": "Custom",
"customSaveLocation": "Custom Save Location"
} }

View File

@ -57,6 +57,6 @@
"useInitImg": "Use Initial Image", "useInitImg": "Use Initial Image",
"info": "Info", "info": "Info",
"deleteImage": "Delete Image", "deleteImage": "Delete Image",
"initialImage": "Inital Image", "initialImage": "Initial Image",
"showOptionsPanel": "Show Options Panel" "showOptionsPanel": "Show Options Panel"
} }

View File

@ -60,6 +60,6 @@
"useInitImg": "Use Initial Image", "useInitImg": "Use Initial Image",
"info": "Info", "info": "Info",
"deleteImage": "Delete Image", "deleteImage": "Delete Image",
"initialImage": "Inital Image", "initialImage": "Initial Image",
"showOptionsPanel": "Show Options Panel" "showOptionsPanel": "Show Options Panel"
} }

View File

@ -56,7 +56,7 @@
"useInitImg": "Use Initial Image", "useInitImg": "Use Initial Image",
"info": "情報", "info": "情報",
"deleteImage": "画像を削除", "deleteImage": "画像を削除",
"initialImage": "Inital Image", "initialImage": "Initial Image",
"showOptionsPanel": "オプションパネルを表示" "showOptionsPanel": "オプションパネルを表示"
} }

View File

@ -3,8 +3,8 @@ import { Flex, Spinner } from '@chakra-ui/react';
const Loading = () => { const Loading = () => {
return ( return (
<Flex <Flex
width={'100vw'} width="100vw"
height={'100vh'} height="100vh"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
> >

View File

@ -219,6 +219,12 @@ export declare type InvokeDiffusersModelConfigProps = {
}; };
}; };
export declare type InvokeModelConversionProps = {
model_name: string;
save_location: string;
custom_location: string | null;
};
/** /**
* These types type data received from the server via socketio. * These types type data received from the server via socketio.
*/ */
@ -228,6 +234,11 @@ export declare type ModelChangeResponse = {
model_list: ModelList; model_list: ModelList;
}; };
export declare type ModelConvertedResponse = {
converted_model_name: string;
model_list: ModelList;
};
export declare type ModelAddedResponse = { export declare type ModelAddedResponse = {
new_model_name: string; new_model_name: string;
model_list: ModelList; model_list: ModelList;

View File

@ -38,6 +38,11 @@ export const addNewModel = createAction<
export const deleteModel = createAction<string>('socketio/deleteModel'); export const deleteModel = createAction<string>('socketio/deleteModel');
export const convertToDiffusers =
createAction<InvokeAI.InvokeModelConversionProps>(
'socketio/convertToDiffusers'
);
export const requestModelChange = createAction<string>( export const requestModelChange = createAction<string>(
'socketio/requestModelChange' 'socketio/requestModelChange'
); );

View File

@ -15,6 +15,7 @@ import {
addLogEntry, addLogEntry,
generationRequested, generationRequested,
modelChangeRequested, modelChangeRequested,
modelConvertRequested,
setIsProcessing, setIsProcessing,
} from 'features/system/store/systemSlice'; } from 'features/system/store/systemSlice';
import { InvokeTabName } from 'features/ui/store/tabMap'; import { InvokeTabName } from 'features/ui/store/tabMap';
@ -178,6 +179,12 @@ const makeSocketIOEmitters = (
emitDeleteModel: (modelName: string) => { emitDeleteModel: (modelName: string) => {
socketio.emit('deleteModel', modelName); socketio.emit('deleteModel', modelName);
}, },
emitConvertToDiffusers: (
modelToConvert: InvokeAI.InvokeModelConversionProps
) => {
dispatch(modelConvertRequested());
socketio.emit('convertToDiffusers', modelToConvert);
},
emitRequestModelChange: (modelName: string) => { emitRequestModelChange: (modelName: string) => {
dispatch(modelChangeRequested()); dispatch(modelChangeRequested());
socketio.emit('requestModelChange', modelName); socketio.emit('requestModelChange', modelName);

View File

@ -365,6 +365,7 @@ const makeSocketIOListeners = (
const { new_model_name, model_list, update } = data; const { new_model_name, model_list, update } = data;
dispatch(setModelList(model_list)); dispatch(setModelList(model_list));
dispatch(setIsProcessing(false)); dispatch(setIsProcessing(false));
dispatch(setCurrentStatus(i18n.t('modelmanager:modelAdded')));
dispatch( dispatch(
addLogEntry({ addLogEntry({
timestamp: dateFormat(new Date(), 'isoDateTime'), timestamp: dateFormat(new Date(), 'isoDateTime'),
@ -407,6 +408,30 @@ const makeSocketIOListeners = (
}) })
); );
}, },
onModelConverted: (data: InvokeAI.ModelConvertedResponse) => {
const { converted_model_name, model_list } = data;
dispatch(setModelList(model_list));
dispatch(setCurrentStatus(i18n.t('common:statusModelConverted')));
dispatch(setIsProcessing(false));
dispatch(setIsCancelable(true));
dispatch(
addLogEntry({
timestamp: dateFormat(new Date(), 'isoDateTime'),
message: `Model converted: ${converted_model_name}`,
level: 'info',
})
);
dispatch(
addToast({
title: `${i18n.t(
'modelmanager:modelConverted'
)}: ${converted_model_name}`,
status: 'success',
duration: 2500,
isClosable: true,
})
);
},
onModelChanged: (data: InvokeAI.ModelChangeResponse) => { onModelChanged: (data: InvokeAI.ModelChangeResponse) => {
const { model_name, model_list } = data; const { model_name, model_list } = data;
dispatch(setModelList(model_list)); dispatch(setModelList(model_list));

View File

@ -48,6 +48,7 @@ export const socketioMiddleware = () => {
onFoundModels, onFoundModels,
onNewModelAdded, onNewModelAdded,
onModelDeleted, onModelDeleted,
onModelConverted,
onModelChangeFailed, onModelChangeFailed,
onTempFolderEmptied, onTempFolderEmptied,
} = makeSocketIOListeners(store); } = makeSocketIOListeners(store);
@ -64,6 +65,7 @@ export const socketioMiddleware = () => {
emitSearchForModels, emitSearchForModels,
emitAddNewModel, emitAddNewModel,
emitDeleteModel, emitDeleteModel,
emitConvertToDiffusers,
emitRequestModelChange, emitRequestModelChange,
emitSaveStagingAreaImageToGallery, emitSaveStagingAreaImageToGallery,
emitRequestEmptyTempFolder, emitRequestEmptyTempFolder,
@ -125,6 +127,10 @@ export const socketioMiddleware = () => {
onModelDeleted(data); onModelDeleted(data);
}); });
socketio.on('modelConverted', (data: InvokeAI.ModelConvertedResponse) => {
onModelConverted(data);
});
socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => { socketio.on('modelChanged', (data: InvokeAI.ModelChangeResponse) => {
onModelChanged(data); onModelChanged(data);
}); });
@ -199,6 +205,11 @@ export const socketioMiddleware = () => {
break; break;
} }
case 'socketio/convertToDiffusers': {
emitConvertToDiffusers(action.payload);
break;
}
case 'socketio/requestModelChange': { case 'socketio/requestModelChange': {
emitRequestModelChange(action.payload); emitRequestModelChange(action.payload);
break; break;

View File

@ -13,7 +13,7 @@ const GuideIcon = forwardRef(
({ feature, icon = MdHelp }: GuideIconProps, ref) => ( ({ feature, icon = MdHelp }: GuideIconProps, ref) => (
<GuidePopover feature={feature}> <GuidePopover feature={feature}>
<Box ref={ref}> <Box ref={ref}>
<Icon marginBottom={'-.15rem'} as={icon} /> <Icon marginBottom="-.15rem" as={icon} />
</Box> </Box>
</GuidePopover> </GuidePopover>
) )

View File

@ -29,15 +29,15 @@ const GuidePopover = ({ children, feature }: GuideProps) => {
if (!shouldDisplayGuides) return null; if (!shouldDisplayGuides) return null;
return ( return (
<Popover trigger={'hover'}> <Popover trigger="hover">
<PopoverTrigger> <PopoverTrigger>
<Box>{children}</Box> <Box>{children}</Box>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent <PopoverContent
className={`guide-popover-content`} className="guide-popover-content"
maxWidth="400px" maxWidth="400px"
onClick={(e) => e.preventDefault()} onClick={(e) => e.preventDefault()}
cursor={'initial'} cursor="initial"
> >
<PopoverArrow className="guide-popover-arrow" /> <PopoverArrow className="guide-popover-arrow" />
<div className="guide-popover-guide-content">{text}</div> <div className="guide-popover-guide-content">{text}</div>

View File

@ -169,7 +169,7 @@ export default function IAISlider(props: IAIFullSliderProps) {
{label} {label}
</FormLabel> </FormLabel>
<HStack w={'100%'} gap={2} alignItems="center"> <HStack w="100%" gap={2} alignItems="center">
<Slider <Slider
aria-label={label} aria-label={label}
value={value} value={value}
@ -259,9 +259,9 @@ export default function IAISlider(props: IAIFullSliderProps) {
{withReset && ( {withReset && (
<IAIIconButton <IAIIconButton
size={'sm'} size="sm"
aria-label={'Reset'} aria-label="Reset"
tooltip={'Reset'} tooltip="Reset"
icon={<BiReset />} icon={<BiReset />}
onClick={handleResetDisable} onClick={handleResetDisable}
isDisabled={isResetDisabled} isDisabled={isResetDisabled}

View File

@ -24,13 +24,13 @@ const ImageUploadOverlay = (props: ImageUploadOverlayProps) => {
<div className="dropzone-container"> <div className="dropzone-container">
{isDragAccept && ( {isDragAccept && (
<div className="dropzone-overlay is-drag-accept"> <div className="dropzone-overlay is-drag-accept">
<Heading size={'lg'}>Upload Image{overlaySecondaryText}</Heading> <Heading size="lg">Upload Image{overlaySecondaryText}</Heading>
</div> </div>
)} )}
{isDragReject && ( {isDragReject && (
<div className="dropzone-overlay is-drag-reject"> <div className="dropzone-overlay is-drag-reject">
<Heading size={'lg'}>Invalid Upload</Heading> <Heading size="lg">Invalid Upload</Heading>
<Heading size={'md'}>Must be single JPEG or PNG image</Heading> <Heading size="md">Must be single JPEG or PNG image</Heading>
</div> </div>
)} )}
</div> </div>

View File

@ -22,7 +22,7 @@ const ImageUploaderButton = (props: ImageUploaderButtonProps) => {
> >
<div className="image-upload-button"> <div className="image-upload-button">
<FaUpload /> <FaUpload />
<Heading size={'lg'}>Click or Drag and Drop</Heading> <Heading size="lg">Click or Drag and Drop</Heading>
</div> </div>
</div> </div>
); );

View File

@ -178,12 +178,16 @@ export const frontendToBackendParameters = (
? randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX) ? randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX)
: seed; : seed;
// parameters common to txt2img and img2img // txt2img exclusive parameters
if (['txt2img', 'img2img'].includes(generationMode)) { if (generationMode === 'txt2img') {
generationParameters.seamless = seamless;
generationParameters.hires_fix = hiresFix; generationParameters.hires_fix = hiresFix;
if (hiresFix) generationParameters.strength = hiresStrength; if (hiresFix) generationParameters.strength = hiresStrength;
}
// parameters common to txt2img and img2img
if (['txt2img', 'img2img'].includes(generationMode)) {
generationParameters.seamless = seamless;
if (shouldRunESRGAN) { if (shouldRunESRGAN) {
esrganParameters = { esrganParameters = {

View File

@ -17,7 +17,7 @@ const ClearCanvasHistoryButtonModal = () => {
acceptCallback={() => dispatch(clearCanvasHistory())} acceptCallback={() => dispatch(clearCanvasHistory())}
acceptButtonText={t('unifiedcanvas:clearHistory')} acceptButtonText={t('unifiedcanvas:clearHistory')}
triggerComponent={ triggerComponent={
<IAIButton size={'sm'} leftIcon={<FaTrash />} isDisabled={isStaging}> <IAIButton size="sm" leftIcon={<FaTrash />} isDisabled={isStaging}>
{t('unifiedcanvas:clearCanvasHistory')} {t('unifiedcanvas:clearCanvasHistory')}
</IAIButton> </IAIButton>
} }

View File

@ -140,7 +140,7 @@ const IAICanvas = () => {
<Stage <Stage
tabIndex={-1} tabIndex={-1}
ref={canvasStageRefCallback} ref={canvasStageRefCallback}
className={'inpainting-canvas-stage'} className="inpainting-canvas-stage"
style={{ style={{
...(stageCursor ? { cursor: stageCursor } : {}), ...(stageCursor ? { cursor: stageCursor } : {}),
}} }}
@ -165,19 +165,19 @@ const IAICanvas = () => {
onWheel={handleWheel} onWheel={handleWheel}
draggable={(tool === 'move' || isStaging) && !isModifyingBoundingBox} draggable={(tool === 'move' || isStaging) && !isModifyingBoundingBox}
> >
<Layer id={'grid'} visible={shouldShowGrid}> <Layer id="grid" visible={shouldShowGrid}>
<IAICanvasGrid /> <IAICanvasGrid />
</Layer> </Layer>
<Layer <Layer
id={'base'} id="base"
ref={canvasBaseLayerRefCallback} ref={canvasBaseLayerRefCallback}
listening={false} listening={false}
imageSmoothingEnabled={false} imageSmoothingEnabled={false}
> >
<IAICanvasObjectRenderer /> <IAICanvasObjectRenderer />
</Layer> </Layer>
<Layer id={'mask'} visible={isMaskEnabled} listening={false}> <Layer id="mask" visible={isMaskEnabled} listening={false}>
<IAICanvasMaskLines visible={true} listening={false} /> <IAICanvasMaskLines visible={true} listening={false} />
<IAICanvasMaskCompositer listening={false} /> <IAICanvasMaskCompositer listening={false} />
</Layer> </Layer>

View File

@ -49,7 +49,7 @@ const IAICanvasBoundingBoxOverlay = () => {
offsetY={stageCoordinates.y / stageScale} offsetY={stageCoordinates.y / stageScale}
height={stageDimensions.height / stageScale} height={stageDimensions.height / stageScale}
width={stageDimensions.width / stageScale} width={stageDimensions.width / stageScale}
fill={'rgba(0,0,0,0.4)'} fill="rgba(0,0,0,0.4)"
listening={false} listening={false}
visible={shouldDarkenOutsideBoundingBox} visible={shouldDarkenOutsideBoundingBox}
/> />
@ -58,10 +58,10 @@ const IAICanvasBoundingBoxOverlay = () => {
y={boundingBoxCoordinates.y} y={boundingBoxCoordinates.y}
width={boundingBoxDimensions.width} width={boundingBoxDimensions.width}
height={boundingBoxDimensions.height} height={boundingBoxDimensions.height}
fill={'rgb(255,255,255)'} fill="rgb(255,255,255)"
listening={false} listening={false}
visible={shouldDarkenOutsideBoundingBox} visible={shouldDarkenOutsideBoundingBox}
globalCompositeOperation={'destination-out'} globalCompositeOperation="destination-out"
/> />
</Group> </Group>
); );

View File

@ -163,10 +163,10 @@ const IAICanvasMaskCompositer = (props: IAICanvasMaskCompositerProps) => {
width={stageDimensions.width / stageScale} width={stageDimensions.width / stageScale}
fillPatternImage={fillPatternImage} fillPatternImage={fillPatternImage}
fillPatternOffsetY={!isNumber(offset) ? 0 : offset} fillPatternOffsetY={!isNumber(offset) ? 0 : offset}
fillPatternRepeat={'repeat'} fillPatternRepeat="repeat"
fillPatternScale={{ x: 1 / stageScale, y: 1 / stageScale }} fillPatternScale={{ x: 1 / stageScale, y: 1 / stageScale }}
listening={true} listening={true}
globalCompositeOperation={'source-in'} globalCompositeOperation="source-in"
{...rest} {...rest}
/> />
); );

View File

@ -36,7 +36,7 @@ const IAICanvasLines = (props: InpaintingCanvasLinesProps) => {
<Line <Line
key={i} key={i}
points={line.points} points={line.points}
stroke={'rgb(0,0,0)'} // The lines can be any color, just need alpha > 0 stroke="rgb(0,0,0)" // The lines can be any color, just need alpha > 0
strokeWidth={line.strokeWidth * 2} strokeWidth={line.strokeWidth * 2}
tension={0} tension={0}
lineCap="round" lineCap="round"

View File

@ -93,8 +93,8 @@ const IAICanvasObjectRenderer = () => {
y={obj.y} y={obj.y}
width={obj.width} width={obj.width}
height={obj.height} height={obj.height}
fill={'rgb(255, 255, 255)'} fill="rgb(255, 255, 255)"
globalCompositeOperation={'destination-out'} globalCompositeOperation="destination-out"
/> />
); );
} }

View File

@ -67,7 +67,7 @@ const IAICanvasStagingArea = (props: Props) => {
width={width} width={width}
height={height} height={height}
strokeWidth={1} strokeWidth={1}
stroke={'white'} stroke="white"
strokeScaleEnabled={false} strokeScaleEnabled={false}
/> />
<Rect <Rect
@ -77,7 +77,7 @@ const IAICanvasStagingArea = (props: Props) => {
height={height} height={height}
dash={[4, 4]} dash={[4, 4]}
strokeWidth={1} strokeWidth={1}
stroke={'black'} stroke="black"
strokeScaleEnabled={false} strokeScaleEnabled={false}
/> />
</Group> </Group>

View File

@ -114,11 +114,11 @@ const IAICanvasStagingAreaToolbar = () => {
return ( return (
<Flex <Flex
pos={'absolute'} pos="absolute"
bottom={'1rem'} bottom="1rem"
w={'100%'} w="100%"
align={'center'} align="center"
justify={'center'} justify="center"
filter="drop-shadow(0 0.5rem 1rem rgba(0,0,0))" filter="drop-shadow(0 0.5rem 1rem rgba(0,0,0))"
onMouseOver={handleMouseOver} onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut} onMouseOut={handleMouseOut}

View File

@ -172,7 +172,7 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
x={brushX} x={brushX}
y={brushY} y={brushY}
radius={radius} radius={radius}
stroke={'rgba(255,255,255,0.4)'} stroke="rgba(255,255,255,0.4)"
strokeWidth={strokeWidth * 2} strokeWidth={strokeWidth * 2}
strokeEnabled={true} strokeEnabled={true}
listening={false} listening={false}
@ -181,7 +181,7 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
x={brushX} x={brushX}
y={brushY} y={brushY}
radius={radius} radius={radius}
stroke={'rgba(0,0,0,1)'} stroke="rgba(0,0,0,1)"
strokeWidth={strokeWidth} strokeWidth={strokeWidth}
strokeEnabled={true} strokeEnabled={true}
listening={false} listening={false}
@ -192,14 +192,14 @@ const IAICanvasToolPreview = (props: GroupConfig) => {
x={brushX} x={brushX}
y={brushY} y={brushY}
radius={dotRadius * 2} radius={dotRadius * 2}
fill={'rgba(255,255,255,0.4)'} fill="rgba(255,255,255,0.4)"
listening={false} listening={false}
/> />
<Circle <Circle
x={brushX} x={brushX}
y={brushY} y={brushY}
radius={dotRadius} radius={dotRadius}
fill={'rgba(0,0,0,1)'} fill="rgba(0,0,0,1)"
listening={false} listening={false}
/> />
</Group> </Group>

View File

@ -269,12 +269,12 @@ const IAICanvasBoundingBox = (props: IAICanvasBoundingBoxPreviewProps) => {
<Transformer <Transformer
anchorCornerRadius={3} anchorCornerRadius={3}
anchorDragBoundFunc={anchorDragBoundFunc} anchorDragBoundFunc={anchorDragBoundFunc}
anchorFill={'rgba(212,216,234,1)'} anchorFill="rgba(212,216,234,1)"
anchorSize={15} anchorSize={15}
anchorStroke={'rgb(42,42,42)'} anchorStroke="rgb(42,42,42)"
borderDash={[4, 4]} borderDash={[4, 4]}
borderEnabled={true} borderEnabled={true}
borderStroke={'black'} borderStroke="black"
draggable={false} draggable={false}
enabledAnchors={tool === 'move' ? undefined : []} enabledAnchors={tool === 'move' ? undefined : []}
flipEnabled={false} flipEnabled={false}

View File

@ -121,7 +121,7 @@ const IAICanvasMaskOptions = () => {
</ButtonGroup> </ButtonGroup>
} }
> >
<Flex direction={'column'} gap={'0.5rem'}> <Flex direction="column" gap="0.5rem">
<IAICheckbox <IAICheckbox
label={`${t('unifiedcanvas:enableMask')} (H)`} label={`${t('unifiedcanvas:enableMask')} (H)`}
isChecked={isMaskEnabled} isChecked={isMaskEnabled}
@ -139,7 +139,7 @@ const IAICanvasMaskOptions = () => {
color={maskColor} color={maskColor}
onChange={(newColor) => dispatch(setMaskColor(newColor))} onChange={(newColor) => dispatch(setMaskColor(newColor))}
/> />
<IAIButton size={'sm'} leftIcon={<FaTrash />} onClick={handleClearMask}> <IAIButton size="sm" leftIcon={<FaTrash />} onClick={handleClearMask}>
{t('unifiedcanvas:clearMask')} (Shift+C) {t('unifiedcanvas:clearMask')} (Shift+C)
</IAIButton> </IAIButton>
</Flex> </Flex>

View File

@ -97,7 +97,7 @@ const IAICanvasSettingsButtonPopover = () => {
/> />
} }
> >
<Flex direction={'column'} gap={'0.5rem'}> <Flex direction="column" gap="0.5rem">
<IAICheckbox <IAICheckbox
label={t('unifiedcanvas:showIntermediates')} label={t('unifiedcanvas:showIntermediates')}
isChecked={shouldShowIntermediates} isChecked={shouldShowIntermediates}

View File

@ -228,13 +228,8 @@ const IAICanvasToolChooserOptions = () => {
/> />
} }
> >
<Flex <Flex minWidth="15rem" direction="column" gap="1rem" width="100%">
minWidth={'15rem'} <Flex gap="1rem" justifyContent="space-between">
direction={'column'}
gap={'1rem'}
width={'100%'}
>
<Flex gap={'1rem'} justifyContent="space-between">
<IAISlider <IAISlider
label={t('unifiedcanvas:brushSize')} label={t('unifiedcanvas:brushSize')}
value={brushSize} value={brushSize}

View File

@ -415,14 +415,14 @@ const CurrentImageButtons = () => {
> >
<div className="current-image-send-to-popover"> <div className="current-image-send-to-popover">
<IAIButton <IAIButton
size={'sm'} size="sm"
onClick={handleClickUseAsInitialImage} onClick={handleClickUseAsInitialImage}
leftIcon={<FaShare />} leftIcon={<FaShare />}
> >
{t('parameters:sendToImg2Img')} {t('parameters:sendToImg2Img')}
</IAIButton> </IAIButton>
<IAIButton <IAIButton
size={'sm'} size="sm"
onClick={handleSendToCanvas} onClick={handleSendToCanvas}
leftIcon={<FaShare />} leftIcon={<FaShare />}
> >
@ -430,14 +430,14 @@ const CurrentImageButtons = () => {
</IAIButton> </IAIButton>
<IAIButton <IAIButton
size={'sm'} size="sm"
onClick={handleCopyImage} onClick={handleCopyImage}
leftIcon={<FaCopy />} leftIcon={<FaCopy />}
> >
{t('parameters:copyImage')} {t('parameters:copyImage')}
</IAIButton> </IAIButton>
<IAIButton <IAIButton
size={'sm'} size="sm"
onClick={handleCopyImageLink} onClick={handleCopyImageLink}
leftIcon={<FaCopy />} leftIcon={<FaCopy />}
> >
@ -445,7 +445,7 @@ const CurrentImageButtons = () => {
</IAIButton> </IAIButton>
<Link download={true} href={currentImage?.url}> <Link download={true} href={currentImage?.url}>
<IAIButton leftIcon={<FaDownload />} size={'sm'} w="100%"> <IAIButton leftIcon={<FaDownload />} size="sm" w="100%">
{t('parameters:downloadImage')} {t('parameters:downloadImage')}
</IAIButton> </IAIButton>
</Link> </Link>

View File

@ -82,7 +82,7 @@ export default function CurrentImagePreview() {
}; };
return ( return (
<div className={'current-image-preview'}> <div className="current-image-preview">
{imageToDisplay && ( {imageToDisplay && (
<Image <Image
src={imageToDisplay.url} src={imageToDisplay.url}

View File

@ -116,13 +116,13 @@ const DeleteImageModal = forwardRef(
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogBody> <AlertDialogBody>
<Flex direction={'column'} gap={5}> <Flex direction="column" gap={5}>
<Text> <Text>
Are you sure? Deleted images will be sent to the Bin. You Are you sure? Deleted images will be sent to the Bin. You
can restore from there if you wish to. can restore from there if you wish to.
</Text> </Text>
<FormControl> <FormControl>
<Flex alignItems={'center'}> <Flex alignItems="center">
<FormLabel mb={0}>Don&apos;t ask me again</FormLabel> <FormLabel mb={0}>Don&apos;t ask me again</FormLabel>
<Switch <Switch
checked={!shouldConfirmOnDelete} checked={!shouldConfirmOnDelete}

View File

@ -175,12 +175,12 @@ const HoverableImage = memo((props: HoverableImageProps) => {
> >
<ContextMenu.Trigger> <ContextMenu.Trigger>
<Box <Box
position={'relative'} position="relative"
key={uuid} key={uuid}
className="hoverable-image" className="hoverable-image"
onMouseOver={handleMouseOver} onMouseOver={handleMouseOver}
onMouseOut={handleMouseOut} onMouseOut={handleMouseOut}
userSelect={'none'} userSelect="none"
draggable={true} draggable={true}
onDragStart={handleDragStart} onDragStart={handleDragStart}
> >
@ -189,15 +189,15 @@ const HoverableImage = memo((props: HoverableImageProps) => {
objectFit={ objectFit={
shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit shouldUseSingleGalleryColumn ? 'contain' : galleryImageObjectFit
} }
rounded={'md'} rounded="md"
src={thumbnail || url} src={thumbnail || url}
loading={'lazy'} loading="lazy"
/> />
<div className="hoverable-image-content" onClick={handleSelectImage}> <div className="hoverable-image-content" onClick={handleSelectImage}>
{isSelected && ( {isSelected && (
<Icon <Icon
width={'50%'} width="50%"
height={'50%'} height="50%"
as={FaCheck} as={FaCheck}
className="hoverable-image-check" className="hoverable-image-check"
/> />
@ -210,7 +210,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
aria-label={t('parameters:deleteImage')} aria-label={t('parameters:deleteImage')}
icon={<FaTrashAlt />} icon={<FaTrashAlt />}
size="xs" size="xs"
variant={'imageHoverIconButton'} variant="imageHoverIconButton"
fontSize={14} fontSize={14}
isDisabled={!mayDeleteImage} isDisabled={!mayDeleteImage}
/> />
@ -221,7 +221,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
</ContextMenu.Trigger> </ContextMenu.Trigger>
<ContextMenu.Content <ContextMenu.Content
className="hoverable-image-context-menu" className="hoverable-image-context-menu"
sticky={'always'} sticky="always"
onInteractOutside={(e) => { onInteractOutside={(e) => {
e.detail.originalEvent.preventDefault(); e.detail.originalEvent.preventDefault();
}} }}

View File

@ -281,7 +281,7 @@ export default function ImageGallery() {
<Resizable <Resizable
minWidth={galleryMinWidth} minWidth={galleryMinWidth}
maxWidth={shouldPinGallery ? galleryMaxWidth : window.innerWidth} maxWidth={shouldPinGallery ? galleryMaxWidth : window.innerWidth}
className={'image-gallery-popup'} className="image-gallery-popup"
handleStyles={{ handleStyles={{
left: { left: {
width: '15px', width: '15px',
@ -395,14 +395,14 @@ export default function ImageGallery() {
{shouldShowButtons ? ( {shouldShowButtons ? (
<> <>
<IAIButton <IAIButton
size={'sm'} size="sm"
data-selected={currentCategory === 'result'} data-selected={currentCategory === 'result'}
onClick={() => dispatch(setCurrentCategory('result'))} onClick={() => dispatch(setCurrentCategory('result'))}
> >
{t('gallery:generations')} {t('gallery:generations')}
</IAIButton> </IAIButton>
<IAIButton <IAIButton
size={'sm'} size="sm"
data-selected={currentCategory === 'user'} data-selected={currentCategory === 'user'}
onClick={() => dispatch(setCurrentCategory('user'))} onClick={() => dispatch(setCurrentCategory('user'))}
> >
@ -433,14 +433,14 @@ export default function ImageGallery() {
<IAIPopover <IAIPopover
isLazy isLazy
trigger="hover" trigger="hover"
placement={'left'} placement="left"
triggerComponent={ triggerComponent={
<IAIIconButton <IAIIconButton
size={'sm'} size="sm"
aria-label={t('gallery:gallerySettings')} aria-label={t('gallery:gallerySettings')}
icon={<FaWrench />} icon={<FaWrench />}
className="image-gallery-icon-btn" className="image-gallery-icon-btn"
cursor={'pointer'} cursor="pointer"
/> />
} }
> >
@ -455,7 +455,7 @@ export default function ImageGallery() {
label={t('gallery:galleryImageSize')} label={t('gallery:galleryImageSize')}
/> />
<IAIIconButton <IAIIconButton
size={'sm'} size="sm"
aria-label={t('gallery:galleryImageResetSize')} aria-label={t('gallery:galleryImageResetSize')}
tooltip={t('gallery:galleryImageResetSize')} tooltip={t('gallery:galleryImageResetSize')}
onClick={() => dispatch(setGalleryImageMinimumWidth(64))} onClick={() => dispatch(setGalleryImageMinimumWidth(64))}
@ -505,8 +505,8 @@ export default function ImageGallery() {
</IAIPopover> </IAIPopover>
<IAIIconButton <IAIIconButton
size={'sm'} size="sm"
className={'image-gallery-icon-btn'} className="image-gallery-icon-btn"
aria-label={t('gallery:pinGallery')} aria-label={t('gallery:pinGallery')}
tooltip={`${t('gallery:pinGallery')} (Shift+G)`} tooltip={`${t('gallery:pinGallery')} (Shift+G)`}
onClick={handleSetShouldPinGallery} onClick={handleSetShouldPinGallery}

View File

@ -71,8 +71,8 @@ const MetadataItem = ({
<IconButton <IconButton
aria-label="Use this parameter" aria-label="Use this parameter"
icon={<IoArrowUndoCircleOutline />} icon={<IoArrowUndoCircleOutline />}
size={'xs'} size="xs"
variant={'ghost'} variant="ghost"
fontSize={20} fontSize={20}
onClick={onClick} onClick={onClick}
/> />
@ -83,23 +83,23 @@ const MetadataItem = ({
<IconButton <IconButton
aria-label={`Copy ${label}`} aria-label={`Copy ${label}`}
icon={<FaCopy />} icon={<FaCopy />}
size={'xs'} size="xs"
variant={'ghost'} variant="ghost"
fontSize={14} fontSize={14}
onClick={() => navigator.clipboard.writeText(value.toString())} onClick={() => navigator.clipboard.writeText(value.toString())}
/> />
</Tooltip> </Tooltip>
)} )}
<Flex direction={labelPosition ? 'column' : 'row'}> <Flex direction={labelPosition ? 'column' : 'row'}>
<Text fontWeight={'semibold'} whiteSpace={'pre-wrap'} pr={2}> <Text fontWeight="semibold" whiteSpace="pre-wrap" pr={2}>
{label}: {label}:
</Text> </Text>
{isLink ? ( {isLink ? (
<Link href={value.toString()} isExternal wordBreak={'break-all'}> <Link href={value.toString()} isExternal wordBreak="break-all">
{value.toString()} <ExternalLinkIcon mx="2px" /> {value.toString()} <ExternalLinkIcon mx="2px" />
</Link> </Link>
) : ( ) : (
<Text overflowY={'scroll'} wordBreak={'break-all'}> <Text overflowY="scroll" wordBreak="break-all">
{value.toString()} {value.toString()}
</Text> </Text>
)} )}
@ -163,10 +163,10 @@ const ImageMetadataViewer = memo(
return ( return (
<div className={`image-metadata-viewer ${styleClass}`}> <div className={`image-metadata-viewer ${styleClass}`}>
<Flex gap={1} direction={'column'} width={'100%'}> <Flex gap={1} direction="column" width="100%">
<Flex gap={2}> <Flex gap={2}>
<Text fontWeight={'semibold'}>File:</Text> <Text fontWeight="semibold">File:</Text>
<Link href={image.url} isExternal maxW={'calc(100% - 3rem)'}> <Link href={image.url} isExternal maxW="calc(100% - 3rem)">
{image.url.length > 64 {image.url.length > 64
? image.url.substring(0, 64).concat('...') ? image.url.substring(0, 64).concat('...')
: image.url} : image.url}
@ -304,7 +304,7 @@ const ImageMetadataViewer = memo(
)} )}
{postprocessing && postprocessing.length > 0 && ( {postprocessing && postprocessing.length > 0 && (
<> <>
<Heading size={'sm'}>Postprocessing</Heading> <Heading size="sm">Postprocessing</Heading>
{postprocessing.map( {postprocessing.map(
( (
postprocess: InvokeAI.PostProcessedImageMetadata, postprocess: InvokeAI.PostProcessedImageMetadata,
@ -313,13 +313,8 @@ const ImageMetadataViewer = memo(
if (postprocess.type === 'esrgan') { if (postprocess.type === 'esrgan') {
const { scale, strength, denoise_str } = postprocess; const { scale, strength, denoise_str } = postprocess;
return ( return (
<Flex <Flex key={i} pl="2rem" gap={1} direction="column">
key={i} <Text size="md">{`${
pl={'2rem'}
gap={1}
direction={'column'}
>
<Text size={'md'}>{`${
i + 1 i + 1
}: Upscale (ESRGAN)`}</Text> }: Upscale (ESRGAN)`}</Text>
<MetadataItem <MetadataItem
@ -348,13 +343,8 @@ const ImageMetadataViewer = memo(
} else if (postprocess.type === 'gfpgan') { } else if (postprocess.type === 'gfpgan') {
const { strength } = postprocess; const { strength } = postprocess;
return ( return (
<Flex <Flex key={i} pl="2rem" gap={1} direction="column">
key={i} <Text size="md">{`${
pl={'2rem'}
gap={1}
direction={'column'}
>
<Text size={'md'}>{`${
i + 1 i + 1
}: Face restoration (GFPGAN)`}</Text> }: Face restoration (GFPGAN)`}</Text>
@ -371,13 +361,8 @@ const ImageMetadataViewer = memo(
} else if (postprocess.type === 'codeformer') { } else if (postprocess.type === 'codeformer') {
const { strength, fidelity } = postprocess; const { strength, fidelity } = postprocess;
return ( return (
<Flex <Flex key={i} pl="2rem" gap={1} direction="column">
key={i} <Text size="md">{`${
pl={'2rem'}
gap={1}
direction={'column'}
>
<Text size={'md'}>{`${
i + 1 i + 1
}: Face restoration (Codeformer)`}</Text> }: Face restoration (Codeformer)`}</Text>
@ -413,30 +398,30 @@ const ImageMetadataViewer = memo(
value={dreamPrompt} value={dreamPrompt}
/> />
)} )}
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<Flex gap={2}> <Flex gap={2}>
<Tooltip label={`Copy metadata JSON`}> <Tooltip label="Copy metadata JSON">
<IconButton <IconButton
aria-label="Copy metadata JSON" aria-label="Copy metadata JSON"
icon={<FaCopy />} icon={<FaCopy />}
size={'xs'} size="xs"
variant={'ghost'} variant="ghost"
fontSize={14} fontSize={14}
onClick={() => onClick={() =>
navigator.clipboard.writeText(metadataJSON) navigator.clipboard.writeText(metadataJSON)
} }
/> />
</Tooltip> </Tooltip>
<Text fontWeight={'semibold'}>Metadata JSON:</Text> <Text fontWeight="semibold">Metadata JSON:</Text>
</Flex> </Flex>
<div className={'image-json-viewer'}> <div className="image-json-viewer">
<pre>{metadataJSON}</pre> <pre>{metadataJSON}</pre>
</div> </div>
</Flex> </Flex>
</> </>
) : ( ) : (
<Center width={'100%'} pt={10}> <Center width="100%" pt={10}>
<Text fontSize={'lg'} fontWeight="semibold"> <Text fontSize="lg" fontWeight="semibold">
No metadata available No metadata available
</Text> </Text>
</Center> </Center>

View File

@ -23,8 +23,8 @@ export default function InvokeAccordionItem(props: InvokeAccordionItemProps) {
return ( return (
<AccordionItem className="advanced-parameters-item"> <AccordionItem className="advanced-parameters-item">
<AccordionButton className="advanced-parameters-header"> <AccordionButton className="advanced-parameters-header">
<Flex width={'100%'} gap={'0.5rem'} align={'center'}> <Flex width="100%" gap="0.5rem" align="center">
<Box flexGrow={1} textAlign={'left'}> <Box flexGrow={1} textAlign="left">
{header} {header}
</Box> </Box>
{additionalHeaderComponents} {additionalHeaderComponents}

View File

@ -63,7 +63,7 @@ const FaceRestoreSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Flex direction={'column'} gap={2}> <Flex direction="column" gap={2}>
<IAISelect <IAISelect
label={t('parameters:type')} label={t('parameters:type')}
validValues={FACETOOL_TYPES.concat()} validValues={FACETOOL_TYPES.concat()}

View File

@ -36,7 +36,7 @@ export default function ImageToImageStrength(props: ImageToImageStrengthProps) {
styleClass={styleClass} styleClass={styleClass}
withInput withInput
withSliderMarks withSliderMarks
inputWidth={'5.5rem'} inputWidth="5.5rem"
withReset withReset
handleReset={handleImg2ImgStrengthReset} handleReset={handleImg2ImgStrengthReset}
/> />

View File

@ -49,7 +49,7 @@ const HiresStrength = () => {
isInteger={false} isInteger={false}
withInput withInput
withSliderMarks withSliderMarks
inputWidth={'5.5rem'} inputWidth="5.5rem"
withReset withReset
handleReset={handleHiResStrengthReset} handleReset={handleHiResStrengthReset}
isSliderDisabled={!hiresFix} isSliderDisabled={!hiresFix}
@ -75,10 +75,10 @@ const HiresSettings = () => {
dispatch(setHiresFix(e.target.checked)); dispatch(setHiresFix(e.target.checked));
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<IAISwitch <IAISwitch
label={t('parameters:hiresOptim')} label={t('parameters:hiresOptim')}
fontSize={'md'} fontSize="md"
isChecked={hiresFix} isChecked={hiresFix}
onChange={handleChangeHiresFix} onChange={handleChangeHiresFix}
/> />

View File

@ -3,7 +3,7 @@ import SeamlessSettings from './SeamlessSettings';
const ImageToImageOutputSettings = () => { const ImageToImageOutputSettings = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<SeamlessSettings /> <SeamlessSettings />
</Flex> </Flex>
); );

View File

@ -4,7 +4,7 @@ import SeamlessSettings from './SeamlessSettings';
const OutputSettings = () => { const OutputSettings = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<SeamlessSettings /> <SeamlessSettings />
<HiresSettings /> <HiresSettings />
</Flex> </Flex>

View File

@ -22,10 +22,10 @@ const SeamlessSettings = () => {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<IAISwitch <IAISwitch
label={t('parameters:seamlessTiling')} label={t('parameters:seamlessTiling')}
fontSize={'md'} fontSize="md"
isChecked={seamless} isChecked={seamless}
onChange={handleChangeSeamless} onChange={handleChangeSeamless}
/> />

View File

@ -10,7 +10,7 @@ import Threshold from './Threshold';
*/ */
const SeedSettings = () => { const SeedSettings = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<RandomizeSeed /> <RandomizeSeed />
<Flex gap={2}> <Flex gap={2}>
<Seed /> <Seed />

View File

@ -18,7 +18,7 @@ export default function ShuffleSeed() {
return ( return (
<Button <Button
size={'sm'} size="sm"
isDisabled={shouldRandomizeSeed} isDisabled={shouldRandomizeSeed}
onClick={handleClickRandomizeSeed} onClick={handleClickRandomizeSeed}
padding="0 1.5rem" padding="0 1.5rem"

View File

@ -18,7 +18,7 @@ export default function GenerateVariationsToggle() {
return ( return (
<IAISwitch <IAISwitch
isChecked={shouldGenerateVariations} isChecked={shouldGenerateVariations}
width={'auto'} width="auto"
onChange={handleChangeShouldGenerateVariations} onChange={handleChangeShouldGenerateVariations}
/> />
); );

View File

@ -7,7 +7,7 @@ import VariationAmount from './VariationAmount';
*/ */
const VariationsSettings = () => { const VariationsSettings = () => {
return ( return (
<Flex gap={2} direction={'column'}> <Flex gap={2} direction="column">
<VariationAmount /> <VariationAmount />
<SeedWeights /> <SeedWeights />
</Flex> </Flex>

View File

@ -71,7 +71,7 @@ const PromptInput = () => {
id="prompt" id="prompt"
name="prompt" name="prompt"
placeholder={t('parameters:promptPlaceholder')} placeholder={t('parameters:promptPlaceholder')}
size={'lg'} size="lg"
value={prompt} value={prompt}
onChange={handleChangePrompt} onChange={handleChangePrompt}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}

View File

@ -27,7 +27,7 @@ const EmptyTempFolderButtonModal = () => {
acceptCallback={acceptCallback} acceptCallback={acceptCallback}
acceptButtonText={t('unifiedcanvas:emptyFolder')} acceptButtonText={t('unifiedcanvas:emptyFolder')}
triggerComponent={ triggerComponent={
<IAIButton leftIcon={<FaTrash />} size={'sm'} isDisabled={isStaging}> <IAIButton leftIcon={<FaTrash />} size="sm" isDisabled={isStaging}>
{t('unifiedcanvas:emptyTempImageFolder')} {t('unifiedcanvas:emptyTempImageFolder')}
</IAIButton> </IAIButton>
} }

View File

@ -109,7 +109,7 @@ const Console = () => {
bottom: 0, bottom: 0,
zIndex: 9999, zIndex: 9999,
}} }}
maxHeight={'90vh'} maxHeight="90vh"
> >
<div className="console" ref={viewerRef} onScroll={handleOnScroll}> <div className="console" ref={viewerRef} onScroll={handleOnScroll}>
{log.map((entry, i) => { {log.map((entry, i) => {
@ -130,11 +130,11 @@ const Console = () => {
label={shouldAutoscroll ? 'Autoscroll On' : 'Autoscroll Off'} label={shouldAutoscroll ? 'Autoscroll On' : 'Autoscroll Off'}
> >
<IconButton <IconButton
className={'console-autoscroll-icon-button'} className="console-autoscroll-icon-button"
data-autoscroll-enabled={shouldAutoscroll} data-autoscroll-enabled={shouldAutoscroll}
size="sm" size="sm"
aria-label="Toggle autoscroll" aria-label="Toggle autoscroll"
variant={'solid'} variant="solid"
icon={<FaAngleDoubleDown />} icon={<FaAngleDoubleDown />}
onClick={() => setShouldAutoscroll(!shouldAutoscroll)} onClick={() => setShouldAutoscroll(!shouldAutoscroll)}
/> />
@ -145,11 +145,11 @@ const Console = () => {
label={shouldShowLogViewer ? 'Hide Console' : 'Show Console'} label={shouldShowLogViewer ? 'Hide Console' : 'Show Console'}
> >
<IconButton <IconButton
className={'console-toggle-icon-button'} className="console-toggle-icon-button"
data-error-seen={hasError || !wasErrorSeen} data-error-seen={hasError || !wasErrorSeen}
size="sm" size="sm"
position={'fixed'} position="fixed"
variant={'solid'} variant="solid"
aria-label="Toggle Log Viewer" aria-label="Toggle Log Viewer"
icon={shouldShowLogViewer ? <FaMinus /> : <FaCode />} icon={shouldShowLogViewer ? <FaMinus /> : <FaCode />}
onClick={handleClickLogViewerToggle} onClick={handleClickLogViewerToggle}

View File

@ -54,7 +54,7 @@ export default function LanguagePicker() {
aria-label={t('common:languagePickerLabel')} aria-label={t('common:languagePickerLabel')}
tooltip={t('common:languagePickerLabel')} tooltip={t('common:languagePickerLabel')}
icon={<FaLanguage />} icon={<FaLanguage />}
size={'sm'} size="sm"
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={26} fontSize={26}

View File

@ -99,8 +99,8 @@ export default function AddCheckpointModel() {
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'}> <VStack rowGap="0.5rem">
<Text fontSize={20} fontWeight="bold" alignSelf={'start'}> <Text fontSize={20} fontWeight="bold" alignSelf="start">
{t('modelmanager:manual')} {t('modelmanager:manual')}
</Text> </Text>
{/* Name */} {/* Name */}
@ -111,7 +111,7 @@ export default function AddCheckpointModel() {
<FormLabel htmlFor="name" fontSize="sm"> <FormLabel htmlFor="name" fontSize="sm">
{t('modelmanager:name')} {t('modelmanager:name')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="name" id="name"
@ -138,7 +138,7 @@ export default function AddCheckpointModel() {
<FormLabel htmlFor="description" fontSize="sm"> <FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')} {t('modelmanager:description')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="description" id="description"
@ -164,7 +164,7 @@ export default function AddCheckpointModel() {
<FormLabel htmlFor="config" fontSize="sm"> <FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:config')} {t('modelmanager:config')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="config" id="config"
@ -190,7 +190,7 @@ export default function AddCheckpointModel() {
<FormLabel htmlFor="config" fontSize="sm"> <FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:modelLocation')} {t('modelmanager:modelLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="weights" id="weights"
@ -213,7 +213,7 @@ export default function AddCheckpointModel() {
<FormLabel htmlFor="vae" fontSize="sm"> <FormLabel htmlFor="vae" fontSize="sm">
{t('modelmanager:vaeLocation')} {t('modelmanager:vaeLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="vae" id="vae"
@ -231,13 +231,13 @@ export default function AddCheckpointModel() {
</VStack> </VStack>
</FormControl> </FormControl>
<HStack width={'100%'}> <HStack width="100%">
{/* Width */} {/* Width */}
<FormControl isInvalid={!!errors.width && touched.width}> <FormControl isInvalid={!!errors.width && touched.width}>
<FormLabel htmlFor="width" fontSize="sm"> <FormLabel htmlFor="width" fontSize="sm">
{t('modelmanager:width')} {t('modelmanager:width')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field id="width" name="width"> <Field id="width" name="width">
{({ {({
field, field,
@ -276,7 +276,7 @@ export default function AddCheckpointModel() {
<FormLabel htmlFor="height" fontSize="sm"> <FormLabel htmlFor="height" fontSize="sm">
{t('modelmanager:height')} {t('modelmanager:height')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field id="height" name="height"> <Field id="height" name="height">
{({ {({
field, field,

View File

@ -105,7 +105,7 @@ export default function AddDiffusersModel() {
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'}> <VStack rowGap="0.5rem">
<FormItemWrapper> <FormItemWrapper>
{/* Name */} {/* Name */}
<FormControl <FormControl
@ -115,7 +115,7 @@ export default function AddDiffusersModel() {
<FormLabel htmlFor="name" fontSize="sm"> <FormLabel htmlFor="name" fontSize="sm">
{t('modelmanager:name')} {t('modelmanager:name')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="name" id="name"
@ -145,7 +145,7 @@ export default function AddDiffusersModel() {
<FormLabel htmlFor="description" fontSize="sm"> <FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')} {t('modelmanager:description')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="description" id="description"
@ -182,7 +182,7 @@ export default function AddDiffusersModel() {
<FormLabel htmlFor="path" fontSize="sm"> <FormLabel htmlFor="path" fontSize="sm">
{t('modelmanager:modelLocation')} {t('modelmanager:modelLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="path" id="path"
@ -205,7 +205,7 @@ export default function AddDiffusersModel() {
<FormLabel htmlFor="repo_id" fontSize="sm"> <FormLabel htmlFor="repo_id" fontSize="sm">
{t('modelmanager:repo_id')} {t('modelmanager:repo_id')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="repo_id" id="repo_id"
@ -242,7 +242,7 @@ export default function AddDiffusersModel() {
<FormLabel htmlFor="vae.path" fontSize="sm"> <FormLabel htmlFor="vae.path" fontSize="sm">
{t('modelmanager:vaeLocation')} {t('modelmanager:vaeLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="vae.path" id="vae.path"
@ -267,7 +267,7 @@ export default function AddDiffusersModel() {
<FormLabel htmlFor="vae.repo_id" fontSize="sm"> <FormLabel htmlFor="vae.repo_id" fontSize="sm">
{t('modelmanager:vaeRepoID')} {t('modelmanager:vaeRepoID')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="vae.repo_id" id="vae.repo_id"

View File

@ -72,9 +72,9 @@ export default function AddModel() {
tooltip={t('modelmanager:addNewModel')} tooltip={t('modelmanager:addNewModel')}
onClick={onOpen} onClick={onOpen}
className="modal-close-btn" className="modal-close-btn"
size={'sm'} size="sm"
> >
<Flex columnGap={'0.5rem'} alignItems="center"> <Flex columnGap="0.5rem" alignItems="center">
<FaPlus /> <FaPlus />
{t('modelmanager:addNew')} {t('modelmanager:addNew')}
</Flex> </Flex>

View File

@ -27,6 +27,7 @@ import type { InvokeModelConfigProps } from 'app/invokeai';
import type { RootState } from 'app/store'; import type { RootState } from 'app/store';
import type { FieldInputProps, FormikProps } from 'formik'; import type { FieldInputProps, FormikProps } from 'formik';
import { isEqual, pickBy } from 'lodash'; import { isEqual, pickBy } from 'lodash';
import ModelConvert from './ModelConvert';
const selector = createSelector( const selector = createSelector(
[systemSelector], [systemSelector],
@ -101,10 +102,11 @@ export default function CheckpointModelEdit() {
return openModel ? ( return openModel ? (
<Flex flexDirection="column" rowGap="1rem" width="100%"> <Flex flexDirection="column" rowGap="1rem" width="100%">
<Flex alignItems="center"> <Flex alignItems="center" gap={4} justifyContent="space-between">
<Text fontSize="lg" fontWeight="bold"> <Text fontSize="lg" fontWeight="bold">
{openModel} {openModel}
</Text> </Text>
<ModelConvert model={openModel} />
</Flex> </Flex>
<Flex <Flex
flexDirection="column" flexDirection="column"
@ -119,7 +121,7 @@ export default function CheckpointModelEdit() {
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'} alignItems="start"> <VStack rowGap="0.5rem" alignItems="start">
{/* Description */} {/* Description */}
<FormControl <FormControl
isInvalid={!!errors.description && touched.description} isInvalid={!!errors.description && touched.description}
@ -128,7 +130,7 @@ export default function CheckpointModelEdit() {
<FormLabel htmlFor="description" fontSize="sm"> <FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')} {t('modelmanager:description')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="description" id="description"
@ -154,7 +156,7 @@ export default function CheckpointModelEdit() {
<FormLabel htmlFor="config" fontSize="sm"> <FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:config')} {t('modelmanager:config')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="config" id="config"
@ -180,7 +182,7 @@ export default function CheckpointModelEdit() {
<FormLabel htmlFor="config" fontSize="sm"> <FormLabel htmlFor="config" fontSize="sm">
{t('modelmanager:modelLocation')} {t('modelmanager:modelLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="weights" id="weights"
@ -203,7 +205,7 @@ export default function CheckpointModelEdit() {
<FormLabel htmlFor="vae" fontSize="sm"> <FormLabel htmlFor="vae" fontSize="sm">
{t('modelmanager:vaeLocation')} {t('modelmanager:vaeLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="vae" id="vae"
@ -221,13 +223,13 @@ export default function CheckpointModelEdit() {
</VStack> </VStack>
</FormControl> </FormControl>
<HStack width={'100%'}> <HStack width="100%">
{/* Width */} {/* Width */}
<FormControl isInvalid={!!errors.width && touched.width}> <FormControl isInvalid={!!errors.width && touched.width}>
<FormLabel htmlFor="width" fontSize="sm"> <FormLabel htmlFor="width" fontSize="sm">
{t('modelmanager:width')} {t('modelmanager:width')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field id="width" name="width"> <Field id="width" name="width">
{({ {({
field, field,
@ -265,7 +267,7 @@ export default function CheckpointModelEdit() {
<FormLabel htmlFor="height" fontSize="sm"> <FormLabel htmlFor="height" fontSize="sm">
{t('modelmanager:height')} {t('modelmanager:height')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field id="height" name="height"> <Field id="height" name="height">
{({ {({
field, field,

View File

@ -128,7 +128,7 @@ export default function DiffusersModelEdit() {
> >
{({ handleSubmit, errors, touched }) => ( {({ handleSubmit, errors, touched }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<VStack rowGap={'0.5rem'} alignItems="start"> <VStack rowGap="0.5rem" alignItems="start">
{/* Description */} {/* Description */}
<FormControl <FormControl
isInvalid={!!errors.description && touched.description} isInvalid={!!errors.description && touched.description}
@ -137,7 +137,7 @@ export default function DiffusersModelEdit() {
<FormLabel htmlFor="description" fontSize="sm"> <FormLabel htmlFor="description" fontSize="sm">
{t('modelmanager:description')} {t('modelmanager:description')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="description" id="description"
@ -163,7 +163,7 @@ export default function DiffusersModelEdit() {
<FormLabel htmlFor="path" fontSize="sm"> <FormLabel htmlFor="path" fontSize="sm">
{t('modelmanager:modelLocation')} {t('modelmanager:modelLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="path" id="path"
@ -186,7 +186,7 @@ export default function DiffusersModelEdit() {
<FormLabel htmlFor="repo_id" fontSize="sm"> <FormLabel htmlFor="repo_id" fontSize="sm">
{t('modelmanager:repo_id')} {t('modelmanager:repo_id')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="repo_id" id="repo_id"
@ -211,7 +211,7 @@ export default function DiffusersModelEdit() {
<FormLabel htmlFor="vae.path" fontSize="sm"> <FormLabel htmlFor="vae.path" fontSize="sm">
{t('modelmanager:vaeLocation')} {t('modelmanager:vaeLocation')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="vae.path" id="vae.path"
@ -236,7 +236,7 @@ export default function DiffusersModelEdit() {
<FormLabel htmlFor="vae.repo_id" fontSize="sm"> <FormLabel htmlFor="vae.repo_id" fontSize="sm">
{t('modelmanager:vaeRepoID')} {t('modelmanager:vaeRepoID')}
</FormLabel> </FormLabel>
<VStack alignItems={'start'}> <VStack alignItems="start">
<Field <Field
as={IAIInput} as={IAIInput}
id="vae.repo_id" id="vae.repo_id"

View File

@ -0,0 +1,148 @@
import {
Flex,
ListItem,
Radio,
RadioGroup,
Text,
UnorderedList,
Tooltip,
} from '@chakra-ui/react';
import { convertToDiffusers } from 'app/socketio/actions';
import { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAIAlertDialog from 'common/components/IAIAlertDialog';
import IAIButton from 'common/components/IAIButton';
import IAIInput from 'common/components/IAIInput';
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
interface ModelConvertProps {
model: string;
}
export default function ModelConvert(props: ModelConvertProps) {
const { model } = props;
const model_list = useAppSelector(
(state: RootState) => state.system.model_list
);
const retrievedModel = model_list[model];
const dispatch = useAppDispatch();
const { t } = useTranslation();
const isProcessing = useAppSelector(
(state: RootState) => state.system.isProcessing
);
const isConnected = useAppSelector(
(state: RootState) => state.system.isConnected
);
const [saveLocation, setSaveLocation] = useState<string>('same');
const [customSaveLocation, setCustomSaveLocation] = useState<string>('');
useEffect(() => {
setSaveLocation('same');
}, [model]);
const modelConvertCancelHandler = () => {
setSaveLocation('same');
};
const modelConvertHandler = () => {
const modelToConvert = {
model_name: model,
save_location: saveLocation,
custom_location:
saveLocation === 'custom' && customSaveLocation !== ''
? customSaveLocation
: null,
};
dispatch(convertToDiffusers(modelToConvert));
};
return (
<IAIAlertDialog
title={`${t('modelmanager:convert')} ${model}`}
acceptCallback={modelConvertHandler}
cancelCallback={modelConvertCancelHandler}
acceptButtonText={`${t('modelmanager:convert')}`}
triggerComponent={
<IAIButton
size={'sm'}
aria-label={t('modelmanager:convertToDiffusers')}
isDisabled={
retrievedModel.status === 'active' || isProcessing || !isConnected
}
className=" modal-close-btn"
marginRight="2rem"
>
🧨 {t('modelmanager:convertToDiffusers')}
</IAIButton>
}
motionPreset="slideInBottom"
>
<Flex flexDirection="column" rowGap={4}>
<Text>{t('modelmanager:convertToDiffusersHelpText1')}</Text>
<UnorderedList>
<ListItem>{t('modelmanager:convertToDiffusersHelpText2')}</ListItem>
<ListItem>{t('modelmanager:convertToDiffusersHelpText3')}</ListItem>
<ListItem>{t('modelmanager:convertToDiffusersHelpText4')}</ListItem>
<ListItem>{t('modelmanager:convertToDiffusersHelpText5')}</ListItem>
</UnorderedList>
<Text>{t('modelmanager:convertToDiffusersHelpText6')}</Text>
</Flex>
<Flex flexDir="column" gap={4}>
<Flex marginTop="1rem" flexDir="column" gap={2}>
<Text fontWeight="bold">
{t('modelmanager:convertToDiffusersSaveLocation')}
</Text>
<RadioGroup value={saveLocation} onChange={(v) => setSaveLocation(v)}>
<Flex gap={4}>
<Radio value="same">
<Tooltip label="Save converted model in the same folder">
{t('modelmanager:sameFolder')}
</Tooltip>
</Radio>
<Radio value="root">
<Tooltip label="Save converted model in the InvokeAI root folder">
{t('modelmanager:invokeRoot')}
</Tooltip>
</Radio>
<Radio value="custom">
<Tooltip label="Save converted model in a custom folder">
{t('modelmanager:custom')}
</Tooltip>
</Radio>
</Flex>
</RadioGroup>
</Flex>
{saveLocation === 'custom' && (
<Flex flexDirection="column" rowGap={2}>
<Text
fontWeight="bold"
fontSize="sm"
color="var(--text-color-secondary)"
>
{t('modelmanager:customSaveLocation')}
</Text>
<IAIInput
value={customSaveLocation}
onChange={(e) => {
if (e.target.value !== '')
setCustomSaveLocation(e.target.value);
}}
width="25rem"
/>
</Flex>
)}
</Flex>
</IAIAlertDialog>
);
}

View File

@ -176,9 +176,9 @@ const ModelList = () => {
}, [models, searchText, t, isSelectedFilter]); }, [models, searchText, t, isSelectedFilter]);
return ( return (
<Flex flexDirection={'column'} rowGap="2rem" width="50%" minWidth="50%"> <Flex flexDirection="column" rowGap="2rem" width="50%" minWidth="50%">
<Flex justifyContent={'space-between'}> <Flex justifyContent="space-between">
<Text fontSize={'1.4rem'} fontWeight="bold"> <Text fontSize="1.4rem" fontWeight="bold">
{t('modelmanager:availableModels')} {t('modelmanager:availableModels')}
</Text> </Text>
<AddModel /> <AddModel />
@ -190,10 +190,10 @@ const ModelList = () => {
/> />
<Flex <Flex
flexDirection={'column'} flexDirection="column"
gap={1} gap={1}
maxHeight={window.innerHeight - 360} maxHeight={window.innerHeight - 360}
overflow={'scroll'} overflow="scroll"
paddingRight="1rem" paddingRight="1rem"
> >
<Flex columnGap="0.5rem"> <Flex columnGap="0.5rem">

View File

@ -56,7 +56,7 @@ export default function ModelListItem(props: ModelListItemProps) {
return ( return (
<Flex <Flex
alignItems={'center'} alignItems="center"
padding="0.5rem 0.5rem" padding="0.5rem 0.5rem"
borderRadius="0.2rem" borderRadius="0.2rem"
backgroundColor={name === openModel ? 'var(--accent-color)' : ''} backgroundColor={name === openModel ? 'var(--accent-color)' : ''}
@ -69,23 +69,24 @@ export default function ModelListItem(props: ModelListItemProps) {
> >
<Box onClick={openModelHandler} cursor="pointer"> <Box onClick={openModelHandler} cursor="pointer">
<Tooltip label={description} hasArrow placement="bottom"> <Tooltip label={description} hasArrow placement="bottom">
<Text fontWeight={'bold'}>{name}</Text> <Text fontWeight="bold">{name}</Text>
</Tooltip> </Tooltip>
</Box> </Box>
<Spacer onClick={openModelHandler} cursor="pointer" /> <Spacer onClick={openModelHandler} cursor="pointer" />
<Flex gap={2} alignItems="center"> <Flex gap={2} alignItems="center">
<Text color={statusTextColor()}>{status}</Text> <Text color={statusTextColor()}>{status}</Text>
<Button <Button
size={'sm'} size="sm"
onClick={handleChangeModel} onClick={handleChangeModel}
isDisabled={status === 'active' || isProcessing || !isConnected} isDisabled={status === 'active' || isProcessing || !isConnected}
className="modal-close-btn" className="modal-close-btn"
> >
{t('modelmanager:load')} {t('modelmanager:load')}
</Button> </Button>
<IAIIconButton <IAIIconButton
icon={<EditIcon />} icon={<EditIcon />}
size={'sm'} size="sm"
onClick={openModelHandler} onClick={openModelHandler}
aria-label="Modify Config" aria-label="Modify Config"
isDisabled={status === 'active' || isProcessing || !isConnected} isDisabled={status === 'active' || isProcessing || !isConnected}
@ -98,7 +99,7 @@ export default function ModelListItem(props: ModelListItemProps) {
triggerComponent={ triggerComponent={
<IAIIconButton <IAIIconButton
icon={<DeleteIcon />} icon={<DeleteIcon />}
size={'sm'} size="sm"
aria-label={t('modelmanager:deleteConfig')} aria-label={t('modelmanager:deleteConfig')}
isDisabled={status === 'active' || isProcessing || !isConnected} isDisabled={status === 'active' || isProcessing || !isConnected}
className=" modal-close-btn" className=" modal-close-btn"
@ -106,7 +107,7 @@ export default function ModelListItem(props: ModelListItemProps) {
/> />
} }
> >
<Flex rowGap={'1rem'} flexDirection="column"> <Flex rowGap="1rem" flexDirection="column">
<p style={{ fontWeight: 'bold' }}>{t('modelmanager:deleteMsg1')}</p> <p style={{ fontWeight: 'bold' }}>{t('modelmanager:deleteMsg1')}</p>
<p style={{ color: 'var(--text-color-secondary' }}> <p style={{ color: 'var(--text-color-secondary' }}>
{t('modelmanager:deleteMsg2')} {t('modelmanager:deleteMsg2')}

View File

@ -58,11 +58,7 @@ export default function ModelManagerModal({
<ModalHeader fontWeight="bold"> <ModalHeader fontWeight="bold">
{t('modelmanager:modelManager')} {t('modelmanager:modelManager')}
</ModalHeader> </ModalHeader>
<Flex <Flex padding="0 1.5rem 1.5rem 1.5rem" width="100%" columnGap="2rem">
padding={'0 1.5rem 1.5rem 1.5rem'}
width="100%"
columnGap={'2rem'}
>
<ModelList /> <ModelList />
{openModel && model_list[openModel]['format'] === 'diffusers' ? ( {openModel && model_list[openModel]['format'] === 'diffusers' ? (
<DiffusersModelEdit /> <DiffusersModelEdit />

View File

@ -3,7 +3,16 @@ import IAICheckbox from 'common/components/IAICheckbox';
import IAIIconButton from 'common/components/IAIIconButton'; import IAIIconButton from 'common/components/IAIIconButton';
import React from 'react'; import React from 'react';
import { Box, Flex, FormControl, HStack, Text, VStack } from '@chakra-ui/react'; import {
Box,
Flex,
FormControl,
HStack,
Radio,
RadioGroup,
Text,
VStack,
} from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppDispatch, useAppSelector } from 'app/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { systemSelector } from 'features/system/store/systemSelectors'; import { systemSelector } from 'features/system/store/systemSelectors';
@ -43,16 +52,16 @@ function ModelExistsTag() {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<Box <Box
position={'absolute'} position="absolute"
zIndex={2} zIndex={2}
right={4} right={4}
top={4} top={4}
fontSize="0.7rem" fontSize="0.7rem"
fontWeight={'bold'} fontWeight="bold"
backgroundColor={'var(--accent-color)'} backgroundColor="var(--accent-color)"
padding={'0.2rem 0.5rem'} padding="0.2rem 0.5rem"
borderRadius="0.2rem" borderRadius="0.2rem"
alignItems={'center'} alignItems="center"
> >
{t('modelmanager:modelExists')} {t('modelmanager:modelExists')}
</Box> </Box>
@ -87,7 +96,7 @@ function SearchModelEntry({
value={model.name} value={model.name}
label={ label={
<> <>
<VStack alignItems={'start'}> <VStack alignItems="start">
<p style={{ fontWeight: 'bold' }}>{model.name}</p> <p style={{ fontWeight: 'bold' }}>{model.name}</p>
<p style={{ fontStyle: 'italic' }}>{model.location}</p> <p style={{ fontStyle: 'italic' }}>{model.location}</p>
</VStack> </VStack>
@ -96,9 +105,9 @@ function SearchModelEntry({
isChecked={modelsToAdd.includes(model.name)} isChecked={modelsToAdd.includes(model.name)}
isDisabled={existingModels.includes(model.location)} isDisabled={existingModels.includes(model.location)}
onChange={foundModelsChangeHandler} onChange={foundModelsChangeHandler}
padding={'1rem'} padding="1rem"
backgroundColor={'var(--background-color)'} backgroundColor="var(--background-color)"
borderRadius={'0.5rem'} borderRadius="0.5rem"
_checked={{ _checked={{
backgroundColor: 'var(--accent-color)', backgroundColor: 'var(--accent-color)',
color: 'var(--text-color)', color: 'var(--text-color)',
@ -135,6 +144,8 @@ export default function SearchModels() {
); );
const [modelsToAdd, setModelsToAdd] = React.useState<string[]>([]); const [modelsToAdd, setModelsToAdd] = React.useState<string[]>([]);
const [modelType, setModelType] = React.useState<string>('v1');
const [pathToConfig, setPathToConfig] = React.useState<string>('');
const resetSearchModelHandler = () => { const resetSearchModelHandler = () => {
dispatch(setSearchFolder(null)); dispatch(setSearchFolder(null));
@ -167,11 +178,19 @@ export default function SearchModels() {
const modelsToBeAdded = foundModels?.filter((foundModel) => const modelsToBeAdded = foundModels?.filter((foundModel) =>
modelsToAdd.includes(foundModel.name) modelsToAdd.includes(foundModel.name)
); );
const configFiles = {
v1: 'configs/stable-diffusion/v1-inference.yaml',
v2: 'configs/stable-diffusion/v2-inference-v.yaml',
inpainting: 'configs/stable-diffusion/v1-inpainting-inference.yaml',
custom: pathToConfig,
};
modelsToBeAdded?.forEach((model) => { modelsToBeAdded?.forEach((model) => {
const modelFormat = { const modelFormat = {
name: model.name, name: model.name,
description: '', description: '',
config: 'configs/stable-diffusion/v1-inference.yaml', config: configFiles[modelType as keyof typeof configFiles],
weights: model.location, weights: model.location,
vae: '', vae: '',
width: 512, width: 512,
@ -224,12 +243,12 @@ export default function SearchModels() {
<> <>
{searchFolder ? ( {searchFolder ? (
<Flex <Flex
flexDirection={'column'} flexDirection="column"
padding={'1rem'} padding="1rem"
backgroundColor={'var(--background-color)'} backgroundColor="var(--background-color)"
borderRadius="0.5rem" borderRadius="0.5rem"
rowGap={'0.5rem'} rowGap="0.5rem"
position={'relative'} position="relative"
> >
<p <p
style={{ style={{
@ -252,7 +271,7 @@ export default function SearchModels() {
aria-label={t('modelmanager:scanAgain')} aria-label={t('modelmanager:scanAgain')}
tooltip={t('modelmanager:scanAgain')} tooltip={t('modelmanager:scanAgain')}
icon={<BiReset />} icon={<BiReset />}
position={'absolute'} position="absolute"
right={16} right={16}
fontSize={18} fontSize={18}
disabled={isProcessing} disabled={isProcessing}
@ -261,7 +280,7 @@ export default function SearchModels() {
<IAIIconButton <IAIIconButton
aria-label={t('modelmanager:clearCheckpointFolder')} aria-label={t('modelmanager:clearCheckpointFolder')}
icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />} icon={<FaPlus style={{ transform: 'rotate(45deg)' }} />}
position={'absolute'} position="absolute"
right={5} right={5}
onClick={resetSearchModelHandler} onClick={resetSearchModelHandler}
/> />
@ -300,8 +319,8 @@ export default function SearchModels() {
</Formik> </Formik>
)} )}
{foundModels && ( {foundModels && (
<Flex flexDirection={'column'} rowGap={'1rem'}> <Flex flexDirection="column" rowGap="1rem">
<Flex justifyContent={'space-between'} alignItems="center"> <Flex justifyContent="space-between" alignItems="center">
<p> <p>
{t('modelmanager:modelsFound')}: {foundModels.length} {t('modelmanager:modelsFound')}: {foundModels.length}
</p> </p>
@ -309,8 +328,8 @@ export default function SearchModels() {
{t('modelmanager:selected')}: {modelsToAdd.length} {t('modelmanager:selected')}: {modelsToAdd.length}
</p> </p>
</Flex> </Flex>
<Flex columnGap={'0.5rem'} justifyContent={'space-between'}> <Flex columnGap="0.5rem" justifyContent="space-between">
<Flex columnGap={'0.5rem'}> <Flex columnGap="0.5rem">
<IAIButton <IAIButton
isDisabled={modelsToAdd.length === foundModels.length} isDisabled={modelsToAdd.length === foundModels.length}
onClick={addAllToSelected} onClick={addAllToSelected}
@ -346,6 +365,55 @@ export default function SearchModels() {
{t('modelmanager:addSelected')} {t('modelmanager:addSelected')}
</IAIButton> </IAIButton>
</Flex> </Flex>
<Flex
gap={4}
backgroundColor="var(--background-color)"
padding="1rem 1rem"
borderRadius="0.2rem"
flexDirection="column"
>
<Flex gap={4}>
<Text fontWeight="bold" color="var(--text-color-secondary)">
Pick Model Type:
</Text>
<RadioGroup
value={modelType}
onChange={(v) => setModelType(v)}
defaultValue="v1"
name="model_type"
>
<Flex gap={4}>
<Radio value="v1">{t('modelmanager:v1')}</Radio>
<Radio value="v2">{t('modelmanager:v2')}</Radio>
<Radio value="inpainting">
{t('modelmanager:inpainting')}
</Radio>
<Radio value="custom">{t('modelmanager:customConfig')}</Radio>
</Flex>
</RadioGroup>
</Flex>
{modelType === 'custom' && (
<Flex flexDirection="column" rowGap={2}>
<Text
fontWeight="bold"
fontSize="sm"
color="var(--text-color-secondary)"
>
{t('modelmanager:pathToCustomConfig')}
</Text>
<IAIInput
value={pathToConfig}
onChange={(e) => {
if (e.target.value !== '') setPathToConfig(e.target.value);
}}
width="42.5rem"
/>
</Flex>
)}
</Flex>
<Flex <Flex
rowGap="1rem" rowGap="1rem"
flexDirection="column" flexDirection="column"

View File

@ -7,6 +7,8 @@
div { div {
background-color: var(--progress-bar-color); background-color: var(--progress-bar-color);
transition: width 0.2s ease-in-out;
&[data-indeterminate] { &[data-indeterminate] {
background-color: unset; background-color: unset;
background-image: linear-gradient( background-image: linear-gradient(

View File

@ -206,7 +206,7 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
</div> </div>
<div className="settings-modal-reset"> <div className="settings-modal-reset">
<Heading size={'md'}>{t('settings:resetWebUI')}</Heading> <Heading size="md">{t('settings:resetWebUI')}</Heading>
<Button colorScheme="red" onClick={handleClickResetWebUI}> <Button colorScheme="red" onClick={handleClickResetWebUI}>
{t('settings:resetWebUI')} {t('settings:resetWebUI')}
</Button> </Button>
@ -232,8 +232,8 @@ const SettingsModal = ({ children }: SettingsModalProps) => {
<ModalOverlay bg="blackAlpha.300" backdropFilter="blur(40px)" /> <ModalOverlay bg="blackAlpha.300" backdropFilter="blur(40px)" />
<ModalContent> <ModalContent>
<ModalBody pb={6} pt={6}> <ModalBody pb={6} pt={6}>
<Flex justifyContent={'center'}> <Flex justifyContent="center">
<Text fontSize={'lg'}> <Text fontSize="lg">
<Text>{t('settings:resetComplete')}</Text> <Text>{t('settings:resetComplete')}</Text>
</Text> </Text>
</Flex> </Flex>

View File

@ -56,7 +56,7 @@ const SiteHeader = () => {
<IAIIconButton <IAIIconButton
aria-label={t('modelmanager:modelManager')} aria-label={t('modelmanager:modelManager')}
tooltip={t('modelmanager:modelManager')} tooltip={t('modelmanager:modelManager')}
size={'sm'} size="sm"
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={20} fontSize={20}
@ -68,7 +68,7 @@ const SiteHeader = () => {
<IAIIconButton <IAIIconButton
aria-label={t('common:hotkeysLabel')} aria-label={t('common:hotkeysLabel')}
tooltip={t('common:hotkeysLabel')} tooltip={t('common:hotkeysLabel')}
size={'sm'} size="sm"
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={20} fontSize={20}
@ -86,7 +86,7 @@ const SiteHeader = () => {
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={20} fontSize={20}
size={'sm'} size="sm"
icon={ icon={
<Link isExternal href="http://github.com/invoke-ai/InvokeAI/issues"> <Link isExternal href="http://github.com/invoke-ai/InvokeAI/issues">
<FaBug /> <FaBug />
@ -100,7 +100,7 @@ const SiteHeader = () => {
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={20} fontSize={20}
size={'sm'} size="sm"
icon={ icon={
<Link isExternal href="http://github.com/invoke-ai/InvokeAI"> <Link isExternal href="http://github.com/invoke-ai/InvokeAI">
<FaGithub /> <FaGithub />
@ -114,7 +114,7 @@ const SiteHeader = () => {
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={20} fontSize={20}
size={'sm'} size="sm"
icon={ icon={
<Link isExternal href="https://discord.gg/ZmtBAhwWhy"> <Link isExternal href="https://discord.gg/ZmtBAhwWhy">
<FaDiscord /> <FaDiscord />
@ -129,7 +129,7 @@ const SiteHeader = () => {
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={22} fontSize={22}
size={'sm'} size="sm"
icon={<MdSettings />} icon={<MdSettings />}
/> />
</SettingsModal> </SettingsModal>

View File

@ -46,7 +46,7 @@ export default function ThemeChanger() {
width: '6rem', width: '6rem',
}} }}
leftIcon={currentTheme === theme ? <FaCheck /> : undefined} leftIcon={currentTheme === theme ? <FaCheck /> : undefined}
size={'sm'} size="sm"
onClick={() => handleChangeTheme(theme)} onClick={() => handleChangeTheme(theme)}
key={theme} key={theme}
> >
@ -64,7 +64,7 @@ export default function ThemeChanger() {
triggerComponent={ triggerComponent={
<IAIIconButton <IAIIconButton
aria-label={t('common:themeLabel')} aria-label={t('common:themeLabel')}
size={'sm'} size="sm"
variant="link" variant="link"
data-variant="link" data-variant="link"
fontSize={20} fontSize={20}
@ -72,7 +72,7 @@ export default function ThemeChanger() {
/> />
} }
> >
<VStack align={'stretch'}>{renderThemeOptions()}</VStack> <VStack align="stretch">{renderThemeOptions()}</VStack>
</IAIPopover> </IAIPopover>
); );
} }

View File

@ -214,6 +214,12 @@ export const systemSlice = createSlice({
state.isProcessing = true; state.isProcessing = true;
state.currentStatusHasSteps = false; state.currentStatusHasSteps = false;
}, },
modelConvertRequested: (state) => {
state.currentStatus = i18n.t('common:statusConvertingModel');
state.isCancelable = false;
state.isProcessing = true;
state.currentStatusHasSteps = false;
},
setSaveIntermediatesInterval: (state, action: PayloadAction<number>) => { setSaveIntermediatesInterval: (state, action: PayloadAction<number>) => {
state.saveIntermediatesInterval = action.payload; state.saveIntermediatesInterval = action.payload;
}, },
@ -265,6 +271,7 @@ export const {
setModelList, setModelList,
setIsCancelable, setIsCancelable,
modelChangeRequested, modelChangeRequested,
modelConvertRequested,
setSaveIntermediatesInterval, setSaveIntermediatesInterval,
setEnableImageDebugging, setEnableImageDebugging,
generationRequested, generationRequested,

View File

@ -1,6 +1,5 @@
import { Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features'; import { Feature } from 'app/features';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings'; import FaceRestoreSettings from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreSettings';
import FaceRestoreToggle from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreToggle'; import FaceRestoreToggle from 'features/parameters/components/AdvancedParameters/FaceRestore/FaceRestoreToggle';
import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit'; import ImageFit from 'features/parameters/components/AdvancedParameters/ImageToImage/ImageFit';
@ -16,10 +15,7 @@ import ParametersAccordion from 'features/parameters/components/ParametersAccord
import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons'; import ProcessButtons from 'features/parameters/components/ProcessButtons/ProcessButtons';
import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput'; import NegativePromptInput from 'features/parameters/components/PromptInput/NegativePromptInput';
import PromptInput from 'features/parameters/components/PromptInput/PromptInput'; import PromptInput from 'features/parameters/components/PromptInput/PromptInput';
import { setHiresFix } from 'features/parameters/store/postprocessingSlice';
import InvokeOptionsPanel from 'features/ui/components/InvokeParametersPanel'; import InvokeOptionsPanel from 'features/ui/components/InvokeParametersPanel';
import { activeTabNameSelector } from 'features/ui/store/uiSelectors';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
export default function ImageToImagePanel() { export default function ImageToImagePanel() {
@ -56,17 +52,6 @@ export default function ImageToImagePanel() {
}, },
}; };
const dispatch = useAppDispatch();
const activeTabName = useAppSelector(activeTabNameSelector);
useEffect(() => {
if (activeTabName === 'img2img') {
const handleChangeHiresFix = () => dispatch(setHiresFix(false));
handleChangeHiresFix();
}
}, [activeTabName, dispatch]);
return ( return (
<InvokeOptionsPanel> <InvokeOptionsPanel>
<Flex flexDir="column" rowGap="0.5rem"> <Flex flexDir="column" rowGap="0.5rem">

View File

@ -35,9 +35,9 @@ export default function InitImagePreview() {
{initialImage && ( {initialImage && (
<div className="init-image-preview"> <div className="init-image-preview">
<Image <Image
fit={'contain'} fit="contain"
maxWidth={'100%'} maxWidth="100%"
maxHeight={'100%'} maxHeight="100%"
src={ src={
typeof initialImage === 'string' ? initialImage : initialImage.url typeof initialImage === 'string' ? initialImage : initialImage.url
} }

View File

@ -9,10 +9,10 @@ export default function InitialImageOverlay() {
return initialImage ? ( return initialImage ? (
<Image <Image
fit={'contain'} fit="contain"
src={typeof initialImage === 'string' ? initialImage : initialImage.url} src={typeof initialImage === 'string' ? initialImage : initialImage.url}
rounded={'md'} rounded="md"
className={'checkerboard'} className="checkerboard"
/> />
) : null; ) : null;
} }

View File

@ -31,32 +31,32 @@ export interface InvokeTabInfo {
export const tabDict: Record<InvokeTabName, InvokeTabInfo> = { export const tabDict: Record<InvokeTabName, InvokeTabInfo> = {
txt2img: { txt2img: {
title: <TextToImageIcon fill={'black'} boxSize={'2.5rem'} />, title: <TextToImageIcon fill="black" boxSize="2.5rem" />,
workarea: <TextToImageWorkarea />, workarea: <TextToImageWorkarea />,
tooltip: 'Text To Image', tooltip: 'Text To Image',
}, },
img2img: { img2img: {
title: <ImageToImageIcon fill={'black'} boxSize={'2.5rem'} />, title: <ImageToImageIcon fill="black" boxSize="2.5rem" />,
workarea: <ImageToImageWorkarea />, workarea: <ImageToImageWorkarea />,
tooltip: 'Image To Image', tooltip: 'Image To Image',
}, },
unifiedCanvas: { unifiedCanvas: {
title: <UnifiedCanvasIcon fill={'black'} boxSize={'2.5rem'} />, title: <UnifiedCanvasIcon fill="black" boxSize="2.5rem" />,
workarea: <UnifiedCanvasWorkarea />, workarea: <UnifiedCanvasWorkarea />,
tooltip: 'Unified Canvas', tooltip: 'Unified Canvas',
}, },
nodes: { nodes: {
title: <NodesIcon fill={'black'} boxSize={'2.5rem'} />, title: <NodesIcon fill="black" boxSize="2.5rem" />,
workarea: <NodesWIP />, workarea: <NodesWIP />,
tooltip: 'Nodes', tooltip: 'Nodes',
}, },
postprocess: { postprocess: {
title: <PostprocessingIcon fill={'black'} boxSize={'2.5rem'} />, title: <PostprocessingIcon fill="black" boxSize="2.5rem" />,
workarea: <PostProcessingWIP />, workarea: <PostProcessingWIP />,
tooltip: 'Post Processing', tooltip: 'Post Processing',
}, },
training: { training: {
title: <TrainingIcon fill={'black'} boxSize={'2.5rem'} />, title: <TrainingIcon fill="black" boxSize="2.5rem" />,
workarea: <TrainingWIP />, workarea: <TrainingWIP />,
tooltip: 'Training', tooltip: 'Training',
}, },
@ -122,7 +122,7 @@ export default function InvokeTabs() {
key={key} key={key}
hasArrow hasArrow
label={tabDict[key as keyof typeof tabDict].tooltip} label={tabDict[key as keyof typeof tabDict].tooltip}
placement={'right'} placement="right"
> >
<Tab>{tabDict[key as keyof typeof tabDict].title}</Tab> <Tab>{tabDict[key as keyof typeof tabDict].title}</Tab>
</Tooltip> </Tooltip>
@ -147,7 +147,7 @@ export default function InvokeTabs() {
<Tabs <Tabs
isLazy isLazy
className="app-tabs" className="app-tabs"
variant={'unstyled'} variant="unstyled"
defaultIndex={activeTab} defaultIndex={activeTab}
index={activeTab} index={activeTab}
onChange={(index: number) => { onChange={(index: number) => {

View File

@ -45,21 +45,16 @@ const UnifiedCanvasDisplayBeta = () => {
}, [dispatch]); }, [dispatch]);
return ( return (
<div className={'workarea-single-view'}> <div className="workarea-single-view">
<Flex <Flex
flexDirection={'row'} flexDirection="row"
width="100%" width="100%"
height="100%" height="100%"
columnGap={'1rem'} columnGap="1rem"
padding="1rem" padding="1rem"
> >
<UnifiedCanvasToolbarBeta /> <UnifiedCanvasToolbarBeta />
<Flex <Flex width="100%" height="100%" flexDirection="column" rowGap="1rem">
width="100%"
height="100%"
flexDirection={'column'}
rowGap={'1rem'}
>
<UnifiedCanvasToolSettingsBeta /> <UnifiedCanvasToolSettingsBeta />
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />} {doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
</Flex> </Flex>

View File

@ -4,7 +4,7 @@ import UnifiedCanvasLimitStrokesToBox from './UnifiedCanvasLimitStrokesToBox';
export default function UnifiedCanvasBaseBrushSettings() { export default function UnifiedCanvasBaseBrushSettings() {
return ( return (
<Flex gap={'1rem'} alignItems="center"> <Flex gap="1rem" alignItems="center">
<UnifiedCanvasBrushSettings /> <UnifiedCanvasBrushSettings />
<UnifiedCanvasLimitStrokesToBox /> <UnifiedCanvasLimitStrokesToBox />
</Flex> </Flex>

View File

@ -4,7 +4,7 @@ import UnifiedCanvasColorPicker from './UnifiedCanvasColorPicker';
export default function UnifiedCanvasBrushSettings() { export default function UnifiedCanvasBrushSettings() {
return ( return (
<Flex columnGap={'1rem'} alignItems="center"> <Flex columnGap="1rem" alignItems="center">
<UnifiedCanvasBrushSize /> <UnifiedCanvasBrushSize />
<UnifiedCanvasColorPicker /> <UnifiedCanvasColorPicker />
</Flex> </Flex>

View File

@ -49,7 +49,7 @@ export default function UnifiedCanvasBrushSize() {
onChange={(newSize) => dispatch(setBrushSize(newSize))} onChange={(newSize) => dispatch(setBrushSize(newSize))}
sliderNumberInputProps={{ max: 500 }} sliderNumberInputProps={{ max: 500 }}
inputReadOnly={false} inputReadOnly={false}
width={'100px'} width="100px"
isCompact isCompact
/> />
); );

View File

@ -14,7 +14,7 @@ export default function UnifiedCanvasClearMask() {
return ( return (
<IAIButton <IAIButton
size={'sm'} size="sm"
leftIcon={<FaTrash />} leftIcon={<FaTrash />}
onClick={handleClearMask} onClick={handleClearMask}
tooltip={`${t('unifiedcanvas:clearMask')} (Shift+C)`} tooltip={`${t('unifiedcanvas:clearMask')} (Shift+C)`}

View File

@ -92,7 +92,7 @@ export default function UnifiedCanvasColorPicker() {
/> />
} }
> >
<Flex minWidth={'15rem'} direction={'column'} gap={'1rem'} width={'100%'}> <Flex minWidth="15rem" direction="column" gap="1rem" width="100%">
{layer === 'base' && ( {layer === 'base' && (
<IAIColorPicker <IAIColorPicker
style={{ style={{

View File

@ -6,7 +6,7 @@ import UnifiedCanvasPreserveMask from './UnifiedCanvasPreserveMask';
export default function UnifiedCanvasMaskBrushSettings() { export default function UnifiedCanvasMaskBrushSettings() {
return ( return (
<Flex gap={'1rem'} alignItems="center"> <Flex gap="1rem" alignItems="center">
<UnifiedCanvasBrushSettings /> <UnifiedCanvasBrushSettings />
<UnifiedCanvasEnableMask /> <UnifiedCanvasEnableMask />
<UnifiedCanvasPreserveMask /> <UnifiedCanvasPreserveMask />

View File

@ -5,7 +5,7 @@ import UnifiedCanvasSnapToGrid from './UnifiedCanvasSnapToGrid';
export default function UnifiedCanvasMoveSettings() { export default function UnifiedCanvasMoveSettings() {
return ( return (
<Flex alignItems={'center'} gap="1rem"> <Flex alignItems="center" gap="1rem">
<UnifiedCanvasShowGrid /> <UnifiedCanvasShowGrid />
<UnifiedCanvasSnapToGrid /> <UnifiedCanvasSnapToGrid />
<UnifiedCanvasDarkenOutsideSelection /> <UnifiedCanvasDarkenOutsideSelection />

Some files were not shown because too many files have changed in this diff Show More