diff --git a/.dockerignore b/.dockerignore index 3874431b2..993ab8a57 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,6 +10,10 @@ pyproject.toml # User generated files /converted /data +!/data/themes/dark.json +!/data/themes/dark_flat.json +!/data/themes/light.json +!/data/themes/light_flat.json /engine /onnx /traced_unet @@ -22,9 +26,6 @@ yarn.lock # Frontend frontend/dist/ -# Static files -/static - # Python /venv @@ -42,6 +43,7 @@ test.docker-compose.yml # Other **/**.pyc +poetry.lock .pytest_cache .coverage /.ruff_cache diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 000000000..563b87d12 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,8 @@ +name: Ruff +on: [push, pull_request] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: chartboost/ruff-action@v1 diff --git a/.gitignore b/.gitignore index d87ca807c..6bc19436a 100644 --- a/.gitignore +++ b/.gitignore @@ -80,7 +80,6 @@ docs/.vitepress/dist external /tmp /data -/data/settings.json /AITemplate # Ignore for black diff --git a/.vscode/settings.json b/.vscode/settings.json index 97fafffbe..c671ed81f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,8 @@ { - "python.linting.pylintEnabled": true, - "python.linting.enabled": true, "python.testing.pytestArgs": ["."], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.analysis.typeCheckingMode": "basic", - "python.formatting.provider": "black", "python.languageServer": "Pylance", "rust-analyzer.linkedProjects": ["./manager/Cargo.toml"] } diff --git a/api/app.py b/api/app.py index bb80189f9..ddb42b43a 100644 --- a/api/app.py +++ b/api/app.py @@ -5,32 +5,21 @@ from pathlib import Path from api_analytics.fastapi import Analytics -from fastapi import Depends, FastAPI, Request +from fastapi import Depends, FastAPI, Request, status from fastapi.exceptions import RequestValidationError from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import FileResponse +from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from fastapi_simple_cachecontrol.middleware import CacheControlMiddleware from fastapi_simple_cachecontrol.types import CacheControl from huggingface_hub.hf_api import LocalTokenNotFoundError -from starlette import status -from starlette.responses import JSONResponse from api import websocket_manager -from api.routes import ( - general, - generate, - hardware, - models, - outputs, - settings, - static, - test, - ws, -) +from api.routes import static, ws from api.websockets.data import Data from api.websockets.notification import Notification from core import shared +from core.types import InferenceBackend logger = logging.getLogger(__name__) @@ -100,9 +89,19 @@ async def hf_token_error(_request, _exc): @app.exception_handler(404) -async def custom_http_exception_handler(_request, _exc): +async def custom_http_exception_handler(request: Request, _exc): "Redirect back to the main page (frontend will handle it)" + if request.url.path.startswith("/api"): + return JSONResponse( + content={ + "status_code": 10404, + "message": "Not Found", + "data": None, + }, + status_code=status.HTTP_404_NOT_FOUND, + ) + return FileResponse("frontend/dist/index.html") @@ -110,40 +109,33 @@ async def custom_http_exception_handler(_request, _exc): async def startup_event(): "Prepare the event loop for other asynchronous tasks" - # Inject the logger - from rich.logging import RichHandler - - # Disable duplicate logger - logging.getLogger("uvicorn").handlers = [] - - for logger_ in ("uvicorn.access", "uvicorn.error", "fastapi"): - l = logging.getLogger(logger_) - handler = RichHandler( - rich_tracebacks=True, show_time=False, omit_repeated_times=False - ) - handler.setFormatter( - logging.Formatter( - fmt="%(asctime)s | %(name)s » %(message)s", datefmt="%H:%M:%S" - ) - ) - l.handlers = [handler] - if logger.level > logging.DEBUG: from transformers import logging as transformers_logging transformers_logging.set_verbosity_error() shared.asyncio_loop = asyncio.get_event_loop() + websocket_manager.loop = shared.asyncio_loop - sync_task = asyncio.create_task(websocket_manager.sync_loop()) - logger.info("Started WebSocketManager sync loop") perf_task = asyncio.create_task(websocket_manager.perf_loop()) - - shared.asyncio_tasks.append(sync_task) shared.asyncio_tasks.append(perf_task) + from core.config import config + + if config.api.autoloaded_models: + from core.shared_dependent import cached_model_list, gpu + + all_models = cached_model_list.all() + + for model in config.api.autoloaded_models: + if model in [i.path for i in all_models]: + backend: InferenceBackend = [i.backend for i in all_models if i.path == model][0] # type: ignore + await gpu.load_model(model, backend) + else: + logger.warning(f"Autoloaded model {model} not found, skipping") + logger.info("Started WebSocketManager performance monitoring loop") - logger.info("UI Available at: http://localhost:5003/") + logger.info(f"UI Available at: http://localhost:{shared.api_port}/") @app.on_event("shutdown") @@ -165,42 +157,49 @@ async def shutdown_event(): # Mount routers ## HTTP app.include_router(static.router) -app.include_router(test.router, prefix="/api/test") -app.include_router(generate.router, prefix="/api/generate") -app.include_router(hardware.router, prefix="/api/hardware") -app.include_router(models.router, prefix="/api/models") -app.include_router(outputs.router, prefix="/api/output") -app.include_router(general.router, prefix="/api/general") -app.include_router(settings.router, prefix="/api/settings") + +# Walk the routes folder and mount all routers +for file in Path("api/routes").iterdir(): + if file.is_file(): + if ( + file.name != "__init__.py" + and file.suffix == ".py" + and file.stem not in ["static", "ws"] + ): + logger.debug(f"Mounting: {file} as /api/{file.stem}") + module = __import__(f"api.routes.{file.stem}", fromlist=["router"]) + app.include_router(module.router, prefix=f"/api/{file.stem}") ## WebSockets app.include_router(ws.router, prefix="/api/websockets") # Mount outputs folder -output_folder = Path("data/outputs") -output_folder.mkdir(exist_ok=True) app.mount("/data/outputs", StaticFiles(directory="data/outputs"), name="outputs") # Mount static files (css, js, images, etc.) static_app = FastAPI() -static_app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) static_app.add_middleware( CacheControlMiddleware, cache_control=CacheControl("no-cache") ) static_app.mount("/", StaticFiles(directory="frontend/dist/assets"), name="assets") app.mount("/assets", static_app) +app.mount("/static", StaticFiles(directory="static"), name="extra_static_files") +app.mount("/themes", StaticFiles(directory="data/themes"), name="themes") + +origins = ["*"] # Allow CORS for specified origins app.add_middleware( CORSMiddleware, - allow_origins=["*"], + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) +static_app.add_middleware( + CORSMiddleware, + allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/api/routes/autofill.py b/api/routes/autofill.py new file mode 100644 index 000000000..a269f0c77 --- /dev/null +++ b/api/routes/autofill.py @@ -0,0 +1,29 @@ +import logging +from pathlib import Path +from typing import List + +from fastapi import APIRouter + +router = APIRouter(tags=["autofill"]) +logger = logging.getLogger(__name__) + + +@router.get("/") +def get_autofill_list() -> List[str]: + "Gathers and returns all words from the prompt autofill files" + + autofill_folder = Path("data/autofill") + + words = [] + + logger.debug(f"Looking for autofill files in {autofill_folder}") + logger.debug(f"Found {list(autofill_folder.iterdir())} files") + + for file in autofill_folder.iterdir(): + if file.is_file(): + if file.suffix == ".txt": + logger.debug(f"Found autofill file: {file}") + with open(file, "r", encoding="utf-8") as f: + words.extend(f.read().splitlines()) + + return list(set(words)) diff --git a/api/routes/general.py b/api/routes/general.py index 7869492f1..c6822a50e 100644 --- a/api/routes/general.py +++ b/api/routes/general.py @@ -1,5 +1,6 @@ import logging import sys +from pathlib import Path from fastapi import APIRouter @@ -83,3 +84,16 @@ async def queue_clear(): queue.clear() return {"message": "Queue cleared"} + + +@router.get("/themes") +async def themes(): + "Get all available themes" + + path = Path("data/themes") + files = [] + for file in path.glob("*.json"): + files.append(file.stem) + + files.sort() + return files diff --git a/api/routes/generate.py b/api/routes/generate.py index d1842b1d1..0bff087e0 100644 --- a/api/routes/generate.py +++ b/api/routes/generate.py @@ -35,9 +35,7 @@ async def txt2img_job(job: Txt2ImgQueueEntry): time: float images, time = await gpu.generate(job) except ModelNotLoadedError: - raise HTTPException( # pylint: disable=raise-missing-from - status_code=400, detail="Model is not loaded" - ) + raise HTTPException(status_code=400, detail="Model is not loaded") return images_to_response(images, time) @@ -55,9 +53,7 @@ async def img2img_job(job: Img2ImgQueueEntry): time: float images, time = await gpu.generate(job) except ModelNotLoadedError: - raise HTTPException( # pylint: disable=raise-missing-from - status_code=400, detail="Model is not loaded" - ) + raise HTTPException(status_code=400, detail="Model is not loaded") return images_to_response(images, time) @@ -79,16 +75,14 @@ async def inpaint_job(job: InpaintQueueEntry): time: float images, time = await gpu.generate(job) except ModelNotLoadedError: - raise HTTPException( # pylint: disable=raise-missing-from - status_code=400, detail="Model is not loaded" - ) + raise HTTPException(status_code=400, detail="Model is not loaded") return images_to_response(images, time) @router.post("/controlnet") async def controlnet_job(job: ControlNetQueueEntry): - "Generate variations of the image" + "Generate images based on a reference image" image_bytes = job.data.image assert isinstance(image_bytes, bytes) @@ -99,9 +93,7 @@ async def controlnet_job(job: ControlNetQueueEntry): time: float images, time = await gpu.generate(job) except ModelNotLoadedError: - raise HTTPException( # pylint: disable=raise-missing-from - status_code=400, detail="Model is not loaded" - ) + raise HTTPException(status_code=400, detail="Model is not loaded") return images_to_response(images, time) @@ -119,9 +111,7 @@ async def realesrgan_upscale_job(job: UpscaleQueueEntry): time: float image, time = await gpu.upscale(job) except ModelNotLoadedError: - raise HTTPException( # pylint: disable=raise-missing-from - status_code=400, detail="Model is not loaded" - ) + raise HTTPException(status_code=400, detail="Model is not loaded") return { "time": time, @@ -133,7 +123,7 @@ async def realesrgan_upscale_job(job: UpscaleQueueEntry): @router.post("/generate-aitemplate") async def generate_aitemplate(request: AITemplateBuildRequest): - "Generate a AITemplate model from a local model" + "Generate an AITemplate model from a local model" await gpu.build_aitemplate_engine(request) @@ -142,7 +132,7 @@ async def generate_aitemplate(request: AITemplateBuildRequest): @router.post("/generate-dynamic-aitemplate") async def generate_dynamic_aitemplate(request: AITemplateDynamicBuildRequest): - "Generate a AITemplate engine from a local model" + "Generate an AITemplate engine from a local model" await gpu.build_dynamic_aitemplate_engine(request) diff --git a/api/routes/hardware.py b/api/routes/hardware.py index e435f9b3d..618b8f57d 100644 --- a/api/routes/hardware.py +++ b/api/routes/hardware.py @@ -45,9 +45,7 @@ async def gpu_memory(gpu_id: int): gpu_data = GPUStatCollection.new_query().gpus[gpu_id] return (gpu_data.memory_total, gpu_data.memory_free, "MB") except IndexError: - raise HTTPException( # pylint: disable=raise-missing-from - status_code=400, detail="GPU not found" - ) + raise HTTPException(status_code=400, detail="GPU not found") @router.get("/capabilities") diff --git a/api/routes/models.py b/api/routes/models.py index 022996921..8149ba644 100644 --- a/api/routes/models.py +++ b/api/routes/models.py @@ -13,6 +13,7 @@ from api import websocket_manager from api.websockets.data import Data +from api.websockets.notification import Notification from core.files import get_full_model_path from core.shared_dependent import cached_model_list, gpu from core.types import ( @@ -27,9 +28,15 @@ router = APIRouter(tags=["models"]) logger = logging.getLogger(__name__) -model_upload_dir = Path("data/models") -lora_upload_dir = Path("data/lora") -textual_inversions_UploadDir = Path("data/textual-inversion") +possible_dirs = [ + "models", + "lora", + "textual-inversion", + "lycoris", + "vae", + "aitemplate", + "onnx", +] class UploadFileTarget(FileTarget): @@ -41,10 +48,11 @@ def __init__(self, dir_: Path, *args, **kwargs): filename=None, file=NamedTemporaryFile(delete=False, dir=dir_) # type: ignore ) self._fd = self.file.file + self.dir = dir_ def on_start(self): self.file.filename = self.filename = self.multipart_filename # type: ignore - if model_upload_dir.joinpath(self.filename).exists(): # type: ignore + if self.dir.joinpath(self.filename).exists(): # type: ignore raise HTTPException(409, "File already exists") @@ -93,9 +101,7 @@ async def load_model( ) except torch.cuda.OutOfMemoryError: # type: ignore logger.warning(traceback.format_exc()) - raise HTTPException( # pylint: disable=raise-missing-from - status_code=500, detail="Out of memory" - ) + raise HTTPException(status_code=500, detail="Out of memory") return {"message": "Model loaded"} @@ -173,11 +179,15 @@ async def get_current_cached_preprocessor(): async def upload_model(request: Request): "Upload a model file to the server" - upload_type = request.query_params.get("type", "model") - logger.info(f"Recieving model of type {upload_type}") + upload_type = request.query_params.get("type") + assert upload_type in possible_dirs, f"Invalid upload type '{upload_type}'" + + logger.info(f"Recieving model of type '{upload_type}'") + + upload_dir = Path("data") / upload_type parser = StreamingFormDataParser(request.headers) - target = UploadFileTarget(model_upload_dir) + target = UploadFileTarget(upload_dir) try: parser.register("file", target) @@ -185,19 +195,7 @@ async def upload_model(request: Request): parser.data_received(chunk) if target.filename: - if upload_type == "lora": - logger.info("Moving file to lora upload dir") - folder = lora_upload_dir - elif upload_type == "textual-inversion": - logger.info("Moving file to textual inversion upload dir") - folder = textual_inversions_UploadDir - elif upload_type == "model": - logger.info("Moving file to model upload dir") - folder = model_upload_dir - else: - raise HTTPException(422, "Invalid upload type") - - shutil.move(target.file.file.name, folder.joinpath(target.filename)) + shutil.move(target.file.file.name, upload_dir.joinpath(target.filename)) else: raise HTTPException(422, "Could not find file in body") finally: @@ -215,19 +213,24 @@ async def upload_model(request: Request): async def delete_model(req: DeleteModelRequest): "Delete a model from the server" - if req.model_type == "pytorch": + assert req.model_type in possible_dirs, f"Invalid upload type {req.model_type}" + + if req.model_type == "models": path = get_full_model_path(req.model_path, diffusers_skip_ref_follow=True) - elif req.model_type == "lora": - path = lora_upload_dir.joinpath(req.model_path) - elif req.model_type == "textual-inversion": - path = textual_inversions_UploadDir.joinpath(req.model_path) else: - raise HTTPException(422, "Invalid model type") + path = Path(req.model_path) - logger.warning(f"Deleting model {path} of type {req.model_type}") + logger.warning(f"Deleting model '{path}' of type '{req.model_type}'") - if not path.exists(): - raise HTTPException(404, "Model not found") + if not path.is_symlink(): + if not path.exists(): + await websocket_manager.broadcast( + data=Notification( + severity="error", + message="Model not found", + ) + ) + raise HTTPException(404, "Model not found") if path.is_dir(): shutil.rmtree(path) diff --git a/api/routes/outputs.py b/api/routes/outputs.py index df9ce80bb..b9092c162 100644 --- a/api/routes/outputs.py +++ b/api/routes/outputs.py @@ -15,6 +15,12 @@ valid_extensions = ["png", "jpeg", "webp"] +def sort_images(images: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + "Sort images by time" + + return sorted(images, key=lambda x: x["time"], reverse=True) + + @router.get("/txt2img") def txt2img() -> List[Dict[str, Any]]: "List all generated images" @@ -31,7 +37,7 @@ def txt2img() -> List[Dict[str, Any]]: {"path": i.as_posix(), "time": os.path.getmtime(i), "id": Path(i).stem} ) - return data + return sort_images(data) @router.get("/img2img") @@ -50,7 +56,7 @@ def img2img() -> List[Dict[str, Any]]: {"path": i.as_posix(), "time": os.path.getmtime(i), "id": Path(i).stem} ) - return data + return sort_images(data) @router.get("/extra") @@ -69,7 +75,7 @@ def extra() -> List[Dict[str, Any]]: {"path": i.as_posix(), "time": os.path.getmtime(i), "id": Path(i).stem} ) - return data + return sort_images(data) @router.get("/data") diff --git a/api/routes/settings.py b/api/routes/settings.py index 76805a060..390e27753 100644 --- a/api/routes/settings.py +++ b/api/routes/settings.py @@ -13,14 +13,11 @@ @router.post("/save") async def save_configuration(settings: config.Configuration): - "Receive settings from the frontend and save them to the config file" + "Update settings and save them to the config file" reload_required = False - if config.config.api.device_id != settings.api.device_id: - logger.info(f"Device ID was changed to {settings.api.device_id}") - reload_required = True - if config.config.api.device_type != settings.api.device_type: - logger.info(f"Device type was changed to {settings.api.device_type}") + if config.config.api.device != settings.api.device: + logger.info(f"Device was changed to {settings.api.device}") reload_required = True if config.config.api.data_type != settings.api.data_type: logger.info(f"Precision changed to {settings.api.data_type}") @@ -43,7 +40,7 @@ async def save_configuration(settings: config.Configuration): @router.get("/") async def get_configuration(): - "Return the current configuration to the frontend" + "Get current settings" logger.debug(f"Sending configuration to frontend: {config.config}") return config.config diff --git a/api/routes/ws.py b/api/routes/ws.py index 91a784da7..a9924e59e 100644 --- a/api/routes/ws.py +++ b/api/routes/ws.py @@ -1,6 +1,5 @@ from fastapi import APIRouter -from fastapi.websockets import WebSocket -from starlette.websockets import WebSocketDisconnect +from fastapi.websockets import WebSocket, WebSocketDisconnect from api import websocket_manager from api.websockets.data import Data diff --git a/api/websockets/manager.py b/api/websockets/manager.py index e3bce6b8d..d4826708c 100644 --- a/api/websockets/manager.py +++ b/api/websockets/manager.py @@ -1,15 +1,15 @@ import asyncio import logging from asyncio import AbstractEventLoop -from typing import Coroutine, List, Optional +from typing import List, Optional import torch from fastapi import WebSocket from psutil import NoSuchProcess from api.websockets.data import Data +from core import shared from core.config import config -from core.shared import all_gpus, amd logger = logging.getLogger(__name__) @@ -20,42 +20,28 @@ class WebSocketManager: def __init__(self): self.active_connections: List[WebSocket] = [] self.loop: Optional[AbstractEventLoop] = None - self.to_run: List[Coroutine] = [] - - async def sync_loop(self): - "Infinite loop that runs all coroutines in the to_run list" - - while True: - for task in self.to_run: - await task - self.to_run.remove(task) - - await asyncio.sleep(config.api.websocket_sync_interval) async def perf_loop(self): "Infinite loop that sends performance data to all active websocket connections" - global amd, all_gpus # pylint: disable=global-statement try: from gpustat.core import GPUStatCollection - all_gpus = [i.entry for i in GPUStatCollection.new_query().gpus] - except Exception as e: # pylint: disable=broad-except + shared.all_gpus = [i.entry for i in GPUStatCollection.new_query().gpus] + except Exception as e: logger.info( f"GPUStat failed to initialize - probably not an NVIDIA GPU: {e}" ) logger.debug("Trying pyamdgpuinfo...") try: - import pyamdgpuinfo # pylint: disable=import-error + import pyamdgpuinfo if pyamdgpuinfo.detect_gpus() == 0: - raise ImportError( # pylint: disable=raise-missing-from - "User doesn't have an AMD gpu" - ) - all_gpus = [ + raise ImportError("User doesn't have an AMD gpu") + shared.all_gpus = [ pyamdgpuinfo.get_gpu(x) for x in range(pyamdgpuinfo.detect_gpus()) ] - amd = True - except Exception: # pylint: disable=broad-except + shared.amd = True + except Exception: logger.warning( "User doesn't have an AMD nor an NVIDIA card. GPU info will be unavailable." ) @@ -63,8 +49,8 @@ async def perf_loop(self): while True: data = [] - if amd: - for stat in all_gpus: + if shared.amd: + for stat in shared.all_gpus: data.append( { "index": stat.gpu_id, @@ -90,8 +76,10 @@ async def perf_loop(self): try: from gpustat.core import GPUStatCollection - all_gpus = [i.entry for i in GPUStatCollection.new_query().gpus] - for stat in all_gpus: + shared.all_gpus = [ + i.entry for i in GPUStatCollection.new_query().gpus + ] + for stat in shared.all_gpus: data.append( { "index": stat["index"], @@ -130,9 +118,9 @@ def disconnect(self, websocket: WebSocket): and config.api.clear_memory_policy == "after_disconnect" ): if torch.cuda.is_available(): - logger.debug(f"Cleaning up GPU memory: {config.api.device_id}") + logger.debug(f"Cleaning up GPU memory: {config.api.device}") - with torch.cuda.device(config.api.device_id): + with torch.device(config.api.device): torch.cuda.empty_cache() torch.cuda.ipc_collect() @@ -160,9 +148,22 @@ async def broadcast(self, data: Data): def broadcast_sync(self, data: Data): "Broadcasts data message to all active websocket connections synchronously" + loop_error_message = "No event loop found, please inject it in the code" + + try: + assert self.loop is not None, loop_error_message + asyncio.get_event_loop() + except RuntimeError: + assert self.loop is not None # For type safety + asyncio.set_event_loop(self.loop) + except AssertionError: + return + for connection in self.active_connections: if connection.application_state.CONNECTED: - self.to_run.append(connection.send_json(data.to_json())) + asyncio.run_coroutine_threadsafe( + connection.send_json(data.to_json()), self.loop + ) else: self.active_connections.remove(connection) @@ -174,9 +175,9 @@ async def close_all(self): if config.api.clear_memory_policy == "after_disconnect": if torch.cuda.is_available(): - logger.debug(f"Cleaning up GPU memory: {config.api.device_id}") + logger.debug(f"Cleaning up GPU memory: {config.api.device}") - with torch.cuda.device(config.api.device_id): + with torch.cuda.device(config.api.device): torch.cuda.empty_cache() torch.cuda.ipc_collect() diff --git a/bot/bot.py b/bot/bot.py index c71b87f63..e133599a9 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -6,6 +6,8 @@ from discord import utils from discord.ext.commands import AutoShardedBot +from bot.shared import config + logger = logging.getLogger(__name__) @@ -14,7 +16,7 @@ class ModularBot(AutoShardedBot): def __init__(self) -> None: super().__init__( - command_prefix="!", + command_prefix=config.prefix, intents=discord.Intents.all(), ) @@ -115,11 +117,15 @@ async def async_init(self): "Initialise that runs before the bot starts" # Load core extension - await self.load_extension("bot.core") - await self.load_extension("bot.listeners") - await self.load_extension("bot.txt2img") - await self.load_extension("bot.models") - await self.load_extension("bot.hardware") + core_extensions = [ + "bot.core", + "bot.listeners", + "bot.txt2img", + "bot.models", + "bot.hardware", + ] + for ext in core_extensions: + await self.load_extension(ext) @property def avatar_url(self) -> str: diff --git a/bot/bot_model_manager.py b/bot/bot_model_manager.py new file mode 100644 index 000000000..e858343fe --- /dev/null +++ b/bot/bot_model_manager.py @@ -0,0 +1,39 @@ +from bot.helper import get_available_models, get_loaded_models + + +class BotModelManager: + "Class for internally managing models" + + def __init__(self): + self._cached_models = [] + self._loaded_models = [] + + async def cached_available_models(self): + "List all available models" + + if not self._cached_models: + self._cached_models, code = await get_available_models() + else: + code = 200 + + return self._cached_models, code + + def set_cached_available_models(self, value): + "Set the internal cached models" + + self._cached_models = value + + async def cached_loaded_models(self): + "List all loaded models" + + if not self._loaded_models: + self._loaded_models, code = await get_loaded_models() + else: + code = 200 + + return self._loaded_models, code + + def set_cached_loaded_models(self, value): + "Set the internal loaded models" + + self._loaded_models = value diff --git a/bot/config.py b/bot/config.py new file mode 100644 index 000000000..d50e1a3f9 --- /dev/null +++ b/bot/config.py @@ -0,0 +1,61 @@ +import logging +from dataclasses import dataclass, field + +from dataclasses_json.api import DataClassJsonMixin +from diffusers.schedulers import KarrasDiffusionSchedulers + +logger = logging.getLogger(__name__) + + +@dataclass +class Config(DataClassJsonMixin): + "Dataclass that will store the configuration for the bot" + + supported_models: dict[str, str] = field(default_factory=dict) + prefix: str = "!" + + extra_prompt: str = "" + extra_negative_prompt: str = "" + + max_width: int = 1920 + max_height: int = 1920 + max_count: int = 4 + max_steps: int = 50 + + default_width: int = 512 + default_height: int = 512 + default_count: int = 1 + default_steps: int = 30 + default_scheduler: KarrasDiffusionSchedulers = ( + KarrasDiffusionSchedulers.DPMSolverMultistepScheduler + ) + default_cfg: float = 7.0 + default_verbose: bool = False + + +def save_config(config: Config): + "Save the configuration to a file" + + logger.info("Saving configuration to data/bot.json") + + with open("data/bot.json", "w", encoding="utf-8") as f: + f.write(config.to_json(ensure_ascii=False, indent=4)) + + +def load_config(): + "Load the configuration from a file" + + logger.info("Loading configuration from data/bot.json") + + try: + with open("data/bot.json", "r", encoding="utf-8") as f: + config = Config.from_json(f.read()) + logger.info("Configuration loaded from data/bot.json") + return config + + except FileNotFoundError: + logger.info("data/bot.json not found, creating a new one") + config = Config() + save_config(config) + logger.info("Configuration saved to data/bot.json") + return config diff --git a/bot/hardware.py b/bot/hardware.py index 29a64d66d..648b346fa 100644 --- a/bot/hardware.py +++ b/bot/hardware.py @@ -5,6 +5,8 @@ from discord.ext import commands from discord.ext.commands import Cog, Context +from core import shared + if TYPE_CHECKING: from bot.bot import ModularBot @@ -21,7 +23,9 @@ async def gpus(self, ctx: Context): "List all available GPUs" async with ClientSession() as session: - async with session.get("http://localhost:5003/api/hardware/gpus") as resp: + async with session.get( + f"http://localhost:{shared.api_port}/api/hardware/gpus" + ) as resp: status = resp.status data: Dict[str, Dict] = await resp.json() @@ -49,7 +53,7 @@ async def clean(self, ctx: Context): async with ClientSession() as session: async with session.post( - "http://localhost:5003/api/models/memory-cleanup" + f"http://localhost:{shared.api_port}/api/models/memory-cleanup" ) as resp: status = resp.status diff --git a/bot/helper.py b/bot/helper.py new file mode 100644 index 000000000..d3eccd533 --- /dev/null +++ b/bot/helper.py @@ -0,0 +1,97 @@ +import asyncio +import difflib +from typing import Any, Dict, List, Literal + +import aiohttp +from aiohttp import ClientSession + +from bot import shared as shared_bot + + +async def find_closest_model(model: str): + """Find the closest model to the one provided""" + models, _ = await shared_bot.models.cached_loaded_models() + return difflib.get_close_matches(model, models, n=1, cutoff=0.1)[0] + + +async def inference_call( + payload: Dict, target: Literal["txt2img", "img2img"] = "txt2img" +): + "Call to the backend to generate an image" + + from core import shared + + async def call(): + async with aiohttp.ClientSession() as session: + async with session.post( + f"http://localhost:{shared.api_port}/api/generate/{target}", + json=payload, + ) as response: + status = response.status + response = await response.json() + + return status, response + + try: + status, response = await call() + except aiohttp.ClientOSError: + await asyncio.sleep(0.5) + status, response = await call() + + return status, response + + +async def get_available_models(): + "List all available models" + + from core import shared + + async with ClientSession() as session: + async with session.get( + f"http://localhost:{shared.api_port}/api/models/available" + ) as response: + status = response.status + data: List[Dict[str, Any]] = await response.json() + models = [ + i["name"] + for i in filter( + lambda model: ( + model["valid"] is True + and ( + model["backend"] == "PyTorch" + or model["backend"] == "AITemplate" + ) + ), + data, + ) + ] + + return models, status + + +async def get_loaded_models(): + "List all available models" + + from core import shared + + async with ClientSession() as session: + async with session.get( + f"http://localhost:{shared.api_port}/api/models/loaded" + ) as response: + status = response.status + data: List[Dict[str, Any]] = await response.json() + models = [ + i["name"] + for i in filter( + lambda model: ( + model["valid"] is True + and ( + model["backend"] == "PyTorch" + or model["backend"] == "AITemplate" + ) + ), + data, + ) + ] + + return models, status diff --git a/bot/image2image.py b/bot/image2image.py new file mode 100644 index 000000000..2bfd45f20 --- /dev/null +++ b/bot/image2image.py @@ -0,0 +1,152 @@ +import logging +import random +import re +from io import BytesIO +from typing import TYPE_CHECKING, Optional +from uuid import uuid4 + +import discord +import requests +from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers +from discord import File +from discord.ext import commands +from discord.ext.commands import Cog, Context +from PIL import Image + +from bot.helper import find_closest_model, inference_call +from bot.shared import config +from core.utils import convert_base64_to_bytes, convert_image_to_base64 + +if TYPE_CHECKING: + from bot.bot import ModularBot + +logger = logging.getLogger(__name__) + +pattern = r"data:image/[\w]+;base64," + + +class Image2Image(Cog): + "Commands for generating images from text" + + def __init__(self, bot: "ModularBot") -> None: + self.bot = bot + + @commands.hybrid_command(name="text2image") + async def dream_unsupported( + self, + ctx: Context, + prompt: str, + model: str, + negative_prompt: str = "", + guidance_scale: float = config.default_cfg, + steps: int = config.default_steps, + width: int = config.default_width, + height: int = config.default_height, + count: int = config.default_count, + seed: Optional[int] = None, + scheduler: KarrasDiffusionSchedulers = config.default_scheduler, + verbose: bool = config.default_verbose, + ): + "Generate an image from prompt" + + print(ctx.message.attachments) + + input_image = ctx.message.attachments[0].url + init_image = Image.open( + BytesIO(requests.get(input_image, timeout=10).content) + ).convert("RGB") + init_image_byte64 = convert_image_to_base64(init_image) + + if seed is None: + seed = random.randint(0, 1000000) + + if config.max_width < width or config.max_height < height: + return await ctx.send( + f"Image size is too big, maximum size is {config.max_width}x{config.max_height}" + ) + + if config.max_count < count: + return await ctx.send( + f"Image count is too big, maximum count is {config.max_count}" + ) + + prompt = prompt + config.extra_prompt + negative_prompt = negative_prompt + config.extra_negative_prompt + + try: + model = await find_closest_model(model) + except IndexError: + await ctx.send(f"No loaded model that is close to `{model}` found") + return + + payload = { + "data": { + "prompt": prompt, + "id": uuid4().hex, + "negative_prompt": negative_prompt, + "image": init_image_byte64, + "width": width, + "height": height, + "steps": steps, + "guidance_scale": guidance_scale, + "seed": seed, + "batch_size": 1, + "batch_count": count, + "scheduler": scheduler.value, + }, + "model": model, + "save_image": False, + } + + message = await ctx.send(f"Generating image with `{model}`...") + + try: + status, response = await inference_call(payload=payload, target="img2img") + except Exception as e: + raise e + + if status == 200: + if verbose: + embed = discord.Embed( + color=discord.Color.green(), + ) + embed.add_field(name="Seed", value=seed) + embed.add_field(name="Time", value=f"{response.get('time'):.2f}s") + embed.add_field(name="Model", value=model) + embed.add_field(name="Negative Prompt", value=negative_prompt) + embed.add_field(name="Guidance Scale", value=guidance_scale) + embed.add_field(name="Steps", value=steps) + embed.add_field(name="Width", value=width) + embed.add_field(name="Height", value=height) + + await message.edit(embed=embed) + + await message.edit( + content=f"{ctx.author.mention} - **{prompt}**, Time: {response.get('time'):.2f}s, Seed: {seed}" + ) + file_array = [ + File( + convert_base64_to_bytes(re.sub(pattern, "", x)), + filename=f"{seed}.png", + ) + for x in response["images"] + ] + + await message.add_files(*file_array[len(file_array) - count :]) + else: + if response.get("detail"): + await message.edit( + content=f"{ctx.author.mention} Dream failed - **{response.get('detail')}**" + ) + else: + await message.edit( + content=f"{ctx.author.mention} Dream failed - {status}" + ) + + logger.info(f"Finished task {prompt} for {str(ctx.author)}") + + +async def setup(bot: "ModularBot"): + "Will be called by the bot" + + await bot.add_cog(Image2Image(bot)) diff --git a/bot/models.py b/bot/models.py index defacbcac..482fd2012 100644 --- a/bot/models.py +++ b/bot/models.py @@ -5,7 +5,10 @@ from discord.ext import commands from discord.ext.commands import Cog, Context -from core.types import InferenceBackend, SupportedModel +from bot import shared as shared_bot +from bot.helper import get_available_models, get_loaded_models +from core import shared +from core.types import InferenceBackend if TYPE_CHECKING: from bot.bot import ModularBot @@ -24,7 +27,9 @@ async def loaded_models(self, ctx: Context) -> None: "Show models loaded in the API" async with ClientSession() as session: - async with session.get("http://localhost:5003/api/models/loaded") as r: + async with session.get( + f"http://localhost:{shared.api_port}/api/models/loaded" + ) as r: status = r.status response: List[Dict[str, Any]] = await r.json() @@ -43,54 +48,27 @@ async def loaded_models(self, ctx: Context) -> None: else: await ctx.send(f"Error: {status}") - @commands.hybrid_command(name="available") - @commands.has_permissions(administrator=True) + @commands.hybrid_command(name="available", aliases=["refresh_models"]) async def available_models(self, ctx: Context) -> None: "List all available models" - async with ClientSession() as session: - async with session.get( - "http://localhost:5003/api/models/available" - ) as response: - status = response.status - data: List[Dict[str, Any]] = await response.json() + available_models, status = await get_available_models() + shared_bot.models.set_cached_available_models(available_models) + available_models, status = await shared_bot.models.cached_available_models() + + loaded_models, status = await get_loaded_models() + shared_bot.models.set_cached_loaded_models(loaded_models) if status == 200: - models = { - i["name"] for i in filter(lambda model: (model["valid"] is True), data) - } - await ctx.send("Available models:\n{}".format("\n ".join(models))) + await ctx.send( + "Available models:\n`{}`".format("\n".join(available_models)) + ) else: await ctx.send(f"Error: {status}") @commands.hybrid_command(name="load") @commands.has_permissions(administrator=True) async def load_model( - self, - ctx: Context, - model: SupportedModel, - backend: InferenceBackend = "PyTorch", - ) -> None: - "Load a model" - - message = await ctx.send(f"Loading model {model.value}...") - - async with ClientSession() as session: - async with session.post( - "http://localhost:5003/api/models/load", - params={"model": model.value, "backend": backend}, - ) as response: - status = response.status - response = await response.json() - - if status == 200: - await message.edit(content=f"{response['message']}: {model.value}") - else: - await message.edit(content=f"Error: **{response.get('detail')}**") - - @commands.hybrid_command(name="load-unsupported") - @commands.has_permissions(administrator=True) - async def load_model_unsupported( self, ctx: Context, model: str, @@ -102,7 +80,7 @@ async def load_model_unsupported( async with ClientSession() as session: async with session.post( - "http://localhost:5003/api/models/load", + f"http://localhost:{shared.api_port}/api/models/load", params={"model": model, "backend": backend}, ) as response: status = response.status @@ -115,34 +93,14 @@ async def load_model_unsupported( @commands.hybrid_command(name="unload") @commands.has_permissions(administrator=True) - async def unload_model(self, ctx: Context, model: SupportedModel) -> None: - "Unload a model" - - message = await ctx.send(f"Unloading model {model.value}...") - - async with ClientSession() as session: - async with session.post( - "http://localhost:5003/api/models/unload", - params={"model": model.value}, - ) as response: - status = response.status - response = await response.json() - - if status == 200: - await message.edit(content=f"{response['message']}: {model.value}") - else: - await message.edit(content=f"Error: {status}") - - @commands.hybrid_command(name="unload-unsupported") - @commands.has_permissions(administrator=True) - async def unload_model_unsupported(self, ctx: Context, model: str) -> None: + async def unload_model(self, ctx: Context, model: str) -> None: "Unload a model" message = await ctx.send(f"Unloading model {model}...") async with ClientSession() as session: async with session.post( - "http://localhost:5003/api/models/unload", + f"http://localhost:{shared.api_port}/api/models/unload", params={"model": model}, ) as response: status = response.status @@ -157,4 +115,5 @@ async def unload_model_unsupported(self, ctx: Context, model: str) -> None: async def setup(bot: "ModularBot") -> None: "Will be called by the bot" - await bot.add_cog(Models(bot)) + ext = Models(bot) + await bot.add_cog(ext) diff --git a/bot/shared.py b/bot/shared.py new file mode 100644 index 000000000..19bc92f17 --- /dev/null +++ b/bot/shared.py @@ -0,0 +1,5 @@ +from bot.bot_model_manager import BotModelManager +from bot.config import load_config + +config = load_config() +models = BotModelManager() diff --git a/bot/txt2img.py b/bot/txt2img.py index 4a6773f87..2d30b0f5d 100644 --- a/bot/txt2img.py +++ b/bot/txt2img.py @@ -1,255 +1,98 @@ -import asyncio import logging import random -from typing import TYPE_CHECKING, Dict, Literal, Optional +import re +from typing import TYPE_CHECKING, Optional from uuid import uuid4 -import aiohttp import discord from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers from discord import File from discord.ext import commands from discord.ext.commands import Cog, Context -from core.config import config -from core.types import SupportedModel +from bot.helper import find_closest_model, inference_call +from bot.shared import config from core.utils import convert_base64_to_bytes if TYPE_CHECKING: from bot.bot import ModularBot +logger = logging.getLogger(__name__) -async def dream_call(payload: Dict): - "Call to the backend to generate an image" +pattern = r"data:image/[\w]+;base64," - async def call(): - async with aiohttp.ClientSession() as session: - async with session.post( - "http://localhost:5003/api/generate/txt2img", json=payload - ) as response: - status = response.status - response = await response.json() - return status, response - - try: - status, response = await call() - except aiohttp.ClientOSError: - await asyncio.sleep(0.5) - status, response = await call() - - return status, response - - -class Inference(Cog): +class Text2Image(Cog): "Commands for generating images from text" def __init__(self, bot: "ModularBot") -> None: - self.bot = (bot,) - self.queue_number: int = 0 - - @commands.hybrid_command(name="reset-queue") - @commands.is_owner() - async def reset_queue(self, ctx: Context): - "Reset the queue number" - - self.queue_number = 0 - await ctx.send("✅ Queue reset!") + self.bot = bot - @commands.hybrid_command(name="dream-unsupported") + @commands.hybrid_command(name="text2image") async def dream_unsupported( self, ctx: Context, prompt: str, model: str, negative_prompt: str = "", - guidance_scale: float = 7.0, - steps: Literal[25, 30, 50] = 30, - aspect_ratio: Literal["16:9", "9:16", "1:1"] = "1:1", + guidance_scale: float = config.default_cfg, + steps: int = config.default_steps, + width: int = config.default_width, + height: int = config.default_height, + count: int = config.default_count, seed: Optional[int] = None, - scheduler: KarrasDiffusionSchedulers = KarrasDiffusionSchedulers.EulerAncestralDiscreteScheduler, - use_default_negative_prompt: bool = True, - verbose: bool = False, + scheduler: KarrasDiffusionSchedulers = config.default_scheduler, + verbose: bool = config.default_verbose, ): "Generate an image from prompt" - self.queue_number += 1 - default_negative_prompt = "nsfw, (((deformed))), blurry, bad anatomy, disfigured, poorly drawn face, mutation, mutated, (extra_limb), (ugly), (poorly drawn hands), fused fingers, messy drawing, broken legs censor, censored, censor_bar, multiple breasts, (mutated hands and fingers:1.5), (long body :1.3), (mutation, poorly drawn :1.2), black-white, bad anatomy, liquid body, liquidtongue, disfigured, malformed, mutated, anatomical nonsense, text font ui, error, malformed hands, long neck, blurred, lowers, low res, bad anatomy, bad proportions, bad shadow, uncoordinated body, unnatural body, fused breasts, bad breasts, huge breasts, poorly drawn breasts, extra breasts, liquid breasts, heavy breasts, missingbreasts, huge haunch, huge thighs, huge calf, bad hands, fused hand, missing hand, disappearing arms, disappearing thigh, disappearing calf, disappearing legs, fusedears, bad ears, poorly drawn ears, extra ears, liquid ears, heavy ears, missing ears, old photo, low res, black and white, black and white filter, colorless" - - if model == SupportedModel.SynthWave: - prompt = "snthwve style, " + prompt - elif model == SupportedModel.OpenJourney: - prompt = "mdjrny-4, " + prompt - elif model == SupportedModel.InkpunkDiffusion: - prompt = "nvinkpunk, " + prompt - if seed is None: seed = random.randint(0, 1000000) - if aspect_ratio == "16:9": - width = 680 - height = 384 - elif aspect_ratio == "9:16": - width = 384 - height = 680 - # elif aspect_ratio == "civitai": - # width = 512 - # height = 704 - else: - width = 512 - height = 512 - - payload = { - "data": { - "prompt": prompt, - "id": uuid4().hex, - "negative_prompt": negative_prompt - if not use_default_negative_prompt - else negative_prompt + default_negative_prompt, - "width": width, - "height": height, - "steps": steps, - "guidance_scale": guidance_scale, - "seed": seed, - "batch_size": 1, - "batch_count": 1, - "scheduler": scheduler.value, - }, - "model": model, - "save_image": False, - } - - message = await ctx.send( - "Dreaming... **Queue number: " + str(self.queue_number) + "**" - ) - - try: - status, response = await dream_call(payload=payload) - except Exception as e: - self.queue_number -= 1 - raise e - - self.queue_number -= 1 - - if status == 200: - if verbose: - embed = discord.Embed( - color=discord.Color.green(), - ) - embed.add_field(name="Seed", value=seed) - embed.add_field(name="Time", value=f"{response.get('time'):.2f}s") - embed.add_field(name="Model", value=model) - embed.add_field( - name="Negative Prompt", - value=negative_prompt - if not use_default_negative_prompt - else "*Default*" - + (" + " if negative_prompt else "") - + negative_prompt, - ) - embed.add_field(name="Guidance Scale", value=guidance_scale) - embed.add_field(name="Steps", value=steps) - embed.add_field(name="Aspect Ratio", value=aspect_ratio) - - await message.edit(embed=embed) - - await message.edit( - content=f"{ctx.author.mention} - **{prompt}**, Time: {response.get('time'):.2f}s, Seed: {seed}" - ) - await message.add_files( - File( - convert_base64_to_bytes( - response["images"][0].replace("data:image/png;base64,", "") - ), - filename=f"{seed}.png", - ) + if config.max_width < width or config.max_height < height: + return await ctx.send( + f"Image size is too big, maximum size is {config.max_width}x{config.max_height}" ) - else: - if response.get("detail"): - await message.edit( - content=f"{ctx.author.mention} Dream failed - **{response.get('detail')}**" - ) - else: - await message.edit( - content=f"{ctx.author.mention} Dream failed - {status}" - ) - logging.info(f"Finished task {prompt} for {str(ctx.author)}") - - @commands.hybrid_command(name="dream") - async def dream( - self, - ctx: Context, - prompt: str, - model: SupportedModel, - negative_prompt: str = "", - guidance_scale: float = 7.0, - steps: Literal[25, 30, 50] = 30, - resolution: Literal[ - "512x512", - "1024x1024", - "512x912", - "912x512", - "1920x1080", - "1080x1920", - "1280x720", - "720x1280", - "768x768", - ] = "512x512", - seed: Optional[int] = None, - scheduler: KarrasDiffusionSchedulers = config.bot.default_scheduler, - use_default_negative_prompt: bool = config.bot.use_default_negative_prompt, - verbose: bool = config.bot.verbose, - ): - "Generate an image from prompt" - - self.queue_number += 1 - default_negative_prompt = "nsfw, (((deformed))), blurry, bad anatomy, disfigured, poorly drawn face, mutation, mutated, (extra_limb), (ugly), (poorly drawn hands), fused fingers, messy drawing, broken legs censor, censored, censor_bar, multiple breasts, (mutated hands and fingers:1.5), (long body :1.3), (mutation, poorly drawn :1.2), black-white, bad anatomy, liquid body, liquidtongue, disfigured, malformed, mutated, anatomical nonsense, text font ui, error, malformed hands, long neck, blurred, lowers, low res, bad anatomy, bad proportions, bad shadow, uncoordinated body, unnatural body, fused breasts, bad breasts, huge breasts, poorly drawn breasts, extra breasts, liquid breasts, heavy breasts, missingbreasts, huge haunch, huge thighs, huge calf, bad hands, fused hand, missing hand, disappearing arms, disappearing thigh, disappearing calf, disappearing legs, fusedears, bad ears, poorly drawn ears, extra ears, liquid ears, heavy ears, missing ears, old photo, low res, black and white, black and white filter, colorless" - - if model == SupportedModel.SynthWave: - prompt = "snthwve style, " + prompt - elif model == SupportedModel.OpenJourney: - prompt = "mdjrny-4, " + prompt - elif model == SupportedModel.InkpunkDiffusion: - prompt = "nvinkpunk, " + prompt + if config.max_count < count: + return await ctx.send( + f"Image count is too big, maximum count is {config.max_count}" + ) - if seed is None: - seed = random.randint(0, 1000000) + prompt = prompt + config.extra_prompt + negative_prompt = negative_prompt + config.extra_negative_prompt - width, height = [int(i) for i in resolution.split("x")] + try: + model = await find_closest_model(model) + except IndexError: + await ctx.send(f"No loaded model that is close to `{model}` found") + return payload = { "data": { "prompt": prompt, "id": uuid4().hex, - "negative_prompt": negative_prompt - if not use_default_negative_prompt - else negative_prompt + default_negative_prompt, + "negative_prompt": negative_prompt, "width": width, "height": height, "steps": steps, "guidance_scale": guidance_scale, "seed": seed, "batch_size": 1, - "batch_count": 1, + "batch_count": count, "scheduler": scheduler.value, }, - "model": model.value, + "model": model, "save_image": False, } - message = await ctx.send( - "Dreaming... **Queue number: " + str(self.queue_number) + "**" - ) + message = await ctx.send(f"Generating image with `{model}`...") try: - status, response = await dream_call(payload=payload) + status, response = await inference_call(payload=payload) except Exception as e: - self.queue_number -= 1 raise e - self.queue_number -= 1 - if status == 200: if verbose: embed = discord.Embed( @@ -257,32 +100,27 @@ async def dream( ) embed.add_field(name="Seed", value=seed) embed.add_field(name="Time", value=f"{response.get('time'):.2f}s") - embed.add_field(name="Model", value=model.value) - embed.add_field( - name="Negative Prompt", - value=negative_prompt - if not use_default_negative_prompt - else "*Default*" - + (" + " if negative_prompt else "") - + negative_prompt, - ) + embed.add_field(name="Model", value=model) + embed.add_field(name="Negative Prompt", value=negative_prompt) embed.add_field(name="Guidance Scale", value=guidance_scale) embed.add_field(name="Steps", value=steps) - embed.add_field(name="Resolution", value=resolution) + embed.add_field(name="Width", value=width) + embed.add_field(name="Height", value=height) await message.edit(embed=embed) await message.edit( content=f"{ctx.author.mention} - **{prompt}**, Time: {response.get('time'):.2f}s, Seed: {seed}" ) - await message.add_files( + file_array = [ File( - convert_base64_to_bytes( - response["images"][0].replace("data:image/png;base64,", "") - ), + convert_base64_to_bytes(re.sub(pattern, "", x)), filename=f"{seed}.png", ) - ) + for x in response["images"] + ] + + await message.add_files(*file_array[len(file_array) - count :]) else: if response.get("detail"): await message.edit( @@ -293,10 +131,10 @@ async def dream( content=f"{ctx.author.mention} Dream failed - {status}" ) - logging.info(f"Finished task {prompt} for {str(ctx.author)}") + logger.info(f"Finished task {prompt} for {str(ctx.author)}") async def setup(bot: "ModularBot"): "Will be called by the bot" - await bot.add_cog(Inference(bot)) + await bot.add_cog(Text2Image(bot)) diff --git a/core/aitemplate/compile.py b/core/aitemplate/compile.py index 50fe83e65..260804dc5 100644 --- a/core/aitemplate/compile.py +++ b/core/aitemplate/compile.py @@ -140,7 +140,7 @@ def compile_diffusers( else: logger.info("CLIP already compiled. Skipping...") - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.error(e) websocket_manager.broadcast_sync( Data(data_type="aitemplate_compile", data={"clip": "error"}) @@ -212,7 +212,7 @@ def compile_diffusers( Data(data_type="aitemplate_compile", data={"unet": "finish"}) ) - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.error(e) websocket_manager.broadcast_sync( Data(data_type="aitemplate_compile", data={"unet": "error"}) @@ -286,7 +286,7 @@ def compile_diffusers( Data(data_type="aitemplate_compile", data={"controlnet_unet": "finish"}) ) - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.error(e) websocket_manager.broadcast_sync( Data(data_type="aitemplate_compile", data={"controlnet_unet": "error"}) @@ -347,7 +347,7 @@ def compile_diffusers( Data(data_type="aitemplate_compile", data={"vae": "finish"}) ) - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.error(e) websocket_manager.broadcast_sync( Data(data_type="aitemplate_compile", data={"vae": "error"}) @@ -381,7 +381,7 @@ def compile_diffusers( Data(data_type="aitemplate_compile", data={"cleanup": "finish"}) ) - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.error(e) websocket_manager.broadcast_sync( Data(data_type="aitemplate_compile", data={"cleanup": "error"}) diff --git a/core/aitemplate/src/common.py b/core/aitemplate/src/common.py index 4d8db1040..d0b501b80 100644 --- a/core/aitemplate/src/common.py +++ b/core/aitemplate/src/common.py @@ -1,5 +1,3 @@ -# pylint: disable=protected-access - import torch @@ -8,7 +6,7 @@ def torch_dtype_from_str(dtype: str): def get_shape(x): - shape = [it.value() for it in x._attrs["shape"]] # pylint: disable=protected-access + shape = [it.value() for it in x._attrs["shape"]] return shape diff --git a/core/aitemplate/src/compile_lib/unet.py b/core/aitemplate/src/compile_lib/unet.py index d5db7ff0a..38b92d31e 100644 --- a/core/aitemplate/src/compile_lib/unet.py +++ b/core/aitemplate/src/compile_lib/unet.py @@ -23,7 +23,7 @@ ) -def compile_unet( # pylint: disable=dangerous-default-value +def compile_unet( pt_mod, batch_size=(1, 8), height=(64, 2048), @@ -82,7 +82,7 @@ def compile_unet( # pylint: disable=dangerous-default-value ait_mod = ait_UNet2DConditionModel( sample_size=sample_size, cross_attention_dim=hidden_dim, - attention_head_dim=attention_head_dim, + attention_head_dim=attention_head_dim, # type: ignore use_linear_projection=use_linear_projection, up_block_types=up_block_types, down_block_types=down_block_types, diff --git a/core/aitemplate/src/compile_lib/vae.py b/core/aitemplate/src/compile_lib/vae.py index f3b830e18..b01acae60 100644 --- a/core/aitemplate/src/compile_lib/vae.py +++ b/core/aitemplate/src/compile_lib/vae.py @@ -21,7 +21,7 @@ from ..modeling.vae import AutoencoderKL as ait_AutoencoderKL -def compile_vae( # pylint: disable=dangerous-default-value +def compile_vae( pt_mod, batch_size=(1, 8), height=(64, 2048), diff --git a/core/aitemplate/src/modeling/clip.py b/core/aitemplate/src/modeling/clip.py index 553db7956..f22e3c3df 100644 --- a/core/aitemplate/src/modeling/clip.py +++ b/core/aitemplate/src/modeling/clip.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# pylint: disable=unused-argument, protected-access + from inspect import isfunction from typing import Any, Optional diff --git a/core/aitemplate/src/modeling/embeddings.py b/core/aitemplate/src/modeling/embeddings.py index 206dc8610..12f66ddec 100644 --- a/core/aitemplate/src/modeling/embeddings.py +++ b/core/aitemplate/src/modeling/embeddings.py @@ -28,9 +28,7 @@ def get_timestep_embedding( dtype: str = "float16", arange_name: str = "arange", ) -> Tensor: - assert ( - timesteps._rank() == 1 # pylint: disable=protected-access - ), "Timesteps should be a 1d-array" + assert timesteps._rank() == 1, "Timesteps should be a 1d-array" half_dim = embedding_dim // 2 @@ -63,7 +61,7 @@ def __init__( self, channel: int, time_embed_dim: int, - act_fn: str = "silu", # pylint: disable=unused-argument + act_fn: str = "silu", dtype: str = "float16", ) -> None: super().__init__() diff --git a/core/aitemplate/src/modeling/resnet.py b/core/aitemplate/src/modeling/resnet.py index 20510eba5..d1c55c4ac 100644 --- a/core/aitemplate/src/modeling/resnet.py +++ b/core/aitemplate/src/modeling/resnet.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# pylint: disable=unused-argument, protected-access + from typing import Optional diff --git a/core/aitemplate/src/modeling/unet_2d_condition.py b/core/aitemplate/src/modeling/unet_2d_condition.py index dd868f3e3..b4186f860 100644 --- a/core/aitemplate/src/modeling/unet_2d_condition.py +++ b/core/aitemplate/src/modeling/unet_2d_condition.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# pylint: disable=protected-access, dangerous-default-value, unused-argument + from typing import List, Optional, Tuple, Union @@ -117,7 +117,7 @@ def __init__( self.up_blocks = nn.ModuleList([]) if isinstance(attention_head_dim, int): - attention_head_dim = (attention_head_dim,) * len(down_block_types) + attention_head_dim = (attention_head_dim,) * len(down_block_types) # type: ignore output_channel = block_out_channels[0] for i, down_block_type in enumerate(down_block_types): @@ -134,7 +134,7 @@ def __init__( add_downsample=not is_final_block, resnet_eps=norm_eps, resnet_act_fn=act_fn, - attn_num_head_channels=attention_head_dim[i], + attn_num_head_channels=attention_head_dim[i], # type: ignore cross_attention_dim=cross_attention_dim, downsample_padding=downsample_padding, use_linear_projection=use_linear_projection, @@ -152,14 +152,14 @@ def __init__( output_scale_factor=mid_block_scale_factor, resnet_time_scale_shift="default", cross_attention_dim=cross_attention_dim, - attn_num_head_channels=attention_head_dim[-1], + attn_num_head_channels=attention_head_dim[-1], # type: ignore resnet_groups=norm_num_groups, use_linear_projection=use_linear_projection, dtype=dtype, ) - reversed_block_out_channels = list(reversed(block_out_channels)) - reversed_attention_head_dim = list(reversed(attention_head_dim)) + reversed_block_out_channels = list(reversed(block_out_channels)) # type: ignore + reversed_attention_head_dim = list(reversed(attention_head_dim)) # type: ignore reversed_transformer_layers_per_block = list( reversed(transformer_layers_per_block) # type: ignore ) diff --git a/core/aitemplate/src/modeling/unet_blocks.py b/core/aitemplate/src/modeling/unet_blocks.py index 52af1eb5a..1db33c5e9 100644 --- a/core/aitemplate/src/modeling/unet_blocks.py +++ b/core/aitemplate/src/modeling/unet_blocks.py @@ -447,7 +447,7 @@ def __init__( cross_attention_dim: int = 1280, attention_type: str = "default", output_scale_factor: float = 1.0, - downsample_padding: int = 1, # pylint: disable=unused-argument + downsample_padding: int = 1, add_upsample: bool = True, use_linear_projection: bool = False, only_cross_attention: bool = False, @@ -518,7 +518,7 @@ def forward( ) -> Tensor: for resnet, attn in zip(self.resnets, self.attentions): res_hidden_states = res_hidden_states_tuple[-1] - res_hidden_states_tuple = res_hidden_states_tuple[:-1] + res_hidden_states_tuple = res_hidden_states_tuple[:-1] # type: ignore hidden_states = ops.concatenate()( # type: ignore [hidden_states, res_hidden_states], dim=-1 ) @@ -598,7 +598,7 @@ def forward( ): for resnet in self.resnets: res_hidden_states = res_hidden_states_tuple[-1] - res_hidden_states_tuple = res_hidden_states_tuple[:-1] + res_hidden_states_tuple = res_hidden_states_tuple[:-1] # type: ignore hidden_states = ops.concatenate()( # type: ignore [hidden_states, res_hidden_states], dim=-1 ) @@ -616,7 +616,7 @@ def shape_to_list(shape): return [ sample["symbolic_value"] # type: ignore if isinstance(sample, Tensor) - else sample._attrs["symbolic_value"] # pylint: disable=protected-access + else sample._attrs["symbolic_value"] for sample in shape ] @@ -837,7 +837,7 @@ def forward( self, hidden_states: Tensor, temb: Optional[Tensor] = None, - encoder_states: Optional[Tensor] = None, # pylint: disable=unused-argument + encoder_states: Optional[Tensor] = None, ) -> Tensor: hidden_states = self.resnets[0](hidden_states, temb) for attn, resnet in zip(self.attentions, self.resnets[1:]): # type: ignore diff --git a/core/aitemplate/src/modeling/vae.py b/core/aitemplate/src/modeling/vae.py index e81564c25..3f595f7e7 100644 --- a/core/aitemplate/src/modeling/vae.py +++ b/core/aitemplate/src/modeling/vae.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# pylint: disable=protected-access, unused-argument + from typing import Tuple diff --git a/core/config/config.py b/core/config/config.py index d2dad6baf..a1dfd8a67 100644 --- a/core/config/config.py +++ b/core/config/config.py @@ -1,15 +1,35 @@ import logging import multiprocessing from dataclasses import Field, dataclass, field, fields -from typing import List, Literal, Optional, Union +from typing import Dict, List, Literal, Optional, Union import torch from dataclasses_json import CatchAll, DataClassJsonMixin, Undefined, dataclass_json from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers +from core.config.samplers.sampler_config import SamplerConfig +from core.types import SigmaScheduler + logger = logging.getLogger(__name__) +@dataclass +class BaseDiffusionMixin: + width: int = 512 + height: int = 512 + batch_count: int = 1 + batch_size: int = 1 + seed: int = -1 + cfg_scale: int = 7 + steps: int = 40 + prompt: str = "" + negative_prompt: str = "" + sampler: Union[ + int, str + ] = KarrasDiffusionSchedulers.DPMSolverSinglestepScheduler.value + sigmas: SigmaScheduler = "automatic" + + @dataclass class QuantDict: vae_decoder: Optional[bool] = None @@ -19,72 +39,32 @@ class QuantDict: @dataclass -class Txt2ImgConfig: +class Txt2ImgConfig(BaseDiffusionMixin): "Configuration for the text to image pipeline" - width: int = 512 - height: int = 512 - seed: int = -1 - cfg_scale: int = 7 - sampler: int = KarrasDiffusionSchedulers.DPMSolverSinglestepScheduler.value - prompt: str = "" - negative_prompt: str = "" - steps: int = 40 - batch_count: int = 1 - batch_size: int = 1 self_attention_scale: float = 0.0 @dataclass -class Img2ImgConfig: +class Img2ImgConfig(BaseDiffusionMixin): "Configuration for the image to image pipeline" - width: int = 512 - height: int = 512 - seed: int = -1 - cfg_scale: int = 7 - sampler: int = KarrasDiffusionSchedulers.DPMSolverSinglestepScheduler.value - prompt: str = "" - negative_prompt: str = "" - steps: int = 40 - batch_count: int = 1 - batch_size: int = 1 resize_method: int = 0 denoising_strength: float = 0.6 self_attention_scale: float = 0.0 @dataclass -class InpaintingConfig: +class InpaintingConfig(BaseDiffusionMixin): "Configuration for the inpainting pipeline" - prompt: str = "" - negative_prompt: str = "" - width: int = 512 - height: int = 512 - steps: int = 40 - cfg_scale: int = 7 - seed: int = -1 - batch_count: int = 1 - batch_size: int = 1 - sampler: int = KarrasDiffusionSchedulers.DPMSolverSinglestepScheduler.value self_attention_scale: float = 0.0 @dataclass -class ControlNetConfig: +class ControlNetConfig(BaseDiffusionMixin): "Configuration for the inpainting pipeline" - prompt: str = "" - negative_prompt: str = "" - width: int = 512 - height: int = 512 - seed: int = -1 - cfg_scale: int = 7 - steps: int = 40 - batch_count: int = 1 - batch_size: int = 1 - sampler: int = KarrasDiffusionSchedulers.DPMSolverSinglestepScheduler.value controlnet: str = "lllyasviel/sd-controlnet-canny" controlnet_conditioning_scale: float = 1.0 detection_resolution: int = 512 @@ -107,17 +87,21 @@ class UpscaleConfig: class APIConfig: "Configuration for the API" + # Autoload + autoloaded_textual_inversions: List[str] = field(default_factory=list) + autoloaded_models: List[str] = field(default_factory=list) + autoloaded_vae: Dict[str, str] = field(default_factory=dict) + # Websockets and intervals websocket_sync_interval: float = 0.02 websocket_perf_interval: float = 1.0 + enable_websocket_logging: bool = True # TomeSD use_tomesd: bool = False # really extreme, probably will have to wait around until tome improves a bit tomesd_ratio: float = 0.25 # had to tone this down, 0.4 is too big of a context loss even on short prompts tomesd_downsample_layers: Literal[1, 2, 4, 8] = 1 - image_preview_delay: float = 2.0 - # General optimizations autocast: bool = False attention_processor: Literal[ @@ -126,8 +110,6 @@ class APIConfig: subquadratic_size: int = 512 attention_slicing: Union[int, Literal["auto", "disabled"]] = "disabled" channels_last: bool = True - vae_slicing: bool = True - vae_tiling: bool = False trace_model: bool = False clear_memory_policy: Literal["always", "after_disconnect", "never"] = "always" offload: Literal["module", "model", "disabled"] = "disabled" @@ -139,8 +121,7 @@ class APIConfig: deterministic_generation: bool = False # Device settings - device_id: int = 0 - device_type: Literal["cpu", "cuda", "mps", "directml", "intel", "vulkan"] = "cuda" + device: str = "cuda:0" # Critical enable_shutdown: bool = True @@ -149,9 +130,6 @@ class APIConfig: clip_skip: int = 1 clip_quantization: Literal["full", "int8", "int4"] = "full" - # Autoload - autoloaded_textual_inversions: List[str] = field(default_factory=list) - huggingface_style_parsing: bool = False # Saving @@ -174,6 +152,23 @@ class APIConfig: "max-autotune", ] = "reduce-overhead" + # Hypertile + hypertile: bool = False + hypertile_unet_chunk: int = 256 + + # K_Diffusion + sgm_noise_multiplier: bool = False # also known as "alternate DDIM ODE" + kdiffusers_quantization: bool = True # improves sampling quality + + # "philox" is what a "cuda" generator would be, except, it's on cpu + generator: Literal["device", "cpu", "philox"] = "device" + + # VAE + live_preview_method: Literal["disabled", "approximation", "taesd"] = "approximation" + live_preview_delay: float = 2.0 + vae_slicing: bool = True + vae_tiling: bool = False + @property def dtype(self): "Return selected data type" @@ -184,26 +179,12 @@ def dtype(self): return torch.float32 @property - def device(self): - "Return the device" - - if self.device_type == "intel": - from core.inference.functions import is_ipex_available - - return torch.device("xpu" if is_ipex_available() else "cpu") - - if self.device_type in ["cpu", "mps"]: - return torch.device(self.device_type) - - if self.device_type in ["vulkan", "cuda"]: - return torch.device(f"{self.device_type}:{self.device_id}") - - if self.device_type == "directml": - import torch_directml # pylint: disable=import-error + def overwrite_generator(self) -> bool: + "Whether the generator needs to be overwritten with 'cpu.'" - return torch_directml.device() - else: - raise ValueError(f"Device type {self.device_type} not supported") + return any( + map(lambda x: x in self.device, ["mps", "directml", "vulkan", "intel"]) + ) @dataclass @@ -254,7 +235,8 @@ class InterrogatorConfig: class FrontendConfig: "Configuration for the frontend" - theme: Literal["dark", "light"] = "dark" + theme: str = "dark" + background_image_override: str = "" enable_theme_editor: bool = False image_browser_columns: int = 5 on_change_timer: int = 0 @@ -277,6 +259,7 @@ class Configuration(DataClassJsonMixin): onnx: ONNXConfig = field(default_factory=ONNXConfig) bot: BotConfig = field(default_factory=BotConfig) frontend: FrontendConfig = field(default_factory=FrontendConfig) + sampler_config: SamplerConfig = field(default_factory=SamplerConfig) extra: CatchAll = field(default_factory=dict) diff --git a/core/config/samplers/kdiffusion_sampler_config.py b/core/config/samplers/kdiffusion_sampler_config.py new file mode 100644 index 000000000..3ef57153c --- /dev/null +++ b/core/config/samplers/kdiffusion_sampler_config.py @@ -0,0 +1,92 @@ +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class BaseMixin: + sigma_min: Optional[float] = None + sigma_max: Optional[float] = None + sigma_rho: Optional[float] = None + sigma_discard: Optional[bool] = None + sampler_tmin: Optional[float] = None + sampler_tmax: Optional[float] = None + sampler_noise: Optional[float] = None + + +@dataclass +class AncestralMixin: + eta: Optional[float] = None + + +@dataclass +class Euler(BaseMixin): + s_churn: Optional[float] = None + + +@dataclass +class Euler_a(BaseMixin, AncestralMixin): + noise_sampler: Optional[str] = None + + +@dataclass +class Heun(BaseMixin): + s_churn: Optional[float] = None + s_noise: Optional[float] = None + + +@dataclass +class DPM_2(BaseMixin): + s_churn: Optional[float] = None + s_noise: Optional[float] = None + + +@dataclass +class DPM_2_a(BaseMixin, AncestralMixin): + pass + + +@dataclass +class LMS(BaseMixin): + order: Optional[int] = None + + +@dataclass +class DPM_fast(BaseMixin, AncestralMixin): + pass + + +@dataclass +class DPM_adaptive(BaseMixin, AncestralMixin): + order: Optional[int] = None + rtol: Optional[float] = None + atol: Optional[float] = None + h_init: Optional[float] = None + pcoeff: Optional[float] = None + icoeff: Optional[float] = None + dcoeff: Optional[float] = None + accept_safety: Optional[float] = None + + +@dataclass +class DPMpp_2S_a(BaseMixin, AncestralMixin): + pass + + +@dataclass +class DPMpp_SDE(BaseMixin, AncestralMixin): + r: Optional[float] = None + + +@dataclass +class DPMpp_2M(BaseMixin): + pass + + +@dataclass +class DPMpp_2M_SDE(BaseMixin, AncestralMixin): + solver_type: Optional[str] = None + + +@dataclass +class DPMpp_3M_SDE(BaseMixin, AncestralMixin): + pass diff --git a/core/config/samplers/sampler_config.py b/core/config/samplers/sampler_config.py new file mode 100644 index 000000000..a9355c7cd --- /dev/null +++ b/core/config/samplers/sampler_config.py @@ -0,0 +1,141 @@ +from dataclasses import dataclass, field +from typing import List, Literal, Union + +from .kdiffusion_sampler_config import ( + DPM_2, + LMS, + DPM_2_a, + DPM_adaptive, + DPM_fast, + DPMpp_2M, + DPMpp_2M_SDE, + DPMpp_2S_a, + DPMpp_3M_SDE, + DPMpp_SDE, + Euler, + Euler_a, + Heun, +) + + +@dataclass +class Slider: + min: int + max: int + step: float + componentType: Literal["slider"] = "slider" + + +@dataclass +class SelectOption: + label: str + value: str + + +@dataclass +class Select: + options: List[SelectOption] + componentType: Literal["select"] = "select" + + +@dataclass +class Checkbox: + componentType: Literal["boolean"] = "boolean" + + +@dataclass +class NumberInput: + min: int + max: int + step: float + componentType: Literal["number"] = "number" + + +FrontendComponent = Union[Slider, Select, Checkbox, NumberInput] + + +@dataclass +class ParamSettings: + # K-diffusion + eta_noise_seed_delta: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=999_999_999_999, step=1) + ) + denoiser_enable_quantization: FrontendComponent = field(default_factory=Checkbox) + karras_sigma_scheduler: FrontendComponent = field(default_factory=Checkbox) + sigma_use_old_karras_scheduler: FrontendComponent = field(default_factory=Checkbox) + sigma_discard: FrontendComponent = field(default_factory=Checkbox) + sigma_rho: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + sigma_min: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + sigma_max: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + eta: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + s_churn: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + sampler_tmin: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + sampler_tmax: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + sampler_noise: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + noise_sampler: FrontendComponent = field(default_factory=lambda: Select([])) + + order: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=1) + ) + rtol: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + atol: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + h_init: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + pcoeff: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + icoeff: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + dcoeff: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + accept_safety: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + r: FrontendComponent = field( + default_factory=lambda: NumberInput(min=0, max=10, step=0.01) + ) + solver_type: FrontendComponent = field(default_factory=lambda: Select([])) + + +@dataclass +class SamplerConfig: + # Settings for UI + ui_settings: ParamSettings = field(default_factory=ParamSettings) + + # Samplers + euler_a: Euler_a = field(default_factory=Euler_a) + euler: Euler = field(default_factory=Euler) + lms: LMS = field(default_factory=LMS) + heun: Heun = field(default_factory=Heun) + dpm_fast: DPM_fast = field(default_factory=DPM_fast) + dpm_adaptive: DPM_adaptive = field(default_factory=DPM_adaptive) + dpm2: DPM_2 = field(default_factory=DPM_2) + dpm2_a: DPM_2_a = field(default_factory=DPM_2_a) + dpmpp_2s_a: DPMpp_2S_a = field(default_factory=DPMpp_2S_a) + dpmpp_2m: DPMpp_2M = field(default_factory=DPMpp_2M) + dpmpp_sde: DPMpp_SDE = field(default_factory=DPMpp_SDE) + dpmpp_2m_sde: DPMpp_2M_SDE = field(default_factory=DPMpp_2M_SDE) + dpmpp_3m_sde: DPMpp_3M_SDE = field(default_factory=DPMpp_3M_SDE) diff --git a/core/files.py b/core/files.py index 71d401690..b345e9944 100644 --- a/core/files.py +++ b/core/files.py @@ -44,7 +44,7 @@ def pytorch(self) -> List[ModelResponse]: logger.debug(f"Found model {model_name}") # Skip if it is not a huggingface model - if not "model" in model_name: + if "model" not in model_name: continue name: str = "/".join(model_name.split("--")[1:3]) @@ -428,7 +428,7 @@ def get_full_model_path( # 2. Check if model is stored in local storage alt_path = Path("data") / model_folder / repo_id - if alt_path.exists() or force: + if alt_path.exists() or force or alt_path.is_symlink(): logger.debug(f"Found model in {alt_path}") return alt_path diff --git a/core/functions.py b/core/functions.py index 33390d2b2..eda77f59d 100644 --- a/core/functions.py +++ b/core/functions.py @@ -2,6 +2,7 @@ import logging import os import re +import threading from io import BytesIO from pathlib import Path from typing import Dict, List, Union @@ -30,11 +31,15 @@ def image_meta_from_file(path: Path) -> Dict[str, str]: return meta else: - data = piexif.load(path.as_posix()) - meta: Dict[str, str] = json.loads( - piexif.helper.UserComment.load(data["Exif"][piexif.ExifIFD.UserComment]) - ) - return meta + try: + data = piexif.load(path.as_posix()) + meta: Dict[str, str] = json.loads( + piexif.helper.UserComment.load(data["Exif"][piexif.ExifIFD.UserComment]) + ) + return meta + except ValueError as e: + logger.warning(f"Error while loading metadata from {path}: {e}") + return {} def inject_var_into_dotenv(key: str, value: str) -> None: @@ -115,3 +120,29 @@ def images_to_response(images: Union[List[Image.Image], List[str]], time: float) "time": time, "images": [convert_image_to_base64(i) for i in images], # type: ignore } + + +def debounce(wait_time: float): + """ + Decorator that will debounce a function so that it is called after wait_time seconds + If it is called multiple times, will wait for the last call to be debounced and run only this one. + """ + + def decorator(function): + def debounced(*args, **kwargs): + def call_function(): + debounced._timer = None + return function(*args, **kwargs) + + # if we already have a call to the function currently waiting to be executed, reset the timer + if debounced._timer is not None: + debounced._timer.cancel() + + # after wait_time, call the function provided to the decorator with its arguments + debounced._timer = threading.Timer(wait_time, call_function) + debounced._timer.start() + + debounced._timer = None + return debounced + + return decorator diff --git a/core/gpu.py b/core/gpu.py index 137058f0f..92c72249e 100644 --- a/core/gpu.py +++ b/core/gpu.py @@ -15,12 +15,12 @@ from core import shared from core.config import config from core.errors import InferenceInterruptedError, ModelNotLoadedError -from core.flags import HighResFixFlag from core.inference.ait import AITemplateStableDiffusion from core.inference.esrgan import RealESRGAN, Upscaler from core.inference.functions import download_model, is_ipex_available from core.inference.pytorch import PyTorchStableDiffusion from core.interrogation.base_interrogator import InterrogationResult +from core.optimizations import is_hypertile_available from core.png_metadata import save_images from core.queue import Queue from core.types import ( @@ -50,8 +50,7 @@ class GPU: "GPU with models attached to it." - def __init__(self, torch_gpu_id: int) -> None: - self.gpu_id = torch_gpu_id + def __init__(self) -> None: self.queue: Queue = Queue() self.loaded_models: Dict[ str, @@ -67,23 +66,29 @@ def _get_capabilities(self) -> Capabilities: "Returns all of the capabilities of this GPU." cap = Capabilities() if torch.cuda.is_available(): - cap.supported_backends.append("cuda") + for i in range(torch.cuda.device_count()): + cap.supported_backends.append( + [f"(CUDA) {torch.cuda.get_device_name(i)}", f"cuda:{i}"] + ) try: - import torch_directml # pylint: disable=unused-import + import torch_directml - cap.supported_backends.append("directml") + for i in range(torch_directml.device_count()): + cap.supported_backends.append( + [f"(DML) {torch_directml.device_name(i)}", f"privateuseone:{i}"] + ) except ImportError: pass if torch.backends.mps.is_available(): # type: ignore - cap.supported_backends.append("mps") + cap.supported_backends.append(["MPS", "mps"]) if torch.is_vulkan_available(): - cap.supported_backends.append("vulkan") + cap.supported_backends.append(["Vulkan", "vulkan"]) if is_ipex_available(): - cap.supported_backends.append("xpu") + cap.supported_backends.append(["Intel CPU/GPU", "xpu"]) test_suite = ["float16", "bfloat16"] support_map: Dict[str, List[str]] = {} - for device in [torch.device("cpu"), config.api.device]: + for device in [torch.device("cpu"), torch.device(config.api.device)]: support_map[device.type] = [] for dt in test_suite: dtype = getattr(torch, dt) @@ -101,27 +106,27 @@ def _get_capabilities(self) -> Capabilities: cap.supported_precisions_gpu = ["float32"] + s try: cap.supported_torch_compile_backends = ( - torch._dynamo.list_backends() # type: ignore # pylint: disable=protected-access + torch._dynamo.list_backends() # type: ignore ) - except Exception: # pylint: disable=broad-exception-caught + except Exception: pass if torch.cuda.is_available(): try: - import bitsandbytes as bnb # pylint: disable=import-error - import bitsandbytes.functional as F # pylint: disable=import-error + import bitsandbytes as bnb + import bitsandbytes.functional as F a = torch.tensor([1.0]).cuda() b, b_state = F.quantize_fp4(torch.tensor([2.0]).cuda()) bnb.matmul_4bit(a, b, quant_state=b_state) # type: ignore cap.supports_int8 = True logger.debug("GPU supports int8") - except Exception: # pylint: disable=broad-except + except Exception: logger.debug("GPU does not support int8") cap.supports_xformers = is_xformers_available() if torch.cuda.is_available(): - caps = torch.cuda.get_device_capability(self.gpu_id) + caps = torch.cuda.get_device_capability(0) if caps[0] < 7: cap.has_tensor_cores = False @@ -130,18 +135,23 @@ def _get_capabilities(self) -> Capabilities: elif caps[0] >= 9: cap.has_tensorfloat = True + if is_hypertile_available(): + cap.hypertile_available = True + return cap def vram_free(self) -> float: "Returns the amount of free VRAM on the GPU in MB." + index = torch.device(config.api.device).index return ( - torch.cuda.get_device_properties(self.gpu_id).total_memory - - torch.cuda.memory_allocated(self.gpu_id) + torch.cuda.get_device_properties(index).total_memory + - torch.cuda.memory_allocated(index) ) / 1024**2 def vram_used(self) -> float: "Returns the amount of used VRAM on the GPU in MB." - return torch.cuda.memory_allocated(self.gpu_id) / 1024**2 + index = torch.device(config.api.device).index + return torch.cuda.memory_allocated(index) / 1024**2 async def generate( self, @@ -183,14 +193,6 @@ def generate_thread_call(job: Job) -> List[Image.Image]: strength: float = getattr(job.data, "strength", 1.0) steps = math.floor(steps * strength) - extra_steps: int = 0 - if "highres_fix" in job.flags: - flag = HighResFixFlag.from_dict(job.flags["highres_fix"]) - extra_steps = math.floor( - flag.steps * flag.strength * job.data.batch_count - ) - - shared.current_steps = steps * job.data.batch_count + extra_steps shared.current_done_steps = 0 if not isinstance(job, ControlNetQueueEntry): @@ -201,6 +203,8 @@ def generate_thread_call(job: Job) -> List[Image.Image]: shared_dependent.cached_controlnet_preprocessor = None self.memory_cleanup() + # shared.current_model = model + if isinstance(model, PyTorchStableDiffusion): logger.debug("Generating with PyTorch") images: List[Image.Image] = model.generate(job) @@ -259,7 +263,7 @@ def generate_thread_call(job: Job) -> List[Image.Image]: logger.debug(f"Strings returned from R2: {len(out)}") images = out - except Exception as err: # pylint: disable=broad-except + except Exception as err: self.memory_cleanup() await self.queue.mark_finished(job.data.id) raise err @@ -292,7 +296,7 @@ def generate_thread_call(job: Job) -> List[Image.Image]: ) return ([], 0.0) - except Exception as e: # pylint: disable=broad-except + except Exception as e: if not isinstance(e, ModelNotLoadedError): await websocket_manager.broadcast( Notification( @@ -409,9 +413,10 @@ def memory_cleanup(self): "Release all unused memory" if config.api.clear_memory_policy == "always": if torch.cuda.is_available(): - logger.debug(f"Cleaning up GPU memory: {self.gpu_id}") + index = torch.device(config.api.device).index + logger.debug(f"Cleaning up GPU memory: {index}") - with torch.cuda.device(self.gpu_id): + with torch.cuda.device(index): torch.cuda.empty_cache() torch.cuda.ipc_collect() @@ -671,8 +676,8 @@ def generate_call(job: UpscaleQueueEntry): else: pipe = Upscaler( model=job.model, - device_id=config.api.device_id, - cpu=config.api.device_type == "cpu", + device_id=torch.device(config.api.device).index, + cpu=config.api.device == "cpu", fp16=True, ) diff --git a/core/inference/ait/aitemplate.py b/core/inference/ait/aitemplate.py index 1e179e90a..322661ba6 100644 --- a/core/inference/ait/aitemplate.py +++ b/core/inference/ait/aitemplate.py @@ -12,28 +12,31 @@ from api import websocket_manager from api.websockets.data import Data +from core import shared from core.config import config +from core.flags import HighResFixFlag from core.inference.ait.pipeline import StableDiffusionAITPipeline from core.inference.base_model import InferenceModel from core.inference.functions import load_pytorch_pipeline -from core.inference_callbacks import ( - controlnet_callback, - img2img_callback, - txt2img_callback, -) +from core.inference.utilities.latents import scale_latents +from core.inference_callbacks import callback from core.types import ( Backend, ControlNetQueueEntry, Img2ImgQueueEntry, Job, + SigmaScheduler, Txt2ImgQueueEntry, ) from core.utils import convert_images_to_base64_grid, convert_to_image, resize -from ..utilities.aitemplate import init_ait_module -from ..utilities.controlnet import image_to_controlnet_input -from ..utilities.lwp import get_weighted_text_embeddings -from ..utilities.scheduling import change_scheduler +from ..utilities import ( + change_scheduler, + create_generator, + get_weighted_text_embeddings, + image_to_controlnet_input, + init_ait_module, +) logger = logging.getLogger(__name__) @@ -59,7 +62,7 @@ def __init__( self.safety_checker: Any self.feature_extractor: CLIPFeatureExtractor - from aitemplate.compiler import Model # pylint: disable=E0611,E0401 + from aitemplate.compiler import Model self.clip_ait_exe: Model self.unet_ait_exe: Model @@ -77,7 +80,6 @@ def directory(self) -> str: return os.path.join("data", "aitemplate", self.model_id) def load(self): - # pylint: disable=redefined-outer-name,reimported from .pipeline import StableDiffusionAITPipeline pipe = load_pytorch_pipeline( @@ -172,9 +174,7 @@ def manage_optional_components( mapping.map_unet(self.unet) ) self.unet_ait_exe.fold_constants() - self.current_unet = ( # pylint: disable=attribute-defined-outside-init - "unet" - ) + self.current_unet = "unet" # self.unet.cpu() @@ -203,9 +203,7 @@ def manage_optional_components( mapping.map_unet(self.unet) ) self.unet_ait_exe.fold_constants() - self.current_unet = ( # pylint: disable=attribute-defined-outside-init - "controlnet_unet" - ) + self.current_unet = "controlnet_unet" # self.unet.cpu() @@ -237,14 +235,13 @@ def manage_optional_components( def create_pipe( self, controlnet: str = "", - seed: int = -1, - scheduler: Optional[Tuple[Any, bool]] = None, - ) -> Tuple["StableDiffusionAITPipeline", torch.Generator]: + scheduler: Optional[Tuple[Any, SigmaScheduler]] = None, + sampler_settings: Optional[dict] = None, + ) -> "StableDiffusionAITPipeline": "Centralized way to create new pipelines." self.manage_optional_components(target_controlnet=controlnet) - # pylint: disable=redefined-outer-name,reimported from .pipeline import StableDiffusionAITPipeline pipe = StableDiffusionAITPipeline( @@ -260,15 +257,14 @@ def create_pipe( vae_ait_exe=self.vae_ait_exe, ) - generator = torch.Generator(self.device).manual_seed(seed) - if scheduler: change_scheduler( model=pipe, scheduler=scheduler[0], - use_karras_sigmas=scheduler[1], + sigma_type=scheduler[1], + sampler_settings=sampler_settings, ) - return pipe, generator + return pipe def generate(self, job: Job) -> List[Image.Image]: logging.info(f"Adding job {job.data.id} to queue") @@ -291,18 +287,27 @@ def txt2img( job: Txt2ImgQueueEntry, ) -> List[Image.Image]: "Generates images from text" - pipe, generator = self.create_pipe( - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + pipe = self.create_pipe( + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(seed=job.data.seed) + total_images: List[Image.Image] = [] + shared.current_method = "txt2img" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): + output_type = "pil" + + if "highres_fix" in job.flags: + output_type = "latent" + prompt_embeds, negative_prompt_embeds = get_weighted_text_embeddings( pipe, job.data.prompt, job.data.negative_prompt ) data = pipe( + generator=generator, prompt_embeds=prompt_embeds, negative_prompt_embeds=negative_prompt_embeds, height=job.data.height, @@ -310,11 +315,41 @@ def txt2img( num_inference_steps=job.data.steps, guidance_scale=job.data.guidance_scale, negative_prompt=job.data.negative_prompt, - output_type="pil", - generator=generator, - callback=txt2img_callback, + output_type=output_type, + callback=callback, num_images_per_prompt=job.data.batch_size, ) + + if output_type == "latent": + latents = data[0] # type: ignore + assert isinstance(latents, (torch.Tensor, torch.FloatTensor)) + + flag = job.flags["highres_fix"] + flag = HighResFixFlag.from_dict(flag) + + latents = scale_latents( + latents=latents, + scale=flag.scale, + latent_scale_mode=flag.latent_scale_mode, + ) + + data = pipe( + generator=generator, + prompt=job.data.prompt, + image=latents, + height=latents.shape[2] * 8, + width=latents.shape[3] * 8, + num_inference_steps=flag.steps, + guidance_scale=job.data.guidance_scale, + self_attention_scale=job.data.self_attention_scale, + negative_prompt=job.data.negative_prompt, + output_type="pil", + callback=callback, + strength=flag.strength, + return_dict=False, + num_images_per_prompt=job.data.batch_size, + ) + images: list[Image.Image] = data[0] # type: ignore total_images.extend(images) @@ -327,7 +362,9 @@ def txt2img( "current_step": 0, "total_steps": 0, "image": convert_images_to_base64_grid( - total_images, quality=90, image_format="webp" + total_images, + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) @@ -340,21 +377,25 @@ def img2img( job: Img2ImgQueueEntry, ) -> List[Image.Image]: "Generates images from images" - pipe, generator = self.create_pipe( - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + pipe = self.create_pipe( + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(seed=job.data.seed) + input_image = convert_to_image(job.data.image) input_image = resize(input_image, job.data.width, job.data.height) total_images: List[Image.Image] = [] + shared.current_method = "img2img" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): prompt_embeds, negative_prompt_embeds = get_weighted_text_embeddings( pipe, job.data.prompt, job.data.negative_prompt ) data = pipe( + generator=generator, prompt_embeds=prompt_embeds, negative_prompt_embeds=negative_prompt_embeds, image=input_image, # type: ignore @@ -362,8 +403,7 @@ def img2img( guidance_scale=job.data.guidance_scale, negative_prompt=job.data.negative_prompt, output_type="pil", - generator=generator, - callback=img2img_callback, + callback=callback, strength=job.data.strength, # type: ignore return_dict=False, num_images_per_prompt=job.data.batch_size, @@ -382,7 +422,9 @@ def img2img( "current_step": 0, "total_steps": 0, "image": convert_images_to_base64_grid( - total_images, quality=90, image_format="webp" + total_images, + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) @@ -395,12 +437,14 @@ def controlnet2img( job: ControlNetQueueEntry, ) -> List[Image.Image]: "Generates images from images" - pipe, generator = self.create_pipe( + pipe = self.create_pipe( controlnet=job.data.controlnet, - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(seed=job.data.seed) + input_image = convert_to_image(job.data.image) input_image = resize(input_image, job.data.width, job.data.height) @@ -409,12 +453,14 @@ def controlnet2img( input_image = image_to_controlnet_input(input_image, job.data) total_images: List[Image.Image] = [input_image] + shared.current_method = "controlnet" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): prompt_embeds, negative_prompt_embeds = get_weighted_text_embeddings( pipe, job.data.prompt, job.data.negative_prompt ) data = pipe( + generator=generator, prompt_embeds=prompt_embeds, negative_prompt_embeds=negative_prompt_embeds, image=input_image, # type: ignore @@ -422,8 +468,7 @@ def controlnet2img( guidance_scale=job.data.guidance_scale, negative_prompt=job.data.negative_prompt, output_type="pil", - generator=generator, - callback=controlnet_callback, + callback=callback, return_dict=False, num_images_per_prompt=job.data.batch_size, controlnet_conditioning_scale=job.data.controlnet_conditioning_scale, # type: ignore @@ -444,7 +489,11 @@ def controlnet2img( "current_step": 0, "total_steps": 0, "image": convert_images_to_base64_grid( - total_images, quality=90, image_format="webp" + total_images + if job.data.return_preprocessed + else total_images[1:], + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) diff --git a/core/inference/ait/pipeline.py b/core/inference/ait/pipeline.py index bb515a2da..e03e2e884 100644 --- a/core/inference/ait/pipeline.py +++ b/core/inference/ait/pipeline.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import inspect import logging import math from pathlib import Path @@ -41,6 +42,8 @@ prepare_latents, preprocess_image, ) +from core.inference.utilities.philox import PhiloxGenerator +from core.scheduling import KdiffusionSchedulerAdapter if is_aitemplate_available(): from core.aitemplate.config import get_unet_in_channels @@ -54,14 +57,14 @@ class StableDiffusionAITPipeline(StableDiffusionPipeline): Pipeline for everything """ - def __init__( # pylint: disable=super-init-not-called + def __init__( self, vae: AutoencoderKL, text_encoder: CLIPTextModel, tokenizer: CLIPTokenizer, scheduler: KarrasDiffusionSchedulers, controlnet: Optional[ControlNetModel], - unet: Optional[UNet2DConditionModel], # type: ignore # pylint: disable=unused-argument + unet: Optional[UNet2DConditionModel], # type: ignore directory: str = "", clip_ait_exe: Optional[Any] = None, unet_ait_exe: Optional[Any] = None, @@ -125,7 +128,7 @@ def __init__( # pylint: disable=super-init-not-called self.unet_in_channels = get_unet_in_channels(directory=Path(directory)) - def unet_inference( # pylint: disable=dangerous-default-value + def unet_inference( self, latent_model_input, timesteps, @@ -134,6 +137,7 @@ def unet_inference( # pylint: disable=dangerous-default-value width, down_block: list = [None], mid_block=None, + **kwargs, ): "Execute AIT#UNet module" exe_module = self.unet_ait_exe @@ -204,8 +208,9 @@ def vae_inference(self, vae_input, height, width): @torch.no_grad() def __call__( self, + generator: Union[PhiloxGenerator, torch.Generator], prompt: Optional[Union[str, List[str]]] = None, - image: Optional[Image.Image] = None, # type: ignore + image: Union[torch.FloatTensor, Image.Image, None] = None, # type: ignore prompt_embeds: Optional[torch.Tensor] = None, negative_prompt_embeds: Optional[torch.Tensor] = None, height: int = 512, @@ -217,7 +222,6 @@ def __call__( strength: Optional[float] = 0.7, negative_prompt: Optional[Union[str, List[str]]] = None, eta: Optional[float] = 0.0, - generator: Optional[torch.Generator] = None, latents: Optional[torch.FloatTensor] = None, output_type: Optional[str] = "pil", return_dict: bool = True, @@ -279,18 +283,22 @@ def __call__( text_embeddings = prompt_embeds self.scheduler.set_timesteps(num_inference_steps, device=self.device) # type: ignore - txt2img = image is None or self.controlnet is not None timesteps, num_inference_steps = get_timesteps( - self.scheduler, num_inference_steps, strength or 0.7, self.device, txt2img + self.scheduler, + num_inference_steps, + strength or 0.7, + self.device, + image is None or self.controlnet is not None, ) latent_timestep = timesteps[:1].repeat(self.batch * num_images_per_prompt) # type: ignore - if image is not None: + if isinstance(image, Image.Image): if isinstance(image, Image.Image): width, height = image.size # type: ignore if self.controlnet is None: image = preprocess_image(image) + image = image.to(device=self.device, dtype=text_embeddings.dtype) else: ctrl_image = prepare_image( image, @@ -314,11 +322,14 @@ def __call__( prompt_embeds.dtype, self.device, generator, + latents, align_to=64, ) - extra_step_kwargs = prepare_extra_step_kwargs(self.scheduler, generator, eta) # type: ignore + extra_step_kwargs = prepare_extra_step_kwargs( + self.scheduler, eta, generator=generator + ) # Necessary for controlnet to function - text_embeddings = text_embeddings.half() # type: ignore + text_embeddings = text_embeddings.half() controlnet_keep = [] if self.controlnet is not None: @@ -328,19 +339,19 @@ def __call__( - float(i / len(timesteps) < 0.0 or (i + 1) / len(timesteps) > 1.0) ) - for i, t in enumerate(tqdm(timesteps, desc="AITemplate")): + def do_denoise(x, t, call: Callable) -> torch.Tensor: latent_model_input = ( - torch.cat([latents] * 2) if do_classifier_free_guidance else latents # type: ignore + torch.cat([x] * 2) if do_classifier_free_guidance else x ) - latent_model_input = self.scheduler.scale_model_input(latent_model_input, t).half() # type: ignore + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) # type: ignore # predict the noise residual - if self.controlnet is not None and ctrl_image is not None: # type: ignore + if self.controlnet is not None and ctrl_image is not None: if guess_mode and do_classifier_free_guidance: # Infer ControlNet only for the conditional batch. - control_model_input = latents + control_model_input = x control_model_input = self.scheduler.scale_model_input( - control_model_input, t + control_model_input, t # type: ignore ).half() controlnet_prompt_embeds = text_embeddings.chunk(2)[1] else: @@ -349,7 +360,7 @@ def __call__( cond_scale = controlnet_conditioning_scale * controlnet_keep[i] down_block_res_samples, mid_block_res_sample = self.controlnet( - control_model_input, + control_model_input.half(), t, encoder_hidden_states=controlnet_prompt_embeds.half(), controlnet_cond=ctrl_image, # type: ignore @@ -373,20 +384,20 @@ def __call__( ] ) - noise_pred = self.unet_inference( + noise_pred = call( latent_model_input, t, - encoder_hidden_states=text_embeddings, + cond=text_embeddings, height=height, width=width, down_block=down_block_res_samples, mid_block=mid_block_res_sample, ) else: - noise_pred = self.unet_inference( + noise_pred = call( latent_model_input, t, - encoder_hidden_states=text_embeddings, + cond=text_embeddings, height=height, width=width, ) @@ -397,12 +408,65 @@ def __call__( noise_pred_text - noise_pred_uncond ) - latents = self.scheduler.step( - noise_pred, t, latents, **extra_step_kwargs, return_dict=False # type: ignore - )[0] + if not isinstance(self.scheduler, KdiffusionSchedulerAdapter): + x = self.scheduler.step( + noise_pred, t, x, **extra_step_kwargs, return_dict=False # type: ignore + )[0] + else: + x = noise_pred + return x + + if isinstance(self.scheduler, KdiffusionSchedulerAdapter): + func_param_keys = inspect.signature( + self.scheduler.do_inference + ).parameters.keys() + + if ( + "optional_device" in func_param_keys + and "optional_dtype" in func_param_keys + ): + latents = self.scheduler.do_inference( + latents, # type: ignore + generator=generator, + call=self.unet_inference, + apply_model=do_denoise, + callback=callback, + callback_steps=1, + optional_device=self.device, # type: ignore + optional_dtype=latents.dtype, # type: ignore + ) + else: + latents = self.scheduler.do_inference( + latents, # type: ignore + generator=generator, + call=self.unet_inference, + apply_model=do_denoise, + callback=callback, + callback_steps=1, + ) + else: + + def _call(*args, **kwargs): + if len(args) == 3: + encoder_hidden_states = args[-1] + args = args[:2] + if kwargs.get("cond", None) is not None: + encoder_hidden_states = kwargs.pop("cond") + return self.unet_inference( + *args, + encoder_hidden_states=encoder_hidden_states, # type: ignore + **kwargs, + ) - if callback is not None: - callback(i, t, latents) # type: ignore + for i, t in enumerate(tqdm(timesteps, desc="AITemplate")): + latents = do_denoise(latents, t, _call) # type: ignore + + # call the callback, if provided + if callback is not None: + callback(i, t, latents) # type: ignore + + if output_type == "latent": + return latents, None latents = 1 / 0.18215 * latents # type: ignore image: torch.Tensor = self.vae_inference(latents, height=height, width=width) @@ -413,9 +477,6 @@ def __call__( if output_type == "pil": image = self.numpy_to_pil(image) # type: ignore - has_nsfw_concept = None - elif output_type == "latent": - image = latents # type: ignore has_nsfw_concept = None else: raise ValueError(f"Invalid output_type {output_type}") diff --git a/core/inference/base_model.py b/core/inference/base_model.py index ee414c5fa..8f0045020 100644 --- a/core/inference/base_model.py +++ b/core/inference/base_model.py @@ -34,14 +34,13 @@ def memory_cleanup(self) -> None: if config.api.clear_memory_policy == "always": gc.collect() - if config.api.device_type not in ["cpu", "directml", "vulkan"]: - if torch.cuda.is_available(): - torch.cuda.empty_cache() - torch.cuda.ipc_collect() - if torch.backends.mps.is_available(): # type: ignore - torch.mps.empty_cache() # type: ignore - try: - if torch.xpu.is_available(): # type: ignore - torch.xpu.empty_cache() # type: ignore - except AttributeError: - pass + if torch.cuda.is_available(): + torch.cuda.empty_cache() + torch.cuda.ipc_collect() + if torch.backends.mps.is_available(): # type: ignore + torch.mps.empty_cache() # type: ignore + try: + if torch.xpu.is_available(): # type: ignore + torch.xpu.empty_cache() # type: ignore + except AttributeError: + pass diff --git a/core/inference/esrgan/real_esrgan.py b/core/inference/esrgan/real_esrgan.py index ba9383bf6..73cde01e1 100644 --- a/core/inference/esrgan/real_esrgan.py +++ b/core/inference/esrgan/real_esrgan.py @@ -43,7 +43,10 @@ def __init__( def gpu_id(self) -> int: "Returns the GPU ID" - return config.api.device_id + if config.api.device.startswith("cuda:"): + return int(config.api.device.split(":")[-1]) + else: + return -1 def load(self): if self.model_id == "RealESRGAN_x4plus": # x4 RRDBNet model diff --git a/core/inference/esrgan/upscale.py b/core/inference/esrgan/upscale.py index 03fe07804..4ac239a59 100644 --- a/core/inference/esrgan/upscale.py +++ b/core/inference/esrgan/upscale.py @@ -12,7 +12,7 @@ import numpy as np import torch from PIL import Image -from rich import print # pylint: disable=redefined-builtin +from rich import print from rich.progress import BarColumn, Progress, TaskID, TimeRemainingColumn from .utils import dataops as ops @@ -350,8 +350,8 @@ def upscale(self, img: np.ndarray) -> np.ndarray: img1 = np.copy(img[:, :, :3]) img2 = np.copy(img[:, :, :3]) for c in range(3): - img1[:, :, c] *= img[:, :, 3] - img2[:, :, c] = (img2[:, :, c] - 1) * img[:, :, 3] + 1 + img1[:, :, c] *= img[:, :, 3] # type: ignore + img2[:, :, c] = (img2[:, :, c] - 1) * img[:, :, 3] + 1 # type: ignore output1 = self.process(img1) output2 = self.process(img2) diff --git a/core/inference/esrgan/utils/architecture/RRDB.py b/core/inference/esrgan/utils/architecture/RRDB.py index 66af96fd5..0442f8bd8 100644 --- a/core/inference/esrgan/utils/architecture/RRDB.py +++ b/core/inference/esrgan/utils/architecture/RRDB.py @@ -253,7 +253,7 @@ def get_num_blocks(self) -> int: nbs.append(int(m.group(1))) if nbs: break - return max(*nbs) + 1 + return max(*nbs) + 1 # type: ignore def forward(self, x): if self.shuffle_factor: diff --git a/core/inference/esrgan/utils/architecture/block.py b/core/inference/esrgan/utils/architecture/block.py index d8c36d9d2..a58b7dd83 100644 --- a/core/inference/esrgan/utils/architecture/block.py +++ b/core/inference/esrgan/utils/architecture/block.py @@ -289,8 +289,8 @@ def __init__( norm_type=None, act_type="leakyrelu", mode="CNA", - convtype="Conv2D", # type: ignore # pylint: disable=unused-argument - spectral_norm=False, # type: ignore # pylint: disable=unused-argument + convtype="Conv2D", # type: ignore + spectral_norm=False, # type: ignore plus=False, c2x2=False, ): @@ -453,7 +453,7 @@ def forward(self, x): x1 = self.conv1(x) # type: ignore x2 = self.conv2(torch.cat((x, x1), 1)) # type: ignore if self.conv1x1: - x2 = x2 + self.conv1x1(x) # type: ignore # pylint: disable=not-callable + x2 = x2 + self.conv1x1(x) # type: ignore x3 = self.conv3(torch.cat((x, x1, x2), 1)) # type: ignore x4 = self.conv4(torch.cat((x, x1, x2, x3), 1)) # type: ignore if self.conv1x1: diff --git a/core/inference/functions.py b/core/inference/functions.py index 3243e8473..5c4ff810c 100644 --- a/core/inference/functions.py +++ b/core/inference/functions.py @@ -26,7 +26,6 @@ WEIGHTS_NAME, ) from diffusers.utils.hub_utils import HF_HUB_OFFLINE -from diffusers.utils.import_utils import is_safetensors_available from huggingface_hub import model_info # type: ignore from huggingface_hub._snapshot_download import snapshot_download from huggingface_hub.file_download import hf_hub_download @@ -55,7 +54,7 @@ def is_aitemplate_available(): "Checks whether AITemplate is available." try: - import aitemplate + import aitemplate # noqa: F401 return True except ImportError: @@ -65,7 +64,7 @@ def is_aitemplate_available(): def is_ipex_available(): "Checks whether Intel Pytorch EXtensions are available/installed." try: - import intel_extension_for_pytorch # pylint: disable=unused-import + import intel_extension_for_pytorch # noqa: F401 return True except ImportError: @@ -75,7 +74,7 @@ def is_ipex_available(): def is_onnxconverter_available(): "Checks whether onnxconverter-common is installed. Onnxconverter-common can be installed using `pip install onnxconverter-common`" try: - import onnxconverter_common # pylint: disable=unused-import + import onnxconverter_common # noqa: F401 return True except ImportError: @@ -85,11 +84,8 @@ def is_onnxconverter_available(): def is_onnx_available(): "Checks whether onnx and onnxruntime is installed. Onnx can be installed using `pip install onnx onnxruntime`" try: - import onnx # pylint: disable=unused-import - from onnxruntime.quantization import ( # pylint: disable=unused-import - QuantType, - quantize_dynamic, - ) + import onnx # noqa: F401 + from onnxruntime.quantization import QuantType, quantize_dynamic # noqa: F401 return True except ImportError: @@ -99,7 +95,7 @@ def is_onnx_available(): def is_onnxscript_available(): "Checks whether onnx-script is installed. Onnx-script can be installed with the instructions from https://github.com/microsoft/onnx-script#installing-onnx-script" try: - import onnxscript # pylint: disable=unused-import + import onnxscript # noqa: F401 return True except ImportError: @@ -109,7 +105,7 @@ def is_onnxscript_available(): def is_onnxsim_available(): "Checks whether onnx-simplifier is available. Onnx-simplifier can be installed using `pip install onnxsim`" try: - from onnxsim import simplify # pylint: disable=import-error,unused-import + from onnxsim import simplify # noqa: F401 return True except ImportError: @@ -313,7 +309,7 @@ def download_model( # # make sure we don't download flax weights ignore_patterns = ["*.msgpack"] - if is_safetensors_available() and not local_files_only: + if not local_files_only: info = model_info( repo_id=pretrained_model_name, revision=revision, @@ -390,8 +386,8 @@ def load_pytorch_pipeline( cl.__init__ = partialmethod(cl.__init__, requires_safety_checker=False) # type: ignore try: pipe = download_from_original_stable_diffusion_ckpt( + str(get_full_model_path(model_id_or_path)), pipeline_class=cl, # type: ignore - checkpoint_path=str(get_full_model_path(model_id_or_path)), from_safetensors=use_safetensors, extract_ema=True, load_safety_checker=False, @@ -399,8 +395,8 @@ def load_pytorch_pipeline( ) except KeyError: pipe = download_from_original_stable_diffusion_ckpt( + str(get_full_model_path(model_id_or_path)), pipeline_class=cl, # type: ignore - checkpoint_path=str(get_full_model_path(model_id_or_path)), from_safetensors=use_safetensors, extract_ema=False, load_safety_checker=False, @@ -411,7 +407,6 @@ def load_pytorch_pipeline( pretrained_model_name_or_path=get_full_model_path(model_id_or_path), torch_dtype=config.api.dtype, safety_checker=None, - requires_safety_checker=False, feature_extractor=None, low_cpu_mem_usage=True, ) diff --git a/core/inference/injectables/__init__.py b/core/inference/injectables/__init__.py index da79f0b84..cc8a96c71 100644 --- a/core/inference/injectables/__init__.py +++ b/core/inference/injectables/__init__.py @@ -59,17 +59,46 @@ def _get_target_modules( ): target_modules = [] for name, module in root_module.named_modules(): - if ( - module.__class__.__name__ in target_replace_modules - and not "transformer_blocks" in name - ): # to adapt latest diffusers: + if module.__class__.__name__ in target_replace_modules: for child_name, child_module in module.named_modules(): - is_linear = child_module.__class__.__name__ == "Linear" - is_conv2d = child_module.__class__.__name__ == "Conv2d" - if is_linear or is_conv2d: - lora_name = prefix + "." + name + "." + child_name - lora_name = lora_name.replace(".", "_") - target_modules.append((lora_name, child_module)) + # retarded change, revert pliz diffusers + name = name.replace(".", "_") + name = name.replace("input_blocks", "down_blocks") + name = name.replace("middle_block", "mid_block") + name = name.replace("output_blocks", "out_blocks") + + name = name.replace("to_out_0_lora", "to_out_lora") + name = name.replace("emb_layers", "time_emb_proj") + + name = name.replace("q_proj_lora", "to_q_lora") + name = name.replace("k_proj_lora", "to_k_lora") + name = name.replace("v_proj_lora", "to_v_lora") + name = name.replace("out_proj_lora", "to_out_lora") + + # Prepare for SDXL + if "emb" in name: + import re + + pattern = r"\_\d+(?=\D*$)" + name = re.sub(pattern, "", name, count=1) + if "in_layers_2" in name: + name = name.replace("in_layers_2", "conv1") + if "out_layers_3" in name: + name = name.replace("out_layers_3", "conv2") + if "downsamplers" in name or "upsamplers" in name: + name = name.replace("op", "conv") + if "skip" in name: + name = name.replace("skip_connection", "conv_shortcut") + + if "transformer_blocks" in name: + if "attn1" in name or "attn2" in name: + name = name.replace("attn1", "attn1_processor") + name = name.replace("attn2", "attn2_processor") + elif "mlp" in name: + name = name.replace("_lora_", "_lora_linear_layer_") + lora_name = prefix + "." + name + "." + child_name + lora_name = lora_name.replace(".", "_") + target_modules.append((lora_name, child_module)) return target_modules def _load_state_dict(self, file: Union[Path, str]) -> Dict[str, torch.nn.Module]: @@ -93,7 +122,7 @@ def change_forwards(self): "Redirect lora forward to this hook manager" d = self - def lora_forward(self, input): # pylint: disable=redefined-builtin + def lora_forward(self, input): d.apply_weights(self) return self.old_forward(input) @@ -104,7 +133,18 @@ def lora_forward(self, input): # pylint: disable=redefined-builtin def install_hooks(self, pipe): """Install LoRAHook to the pipe""" assert len(self.modules) == 0 - text_encoder_targets = self._get_target_modules( + text_encoder_targets = [] + if hasattr(pipe, "text_encoder_2"): + text_encoder_targets = ( + text_encoder_targets + + self._get_target_modules( + pipe.text_encoder_2, "lora_te2", ["CLIPAttention", "CLIPMLP"] + ) + + self._get_target_modules( + pipe.text_encoder, "lora_te1", ["CLIPAttention", "CLIPMLP"] + ) + ) + text_encoder_targets = text_encoder_targets + self._get_target_modules( pipe.text_encoder, "lora_te", ["CLIPAttention", "CLIPMLP"] ) targets = [] diff --git a/core/inference/injectables/lycoris.py b/core/inference/injectables/lycoris.py index f1297aa1c..128bb1826 100644 --- a/core/inference/injectables/lycoris.py +++ b/core/inference/injectables/lycoris.py @@ -1,4 +1,3 @@ -# pylint: disable=attribute-defined-outside-init import logging from typing import Any, Dict, Sized @@ -222,9 +221,9 @@ def load( module = torch.nn.Conv2d( v.shape[1], # type: ignore v.shape[0], # type: ignore - sd_module.kernel_size, - sd_module.stride, - sd_module.padding, + sd_module.kernel_size, # type: ignore + sd_module.stride, # type: ignore + sd_module.padding, # type: ignore bias=False, ) else: @@ -235,9 +234,9 @@ def load( module = torch.nn.Conv2d( v.shape[1], # type: ignore v.shape[0], # type: ignore - sd_module.kernel_size, - sd_module.stride, - sd_module.padding, + sd_module.kernel_size, # type: ignore + sd_module.stride, # type: ignore + sd_module.padding, # type: ignore bias=False, ) elif lyco_key == "lora_up.weight" or lyco_key == "dyn_down": @@ -541,7 +540,7 @@ def _rebuild_weight(module, orig_weight: torch.Tensor, dyn_dim: int = None) -> t "You may have other lyco extension that conflict with locon extension." ) - if hasattr(module, "bias") and module.bias != None: + if hasattr(module, "bias") and module.bias is not None: updown = updown.reshape(module.bias.shape) updown += module.bias.to(orig_weight.device, dtype=orig_weight.dtype) updown = updown.reshape(output_shape) diff --git a/core/inference/injectables/utils.py b/core/inference/injectables/utils.py index 2b285b98d..45913af45 100644 --- a/core/inference/injectables/utils.py +++ b/core/inference/injectables/utils.py @@ -8,7 +8,7 @@ class HookObject(ABC): "Module containing information on this subset of injectables." - def __init__( # pylint: disable=dangerous-default-value + def __init__( self, name: str, prompt_key: str, diff --git a/core/inference/onnx/pipeline.py b/core/inference/onnx/pipeline.py index 08f19db1c..ca39e3e18 100644 --- a/core/inference/onnx/pipeline.py +++ b/core/inference/onnx/pipeline.py @@ -1,3 +1,4 @@ +import functools import importlib import inspect import logging @@ -20,9 +21,7 @@ from diffusers.models.unet_2d_condition import UNet2DConditionModel from diffusers.models.vae import DecoderOutput from diffusers.pipelines.onnx_utils import ORT_TO_NP_TYPE -from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion import ( - StableDiffusionPipelineOutput, -) +from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput from diffusers.utils import PIL_INTERPOLATION from numpy.random import MT19937, RandomState, SeedSequence from PIL import Image @@ -43,11 +42,8 @@ is_onnxsim_available, torch_newer_than_201, ) -from core.inference_callbacks import ( - img2img_callback, - inpaint_callback, - txt2img_callback, -) +from core.inference_callbacks import callback +from core.scheduling import KdiffusionSchedulerAdapter from core.types import ( Backend, Img2ImgQueueEntry, @@ -126,6 +122,7 @@ def __init__( autoload: bool = True, ) -> None: if is_onnx_available(): + # pylint: disable=import-error import onnxruntime as ort super().__init__(model_id) @@ -145,6 +142,7 @@ def __init__( def load(self): if is_onnx_available(): + # pylint: disable=import-error import onnxruntime as ort def _load( @@ -446,6 +444,7 @@ def onnx_export( quantize_success = False if signed != "no-quant" else True try: if not quantize_success: + # pylint: disable=import-error from onnxruntime.quantization import QuantType, quantize_dynamic t = time() @@ -1028,52 +1027,81 @@ def _init_latent_model(latents, do_classifier_free_guidance, t): logger.debug("timestep start") rt = time() - timesteps = self.scheduler.timesteps if timesteps is None else timesteps - for i, t in enumerate(tqdm(timesteps)): + + def do_inference(x: torch.Tensor, t: torch.Tensor, call) -> torch.Tensor: if kw is not None: - latent_model_input = kw(latents, do_classifier_free_guidance, t) + latent_model_input = kw(x.numpy(), do_classifier_free_guidance, t) else: latent_model_input = _init_latent_model( - latents, do_classifier_free_guidance, t + x.numpy(), do_classifier_free_guidance, t ) timestep = np.array([t], dtype=timestep_dtype) if class_labels is None: # rookie mistake - noise_pred = self._run_model( - self.unet, + noise_pred = call( sample=latent_model_input, timestep=timestep, - encoder_hidden_states=prompt_embeds, + cond=prompt_embeds, ) else: - noise_pred = self._run_model( - self.unet, + noise_pred = call( sample=latent_model_input, timestep=timestep, - encoder_hidden_states=prompt_embeds, + cond=prompt_embeds, class_labels=class_labels, ) - noise_pred = noise_pred[0] if do_classifier_free_guidance: noise_pred_uncond, noise_pred_text = np.split(noise_pred, 2) noise_pred = noise_pred_uncond + guidance_scale * ( noise_pred_text - noise_pred_uncond ) - scheduler_output = self.scheduler.step( - torch.from_numpy(noise_pred), # type: ignore - t, # type: ignore - torch.from_numpy(latents), # type: ignore - **extra_step_kwargs, - ) - latents = scheduler_output.prev_sample # type: ignore + if not isinstance(self.scheduler, KdiffusionSchedulerAdapter): + scheduler_output = self.scheduler.step( + torch.from_numpy(noise_pred), # type: ignore + t, # type: ignore + torch.from_numpy(x), # type: ignore + **extra_step_kwargs, + ) + latents = scheduler_output.prev_sample # type: ignore + else: + latents = torch.from_numpy(noise_pred) if callback is not None: callback(i, t, latents) - latents = latents.numpy() - logger.debug("timestep end (%.2fs)", time() - rt) return latents + timesteps = self.scheduler.timesteps if timesteps is None else timesteps + + if isinstance(self.scheduler, KdiffusionSchedulerAdapter): + latents = self.scheduler.do_inference( + latents, + functools.partial(self._run_model, self.unet), + do_inference, + torch.Generator(), + callback, + 1, + ).numpy() + else: + + def _call(*args, **kwargs): + if len(args) == 3: + encoder_hidden_states = args[-1] + args = args[:2] + if kwargs.get("cond", None) is not None: + encoder_hidden_states = kwargs.pop("cond") + return self._run_model( + self.unet, + *args, + encoder_hidden_states=encoder_hidden_states, # type: ignore + **kwargs, + )[0] + + for i, t in enumerate(tqdm(timesteps)): + latents = do_inference(latents, t, _call) + logger.debug("timestep end (%.2fs)", time() - rt) + return latents + def _extra_args(self): accepts_eta = "eta" in set( inspect.signature(self.scheduler.step).parameters.keys() @@ -1094,7 +1122,7 @@ def _decode_latents(self, latents, latent_value): ] ) logger.debug("vae_decoder end (%.2fs)", time() - t) - image = np.clip(image / 2 + 0.5, 0, 1) + image = np.clip(image / 2 + 0.5, 0, 1) # type: ignore image = image.transpose((0, 2, 3, 1)) return image @@ -1115,7 +1143,7 @@ def _inpaint( seed: int = -1, generator: RandomState = None, # type: ignore pylint: disable=no-member latents: np.ndarray = None, # type: ignore - ) -> StableDiffusionPipelineOutput: + ): if generator is None: generator = RandomState(MT19937(SeedSequence(seed))) do_classifier_free_guidance = guidance_scale > 1.0 @@ -1168,7 +1196,7 @@ def _inpaint( else masked_image_latents ) - latents = latents * np.float64(self.scheduler.init_noise_sigma) + latents = latents * np.float64(self.scheduler.init_noise_sigma) # type: ignore extra_step_kwargs = self._extra_args() timestep_dtype = self._get_timestep_dtype() @@ -1216,7 +1244,7 @@ def _txt2img( seed: int = -1, generator: RandomState = None, # type: ignore pylint: disable=no-member latents: np.ndarray = None, # type: ignore - ) -> StableDiffusionPipelineOutput: + ): if generator is None: generator = RandomState(MT19937(SeedSequence(seed))) do_classifier_free_guidance = guidance_scale > 1.0 @@ -1229,7 +1257,7 @@ def _txt2img( latents = generator.randn(*latents_shape).astype(latents_dtype) self.scheduler.set_timesteps(num_inference_steps) - latents = latents * np.float64(self.scheduler.init_noise_sigma) + latents = latents * np.float64(self.scheduler.init_noise_sigma) # type: ignore extra_step_kwargs = self._extra_args() timestep_dtype = self._get_timestep_dtype() @@ -1262,7 +1290,7 @@ def _img2img( num_images_per_prompt: int = 1, callback=None, seed: int = -1, - ) -> StableDiffusionPipelineOutput: + ): do_classifier_free_guidance = guidance_scale > 1.0 width, height = ( @@ -1361,7 +1389,7 @@ def generate(self, job: Job) -> List[Image.Image]: job.data.guidance_scale, job.data.negative_prompt, job.data.batch_size, - txt2img_callback, + callback, job.data.seed, ).images ) @@ -1391,7 +1419,7 @@ def generate(self, job: Job) -> List[Image.Image]: job.data.guidance_scale, job.data.negative_prompt, job.data.batch_size, - img2img_callback, + callback, job.data.seed, ).images ) @@ -1421,7 +1449,7 @@ def generate(self, job: Job) -> List[Image.Image]: job.data.guidance_scale, job.data.negative_prompt, job.data.batch_size, - inpaint_callback, + callback, job.data.seed, ).images ) diff --git a/core/inference/pytorch/pipeline.py b/core/inference/pytorch/pipeline.py index e129bfe2d..c938df70e 100644 --- a/core/inference/pytorch/pipeline.py +++ b/core/inference/pytorch/pipeline.py @@ -1,7 +1,7 @@ # HuggingFace example pipeline taken from https://github.com/huggingface/diffusers/blob/main/examples/community/lpw_stable_diffusion.py from contextlib import ExitStack -from typing import Callable, List, Literal, Optional, Union +from typing import Any, Callable, List, Literal, Optional, Union import PIL import torch @@ -12,10 +12,11 @@ from tqdm import tqdm from transformers.models.clip import CLIPTextModel, CLIPTokenizer -from core.config import config from core.inference.utilities import ( + full_vae, get_timesteps, get_weighted_text_embeddings, + numpy_to_pil, pad_tensor, prepare_extra_step_kwargs, prepare_image, @@ -24,13 +25,15 @@ prepare_mask_latents, preprocess_image, ) -from core.optimizations import autocast +from core.inference.utilities.philox import PhiloxGenerator +from core.optimizations import inference_context +from core.scheduling import KdiffusionSchedulerAdapter from .sag import CrossAttnStoreProcessor, pred_epsilon, pred_x0, sag_masking # ------------------------------------------------------------------------------ -logger = logging.get_logger(__name__) # pylint: disable=invalid-name +logger = logging.get_logger(__name__) class StableDiffusionLongPromptWeightingPipeline(StableDiffusionPipeline): @@ -64,12 +67,14 @@ class StableDiffusionLongPromptWeightingPipeline(StableDiffusionPipeline): def __init__( self, - parent, vae: AutoencoderKL, text_encoder: CLIPTextModel, tokenizer: CLIPTokenizer, unet: UNet2DConditionModel, scheduler: SchedulerMixin, + safety_checker: Any = None, + feature_extractor: Any = None, + requires_safety_checker: bool = False, controlnet: Optional[ControlNetModel] = None, ): super().__init__( @@ -84,14 +89,13 @@ def __init__( ) self.__init__additional__() - self.parent = parent + self.parent: Any self.vae: AutoencoderKL self.text_encoder: CLIPTextModel self.tokenizer: CLIPTokenizer self.unet: UNet2DConditionModel self.scheduler: LMSDiscreteScheduler self.controlnet: Optional[ControlNetModel] = controlnet - self.requires_safety_checker: bool def __init__additional__(self): if not hasattr(self, "vae_scale_factor"): @@ -114,21 +118,18 @@ def _execution_device(self): if ( hasattr(module, "_hf_hook") and hasattr( - module._hf_hook, # pylint: disable=protected-access + module._hf_hook, "execution_device", ) - and module._hf_hook.execution_device # pylint: disable=protected-access # type: ignore - is not None + and module._hf_hook.execution_device is not None # type: ignore ): - return torch.device( - module._hf_hook.execution_device # pylint: disable=protected-access # type: ignore - ) + return torch.device(module._hf_hook.execution_device) # type: ignore return self.device def _encode_prompt( self, prompt, - _device, + dtype, num_images_per_prompt, do_classifier_free_guidance, negative_prompt, @@ -191,7 +192,7 @@ def _encode_prompt( ) text_embeddings = torch.cat([uncond_embeddings, text_embeddings]) - return text_embeddings.to(dtype=config.api.dtype) + return text_embeddings.to(dtype=dtype) def _check_inputs(self, prompt, strength, callback_steps): if not isinstance(prompt, str) and not isinstance(prompt, list): @@ -226,7 +227,7 @@ def _decode_latents(self, latents, height, width): def __call__( self, prompt: Union[str, List[str]], - generator: torch.Generator, + generator: Union[PhiloxGenerator, torch.Generator], negative_prompt: Optional[Union[str, List[str]]] = None, image: Union[torch.FloatTensor, PIL.Image.Image] = None, # type: ignore mask_image: Union[torch.FloatTensor, PIL.Image.Image] = None, # type: ignore @@ -322,19 +323,12 @@ def __call__( list of `bool`s denoting whether the corresponding generated image likely represents "not-safe-for-work" (nsfw) content, according to the `safety_checker`. """ - if config.api.torch_compile: - self.unet = torch.compile( - self.unet, - fullgraph=config.api.torch_compile_fullgraph, - dynamic=config.api.torch_compile_dynamic, - mode=config.api.torch_compile_mode, - ) # type: ignore - - # 0. Default height and width to unet - with autocast( - dtype=self.unet.dtype, - disable=not config.api.autocast, - ): + + with inference_context(self.unet, self.vae, height, width) as inf: + # 0. Modify unet and vae to the (optionally) modified versions from inf + self.unet = inf.unet # type: ignore + self.vae = inf.vae # type: ignore + # 1. Check inputs. Raise error if not correct self._check_inputs(prompt, strength, callback_steps) if self.controlnet is not None: @@ -356,7 +350,7 @@ def __call__( # 3. Encode input prompt text_embeddings = self._encode_prompt( prompt, - device, + self.unet.dtype, num_images_per_prompt, do_classifier_free_guidance, negative_prompt, @@ -393,11 +387,11 @@ def __call__( width, dtype, device, - generator, do_classifier_free_guidance, self.vae, self.vae_scale_factor, self.vae.config.scaling_factor, # type: ignore + generator=generator, ) else: mask = None @@ -411,6 +405,9 @@ def __call__( device, image is None or self.controlnet is not None, ) + if isinstance(self.scheduler, KdiffusionSchedulerAdapter): + self.scheduler.timesteps = timesteps + self.scheduler.steps = num_inference_steps latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt) # type: ignore # 6. Prepare latent variables @@ -429,7 +426,7 @@ def __call__( ) # 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline - extra_step_kwargs = prepare_extra_step_kwargs(self.scheduler, generator, eta) # type: ignore + extra_step_kwargs = prepare_extra_step_kwargs(self.scheduler, eta, generator) # type: ignore controlnet_keep = [] if self.controlnet is not None: @@ -453,159 +450,190 @@ def get_map_size(_, __, output): -2: ] # output.sample.shape[-2:] in older diffusers - # 8. Denoising loop - with ExitStack() as gs: - if do_self_attention_guidance: - gs.enter_context(self.unet.mid_block.attentions[0].register_forward_hook(get_map_size)) # type: ignore + def do_denoise( + x: torch.Tensor, + t: torch.IntTensor, + call: Callable, + ): + # expand the latents if we are doing classifier free guidance + latent_model_input = ( + torch.cat([x] * 2) if do_classifier_free_guidance else x # type: ignore + ) + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) # type: ignore + + if num_channels_unet == 9: + latent_model_input = torch.cat([latent_model_input, mask, masked_image_latents], dim=1) # type: ignore + + # predict the noise residual + if self.controlnet is None: + noise_pred = call( # type: ignore + latent_model_input, + t, + cond=text_embeddings, + ) + else: + if guess_mode and do_classifier_free_guidance: + # Infer ControlNet only for the conditional batch. + control_model_input = x + control_model_input = self.scheduler.scale_model_input(control_model_input, t).half() # type: ignore + controlnet_prompt_embeds = text_embeddings.chunk(2)[1] + else: + control_model_input = latent_model_input + controlnet_prompt_embeds = text_embeddings + + cond_scale = controlnet_conditioning_scale * controlnet_keep[i] + + ( + down_block_res_samples, + mid_block_res_sample, + ) = self.controlnet( + control_model_input, + t, + encoder_hidden_states=controlnet_prompt_embeds, + controlnet_cond=image, + conditioning_scale=cond_scale, + guess_mode=guess_mode, + return_dict=False, + ) - for i, t in enumerate(tqdm(timesteps, desc="PyTorch")): - # expand the latents if we are doing classifier free guidance - latent_model_input = ( - torch.cat([latents] * 2) if do_classifier_free_guidance else latents # type: ignore + if guess_mode and do_classifier_free_guidance: + # Infered ControlNet only for the conditional batch. + # To apply the output of ControlNet to both the unconditional and conditional batches, + # add 0 to the unconditional batch to keep it unchanged. + down_block_res_samples = [ + torch.cat([torch.zeros_like(d), d]) + for d in down_block_res_samples + ] + mid_block_res_sample = torch.cat( + [ + torch.zeros_like(mid_block_res_sample), + mid_block_res_sample, + ] + ) + noise_pred = call( # type: ignore + latent_model_input, + t, + cond=text_embeddings, + down_block_additional_residuals=down_block_res_samples, + mid_block_additional_residual=mid_block_res_sample, ) - latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) # type: ignore - if num_channels_unet == 9: - latent_model_input = torch.cat([latent_model_input, mask, masked_image_latents], dim=1) # type: ignore + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * ( + noise_pred_text - noise_pred_uncond + ) - # predict the noise residual - if self.controlnet is None: - noise_pred = self.unet( # type: ignore - latent_model_input, + if do_self_attention_guidance: + if do_classifier_free_guidance: + pred = pred_x0(self, x, noise_pred_uncond, t) # type: ignore + uncond_attn, cond_attn = store_processor.attention_probs.chunk(2) # type: ignore + degraded_latents = sag_masking( + self, pred, uncond_attn, map_size, t, pred_epsilon(self, x, noise_pred_uncond, t) # type: ignore + ) + uncond_emb, _ = text_embeddings.chunk(2) + # predict the noise residual + # this probably could have been done better but honestly fuck this + degraded_prep = call( # type: ignore + degraded_latents.to(dtype=self.unet.dtype), t, - encoder_hidden_states=text_embeddings, - ).sample + cond=uncond_emb, + ) + noise_pred += self_attention_scale * (noise_pred_uncond - degraded_prep) # type: ignore else: - if guess_mode and do_classifier_free_guidance: - # Infer ControlNet only for the conditional batch. - control_model_input = latents - control_model_input = self.scheduler.scale_model_input(control_model_input, t) # type: ignore - controlnet_prompt_embeds = text_embeddings.chunk(2)[1] - else: - control_model_input = latent_model_input - controlnet_prompt_embeds = text_embeddings - - cond_scale = controlnet_conditioning_scale * controlnet_keep[i] - - ( - down_block_res_samples, - mid_block_res_sample, - ) = self.controlnet( - control_model_input, + pred = pred_x0(self, x, noise_pred, t) + cond_attn = store_processor.attention_probs # type: ignore + degraded_latents = sag_masking( + self, + pred, + cond_attn, + map_size, t, - encoder_hidden_states=controlnet_prompt_embeds, - controlnet_cond=image, - conditioning_scale=cond_scale, - guess_mode=guess_mode, - return_dict=False, + pred_epsilon(self, x, noise_pred, t), ) - - if guess_mode and do_classifier_free_guidance: - # Infered ControlNet only for the conditional batch. - # To apply the output of ControlNet to both the unconditional and conditional batches, - # add 0 to the unconditional batch to keep it unchanged. - down_block_res_samples = [ - torch.cat([torch.zeros_like(d), d]) - for d in down_block_res_samples - ] - mid_block_res_sample = torch.cat( - [ - torch.zeros_like(mid_block_res_sample), - mid_block_res_sample, - ] - ) - noise_pred = self.unet( # type: ignore - latent_model_input, + # predict the noise residual + degraded_prep = call( # type: ignore + degraded_latents.to(dtype=self.unet.dtype), t, - encoder_hidden_states=text_embeddings, - down_block_additional_residuals=down_block_res_samples, - mid_block_additional_residual=mid_block_res_sample, - return_dict=False, - )[0] - - # perform guidance - if do_classifier_free_guidance: - noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) - noise_pred = noise_pred_uncond + guidance_scale * ( - noise_pred_text - noise_pred_uncond + cond=text_embeddings, ) + noise_pred += self_attention_scale * (noise_pred - degraded_prep) # type: ignore - if do_self_attention_guidance: - if do_classifier_free_guidance: - pred = pred_x0(self, latents, noise_pred_uncond, t) # type: ignore - uncond_attn, cond_attn = store_processor.attention_probs.chunk(2) # type: ignore - degraded_latents = sag_masking( - self, pred, uncond_attn, map_size, t, pred_epsilon(self, latents, noise_pred_uncond, t) # type: ignore - ) - uncond_emb, _ = text_embeddings.chunk(2) - # predict the noise residual - # this probably could have been done better but honestly fuck this - degraded_prep = self.unet( # type: ignore - degraded_latents, - t, - encoder_hidden_states=uncond_emb, - ).sample - noise_pred += self_attention_scale * (noise_pred_uncond - degraded_prep) # type: ignore - else: - pred = pred_x0(self, latents, noise_pred, t) - cond_attn = store_processor.attention_probs # type: ignore - degraded_latents = sag_masking( - self, - pred, - cond_attn, - map_size, - t, - pred_epsilon(self, latents, noise_pred, t), - ) - # predict the noise residual - degraded_prep = self.unet( # type: ignore - degraded_latents, - t, - encoder_hidden_states=text_embeddings, - ).sample - noise_pred += self_attention_scale * (noise_pred - degraded_prep) # type: ignore - + if not isinstance(self.scheduler, KdiffusionSchedulerAdapter): # compute the previous noisy sample x_t -> x_t-1 - latents = self.scheduler.step( # type: ignore - noise_pred, t.to(noise_pred.device), latents.to(noise_pred.device), **extra_step_kwargs # type: ignore + x = self.scheduler.step( # type: ignore + noise_pred, t.to(noise_pred.device), x.to(noise_pred.device), **extra_step_kwargs # type: ignore ).prev_sample # type: ignore - - if mask is not None and num_channels_unet == 4: - # masking - init_latents_proper = image_latents[:1] # type: ignore - init_mask = mask[:1] - init_mask = pad_tensor( - init_mask, 8, (latents.shape[2], latents.shape[3]) + else: + x = noise_pred + + if mask is not None and num_channels_unet == 4: + # masking + init_latents_proper = image_latents[:1] # type: ignore + init_mask = mask[:1] + init_mask = pad_tensor(init_mask, 8, (x.shape[2], x.shape[3])) + + if i < len(timesteps) - 1: + noise_timestep = timesteps[i + 1] + init_latents_proper = self.scheduler.add_noise( + init_latents_proper, noise, torch.tensor([noise_timestep]) # type: ignore ) - if i < len(timesteps) - 1: - noise_timestep = timesteps[i + 1] - init_latents_proper = self.scheduler.add_noise( - init_latents_proper, noise, torch.tensor([noise_timestep]) # type: ignore - ) + x = (1 - init_mask) * init_latents_proper + init_mask * x # type: ignore + return x - latents = (1 - init_mask) * init_latents_proper + init_mask * latents # type: ignore + # 8. Denoising loop + with ExitStack() as gs: + if do_self_attention_guidance: + gs.enter_context(self.unet.mid_block.attentions[0].register_forward_hook(get_map_size)) # type: ignore - # call the callback, if provided - if i % callback_steps == 0: - if callback is not None: - callback(i, t, latents) # type: ignore - if ( - is_cancelled_callback is not None - and is_cancelled_callback() - ): - return None + if isinstance(self.scheduler, KdiffusionSchedulerAdapter): + latents = self.scheduler.do_inference( + latents, # type: ignore + generator=generator, + call=self.unet, # type: ignore + apply_model=do_denoise, + callback=callback, + callback_steps=callback_steps, + ) + else: + + def _call(*args, **kwargs): + if len(args) == 3: + encoder_hidden_states = args[-1] + args = args[:2] + if kwargs.get("cond", None) is not None: + encoder_hidden_states = kwargs.pop("cond") + return self.unet( + *args, + encoder_hidden_states=encoder_hidden_states, # type: ignore + return_dict=True, + **kwargs, + )[0] + + for i, t in enumerate(tqdm(timesteps, desc="PyTorch")): + latents = do_denoise(latents, t, _call) # type: ignore + + # call the callback, if provided + if i % callback_steps == 0: + if callback is not None: + callback(i, t, latents) # type: ignore + if ( + is_cancelled_callback is not None + and is_cancelled_callback() + ): + return None # 9. Post-processing if output_type == "latent": return latents, False - # TODO: maybe implement asymmetric vqgan? - image = self._decode_latents(latents, height=height, width=width) + image = full_vae(latents, overwrite=lambda sample: self.vae.decode(sample).sample, height=height, width=width) # type: ignore # 11. Convert to PIL if output_type == "pil": - image = self.numpy_to_pil(image) + image = numpy_to_pil(image) if hasattr(self, "final_offload_hook"): self.final_offload_hook.offload() # type: ignore @@ -620,7 +648,7 @@ def get_map_size(_, __, output): def text2img( self, prompt: Union[str, List[str]], - generator: torch.Generator, + generator: Union[PhiloxGenerator, torch.Generator], negative_prompt: Optional[Union[str, List[str]]] = None, height: int = 512, width: int = 512, @@ -696,6 +724,7 @@ def text2img( """ return self( prompt=prompt, + generator=generator, negative_prompt=negative_prompt, height=height, width=width, @@ -704,7 +733,6 @@ def text2img( self_attention_scale=self_attention_scale, num_images_per_prompt=num_images_per_prompt, eta=eta, - generator=generator, latents=latents, max_embeddings_multiples=max_embeddings_multiples, output_type=output_type, @@ -718,7 +746,7 @@ def img2img( self, image: Union[torch.FloatTensor, PIL.Image.Image], # type: ignore prompt: Union[str, List[str]], - generator: torch.Generator, + generator: Union[PhiloxGenerator, torch.Generator], height: int = 512, width: int = 512, negative_prompt: Optional[Union[str, List[str]]] = None, @@ -793,8 +821,9 @@ def img2img( list of `bool`s denoting whether the corresponding generated image likely represents "not-safe-for-work" (nsfw) content, according to the `safety_checker`. """ - return self( + return self.__call__( prompt=prompt, + generator=generator, negative_prompt=negative_prompt, image=image, height=height, @@ -805,7 +834,6 @@ def img2img( strength=strength, num_images_per_prompt=num_images_per_prompt, eta=eta, # type: ignore - generator=generator, max_embeddings_multiples=max_embeddings_multiples, output_type=output_type, return_dict=return_dict, @@ -819,7 +847,7 @@ def inpaint( image: Union[torch.FloatTensor, PIL.Image.Image], # type: ignore mask_image: Union[torch.FloatTensor, PIL.Image.Image], # type: ignore prompt: Union[str, List[str]], - generator: torch.Generator, + generator: Union[PhiloxGenerator, torch.Generator], negative_prompt: Optional[Union[str, List[str]]] = None, strength: float = 0.8, num_inference_steps: Optional[int] = 50, @@ -902,8 +930,9 @@ def inpaint( list of `bool`s denoting whether the corresponding generated image likely represents "not-safe-for-work" (nsfw) content, according to the `safety_checker`. """ - return self( + return self.__call__( prompt=prompt, + generator=generator, negative_prompt=negative_prompt, image=image, mask_image=mask_image, @@ -913,7 +942,6 @@ def inpaint( strength=strength, num_images_per_prompt=num_images_per_prompt, eta=eta, # type: ignore - generator=generator, max_embeddings_multiples=max_embeddings_multiples, output_type=output_type, return_dict=return_dict, diff --git a/core/inference/pytorch/pytorch.py b/core/inference/pytorch/pytorch.py index 84afb52da..9e8add605 100755 --- a/core/inference/pytorch/pytorch.py +++ b/core/inference/pytorch/pytorch.py @@ -1,11 +1,14 @@ +import importlib import logging from pathlib import Path from typing import Any, List, Optional, Tuple, Union +import requests import torch from diffusers import ( AutoencoderKL, ControlNetModel, + ModelMixin, StableDiffusionPipeline, UNet2DConditionModel, ) @@ -17,6 +20,7 @@ from api import websocket_manager from api.websockets import Data from api.websockets.notification import Notification +from core import shared from core.config import config from core.flags import HighResFixFlag from core.inference.base_model import InferenceModel @@ -24,21 +28,18 @@ from core.inference.pytorch.pipeline import StableDiffusionLongPromptWeightingPipeline from core.inference.utilities import ( change_scheduler, + create_generator, image_to_controlnet_input, scale_latents, ) -from core.inference_callbacks import ( - controlnet_callback, - img2img_callback, - inpaint_callback, - txt2img_callback, -) +from core.inference_callbacks import callback from core.types import ( Backend, ControlNetQueueEntry, Img2ImgQueueEntry, InpaintQueueEntry, Job, + SigmaScheduler, Txt2ImgQueueEntry, ) from core.utils import convert_images_to_base64_grid, convert_to_image, resize @@ -103,12 +104,18 @@ def load(self): self.requires_safety_checker = False # type: ignore self.safety_checker = pipe.safety_checker # type: ignore + if config.api.autoloaded_vae.get(self.model_id): + try: + self.change_vae(config.api.autoloaded_vae[self.model_id]) + except FileNotFoundError as e: + logger.error(f"Failed to load autoloaded VAE: {e}") + if not self.bare: # Autoload textual inversions for textural_inversion in config.api.autoloaded_textual_inversions: try: self.load_textual_inversion(textural_inversion) - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.warning( f"Failed to load textual inversion {textural_inversion}: {e}" ) @@ -127,6 +134,8 @@ def load(self): def change_vae(self, vae: str) -> None: "Change the vae to the one specified" + logger.info(f"Changing VAE to {vae}") + if self.vae_path == "default": setattr(self, "original_vae", self.vae) @@ -134,14 +143,38 @@ def change_vae(self, vae: str) -> None: if vae == "default": self.vae = old_vae else: - # Why the fuck do you think that's constant pylint? - # Are you mentally insane? - if Path(vae).is_dir(): - self.vae = AutoencoderKL.from_pretrained(vae) # type: ignore - else: - self.vae = convert_vaept_to_diffusers(vae).to( + if len(vae.split("/")) == 2: + cont = requests.get( + f"https://huggingface.co/{vae}/raw/main/config.json" + ).json()["_class_name"] + cont = getattr(importlib.import_module("diffusers"), cont) + self.vae = cont.from_pretrained(vae).to( device=old_vae.device, dtype=old_vae.dtype ) + if not hasattr(self.vae.config, "block_out_channels"): + setattr( + self.vae.config, + "block_out_channels", + ( + 1, + 1, + 1, + 1, + ), + ) + else: + if Path(vae).exists(): + if Path(vae).is_dir(): + self.vae = ModelMixin.from_pretrained(vae) # type: ignore + else: + self.vae = convert_vaept_to_diffusers(vae).to( + device=old_vae.device, dtype=old_vae.dtype + ) + else: + raise FileNotFoundError(f"{vae} is not a valid path") + + logger.info(f"Successfully changed vae to {vae} of type {type(self.vae)}") + # This is at the end 'cause I've read horror stories about pythons prefetch system self.vae_path = vae @@ -223,23 +256,22 @@ def manage_optional_components( cn.to(self.device) self.controlnet = cn self.current_controlnet = target_controlnet + + # Clean memory + self.memory_cleanup() else: logger.debug("No change in controlnet mode") - # Clean memory - self.memory_cleanup() - def create_pipe( self, controlnet: Optional[str] = "", - seed: Optional[int] = -1, - scheduler: Optional[Tuple[Any, bool]] = None, - ) -> Tuple[StableDiffusionLongPromptWeightingPipeline, torch.Generator]: + scheduler: Optional[Tuple[Any, SigmaScheduler]] = None, + sampler_settings: Optional[dict] = None, + ) -> StableDiffusionLongPromptWeightingPipeline: "Create a pipeline -- useful for reducing backend clutter." self.manage_optional_components(target_controlnet=controlnet or "") pipe = StableDiffusionLongPromptWeightingPipeline( - parent=self, vae=self.vae, unet=self.unet, text_encoder=self.text_encoder, @@ -247,30 +279,30 @@ def create_pipe( scheduler=self.scheduler, controlnet=self.controlnet, ) - - if config.api.device_type == "directml": - generator = torch.Generator().manual_seed(seed) # type: ignore - else: - generator = torch.Generator(config.api.device).manual_seed(seed) # type: ignore + pipe.parent = self if scheduler: change_scheduler( model=pipe, scheduler=scheduler[0], # type: ignore - use_karras_sigmas=scheduler[1], + sigma_type=scheduler[1], + sampler_settings=sampler_settings, ) - return pipe, generator + return pipe def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: "Generate an image from a prompt" - pipe, generator = self.create_pipe( - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + pipe = self.create_pipe( + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(job.data.seed) + total_images: List[Image.Image] = [] + shared.current_method = "txt2img" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): output_type = "pil" @@ -279,6 +311,7 @@ def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: output_type = "latent" data = pipe.text2img( + generator=generator, prompt=job.data.prompt, height=job.data.height, width=job.data.width, @@ -287,8 +320,7 @@ def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: self_attention_scale=job.data.self_attention_scale, negative_prompt=job.data.negative_prompt, output_type=output_type, - generator=generator, - callback=txt2img_callback, + callback=callback, num_images_per_prompt=job.data.batch_size, ) @@ -305,9 +337,8 @@ def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: latent_scale_mode=flag.latent_scale_mode, ) - self.memory_cleanup() - data = pipe.img2img( + generator=generator, prompt=job.data.prompt, image=latents, height=latents.shape[2] * 8, @@ -317,8 +348,7 @@ def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: self_attention_scale=job.data.self_attention_scale, negative_prompt=job.data.negative_prompt, output_type="pil", - generator=generator, - callback=txt2img_callback, + callback=callback, strength=flag.strength, return_dict=False, num_images_per_prompt=job.data.batch_size, @@ -336,7 +366,9 @@ def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: "current_step": 0, "total_steps": 0, "image": convert_images_to_base64_grid( - total_images, quality=90, image_format="webp" + total_images, + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) @@ -347,19 +379,23 @@ def txt2img(self, job: Txt2ImgQueueEntry) -> List[Image.Image]: def img2img(self, job: Img2ImgQueueEntry) -> List[Image.Image]: "Generate an image from an image" - pipe, generator = self.create_pipe( - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + pipe = self.create_pipe( + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(job.data.seed) + # Preprocess the image input_image = convert_to_image(job.data.image) input_image = resize(input_image, job.data.width, job.data.height) total_images: List[Image.Image] = [] + shared.current_method = "img2img" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): data = pipe.img2img( + generator=generator, prompt=job.data.prompt, image=input_image, height=job.data.height, # technically isn't needed, but it's here for consistency sake @@ -369,8 +405,7 @@ def img2img(self, job: Img2ImgQueueEntry) -> List[Image.Image]: self_attention_scale=job.data.self_attention_scale, negative_prompt=job.data.negative_prompt, output_type="pil", - generator=generator, - callback=img2img_callback, + callback=callback, strength=job.data.strength, return_dict=False, num_images_per_prompt=job.data.batch_size, @@ -392,7 +427,9 @@ def img2img(self, job: Img2ImgQueueEntry) -> List[Image.Image]: "current_step": 0, "total_steps": 0, "image": convert_images_to_base64_grid( - total_images, quality=90, image_format="webp" + total_images, + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) @@ -403,11 +440,13 @@ def img2img(self, job: Img2ImgQueueEntry) -> List[Image.Image]: def inpaint(self, job: InpaintQueueEntry) -> List[Image.Image]: "Generate an image from an image" - pipe, generator = self.create_pipe( - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + pipe = self.create_pipe( + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(job.data.seed) + # Preprocess images input_image = convert_to_image(job.data.image).convert("RGB") input_image = resize(input_image, job.data.width, job.data.height) @@ -417,9 +456,11 @@ def inpaint(self, job: InpaintQueueEntry) -> List[Image.Image]: input_mask_image = resize(input_mask_image, job.data.width, job.data.height) total_images: List[Image.Image] = [] + shared.current_method = "inpainting" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): data = pipe.inpaint( + generator=generator, prompt=job.data.prompt, image=input_image, mask_image=input_mask_image, @@ -428,8 +469,7 @@ def inpaint(self, job: InpaintQueueEntry) -> List[Image.Image]: self_attention_scale=job.data.self_attention_scale, negative_prompt=job.data.negative_prompt, output_type="pil", - generator=generator, - callback=inpaint_callback, + callback=callback, return_dict=False, num_images_per_prompt=job.data.batch_size, width=job.data.width, @@ -452,7 +492,9 @@ def inpaint(self, job: InpaintQueueEntry) -> List[Image.Image]: "current_step": 0, "total_steps": 0, "image": convert_images_to_base64_grid( - total_images, quality=90, image_format="webp" + total_images, + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) @@ -469,12 +511,14 @@ def controlnet2img(self, job: ControlNetQueueEntry) -> List[Image.Image]: ) logger.debug(f"Requested ControlNet: {job.data.controlnet}") - pipe, generator = self.create_pipe( + pipe = self.create_pipe( controlnet=job.data.controlnet, - seed=job.data.seed, - scheduler=(job.data.scheduler, job.data.use_karras_sigmas), + scheduler=(job.data.scheduler, job.data.sigmas), + sampler_settings=job.data.sampler_settings, ) + generator = create_generator(job.data.seed) + # Preprocess the image input_image = convert_to_image(job.data.image) input_image = resize(input_image, job.data.width, job.data.height) @@ -485,17 +529,18 @@ def controlnet2img(self, job: ControlNetQueueEntry) -> List[Image.Image]: logger.debug(f"Preprocessed image size: {input_image.size}") total_images: List[Image.Image] = [input_image] + shared.current_method = "controlnet" for _ in tqdm(range(job.data.batch_count), desc="Queue", position=1): data = pipe( + generator=generator, prompt=job.data.prompt, negative_prompt=job.data.negative_prompt, image=input_image, num_inference_steps=job.data.steps, guidance_scale=job.data.guidance_scale, output_type="pil", - generator=generator, - callback=controlnet_callback, + callback=callback, return_dict=False, num_images_per_prompt=job.data.batch_size, controlnet_conditioning_scale=job.data.controlnet_conditioning_scale, @@ -519,8 +564,8 @@ def controlnet2img(self, job: ControlNetQueueEntry) -> List[Image.Image]: total_images if job.data.return_preprocessed else total_images[1:], - quality=90, - image_format="webp", + quality=config.api.image_quality, + image_format=config.api.image_extension, ), }, ) @@ -532,7 +577,6 @@ def generate(self, job: Job): "Generate images from the queue" logging.info(f"Adding job {job.data.id} to queue") - self.memory_cleanup() try: if isinstance(job, Txt2ImgQueueEntry): @@ -549,23 +593,21 @@ def generate(self, job: Job): self.memory_cleanup() raise e if len(self.unload_loras) != 0: - for l in self.unload_loras: + for lora in self.unload_loras: try: - self.lora_injector.remove_lora(l) # type: ignore - logger.debug(f"Unloading LoRA: {l}") + self.lora_injector.remove_lora(lora) # type: ignore + logger.debug(f"Unloading LoRA: {lora}") except KeyError: pass self.unload_loras.clear() if len(self.unload_lycoris) != 0: # type: ignore - for l in self.unload_lycoris: # type: ignore + for lora in self.unload_lycoris: # type: ignore try: - self.lora_injector.remove_lycoris(l) # type: ignore - logger.debug(f"Unloading LyCORIS: {l}") + self.lora_injector.remove_lycoris(lora) # type: ignore + logger.debug(f"Unloading LyCORIS: {lora}") except KeyError: pass - # Clean memory and return images - self.memory_cleanup() return images def save(self, path: str = "converted", safetensors: bool = False): @@ -591,14 +633,13 @@ def load_textual_inversion(self, textual_inversion: str): f"Loading textual inversion model {textual_inversion} onto {self.model_id}..." ) - if any(textual_inversion in l for l in self.textual_inversions): + if any(textual_inversion in lora for lora in self.textual_inversions): logger.info( f"Textual inversion model {textual_inversion} already loaded onto {self.model_id}" ) return pipe = StableDiffusionLongPromptWeightingPipeline( - parent=self, vae=self.vae, unet=self.unet, text_encoder=self.text_encoder, @@ -606,6 +647,7 @@ def load_textual_inversion(self, textual_inversion: str): scheduler=self.scheduler, controlnet=self.controlnet, ) + pipe.parent = self token = Path(textual_inversion).stem logger.info(f"Loading token {token} for textual inversion model") diff --git a/core/inference/pytorch/sag/sag_utils.py b/core/inference/pytorch/sag/sag_utils.py index 23136f291..2213940b4 100644 --- a/core/inference/pytorch/sag/sag_utils.py +++ b/core/inference/pytorch/sag/sag_utils.py @@ -12,13 +12,13 @@ def pred_x0(pipe, sample, model_output, timestep): ] beta_prod_t = 1 - alpha_prod_t - if pipe.scheduler.config.prediction_type == "epsilon": + if pipe.scheduler.config["prediction_type"] == "epsilon": pred_original_sample = ( sample - beta_prod_t ** (0.5) * model_output ) / alpha_prod_t ** (0.5) - elif pipe.scheduler.config.prediction_type == "sample": + elif pipe.scheduler.config["prediction_type"] == "sample": pred_original_sample = model_output - elif pipe.scheduler.config.prediction_type == "v_prediction": + elif pipe.scheduler.config["prediction_type"] == "v_prediction": pred_original_sample = (alpha_prod_t**0.5) * sample - ( beta_prod_t**0.5 ) * model_output @@ -28,7 +28,7 @@ def pred_x0(pipe, sample, model_output, timestep): ) * sample else: raise ValueError( - f"prediction_type given as {pipe.scheduler.config.prediction_type} must be one of `epsilon`, `sample`," + f"prediction_type given as {pipe.scheduler.config['prediction_type']} must be one of `epsilon`, `sample`," " or `v_prediction`" ) @@ -74,17 +74,17 @@ def pred_epsilon(pipe, sample, model_output, timestep): ] beta_prod_t = 1 - alpha_prod_t - if pipe.scheduler.config.prediction_type == "epsilon": + if pipe.scheduler.config["prediction_type"] == "epsilon": pred_eps = model_output - elif pipe.scheduler.config.prediction_type == "sample": + elif pipe.scheduler.config["prediction_type"] == "sample": pred_eps = (sample - (alpha_prod_t**0.5) * model_output) / ( beta_prod_t**0.5 ) - elif pipe.scheduler.config.prediction_type == "v_prediction": + elif pipe.scheduler.config["prediction_type"] == "v_prediction": pred_eps = (beta_prod_t**0.5) * sample + (alpha_prod_t**0.5) * model_output else: raise ValueError( - f"prediction_type given as {pipe.scheduler.config.prediction_type} must be one of `epsilon`, `sample`," + f"prediction_type given as {pipe.scheduler.config['prediction_type']} must be one of `epsilon`, `sample`," " or `v_prediction`" ) diff --git a/core/inference/utilities/__init__.py b/core/inference/utilities/__init__.py index 74a3f989c..5be192d1c 100644 --- a/core/inference/utilities/__init__.py +++ b/core/inference/utilities/__init__.py @@ -12,3 +12,5 @@ ) from .lwp import get_weighted_text_embeddings from .scheduling import change_scheduler, get_timesteps, prepare_extra_step_kwargs +from .random import create_generator, randn, randn_like +from .vae import taesd, full_vae, cheap_approximation, numpy_to_pil, decode_latents diff --git a/core/inference/utilities/controlnet.py b/core/inference/utilities/controlnet.py index 5245c8220..b282be5cb 100644 --- a/core/inference/utilities/controlnet.py +++ b/core/inference/utilities/controlnet.py @@ -4,6 +4,16 @@ import numpy as np import torch +from controlnet_aux import ( + CannyDetector, + HEDdetector, + LineartAnimeDetector, + LineartDetector, + MidasDetector, + MLSDdetector, + NormalBaeDetector, + OpenposeDetector, +) from PIL import Image from transformers.models.auto.image_processing_auto import AutoImageProcessor from transformers.models.upernet import UperNetForSemanticSegmentation @@ -13,22 +23,6 @@ logger = logging.getLogger(__name__) -try: - from controlnet_aux import ( - CannyDetector, - HEDdetector, - LineartAnimeDetector, - LineartDetector, - MidasDetector, - MLSDdetector, - NormalBaeDetector, - OpenposeDetector, - ) -except ImportError: - logger.warning( - "You have old version of controlnet-aux, please run `pip uninstall controlnet-aux && pip install controlnet-aux` to update it to the lates version." - ) - def _wipe_old(): "Wipes old controlnet preprocessor from memory" @@ -38,7 +32,7 @@ def _wipe_old(): logger.debug("Did not find this controlnet preprocessor cached, wiping old ones") shared_dependent.cached_controlnet_preprocessor = None - if config.api.device_type == "cuda": + if "cuda" in config.api.device: torch.cuda.empty_cache() torch.cuda.ipc_collect() gc.collect() @@ -231,7 +225,7 @@ def _segmentation(input_image: Image.Image) -> Image.Image: from core import shared_dependent if isinstance(shared_dependent.cached_controlnet_preprocessor, Tuple): - ( # pylint: disable=unpacking-non-sequence + ( image_processor, image_segmentor, ) = shared_dependent.cached_controlnet_preprocessor diff --git a/core/inference/utilities/latents.py b/core/inference/utilities/latents.py index 0abcfeb17..d28381aa1 100644 --- a/core/inference/utilities/latents.py +++ b/core/inference/utilities/latents.py @@ -1,21 +1,39 @@ import logging import math from time import time -from typing import Optional, Tuple, Union +from typing import List, Optional, Tuple, Union import numpy as np import torch import torch.nn.functional as F from diffusers import StableDiffusionPipeline +from diffusers.models import vae as diffusers_vae from diffusers.utils import PIL_INTERPOLATION from PIL import Image from core.config import config from core.flags import LatentScaleModel +from core.inference.utilities.philox import PhiloxGenerator + +from .random import randn logger = logging.getLogger(__name__) +def _randn_tensor( + shape: Union[Tuple, List], + generator: Union[PhiloxGenerator, torch.Generator], + device: Optional["torch.device"] = None, + dtype: Optional["torch.dtype"] = None, + layout: Optional["torch.layout"] = None, +): + return randn(shape, generator, device, dtype) + + +diffusers_vae.randn_tensor = _randn_tensor +logger.debug("Overwritten diffusers randn_tensor") + + def pad_tensor( tensor: torch.Tensor, multiple: int, size: Optional[Tuple[int, int]] = None ) -> torch.Tensor: @@ -113,11 +131,11 @@ def prepare_mask_latents( width: int, dtype: torch.dtype, device: torch.device, - generator: torch.Generator, do_classifier_free_guidance: bool, vae, vae_scale_factor: float, vae_scaling_factor: float, + generator: Union[PhiloxGenerator, torch.Generator], ) -> Tuple[torch.Tensor, torch.Tensor]: """This function resizes and converts the input mask to a PyTorch tensor, encodes the input masked image to its latent representation, @@ -196,7 +214,7 @@ def prepare_image( return image -def preprocess_mask(mask): # pylint: disable=unused-argument +def preprocess_mask(mask): mask = mask.convert("L") # w, h = mask.size # w, h = map(lambda x: x - x % 32, (w, h)) # resize to integer multiple of 32 @@ -221,7 +239,7 @@ def prepare_latents( width: Optional[int], dtype: torch.dtype, device: torch.device, - generator: Optional[torch.Generator], + generator: Union[PhiloxGenerator, torch.Generator], latents=None, latent_channels: Optional[int] = None, align_to: int = 1, @@ -235,15 +253,8 @@ def prepare_latents( ) if latents is None: - if device.type == "mps" or config.api.device_type == "directml": - # randn does not work reproducibly on mps - latents = torch.randn( - shape, generator=generator, device="cpu", dtype=dtype # type: ignore - ).to(device) - else: - latents = torch.randn( - shape, generator=generator, device=generator.device, dtype=dtype # type: ignore - ) + # randn does not work reproducibly on mps + latents = randn(shape, generator, device=device, dtype=dtype) # type: ignore else: if latents.shape != shape: raise ValueError( @@ -265,6 +276,9 @@ def prepare_latents( logger.debug("Skipping VAE encode, already have latents") init_latents = image + # if isinstance(pipe.scheduler, KdiffusionSchedulerAdapter): + # init_latents = init_latents * pipe.scheduler.init_noise_sigma + init_latents_orig = init_latents shape = init_latents.shape if latent_channels is not None: @@ -276,25 +290,7 @@ def prepare_latents( ) # add noise to latents using the timesteps - if device.type == "mps" or config.api.device_type == "directml": - noise = torch.randn( - shape, generator=generator, device="cpu", dtype=dtype - ).to(device) - else: - # Retarded fix, but hey, if it works, it works - if hasattr(pipe.vae, "main_device"): - noise = torch.randn( - shape, - generator=torch.Generator("cpu").manual_seed(1), - device="cpu", - dtype=dtype, - ).to(device) - else: - noise = torch.randn( - shape, generator=generator, device=device, dtype=dtype - ) - # Now this... I may have called the previous "hack" retarded, but this... - # This just takes it to a whole new level + noise = randn(shape, generator, device=device, dtype=dtype) latents = pipe.scheduler.add_noise(init_latents.to(device), noise.to(device), timestep.to(device)) # type: ignore return latents, init_latents_orig, noise diff --git a/core/inference/utilities/lwp.py b/core/inference/utilities/lwp.py index 43515d4db..d16a598ea 100644 --- a/core/inference/utilities/lwp.py +++ b/core/inference/utilities/lwp.py @@ -6,6 +6,8 @@ import torch from diffusers import StableDiffusionPipeline +from core.utils import download_file + from ...config import config from ...files import get_full_model_path @@ -31,7 +33,7 @@ ) special_parser = re.compile( - r"\<(lora|ti):([^\:\(\)\<\>\[\]]+)(?::[\s]*([+-]?(?:[0-9]*[.])?[0-9]+))?\>" + r"\<(lora|ti):([^\:\(\)\<\>\[\]]+)(?::[\s]*([+-]?(?:[0-9]*[.])?[0-9]+))?\>|\<(lora|ti):(http[^\(\)\<\>\[\]]+\/[^:]+)(?::[\s]*([+-]?(?:[0-9]*[.])?[0-9]+))?\>" ) @@ -50,11 +52,23 @@ def parse_prompt_special( load_map = {} def replace(match): - type_: str = match.group(1) # type: ignore + type_: str = match.group(4) or match.group(1) name = match.group(2) - strength = match.group(3) + strength = match.group(6) or match.group(3) + url: str = match.group(5) + + if url: + filename = url.split("/")[-1] + file = Path("data/lora") / filename + name = file.stem + + # Check if file exists + if not file.exists(): + name = download_file(url, Path("data/lora"), add_filename=True).stem + else: + logger.debug(f"File {file} already cached") - load_map[type_] = load_map.get(type_, list()) + load_map[type_] = load_map.get(type_, []) if type_ == "ti": load_map[type_].append(name) return f"({name}:{strength if strength else '1.0'})" @@ -203,7 +217,7 @@ def pad_tokens_and_weights( weights_length = ( max_length if no_boseos_middle else max_embeddings_multiples * chunk_length ) - for i in range(len(tokens)): # pylint: disable=consider-using-enumerate + for i in range(len(tokens)): tokens[i] = [bos] + tokens[i] + [eos] * (max_length - 1 - len(tokens[i])) if no_boseos_middle: weights[i] = [1.0] + weights[i] + [1.0] * (max_length - 1 - len(weights[i])) @@ -237,6 +251,9 @@ def get_unweighted_text_embeddings( When the length of tokens is a multiple of the capacity of the text encoder, it should be split into chunks and sent to the text encoder individually. """ + + # TODO: when SDXL releases, refactor CLIP_stop_at_last_layer here. + max_embeddings_multiples = (text_input.shape[1] - 2) // (chunk_length - 2) if max_embeddings_multiples > 1: @@ -366,8 +383,7 @@ def get_weighted_text_embeddings( old_l = loralist loralist = list( filter( - lambda x: Path(x[0]).stem.casefold() - in prompt_.casefold(), # pylint: disable=cell-var-from-loop + lambda x: Path(x[0]).stem.casefold() in prompt_.casefold(), loralist, ) ) @@ -435,7 +451,7 @@ def get_weighted_text_embeddings( # round up the longest length of tokens to a multiple of (model_max_length - 2) max_length = max([len(token) for token in prompt_tokens]) if uncond_prompt is not None: - max_length = max(max_length, max([len(token) for token in uncond_tokens])) # type: ignore # pylint: disable=nested-min-max + max_length = max(max_length, max([len(token) for token in uncond_tokens])) # type: ignore max_embeddings_multiples = min( max_embeddings_multiples, # type: ignore diff --git a/core/inference/utilities/philox.py b/core/inference/utilities/philox.py new file mode 100644 index 000000000..c27dc2bef --- /dev/null +++ b/core/inference/utilities/philox.py @@ -0,0 +1,91 @@ +# Shamelessly stolen from A1111 +# https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/rng_philox.py + + +import numpy as np + +philox_m = [0xD2511F53, 0xCD9E8D57] +philox_w = [0x9E3779B9, 0xBB67AE85] + +two_pow32_inv = np.array([2.3283064e-10], dtype=np.float32) +two_pow32_inv_2pi = np.array([2.3283064e-10 * 6.2831855], dtype=np.float32) + + +def uint32(x): + """Converts (N,) np.uint64 array into (2, N) np.unit32 array.""" + return x.view(np.uint32).reshape(-1, 2).transpose(1, 0) + + +def philox4_round(counter, key): + """A single round of the Philox 4x32 random number generator.""" + + v1 = uint32(counter[0].astype(np.uint64) * philox_m[0]) + v2 = uint32(counter[2].astype(np.uint64) * philox_m[1]) + + counter[0] = v2[1] ^ counter[1] ^ key[0] + counter[1] = v2[0] + counter[2] = v1[1] ^ counter[3] ^ key[1] + counter[3] = v1[0] + + +def philox4_32(counter, key, rounds=10): + """Generates 32-bit random numbers using the Philox 4x32 random number generator. + + Parameters: + counter (numpy.ndarray): A 4xN array of 32-bit integers representing the counter values (offset into generation). + key (numpy.ndarray): A 2xN array of 32-bit integers representing the key values (seed). + rounds (int): The number of rounds to perform. + + Returns: + numpy.ndarray: A 4xN array of 32-bit integers containing the generated random numbers. + """ + + for _ in range(rounds - 1): + philox4_round(counter, key) + + key[0] = key[0] + philox_w[0] + key[1] = key[1] + philox_w[1] + + philox4_round(counter, key) + return counter + + +def box_muller(x, y): + """Returns just the first out of two numbers generated by Box–Muller transform algorithm.""" + u = x * two_pow32_inv + two_pow32_inv / 2 + v = y * two_pow32_inv_2pi + two_pow32_inv_2pi / 2 + + s = np.sqrt(-2.0 * np.log(u)) + + r1 = s * np.sin(v) + return r1.astype(np.float32) + + +class PhiloxGenerator: + """RNG that produces same outputs as torch.randn(..., device='cuda') on CPU""" + + def __init__(self, seed): + self.seed = seed + self.offset = 0 + + def randn(self, shape): + """Generate a sequence of n standard normal random variables using the Philox 4x32 random number generator and the Box-Muller transform.""" + + n = 1 + for x in shape: + n *= x + + counter = np.zeros((4, n), dtype=np.uint32) + counter[0] = self.offset + counter[2] = np.arange( + n, dtype=np.uint32 + ) # up to 2^32 numbers can be generated - if you want more you'd need to spill into counter[3] + self.offset += 1 + + key = np.empty(n, dtype=np.uint64) + key.fill(self.seed) + key = uint32(key) + + g = philox4_32(counter, key) + + return box_muller(g[0], g[1]).reshape(shape) # discard g[2] and g[3] diff --git a/core/inference/utilities/random.py b/core/inference/utilities/random.py new file mode 100644 index 000000000..0eaedd631 --- /dev/null +++ b/core/inference/utilities/random.py @@ -0,0 +1,46 @@ +from typing import Optional, Union + +import torch +from torch import Generator as native + +from core.config import config + +from .philox import PhiloxGenerator + + +def create_generator(seed: int) -> Union[PhiloxGenerator, torch.Generator]: + generator = config.api.generator + if generator == "device" and config.api.overwrite_generator: + generator = "cpu" + if generator == "philox": + return PhiloxGenerator(seed) + + return native(config.api.device if generator == "device" else "cpu").manual_seed( + seed + ) + + +def randn( + shape, + generator: Union[PhiloxGenerator, torch.Generator], + device: Optional[torch.device] = None, + dtype: Optional[torch.dtype] = None, +) -> torch.Tensor: + if isinstance(generator, PhiloxGenerator): + return torch.asarray(generator.randn(shape), device=device, dtype=dtype) + + return torch.randn( + shape, + generator=generator, + dtype=dtype, + device="cpu" if config.api.overwrite_generator else generator.device, + ).to(device) + + +def randn_like( + x: torch.Tensor, + generator: Union[PhiloxGenerator, torch.Generator], + device: Optional[torch.device] = None, + dtype: Optional[torch.dtype] = None, +) -> torch.Tensor: + return randn(x.shape, generator, device, dtype) diff --git a/core/inference/utilities/scheduling.py b/core/inference/utilities/scheduling.py index 8031b1417..1c8fc2a71 100644 --- a/core/inference/utilities/scheduling.py +++ b/core/inference/utilities/scheduling.py @@ -1,13 +1,18 @@ import importlib import inspect import logging -from typing import Dict, Optional +from typing import Dict, Optional, Union import torch -from diffusers import SchedulerMixin +from diffusers import DDIMScheduler, SchedulerMixin from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers -from core.types import PyTorchModelType +from core import shared +from core.config import config +from core.inference.utilities.philox import PhiloxGenerator +from core.scheduling import KdiffusionSchedulerAdapter, create_sampler +from core.types import PyTorchModelType, SigmaScheduler +from core.utils import unwrap_enum logger = logging.getLogger(__name__) @@ -21,6 +26,7 @@ def get_timesteps( ): "Get the amount of timesteps for the provided options" if is_text2img: + shared.current_steps = num_inference_steps return scheduler.timesteps.to(device), num_inference_steps # type: ignore else: # get the original timestep using init_timestep @@ -30,17 +36,25 @@ def get_timesteps( t_start = max(num_inference_steps - init_timestep + offset, 0) timesteps = scheduler.timesteps[t_start:].to(device) # type: ignore + + shared.current_steps = num_inference_steps + return timesteps, num_inference_steps - t_start def prepare_extra_step_kwargs( - scheduler: SchedulerMixin, generator: torch.Generator, eta: float + scheduler: SchedulerMixin, + eta: Optional[float], + generator: Union[PhiloxGenerator, torch.Generator], ): """prepare extra kwargs for the scheduler step, since not all schedulers have the same signature eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 and should be between [0, 1]""" + if isinstance(scheduler, KdiffusionSchedulerAdapter): + return {} + accepts_eta = "eta" in set( inspect.signature(scheduler.step).parameters.keys() # type: ignore ) @@ -49,8 +63,10 @@ def prepare_extra_step_kwargs( extra_step_kwargs["eta"] = eta # check if the scheduler accepts generator - accepts_generator = "generator" in set( - inspect.signature(scheduler.step).parameters.keys() # type: ignore + accepts_generator = ( + "generator" + in set(inspect.signature(scheduler.step).parameters.keys()) # type: ignore + and config.api.generator != "philox" ) if accepts_generator: extra_step_kwargs["generator"] = generator @@ -59,27 +75,47 @@ def prepare_extra_step_kwargs( def change_scheduler( model: Optional[PyTorchModelType], - scheduler: KarrasDiffusionSchedulers, - config: Optional[Dict] = None, - autoload: bool = True, - use_karras_sigmas: bool = False, -): + scheduler: Union[str, KarrasDiffusionSchedulers], + configuration: Optional[Dict] = None, + sigma_type: SigmaScheduler = "automatic", + sampler_settings: Optional[Dict] = None, +) -> SchedulerMixin: "Change the scheduler of the model" - config = model.scheduler.config # type: ignore + configuration = model.scheduler.config # type: ignore - try: - new_scheduler = getattr(importlib.import_module("diffusers"), scheduler.name) - except AttributeError: - new_scheduler = model.scheduler # type: ignore + if (isinstance(scheduler, str) and scheduler.isdigit()) or isinstance( + scheduler, (int, KarrasDiffusionSchedulers) + ): + scheduler = KarrasDiffusionSchedulers(int(unwrap_enum(scheduler))) + try: + new_scheduler = getattr( + importlib.import_module("diffusers"), scheduler.name + ) + except AttributeError: + new_scheduler = model.scheduler # type: ignore - if autoload: if scheduler.value in [10, 11]: logger.debug( - f"Loading scheduler {new_scheduler.__class__.__name__} with config karras_sigmas={use_karras_sigmas}" + f"Loading scheduler {new_scheduler.__class__.__name__} with config sigmas={sigma_type}" ) - model.scheduler = new_scheduler.from_config(config=config, use_karras_sigmas=use_karras_sigmas) # type: ignore + new_scheduler = new_scheduler.from_config(config=configuration, use_karras_sigmas=sigma_type == "") # type: ignore else: - model.scheduler = new_scheduler.from_config(config=config) # type: ignore + new_scheduler = new_scheduler.from_config(config=configuration) # type: ignore else: - return new_scheduler + sched = DDIMScheduler.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="scheduler") # type: ignore + + new_scheduler = create_sampler( + alphas_cumprod=sched.alphas_cumprod, # type: ignore + denoiser_enable_quantization=True, + sampler=scheduler, + sigma_type=sigma_type, + eta_noise_seed_delta=0, + sigma_always_discard_next_to_last=False, + sigma_use_old_karras_scheduler=False, + device=model.unet.device, # type: ignore + dtype=model.unet.dtype, # type: ignore + sampler_settings=sampler_settings, + ) + model.scheduler = new_scheduler # type: ignore + return new_scheduler # type: ignore diff --git a/core/inference/utilities/vae.py b/core/inference/utilities/vae.py new file mode 100644 index 000000000..be6890aef --- /dev/null +++ b/core/inference/utilities/vae.py @@ -0,0 +1,111 @@ +from typing import Callable, Optional + +import numpy as np +import torch +from PIL import Image + +from core.config import config + +taesd_model = None + + +def taesd( + samples: torch.Tensor, height: Optional[int] = None, width: Optional[int] = None +) -> torch.Tensor: + global taesd_model + + if taesd_model is None: + from diffusers import AutoencoderTiny + + model = "madebyollin/taesd" + if False: # TODO: if is_sdxl: + model = "madebyollin/taesdxl" + taesd_model = AutoencoderTiny.from_pretrained( + model, torch_dtype=torch.float16 + ).to( # type: ignore + config.api.device + ) + + return decode_latents( + lambda sample: taesd_model.decode(sample).sample, # type: ignore + samples.to(torch.float16), + height=height or samples[0].shape[1] * 8, + width=width or samples[0].shape[2] * 8, + ) + + +def cheap_approximation(sample: torch.Tensor) -> Image.Image: + "Convert a tensor of latents to RGB" + + # Credit to Automatic111 stable-diffusion-webui + # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/2 + + coeffs = [ + [0.298, 0.207, 0.208], + [0.187, 0.286, 0.173], + [-0.158, 0.189, 0.264], + [-0.184, -0.271, -0.473], + ] + if False: # TODO: if is_sdxl: + coeffs = [ + [0.3448, 0.4168, 0.4395], + [-0.1953, -0.0290, 0.0250], + [0.1074, 0.0886, -0.0163], + [-0.3730, -0.2499, -0.2088], + ] + coeffs = torch.tensor(coeffs, dtype=torch.float32, device="cpu") + + decoded_rgb = torch.einsum( + "lxy,lr -> rxy", sample.to(torch.float32).to("cpu"), coeffs + ) + decoded_rgb = torch.clamp((decoded_rgb + 1.0) / 2.0, min=0.0, max=1.0) + decoded_rgb = 255.0 * np.moveaxis(decoded_rgb.cpu().numpy(), 0, 2) + decoded_rgb = decoded_rgb.astype(np.uint8) + + return Image.fromarray(decoded_rgb) + + +def full_vae( + samples: torch.Tensor, + overwrite: Callable[[torch.Tensor], torch.Tensor], + height: Optional[int] = None, + width: Optional[int] = None, +) -> torch.Tensor: + return decode_latents( + overwrite, + samples, + height or samples[0].shape[1] * 8, + width or samples[0].shape[2] * 8, + ) + + +def decode_latents( + decode_lambda: Callable[[torch.Tensor], torch.Tensor], + latents: torch.Tensor, + height: int, + width: int, +) -> torch.Tensor: + "Decode latents" + latents = 1 / 0.18215 * latents + image = decode_lambda(latents) # type: ignore + image = (image / 2 + 0.5).clamp(0, 1) + # we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16 + image = image.cpu().permute(0, 2, 3, 1).float().numpy() + img = image[:, :height, :width, :] + return img + + +def numpy_to_pil(images): + """ + Convert a numpy image or a batch of images to a PIL image. + """ + if images.ndim == 3: + images = images[None, ...] + images = (images * 255).round().astype("uint8") + if images.shape[-1] == 1: + # special case for grayscale (single channel) images + pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images] + else: + pil_images = [Image.fromarray(image) for image in images] + + return pil_images diff --git a/core/inference_callbacks.py b/core/inference_callbacks.py index 7ea0179ec..bf73938db 100644 --- a/core/inference_callbacks.py +++ b/core/inference_callbacks.py @@ -1,7 +1,6 @@ import time -from typing import List, Tuple +from typing import List -import numpy as np import torch from PIL import Image @@ -10,141 +9,41 @@ from core import shared from core.config import config from core.errors import InferenceInterruptedError +from core.inference.utilities import cheap_approximation, numpy_to_pil, taesd from core.utils import convert_images_to_base64_grid last_image_time = time.time() -def cheap_approximation(sample: torch.Tensor): - "Convert a tensor of latents to RGB" +def callback(step: int, _timestep: int, tensor: torch.Tensor): + "Callback for all processes that have steps and partial images." - # Credit to Automatic111 stable-diffusion-webui - # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/2 + global last_image_time - coefs = ( - torch.tensor( - [ - [0.298, 0.207, 0.208], - [0.187, 0.286, 0.173], - [-0.158, 0.189, 0.264], - [-0.184, -0.271, -0.473], - ] - ) - .to(torch.float32) - .to("cpu") - ) - - cast_sample = sample.to(torch.float32).to("cpu") - - x_sample = torch.einsum("lxy,lr -> rxy", cast_sample, coefs) - - return x_sample - - -def txt2img_callback(step: int, _timestep: int, tensor: torch.Tensor): - "Callback for txt2img with progress and partial image" - - images, send_image = pytorch_callback(step, _timestep, tensor) - - websocket_manager.broadcast_sync( - data=Data( - data_type="txt2img", - data={ - "progress": int( - (shared.current_done_steps / shared.current_steps) * 100 - ), - "current_step": shared.current_done_steps, - "total_steps": shared.current_steps, - "image": convert_images_to_base64_grid( - images, quality=60, image_format="webp" - ) - if send_image - else "", - }, - ) - ) - - -def img2img_callback(step: int, _timestep: int, tensor: torch.Tensor): - "Callback for img2img with progress and partial image" - - images, send_image = pytorch_callback(step, _timestep, tensor) - - websocket_manager.broadcast_sync( - data=Data( - data_type="img2img", - data={ - "progress": int( - (shared.current_done_steps / shared.current_steps) * 100 - ), - "current_step": shared.current_done_steps, - "total_steps": shared.current_steps, - "image": convert_images_to_base64_grid( - images, quality=60, image_format="webp" - ) - if send_image - else "", - }, - ) - ) - - -def inpaint_callback(step: int, _timestep: int, tensor: torch.Tensor): - "Callback for inpaint with progress and partial image" - - images, send_image = pytorch_callback(step, _timestep, tensor) - - websocket_manager.broadcast_sync( - data=Data( - data_type="inpainting", - data={ - "progress": int( - (shared.current_done_steps / shared.current_steps) * 100 - ), - "current_step": shared.current_done_steps, - "total_steps": shared.current_steps, - "image": convert_images_to_base64_grid( - images, quality=60, image_format="webp" - ) - if send_image - else "", - }, - ) - ) - - -def image_variations_callback(step: int, _timestep: int, tensor: torch.Tensor): - "Callback for image variations with progress and partial image" - - images, send_image = pytorch_callback(step, _timestep, tensor) + if shared.interrupt: + shared.interrupt = False + raise InferenceInterruptedError - websocket_manager.broadcast_sync( - data=Data( - data_type="image_variations", - data={ - "progress": int( - (shared.current_done_steps / shared.current_steps) * 100 - ), - "current_step": shared.current_done_steps, - "total_steps": shared.current_steps, - "image": convert_images_to_base64_grid( - images, quality=60, image_format="webp" - ) - if send_image - else "", - }, - ) + shared.current_done_steps += 1 + if step > shared.current_steps: + shared.current_steps = shared.current_done_steps + send_image = config.api.live_preview_method != "disabled" and ( + (time.time() - last_image_time > config.api.live_preview_delay) ) - -def controlnet_callback(step: int, _timestep: int, tensor: torch.Tensor): - "Callback for controlnet with progress and partial image" - - images, send_image = pytorch_callback(step, _timestep, tensor) + images: List[Image.Image] = [] + if send_image: + last_image_time = time.time() + if config.api.live_preview_method == "approximation": + for t in range(tensor.shape[0]): + images.append(cheap_approximation(tensor[t])) + else: + for img in numpy_to_pil(taesd(tensor)): + images.append(img) websocket_manager.broadcast_sync( data=Data( - data_type="controlnet", + data_type=shared.current_method, # type: ignore data={ "progress": int( (shared.current_done_steps / shared.current_steps) * 100 @@ -159,30 +58,3 @@ def controlnet_callback(step: int, _timestep: int, tensor: torch.Tensor): }, ) ) - - -def pytorch_callback( - _step: int, _timestep: int, tensor: torch.Tensor -) -> Tuple[List[Image.Image], bool]: - "Send a websocket message to the client with the progress percentage and partial image" - - global last_image_time # pylint: disable=global-statement - - if shared.interrupt: - shared.interrupt = False - raise InferenceInterruptedError - - shared.current_done_steps += 1 - send_image: bool = time.time() - last_image_time > config.api.image_preview_delay - images: List[Image.Image] = [] - - if send_image: - last_image_time = time.time() - for i in range(tensor.shape[0]): - decoded_rgb = cheap_approximation(tensor[i]) - decoded_rgb = torch.clamp((decoded_rgb + 1.0) / 2.0, min=0.0, max=1.0) - decoded_rgb = 255.0 * np.moveaxis(decoded_rgb.cpu().numpy(), 0, 2) - decoded_rgb = decoded_rgb.astype(np.uint8) - images.append(Image.fromarray(decoded_rgb)) - - return images, send_image diff --git a/core/install_requirements.py b/core/install_requirements.py index f20bc6800..566433c34 100644 --- a/core/install_requirements.py +++ b/core/install_requirements.py @@ -1,6 +1,7 @@ import importlib.metadata import importlib.util import logging +import os import platform import subprocess import sys @@ -15,6 +16,8 @@ "cuda-python": "cuda", "open_clip_torch": "open_clip", "python-multipart": "multipart", + "discord.py": "discord", + "HyperTile": "hyper-tile", } logger = logging.getLogger(__name__) @@ -43,8 +46,11 @@ def install_requirements(path_to_requirements: str = "requirements.txt"): continue if "git+http" in i: - logger.debug(f"Skipping git requirement (cannot check version): {i}") - continue + tmp = i.split("@")[0].split("/")[-1].replace(".git", "").strip() + logger.debug( + f"Rewrote git requirement (cannot check hash, but proceeding): {i} => {tmp}" + ) + i = tmp if "==" in i: requirements[i.split("==")[0]] = i.replace(i.split("==")[0], "").strip() @@ -208,7 +214,7 @@ class PytorchDistribution: ] -def install_pytorch(force_distribution: int = -1): +def install_deps(force_distribution: int = -1): "Install necessary requirements for inference" # Install pytorch @@ -236,7 +242,7 @@ def install_pytorch(force_distribution: int = -1): (c.windows_supported if platform.system() == "Windows" else True) and ( ( - subprocess.run( # pylint: disable=subprocess-run-check + subprocess.run( c.check_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, @@ -259,6 +265,9 @@ def install_pytorch(force_distribution: int = -1): install_requirements("requirements/api.txt") install_requirements("requirements/interrogation.txt") + if os.environ.get("DISCORD_BOT_TOKEN"): + install_requirements("requirements/bot.txt") + def install_bot(): "Install necessary requirements for the discord bot" @@ -366,9 +375,7 @@ def version_check(commit: str): .strip() ) username = origin.split("/")[-2] - project = origin.split("/")[-1].split(".")[ # pylint: disable=use-maxsplit-arg - 0 - ] + project = origin.split("/")[-1].split(".")[0] commits = requests.get( f"https://api.github.com/repos/{username}/{project}/branches/{current_branch}", @@ -384,7 +391,7 @@ def version_check(commit: str): print("You are up to date with the most recent release.") else: print("Not a git clone, can't perform version check.") - except Exception as e: # pylint: disable=broad-except + except Exception as e: logger.debug(f"Version check failed: {e}") logger.info( "No git repo found, assuming that we are in containerized environment" diff --git a/core/interrogation/base_interrogator.py b/core/interrogation/base_interrogator.py index b3d077761..89430160b 100644 --- a/core/interrogation/base_interrogator.py +++ b/core/interrogation/base_interrogator.py @@ -38,7 +38,7 @@ def generate(self, job: Job) -> InterrogationResult: def memory_cleanup(self) -> None: "Cleanup the GPU memory" - if config.api.device_type == "cpu" or config.api.device_type == "directml": + if "cpu" in config.api.device or "privateuseone" in config.api.device: gc.collect() else: torch.cuda.empty_cache() diff --git a/core/interrogation/clip.py b/core/interrogation/clip.py index 49fe691cf..97a79b1d6 100644 --- a/core/interrogation/clip.py +++ b/core/interrogation/clip.py @@ -306,9 +306,7 @@ def __init__(self, labels: List[str], descriptor: str, interrogator): if is_cpu(self.device): self.embeds = [e.astype(np.float32) for e in self.embeds] - def _load_cached( - self, descriptor: str, hash_: str, sanitized_name: str - ) -> bool: # pylint: disable=redefined-builtin + def _load_cached(self, descriptor: str, hash_: str, sanitized_name: str) -> bool: cached_safetensors = self.cache_path.joinpath( f"{sanitized_name}_{descriptor}.safetensors" ) diff --git a/core/interrogation/deepdanbooru.py b/core/interrogation/deepdanbooru.py index 3c0f58959..e7df3f7e3 100644 --- a/core/interrogation/deepdanbooru.py +++ b/core/interrogation/deepdanbooru.py @@ -76,7 +76,7 @@ def load(self): else: try: self.model = convert_to_reference_fx(prepared, backend_config=get_tensorrt_backend_config_dict()) # type: ignore - except Exception: # pylint: disable=broad-except + except Exception: pass del prepared else: diff --git a/core/logger/websocket_logging.py b/core/logger/websocket_logging.py new file mode 100644 index 000000000..721ba516f --- /dev/null +++ b/core/logger/websocket_logging.py @@ -0,0 +1,42 @@ +from logging import LogRecord, StreamHandler +from typing import TYPE_CHECKING, Optional + +from api import websocket_manager +from api.websockets.data import Data +from core.functions import debounce + +if TYPE_CHECKING: + from core.config.config import Configuration + + +class WebSocketLoggingHandler(StreamHandler): + "Broadcasts log messages to all connected clients." + + def __init__(self, config: Optional["Configuration"]): + super().__init__() + self.buffer = [] + self.config = config + + def emit(self, record: LogRecord): + if not self.config: + return + if self.config.api.enable_websocket_logging is False: + return + + msg = f"{record.levelname} {self.format(record)}" + self.buffer.insert(0, msg) + + # Prevent buffer from growing too large + if len(self.buffer) > 100: + self.send() + + self.debounced_send() + + @debounce(0.5) + def debounced_send(self): + self.send() + + def send(self): + msg = "\n".join(self.buffer) + self.buffer.clear() + websocket_manager.broadcast_sync(Data(data={"message": msg}, data_type="log")) diff --git a/core/optimizations/__init__.py b/core/optimizations/__init__.py index 431337bba..98b0920e8 100644 --- a/core/optimizations/__init__.py +++ b/core/optimizations/__init__.py @@ -1,8 +1,14 @@ from .autocast_utils import autocast, without_autocast +from .context_manager import inference_context, InferenceContext from .pytorch_optimizations import optimize_model +from .hypertile import is_hypertile_available, hypertile __all__ = [ "optimize_model", "without_autocast", "autocast", + "inference_context", + "InferenceContext", + "is_hypertile_available", + "hypertile", ] diff --git a/core/optimizations/attn/__init__.py b/core/optimizations/attn/__init__.py index cce3fda89..cec85ef8c 100644 --- a/core/optimizations/attn/__init__.py +++ b/core/optimizations/attn/__init__.py @@ -15,11 +15,11 @@ def _xf(pipe): try: - if is_xformers_available() and config.api.device_type != "directml": + if is_xformers_available() and config.api.device != "directml": return False pipe.enable_xformers_memory_efficient_attention() return True - except Exception: # pylint: disable=broad-except + except Exception: pass return False diff --git a/core/optimizations/attn/multihead_attention.py b/core/optimizations/attn/multihead_attention.py index d4f73ae7d..fe715a303 100644 --- a/core/optimizations/attn/multihead_attention.py +++ b/core/optimizations/attn/multihead_attention.py @@ -30,7 +30,7 @@ def __init__( vdim=cross_attention_dim, ) - def forward( # pylint: disable=arguments-differ + def forward( self, hidden_states: torch.Tensor, encoder_hidden_states: Optional[torch.Tensor] = None, diff --git a/core/optimizations/autocast_utils.py b/core/optimizations/autocast_utils.py index 7caf2bc4e..127afc049 100644 --- a/core/optimizations/autocast_utils.py +++ b/core/optimizations/autocast_utils.py @@ -40,19 +40,19 @@ def autocast( ): "Context manager to autocast tensors to desired dtype for all supported backends" - global _initialized_directml # pylint: disable=global-statement + global _initialized_directml if dtype == torch.float32 or disable: return contextlib.nullcontext() - if config.api.device_type == "directml": + if "privateuseone" in config.api.device: if not _initialized_directml: for p in _patch_list: _patch(p) _initialized_directml = True return torch.dml.autocast(dtype=dtype, disable=False) # type: ignore - if config.api.device_type == "intel": + if "xpu" in config.api.device: return torch.xpu.amp.autocast(enabled=True, dtype=dtype, cache_enabled=False) # type: ignore - if config.api.device_type == "cpu": + if "cpu" in config.api.device: return torch.cpu.amp.autocast(enabled=True, dtype=dtype, cache_enabled=False) # type: ignore return torch.cuda.amp.autocast(enabled=True, dtype=dtype, cache_enabled=False) # type: ignore @@ -62,11 +62,11 @@ def without_autocast(disable: bool = False): if disable: return contextlib.nullcontext() - if config.api.device_type == "directml": + if "privateuseone" in config.api.device: return torch.dml.autocast(disable=True) # type: ignore - if config.api.device_type == "intel": + if "xpu" in config.api.device: return torch.xpu.amp.autocast(enabled=False, cache_enabled=False) # type: ignore - if config.api.device_type == "cpu": + if "cpu" in config.api.device: return torch.cpu.amp.autocast(enabled=False, cache_enabled=False) # type: ignore return torch.cuda.amp.autocast(enabled=False, cache_enabled=False) # type: ignore diff --git a/core/optimizations/context_manager.py b/core/optimizations/context_manager.py new file mode 100644 index 000000000..e02143035 --- /dev/null +++ b/core/optimizations/context_manager.py @@ -0,0 +1,32 @@ +from contextlib import ExitStack + +import torch + +from core.config import config +from .autocast_utils import autocast +from .hypertile import is_hypertile_available, hypertile + + +class InferenceContext(ExitStack): + """inference context""" + + unet: torch.nn.Module + vae: torch.nn.Module + + +def inference_context(unet, vae, height, width) -> InferenceContext: + "Helper function for centralizing context management" + s = InferenceContext() + s.unet = unet + s.vae = vae + s.enter_context(autocast(unet.dtype, disable=config.api.autocast)) + if is_hypertile_available() and config.api.hypertile: + s.enter_context(hypertile(unet, height, width)) + if config.api.torch_compile: + s.unet = torch.compile( # type: ignore + unet, + fullgraph=config.api.torch_compile_fullgraph, + dynamic=config.api.torch_compile_dynamic, + mode=config.api.torch_compile_mode, + ) + return s diff --git a/core/optimizations/hypertile.py b/core/optimizations/hypertile.py new file mode 100644 index 000000000..02d86190c --- /dev/null +++ b/core/optimizations/hypertile.py @@ -0,0 +1,27 @@ +from contextlib import ExitStack + +from core.config import config + + +def is_hypertile_available(): + "Checks whether hypertile is available" + try: + import hyper_tile # noqa: F401 + + return True + except ImportError: + return False + + +if is_hypertile_available(): + from hyper_tile import split_attention # noqa: F401 + + +def hypertile(unet, height: int, width: int) -> ExitStack: + from hyper_tile import split_attention # noqa: F811 + + s = ExitStack() + s.enter_context( + split_attention(unet, height / width, tile_size=config.api.hypertile_unet_chunk) + ) + return s diff --git a/core/optimizations/pytorch_optimizations.py b/core/optimizations/pytorch_optimizations.py index a34472ba0..afb2d3add 100644 --- a/core/optimizations/pytorch_optimizations.py +++ b/core/optimizations/pytorch_optimizations.py @@ -53,11 +53,9 @@ def optimize_model( if (is_pytorch_pipe(pipe) and not is_for_aitemplate) else None ) - can_offload = config.api.device_type not in [ - "cpu", - "vulkan", - "mps", - ] and (offload != "disabled" and offload is not None) + can_offload = any( + map(lambda x: x not in config.api.device, ["cpu", "vulkan", "mps"]) + ) and (offload != "disabled" and offload is not None) # Took me an hour to understand why CPU stopped working... # Turns out AMD just lacks support for BF16... @@ -65,7 +63,7 @@ def optimize_model( if not can_offload and not is_for_aitemplate: pipe.to(device, torch_dtype=config.api.dtype) - if config.api.device_type == "cuda" and not is_for_aitemplate: + if "cuda" in config.api.device and not is_for_aitemplate: supports_tf = supports_tf32(device) if config.api.reduced_precision: if supports_tf: @@ -107,8 +105,8 @@ def optimize_model( # Disable for IPEX as well, they don't like torch's way of setting memory format if ( config.api.channels_last - and config.api.device_type != "directml" - and (not is_ipex_available() and config.api.device_type != "cpu") + and "privateuseone" not in config.api.device + and (not is_ipex_available() and "cpu" not in config.api.device) and not is_for_aitemplate ): pipe.unet.to(memory_format=torch.channels_last) # type: ignore @@ -132,7 +130,7 @@ def optimize_model( # Offload to CPU from accelerate import cpu_offload_with_hook - if config.api.device_type == "cuda": + if "cuda" in config.api.device: torch.cuda.empty_cache() # otherwise we don't see the memory savings (but they probably exist) hook = None @@ -210,7 +208,7 @@ def optimize_model( ) ipexed = False - if config.api.device_type == "cpu": + if "cpu" in config.api.device: from cpufeature import CPUFeature as cpu n = (cpu["num_virtual_cores"] // 4) * 3 @@ -222,7 +220,7 @@ def optimize_model( ) if is_ipex_available(): - import intel_extension_for_pytorch as ipex # pylint: disable=import-error + import intel_extension_for_pytorch as ipex logger.info("Optimization: Running IPEX optimizations") diff --git a/core/optimizations/trace_utils.py b/core/optimizations/trace_utils.py index 3cfa0ceba..5515250ce 100644 --- a/core/optimizations/trace_utils.py +++ b/core/optimizations/trace_utils.py @@ -72,7 +72,7 @@ def trace_model( logger.debug("Starting trace") with warnings.catch_warnings(): warnings.simplefilter("ignore") - if config.api.device_type == "cpu": + if "cpu" in config.api.device: torch.jit.enable_onednn_fusion(True) model = torch.jit.trace(model, generate_inputs(dtype, device), check_trace=False) # type: ignore model = torch.jit.freeze(model) # type: ignore diff --git a/core/png_metadata.py b/core/png_metadata.py index ecd8f9d37..51c426e19 100644 --- a/core/png_metadata.py +++ b/core/png_metadata.py @@ -138,7 +138,6 @@ def save_images( else: folder = "img2img" - filename = f"{job.data.id}-{i}.png" metadata = create_metadata(job, i) if job.save_image == "r2": @@ -147,6 +146,7 @@ def save_images( assert r2 is not None, "R2 is not configured, enable debug mode to see why" + filename = f"{job.data.id}-{i}.png" image_bytes = BytesIO() image.save(image_bytes, pnginfo=metadata, format=config.api.image_extension) image_bytes.seek(0) diff --git a/core/scheduling/__init__.py b/core/scheduling/__init__.py new file mode 100644 index 000000000..72a861ec8 --- /dev/null +++ b/core/scheduling/__init__.py @@ -0,0 +1,3 @@ +from .adapter.k_adapter import KdiffusionSchedulerAdapter +from .adapter.unipc_adapter import UnipcSchedulerAdapter +from .scheduling import create_sampler diff --git a/core/scheduling/adapter/k_adapter.py b/core/scheduling/adapter/k_adapter.py new file mode 100644 index 000000000..c41caddec --- /dev/null +++ b/core/scheduling/adapter/k_adapter.py @@ -0,0 +1,202 @@ +import functools +import inspect +import logging +from typing import Callable, Optional, Tuple, Union + +import k_diffusion +import torch + +from core.config import config +from core.inference.utilities.philox import PhiloxGenerator +from core.types import SigmaScheduler + +from ..hijack import TorchHijack +from ..sigmas import build_sigmas +from ..types import Denoiser, Sampler + +sampling = k_diffusion.sampling +logger = logging.getLogger(__name__) + + +class KdiffusionSchedulerAdapter: + "Somewhat diffusers compatible scheduler-like K-diffusion adapter." + sampler_tuple: Sampler + denoiser: Denoiser + + # diffusers compat + config: dict = {"steps_offset": 0, "prediction_type": "epsilon"} + + # should really be "sigmas," but for compatibility with diffusers + # it's named timesteps. + timesteps: torch.Tensor # calculated sigmas + scheduler_name: Optional[SigmaScheduler] + + alphas_cumprod: torch.Tensor + + sigma_range: Tuple[float, float] = (0, 1.0) + sigma_rho: float = 1 + sigma_always_discard_next_to_last: bool = False + + sampler_eta: Optional[float] = None + sampler_churn: Optional[float] = None + sampler_trange: Tuple[float, float] = (0, 0) + sampler_noise: Optional[float] = None + + steps: int = 50 + + eta_noise_seed_delta: float = 0 + + device: torch.device + dtype: torch.dtype + + sampler_settings: dict + + def __init__( + self, + alphas_cumprod: torch.Tensor, + scheduler_name: Optional[SigmaScheduler], + sampler_tuple: Sampler, + sigma_range: Tuple[float, float], + sigma_rho: float, + sigma_discard: bool, + sampler_eta: float, + sampler_churn: float, + sampler_trange: Tuple[float, float], + sampler_noise: float, + device: torch.device, + dtype: torch.dtype, + sampler_settings: dict, + ) -> None: + self.alphas_cumprod = alphas_cumprod.to(device=device) + + self.scheduler_name = scheduler_name + self.sampler_tuple = sampler_tuple + + self.sigma_range = sigma_range + + self.sampler_settings = sampler_settings + if self.sampler_settings: + for key, value in self.sampler_settings.copy().items(): + if value is None: + del self.sampler_settings[key] + + logger.debug(f"Sampler settings overwrite: {self.sampler_settings}") + + self.sampler_eta = sampler_eta + if self.sampler_eta is None: + self.sampler_eta = self.sampler_tuple[2].get("default_eta", None) + self.sampler_churn = sampler_churn + self.sampler_trange = sampler_trange + self.sampler_noise = sampler_noise + + self.device = device + self.dtype = dtype + + def set_timesteps( + self, + steps: int, + device: Optional[torch.device] = None, + dtype: Optional[torch.dtype] = None, + ) -> None: + "Initialize timesteps (sigmas) and set steps to correct amount." + self.steps = steps + self.timesteps = build_sigmas( + steps=steps, + denoiser=self.denoiser, + discard_next_to_last_sigma=self.sampler_tuple[2].get( + "discard_next_to_last_sigma", self.sigma_always_discard_next_to_last + ), + scheduler=self.scheduler_name, + custom_rho=self.sigma_rho, + custom_sigma_min=self.sigma_range[0], + custom_sigma_max=self.sigma_range[1], + ).to(device=device or self.device, dtype=dtype or self.dtype) + + def scale_model_input( + self, sample: torch.FloatTensor, timestep: Optional[int] = None + ) -> torch.FloatTensor: + "diffusers#scale_model_input" + return sample + + @property + def init_noise_sigma(self) -> torch.Tensor: + "diffusers#init_noise_sigma" + # SGM / ODE doesn't necessarily produce "better" images, it's here for feature parity with both A1111 and SGM. + return ( + torch.sqrt(1.0 + self.timesteps[0] ** 2.0) + if config.api.sgm_noise_multiplier + else self.timesteps[0] + ) + + def do_inference( + self, + x: torch.Tensor, + call: Callable, + apply_model: Callable[..., torch.Tensor], + generator: Union[PhiloxGenerator, torch.Generator], + callback, + callback_steps, + ) -> torch.Tensor: + "Run inference function provided with denoiser." + apply_model = functools.partial(apply_model, call=self.denoiser) + self.denoiser.inner_model.callable = call + + def callback_func(data): + if callback is not None and data["i"] % callback_steps == 0: + callback(data["i"], data["sigma"], data["x"]) + + def create_noise_sampler(): + if self.sampler_tuple[2].get("brownian_noise", False): + return k_diffusion.sampling.BrownianTreeNoiseSampler( + x, + self.timesteps[self.timesteps > 0].min(), + self.timesteps.max(), + ) + + def noiser(sigma=None, sigma_next=None): + from core.inference.utilities import randn_like + + return randn_like(x, generator, device=x.device, dtype=x.dtype) + + return noiser + + sampler_args = { + "n": self.steps, + "model": apply_model, + "x": x, + "callback": callback_func, + "sigmas": self.timesteps, + "sigma_min": self.denoiser.sigmas[0].item(), # type: ignore + "sigma_max": self.denoiser.sigmas[-1].item(), # type: ignore + "noise_sampler": create_noise_sampler(), + "eta": self.sampler_eta, + "s_churn": self.sampler_churn, + "s_tmin": self.sampler_trange[0], + "s_tmax": self.sampler_trange[1], + "s_noise": self.sampler_noise, + "order": 2 if self.sampler_tuple[2].get("second_order", False) else None, + **self.sampler_settings, + } + + k_diffusion.sampling.torch = TorchHijack(generator) + + if isinstance(self.sampler_tuple[1], str): + sampler_func = getattr(sampling, self.sampler_tuple[1]) + else: + sampler_func = self.sampler_tuple[1] + parameters = inspect.signature(sampler_func).parameters.keys() + for key, value in sampler_args.copy().items(): + if key not in parameters or value is None: + del sampler_args[key] + return sampler_func(**sampler_args) + + def add_noise( + self, + original_samples: torch.FloatTensor, + noise: torch.FloatTensor, + timesteps: torch.Tensor, + ) -> torch.Tensor: + "diffusers#add_noise" + return original_samples + (noise * self.init_noise_sigma).to( + original_samples.device, original_samples.dtype + ) diff --git a/core/scheduling/adapter/unipc_adapter.py b/core/scheduling/adapter/unipc_adapter.py new file mode 100644 index 000000000..c93dd42c2 --- /dev/null +++ b/core/scheduling/adapter/unipc_adapter.py @@ -0,0 +1,170 @@ +import functools +from typing import Any, Callable, Optional, Union + +import torch + +from core.inference.utilities.philox import PhiloxGenerator + +from ..types import Method, ModelType, SkipType, Variant +from ..unipc import NoiseScheduleVP, UniPC +from .k_adapter import KdiffusionSchedulerAdapter + + +class UnipcSchedulerAdapter(KdiffusionSchedulerAdapter): + scheduler: NoiseScheduleVP + + skip_type: SkipType + model_type: ModelType + variant: Variant + method: Method + + order: int + lower_order_final: bool + + timesteps: torch.Tensor + steps: int + + def __init__( + self, + alphas_cumprod: torch.Tensor, + device: torch.device, + dtype: torch.dtype, + skip_type: SkipType = "time_uniform", + model_type: ModelType = "noise", + variant: Variant = "bh1", + method: Method = "multistep", + order: int = 3, + lower_order_final: bool = True, + ) -> None: + super().__init__( + alphas_cumprod, + "karras", + ("", "", {}), + (0, 0), + 0, + False, + 0, + 0, + (0, 0), + 0, + device, + dtype, + sampler_settings={}, + ) + + self.skip_type = skip_type + self.model_type = model_type + self.variant = variant + self.method = method + self.order = order + self.lower_order_final = lower_order_final + self.scheduler = NoiseScheduleVP("discrete", alphas_cumprod=alphas_cumprod) + + def set_timesteps( + self, + steps: int, + device: Optional[torch.device] = None, + dtype: Optional[torch.dtype] = None, + ) -> None: + self.steps = steps + + def get_time_steps(skip_type, t_T, t_0, N, device): + """Compute the intermediate time steps for sampling.""" + if skip_type == "logSNR": + lambda_T = self.scheduler.marginal_lambda(torch.tensor(t_T).to(device)) + lambda_0 = self.scheduler.marginal_lambda(torch.tensor(t_0).to(device)) + logSNR_steps = torch.linspace( + lambda_T.cpu().item(), lambda_0.cpu().item(), N + 1 + ).to(device) + return self.scheduler.inverse_lambda(logSNR_steps) + elif skip_type == "time_uniform": + return torch.linspace(t_T, t_0, N + 1).to(device) + elif skip_type == "time_quadratic": + t_order = 2 + t = ( + torch.linspace( + t_T ** (1.0 / t_order), t_0 ** (1.0 / t_order), N + 1 + ) + .pow(t_order) + .to(device) + ) + return t + else: + raise ValueError( + f"Unsupported skip_type {skip_type}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'" + ) + + t_0 = 1.0 / self.scheduler.total_N + t_T = self.scheduler.T + self.timesteps = get_time_steps( + skip_type=self.skip_type, + t_T=t_T, + t_0=t_0, + N=self.steps, + device=device or self.device, + ) + + def do_inference( + self, + x, + call: Callable[..., Any], # type: ignore + apply_model: Callable[..., torch.Tensor], + generator: Union[PhiloxGenerator, torch.Generator], + callback, + callback_steps, + optional_device: Optional[torch.device] = None, + optional_dtype: Optional[torch.dtype] = None, + ) -> torch.Tensor: + device = optional_device or call.device + dtype = optional_dtype or call.dtype + + def noise_pred_fn(x, t_continuous, cond=None, **model_kwargs): + # Was originally get_model_input_time(t_continous) + # but "schedule" is ALWAYS "discrete," so we can skip it :) + t_input = (t_continuous - 1.0 / self.scheduler.total_N) * 1000 + if cond is None: + output = call( + x.to(device=device, dtype=dtype), + t_input.to(device=device, dtype=dtype), + return_dict=True, + **model_kwargs, + )[0] + else: + output = call(x.to(device=device, dtype=dtype), t_input.to(device=device, dtype=dtype), return_dict=True, encoder_hidden_states=cond, **model_kwargs)[0] # type: ignore + if self.model_type == "noise": + return output + elif self.model_type == "x_start": + alpha_t, sigma_t = self.scheduler.marginal_alpha( + t_continuous + ), self.scheduler.marginal_std(t_continuous) + return (x - alpha_t * output) / sigma_t + elif self.model_type == "v": + alpha_t, sigma_t = self.scheduler.marginal_alpha( + t_continuous + ), self.scheduler.marginal_std(t_continuous) + return alpha_t * output + sigma_t * x + elif self.model_type == "score": + sigma_t = self.scheduler.marginal_std(t_continuous) + return -sigma_t * output + + apply_model = functools.partial(apply_model, call=noise_pred_fn) + + # predict_x0=True -> algorithm_type="data_prediction" + # predict_x0=False -> algorithm_type="noise_prediction" + uni_pc = UniPC( + model_fn=apply_model, + noise_schedule=self.scheduler, + algorithm_type="data_prediction", + variant=self.variant, + ) + ret: torch.Tensor = uni_pc.sample( # type: ignore + x, + timesteps=self.timesteps, + steps=self.steps, + method=self.method, + order=self.order, + lower_order_final=self.lower_order_final, + callback=callback, + callback_steps=callback_steps, + ) + return ret.to(dtype=self.dtype, device=self.device) diff --git a/core/scheduling/custom/dpmpp_2m.py b/core/scheduling/custom/dpmpp_2m.py new file mode 100644 index 000000000..d1ef0b2da --- /dev/null +++ b/core/scheduling/custom/dpmpp_2m.py @@ -0,0 +1,41 @@ +# God bless this guy: https://github.com/AUTOMATIC1111/stable-diffusion-webui/discussions/8457 + +import torch +from tqdm import trange + + +@torch.no_grad() +def sample_dpmpp_2mV2(model, x, sigmas, extra_args=None, callback=None, disable=None): + """DPM-Solver++(2M) V2.""" + extra_args = {} if extra_args is None else extra_args + s_in: torch.Tensor = x.new_ones([x.shape[0]]) + sigma_fn = lambda t: t.neg().exp() + t_fn = lambda sigma: sigma.log().neg() + old_denoised = None + + for i in trange(len(sigmas) - 1, disable=disable): + print(sigmas[i].item(), s_in.item(), (sigmas[i] * s_in).item()) + denoised = model(x, sigmas[i] * s_in, **extra_args) + if callback is not None: + callback( + { + "x": x, + "i": i, + "sigma": sigmas[i], + "sigma_hat": sigmas[i], + "denoised": denoised, + } + ) + t, t_next = t_fn(sigmas[i]), t_fn(sigmas[i + 1]) + h = t_next - t + if old_denoised is None or sigmas[i + 1] == 0: + x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised + else: + h_last = t - t_fn(sigmas[i - 1]) + r = h_last / h + denoised_d = (1 + 1 / (2 * r)) * denoised - (1 / (2 * r)) * old_denoised + x = (sigma_fn(t_next) / sigma_fn(t)) * x - (-h).expm1() * denoised_d + sigma_progress = i / len(sigmas) + adjustment_factor = 1 + (0.15 * (sigma_progress * sigma_progress)) + old_denoised = denoised * adjustment_factor + return x diff --git a/core/scheduling/custom/restart.py b/core/scheduling/custom/restart.py new file mode 100644 index 000000000..411124ff1 --- /dev/null +++ b/core/scheduling/custom/restart.py @@ -0,0 +1,120 @@ +# Taken from https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/sd_samplers_extra.py + +import k_diffusion.sampling +import torch +import tqdm + + +@torch.no_grad() +def restart_sampler( + model, + x, + sigmas, + extra_args=None, + callback=None, + disable=None, + s_noise=1.0, + restart_list=None, +): + """Implements restart sampling in Restart Sampling for Improving Generative Processes (2023) + Restart_list format: {min_sigma: [ restart_steps, restart_times, max_sigma]} + If restart_list is None: will choose restart_list automatically, otherwise will use the given restart_list + """ + og_dev = x.device + og_d = x.dtype + extra_args = {} if extra_args is None else extra_args + s_in = x.new_ones([x.shape[0]]) + step_id = 0 + from k_diffusion.sampling import get_sigmas_karras, to_d + + def heun_step(x, old_sigma, new_sigma, second_order=True): + nonlocal step_id + denoised = model(x.to(og_d), (old_sigma * s_in).to(og_d), **extra_args) + d = to_d(x, old_sigma, denoised) + if callback is not None: + callback( + { + "x": x, + "i": step_id, + "sigma": new_sigma, + "sigma_hat": old_sigma, + "denoised": denoised, + } + ) + dt = new_sigma - old_sigma + if new_sigma == 0 or not second_order: + # Euler method + x = x + d * dt + else: + # Heun's method + x_2 = x + d * dt + denoised_2 = model(x_2.to(og_d), (new_sigma * s_in).to(og_d), **extra_args) + d_2 = to_d(x_2, new_sigma, denoised_2) + d_prime = (d + d_2) / 2 + x = x + d_prime * dt + step_id += 1 + return x + + steps = sigmas.shape[0] - 1 + if restart_list is None: + if steps >= 20: + restart_steps = 9 + restart_times = 1 + if steps >= 36: + restart_steps = steps // 4 + restart_times = 2 + sigmas = get_sigmas_karras( + steps - restart_steps * restart_times, + sigmas[-2].item(), + sigmas[0].item(), + device=sigmas.device, + ) + restart_list = {0.1: [restart_steps + 1, restart_times, 2]} + else: + restart_list = {} + + restart_list = { + int(torch.argmin(abs(sigmas - key), dim=0)): value + for key, value in restart_list.items() + } + + step_list = [] + for i in range(len(sigmas) - 1): + step_list.append((sigmas[i], sigmas[i + 1])) + if i + 1 in restart_list: + restart_steps, restart_times, restart_max = restart_list[i + 1] + min_idx = i + 1 + max_idx = int(torch.argmin(abs(sigmas - restart_max), dim=0)) + if max_idx < min_idx: + sigma_restart = get_sigmas_karras( + restart_steps, + sigmas[min_idx].item(), + sigmas[max_idx].item(), + device=sigmas.device, # type: ignore + )[:-1] + while restart_times > 0: + restart_times -= 1 + step_list.extend( + [ + (old_sigma, new_sigma) + for (old_sigma, new_sigma) in zip( + sigma_restart[:-1], sigma_restart[1:] + ) + ] + ) + + last_sigma = None + for old_sigma, new_sigma in tqdm.tqdm(step_list, disable=disable): + if last_sigma is None: + last_sigma = old_sigma + elif last_sigma < old_sigma: + x = ( + x + + k_diffusion.sampling.torch.randn_like(x) + * s_noise + * (old_sigma**2 - last_sigma**2) ** 0.5 + ) + x = heun_step(x, old_sigma, new_sigma) + last_sigma = new_sigma + + return x.to(dtype=og_d, device=og_dev) diff --git a/core/scheduling/denoiser.py b/core/scheduling/denoiser.py new file mode 100644 index 000000000..bfecffb3f --- /dev/null +++ b/core/scheduling/denoiser.py @@ -0,0 +1,41 @@ +import torch +from k_diffusion.external import CompVisDenoiser, CompVisVDenoiser + +from .types import Denoiser + + +class _ModelWrapper: + def __init__(self, alphas_cumprod: torch.Tensor) -> None: + self.callable: torch.Module = None # type: ignore + self.alphas_cumprod = alphas_cumprod + + def apply_model(self, *args, **kwargs) -> torch.Tensor: + "denoiser#apply_model" + if len(args) == 3: + encoder_hidden_states = args[-1] + args = args[:2] + if kwargs.get("cond", None) is not None: + encoder_hidden_states = kwargs.pop("cond") + if isinstance(self.callable, torch.nn.Module): + return self.callable(*args, encoder_hidden_states=encoder_hidden_states, return_dict=True, **kwargs)[0] # type: ignore + else: + return self.callable(*args, encoder_hidden_states=encoder_hidden_states, **kwargs) # type: ignore + + +def create_denoiser( + alphas_cumprod: torch.Tensor, + prediction_type: str, + device: torch.device = torch.device("cuda"), + dtype: torch.dtype = torch.float16, + denoiser_enable_quantization: bool = False, +) -> Denoiser: + "Create a denoiser based on the provided prediction_type" + model = _ModelWrapper(alphas_cumprod) + model.alphas_cumprod = alphas_cumprod + if prediction_type == "v_prediction": + denoiser = CompVisVDenoiser(model, quantize=denoiser_enable_quantization) + else: + denoiser = CompVisDenoiser(model, quantize=denoiser_enable_quantization) + denoiser.sigmas = denoiser.sigmas.to(device, dtype) + denoiser.log_sigmas = denoiser.log_sigmas.to(device, dtype) + return denoiser diff --git a/core/scheduling/hijack.py b/core/scheduling/hijack.py new file mode 100644 index 000000000..56b215bc6 --- /dev/null +++ b/core/scheduling/hijack.py @@ -0,0 +1,36 @@ +from typing import Union + +import torch + +from core.inference.utilities.philox import PhiloxGenerator + + +class TorchHijack: + """This is here to replace torch.randn_like of k-diffusion. + + k-diffusion has random_sampler argument for most samplers, but not for all, so + this is needed to properly replace every use of torch.randn_like. + + We need to replace to make images generated in batches to be same as images generated individually. + """ + + def __init__(self, generator: Union[PhiloxGenerator, torch.Generator]) -> None: + self.generator = generator + + super().__init__() + + def __getattr__(self, item): + if item == "randn_like": + return self.randn_like + + if hasattr(torch, item): + return getattr(torch, item) + + raise AttributeError( + f"'{type(self).__name__}' object has no attribute '{item}'" + ) + + def randn_like(self, x): + from core.inference.utilities import randn_like + + return randn_like(x, self.generator, x.device, x.dtype) diff --git a/core/scheduling/scheduling.py b/core/scheduling/scheduling.py new file mode 100644 index 000000000..0ca46aff9 --- /dev/null +++ b/core/scheduling/scheduling.py @@ -0,0 +1,172 @@ +import logging +from typing import Callable, Optional, Tuple, Union + +import torch +from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers + +from core.types import SigmaScheduler + +from .adapter.k_adapter import KdiffusionSchedulerAdapter +from .adapter.unipc_adapter import UnipcSchedulerAdapter +from .custom.dpmpp_2m import sample_dpmpp_2mV2 +from .custom.restart import restart_sampler +from .denoiser import create_denoiser + +logger = logging.getLogger(__name__) + +samplers_diffusers = { + KarrasDiffusionSchedulers.DPMSolverMultistepScheduler: "DPM++ 2M Karras", + KarrasDiffusionSchedulers.DPMSolverSinglestepScheduler: "DPM++ 2M SDE Karras", + KarrasDiffusionSchedulers.KDPM2DiscreteScheduler: "DPM2 Karras", + KarrasDiffusionSchedulers.KDPM2AncestralDiscreteScheduler: "DPM2 a Karras", + KarrasDiffusionSchedulers.EulerDiscreteScheduler: "Euler", + KarrasDiffusionSchedulers.EulerAncestralDiscreteScheduler: "Euler a", + KarrasDiffusionSchedulers.HeunDiscreteScheduler: "Heun", + KarrasDiffusionSchedulers.LMSDiscreteScheduler: "LMS Karras", + KarrasDiffusionSchedulers.UniPCMultistepScheduler: "UniPC Multistep", +} +samplers_kdiffusion = [ + ("euler_a", "sample_euler_ancestral", {"uses_ensd": True}), + ("euler", "sample_euler", {}), + ("lms", "sample_lms", {}), + ("heun", "sample_heun", {"second_order": True}), + ("dpm_fast", "sample_dpm_fast", {"uses_ensd": True, "default_eta": 0.0}), + ("dpm_adaptive", "sample_dpm_adaptive", {"uses_ensd": True, "default_eta": 0.0}), + ( + "dpm2", + "sample_dpm_2", + { + "discard_next_to_last_sigma": True, + "uses_ensd": True, + "second_order": True, + }, + ), + ( + "dpm2_a", + "sample_dpm_2_ancestral", + { + "discard_next_to_last_sigma": True, + "uses_ensd": True, + "second_order": True, + }, + ), + ( + "dpmpp_2s_a", + "sample_dpmpp_2s_ancestral", + {"uses_ensd": True, "second_order": True}, + ), + ("dpmpp_2m", "sample_dpmpp_2m", {}), + ( + "dpmpp_sde", + "sample_dpmpp_sde", + {"second_order": True, "brownian_noise": True}, + ), + ( + "dpmpp_2m_sharp", + sample_dpmpp_2mV2, # pretty much experimental, only for testing things + {}, + ), + ( + "dpmpp_2m_sde", + "sample_dpmpp_2m_sde", + {"brownian_noise": True}, + ), + ( + "dpmpp_3m_sde", + "sample_dpmpp_3m_sde", + {"brownian_noise": True}, + ), + ("unipc_multistep", "unipc", {}), + ("restart", restart_sampler, {}), +] + + +def _get_sampler( + sampler: Union[str, KarrasDiffusionSchedulers] +) -> Union[None, Tuple[str, Union[Callable, str], dict]]: + if isinstance(sampler, KarrasDiffusionSchedulers): + sampler = samplers_diffusers.get(sampler, "Euler a") # type: ignore + return next( + (ksampler for ksampler in samplers_kdiffusion if ksampler[0] == sampler), None + ) + + +def create_sampler( + alphas_cumprod: torch.Tensor, + sampler: Union[str, KarrasDiffusionSchedulers], + device: torch.device, + dtype: torch.dtype, + eta_noise_seed_delta: Optional[float] = None, + denoiser_enable_quantization: bool = False, + sigma_type: SigmaScheduler = "automatic", + sigma_use_old_karras_scheduler: bool = False, + sigma_always_discard_next_to_last: bool = False, + sigma_rho: Optional[float] = None, + sigma_min: Optional[float] = None, + sigma_max: Optional[float] = None, + sampler_eta: Optional[float] = None, + sampler_churn: Optional[float] = None, + sampler_tmin: Optional[float] = None, + sampler_tmax: Optional[float] = None, + sampler_noise: Optional[float] = None, + sampler_settings: Optional[dict] = None, +): + "Helper function for figuring out and creating a KdiffusionSchedulerAdapter for the appropriate settings given." + sampler_tuple = _get_sampler(sampler) + sampler_settings = sampler_settings or {} + + sigma_min = sampler_settings.pop("sigma_min", sigma_min) + sigma_max = sampler_settings.pop("sigma_max", sigma_max) + sigma_rho = sampler_settings.pop("sigma_rho", sigma_rho) + sigma_always_discard_next_to_last = sampler_settings.pop( + "sigma_discard", sigma_always_discard_next_to_last + ) + sampler_tmin = sampler_settings.pop("sampler_tmin", sampler_tmin) + sampler_tmax = sampler_settings.pop("sampler_tmax", sampler_tmax) + sampler_noise = sampler_settings.pop("sampler_noise", sampler_noise) + + if sampler_tuple is None: + raise ValueError("sampler_tuple is invalid") + + if sampler_tuple[1] == "unipc": + adapter = UnipcSchedulerAdapter( + alphas_cumprod=alphas_cumprod, + device=device, + dtype=dtype, + **sampler_tuple[2], + ) + else: + scheduler_name = sampler_tuple[2].get("scheduler", sigma_type) + if scheduler_name == "karras" and sigma_use_old_karras_scheduler: + sigma_min = 0.1 + sigma_max = 10 + + prediction_type = sampler_tuple[2].get("prediction_type", "epsilon") + logger.debug(f"Selected scheduler: {sampler_tuple[0]}-{prediction_type}") + + adapter = KdiffusionSchedulerAdapter( + alphas_cumprod=alphas_cumprod, + scheduler_name=scheduler_name, + sampler_tuple=sampler_tuple, + sigma_range=(sigma_min, sigma_max), # type: ignore + sigma_rho=sigma_rho, # type: ignore + sigma_discard=sigma_always_discard_next_to_last, + sampler_churn=sampler_churn, # type: ignore + sampler_eta=sampler_eta, # type: ignore + sampler_noise=sampler_noise, # type: ignore + sampler_trange=(sampler_tmin, sampler_tmax), # type: ignore + device=device, + dtype=dtype, + sampler_settings=sampler_settings, + ) + + adapter.eta_noise_seed_delta = eta_noise_seed_delta or 0 + + adapter.denoiser = create_denoiser( + alphas_cumprod=alphas_cumprod, + prediction_type=prediction_type, + denoiser_enable_quantization=denoiser_enable_quantization, + device=device, + dtype=dtype, + ) + return adapter diff --git a/core/scheduling/sigmas.py b/core/scheduling/sigmas.py new file mode 100644 index 000000000..b0fc0a133 --- /dev/null +++ b/core/scheduling/sigmas.py @@ -0,0 +1,61 @@ +import inspect +from logging import getLogger +from typing import Optional + +import k_diffusion +import torch + +from core.types import SigmaScheduler + +from .types import Denoiser + +sampling = k_diffusion.sampling +logger = getLogger(__name__) + + +def build_sigmas( + steps: int, + denoiser: Denoiser, + discard_next_to_last_sigma: bool = False, + scheduler: Optional[SigmaScheduler] = None, + custom_rho: Optional[float] = None, + custom_sigma_min: Optional[float] = None, + custom_sigma_max: Optional[float] = None, +) -> torch.Tensor: + "Build sigmas (timesteps) from custom values." + steps += 1 if discard_next_to_last_sigma else 0 + + if scheduler is None or scheduler == "automatic": + logger.debug("No optional scheduler provided. Using default.") + sigmas = denoiser.get_sigmas(steps) + else: + sigma_min, sigma_max = (denoiser.sigmas[0].item(), denoiser.sigmas[-1].item()) # type: ignore + rho = None + if scheduler == "polyexponential": + rho = 1 + elif scheduler == "karras": + rho = 7 + + sigma_min = custom_sigma_min if custom_sigma_min is not None else sigma_min + sigma_max = custom_sigma_max if custom_sigma_max is not None else sigma_max + rho = custom_rho if custom_rho is not None else rho + + arguments = { + "n": steps, + "sigma_min": sigma_min, + "sigma_max": sigma_max, + "rho": rho, + } + + sigma_func = getattr(sampling, f"get_sigmas_{scheduler}") + params = inspect.signature(sigma_func).parameters.keys() + for arg, val in arguments.copy().items(): + if arg not in params or val is None: + del arguments[arg] + + logger.debug(f"Building sigmas with {arguments}") + sigmas = getattr(sampling, f"get_sigmas_{scheduler}")(**arguments) + + if discard_next_to_last_sigma: + sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) + return sigmas diff --git a/core/scheduling/types.py b/core/scheduling/types.py new file mode 100644 index 000000000..05a5de7a4 --- /dev/null +++ b/core/scheduling/types.py @@ -0,0 +1,14 @@ +from typing import Any, Callable, Dict, Literal, Tuple, Union + +from k_diffusion.external import CompVisDenoiser, CompVisVDenoiser + +Sampler = Tuple[str, Union[Callable, str], Dict[str, Any]] +Denoiser = Union[CompVisVDenoiser, CompVisDenoiser] + +# UniPC +AlgorithmType = Literal["noise_prediction", "data_prediction"] +ModelType = Literal["v", "noise"] # "v" for 2.x and "noise" for 1.x +Variant = Literal["bh1", "bh2"] +SkipType = Literal["logSNR", "time_uniform", "time_quadratic"] +Method = Literal["multistep", "singlestep", "singlestep_fixed"] +UniPCSchedule = Literal["discrete", "linear", "cosine"] diff --git a/core/scheduling/unipc/__init__.py b/core/scheduling/unipc/__init__.py new file mode 100644 index 000000000..bd4c19cc9 --- /dev/null +++ b/core/scheduling/unipc/__init__.py @@ -0,0 +1,2 @@ +from .unipc import UniPC +from .noise_scheduler import NoiseScheduleVP diff --git a/core/scheduling/unipc/noise_scheduler.py b/core/scheduling/unipc/noise_scheduler.py new file mode 100644 index 000000000..c9f035d50 --- /dev/null +++ b/core/scheduling/unipc/noise_scheduler.py @@ -0,0 +1,197 @@ +# type: ignore pylint: disable=W1401 +# Taken from https://github.com/wl-zhao/UniPC/blob/main/uni_pc.py + +from typing import Optional +import math + +import torch + +from .utility import interpolate_fn +from ..types import UniPCSchedule + + +class NoiseScheduleVP: + def __init__( + self, + schedule: UniPCSchedule = "discrete", + betas: Optional[torch.Tensor] = None, + alphas_cumprod: Optional[torch.Tensor] = None, + continuous_beta_0: float = 0.1, + continuous_beta_1: float = 20.0, + dtype: torch.dtype = torch.float32, + ) -> None: + """Create a wrapper class for the forward SDE (VP type). + *** + Update: We support discrete-time diffusion models by implementing a picewise linear interpolation for log_alpha_t. + We recommend to use schedule='discrete' for the discrete-time diffusion models, especially for high-resolution images. + *** + The forward SDE ensures that the condition distribution q_{t|0}(x_t | x_0) = N ( alpha_t * x_0, sigma_t^2 * I ). + We further define lambda_t = log(alpha_t) - log(sigma_t), which is the half-logSNR (described in the DPM-Solver paper). + Therefore, we implement the functions for computing alpha_t, sigma_t and lambda_t. For t in [0, T], we have: + log_alpha_t = self.marginal_log_mean_coeff(t) + sigma_t = self.marginal_std(t) + lambda_t = self.marginal_lambda(t) + Moreover, as lambda(t) is an invertible function, we also support its inverse function: + t = self.inverse_lambda(lambda_t) + =============================================================== + We support both discrete-time DPMs (trained on n = 0, 1, ..., N-1) and continuous-time DPMs (trained on t in [t_0, T]). + 1. For discrete-time DPMs: + For discrete-time DPMs trained on n = 0, 1, ..., N-1, we convert the discrete steps to continuous time steps by: + t_i = (i + 1) / N + e.g. for N = 1000, we have t_0 = 1e-3 and T = t_{N-1} = 1. + We solve the corresponding diffusion ODE from time T = 1 to time t_0 = 1e-3. + Args: + betas: A `torch.Tensor`. The beta array for the discrete-time DPM. (See the original DDPM paper for details) + alphas_cumprod: A `torch.Tensor`. The cumprod alphas for the discrete-time DPM. (See the original DDPM paper for details) + Note that we always have alphas_cumprod = cumprod(1 - betas). Therefore, we only need to set one of `betas` and `alphas_cumprod`. + **Important**: Please pay special attention for the args for `alphas_cumprod`: + The `alphas_cumprod` is the \hat{alpha_n} arrays in the notations of DDPM. Specifically, DDPMs assume that + q_{t_n | 0}(x_{t_n} | x_0) = N ( \sqrt{\hat{alpha_n}} * x_0, (1 - \hat{alpha_n}) * I ). + Therefore, the notation \hat{alpha_n} is different from the notation alpha_t in DPM-Solver. In fact, we have + alpha_{t_n} = \sqrt{\hat{alpha_n}}, + and + log(alpha_{t_n}) = 0.5 * log(\hat{alpha_n}). + 2. For continuous-time DPMs: + We support two types of VPSDEs: linear (DDPM) and cosine (improved-DDPM). The hyperparameters for the noise + schedule are the default settings in DDPM and improved-DDPM: + Args: + beta_min: A `float` number. The smallest beta for the linear schedule. + beta_max: A `float` number. The largest beta for the linear schedule. + cosine_s: A `float` number. The hyperparameter in the cosine schedule. + cosine_beta_max: A `float` number. The hyperparameter in the cosine schedule. + T: A `float` number. The ending time of the forward process. + =============================================================== + Args: + schedule: A `str`. The noise schedule of the forward SDE. 'discrete' for discrete-time DPMs, + 'linear' or 'cosine' for continuous-time DPMs. + Returns: + A wrapper object of the forward SDE (VP type). + + =============================================================== + Example: + # For discrete-time DPMs, given betas (the beta array for n = 0, 1, ..., N - 1): + >>> ns = NoiseScheduleVP('discrete', betas=betas) + # For discrete-time DPMs, given alphas_cumprod (the \hat{alpha_n} array for n = 0, 1, ..., N - 1): + >>> ns = NoiseScheduleVP('discrete', alphas_cumprod=alphas_cumprod) + # For continuous-time DPMs (VPSDE), linear schedule: + >>> ns = NoiseScheduleVP('linear', continuous_beta_0=0.1, continuous_beta_1=20.) + """ + + self.schedule = schedule + if schedule == "discrete": + if betas is not None: + log_alphas = 0.5 * torch.log(1 - betas).cumsum(dim=0) + else: + assert alphas_cumprod is not None + log_alphas = 0.5 * torch.log(alphas_cumprod) + self.total_N = len(log_alphas) + self.T = 1.0 + self.t_array = ( + torch.linspace(0.0, 1.0, self.total_N + 1)[1:] + .reshape((1, -1)) + .to(dtype=dtype) + ) + self.log_alpha_array = log_alphas.reshape( + ( + 1, + -1, + ) + ).to(dtype=dtype) + else: + self.total_N = 1000 + self.beta_0 = continuous_beta_0 + self.beta_1 = continuous_beta_1 + self.cosine_s = 0.008 + self.cosine_beta_max = 999.0 + self.cosine_t_max = ( + math.atan(self.cosine_beta_max * (1.0 + self.cosine_s) / math.pi) + * 2.0 + * (1.0 + self.cosine_s) + / math.pi + - self.cosine_s + ) + self.cosine_log_alpha_0 = math.log( + math.cos(self.cosine_s / (1.0 + self.cosine_s) * math.pi / 2.0) + ) + self.schedule = schedule + if schedule == "cosine": + # For the cosine schedule, T = 1 will have numerical issues. So we manually set the ending time T. + # Note that T = 0.9946 may be not the optimal setting. However, we find it works well. + self.T = 0.9946 + else: + self.T = 1.0 + + def marginal_log_mean_coeff(self, t: torch.Tensor) -> torch.Tensor: + """ + Compute log(alpha_t) of a given continuous-time label t in [0, T]. + """ + if self.schedule == "discrete": + return interpolate_fn( + t.reshape((-1, 1)), + self.t_array.to(t.device), + self.log_alpha_array.to(t.device), + ).reshape((-1)) + elif self.schedule == "linear": + return -0.25 * t**2 * (self.beta_1 - self.beta_0) - 0.5 * t * self.beta_0 + elif self.schedule == "cosine": + log_alpha_fn = lambda s: torch.log( + torch.cos((s + self.cosine_s) / (1.0 + self.cosine_s) * math.pi / 2.0) + ) + log_alpha_t = log_alpha_fn(t) - self.cosine_log_alpha_0 + return log_alpha_t + + def marginal_alpha(self, t: torch.Tensor) -> torch.Tensor: + """ + Compute alpha_t of a given continuous-time label t in [0, T]. + """ + return torch.exp(self.marginal_log_mean_coeff(t)) # type: ignore + + def marginal_std(self, t: torch.Tensor) -> torch.Tensor: + """ + Compute sigma_t of a given continuous-time label t in [0, T]. + """ + return torch.sqrt(1.0 - torch.exp(2.0 * self.marginal_log_mean_coeff(t))) # type: ignore + + def marginal_lambda(self, t: torch.Tensor) -> torch.Tensor: + """ + Compute lambda_t = log(alpha_t) - log(sigma_t) of a given continuous-time label t in [0, T]. + """ + log_mean_coeff = self.marginal_log_mean_coeff(t) + log_std = 0.5 * torch.log(1.0 - torch.exp(2.0 * log_mean_coeff)) # type: ignore + return log_mean_coeff - log_std + + def inverse_lambda(self, lamb: torch.Tensor) -> torch.Tensor: + """ + Compute the continuous-time label t in [0, T] of a given half-logSNR lambda_t. + """ + if self.schedule == "linear": + tmp = ( + 2.0 + * (self.beta_1 - self.beta_0) + * torch.logaddexp(-2.0 * lamb, torch.zeros((1,)).to(lamb)) + ) + Delta = self.beta_0**2 + tmp + return tmp / (torch.sqrt(Delta) + self.beta_0) / (self.beta_1 - self.beta_0) + elif self.schedule == "discrete": + log_alpha = -0.5 * torch.logaddexp( + torch.zeros((1,)).to(lamb.device), -2.0 * lamb + ) + t = interpolate_fn( + log_alpha.reshape((-1, 1)), + torch.flip(self.log_alpha_array.to(lamb.device), [1]), + torch.flip(self.t_array.to(lamb.device), [1]), + ) + return t.reshape((-1,)) + else: + log_alpha = -0.5 * torch.logaddexp(-2.0 * lamb, torch.zeros((1,)).to(lamb)) + t_fn = ( + lambda log_alpha_t: torch.arccos( + torch.exp(log_alpha_t + self.cosine_log_alpha_0) + ) + * 2.0 + * (1.0 + self.cosine_s) + / math.pi + - self.cosine_s + ) + t = t_fn(log_alpha) + return t diff --git a/core/scheduling/unipc/unipc.py b/core/scheduling/unipc/unipc.py new file mode 100644 index 000000000..23f8a34b1 --- /dev/null +++ b/core/scheduling/unipc/unipc.py @@ -0,0 +1,609 @@ +# Taken from https://github.com/wl-zhao/UniPC/blob/main/uni_pc.py#L236 + +import logging +from typing import Callable, List, Optional, Tuple + +import torch +from tqdm import tqdm + +from ..types import AlgorithmType, Method, SkipType, Variant +from .noise_scheduler import NoiseScheduleVP +from .utility import expand_dims + +logger = logging.getLogger(__name__) + + +class UniPC: + "https://github.com/wl-zhao/UniPC/blob/main/uni_pc.py#L236" + model: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] + noise_schedule: NoiseScheduleVP + + correcting_x0_fn: Optional[ + Callable[[torch.Tensor, Optional[torch.Tensor]], torch.Tensor] + ] + correcting_xt_fn: Optional[Callable] + + dynamic_thresholding_ratio: float + thresholding_max_val: float + + variant: Variant + predict_x0: bool + + def __init__( + self, + model_fn: Callable, + noise_schedule: NoiseScheduleVP, + algorithm_type: AlgorithmType = "data_prediction", + correcting_x0_fn: Optional[Callable] = None, + correcting_xt_fn: Optional[Callable] = None, + thresholding_max_val: float = 1.0, + dynamic_thresholding_ratio: float = 0.995, + variant: Variant = "bh1", + ) -> None: + """Construct a UniPC. + + We support both data_prediction and noise_prediction. + """ + self.model = lambda x, t: model_fn(x, t.expand((x.shape[0]))) + self.noise_schedule = noise_schedule + + if correcting_x0_fn == "dynamic_thresholding": + self.correcting_x0_fn = self.dynamic_thresholding_fn + else: + self.correcting_x0_fn = correcting_x0_fn + + self.correcting_xt_fn = correcting_xt_fn + self.dynamic_thresholding_ratio = dynamic_thresholding_ratio + self.thresholding_max_val = thresholding_max_val + + self.variant = variant + self.predict_x0 = algorithm_type == "data_prediction" + + def dynamic_thresholding_fn( + self, + x0: torch.Tensor, + t: Optional[torch.Tensor] = None, + ) -> torch.Tensor: + """ + The dynamic thresholding method. + """ + dims = x0.dim() + p = self.dynamic_thresholding_ratio + s = torch.quantile(torch.abs(x0).reshape((x0.shape[0], -1)), p, dim=1) + s = expand_dims( + torch.maximum( + s, self.thresholding_max_val * torch.ones_like(s).to(s.device) + ), + dims, + ) + x0 = torch.clamp(x0, -s, s) / s + return x0 + + def noise_prediction_fn(self, x: torch.Tensor, t: torch.Tensor) -> torch.Tensor: + """ + Return the noise prediction model. + """ + return self.model(x, t) + + def data_prediction_fn(self, x: torch.Tensor, t: torch.Tensor) -> torch.Tensor: + """ + Return the data prediction model (with corrector). + """ + noise = self.noise_prediction_fn(x, t) + alpha_t, sigma_t = self.noise_schedule.marginal_alpha( + t + ), self.noise_schedule.marginal_std(t) + x0 = (x - sigma_t * noise) / alpha_t + if self.correcting_x0_fn is not None: + x0 = self.correcting_x0_fn(x0, None) + return x0 + + def model_fn(self, x: torch.Tensor, t: torch.Tensor) -> torch.Tensor: + """ + Convert the model to the noise prediction model or the data prediction model. + """ + if self.predict_x0: + return self.data_prediction_fn(x, t) + else: + return self.noise_prediction_fn(x, t) + + def get_time_steps( + self, + skip_type: SkipType, + t_T: int, + t_0: int, + N: int, + device: torch.device, + ) -> torch.Tensor: + """Compute the intermediate time steps for sampling.""" + if skip_type == "logSNR": + lambda_T = self.noise_schedule.marginal_lambda(torch.tensor(t_T).to(device)) + lambda_0 = self.noise_schedule.marginal_lambda(torch.tensor(t_0).to(device)) + logSNR_steps = torch.linspace( + lambda_T.cpu().item(), lambda_0.cpu().item(), N + 1 + ).to(device) + return self.noise_schedule.inverse_lambda(logSNR_steps) + elif skip_type == "time_uniform": + return torch.linspace(t_T, t_0, N + 1).to(device) + elif skip_type == "time_quadratic": + t_order = 2 + t = ( + torch.linspace(t_T ** (1.0 / t_order), t_0 ** (1.0 / t_order), N + 1) + .pow(t_order) + .to(device) + ) + return t + else: + raise ValueError( + "Unsupported skip_type {}, need to be 'logSNR' or 'time_uniform' or 'time_quadratic'".format( + skip_type + ) + ) + + def get_orders_and_timesteps_for_singlestep_solver( + self, + steps: int, + order: int, + skip_type: SkipType, + t_T: int, + t_0: int, + device: torch.device, + ) -> Tuple[torch.Tensor, List[int]]: + """ + Get the order of each step for sampling by the singlestep DPM-Solver. + """ + if order == 3: + K = steps // 3 + 1 + if steps % 3 == 0: + orders = [ + 3, + ] * ( + K - 2 + ) + [2, 1] + elif steps % 3 == 1: + orders = [ + 3, + ] * ( + K - 1 + ) + [1] + else: + orders = [ + 3, + ] * ( + K - 1 + ) + [2] + elif order == 2: + if steps % 2 == 0: + K = steps // 2 + orders = [ + 2, + ] * K + else: + K = steps // 2 + 1 + orders = [ + 2, + ] * ( + K - 1 + ) + [1] + elif order == 1: + K = steps + orders = [ + 1, + ] * steps + else: + raise ValueError("'order' must be '1' or '2' or '3'.") + if skip_type == "logSNR": + # To reproduce the results in DPM-Solver paper + timesteps_outer = self.get_time_steps(skip_type, t_T, t_0, K, device) + else: + timesteps_outer = self.get_time_steps(skip_type, t_T, t_0, steps, device)[ + torch.cumsum( + torch.tensor( + [ + 0, + ] + + orders + ), + 0, + ).to(device) + ] + return timesteps_outer, orders + + def denoise_to_zero_fn(self, x: torch.Tensor, s: torch.Tensor) -> torch.Tensor: + """ + Denoise at the final step, which is equivalent to solve the ODE from lambda_s to infty by first-order discretization. + """ + return self.data_prediction_fn(x, s) + + def multistep_uni_pc_update( + self, + x: torch.Tensor, + model_prev_list: List[torch.Tensor], + t_prev_list: List[torch.Tensor], + t: torch.Tensor, + order: int, + **kwargs, + ) -> Tuple[torch.Tensor, torch.Tensor]: + if len(t.shape) == 0: + t = t.view(-1) + if "bh" in self.variant: + return self.multistep_uni_pc_bh_update( + x, model_prev_list, t_prev_list, t, order, **kwargs + ) + else: + assert self.variant == "vary_coeff" + return self.multistep_uni_pc_vary_update( + x, model_prev_list, t_prev_list, t, order, **kwargs + ) + + def multistep_uni_pc_vary_update( + self, + x: torch.Tensor, + model_prev_list: List[torch.Tensor], + t_prev_list: List[torch.Tensor], + t: torch.Tensor, + order: int, + use_corrector: bool = True, + ) -> Tuple[torch.Tensor, torch.Tensor]: + logger.debug( + f"using unified predictor-corrector with order {order} (solver type: vary coeff)" + ) + ns = self.noise_schedule + assert order <= len(model_prev_list) + + # first compute rks + t_prev_0 = t_prev_list[-1] + lambda_prev_0 = ns.marginal_lambda(t_prev_0) + lambda_t = ns.marginal_lambda(t) + model_prev_0 = model_prev_list[-1] + sigma_prev_0, sigma_t = ns.marginal_std(t_prev_0), ns.marginal_std(t) + log_alpha_t = ns.marginal_log_mean_coeff(t) + alpha_t = torch.exp(log_alpha_t) + + h = lambda_t - lambda_prev_0 + + rks = [] + D1s = [] + for i in range(1, order): + t_prev_i = t_prev_list[-(i + 1)] + model_prev_i = model_prev_list[-(i + 1)] + lambda_prev_i = ns.marginal_lambda(t_prev_i) + rk = (lambda_prev_i - lambda_prev_0) / h + rks.append(rk) + D1s.append((model_prev_i - model_prev_0) / rk) + + rks.append(1.0) + rks = torch.tensor(rks, device=x.device) + + K = len(rks) + # build C matrix + C = [] + + col = torch.ones_like(rks) + for k in range(1, K + 1): + C.append(col) + col = col * rks / (k + 1) + C = torch.stack(C, dim=1) + + if len(D1s) > 0: + D1s = torch.stack(D1s, dim=1) # (B, K) + C_inv_p = torch.linalg.inv(C[:-1, :-1]) + A_p = C_inv_p + + if use_corrector: + logger.debug("using corrector") + C_inv = torch.linalg.inv(C) + A_c = C_inv + + hh = -h if self.predict_x0 else h + h_phi_1 = torch.expm1(hh) + h_phi_ks = [] + factorial_k = 1 + h_phi_k = h_phi_1 + for k in range(1, K + 2): + h_phi_ks.append(h_phi_k) + h_phi_k = h_phi_k / hh - 1 / factorial_k + factorial_k *= k + 1 + + model_t = None + if self.predict_x0: + x_t_ = sigma_t / sigma_prev_0 * x - alpha_t * h_phi_1 * model_prev_0 + # now predictor + x_t = x_t_ + if len(D1s) > 0: + # compute the residuals for predictor + for k in range(K - 1): + x_t = x_t - alpha_t * h_phi_ks[k + 1] * torch.einsum( + "bkchw,k->bchw", D1s, A_p[k] # type: ignore + ) + # now corrector + if use_corrector: + model_t = self.model_fn(x_t, t) + D1_t = model_t - model_prev_0 + x_t = x_t_ + k = 0 + for k in range(K - 1): + x_t = x_t - alpha_t * h_phi_ks[k + 1] * torch.einsum( + "bkchw,k->bchw", D1s, A_c[k][:-1] # type: ignore + ) + x_t = x_t - alpha_t * h_phi_ks[K] * (D1_t * A_c[k][-1]) # type: ignore + else: + log_alpha_prev_0, log_alpha_t = ns.marginal_log_mean_coeff( + t_prev_0 + ), ns.marginal_log_mean_coeff(t) + x_t_ = (torch.exp(log_alpha_t - log_alpha_prev_0)) * x - ( + sigma_t * h_phi_1 + ) * model_prev_0 + # now predictor + x_t = x_t_ + if len(D1s) > 0: + # compute the residuals for predictor + for k in range(K - 1): + x_t = x_t - sigma_t * h_phi_ks[k + 1] * torch.einsum( + "bkchw,k->bchw", D1s, A_p[k] # type: ignore + ) + # now corrector + if use_corrector: + model_t = self.model_fn(x_t, t) + D1_t = model_t - model_prev_0 + x_t = x_t_ + k = 0 + for k in range(K - 1): + x_t = x_t - sigma_t * h_phi_ks[k + 1] * torch.einsum( + "bkchw,k->bchw", D1s, A_c[k][:-1] # type: ignore + ) + x_t = x_t - sigma_t * h_phi_ks[K] * (D1_t * A_c[k][-1]) # type: ignore + return x_t, model_t # type: ignore + + def multistep_uni_pc_bh_update( + self, + x: torch.Tensor, + model_prev_list: List[torch.Tensor], + t_prev_list: List[torch.Tensor], + t: torch.Tensor, + order: int, + x_t: Optional[torch.Tensor] = None, + use_corrector: bool = True, + ) -> Tuple[torch.Tensor, torch.Tensor]: + logger.debug( + f"using unified predictor-corrector with order {order} (solver type: B(h))" + ) + ns = self.noise_schedule + assert order <= len(model_prev_list) + + # first compute rks + t_prev_0 = t_prev_list[-1] + lambda_prev_0 = ns.marginal_lambda(t_prev_0) + lambda_t = ns.marginal_lambda(t) + model_prev_0 = model_prev_list[-1] + sigma_prev_0, sigma_t = ns.marginal_std(t_prev_0), ns.marginal_std(t) + log_alpha_prev_0, log_alpha_t = ns.marginal_log_mean_coeff( + t_prev_0 + ), ns.marginal_log_mean_coeff(t) + alpha_t = torch.exp(log_alpha_t) + + h = lambda_t - lambda_prev_0 + + rks = [] + D1s = [] + for i in range(1, order): + t_prev_i = t_prev_list[-(i + 1)] + model_prev_i = model_prev_list[-(i + 1)] + lambda_prev_i = ns.marginal_lambda(t_prev_i) + rk = (lambda_prev_i - lambda_prev_0) / h + rks.append(rk) + D1s.append((model_prev_i - model_prev_0) / rk) + + rks.append(1.0) + rks = torch.tensor(rks, device=x.device) + + R = [] + b = [] + + hh = -h if self.predict_x0 else h + h_phi_1 = torch.expm1(hh) # h\phi_1(h) = e^h - 1 + h_phi_k = h_phi_1 / hh - 1 + + factorial_i = 1 + + if self.variant == "bh1": + B_h = hh + elif self.variant == "bh2": + B_h = torch.expm1(hh) + else: + raise NotImplementedError() + + for i in range(1, order + 1): + R.append(torch.pow(rks, i - 1)) + b.append(h_phi_k * factorial_i / B_h) + factorial_i *= i + 1 + h_phi_k = h_phi_k / hh - 1 / factorial_i + + R = torch.stack(R) + b = torch.cat(b) + + # now predictor + use_predictor = len(D1s) > 0 and x_t is None + if len(D1s) > 0: + D1s = torch.stack(D1s, dim=1) # (B, K) + if x_t is None: + # for order 2, we use a simplified version + if order == 2: + rhos_p = torch.tensor([0.5], device=b.device) + else: + rhos_p = torch.linalg.solve(R[:-1, :-1], b[:-1]) + else: + D1s = None + + if use_corrector: + logger.debug("using corrector") + # for order 1, we use a simplified version + if order == 1: + rhos_c = torch.tensor([0.5], device=b.device) + else: + rhos_c = torch.linalg.solve(R, b) + + model_t = None + if self.predict_x0: + x_t_ = sigma_t / sigma_prev_0 * x - alpha_t * h_phi_1 * model_prev_0 + + if x_t is None: + if use_predictor: + pred_res = torch.einsum("k,bkchw->bchw", rhos_p, D1s) # type: ignore + else: + pred_res = 0 + x_t = x_t_ - alpha_t * B_h * pred_res + + if use_corrector: + model_t = self.model_fn(x_t, t) + if D1s is not None: + corr_res = torch.einsum("k,bkchw->bchw", rhos_c[:-1], D1s) # type: ignore + else: + corr_res = 0 + D1_t = model_t - model_prev_0 + x_t = x_t_ - alpha_t * B_h * (corr_res + rhos_c[-1] * D1_t) # type: ignore + else: + x_t_ = ( + torch.exp(log_alpha_t - log_alpha_prev_0) * x + - sigma_t * h_phi_1 * model_prev_0 + ) + if x_t is None: + if use_predictor: + pred_res = torch.einsum("k,bkchw->bchw", rhos_p, D1s) # type: ignore + else: + pred_res = 0 + x_t = x_t_ - sigma_t * B_h * pred_res + + if use_corrector: + model_t = self.model_fn(x_t, t) + if D1s is not None: + corr_res = torch.einsum("k,bkchw->bchw", rhos_c[:-1], D1s) # type: ignore + else: + corr_res = 0 + D1_t = model_t - model_prev_0 + x_t = x_t_ - sigma_t * B_h * (corr_res + rhos_c[-1] * D1_t) # type: ignore + return x_t, model_t # type: ignore + + def sample( + self, + x: torch.Tensor, + timesteps: torch.Tensor, + steps: int = 20, + t_start: Optional[int] = None, + t_end: Optional[int] = None, + order: int = 2, + method: Method = "multistep", + lower_order_final: bool = True, + denoise_to_zero: bool = False, + callback: Optional[Callable[[int, torch.Tensor, torch.Tensor], None]] = None, + callback_steps: int = 1, + return_intermediate: bool = False, + ) -> torch.Tensor: + """ + Compute the sample at time `t_end` by UniPC, given the initial `x` at time `t_start`. + """ + t_0 = 1.0 / self.noise_schedule.total_N if t_end is None else t_end + t_T = self.noise_schedule.T if t_start is None else t_start + assert ( + t_0 > 0 and t_T > 0 + ), "Time range needs to be greater than 0. For discrete-time DPMs, it needs to be in [1 / N, 1], where N is the length of betas array" + if return_intermediate: + assert method in [ + "multistep", + "singlestep", + "singlestep_fixed", + ], "Cannot use adaptive solver when saving intermediate values" + if self.correcting_xt_fn is not None: + assert method in [ + "multistep", + "singlestep", + "singlestep_fixed", + ], "Cannot use adaptive solver when correcting_xt_fn is not None" + device = x.device + intermediates = [] + + progress_bar = tqdm(total=steps) + + with torch.no_grad(): + if method == "multistep": + assert steps >= order + assert timesteps.shape[0] - 1 == steps + # Init the initial values. + step = 0 + t = timesteps[step] + t_prev_list = [t] + model_prev_list = [self.model_fn(x, t)] + if self.correcting_xt_fn is not None: + x = self.correcting_xt_fn(x, t, step) + if return_intermediate: + intermediates.append(x) + + # Init the first `order` values by lower order multistep UniPC. + for step in range(1, order): + t = timesteps[step] + x, model_x = self.multistep_uni_pc_update( + x, model_prev_list, t_prev_list, t, step, use_corrector=True # type: ignore + ) + if model_x is None: + model_x = self.model_fn(x, t) + if self.correcting_xt_fn is not None: + x = self.correcting_xt_fn(x, t, step) + if return_intermediate: + intermediates.append(x) + t_prev_list.append(t) + model_prev_list.append(model_x) + if callback is not None and step % callback_steps == 0: + callback(step, t, x) + progress_bar.update() + + # Compute the remaining values by `order`-th order multistep DPM-Solver. + for step in range(order, steps + 1): + t = timesteps[step] + if lower_order_final: + step_order = min(order, steps + 1 - step) + else: + step_order = order + if step == steps: + logger.debug("do not run corrector at the last step") + use_corrector = False + else: + use_corrector = True + x, model_x = self.multistep_uni_pc_update( + x, + model_prev_list, + t_prev_list, + t, + step_order, # type: ignore + use_corrector=use_corrector, + ) + if self.correcting_xt_fn is not None: + x = self.correcting_xt_fn(x, t, step) + if return_intermediate: + intermediates.append(x) + for i in range(order - 1): + t_prev_list[i] = t_prev_list[i + 1] + model_prev_list[i] = model_prev_list[i + 1] + t_prev_list[-1] = t + # We do not need to evaluate the final model value. + if step < steps: + if model_x is None: + model_x = self.model_fn(x, t) + model_prev_list[-1] = model_x + if callback is not None and step % callback_steps == 0: + callback(step, t, x) + progress_bar.update() + else: + raise ValueError(f"Got wrong method {method}") + + if denoise_to_zero: + t = torch.ones((1,)).to(device) * t_0 + x = self.denoise_to_zero_fn(x, t) + if self.correcting_xt_fn is not None: + x = self.correcting_xt_fn(x, t, step + 1) + if return_intermediate: + intermediates.append(x) + progress_bar.close() + if return_intermediate: + return x, intermediates # type: ignore + else: + return x diff --git a/core/scheduling/unipc/utility.py b/core/scheduling/unipc/utility.py new file mode 100644 index 000000000..a14cb15c1 --- /dev/null +++ b/core/scheduling/unipc/utility.py @@ -0,0 +1,70 @@ +import torch + +############################################################# +# other utility functions +############################################################# + + +def interpolate_fn(x, xp, yp): + """ + A piecewise linear function y = f(x), using xp and yp as keypoints. + We implement f(x) in a differentiable way (i.e. applicable for autograd). + The function f(x) is well-defined for all x-axis. (For x beyond the bounds of xp, we use the outmost points of xp to define the linear function.) + + Args: + x: PyTorch tensor with shape [N, C], where N is the batch size, C is the number of channels (we use C = 1 for DPM-Solver). + xp: PyTorch tensor with shape [C, K], where K is the number of keypoints. + yp: PyTorch tensor with shape [C, K]. + Returns: + The function values f(x), with shape [N, C]. + """ + N, K = x.shape[0], xp.shape[1] + all_x = torch.cat([x.unsqueeze(2), xp.unsqueeze(0).repeat((N, 1, 1))], dim=2) + sorted_all_x, x_indices = torch.sort(all_x, dim=2) + x_idx = torch.argmin(x_indices, dim=2) + cand_start_idx = x_idx - 1 + start_idx = torch.where( + torch.eq(x_idx, 0), + torch.tensor(1, device=x.device), + torch.where( + torch.eq(x_idx, K), + torch.tensor(K - 2, device=x.device), + cand_start_idx, + ), + ) + end_idx = torch.where( + torch.eq(start_idx, cand_start_idx), start_idx + 2, start_idx + 1 + ) + start_x = torch.gather(sorted_all_x, dim=2, index=start_idx.unsqueeze(2)).squeeze(2) + end_x = torch.gather(sorted_all_x, dim=2, index=end_idx.unsqueeze(2)).squeeze(2) + start_idx2 = torch.where( + torch.eq(x_idx, 0), + torch.tensor(0, device=x.device), + torch.where( + torch.eq(x_idx, K), + torch.tensor(K - 2, device=x.device), + cand_start_idx, + ), + ) + y_positions_expanded = yp.unsqueeze(0).expand(N, -1, -1) + start_y = torch.gather( + y_positions_expanded, dim=2, index=start_idx2.unsqueeze(2) + ).squeeze(2) + end_y = torch.gather( + y_positions_expanded, dim=2, index=(start_idx2 + 1).unsqueeze(2) + ).squeeze(2) + cand = start_y + (x - start_x) * (end_y - start_y) / (end_x - start_x) + return cand + + +def expand_dims(v, dims): + """ + Expand the tensor `v` to the dim `dims`. + + Args: + `v`: a PyTorch tensor with shape [N]. + `dim`: a `int`. + Returns: + a PyTorch tensor with shape [N, 1, 1, ..., 1] and the total dimension is `dims`. + """ + return v[(...,) + (None,) * (dims - 1)] diff --git a/core/shared.py b/core/shared.py index 010c3c1f4..8530d61b6 100644 --- a/core/shared.py +++ b/core/shared.py @@ -1,6 +1,6 @@ import asyncio from concurrent.futures import ThreadPoolExecutor -from typing import TYPE_CHECKING, List, Optional, Union +from typing import TYPE_CHECKING, List, Optional, Union, Literal if TYPE_CHECKING: from uvicorn import Server @@ -10,6 +10,7 @@ amd: bool = False all_gpus: List = [] current_model: Union["PyTorchStableDiffusion", None] = None +current_method: Literal[None, "txt2img", "img2img", "inpainting", "controlnet"] = None current_steps: int = 50 current_done_steps: int = 0 asyncio_loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() @@ -19,3 +20,5 @@ uvicorn_server: Optional["Server"] = None uvicorn_loop: Optional[asyncio.AbstractEventLoop] = None asyncio_tasks: List[asyncio.Task] = [] + +api_port: int = 5003 diff --git a/core/shared_dependent.py b/core/shared_dependent.py index bbb32455a..bcfafe424 100644 --- a/core/shared_dependent.py +++ b/core/shared_dependent.py @@ -1,6 +1,5 @@ from typing import TYPE_CHECKING, Any, Optional, Tuple, Union -from core.config import config from core.files import CachedModelList from core.gpu import GPU @@ -22,7 +21,7 @@ disable_hardware_warning: bool = False cached_model_list = CachedModelList() -gpu = GPU(config.api.device_id) +gpu = GPU() cached_controlnet_preprocessor: Union[ None, "CannyDetector", diff --git a/core/thread.py b/core/thread.py index ddc404815..2fccbb7b4 100644 --- a/core/thread.py +++ b/core/thread.py @@ -43,7 +43,7 @@ def run(self): ) try: self._return = target(*self._args, **self._kwargs) # type: ignore - except Exception as err: # pylint: disable=broad-except + except Exception as err: self._err = err else: logger.debug( @@ -56,7 +56,7 @@ def run(self): ).result() else: raise Exception("Asyncio loop not found") - except Exception as err: # pylint: disable=broad-except + except Exception as err: self._err = err def join(self, *args) -> Tuple[Union[Any, None], Union[Exception, None]]: diff --git a/core/types.py b/core/types.py index 73436b295..0bca173c1 100644 --- a/core/types.py +++ b/core/types.py @@ -1,5 +1,4 @@ from dataclasses import dataclass, field -from enum import Enum from typing import Any, Dict, List, Literal, Optional, Tuple, Union from uuid import uuid4 @@ -16,6 +15,7 @@ from diffusers.schedulers.scheduling_utils import KarrasDiffusionSchedulers InferenceBackend = Literal["PyTorch", "AITemplate", "ONNX"] +SigmaScheduler = Literal["automatic", "karras", "exponential", "polyexponential", "vp"] Backend = Literal[ "PyTorch", "AITemplate", @@ -27,6 +27,7 @@ "VAE", "Upscaler", ] +ImageFormats = Literal["png", "jpeg", "webp"] @dataclass @@ -40,21 +41,6 @@ class Job: flags: Dict[str, Dict] = field(default_factory=dict) -class SupportedModel(Enum): - "Enum of models supported by the API" - - AnythingV3 = "Linaqruf/anything-v3.0" - StableDiffusion2_1 = "stabilityai/stable-diffusion-2-1" - OpenJourney = "prompthero/openjourney" - DreamlikeDiffusion = "dreamlike-art/dreamlike-diffusion-1.0" - DreamlikePhotoreal = "dreamlike-art/dreamlike-photoreal-2.0" - Protogen5_8_Anime = "darkstorm2150/Protogen_x5.8_Official_Release" - SynthWave = "ItsJayQz/SynthwavePunk-v2" - InkpunkDiffusion = "Envvi/Inkpunk-Diffusion" - Protogen5_3_Realism = "darkstorm2150/Protogen_v5.3_Official_Release" - AnythingV4 = "andite/anything-v4.0" - - @dataclass class InterrogationData: "Dataclass for the data of an interrogation request" @@ -70,7 +56,7 @@ class Txt2imgData: "Dataclass for the data of a txt2img request" prompt: str - scheduler: KarrasDiffusionSchedulers + scheduler: Union[str, KarrasDiffusionSchedulers] id: str = field(default_factory=lambda: uuid4().hex) negative_prompt: str = field(default="") width: int = field(default=512) @@ -78,10 +64,11 @@ class Txt2imgData: steps: int = field(default=25) guidance_scale: float = field(default=7) self_attention_scale: float = field(default=0.0) - use_karras_sigmas: bool = field(default=False) + sigmas: SigmaScheduler = field(default="automatic") seed: int = field(default=0) batch_size: int = field(default=1) batch_count: int = field(default=1) + sampler_settings: Dict = field(default_factory=dict) @dataclass @@ -90,7 +77,7 @@ class Img2imgData: prompt: str image: Union[bytes, str] - scheduler: KarrasDiffusionSchedulers + scheduler: Union[str, KarrasDiffusionSchedulers] id: str = field(default_factory=lambda: uuid4().hex) negative_prompt: str = field(default="") width: int = field(default=512) @@ -98,11 +85,12 @@ class Img2imgData: steps: int = field(default=25) guidance_scale: float = field(default=7) self_attention_scale: float = field(default=0.0) - use_karras_sigmas: bool = field(default=False) + sigmas: SigmaScheduler = field(default="automatic") seed: int = field(default=0) batch_size: int = field(default=1) batch_count: int = field(default=1) strength: float = field(default=0.6) + sampler_settings: Dict = field(default_factory=dict) @dataclass @@ -112,7 +100,7 @@ class InpaintData: prompt: str image: Union[bytes, str] mask_image: Union[bytes, str] - scheduler: KarrasDiffusionSchedulers + scheduler: Union[str, KarrasDiffusionSchedulers] id: str = field(default_factory=lambda: uuid4().hex) negative_prompt: str = field(default="") width: int = field(default=512) @@ -120,10 +108,11 @@ class InpaintData: steps: int = field(default=25) guidance_scale: float = field(default=7) self_attention_scale: float = field(default=0.0) - use_karras_sigmas: bool = field(default=False) + sigmas: SigmaScheduler = field(default="automatic") seed: int = field(default=0) batch_size: int = field(default=1) batch_count: int = field(default=1) + sampler_settings: Dict = field(default_factory=dict) @dataclass @@ -132,7 +121,7 @@ class ControlNetData: prompt: str image: Union[bytes, str] - scheduler: KarrasDiffusionSchedulers + scheduler: Union[str, KarrasDiffusionSchedulers] controlnet: str id: str = field(default_factory=lambda: uuid4().hex) negative_prompt: str = field(default="") @@ -140,12 +129,13 @@ class ControlNetData: height: int = field(default=512) steps: int = field(default=25) guidance_scale: float = field(default=7) - use_karras_sigmas: bool = field(default=False) + sigmas: SigmaScheduler = field(default="automatic") seed: int = field(default=0) batch_size: int = field(default=1) batch_count: int = field(default=1) controlnet_conditioning_scale: float = field(default=1.0) detection_resolution: int = field(default=512) + sampler_settings: Dict = field(default_factory=dict) canny_low_threshold: int = field(default=100) canny_high_threshold: int = field(default=200) @@ -325,7 +315,9 @@ class DeleteModelRequest: "Dataclass for requesting a deletion of a model" model_path: str - model_type: Literal["pytorch", "lora", "textual-inversion", "aitemplate"] + model_type: Literal[ + "models", "lora", "textual-inversion", "lycoris", "vae", "aitemplate" + ] @dataclass @@ -333,7 +325,9 @@ class Capabilities: "Dataclass for capabilities of a GPU" # ["cpu", "cuda", "directml", "mps", "xpu", "vulkan"] - supported_backends: List[str] = field(default_factory=lambda: ["cpu"]) + supported_backends: List[List[str]] = field( + default_factory=lambda: [["CPU", "cpu"]] + ) # ["float16", "float32", "bfloat16"] supported_precisions_gpu: List[str] = field(default_factory=lambda: ["float32"]) # ["float16", "float32", "bfloat16"] @@ -355,3 +349,5 @@ class Capabilities: # Ampere+ (>=8.6) has_tensorfloat: bool = False + + hypertile_available: bool = False diff --git a/core/utils.py b/core/utils.py index c8fbe4ecf..5b4345936 100644 --- a/core/utils.py +++ b/core/utils.py @@ -7,7 +7,7 @@ from enum import Enum from io import BytesIO from pathlib import Path -from typing import Any, Callable, Coroutine, Dict, List, Literal, Optional, Tuple, Union +from typing import Any, Callable, Coroutine, Dict, List, Optional, Tuple, Union import requests from PIL import Image @@ -15,11 +15,12 @@ from tqdm import tqdm from core.thread import ThreadWithReturnValue +from core.types import ImageFormats logger = logging.getLogger(__name__) -def unwrap_enum(possible_enum: Union[Enum, Any]): +def unwrap_enum(possible_enum: Union[Enum, Any]) -> Any: "Unwrap an enum to its value" if isinstance(possible_enum, Enum): @@ -43,11 +44,13 @@ def get_grid_dimension(length: int) -> Tuple[int, int]: return cols, rows -def convert_image_to_stream(image: Image.Image, quality: int = 95) -> BytesIO: +def convert_image_to_stream( + image: Image.Image, quality: int = 95, _format: ImageFormats = "webp" +) -> BytesIO: "Convert an image to a stream of bytes" stream = BytesIO() - image.save(stream, format="webp", quality=quality) + image.save(stream, format=_format, quality=quality) stream.seek(0) return stream @@ -74,24 +77,23 @@ def convert_to_image( return im - return image + if isinstance(image, Image.Image): + return image + + raise ValueError(f"Type {type(image)} not supported yet") def convert_image_to_base64( image: Image.Image, quality: int = 95, - image_format: Literal["png", "webp"] = "png", + image_format: ImageFormats = "webp", prefix_js: bool = True, ) -> str: "Convert an image to a base64 string" stream = convert_image_to_stream(image, quality=quality) if prefix_js: - prefix = ( - f"data:image/{image_format};base64," - if image_format == "png" - else "data:image/webp;base64," - ) + prefix = f"data:image/{image_format};base64," else: prefix = "" return prefix + base64.b64encode(stream.read()).decode("utf-8") @@ -147,7 +149,7 @@ def image_grid(imgs: List[Image.Image]): def convert_images_to_base64_grid( images: List[Image.Image], quality: int = 95, - image_format: Literal["png", "webp"] = "png", + image_format: ImageFormats = "png", ) -> str: "Convert a list of images to a list of base64 strings" @@ -184,12 +186,20 @@ def download_file(url: str, file: Path, add_filename: bool = False): session.mount("https://", HTTPAdapter(max_retries=retries)) with session.get(url, stream=True, timeout=30) as r: - file_name = r.headers["Content-Disposition"].split('"')[1] + try: + file_name = r.headers["Content-Disposition"].split('"')[1] + except KeyError: + file_name = url.split("/")[-1] + if add_filename: file = file / file_name total = int(r.headers["Content-Length"]) - logger.info(f"Downloading {file_name} into {file.as_posix()}") + if file.exists(): + logger.debug(f"File {file.as_posix()} already exists, skipping") + return file + + logger.info(f"Downloading {file_name} into {file.as_posix()}") # AFAIK Windows doesn't like big buffers s = (64 if os.name == "nt" else 1024) * 1024 with open(file, mode="wb+") as f: diff --git a/data/autofill/quality.txt b/data/autofill/quality.txt new file mode 100644 index 000000000..6576d0b05 --- /dev/null +++ b/data/autofill/quality.txt @@ -0,0 +1,18 @@ +masterpiece +unreal engine +8k +octane render +cinematic +concept art +photorealistic +vibrant colors +high quality +ultra realistic +highly detailed +highres +CG +wallpaper +best illustration +delicate +beautiful +fine detail diff --git a/data/themes/dark.json b/data/themes/dark.json new file mode 100644 index 000000000..ebe327e14 --- /dev/null +++ b/data/themes/dark.json @@ -0,0 +1,21 @@ +{ + "volta": { + "base": "dark", + "blur": "6px", + "backgroundImage": "https://raw.githubusercontent.com/VoltaML/voltaML-fast-stable-diffusion/2cf7a8abf1e5035a0dc57a67cd13505653c492f6/static/volta-dark-background.svg" + }, + "common": { + "fontSize": "15px", + "fontWeight": "600" + }, + "Card": { + "color": "rgba(24, 24, 28, 0.6)" + }, + "Layout": { + "color": "rgba(16, 16, 20, 0.6)", + "siderColor": "rgba(24, 24, 28, 0)" + }, + "Tabs": { + "colorSegment": "rgba(24, 24, 28, 0.6)" + } +} \ No newline at end of file diff --git a/data/themes/dark_flat.json b/data/themes/dark_flat.json new file mode 100644 index 000000000..24455b0ed --- /dev/null +++ b/data/themes/dark_flat.json @@ -0,0 +1,10 @@ +{ + "volta": { + "base": "dark", + "blur": "0" + }, + "common": { + "fontSize": "15px", + "fontWeight": "600" + } +} \ No newline at end of file diff --git a/data/themes/light.json b/data/themes/light.json new file mode 100644 index 000000000..755c6d9fa --- /dev/null +++ b/data/themes/light.json @@ -0,0 +1,21 @@ +{ + "volta": { + "base": "light", + "blur": "6px", + "backgroundImage": "https://raw.githubusercontent.com/VoltaML/voltaML-fast-stable-diffusion/2cf7a8abf1e5035a0dc57a67cd13505653c492f6/static/volta-light-background.svg" + }, + "common": { + "fontSize": "15px", + "fontWeight": "600" + }, + "Card": { + "color": "rgba(255, 255, 255, 0.6)" + }, + "Layout": { + "color": "rgba(255, 255, 255, 0.6)", + "siderColor": "rgba(24, 24, 28, 0)" + }, + "Tabs": { + "colorSegment": "rgba(255, 255, 255, 0.6)" + } +} \ No newline at end of file diff --git a/data/themes/light_flat.json b/data/themes/light_flat.json new file mode 100644 index 000000000..6b7474b83 --- /dev/null +++ b/data/themes/light_flat.json @@ -0,0 +1,10 @@ +{ + "volta": { + "base": "light", + "blur": "0" + }, + "common": { + "fontSize": "15px", + "fontWeight": "600" + } +} \ No newline at end of file diff --git a/dockerfile b/dockerfile index 45aae1a3a..bd42c1d43 100644 --- a/dockerfile +++ b/dockerfile @@ -28,6 +28,8 @@ RUN rm -rf frontend/node_modules RUN rm -rf /root/.cache RUN rm -rf /usr/local/share/.cache +RUN rm -rf /AITemplate/3rdparty/cutlass/docs +RUN rm -rf /AITemplate/3rdparty/cutlass/media RUN pip uninstall -y triton # Run the server diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.mts similarity index 71% rename from docs/.vitepress/config.ts rename to docs/.vitepress/config.mts index 0968242a0..3f17d5a97 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.mts @@ -1,5 +1,6 @@ import { defineConfig } from "vitepress"; +// https://vitepress.dev/reference/site-config export default defineConfig({ title: "VoltaML", description: "Easy to use, yet feature-rich WebUI", @@ -7,6 +8,12 @@ export default defineConfig({ appearance: "dark", lastUpdated: true, base: "/voltaML-fast-stable-diffusion/", + locales: { + root: { + lang: "en-US", + label: "English", + }, + }, head: [ [ "link", @@ -23,6 +30,18 @@ export default defineConfig({ content: "/voltaML-fast-stable-diffusion/volta-og-image.webp", }, ], + [ + "script", + { + async: "", + src: "https://www.googletagmanager.com/gtag/js?id=G-WZPQL8HDP0", + }, + ], + [ + "script", + {}, + "window.dataLayer = window.dataLayer || [];\nfunction gtag(){dataLayer.push(arguments);}\ngtag('js', new Date());\ngtag('config', 'G-WZPQL8HDP0');", + ], ], themeConfig: { search: { @@ -31,6 +50,7 @@ export default defineConfig({ nav: [ { text: "Home", link: "/" }, { text: "Docs", link: "/getting-started/introduction" }, + { text: "Changelog", link: "/changelog" }, ], editLink: { pattern: @@ -60,23 +80,40 @@ export default defineConfig({ text: "Introduction", items: [ { text: "Introduction", link: "/getting-started/introduction" }, - { text: "Features", link: "/getting-started/features" }, - { text: "First image", link: "/getting-started/first-image" }, ], collapsed: false, }, { text: "Installation", items: [ - { text: "Windows", link: "/installation/windows" }, - { text: "Linux", link: "/installation/linux" }, - { text: "WSL", link: "/installation/wsl" }, - { text: "Docker", link: "/installation/docker" }, - { text: "Old", link: "/installation/old" }, - { text: "xFormers", link: "/installation/xformers" }, + { + text: "Local", + items: [ + { text: "Windows", link: "/installation/windows" }, + { text: "Linux", link: "/installation/linux" }, + { text: "WSL", link: "/installation/wsl" }, + { text: "Docker", link: "/installation/docker" }, + ], + }, + { + text: "Cloud", + items: [{ text: "Vast.ai", link: "/installation/vast" }], + }, + { + text: "Extra", + items: [ + { text: "Old", link: "/installation/old" }, + { text: "xFormers", link: "/installation/xformers" }, + ], + }, ], collapsed: false, }, + { + text: "Guides", + items: [{ text: "First image", link: "/guides/first-image" }], + collapsed: false, + }, { text: "Basics", items: [ @@ -87,18 +124,17 @@ export default defineConfig({ text: "AITemplate Acceleration", link: "/basics/aitemplate", }, + { text: "Autoload", link: "/basics/autoload" }, ], collapsed: false, }, { text: "WebUI", items: [ - { text: "Models", link: "/webui/models" }, + { text: "Themes", link: "/webui/themes" }, { text: "Image to Image", link: "/webui/img2img" }, - { text: "Extra", link: "/webui/extra" }, { text: "Downloading Models", link: "/webui/download" }, { text: "Image browser", link: "/webui/imagebrowser" }, - { text: "Convert", link: "/webui/convert" }, ], collapsed: false, }, @@ -116,6 +152,7 @@ export default defineConfig({ { text: "Settings", items: [{ text: "Settings", link: "/settings/settings" }], + collapsed: false, }, { text: "Experimental", @@ -125,7 +162,7 @@ export default defineConfig({ { text: "API", items: [{ text: "API", link: "/api/" }], - collapsed: false, + collapsed: true, }, { text: "Developers", @@ -144,7 +181,7 @@ export default defineConfig({ link: "/developers/testing", }, ], - collapsed: false, + collapsed: true, }, { text: "Troubleshooting", @@ -159,7 +196,7 @@ export default defineConfig({ link: "/troubleshooting/docker", }, ], - collapsed: false, + collapsed: true, }, ], }, diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css deleted file mode 100644 index 7f10197c9..000000000 --- a/docs/.vitepress/theme/custom.css +++ /dev/null @@ -1,13 +0,0 @@ -:root { - --vp-c-brand: rgb(255, 118, 60); - --vp-c-brand-light: rgb(255, 118, 60); - --vp-c-brand-dark: rgb(237, 70, 150); - - --vp-button-brand-bg: rgb(237, 70, 150); - --vp-button-brand-border: rgb(237, 70, 150); - --vp-home-hero-name-color: rgb(237, 70, 150); - --vp-button-brand-hover-border: rgb(255, 118, 60); - --vp-button-brand-hover-bg: rgb(255, 118, 60); - --vp-button-brand-active-bg: rgb(237, 70, 150); - --vp-button-brand-active-border: rgb(237, 70, 150); -} diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index c495bc1b8..bdd506d91 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,4 +1,16 @@ -import DefaultTheme from "vitepress/theme"; -import "./custom.css"; +// https://vitepress.dev/guide/custom-theme +import { h } from 'vue' +import Theme from 'vitepress/theme' +import './style.css' -export default DefaultTheme; +export default { + extends: Theme, + Layout: () => { + return h(Theme.Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + }) + }, + enhanceApp({ app, router, siteData }) { + // ... + } +} diff --git a/docs/.vitepress/theme/style.css b/docs/.vitepress/theme/style.css new file mode 100644 index 000000000..6b01c953e --- /dev/null +++ b/docs/.vitepress/theme/style.css @@ -0,0 +1,139 @@ +/** + * Customize default theme styling by overriding CSS variables: + * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css + */ + +/** + * Colors + * + * Each colors have exact same color scale system with 3 levels of solid + * colors with different brightness, and 1 soft color. + * + * - `XXX-1`: The most solid color used mainly for colored text. It must + * satisfy the contrast ratio against when used on top of `XXX-soft`. + * + * - `XXX-2`: The color used mainly for hover state of the button. + * + * - `XXX-3`: The color for solid background, such as bg color of the button. + * It must satisfy the contrast ratio with pure white (#ffffff) text on + * top of it. + * + * - `XXX-soft`: The color used for subtle background such as custom container + * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors + * on top of it. + * + * The soft color must be semi transparent alpha channel. This is crucial + * because it allows adding multiple "soft" colors on top of each other + * to create a accent, such as when having inline code block inside + * custom containers. + * + * - `default`: The color used purely for subtle indication without any + * special meanings attched to it such as bg color for menu hover state. + * + * - `brand`: Used for primary brand colors, such as link text, button with + * brand theme, etc. + * + * - `tip`: Used to indicate useful information. The default theme uses the + * brand color for this by default. + * + * - `warning`: Used to indicate warning to the users. Used in custom + * container, badges, etc. + * + * - `danger`: Used to show error, or dangerous message to the users. Used + * in custom container, badges, etc. + * -------------------------------------------------------------------------- */ + + :root { + --vp-c-default-1: var(--vp-c-gray-1); + --vp-c-default-2: var(--vp-c-gray-2); + --vp-c-default-3: var(--vp-c-gray-3); + --vp-c-default-soft: var(--vp-c-gray-soft); + + --vp-c-brand-1: var(--vp-c-indigo-1); + --vp-c-brand-2: var(--vp-c-indigo-2); + --vp-c-brand-3: var(--vp-c-indigo-3); + --vp-c-brand-soft: var(--vp-c-indigo-soft); + + --vp-c-tip-1: var(--vp-c-brand-1); + --vp-c-tip-2: var(--vp-c-brand-2); + --vp-c-tip-3: var(--vp-c-brand-3); + --vp-c-tip-soft: var(--vp-c-brand-soft); + + --vp-c-warning-1: var(--vp-c-yellow-1); + --vp-c-warning-2: var(--vp-c-yellow-2); + --vp-c-warning-3: var(--vp-c-yellow-3); + --vp-c-warning-soft: var(--vp-c-yellow-soft); + + --vp-c-danger-1: var(--vp-c-red-1); + --vp-c-danger-2: var(--vp-c-red-2); + --vp-c-danger-3: var(--vp-c-red-3); + --vp-c-danger-soft: var(--vp-c-red-soft); +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-border: transparent; + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand-3); + --vp-button-brand-hover-border: transparent; + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-2); + --vp-button-brand-active-border: transparent; + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: var(--vp-c-brand-1); +} + +/** + * Component: Home + * -------------------------------------------------------------------------- */ + +:root { + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #bd34fe 30%, + #41d1ff + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #bd34fe 50%, + #47caff 50% + ); + --vp-home-hero-image-filter: blur(40px); +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(72px); + } +} + +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-tip-border: transparent; + --vp-custom-block-tip-text: var(--vp-c-text-1); + --vp-custom-block-tip-bg: var(--vp-c-brand-soft); + --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); +} + +/** + * Component: Algolia + * -------------------------------------------------------------------------- */ + +.DocSearch { + --docsearch-primary-color: var(--vp-c-brand-1) !important; +} + diff --git a/docs/api/index.md b/docs/api/index.md index 3a59520d3..2c8adaf86 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -30,4 +30,5 @@ class KarrasDiffusionSchedulers(Enum): KDPM2AncestralDiscreteScheduler = 11 DEISMultistepScheduler = 12 UniPCMultistepScheduler = 13 + DPMSolverSDEScheduler = 14 ``` diff --git a/docs/basics/autoload.md b/docs/basics/autoload.md new file mode 100644 index 000000000..7bf68ce03 --- /dev/null +++ b/docs/basics/autoload.md @@ -0,0 +1,23 @@ +# Autoload + +Volta can be configured to automatically load a model (or even multiple models at once), Textual Inversion or even a custom VAE for a specific model. + +::: tip +To see autoload in action, save the settings and restart Volta. You should see the model loading automatically. +::: + +## How to use + +Navigate to `Settings > API > Autoload` and select a model that you would like to load at the startup. Feel free to select multiple models at once if you have enough GPU memory / Offload enabled. + +::: warning +To save settings, click either on the `Save settings` button or navigate to other tab. Notification will appear if the settings were saved successfully. +::: + +## Autoloading Textual Inversion + +Autoloading Textual inversion will apply to all models. You can check the status in the Model Loader. + +## Autoloading custom VAE + +Custom VAEs are loaded depending on the model and should be applied automatically. You can check this behavior in the Model Loader. diff --git a/docs/changelog.md b/docs/changelog.md new file mode 100644 index 000000000..c8628d110 --- /dev/null +++ b/docs/changelog.md @@ -0,0 +1,56 @@ +# Changelog + +## v0.4.0 + +### Biggest Changes + +- Hi-res fix for AITemplate +- Model and VAE autoloading +- Partial support for Kdiffusion samplers (might be broken in some cases - controlnet, hi-res...) +- Hypertile support (https://github.com/tfernd/HyperTile) +- Theme overhaul + +### All Changes + +- Added docs for Vast.ai +- Better Settings UI +- Updated Model Manager +- Garbage Collection improvements +- Hi-res fix for AITemplate +- Added DPMSolverSDE diffusers sampler +- Model autoloading +- Partial support for Kdiffusion samplers (might be broken in some cases - controlnet, hi-res...) +- Tag autofill +- New SendTo UI that is context aware +- Fixed symlink deletion bug for models +- New documentation theme +- New sigma types for Kdiffusion (exponential, polyexponential, VP) +- Image upload should now display correct dimensions +- Fixed WebP crashes in some cases +- Remote LoRA support `` +- Fix some CORS issues +- Hypertile support (https://github.com/tfernd/HyperTile) +- Fixed uvicorn logging issues +- Fixed update checker +- Added some extra tooltips to the UI +- Sampler config override for people that hate their free time +- Bumped dependency versions +- Image browser entries should get sorted on server, removing the need for layout shift in the UI +- Cleaned up some old documentation +- Transfer project from PyLint to Ruff +- Github Actions CI for Ruff linting +- Theme overhaul +- Fixed NaiveUI ThemeEditor +- Sort models in Model Loader +- Console logs now accessible in the UI +- ...and probably a lot more that I already forgot + +### Contributors + +- gabe56f (https://github.com/gabe56f) +- Stax124 (https://github.com/Stax124) +- Katehuuh (https://github.com/Katehuuh) + +### Additional Notes + +Thank you for 850 stars on GitHub and 500 Discord members ❤️ diff --git a/docs/getting-started/features.md b/docs/getting-started/features.md deleted file mode 100644 index 97cae347d..000000000 --- a/docs/getting-started/features.md +++ /dev/null @@ -1,26 +0,0 @@ -# Features - -- ✅ Feature available and supported -- ❌ Feature not available yet -- 🚧 Feature is in the development or testing phase - -| Feature | PyTorch | AITemplate | Long Weighted Prompt (PyTorch Only) | -| ---------- | ------- | ---------- | ----------------------------------- | -| Txt2Img | ✅ | ✅ | ✅ | -| Img2Img | ✅ | ✅ | ✅ | -| ControlNet | ✅ | ✅ | ✅ | -| Inpainting | ✅ | 🚧 | ✅ | -| SD Upscale | 🚧 | ❌ | ❌ | - -| Feature | Availability | -| ------------------------------- | ------------ | -| Discord bot | ✅ | -| Real-ESRGAN | ✅ | -| Latent Upscale | ✅ | -| Documentation | ✅ | -| Image Browser | ✅ | -| Model Conversion | ✅ | -| Model Training | ❌ | -| Configuration | ✅ | -| UI Performance monitoring | ✅ | -| Loading from CKPT / Safetensors | ✅ | diff --git a/docs/getting-started/introduction.md b/docs/getting-started/introduction.md index 3d7350831..aed1717f2 100644 --- a/docs/getting-started/introduction.md +++ b/docs/getting-started/introduction.md @@ -1,15 +1,15 @@ # Welcome to VoltaML

-Made with ❤️ by Stax124 +Made with ❤️ by Stax124 and Gabe

::: danger IMPORTANT -For all Pull Requests, please make sure to target the `experimental` branch. The `main` branch is only used for releases (or in some situations, issues and PRs with high priority - marked as `Fast-Forward`). +For all Pull Requests, please make sure to target the `experimental` branch. The `main` branch is only used for releases (or in some situations, PRs with high priority - marked as `Fast-Forward`) ::: ::: info -Documentation is still a work in progress, if you have any questions, feel free to join our [Discord server](https://discord.gg/pY5SVyHmWm) or open an issue on GitHub. +Feel free to join our [Discord server](https://discord.gg/pY5SVyHmWm) ::: Stable Diffusion WebUI accelerated by AITemplate @@ -24,11 +24,9 @@ There is also a dedicated section to the **Discord bot, API** and a section for - Clean and simple Web UI - Supports PyTorch as well as AITemplate for inference - Support for Windows and Linux -- xFormers supported out of the box -- GPU cluster load balancing +- xFormers support - Discord bot -- Documented API -- Clean source code that should be easy to understand +- Built-in CivitAI browser ## Speed comparison diff --git a/docs/getting-started/first-image.md b/docs/guides/first-image.md similarity index 100% rename from docs/getting-started/first-image.md rename to docs/guides/first-image.md diff --git a/docs/index.md b/docs/index.md index f2a0d6824..a00cd9260 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,8 +21,8 @@ features: title: Modern and elegant details: Modern UI designed for simplicity and ease of use. - icon: 🔧 - title: Upcoming - Volta Manager - details: Volta Manager will streamline the installation process and will make it even easier to use VoltaML. + title: Volta Manager + details: Volta Manager streamlines the installation process and makes it even easier to use VoltaML. - icon: ❤‍🔥 title: Open Source details: VoltaML is open source and free to use. Forever. diff --git a/docs/installation/vast.md b/docs/installation/vast.md new file mode 100644 index 000000000..b13f184c2 --- /dev/null +++ b/docs/installation/vast.md @@ -0,0 +1,41 @@ +# Vast.ai + +::: info +This is a tutorial for using Volta on Vast AI. It is a cloud computing platform that allows you to easily rent GPU instances. +::: + +::: info Transparency notice +I get about 2.25% of the money you spend on Vast.ai if you use the link below. We use Vast for testing Volta on other GPUs all the time so I can personally recommend them, but it is only fair that I let you know about that. +::: + +Probably the simplest way to get Volta up and running within a few minutes in the cloud. + +## Step 1: Create an account + +Create an account on [Vast.ai](https://vast.ai/). You will get few cents as credit when you add your card details (good for about half an hour of instance time). I would recommend only using Credit system in case you accidentally leave your instance running for too long. + +## Step 2: Create an instance + +Click on this ->[link](https://cloud.vast.ai/?ref_id=58604&template_id=49e308f241db18d3656d69f0af75f60d)<- to open a new window that should have Volta pre-selected as the template. I would recommend selecting a server with decent download speed. + +![Vast search](../static/installation/vastai-search.webp) + +## Step 3: Wait for the instance to be ready and connect to it + +Once you have created the instance, you should see it on the instances page. Wait for the instance to be ready. This should take about 5 minutes (on a decent internet connection). + +Click the `Open` button to connect to the instance. This will open a new tab with Volta (might take about 30s to load properly - just be patient and reload the page if it doesn't load). You can now use Volta as you would on your local machine. + +![Vast search](../static/installation/vastai-instances.webp) + +## Step 4: Stop the instance + +Once you are done using the instance, click the `Delete` button to get rid of the instance (mark `4` on the image above). This will stop the billing for the instance. You can start the instance again by following the steps above again. + +You can technically stop the instance - but you will be billed for the storage - so it is recommended to delete the instance. + +## Syncing data between your instances + +Vast allows you to connect your cloud buckets to the instance to be used as volumes. This is a great way to sync data between your local machine and the instance. You can find more information about this [here](https://vast.ai/docs/gpu-instances/cloud-sync). + +I personally do not use it as I do not care about outputs that I generate for testing but you might find it useful. diff --git a/docs/installation/windows.md b/docs/installation/windows.md index 44283d210..af48b5538 100644 --- a/docs/installation/windows.md +++ b/docs/installation/windows.md @@ -13,9 +13,29 @@ | ONNX | 🚧 | ::: warning -For AITemplate acceleration support, please navigate to [WSL2 Installation Guide](/installation/wsl). +For [AITemplate acceleration](/basics/aitemplate) support, please navigate to [WSL2 Installation Guide](/installation/wsl). ::: +## Pre-requisites + +### Python + +- Python 3.10 or higher (3.11 is used for testing and development, probably the best option if you do not have any other Python versions installed) +- Installed from official [Python website](https://www.python.org/downloads/) (not from Microsoft Store) +- Added to PATH (done by checking the "Add python.exe to PATH" box during installation) automatically or [manually](https://datatofish.com/add-python-to-windows-path/) + +::: tip +You can check if you have Python correctly on path by opening CMD/PowerShell and typing `python --version`. If you get an error, you will need to add Python to path. Also make sure that you have the correct version of Python installed. +::: + +::: warning +If you installed Python from the Microsoft Store, you will need to uninstall it and install it from the official website. +::: + +### Git + +- Installed from official [Git website](https://git-scm.com/downloads) + ## Installation ### 1. Create a new folder for the project diff --git a/docs/static/installation/vastai-instances.webp b/docs/static/installation/vastai-instances.webp new file mode 100644 index 000000000..290569703 Binary files /dev/null and b/docs/static/installation/vastai-instances.webp differ diff --git a/docs/static/installation/vastai-search.webp b/docs/static/installation/vastai-search.webp new file mode 100644 index 000000000..c142fc799 Binary files /dev/null and b/docs/static/installation/vastai-search.webp differ diff --git a/docs/webui/convert.md b/docs/webui/convert.md deleted file mode 100644 index 60c9e77d3..000000000 --- a/docs/webui/convert.md +++ /dev/null @@ -1 +0,0 @@ -# Convert (WIP) diff --git a/docs/webui/extra.md b/docs/webui/extra.md deleted file mode 100644 index bf26aae3e..000000000 --- a/docs/webui/extra.md +++ /dev/null @@ -1,3 +0,0 @@ -# Extra (WIP) - -This page is about the extra features of AITemplate. These include Upscalers, Color correction and more. diff --git a/docs/webui/models.md b/docs/webui/models.md deleted file mode 100644 index c0b8e2f63..000000000 --- a/docs/webui/models.md +++ /dev/null @@ -1,25 +0,0 @@ -# Models - -VoltaML support all of these methods for loading models: - -| | Diffusers | Checkpoint (.ckpt) | Safetensors (.safetensors) | AITemplate | -| ------------------ | --------- | ---------------------- | -------------------------- | ----------- | -| AITemplate compile | Yes | No | No | Unavailable | -| Float 16 | Yes | Only if already pruned | Only if already pruned | Yes | -| Float 32 | Yes | Yes | Yes | No | - -Both `Checkpoint` and `Safetensors` are loaded with float type that they were saved with. Fix needs to be done in the `diffusers` package - follow [this](https://github.com/huggingface/diffusers/issues/2755) thread for more info. - -## AITemplate compilation - -The AITemplate compilation is a process that traces the model and creates more optimized version of it. This process can be started on the `Accelerate` page. - -## Float16 / Float32 - -This refers to the precision of the model. Float16 is a half precision model, which is faster to load and run, but less accurate. Float32 is a full precision model, which is slower to load and run, but more accurate. - -It can be also seen on the filesizes of the models. Float16 models are 2x smaller than Float32 models (2GB compared to 4GB). - -## Model conversion - -Work in progress. diff --git a/docs/webui/themes.md b/docs/webui/themes.md new file mode 100644 index 000000000..8cb00d46b --- /dev/null +++ b/docs/webui/themes.md @@ -0,0 +1,54 @@ +# Themes + +Volta includes 4 themes as of the time of writing: + +- Dark (_default_) +- Dark Flat +- Light +- Light Flat + +Dark and Light have neon-ish vibes, while Flat themes are more minimalistic and lack a background. + +## Changing the theme + +Theme can be changed on the settings page: `Settings > Theme > Theme` + +## Importing themes + +Any themes that you download should be placed in the `data/themes` directory and they should be picked up automatically (refresh the UI page for them to show up there). + +## Creating a theme + +I would recommend setting `Settings > Theme > Enable Theme Editor` to `true` to make the process of creating a theme easier. +This should enable the theme editor inside the UI, you should be able to see it in the bottom right corner of the screen. + +Changes are cached, so I would recommend pressing the `Clear All Variables` button first to just be sure. + +Now, you can start changing the variables and see the changes in real time. Once you are happy with the result, you can press the `Export` button and save the theme to a file. + +Then, open either `data/themes/dark.json` or `data/themes/light.json` and copy the `volta` object to your theme file. + +```json{3-7} +{ + // Feel free to change these settings once you copy them! + "volta": { + "base": "dark", + "blur": "6px", + "backgroundImage": "https://raw.githubusercontent.com/VoltaML/voltaML-fast-stable-diffusion/2cf7a8abf1e5035a0dc57a67cd13505653c492f6/static/volta-dark-background.svg" + }, + "common": { + "fontSize": "15px", + "fontWeight": "600" + }, + "Card": { + "color": "rgba(24, 24, 28, 0.6)" + }, + "Layout": { + "color": "rgba(16, 16, 20, 0.6)", + "siderColor": "rgba(24, 24, 28, 0)" + }, + "Tabs": { + "colorSegment": "rgba(24, 24, 28, 0.6)" + } +} +``` diff --git a/frontend/dist/assets/404View.js b/frontend/dist/assets/404View.js new file mode 100644 index 000000000..f6622bc62 --- /dev/null +++ b/frontend/dist/assets/404View.js @@ -0,0 +1,25 @@ +import { d as defineComponent, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, c3 as NResult, i as NCard } from "./index.js"; +const _hoisted_1 = { style: { "width": "100vw", "height": "100vh", "display": "flex", "align-items": "center", "justify-content": "center", "backdrop-filter": "blur(4px)" } }; +const _sfc_main = /* @__PURE__ */ defineComponent({ + __name: "404View", + setup(__props) { + return (_ctx, _cache) => { + return openBlock(), createElementBlock("div", _hoisted_1, [ + createVNode(unref(NCard), { style: { "max-width": "40vw", "border-radius": "12px" } }, { + default: withCtx(() => [ + createVNode(unref(NResult), { + status: "404", + title: "You got lucky, this page doesn't exist!", + description: "Next time, there will be a rickroll.", + size: "large" + }) + ]), + _: 1 + }) + ]); + }; + } +}); +export { + _sfc_main as default +}; diff --git a/frontend/dist/assets/AccelerateView.js b/frontend/dist/assets/AccelerateView.js index 0979740db..98b147476 100644 --- a/frontend/dist/assets/AccelerateView.js +++ b/frontend/dist/assets/AccelerateView.js @@ -1,7 +1,6 @@ -import { Y as cB, $ as cM, X as c, Z as cE, a0 as iconSwitchTransition, aq as cNotM, d as defineComponent, Q as useConfig, a7 as useRtl, a5 as useTheme, T as provide, D as h, aC as flatten, aD as getSlot, V as createInjectionKey, be as stepsLight, a3 as inject, a_ as throwError, c as computed, a9 as useThemeClass, aB as resolveWrappedSlot, at as resolveSlot, ab as NIconSwitchTransition, a8 as createKey, W as call, au as NBaseIcon, bf as FinishedIcon, bg as ErrorIcon, b as useMessage, u as useState, E as ref, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, j as NSpace, i as NCard, n as createBaseVNode, r as NSelect, F as NButton, m as createTextVNode, bd as NModal, s as serverUrl, a as useSettings, v as createBlock, H as NTabPane, I as NTabs } from "./index.js"; -import { N as NSlider } from "./Slider.js"; +import { Q as cB, ab as cM, aa as c, at as cE, aT as iconSwitchTransition, ac as cNotM, d as defineComponent, S as useConfig, ag as useRtl, T as useTheme, a3 as provide, C as h, aw as flatten, ax as getSlot, P as createInjectionKey, bg as stepsLight, R as inject, a_ as throwError, c as computed, W as useThemeClass, av as resolveWrappedSlot, ah as resolveSlot, aI as NIconSwitchTransition, al as createKey, $ as call, ai as NBaseIcon, bh as FinishedIcon, bi as ErrorIcon, b as useMessage, u as useState, D as ref, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, j as NSpace, i as NCard, n as createBaseVNode, x as NSelect, E as NButton, m as createTextVNode, bd as NModal, s as serverUrl, a as useSettings, v as createBlock, G as NTabPane, H as NTabs } from "./index.js"; +import { N as NSlider, a as NSwitch } from "./Switch.js"; import { N as NInputNumber } from "./InputNumber.js"; -import { N as NSwitch } from "./Switch.js"; const style = cB("steps", ` width: 100%; display: flex; @@ -68,6 +67,7 @@ const style = cB("steps", ` position: absolute; left: 0; top: 0; + white-space: nowrap; font-size: var(--n-indicator-index-font-size); width: var(--n-indicator-icon-size); height: var(--n-indicator-icon-size); @@ -284,252 +284,6 @@ const NStep = defineComponent({ ); } }); -const _hoisted_1$2 = { style: { "margin": "16px" } }; -const _hoisted_2$2 = { class: "flex-container" }; -const _hoisted_3$2 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Width", -1); -const _hoisted_4$2 = { class: "flex-container" }; -const _hoisted_5$2 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Height", -1); -const _hoisted_6$2 = { class: "flex-container" }; -const _hoisted_7$2 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Size", -1); -const _hoisted_8$1 = { class: "flex-container" }; -const _hoisted_9$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CPU Threads (affects RAM usage)", -1); -const _hoisted_10$1 = { class: "flex-container" }; -const _hoisted_11$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Model", -1); -const _sfc_main$3 = /* @__PURE__ */ defineComponent({ - __name: "AITemplateAccelerate", - setup(__props) { - var _a, _b; - const message = useMessage(); - const global = useState(); - const width = ref(512); - const height = ref(512); - const batchSize = ref(1); - const model = ref(""); - const threads = ref(8); - const building = ref(false); - const showUnloadModal = ref(false); - const modelOptions = computed(() => { - const options = []; - for (const model2 of global.state.models) { - if (model2.backend === "PyTorch" && model2.valid && !model2.name.endsWith(".safetensors") && !model2.name.endsWith(".ckpt")) { - options.push({ - label: model2.name, - value: model2.path - }); - } - } - return options; - }); - model.value = ((_b = (_a = modelOptions.value[0]) == null ? void 0 : _a.value) == null ? void 0 : _b.toString()) ?? ""; - const accelerateUnload = async () => { - try { - await fetch(`${serverUrl}/api/models/unload-all`, { - method: "POST" - }); - showUnloadModal.value = false; - await accelerate(); - } catch { - showUnloadModal.value = false; - message.error("Failed to unload, check the console for more info."); - } - }; - const accelerate = async () => { - showUnloadModal.value = false; - building.value = true; - await fetch(`${serverUrl}/api/generate/generate-aitemplate`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - model_id: model.value, - width: width.value, - height: height.value, - batch_size: batchSize.value, - threads: threads.value - }) - }).then(() => { - building.value = false; - }).catch(() => { - building.value = false; - message.error("Failed to accelerate, check the console for more info."); - }); - }; - return (_ctx, _cache) => { - return openBlock(), createElementBlock("div", _hoisted_1$2, [ - createVNode(unref(NCard), { title: "Acceleration progress (around 20 minutes)" }, { - default: withCtx(() => [ - createVNode(unref(NSpace), { - vertical: "", - justify: "center" - }, { - default: withCtx(() => [ - createVNode(unref(NSteps), null, { - default: withCtx(() => [ - createVNode(unref(NStep), { - title: "CLIP", - status: unref(global).state.aitBuildStep.clip - }, null, 8, ["status"]), - createVNode(unref(NStep), { - title: "UNet", - status: unref(global).state.aitBuildStep.unet - }, null, 8, ["status"]), - createVNode(unref(NStep), { - title: "ControlNet UNet", - status: unref(global).state.aitBuildStep.controlnet_unet - }, null, 8, ["status"]), - createVNode(unref(NStep), { - title: "VAE", - status: unref(global).state.aitBuildStep.vae - }, null, 8, ["status"]), - createVNode(unref(NStep), { - title: "Cleanup", - status: unref(global).state.aitBuildStep.cleanup - }, null, 8, ["status"]) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NCard), { style: { "margin-top": "16px" } }, { - default: withCtx(() => [ - createBaseVNode("div", _hoisted_2$2, [ - _hoisted_3$2, - createVNode(unref(NSlider), { - value: width.value, - "onUpdate:value": _cache[0] || (_cache[0] = ($event) => width.value = $event), - min: 128, - max: 2048, - step: 64, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: width.value, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => width.value = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 64, - min: 128, - max: 2048 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_4$2, [ - _hoisted_5$2, - createVNode(unref(NSlider), { - value: height.value, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => height.value = $event), - min: 128, - max: 2048, - step: 64, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: height.value, - "onUpdate:value": _cache[3] || (_cache[3] = ($event) => height.value = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 64, - min: 128, - max: 2048 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_6$2, [ - _hoisted_7$2, - createVNode(unref(NSlider), { - value: batchSize.value, - "onUpdate:value": _cache[4] || (_cache[4] = ($event) => batchSize.value = $event), - min: 1, - max: 9, - step: 1, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: batchSize.value, - "onUpdate:value": _cache[5] || (_cache[5] = ($event) => batchSize.value = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 1, - min: 1, - max: 9 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_8$1, [ - _hoisted_9$1, - createVNode(unref(NSlider), { - value: threads.value, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => threads.value = $event), - step: 1, - min: 1, - max: 64, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: threads.value, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => threads.value = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 1, - min: 1, - max: 64 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_10$1, [ - _hoisted_11$1, - createVNode(unref(NSelect), { - value: model.value, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => model.value = $event), - options: modelOptions.value, - style: { "margin-right": "12px" } - }, null, 8, ["value", "options"]) - ]) - ]), - _: 1 - }), - createVNode(unref(NSpace), { - vertical: "", - justify: "center", - style: { "width": "100%" }, - align: "center" - }, { - default: withCtx(() => [ - createVNode(unref(NButton), { - style: { "margin-top": "16px", "padding": "0 92px" }, - type: "success", - ghost: "", - loading: building.value, - disabled: building.value || modelOptions.value.length === 0, - onClick: _cache[9] || (_cache[9] = ($event) => showUnloadModal.value = true) - }, { - default: withCtx(() => [ - createTextVNode("Accelerate") - ]), - _: 1 - }, 8, ["loading", "disabled"]) - ]), - _: 1 - }), - createVNode(unref(NModal), { - show: showUnloadModal.value, - "onUpdate:show": _cache[10] || (_cache[10] = ($event) => showUnloadModal.value = $event), - preset: "dialog", - title: "Unload other models", - width: "400px", - closable: false, - "show-close": false, - content: "Acceleration can be done with the other models loaded as well, but it will take a lot of resources. It is recommended to unload the other models before accelerating. Do you want to unload the other models?", - "positive-text": "Unload models", - "negative-text": "Keep models", - onPositiveClick: accelerateUnload, - onNegativeClick: accelerate - }, null, 8, ["show"]) - ]); - }; - } -}); const _hoisted_1$1 = { style: { "margin": "16px" } }; const _hoisted_2$1 = { class: "flex-container" }; const _hoisted_3$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Width", -1); @@ -570,6 +324,16 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ return options; }); model.value = ((_b = (_a = modelOptions.value[0]) == null ? void 0 : _a.value) == null ? void 0 : _b.toString()) ?? ""; + const numLoadedModels = computed(() => { + return global.state.models.filter((model2) => model2.state === "loaded").length; + }); + function onAccelerateClick() { + if (numLoadedModels.value >= 1) { + showUnloadModal.value = true; + } else { + accelerate(); + } + } const accelerateUnload = async () => { try { await fetch(`${serverUrl}/api/models/unload-all`, { @@ -730,36 +494,36 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ options: modelOptions.value, style: { "margin-right": "12px" } }, null, 8, ["value", "options"]) - ]) - ]), - _: 1 - }), - createVNode(unref(NSpace), { - vertical: "", - justify: "center", - style: { "width": "100%" }, - align: "center" - }, { - default: withCtx(() => [ - createVNode(unref(NButton), { - style: { "margin-top": "16px", "padding": "0 92px" }, - type: "success", - ghost: "", - loading: building.value, - disabled: building.value || modelOptions.value.length === 0, - onClick: _cache[8] || (_cache[8] = ($event) => showUnloadModal.value = true) + ]), + createVNode(unref(NSpace), { + vertical: "", + justify: "center", + style: { "width": "100%" }, + align: "center" }, { default: withCtx(() => [ - createTextVNode("Accelerate") + createVNode(unref(NButton), { + style: { "margin-top": "16px", "padding": "0 92px" }, + type: "success", + ghost: "", + loading: building.value, + disabled: building.value || modelOptions.value.length === 0, + onClick: onAccelerateClick + }, { + default: withCtx(() => [ + createTextVNode("Accelerate") + ]), + _: 1 + }, 8, ["loading", "disabled"]) ]), _: 1 - }, 8, ["loading", "disabled"]) + }) ]), _: 1 }), createVNode(unref(NModal), { show: showUnloadModal.value, - "onUpdate:show": _cache[9] || (_cache[9] = ($event) => showUnloadModal.value = $event), + "onUpdate:show": _cache[8] || (_cache[8] = ($event) => showUnloadModal.value = $event), preset: "dialog", title: "Unload other models", width: "400px", @@ -788,7 +552,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ var _a, _b; const message = useMessage(); const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const model = ref(""); const building = ref(false); const showUnloadModal = ref(false); @@ -827,9 +591,9 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }, body: JSON.stringify({ model_id: model.value, - quant_dict: conf.data.settings.onnx.quant_dict, - simplify_unet: conf.data.settings.onnx.simplify_unet, - convert_to_fp16: conf.data.settings.onnx.convert_to_fp16 + quant_dict: settings.data.settings.onnx.quant_dict, + simplify_unet: settings.data.settings.onnx.simplify_unet, + convert_to_fp16: settings.data.settings.onnx.convert_to_fp16 }) }).then(() => { building.value = false; @@ -888,15 +652,15 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ createBaseVNode("div", _hoisted_4, [ _hoisted_5, createVNode(unref(NSwitch), { - value: unref(conf).data.settings.onnx.simplify_unet, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(conf).data.settings.onnx.simplify_unet = $event) + value: unref(settings).data.settings.onnx.simplify_unet, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).data.settings.onnx.simplify_unet = $event) }, null, 8, ["value"]) ]), createBaseVNode("div", _hoisted_6, [ _hoisted_7, createVNode(unref(NSwitch), { - value: unref(conf).data.settings.onnx.convert_to_fp16, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(conf).data.settings.onnx.convert_to_fp16 = $event) + value: unref(settings).data.settings.onnx.convert_to_fp16, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).data.settings.onnx.convert_to_fp16 = $event) }, null, 8, ["value"]) ]) ]), @@ -949,12 +713,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ return (_ctx, _cache) => { return openBlock(), createBlock(unref(NTabs), { type: "segment" }, { default: withCtx(() => [ - createVNode(unref(NTabPane), { name: "AITemplate" }, { - default: withCtx(() => [ - createVNode(_sfc_main$3) - ]), - _: 1 - }), createVNode(unref(NTabPane), { name: "Dynamic AITemplate" }, { default: withCtx(() => [ createVNode(_sfc_main$2) diff --git a/frontend/dist/assets/DescriptionsItem.js b/frontend/dist/assets/DescriptionsItem.js index a0f015cec..73b682e49 100644 --- a/frontend/dist/assets/DescriptionsItem.js +++ b/frontend/dist/assets/DescriptionsItem.js @@ -1,4 +1,4 @@ -import { X as c, Y as cB, aq as cNotM, $ as cM, Z as cE, a1 as insideModal, a2 as insidePopover, d as defineComponent, Q as useConfig, a5 as useTheme, c as computed, a9 as useThemeClass, bK as useCompitable, aC as flatten, D as h, aD as getSlot, bL as descriptionsLight, a8 as createKey } from "./index.js"; +import { aa as c, Q as cB, ac as cNotM, ab as cM, at as cE, aU as insideModal, aV as insidePopover, d as defineComponent, S as useConfig, T as useTheme, c as computed, W as useThemeClass, bS as useCompitable, aw as flatten, C as h, aQ as repeat, ax as getSlot, bT as descriptionsLight, al as createKey } from "./index.js"; function getVNodeChildren(vNode, slotName = "default", fallback = []) { const { children } = vNode; if (children !== null && typeof children === "object" && !Array.isArray(children)) { @@ -255,7 +255,14 @@ const NDescriptions = defineComponent({ h( "table", { class: `${mergedClsPrefix}-descriptions-table` }, - h("tbody", null, rows) + h( + "tbody", + null, + labelPlacement === "top" && h("tr", { class: `${mergedClsPrefix}-descriptions-table-row`, style: { + visibility: "collapse" + } }, repeat(compitableColumn * 2, h("td", null))), + rows + ) ) ) ); diff --git a/frontend/dist/assets/ExtraView.js b/frontend/dist/assets/ExtraView.js index d3e083aae..ece0c8f3c 100644 --- a/frontend/dist/assets/ExtraView.js +++ b/frontend/dist/assets/ExtraView.js @@ -1,217 +1,14 @@ -import { _ as _sfc_main$2 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; -import { _ as _sfc_main$3 } from "./ImageOutput.vue_vue_type_script_setup_true_lang.js"; -import { I as ImageUpload } from "./ImageUpload.js"; -import { d as defineComponent, u as useState, a as useSettings, b as useMessage, c as computed, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, N as NGi, i as NCard, j as NSpace, n as createBaseVNode, r as NSelect, q as NTooltip, m as createTextVNode, y as NGrid, L as upscalerOptions, s as serverUrl, A as pushScopeId, B as popScopeId, _ as _export_sfc, v as createBlock, H as NTabPane, I as NTabs } from "./index.js"; -import { N as NSlider } from "./Slider.js"; -import { N as NInputNumber } from "./InputNumber.js"; -import "./SendOutputTo.vue_vue_type_script_setup_true_lang.js"; -import "./TrashBin.js"; -import "./CloudUpload.js"; -const _withScopeId = (n) => (pushScopeId("data-v-2a772a2b"), n = n(), popScopeId(), n); -const _hoisted_1 = { style: { "margin": "0 12px" } }; -const _hoisted_2 = { class: "flex-container" }; -const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Model", -1)); -const _hoisted_4 = { class: "flex-container" }; -const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Scale Factor", -1)); -const _hoisted_6 = { class: "flex-container" }; -const _hoisted_7 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Tile Size", -1)); -const _hoisted_8 = { class: "flex-container" }; -const _hoisted_9 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Tile Padding", -1)); -const _sfc_main$1 = /* @__PURE__ */ defineComponent({ - __name: "Upscale", - setup(__props) { - const global = useState(); - const conf = useSettings(); - const messageHandler = useMessage(); - const imageSelectCallback = (base64Image) => { - conf.data.settings.upscale.image = base64Image; - }; - const upscalerOptionsFull = computed(() => { - const localModels = global.state.models.filter( - (model) => model.backend === "Upscaler" && !(upscalerOptions.map((option) => option.label).indexOf(model.name) !== -1) - ).map((model) => ({ - label: model.name, - value: model.path - })); - return [...upscalerOptions, ...localModels]; - }); - const generate = () => { - global.state.generating = true; - fetch(`${serverUrl}/api/generate/upscale`, { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - data: { - image: conf.data.settings.upscale.image, - upscale_factor: conf.data.settings.upscale.upscale_factor, - model: conf.data.settings.upscale.model, - tile_size: conf.data.settings.upscale.tile_size, - tile_padding: conf.data.settings.upscale.tile_padding - }, - model: conf.data.settings.upscale.model - }) - }).then((res) => { - global.state.generating = false; - res.json().then((data) => { - console.log(data); - global.state.extra.images = [data.images]; - global.state.progress = 0; - global.state.total_steps = 0; - global.state.current_step = 0; - }); - }).catch((err) => { - global.state.generating = false; - messageHandler.error(err); - console.log(err); - }); - }; - return (_ctx, _cache) => { - return openBlock(), createElementBlock("div", _hoisted_1, [ - createVNode(unref(NGrid), { - cols: "1 m:2", - "x-gap": "12", - responsive: "screen" - }, { - default: withCtx(() => [ - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(ImageUpload, { - callback: imageSelectCallback, - preview: unref(conf).data.settings.upscale.image, - style: { "margin-bottom": "12px" }, - onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(conf).data.settings.upscale.image = $event) - }, null, 8, ["preview"]), - createVNode(unref(NCard), { - title: "Settings", - style: { "margin-bottom": "12px" } - }, { - default: withCtx(() => [ - createVNode(unref(NSpace), { - vertical: "", - class: "left-container" - }, { - default: withCtx(() => [ - createBaseVNode("div", _hoisted_2, [ - _hoisted_3, - createVNode(unref(NSelect), { - value: unref(conf).data.settings.upscale.model, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(conf).data.settings.upscale.model = $event), - style: { "margin-right": "12px" }, - filterable: "", - tag: "", - options: upscalerOptionsFull.value - }, null, 8, ["value", "options"]) - ]), - createBaseVNode("div", _hoisted_4, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_5 - ]), - default: withCtx(() => [ - createTextVNode(" TODO ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.upscale.upscale_factor, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(conf).data.settings.upscale.upscale_factor = $event), - min: 1, - max: 4, - step: 0.1, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.upscale.upscale_factor, - "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(conf).data.settings.upscale.upscale_factor = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 1, - max: 4, - step: 0.1 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_6, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_7 - ]), - default: withCtx(() => [ - createTextVNode(" How large each tile should be. Larger tiles will use more memory. 0 will disable tiling. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.upscale.tile_size, - "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(conf).data.settings.upscale.tile_size = $event), - min: 32, - max: 2048, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.upscale.tile_size, - "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(conf).data.settings.upscale.tile_size = $event), - size: "small", - min: 32, - max: 2048, - style: { "min-width": "96px", "width": "96px" } - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_8, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_9 - ]), - default: withCtx(() => [ - createTextVNode(" How much should tiles overlap. Larger padding will use more memory, but image should not have visible seams. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.upscale.tile_padding, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(conf).data.settings.upscale.tile_padding = $event), - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.upscale.tile_padding, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(conf).data.settings.upscale.tile_padding = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" } - }, null, 8, ["value"]) - ]) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(_sfc_main$2, { - generate, - "do-not-disable-generate": "" - }), - createVNode(_sfc_main$3, { - "current-image": unref(global).state.extra.currentImage, - images: unref(global).state.extra.images, - onImageClicked: _cache[8] || (_cache[8] = ($event) => unref(global).state.extra.currentImage = $event) - }, null, 8, ["current-image", "images"]) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]); - }; - } -}); -const Upscale_vue_vue_type_style_index_0_scoped_2a772a2b_lang = ""; -const Upscale = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-2a772a2b"]]); +import { _ as _export_sfc, d as defineComponent, u as useState, e as openBlock, v as createBlock, w as withCtx, h as unref, g as createVNode, G as NTabPane, H as NTabs } from "./index.js"; +const _sfc_main$2 = {}; +function _sfc_render$1(_ctx, _cache) { + return "Autofill manager"; +} +const AutofillManager = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1]]); +const _sfc_main$1 = {}; +function _sfc_render(_ctx, _cache) { + return "Dependency manager"; +} +const DependencyManager = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render]]); const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "ExtraView", setup(__props) { @@ -223,9 +20,21 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(state).state.extra.tab = $event) }, { default: withCtx(() => [ - createVNode(unref(NTabPane), { name: "Upscale" }, { + createVNode(unref(NTabPane), { + tab: "Dependencies", + name: "dependencies" + }, { + default: withCtx(() => [ + createVNode(DependencyManager) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { + tab: "Autofill", + name: "autofill" + }, { default: withCtx(() => [ - createVNode(Upscale) + createVNode(AutofillManager) ]), _: 1 }) diff --git a/frontend/dist/assets/GenerateSection.vue_vue_type_script_setup_true_lang.js b/frontend/dist/assets/GenerateSection.vue_vue_type_script_setup_true_lang.js index 2db4df60f..f0b7d8ad3 100644 --- a/frontend/dist/assets/GenerateSection.vue_vue_type_script_setup_true_lang.js +++ b/frontend/dist/assets/GenerateSection.vue_vue_type_script_setup_true_lang.js @@ -1,4 +1,4 @@ -import { d as defineComponent, e as openBlock, f as createElementBlock, n as createBaseVNode, u as useState, a as useSettings, E as ref, b9 as onMounted, o as onUnmounted, s as serverUrl, v as createBlock, w as withCtx, g as createVNode, h as unref, N as NGi, F as NButton, G as NIcon, m as createTextVNode, y as NGrid, bN as NAlert, x as createCommentVNode, i as NCard } from "./index.js"; +import { d as defineComponent, e as openBlock, f as createElementBlock, n as createBaseVNode, u as useState, a as useSettings, D as ref, b9 as onMounted, o as onUnmounted, s as serverUrl, v as createBlock, w as withCtx, g as createVNode, h as unref, N as NGi, E as NButton, F as NIcon, m as createTextVNode, y as NGrid, bV as NAlert, r as createCommentVNode, i as NCard } from "./index.js"; const _hoisted_1$1 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", @@ -59,7 +59,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ setup(__props) { const props = __props; const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const generateButton = ref(null); onMounted(() => { window.addEventListener("keydown", handleKeyDown); @@ -109,7 +109,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ ref_key: "generateButton", ref: generateButton, onClick: props.generate, - disabled: !props.doNotDisableGenerate && (unref(global).state.generating || ((_a2 = unref(conf).data.settings.model) == null ? void 0 : _a2.name) === "" || ((_b2 = unref(conf).data.settings.model) == null ? void 0 : _b2.name) === void 0), + disabled: !props.doNotDisableGenerate && (unref(global).state.generating || ((_a2 = unref(settings).data.settings.model) == null ? void 0 : _a2.name) === "" || ((_b2 = unref(settings).data.settings.model) == null ? void 0 : _b2.name) === void 0), loading: unref(global).state.generating, style: { "width": "100%" }, ghost: "" @@ -159,7 +159,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - !props.doNotDisableGenerate && (((_a = unref(conf).data.settings.model) == null ? void 0 : _a.name) === "" || ((_b = unref(conf).data.settings.model) == null ? void 0 : _b.name) === void 0) ? (openBlock(), createBlock(unref(NAlert), { + !props.doNotDisableGenerate && (((_a = unref(settings).data.settings.model) == null ? void 0 : _a.name) === "" || ((_b = unref(settings).data.settings.model) == null ? void 0 : _b.name) === void 0) ? (openBlock(), createBlock(unref(NAlert), { key: 0, style: { "margin-top": "12px" }, type: "warning", diff --git a/frontend/dist/assets/Image2ImageView.css b/frontend/dist/assets/Image2ImageView.css index d81842d1d..43c9aff3d 100644 --- a/frontend/dist/assets/Image2ImageView.css +++ b/frontend/dist/assets/Image2ImageView.css @@ -1,37 +1,37 @@ -.image-container img[data-v-24e2a46c] { +.image-container img[data-v-572c2180] { width: 100%; height: 100%; object-fit: contain; overflow: hidden; } -.image-container[data-v-24e2a46c] { +.image-container[data-v-572c2180] { height: 70vh; width: 100%; display: flex; justify-content: center; } -.image-container img[data-v-99f48af3] { +.image-container img[data-v-fe0214b4] { width: 100%; height: 100%; object-fit: contain; overflow: hidden; } -.image-container[data-v-99f48af3] { +.image-container[data-v-fe0214b4] { height: 70vh; width: 100%; display: flex; justify-content: center; } -.hidden-input[data-v-721f7818] { +.hidden-input[data-v-debbedfc] { display: none; } -.utility-button[data-v-721f7818] { +.utility-button[data-v-debbedfc] { margin-right: 8px; } -.file-upload[data-v-721f7818] { +.file-upload[data-v-debbedfc] { appearance: none; background-color: transparent; border: 1px solid #63e2b7; @@ -51,21 +51,21 @@ vertical-align: middle; white-space: nowrap; } -.file-upload[data-v-721f7818]:focus:not(:focus-visible):not(.focus-visible) { +.file-upload[data-v-debbedfc]:focus:not(:focus-visible):not(.focus-visible) { box-shadow: none; outline: none; } -.file-upload[data-v-721f7818]:focus { +.file-upload[data-v-debbedfc]:focus { box-shadow: rgba(46, 164, 79, 0.4) 0 0 0 3px; outline: none; } -.file-upload[data-v-721f7818]:disabled { +.file-upload[data-v-debbedfc]:disabled { background-color: #94d3a2; border-color: rgba(27, 31, 35, 0.1); color: rgba(255, 255, 255, 0.8); cursor: default; } -.image-container[data-v-721f7818] { +.image-container[data-v-debbedfc] { width: 100%; display: flex; justify-content: center; diff --git a/frontend/dist/assets/Image2ImageView.js b/frontend/dist/assets/Image2ImageView.js index c5b98c065..bd8eafb63 100644 --- a/frontend/dist/assets/Image2ImageView.js +++ b/frontend/dist/assets/Image2ImageView.js @@ -1,16 +1,17 @@ -import { _ as _sfc_main$6 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; -import { B as BurnerClock, _ as _sfc_main$4, a as _sfc_main$5, b as _sfc_main$8 } from "./clock.js"; -import { _ as _sfc_main$7 } from "./ImageOutput.vue_vue_type_script_setup_true_lang.js"; +import { _ as _sfc_main$7 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; +import { B as BurnerClock, _ as _sfc_main$5, a as _sfc_main$6, b as _sfc_main$9 } from "./clock.js"; +import { _ as _sfc_main$8 } from "./ImageOutput.vue_vue_type_script_setup_true_lang.js"; import { I as ImageUpload } from "./ImageUpload.js"; -import { d as defineComponent, e as openBlock, f as createElementBlock, n as createBaseVNode, u as useState, a as useSettings, b as useMessage, c as computed, o as onUnmounted, g as createVNode, w as withCtx, h as unref, N as NGi, i as NCard, j as NSpace, k as NInput, p as promptHandleKeyUp, l as promptHandleKeyDown, m as createTextVNode, t as toDisplayString, q as NTooltip, r as NSelect, y as NGrid, z as spaceRegex, s as serverUrl, A as pushScopeId, B as popScopeId, _ as _export_sfc, C as resolveComponent, D as h, E as ref, F as NButton, G as NIcon, v as createBlock, H as NTabPane, I as NTabs } from "./index.js"; +import { _ as _sfc_main$4 } from "./SamplerPicker.vue_vue_type_script_setup_true_lang.js"; +import { d as defineComponent, e as openBlock, f as createElementBlock, n as createBaseVNode, u as useState, a as useSettings, b as useMessage, c as computed, o as onUnmounted, g as createVNode, w as withCtx, h as unref, N as NGi, i as NCard, j as NSpace, k as NInput, p as promptHandleKeyUp, l as promptHandleKeyDown, m as createTextVNode, t as toDisplayString, q as NTooltip, x as NSelect, y as NGrid, z as spaceRegex, s as serverUrl, A as pushScopeId, B as popScopeId, _ as _export_sfc, r as createCommentVNode, C as h, D as ref, E as NButton, F as NIcon, v as createBlock, G as NTabPane, H as NTabs } from "./index.js"; import { v as v4 } from "./v4.js"; -import { N as NSwitch } from "./Switch.js"; -import { N as NSlider } from "./Slider.js"; +import { N as NSlider, a as NSwitch } from "./Switch.js"; import { N as NInputNumber } from "./InputNumber.js"; import "./DescriptionsItem.js"; import "./SendOutputTo.vue_vue_type_script_setup_true_lang.js"; import "./TrashBin.js"; import "./CloudUpload.js"; +import "./Settings.js"; const _hoisted_1$6 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", @@ -144,52 +145,43 @@ const TrashBinSharp = defineComponent({ return openBlock(), createElementBlock("svg", _hoisted_1$3, _hoisted_6$3); } }); -const _withScopeId$2 = (n) => (pushScopeId("data-v-24e2a46c"), n = n(), popScopeId(), n); +const _withScopeId$2 = (n) => (pushScopeId("data-v-572c2180"), n = n(), popScopeId(), n); const _hoisted_1$2 = { style: { "margin": "0 12px" } }; const _hoisted_2$2 = { class: "flex-container" }; -const _hoisted_3$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "150px" } }, "Sampler", -1)); -const _hoisted_4$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using DPMSolverMultistep for the best results . ", -1)); -const _hoisted_5$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("a", { - target: "_blank", - href: "https://docs.google.com/document/d/1n0YozLAUwLJWZmbsx350UD_bwAx3gZMnRuleIZt_R1w" -}, "Learn more", -1)); -const _hoisted_6$2 = { class: "flex-container" }; -const _hoisted_7$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { style: { "width": "120px" } }, "Karras Sigmas", -1)); -const _hoisted_8$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "Works only with KDPM samplers. Ignored by other samplers.", -1)); -const _hoisted_9$2 = { class: "flex-container" }; -const _hoisted_10$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "150px" } }, "ControlNet", -1)); +const _hoisted_3$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "150px" } }, "ControlNet", -1)); +const _hoisted_4$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("a", { href: "https://github.com/lllyasviel/ControlNet-v1-1-nightly?tab=readme-ov-file#controlnet-11" }, "Learn more", -1)); +const _hoisted_5$2 = { class: "flex-container" }; +const _hoisted_6$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1)); +const _hoisted_7$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1)); +const _hoisted_8$2 = { class: "flex-container" }; +const _hoisted_9$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1)); +const _hoisted_10$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1)); const _hoisted_11$2 = { class: "flex-container" }; -const _hoisted_12$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1)); -const _hoisted_13$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1)); -const _hoisted_14$2 = { class: "flex-container" }; -const _hoisted_15$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1)); -const _hoisted_16$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1)); +const _hoisted_12$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1)); +const _hoisted_13$2 = { class: "flex-container" }; +const _hoisted_14$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "ControlNet Conditioning Scale", -1)); +const _hoisted_15$2 = { class: "flex-container" }; +const _hoisted_16$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Detection resolution", -1)); const _hoisted_17$2 = { class: "flex-container" }; -const _hoisted_18$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1)); -const _hoisted_19$2 = { class: "flex-container" }; -const _hoisted_20$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "ControlNet Conditioning Scale", -1)); -const _hoisted_21$2 = { class: "flex-container" }; -const _hoisted_22$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Detection resolution", -1)); -const _hoisted_23$2 = { class: "flex-container" }; -const _hoisted_24$2 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Seed", -1)); -const _hoisted_25$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1)); -const _hoisted_26$1 = { class: "flex-container" }; -const _hoisted_27$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Is Preprocessed", -1)); -const _hoisted_28$1 = { class: "flex-container" }; -const _hoisted_29$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Save Preprocessed", -1)); -const _hoisted_30$1 = { class: "flex-container" }; -const _hoisted_31$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Return Preprocessed", -1)); +const _hoisted_18$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Seed", -1)); +const _hoisted_19$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1)); +const _hoisted_20$1 = { class: "flex-container" }; +const _hoisted_21$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Is Preprocessed", -1)); +const _hoisted_22$1 = { class: "flex-container" }; +const _hoisted_23$1 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Save Preprocessed", -1)); +const _hoisted_24$1 = { class: "flex-container" }; +const _hoisted_25 = /* @__PURE__ */ _withScopeId$2(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Return Preprocessed", -1)); const _sfc_main$3 = /* @__PURE__ */ defineComponent({ __name: "ControlNet", setup(__props) { const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const messageHandler = useMessage(); const promptCount = computed(() => { - return conf.data.settings.controlnet.prompt.split(spaceRegex).length - 1; + return settings.data.settings.controlnet.prompt.split(spaceRegex).length - 1; }); const negativePromptCount = computed(() => { - return conf.data.settings.controlnet.negative_prompt.split(spaceRegex).length - 1; + return settings.data.settings.controlnet.negative_prompt.split(spaceRegex).length - 1; }); const checkSeed = (seed) => { if (seed === -1) { @@ -198,16 +190,16 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ return seed; }; const imageSelectCallback = (base64Image) => { - conf.data.settings.controlnet.image = base64Image; + settings.data.settings.controlnet.image = base64Image; }; const generate = () => { var _a; - if (conf.data.settings.controlnet.seed === null) { + if (settings.data.settings.controlnet.seed === null) { messageHandler.error("Please set a seed"); return; } global.state.generating = true; - const seed = checkSeed(conf.data.settings.controlnet.seed); + const seed = checkSeed(settings.data.settings.controlnet.seed); fetch(`${serverUrl}/api/generate/controlnet`, { method: "POST", headers: { @@ -215,38 +207,38 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }, body: JSON.stringify({ data: { - prompt: conf.data.settings.controlnet.prompt, - image: conf.data.settings.controlnet.image, + prompt: settings.data.settings.controlnet.prompt, + image: settings.data.settings.controlnet.image, id: v4(), - negative_prompt: conf.data.settings.controlnet.negative_prompt, - width: conf.data.settings.controlnet.width, - height: conf.data.settings.controlnet.height, - steps: conf.data.settings.controlnet.steps, - guidance_scale: conf.data.settings.controlnet.cfg_scale, + negative_prompt: settings.data.settings.controlnet.negative_prompt, + width: settings.data.settings.controlnet.width, + height: settings.data.settings.controlnet.height, + steps: settings.data.settings.controlnet.steps, + guidance_scale: settings.data.settings.controlnet.cfg_scale, seed, - batch_size: conf.data.settings.controlnet.batch_size, - batch_count: conf.data.settings.controlnet.batch_count, - controlnet: conf.data.settings.controlnet.controlnet, - controlnet_conditioning_scale: conf.data.settings.controlnet.controlnet_conditioning_scale, - detection_resolution: conf.data.settings.controlnet.detection_resolution, - scheduler: conf.data.settings.controlnet.sampler, - use_karras_sigmas: conf.data.settings.controlnet.use_karras_sigmas, + batch_size: settings.data.settings.controlnet.batch_size, + batch_count: settings.data.settings.controlnet.batch_count, + controlnet: settings.data.settings.controlnet.controlnet, + controlnet_conditioning_scale: settings.data.settings.controlnet.controlnet_conditioning_scale, + detection_resolution: settings.data.settings.controlnet.detection_resolution, + scheduler: settings.data.settings.controlnet.sampler, + sigmas: settings.data.settings.controlnet.sigmas, + sampler_settings: settings.data.settings.sampler_config[settings.data.settings.controlnet.sampler], canny_low_threshold: 100, canny_high_threshold: 200, mlsd_thr_v: 0.1, mlsd_thr_d: 0.1, - is_preprocessed: conf.data.settings.controlnet.is_preprocessed, - save_preprocessed: conf.data.settings.controlnet.save_preprocessed, - return_preprocessed: conf.data.settings.controlnet.return_preprocessed + is_preprocessed: settings.data.settings.controlnet.is_preprocessed, + save_preprocessed: settings.data.settings.controlnet.save_preprocessed, + return_preprocessed: settings.data.settings.controlnet.return_preprocessed }, - model: (_a = conf.data.settings.model) == null ? void 0 : _a.name + model: (_a = settings.data.settings.model) == null ? void 0 : _a.name }) }).then((res) => { if (!res.ok) { throw new Error(res.statusText); } global.state.generating = false; - console.log(res); res.json().then((data) => { global.state.controlnet.images = data.images; global.state.controlnet.currentImage = data.images[0]; @@ -261,10 +253,13 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }).catch((err) => { global.state.generating = false; messageHandler.error(err); - console.log(err); }); }; - const burner = new BurnerClock(conf.data.settings.controlnet, conf, generate); + const burner = new BurnerClock( + settings.data.settings.controlnet, + settings, + generate + ); onUnmounted(() => { burner.cleanup(); }); @@ -280,9 +275,9 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(ImageUpload, { callback: imageSelectCallback, - preview: unref(conf).data.settings.controlnet.image, + preview: unref(settings).data.settings.controlnet.image, style: { "margin-bottom": "12px" }, - onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(conf).data.settings.controlnet.image = $event) + onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(settings).data.settings.controlnet.image = $event) }, null, 8, ["preview"]), createVNode(unref(NCard), { title: "Settings", @@ -295,14 +290,14 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }, { default: withCtx(() => [ createVNode(unref(NInput), { - value: unref(conf).data.settings.controlnet.prompt, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(conf).data.settings.controlnet.prompt = $event), + value: unref(settings).data.settings.controlnet.prompt, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).data.settings.controlnet.prompt = $event), type: "textarea", placeholder: "Prompt", "show-count": "", onKeyup: _cache[2] || (_cache[2] = ($event) => unref(promptHandleKeyUp)( $event, - unref(conf).data.settings.controlnet, + unref(settings).data.settings.controlnet, "prompt", unref(global) )), @@ -314,14 +309,14 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ _: 1 }, 8, ["value", "onKeydown"]), createVNode(unref(NInput), { - value: unref(conf).data.settings.controlnet.negative_prompt, - "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(conf).data.settings.controlnet.negative_prompt = $event), + value: unref(settings).data.settings.controlnet.negative_prompt, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).data.settings.controlnet.negative_prompt = $event), type: "textarea", placeholder: "Negative prompt", "show-count": "", onKeyup: _cache[4] || (_cache[4] = ($event) => unref(promptHandleKeyUp)( $event, - unref(conf).data.settings.controlnet, + unref(settings).data.settings.controlnet, "negative_prompt", unref(global) )), @@ -332,113 +327,79 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ ]), _: 1 }, 8, ["value", "onKeydown"]), + createVNode(_sfc_main$4, { type: "controlnet" }), createBaseVNode("div", _hoisted_2$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ _hoisted_3$2 ]), default: withCtx(() => [ - createTextVNode(" The sampler is the method used to generate the image. Your result may vary drastically depending on the sampler you choose. "), - _hoisted_4$2, - _hoisted_5$2 - ]), - _: 1 - }), - createVNode(unref(NSelect), { - options: unref(conf).scheduler_options, - value: unref(conf).data.settings.controlnet.sampler, - "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(conf).data.settings.controlnet.sampler = $event), - style: { "flex-grow": "1" } - }, null, 8, ["options", "value"]) - ]), - createBaseVNode("div", _hoisted_6$2, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_7$2 - ]), - default: withCtx(() => [ - createTextVNode(" Changes the sigmas used in the Karras diffusion process. Might provide better results for some images. "), - _hoisted_8$2 - ]), - _: 1 - }), - createVNode(unref(NSwitch), { - value: unref(conf).data.settings.controlnet.use_karras_sigmas, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(conf).data.settings.controlnet.use_karras_sigmas = $event), - style: { "justify-self": "flex-end" } - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_9$2, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_10$2 - ]), - default: withCtx(() => [ - createTextVNode(" TODO ") + createTextVNode(' ControlNet is a method of guiding the diffusion process. It allows you to control the output by providing a guidance image. This image will be processed automatically. You can also opt out and enable "Is Preprocessed" to provide your own preprocessed image. '), + _hoisted_4$2 ]), _: 1 }), createVNode(unref(NSelect), { - options: unref(conf).controlnet_options, - value: unref(conf).data.settings.controlnet.controlnet, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(conf).data.settings.controlnet.controlnet = $event), + options: unref(settings).controlnet_options, + value: unref(settings).data.settings.controlnet.controlnet, + "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(settings).data.settings.controlnet.controlnet = $event), filterable: "", tag: "", style: { "flex-grow": "1" } }, null, 8, ["options", "value"]) ]), - createVNode(_sfc_main$4, { - "dimensions-object": unref(conf).data.settings.controlnet + createVNode(_sfc_main$5, { + "dimensions-object": unref(settings).data.settings.controlnet }, null, 8, ["dimensions-object"]), - createBaseVNode("div", _hoisted_11$2, [ + createBaseVNode("div", _hoisted_5$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_12$2 + _hoisted_6$2 ]), default: withCtx(() => [ createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), - _hoisted_13$2 + _hoisted_7$2 ]), _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.controlnet.steps, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(conf).data.settings.controlnet.steps = $event), + value: unref(settings).data.settings.controlnet.steps, + "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).data.settings.controlnet.steps = $event), min: 5, max: 300, style: { "margin-right": "12px" } }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.controlnet.steps, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(conf).data.settings.controlnet.steps = $event), + value: unref(settings).data.settings.controlnet.steps, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).data.settings.controlnet.steps = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 5, max: 300 }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_14$2, [ + createBaseVNode("div", _hoisted_8$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_15$2 + _hoisted_9$2 ]), default: withCtx(() => [ createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), - _hoisted_16$2 + _hoisted_10$2 ]), _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.controlnet.cfg_scale, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(conf).data.settings.controlnet.cfg_scale = $event), + value: unref(settings).data.settings.controlnet.cfg_scale, + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).data.settings.controlnet.cfg_scale = $event), min: 1, max: 30, step: 0.5, style: { "margin-right": "12px" } }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.controlnet.cfg_scale, - "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(conf).data.settings.controlnet.cfg_scale = $event), + value: unref(settings).data.settings.controlnet.cfg_scale, + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).data.settings.controlnet.cfg_scale = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 1, @@ -446,10 +407,10 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ step: 0.5 }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_17$2, [ + createBaseVNode("div", _hoisted_11$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_18$2 + _hoisted_12$2 ]), default: withCtx(() => [ createTextVNode(" Number of images to generate after each other. ") @@ -457,28 +418,28 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.controlnet.batch_count, - "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(conf).data.settings.controlnet.batch_count = $event), + value: unref(settings).data.settings.controlnet.batch_count, + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).data.settings.controlnet.batch_count = $event), min: 1, max: 9, style: { "margin-right": "12px" } }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.controlnet.batch_count, - "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(conf).data.settings.controlnet.batch_count = $event), + value: unref(settings).data.settings.controlnet.batch_count, + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).data.settings.controlnet.batch_count = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 1, max: 9 }, null, 8, ["value"]) ]), - createVNode(_sfc_main$5, { - "batch-size-object": unref(conf).data.settings.controlnet + createVNode(_sfc_main$6, { + "batch-size-object": unref(settings).data.settings.controlnet }, null, 8, ["batch-size-object"]), - createBaseVNode("div", _hoisted_19$2, [ + createBaseVNode("div", _hoisted_13$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_20$2 + _hoisted_14$2 ]), default: withCtx(() => [ createTextVNode(" How much should the ControlNet affect the image. ") @@ -486,16 +447,16 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.controlnet.controlnet_conditioning_scale, - "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(conf).data.settings.controlnet.controlnet_conditioning_scale = $event), + value: unref(settings).data.settings.controlnet.controlnet_conditioning_scale, + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).data.settings.controlnet.controlnet_conditioning_scale = $event), min: 0.1, max: 2, style: { "margin-right": "12px" }, step: 0.025 }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.controlnet.controlnet_conditioning_scale, - "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(conf).data.settings.controlnet.controlnet_conditioning_scale = $event), + value: unref(settings).data.settings.controlnet.controlnet_conditioning_scale, + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).data.settings.controlnet.controlnet_conditioning_scale = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 0.1, @@ -503,10 +464,10 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ step: 0.025 }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_21$2, [ + createBaseVNode("div", _hoisted_15$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_22$2 + _hoisted_16$2 ]), default: withCtx(() => [ createTextVNode(" What resolution to use for the image processing. This process does not affect the final result but can affect the quality of the ControlNet processing. ") @@ -514,16 +475,16 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.controlnet.detection_resolution, - "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(conf).data.settings.controlnet.detection_resolution = $event), + value: unref(settings).data.settings.controlnet.detection_resolution, + "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).data.settings.controlnet.detection_resolution = $event), min: 128, max: 2048, style: { "margin-right": "12px" }, step: 8 }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.controlnet.detection_resolution, - "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(conf).data.settings.controlnet.detection_resolution = $event), + value: unref(settings).data.settings.controlnet.detection_resolution, + "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(settings).data.settings.controlnet.detection_resolution = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 128, @@ -531,45 +492,45 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ step: 8 }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_23$2, [ + createBaseVNode("div", _hoisted_17$2, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_24$2 + _hoisted_18$1 ]), default: withCtx(() => [ createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), - _hoisted_25$1 + _hoisted_19$1 ]), _: 1 }), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.controlnet.seed, - "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(conf).data.settings.controlnet.seed = $event), + value: unref(settings).data.settings.controlnet.seed, + "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(settings).data.settings.controlnet.seed = $event), size: "small", min: -1, max: 999999999999, style: { "flex-grow": "1" } }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_26$1, [ - _hoisted_27$1, + createBaseVNode("div", _hoisted_20$1, [ + _hoisted_21$1, createVNode(unref(NSwitch), { - value: unref(conf).data.settings.controlnet.is_preprocessed, - "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(conf).data.settings.controlnet.is_preprocessed = $event) + value: unref(settings).data.settings.controlnet.is_preprocessed, + "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(settings).data.settings.controlnet.is_preprocessed = $event) }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_28$1, [ - _hoisted_29$1, + createBaseVNode("div", _hoisted_22$1, [ + _hoisted_23$1, createVNode(unref(NSwitch), { - value: unref(conf).data.settings.controlnet.save_preprocessed, - "onUpdate:value": _cache[20] || (_cache[20] = ($event) => unref(conf).data.settings.controlnet.save_preprocessed = $event) + value: unref(settings).data.settings.controlnet.save_preprocessed, + "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(settings).data.settings.controlnet.save_preprocessed = $event) }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_30$1, [ - _hoisted_31$1, + createBaseVNode("div", _hoisted_24$1, [ + _hoisted_25, createVNode(unref(NSwitch), { - value: unref(conf).data.settings.controlnet.return_preprocessed, - "onUpdate:value": _cache[21] || (_cache[21] = ($event) => unref(conf).data.settings.controlnet.return_preprocessed = $event) + value: unref(settings).data.settings.controlnet.return_preprocessed, + "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(settings).data.settings.controlnet.return_preprocessed = $event) }, null, 8, ["value"]) ]) ]), @@ -583,13 +544,14 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }), createVNode(unref(NGi), null, { default: withCtx(() => [ - createVNode(_sfc_main$6, { generate }), - createVNode(_sfc_main$7, { + createVNode(_sfc_main$7, { generate }), + createVNode(_sfc_main$8, { "current-image": unref(global).state.controlnet.currentImage, images: unref(global).state.controlnet.images, - onImageClicked: _cache[22] || (_cache[22] = ($event) => unref(global).state.controlnet.currentImage = $event) - }, null, 8, ["current-image", "images"]), - createVNode(_sfc_main$8, { + data: unref(settings).data.settings.controlnet, + onImageClicked: _cache[20] || (_cache[20] = ($event) => unref(global).state.controlnet.currentImage = $event) + }, null, 8, ["current-image", "images", "data"]), + createVNode(_sfc_main$9, { style: { "margin-top": "12px" }, "gen-data": unref(global).state.controlnet.genData }, null, 8, ["gen-data"]) @@ -603,47 +565,40 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }; } }); -const ControlNet_vue_vue_type_style_index_0_scoped_24e2a46c_lang = ""; -const ControlNet = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-24e2a46c"]]); -const _withScopeId$1 = (n) => (pushScopeId("data-v-99f48af3"), n = n(), popScopeId(), n); +const ControlNet_vue_vue_type_style_index_0_scoped_572c2180_lang = ""; +const ControlNet = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-572c2180"]]); +const _withScopeId$1 = (n) => (pushScopeId("data-v-fe0214b4"), n = n(), popScopeId(), n); const _hoisted_1$1 = { style: { "margin": "0 12px" } }; const _hoisted_2$1 = { class: "flex-container" }; -const _hoisted_3$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "150px" } }, "Sampler", -1)); -const _hoisted_4$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using DPMSolverMultistep for the best results . ", -1)); -const _hoisted_5$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("a", { - target: "_blank", - href: "https://docs.google.com/document/d/1n0YozLAUwLJWZmbsx350UD_bwAx3gZMnRuleIZt_R1w" -}, "Learn more", -1)); -const _hoisted_6$1 = { class: "flex-container" }; -const _hoisted_7$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { style: { "width": "120px" } }, "Karras Sigmas", -1)); -const _hoisted_8$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "Works only with KDPM samplers. Ignored by other samplers.", -1)); -const _hoisted_9$1 = { class: "flex-container" }; -const _hoisted_10$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1)); -const _hoisted_11$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1)); -const _hoisted_12$1 = { class: "flex-container" }; -const _hoisted_13$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1)); -const _hoisted_14$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1)); +const _hoisted_3$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1)); +const _hoisted_4$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1)); +const _hoisted_5$1 = { class: "flex-container" }; +const _hoisted_6$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1)); +const _hoisted_7$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1)); +const _hoisted_8$1 = { + key: 0, + class: "flex-container" +}; +const _hoisted_9$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Self Attention Scale", -1)); +const _hoisted_10$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "PyTorch ONLY.", -1)); +const _hoisted_11$1 = { class: "flex-container" }; +const _hoisted_12$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1)); +const _hoisted_13$1 = { class: "flex-container" }; +const _hoisted_14$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Denoising Strength", -1)); const _hoisted_15$1 = { class: "flex-container" }; -const _hoisted_16$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Self Attention Scale", -1)); -const _hoisted_17$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "PyTorch ONLY.", -1)); -const _hoisted_18$1 = { class: "flex-container" }; -const _hoisted_19$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1)); -const _hoisted_20$1 = { class: "flex-container" }; -const _hoisted_21$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Denoising Strength", -1)); -const _hoisted_22$1 = { class: "flex-container" }; -const _hoisted_23$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Seed", -1)); -const _hoisted_24$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1)); +const _hoisted_16$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Seed", -1)); +const _hoisted_17$1 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1)); const _sfc_main$2 = /* @__PURE__ */ defineComponent({ __name: "Img2Img", setup(__props) { const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const messageHandler = useMessage(); const promptCount = computed(() => { - return conf.data.settings.img2img.prompt.split(spaceRegex).length - 1; + return settings.data.settings.img2img.prompt.split(spaceRegex).length - 1; }); const negativePromptCount = computed(() => { - return conf.data.settings.img2img.negative_prompt.split(spaceRegex).length - 1; + return settings.data.settings.img2img.negative_prompt.split(spaceRegex).length - 1; }); const checkSeed = (seed) => { if (seed === -1) { @@ -652,16 +607,16 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ return seed; }; const imageSelectCallback = (base64Image) => { - conf.data.settings.img2img.image = base64Image; + settings.data.settings.img2img.image = base64Image; }; const generate = () => { var _a; - if (conf.data.settings.img2img.seed === null) { + if (settings.data.settings.img2img.seed === null) { messageHandler.error("Please set a seed"); return; } global.state.generating = true; - const seed = checkSeed(conf.data.settings.img2img.seed); + const seed = checkSeed(settings.data.settings.img2img.seed); fetch(`${serverUrl}/api/generate/img2img`, { method: "POST", headers: { @@ -669,23 +624,24 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }, body: JSON.stringify({ data: { - prompt: conf.data.settings.img2img.prompt, - image: conf.data.settings.img2img.image, + prompt: settings.data.settings.img2img.prompt, + image: settings.data.settings.img2img.image, id: v4(), - negative_prompt: conf.data.settings.img2img.negative_prompt, - width: conf.data.settings.img2img.width, - height: conf.data.settings.img2img.height, - steps: conf.data.settings.img2img.steps, - guidance_scale: conf.data.settings.img2img.cfg_scale, + negative_prompt: settings.data.settings.img2img.negative_prompt, + width: settings.data.settings.img2img.width, + height: settings.data.settings.img2img.height, + steps: settings.data.settings.img2img.steps, + guidance_scale: settings.data.settings.img2img.cfg_scale, seed, - batch_size: conf.data.settings.img2img.batch_size, - batch_count: conf.data.settings.img2img.batch_count, - strength: conf.data.settings.img2img.denoising_strength, - scheduler: conf.data.settings.img2img.sampler, - self_attention_scale: conf.data.settings.txt2img.self_attention_scale, - use_karras_sigmas: conf.data.settings.img2img.use_karras_sigmas + batch_size: settings.data.settings.img2img.batch_size, + batch_count: settings.data.settings.img2img.batch_count, + strength: settings.data.settings.img2img.denoising_strength, + scheduler: settings.data.settings.img2img.sampler, + self_attention_scale: settings.data.settings.img2img.self_attention_scale, + sigmas: settings.data.settings.img2img.sigmas, + sampler_settings: settings.data.settings.sampler_config[settings.data.settings.img2img.sampler] }, - model: (_a = conf.data.settings.model) == null ? void 0 : _a.name + model: (_a = settings.data.settings.model) == null ? void 0 : _a.name }) }).then((res) => { if (!res.ok) { @@ -706,15 +662,17 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }).catch((err) => { global.state.generating = false; messageHandler.error(err); - console.log(err); }); }; - const burner = new BurnerClock(conf.data.settings.img2img, conf, generate); + const burner = new BurnerClock( + settings.data.settings.img2img, + settings, + generate + ); onUnmounted(() => { burner.cleanup(); }); return (_ctx, _cache) => { - const _component_NSwitch = resolveComponent("NSwitch"); return openBlock(), createElementBlock("div", _hoisted_1$1, [ createVNode(unref(NGrid), { cols: "1 m:2", @@ -726,9 +684,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(ImageUpload, { callback: imageSelectCallback, - preview: unref(conf).data.settings.img2img.image, + preview: unref(settings).data.settings.img2img.image, style: { "margin-bottom": "12px" }, - onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(conf).data.settings.img2img.image = $event) + onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(settings).data.settings.img2img.image = $event) }, null, 8, ["preview"]), createVNode(unref(NCard), { title: "Settings", @@ -739,247 +697,215 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ vertical: "", class: "left-container" }, { - default: withCtx(() => [ - createVNode(unref(NInput), { - value: unref(conf).data.settings.img2img.prompt, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(conf).data.settings.img2img.prompt = $event), - type: "textarea", - placeholder: "Prompt", - "show-count": "", - onKeyup: _cache[2] || (_cache[2] = ($event) => unref(promptHandleKeyUp)( - $event, - unref(conf).data.settings.img2img, - "prompt", - unref(global) - )), - onKeydown: unref(promptHandleKeyDown) - }, { - count: withCtx(() => [ - createTextVNode(toDisplayString(promptCount.value), 1) - ]), - _: 1 - }, 8, ["value", "onKeydown"]), - createVNode(unref(NInput), { - value: unref(conf).data.settings.img2img.negative_prompt, - "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(conf).data.settings.img2img.negative_prompt = $event), - type: "textarea", - placeholder: "Negative prompt", - "show-count": "", - onKeyup: _cache[4] || (_cache[4] = ($event) => unref(promptHandleKeyUp)( - $event, - unref(conf).data.settings.img2img, - "negative_prompt", - unref(global) - )), - onKeydown: unref(promptHandleKeyDown) - }, { - count: withCtx(() => [ - createTextVNode(toDisplayString(negativePromptCount.value), 1) - ]), - _: 1 - }, 8, ["value", "onKeydown"]), - createBaseVNode("div", _hoisted_2$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_3$1 - ]), - default: withCtx(() => [ - createTextVNode(" The sampler is the method used to generate the image. Your result may vary drastically depending on the sampler you choose. "), - _hoisted_4$1, - _hoisted_5$1 - ]), - _: 1 - }), - createVNode(unref(NSelect), { - options: unref(conf).scheduler_options, - value: unref(conf).data.settings.img2img.sampler, - "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(conf).data.settings.img2img.sampler = $event), - style: { "flex-grow": "1" } - }, null, 8, ["options", "value"]) - ]), - createBaseVNode("div", _hoisted_6$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_7$1 - ]), - default: withCtx(() => [ - createTextVNode(" Changes the sigmas used in the Karras diffusion process. Might provide better results for some images. "), - _hoisted_8$1 - ]), - _: 1 - }), - createVNode(_component_NSwitch, { - value: unref(conf).data.settings.txt2img.use_karras_sigmas, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(conf).data.settings.txt2img.use_karras_sigmas = $event), - style: { "justify-self": "flex-end" } - }, null, 8, ["value"]) - ]), - createVNode(_sfc_main$4, { - "dimensions-object": unref(conf).data.settings.img2img - }, null, 8, ["dimensions-object"]), - createBaseVNode("div", _hoisted_9$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_10$1 - ]), - default: withCtx(() => [ - createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), - _hoisted_11$1 - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.img2img.steps, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(conf).data.settings.img2img.steps = $event), - min: 5, - max: 300, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.img2img.steps, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(conf).data.settings.img2img.steps = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 5, - max: 300 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_12$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_13$1 - ]), - default: withCtx(() => [ - createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), - _hoisted_14$1 - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.img2img.cfg_scale, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(conf).data.settings.img2img.cfg_scale = $event), - min: 1, - max: 30, - step: 0.5, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.img2img.cfg_scale, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(conf).data.settings.img2img.cfg_scale = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 1, - max: 30, - step: 0.5 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_15$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_16$1 - ]), - default: withCtx(() => [ - _hoisted_17$1, - createTextVNode(" If self attention is >0, SAG will guide the model and improve the quality of the image at the cost of speed. Higher values will follow the guidance more closely, which can lead to better, more sharp and detailed outputs. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.txt2img.self_attention_scale, - "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(conf).data.settings.txt2img.self_attention_scale = $event), - min: 0, - max: 1, - step: 0.05, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.self_attention_scale, - "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(conf).data.settings.txt2img.self_attention_scale = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 0, - max: 1, - step: 0.05 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_18$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_19$1 - ]), - default: withCtx(() => [ - createTextVNode(" Number of images to generate after each other. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.img2img.batch_count, - "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(conf).data.settings.img2img.batch_count = $event), - min: 1, - max: 9, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.img2img.batch_count, - "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(conf).data.settings.img2img.batch_count = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 1, - max: 9 - }, null, 8, ["value"]) - ]), - createVNode(_sfc_main$5, { - "batch-size-object": unref(conf).data.settings.img2img - }, null, 8, ["batch-size-object"]), - createBaseVNode("div", _hoisted_20$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_21$1 - ]), - default: withCtx(() => [ - createTextVNode(" Lower values will stick more to the original image, 0.5-0.75 is ideal ") + default: withCtx(() => { + var _a; + return [ + createVNode(unref(NInput), { + value: unref(settings).data.settings.img2img.prompt, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).data.settings.img2img.prompt = $event), + type: "textarea", + placeholder: "Prompt", + "show-count": "", + onKeyup: _cache[2] || (_cache[2] = ($event) => unref(promptHandleKeyUp)( + $event, + unref(settings).data.settings.img2img, + "prompt", + unref(global) + )), + onKeydown: unref(promptHandleKeyDown) + }, { + count: withCtx(() => [ + createTextVNode(toDisplayString(promptCount.value), 1) ]), _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.img2img.denoising_strength, - "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(conf).data.settings.img2img.denoising_strength = $event), - min: 0.1, - max: 1, - style: { "margin-right": "12px" }, - step: 0.025 - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.img2img.denoising_strength, - "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(conf).data.settings.img2img.denoising_strength = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 0.1, - max: 1, - step: 0.025 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_22$1, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_23$1 - ]), - default: withCtx(() => [ - createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), - _hoisted_24$1 + }, 8, ["value", "onKeydown"]), + createVNode(unref(NInput), { + value: unref(settings).data.settings.img2img.negative_prompt, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).data.settings.img2img.negative_prompt = $event), + type: "textarea", + placeholder: "Negative prompt", + "show-count": "", + onKeyup: _cache[4] || (_cache[4] = ($event) => unref(promptHandleKeyUp)( + $event, + unref(settings).data.settings.img2img, + "negative_prompt", + unref(global) + )), + onKeydown: unref(promptHandleKeyDown) + }, { + count: withCtx(() => [ + createTextVNode(toDisplayString(negativePromptCount.value), 1) ]), _: 1 - }), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.img2img.seed, - "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(conf).data.settings.img2img.seed = $event), - size: "small", - min: -1, - max: 999999999999, - style: { "flex-grow": "1" } - }, null, 8, ["value"]) - ]) - ]), + }, 8, ["value", "onKeydown"]), + createVNode(_sfc_main$4, { type: "img2img" }), + createVNode(_sfc_main$5, { + "dimensions-object": unref(settings).data.settings.img2img + }, null, 8, ["dimensions-object"]), + createBaseVNode("div", _hoisted_2$1, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_3$1 + ]), + default: withCtx(() => [ + createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), + _hoisted_4$1 + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.img2img.steps, + "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(settings).data.settings.img2img.steps = $event), + min: 5, + max: 300, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.img2img.steps, + "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).data.settings.img2img.steps = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 5, + max: 300 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_5$1, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_6$1 + ]), + default: withCtx(() => [ + createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), + _hoisted_7$1 + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.img2img.cfg_scale, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).data.settings.img2img.cfg_scale = $event), + min: 1, + max: 30, + step: 0.5, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.img2img.cfg_scale, + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).data.settings.img2img.cfg_scale = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 1, + max: 30, + step: 0.5 + }, null, 8, ["value"]) + ]), + Number.isInteger(unref(settings).data.settings.img2img.sampler) && ((_a = unref(settings).data.settings.model) == null ? void 0 : _a.backend) === "PyTorch" ? (openBlock(), createElementBlock("div", _hoisted_8$1, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_9$1 + ]), + default: withCtx(() => [ + _hoisted_10$1, + createTextVNode(" If self attention is >0, SAG will guide the model and improve the quality of the image at the cost of speed. Higher values will follow the guidance more closely, which can lead to better, more sharp and detailed outputs. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.img2img.self_attention_scale, + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).data.settings.img2img.self_attention_scale = $event), + min: 0, + max: 1, + step: 0.05, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.img2img.self_attention_scale, + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).data.settings.img2img.self_attention_scale = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 0, + max: 1, + step: 0.05 + }, null, 8, ["value"]) + ])) : createCommentVNode("", true), + createBaseVNode("div", _hoisted_11$1, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_12$1 + ]), + default: withCtx(() => [ + createTextVNode(" Number of images to generate after each other. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.img2img.batch_count, + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).data.settings.img2img.batch_count = $event), + min: 1, + max: 9, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.img2img.batch_count, + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).data.settings.img2img.batch_count = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 1, + max: 9 + }, null, 8, ["value"]) + ]), + createVNode(_sfc_main$6, { + "batch-size-object": unref(settings).data.settings.img2img + }, null, 8, ["batch-size-object"]), + createBaseVNode("div", _hoisted_13$1, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_14$1 + ]), + default: withCtx(() => [ + createTextVNode(" Lower values will stick more to the original image, 0.5-0.75 is ideal ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.img2img.denoising_strength, + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).data.settings.img2img.denoising_strength = $event), + min: 0.1, + max: 1, + style: { "margin-right": "12px" }, + step: 0.025 + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.img2img.denoising_strength, + "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).data.settings.img2img.denoising_strength = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 0.1, + max: 1, + step: 0.025 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_15$1, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_16$1 + ]), + default: withCtx(() => [ + createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), + _hoisted_17$1 + ]), + _: 1 + }), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.img2img.seed, + "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(settings).data.settings.img2img.seed = $event), + size: "small", + min: -1, + max: 999999999999, + style: { "flex-grow": "1" } + }, null, 8, ["value"]) + ]) + ]; + }), _: 1 }) ]), @@ -990,13 +916,14 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }), createVNode(unref(NGi), null, { default: withCtx(() => [ - createVNode(_sfc_main$6, { generate }), - createVNode(_sfc_main$7, { + createVNode(_sfc_main$7, { generate }), + createVNode(_sfc_main$8, { "current-image": unref(global).state.img2img.currentImage, images: unref(global).state.img2img.images, - onImageClicked: _cache[18] || (_cache[18] = ($event) => unref(global).state.img2img.currentImage = $event) - }, null, 8, ["current-image", "images"]), - createVNode(_sfc_main$8, { + data: unref(settings).data.settings.img2img, + onImageClicked: _cache[16] || (_cache[16] = ($event) => unref(global).state.img2img.currentImage = $event) + }, null, 8, ["current-image", "images", "data"]), + createVNode(_sfc_main$9, { style: { "margin-top": "12px" }, "gen-data": unref(global).state.img2img.genData }, null, 8, ["gen-data"]) @@ -1010,8 +937,8 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }; } }); -const Img2Img_vue_vue_type_style_index_0_scoped_99f48af3_lang = ""; -const Img2Img = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-99f48af3"]]); +const Img2Img_vue_vue_type_style_index_0_scoped_fe0214b4_lang = ""; +const Img2Img = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-fe0214b4"]]); var VueDrawingCanvas = /* @__PURE__ */ defineComponent({ name: "VueDrawingCanvas", props: { @@ -1587,7 +1514,7 @@ var VueDrawingCanvas = /* @__PURE__ */ defineComponent({ }); } }); -const _withScopeId = (n) => (pushScopeId("data-v-721f7818"), n = n(), popScopeId(), n); +const _withScopeId = (n) => (pushScopeId("data-v-debbedfc"), n = n(), popScopeId(), n); const _hoisted_1 = { style: { "margin": "0 12px" } }; const _hoisted_2 = { style: { "display": "inline-flex", "align-items": "center" } }; const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("svg", { @@ -1604,46 +1531,39 @@ const _hoisted_4 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBase /* @__PURE__ */ createBaseVNode("span", { class: "file-upload" }, "Select image") ], -1)); const _hoisted_5 = { class: "flex-container" }; -const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "150px" } }, "Sampler", -1)); -const _hoisted_7 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using DPMSolverMultistep for the best results . ", -1)); -const _hoisted_8 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("a", { - target: "_blank", - href: "https://docs.google.com/document/d/1n0YozLAUwLJWZmbsx350UD_bwAx3gZMnRuleIZt_R1w" -}, "Learn more", -1)); +const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Width", -1)); +const _hoisted_7 = { class: "flex-container" }; +const _hoisted_8 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Height", -1)); const _hoisted_9 = { class: "flex-container" }; -const _hoisted_10 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { style: { "width": "120px" } }, "Karras Sigmas", -1)); -const _hoisted_11 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "Works only with KDPM samplers. Ignored by other samplers.", -1)); +const _hoisted_10 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1)); +const _hoisted_11 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1)); const _hoisted_12 = { class: "flex-container" }; -const _hoisted_13 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Width", -1)); -const _hoisted_14 = { class: "flex-container" }; -const _hoisted_15 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Height", -1)); -const _hoisted_16 = { class: "flex-container" }; -const _hoisted_17 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1)); -const _hoisted_18 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1)); -const _hoisted_19 = { class: "flex-container" }; -const _hoisted_20 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1)); -const _hoisted_21 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1)); +const _hoisted_13 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1)); +const _hoisted_14 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1)); +const _hoisted_15 = { + key: 0, + class: "flex-container" +}; +const _hoisted_16 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Self Attention Scale", -1)); +const _hoisted_17 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "PyTorch ONLY.", -1)); +const _hoisted_18 = { class: "flex-container" }; +const _hoisted_19 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1)); +const _hoisted_20 = { class: "flex-container" }; +const _hoisted_21 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Size", -1)); const _hoisted_22 = { class: "flex-container" }; -const _hoisted_23 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Self Attention Scale", -1)); -const _hoisted_24 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "PyTorch ONLY.", -1)); -const _hoisted_25 = { class: "flex-container" }; -const _hoisted_26 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1)); -const _hoisted_27 = { class: "flex-container" }; -const _hoisted_28 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Size", -1)); -const _hoisted_29 = { class: "flex-container" }; -const _hoisted_30 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Seed", -1)); -const _hoisted_31 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1)); +const _hoisted_23 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Seed", -1)); +const _hoisted_24 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1)); const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "Inpainting", setup(__props) { const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const messageHandler = useMessage(); const promptCount = computed(() => { - return conf.data.settings.inpainting.prompt.split(spaceRegex).length - 1; + return settings.data.settings.inpainting.prompt.split(spaceRegex).length - 1; }); const negativePromptCount = computed(() => { - return conf.data.settings.inpainting.negative_prompt.split(spaceRegex).length - 1; + return settings.data.settings.inpainting.negative_prompt.split(spaceRegex).length - 1; }); const checkSeed = (seed) => { if (seed === -1) { @@ -1653,13 +1573,13 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }; const generate = () => { var _a; - if (conf.data.settings.inpainting.seed === null) { + if (settings.data.settings.inpainting.seed === null) { messageHandler.error("Please set a seed"); return; } generateMask(); global.state.generating = true; - const seed = checkSeed(conf.data.settings.inpainting.seed); + const seed = checkSeed(settings.data.settings.inpainting.seed); fetch(`${serverUrl}/api/generate/inpainting`, { method: "POST", headers: { @@ -1667,23 +1587,24 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }, body: JSON.stringify({ data: { - prompt: conf.data.settings.inpainting.prompt, - image: conf.data.settings.inpainting.image, - mask_image: conf.data.settings.inpainting.mask_image, + prompt: settings.data.settings.inpainting.prompt, + image: settings.data.settings.inpainting.image, + mask_image: settings.data.settings.inpainting.mask_image, id: v4(), - negative_prompt: conf.data.settings.inpainting.negative_prompt, - width: conf.data.settings.inpainting.width, - height: conf.data.settings.inpainting.height, - steps: conf.data.settings.inpainting.steps, - guidance_scale: conf.data.settings.inpainting.cfg_scale, + negative_prompt: settings.data.settings.inpainting.negative_prompt, + width: settings.data.settings.inpainting.width, + height: settings.data.settings.inpainting.height, + steps: settings.data.settings.inpainting.steps, + guidance_scale: settings.data.settings.inpainting.cfg_scale, seed, - batch_size: conf.data.settings.inpainting.batch_size, - batch_count: conf.data.settings.inpainting.batch_count, - scheduler: conf.data.settings.inpainting.sampler, - self_attention_scale: conf.data.settings.txt2img.self_attention_scale, - use_karras_sigmas: conf.data.settings.inpainting.use_karras_sigmas + batch_size: settings.data.settings.inpainting.batch_size, + batch_count: settings.data.settings.inpainting.batch_count, + scheduler: settings.data.settings.inpainting.sampler, + self_attention_scale: settings.data.settings.inpainting.self_attention_scale, + sigmas: settings.data.settings.inpainting.sigmas, + sampler_settings: settings.data.settings.sampler_config[settings.data.settings.inpainting.sampler] }, - model: (_a = conf.data.settings.model) == null ? void 0 : _a.name + model: (_a = settings.data.settings.model) == null ? void 0 : _a.name }) }).then((res) => { if (!res.ok) { @@ -1704,7 +1625,6 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }).catch((err) => { global.state.generating = false; messageHandler.error(err); - console.log(err); }); }; const canvas = ref(); @@ -1748,7 +1668,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ img.src = s; img.onload = () => { handleImageUpdate(img); - conf.data.settings.inpainting.image = s; + settings.data.settings.inpainting.image = s; }; } }; @@ -1768,9 +1688,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ (_a = canvas.value) == null ? void 0 : _a.redo(); } function toggleEraser() { - console.log(eraser.value); eraser.value = !eraser.value; - console.log(eraser.value); } function generateMask() { var _a; @@ -1780,21 +1698,23 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ maskCanvas.value.redraw(true); } } - const burner = new BurnerClock(conf.data.settings.inpainting, conf, generate); + const burner = new BurnerClock( + settings.data.settings.inpainting, + settings, + generate + ); onUnmounted(() => { burner.cleanup(); }); - if (conf.data.settings.inpainting.image !== "") { - preview.value = conf.data.settings.inpainting.image; + if (settings.data.settings.inpainting.image !== "") { + preview.value = settings.data.settings.inpainting.image; const img = new Image(); - img.src = conf.data.settings.inpainting.image; + img.src = settings.data.settings.inpainting.image; img.onload = () => { - console.log(img); handleImageUpdate(img); }; } return (_ctx, _cache) => { - const _component_NSwitch = resolveComponent("NSwitch"); return openBlock(), createElementBlock("div", _hoisted_1, [ createVNode(unref(NGrid), { cols: "1 m:2", @@ -1828,8 +1748,8 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ "canvas-id": "VueDrawingCanvas1" }, null, 8, ["width", "height", "backgroundImage", "lineWidth", "eraser"]), createVNode(unref(VueDrawingCanvas), { - image: unref(conf).data.settings.inpainting.mask_image, - "onUpdate:image": _cache[0] || (_cache[0] = ($event) => unref(conf).data.settings.inpainting.mask_image = $event), + image: unref(settings).data.settings.inpainting.mask_image, + "onUpdate:image": _cache[0] || (_cache[0] = ($event) => unref(settings).data.settings.inpainting.mask_image = $event), width: width.value, height: height.value, ref_key: "maskCanvas", @@ -1941,279 +1861,247 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ vertical: "", class: "left-container" }, { - default: withCtx(() => [ - createVNode(unref(NInput), { - value: unref(conf).data.settings.inpainting.prompt, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(conf).data.settings.inpainting.prompt = $event), - type: "textarea", - placeholder: "Prompt", - "show-count": "", - onKeyup: _cache[3] || (_cache[3] = ($event) => unref(promptHandleKeyUp)( - $event, - unref(conf).data.settings.inpainting, - "prompt", - unref(global) - )), - onKeydown: unref(promptHandleKeyDown) - }, { - count: withCtx(() => [ - createTextVNode(toDisplayString(promptCount.value), 1) - ]), - _: 1 - }, 8, ["value", "onKeydown"]), - createVNode(unref(NInput), { - value: unref(conf).data.settings.inpainting.negative_prompt, - "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(conf).data.settings.inpainting.negative_prompt = $event), - type: "textarea", - placeholder: "Negative prompt", - "show-count": "", - onKeyup: _cache[5] || (_cache[5] = ($event) => unref(promptHandleKeyUp)( - $event, - unref(conf).data.settings.inpainting, - "negative_prompt", - unref(global) - )), - onKeydown: unref(promptHandleKeyDown) - }, { - count: withCtx(() => [ - createTextVNode(toDisplayString(negativePromptCount.value), 1) - ]), - _: 1 - }, 8, ["value", "onKeydown"]), - createBaseVNode("div", _hoisted_5, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_6 - ]), - default: withCtx(() => [ - createTextVNode(" The sampler is the method used to generate the image. Your result may vary drastically depending on the sampler you choose. "), - _hoisted_7, - _hoisted_8 - ]), - _: 1 - }), - createVNode(unref(NSelect), { - options: unref(conf).scheduler_options, - value: unref(conf).data.settings.inpainting.sampler, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(conf).data.settings.inpainting.sampler = $event), - style: { "flex-grow": "1" } - }, null, 8, ["options", "value"]) - ]), - createBaseVNode("div", _hoisted_9, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_10 - ]), - default: withCtx(() => [ - createTextVNode(" Changes the sigmas used in the Karras diffusion process. Might provide better results for some images. "), - _hoisted_11 - ]), - _: 1 - }), - createVNode(_component_NSwitch, { - value: unref(conf).data.settings.txt2img.use_karras_sigmas, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(conf).data.settings.txt2img.use_karras_sigmas = $event), - style: { "justify-self": "flex-end" } - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_12, [ - _hoisted_13, - createVNode(unref(NSlider), { - value: unref(conf).data.settings.inpainting.width, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(conf).data.settings.inpainting.width = $event), - min: 128, - max: 2048, - step: 8, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.width, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(conf).data.settings.inpainting.width = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 8, - min: 128, - max: 2048 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_14, [ - _hoisted_15, - createVNode(unref(NSlider), { - value: unref(conf).data.settings.inpainting.height, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(conf).data.settings.inpainting.height = $event), - min: 128, - max: 2048, - step: 8, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.height, - "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(conf).data.settings.inpainting.height = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 8, - min: 128, - max: 2048 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_16, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_17 - ]), - default: withCtx(() => [ - createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), - _hoisted_18 - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.inpainting.steps, - "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(conf).data.settings.inpainting.steps = $event), - min: 5, - max: 300, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.steps, - "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(conf).data.settings.inpainting.steps = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 5, - max: 300 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_19, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_20 - ]), - default: withCtx(() => [ - createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), - _hoisted_21 - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.inpainting.cfg_scale, - "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(conf).data.settings.inpainting.cfg_scale = $event), - min: 1, - max: 30, - step: 0.5, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.cfg_scale, - "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(conf).data.settings.inpainting.cfg_scale = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 1, - max: 30, - step: 0.5 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_22, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_23 - ]), - default: withCtx(() => [ - _hoisted_24, - createTextVNode(" If self attention is >0, SAG will guide the model and improve the quality of the image at the cost of speed. Higher values will follow the guidance more closely, which can lead to better, more sharp and detailed outputs. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.txt2img.self_attention_scale, - "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(conf).data.settings.txt2img.self_attention_scale = $event), - min: 0, - max: 1, - step: 0.05, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.self_attention_scale, - "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(conf).data.settings.txt2img.self_attention_scale = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 0, - max: 1, - step: 0.05 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_25, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_26 - ]), - default: withCtx(() => [ - createTextVNode(" Number of images to generate after each other. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.inpainting.batch_count, - "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(conf).data.settings.inpainting.batch_count = $event), - min: 1, - max: 9, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.batch_count, - "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(conf).data.settings.inpainting.batch_count = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 1, - max: 9 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_27, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_28 - ]), - default: withCtx(() => [ - createTextVNode(" Number of images to generate in paralel. ") + default: withCtx(() => { + var _a; + return [ + createVNode(unref(NInput), { + value: unref(settings).data.settings.inpainting.prompt, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).data.settings.inpainting.prompt = $event), + type: "textarea", + placeholder: "Prompt", + "show-count": "", + onKeyup: _cache[3] || (_cache[3] = ($event) => unref(promptHandleKeyUp)( + $event, + unref(settings).data.settings.inpainting, + "prompt", + unref(global) + )), + onKeydown: unref(promptHandleKeyDown) + }, { + count: withCtx(() => [ + createTextVNode(toDisplayString(promptCount.value), 1) ]), _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.inpainting.batch_size, - "onUpdate:value": _cache[20] || (_cache[20] = ($event) => unref(conf).data.settings.inpainting.batch_size = $event), - min: 1, - max: 9, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.batch_size, - "onUpdate:value": _cache[21] || (_cache[21] = ($event) => unref(conf).data.settings.inpainting.batch_size = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - min: 1, - max: 9 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_29, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_30 - ]), - default: withCtx(() => [ - createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), - _hoisted_31 + }, 8, ["value", "onKeydown"]), + createVNode(unref(NInput), { + value: unref(settings).data.settings.inpainting.negative_prompt, + "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(settings).data.settings.inpainting.negative_prompt = $event), + type: "textarea", + placeholder: "Negative prompt", + "show-count": "", + onKeyup: _cache[5] || (_cache[5] = ($event) => unref(promptHandleKeyUp)( + $event, + unref(settings).data.settings.inpainting, + "negative_prompt", + unref(global) + )), + onKeydown: unref(promptHandleKeyDown) + }, { + count: withCtx(() => [ + createTextVNode(toDisplayString(negativePromptCount.value), 1) ]), _: 1 - }), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.inpainting.seed, - "onUpdate:value": _cache[22] || (_cache[22] = ($event) => unref(conf).data.settings.inpainting.seed = $event), - size: "small", - min: -1, - max: 999999999999, - style: { "flex-grow": "1" } - }, null, 8, ["value"]) - ]) - ]), + }, 8, ["value", "onKeydown"]), + createVNode(_sfc_main$4, { type: "inpainting" }), + createBaseVNode("div", _hoisted_5, [ + _hoisted_6, + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.width, + "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).data.settings.inpainting.width = $event), + min: 128, + max: 2048, + step: 8, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.width, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).data.settings.inpainting.width = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + step: 8, + min: 128, + max: 2048 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_7, [ + _hoisted_8, + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.height, + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).data.settings.inpainting.height = $event), + min: 128, + max: 2048, + step: 8, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.height, + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).data.settings.inpainting.height = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + step: 8, + min: 128, + max: 2048 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_9, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_10 + ]), + default: withCtx(() => [ + createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), + _hoisted_11 + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.steps, + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).data.settings.inpainting.steps = $event), + min: 5, + max: 300, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.steps, + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).data.settings.inpainting.steps = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 5, + max: 300 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_12, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_13 + ]), + default: withCtx(() => [ + createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), + _hoisted_14 + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.cfg_scale, + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).data.settings.inpainting.cfg_scale = $event), + min: 1, + max: 30, + step: 0.5, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.cfg_scale, + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).data.settings.inpainting.cfg_scale = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 1, + max: 30, + step: 0.5 + }, null, 8, ["value"]) + ]), + Number.isInteger(unref(settings).data.settings.inpainting.sampler) && ((_a = unref(settings).data.settings.model) == null ? void 0 : _a.backend) === "PyTorch" ? (openBlock(), createElementBlock("div", _hoisted_15, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_16 + ]), + default: withCtx(() => [ + _hoisted_17, + createTextVNode(" If self attention is >0, SAG will guide the model and improve the quality of the image at the cost of speed. Higher values will follow the guidance more closely, which can lead to better, more sharp and detailed outputs. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.self_attention_scale, + "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).data.settings.inpainting.self_attention_scale = $event), + min: 0, + max: 1, + step: 0.05, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.self_attention_scale, + "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(settings).data.settings.inpainting.self_attention_scale = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 0, + max: 1, + step: 0.05 + }, null, 8, ["value"]) + ])) : createCommentVNode("", true), + createBaseVNode("div", _hoisted_18, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_19 + ]), + default: withCtx(() => [ + createTextVNode(" Number of images to generate after each other. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.batch_count, + "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(settings).data.settings.inpainting.batch_count = $event), + min: 1, + max: 9, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.batch_count, + "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(settings).data.settings.inpainting.batch_count = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 1, + max: 9 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_20, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_21 + ]), + default: withCtx(() => [ + createTextVNode(" Number of images to generate in paralel. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.inpainting.batch_size, + "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(settings).data.settings.inpainting.batch_size = $event), + min: 1, + max: 9, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.batch_size, + "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(settings).data.settings.inpainting.batch_size = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 1, + max: 9 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_22, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_23 + ]), + default: withCtx(() => [ + createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), + _hoisted_24 + ]), + _: 1 + }), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.inpainting.seed, + "onUpdate:value": _cache[20] || (_cache[20] = ($event) => unref(settings).data.settings.inpainting.seed = $event), + size: "small", + min: -1, + max: 999999999999, + style: { "flex-grow": "1" } + }, null, 8, ["value"]) + ]) + ]; + }), _: 1 }) ]), @@ -2224,13 +2112,14 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }), createVNode(unref(NGi), null, { default: withCtx(() => [ - createVNode(_sfc_main$6, { generate }), - createVNode(_sfc_main$7, { + createVNode(_sfc_main$7, { generate }), + createVNode(_sfc_main$8, { "current-image": unref(global).state.inpainting.currentImage, images: unref(global).state.inpainting.images, - onImageClicked: _cache[23] || (_cache[23] = ($event) => unref(global).state.inpainting.currentImage = $event) - }, null, 8, ["current-image", "images"]), - createVNode(_sfc_main$8, { + data: unref(settings).data.settings.inpainting, + onImageClicked: _cache[21] || (_cache[21] = ($event) => unref(global).state.inpainting.currentImage = $event) + }, null, 8, ["current-image", "images", "data"]), + createVNode(_sfc_main$9, { style: { "margin-top": "12px" }, "gen-data": unref(global).state.inpainting.genData }, null, 8, ["gen-data"]) @@ -2244,8 +2133,8 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }; } }); -const Inpainting_vue_vue_type_style_index_0_scoped_721f7818_lang = ""; -const Inpainting = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-721f7818"]]); +const Inpainting_vue_vue_type_style_index_0_scoped_debbedfc_lang = ""; +const Inpainting = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-debbedfc"]]); const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "Image2ImageView", setup(__props) { @@ -2257,19 +2146,28 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(state).state.img2img.tab = $event) }, { default: withCtx(() => [ - createVNode(unref(NTabPane), { name: "Image to Image" }, { + createVNode(unref(NTabPane), { + tab: "Image to Image", + name: "img2img" + }, { default: withCtx(() => [ createVNode(Img2Img) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "ControlNet" }, { + createVNode(unref(NTabPane), { + tab: "ControlNet", + name: "controlnet" + }, { default: withCtx(() => [ createVNode(ControlNet) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "Inpainting" }, { + createVNode(unref(NTabPane), { + tab: "Inpainting", + name: "inpainting" + }, { default: withCtx(() => [ createVNode(Inpainting) ]), diff --git a/frontend/dist/assets/ImageBrowserView.css b/frontend/dist/assets/ImageBrowserView.css index 4aa8a1485..6aeb7953f 100644 --- a/frontend/dist/assets/ImageBrowserView.css +++ b/frontend/dist/assets/ImageBrowserView.css @@ -1,21 +1,21 @@ -.img-slider[data-v-c0900653] { +.img-slider[data-v-7b550fae] { aspect-ratio: 1/1; height: 182px; width: auto; } -.image-grid[data-v-c0900653] { +.image-grid[data-v-7b550fae] { display: grid; grid-template-columns: repeat( - var(--0fdcbc19), + var(--1bf86f7d), 1fr ); grid-gap: 8px; } -.top-bar[data-v-c0900653] { - background-color: var(--ae02b812); +.top-bar[data-v-7b550fae] { + background-color: var(--6326b8e7); } -.image-column[data-v-c0900653] { +.image-column[data-v-7b550fae] { display: flex; flex-direction: column; } diff --git a/frontend/dist/assets/ImageBrowserView.js b/frontend/dist/assets/ImageBrowserView.js index f8708cf79..30dd2ddc4 100644 --- a/frontend/dist/assets/ImageBrowserView.js +++ b/frontend/dist/assets/ImageBrowserView.js @@ -1,11 +1,11 @@ -import { d as defineComponent, b7 as useCssVars, u as useState, a as useSettings, E as ref, c as computed, b8 as reactive, b9 as onMounted, o as onUnmounted, e as openBlock, f as createElementBlock, n as createBaseVNode, g as createVNode, h as unref, w as withCtx, J as Fragment, M as renderList, s as serverUrl, k as NInput, G as NIcon, bd as NModal, y as NGrid, N as NGi, F as NButton, m as createTextVNode, O as NScrollbar, v as createBlock, t as toDisplayString, x as createCommentVNode, bC as urlFromPath, _ as _export_sfc } from "./index.js"; +import { d as defineComponent, b6 as useCssVars, u as useState, a as useSettings, R as inject, D as ref, c as computed, b8 as reactive, b9 as onMounted, o as onUnmounted, e as openBlock, f as createElementBlock, n as createBaseVNode, g as createVNode, h as unref, w as withCtx, I as Fragment, L as renderList, b7 as themeOverridesKey, s as serverUrl, k as NInput, F as NIcon, bd as NModal, y as NGrid, N as NGi, E as NButton, m as createTextVNode, M as NScrollbar, v as createBlock, bC as convertToTextString, t as toDisplayString, r as createCommentVNode, bI as urlFromPath, bJ as diffusersSchedulerTuple, _ as _export_sfc } from "./index.js"; import { D as Download, _ as _sfc_main$1 } from "./SendOutputTo.vue_vue_type_script_setup_true_lang.js"; import { G as GridOutline } from "./GridOutline.js"; import { N as NImage, T as TrashBin } from "./TrashBin.js"; -import { N as NSlider } from "./Slider.js"; +import { N as NSlider } from "./Switch.js"; import { N as NDescriptionsItem, a as NDescriptions } from "./DescriptionsItem.js"; const _hoisted_1 = { - style: { "width": "calc(100vw - 98px)", "height": "48px", "border-bottom": "#505050 1px solid", "margin-top": "53px", "display": "flex", "justify-content": "end", "align-items": "center", "padding-right": "24px", "position": "fixed", "top": "0", "z-index": "1" }, + style: { "width": "calc(100vw - 98px)", "height": "48px", "border-bottom": "#505050 1px solid", "margin-top": "52px", "display": "flex", "justify-content": "end", "align-items": "center", "padding-right": "24px", "position": "fixed", "top": "0", "z-index": "1" }, class: "top-bar" }; const _hoisted_2 = { @@ -17,12 +17,16 @@ const _hoisted_4 = ["src", "onClick"]; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "ImageBrowserView", setup(__props) { - useCssVars((_ctx) => ({ - "0fdcbc19": unref(conf).data.settings.frontend.image_browser_columns, - "ae02b812": backgroundColor.value - })); + useCssVars((_ctx) => { + var _a, _b; + return { + "1bf86f7d": unref(settings).data.settings.frontend.image_browser_columns, + "6326b8e7": (_b = (_a = unref(theme)) == null ? void 0 : _a.Card) == null ? void 0 : _b.color + }; + }); const global = useState(); - const conf = useSettings(); + const settings = useSettings(); + const theme = inject(themeOverridesKey); const showDeleteModal = ref(false); const showImageModal = ref(false); const scrollComponent = ref(null); @@ -34,7 +38,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ return url; }); function deleteImage() { - const url = new URL(`${serverUrl}/api/output/delete/`); + const url = new URL(`${serverUrl}/api/outputs/delete/`); url.searchParams.append( "filename", global.state.imageBrowser.currentImage.path @@ -51,16 +55,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ time: 0 }; global.state.imageBrowser.currentImageByte64 = ""; - global.state.imageBrowser.currentImageMetadata = /* @__PURE__ */ new Map(); + global.state.imageBrowser.currentImageMetadata = {}; }); } - function toDescriptionString(str) { - const upper = str.charAt(0).toUpperCase() + str.slice(1); - return upper.replace(/_/g, " "); - } function downloadImage() { const url = urlFromPath(global.state.imageBrowser.currentImage.path); - fetch(url).then((res) => res.blob()).then((blob) => { + fetch(url, { mode: "no-cors" }).then((res) => res.blob()).then((blob) => { const reader = new FileReader(); reader.readAsDataURL(blob); reader.onloadend = function() { @@ -74,7 +74,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ a.click(); document.body.removeChild(a); } else { - console.log("base64data is null!"); + console.error("base64data is null!"); } }; }); @@ -89,23 +89,45 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ if (base64data !== null) { global.state.imageBrowser.currentImageByte64 = base64data.toString(); } else { - console.log("base64data is null!"); + console.error("base64data is null!"); } }; }); } const currentColumn = ref(0); const currentRowIndex = ref(0); + function parseMetadataFromString(key, value) { + value = value.trim().toLowerCase(); + if (value === "true") { + return true; + } else if (value === "false") { + return false; + } else { + if (isFinite(+value)) { + return +value; + } else { + return value; + } + } + } function imgClick(column_index, item_index) { currentRowIndex.value = item_index; currentColumn.value = column_index; const item = columns.value[column_index][item_index]; global.state.imageBrowser.currentImage = item; setByte64FromImage(item.path); - const url = new URL(`${serverUrl}/api/output/data/`); + const url = new URL(`${serverUrl}/api/outputs/data/`); url.searchParams.append("filename", item.path); fetch(url).then((res) => res.json()).then((data) => { - global.state.imageBrowser.currentImageMetadata = data; + global.state.imageBrowser.currentImageMetadata = JSON.parse( + JSON.stringify(data), + (key, value) => { + if (typeof value === "string") { + return parseMetadataFromString(key, value); + } + return value; + } + ); }); showImageModal.value = true; } @@ -123,11 +145,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }); const columns = computed(() => { const cols = []; - for (let i = 0; i < conf.data.settings.frontend.image_browser_columns; i++) { + for (let i = 0; i < settings.data.settings.frontend.image_browser_columns; i++) { cols.push([]); } for (let i = 0; i < computedImgDataLimit.value; i++) { - cols[i % conf.data.settings.frontend.image_browser_columns].push( + cols[i % settings.data.settings.frontend.image_browser_columns].push( filteredImgData.value[i] ); } @@ -135,17 +157,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }); async function refreshImages() { imgData.splice(0, imgData.length); - await fetch(`${serverUrl}/api/output/txt2img`).then((res) => res.json()).then((data) => { + await fetch(`${serverUrl}/api/outputs/txt2img`).then((res) => res.json()).then((data) => { data.forEach((item) => { imgData.push(item); }); }); - await fetch(`${serverUrl}/api/output/img2img`).then((res) => res.json()).then((data) => { + await fetch(`${serverUrl}/api/outputs/img2img`).then((res) => res.json()).then((data) => { data.forEach((item) => { imgData.push(item); }); }); - await fetch(`${serverUrl}/api/output/extra`).then((res) => res.json()).then((data) => { + await fetch(`${serverUrl}/api/outputs/extra`).then((res) => res.json()).then((data) => { data.forEach((item) => { imgData.push(item); }); @@ -179,7 +201,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ } }; function moveImage(direction) { - const numColumns = conf.data.settings.frontend.image_browser_columns; + const numColumns = settings.data.settings.frontend.image_browser_columns; if (direction === -1) { if (currentColumn.value > 0) { imgClick(currentColumn.value - 1, currentRowIndex.value); @@ -214,16 +236,19 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ } }); }); - refreshImages(); - const backgroundColor = computed(() => { - if (conf.data.settings.frontend.theme === "dark") { - return "#121215"; - } else { - return "#fff"; + function getNamedSampler(value) { + const parsed_string = +value; + for (const objectKey of Object.keys(diffusersSchedulerTuple)) { + const val = diffusersSchedulerTuple[objectKey]; + if (val === parsed_string) { + return objectKey; + } } - }); + return value; + } + refreshImages(); return (_ctx, _cache) => { - return openBlock(), createElementBlock(Fragment, null, [ + return openBlock(), createElementBlock("div", null, [ createBaseVNode("div", _hoisted_1, [ createVNode(unref(NInput), { value: itemFilter.value, @@ -244,8 +269,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ style: { "width": "50vw" }, min: 1, max: 10, - value: unref(conf).data.settings.frontend.image_browser_columns, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(conf).data.settings.frontend.image_browser_columns = $event) + value: unref(settings).data.settings.frontend.image_browser_columns, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).data.settings.frontend.image_browser_columns = $event) }, null, 8, ["value"]) ]), createBaseVNode("div", _hoisted_2, [ @@ -268,6 +293,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ "onUpdate:show": _cache[5] || (_cache[5] = ($event) => showImageModal.value = $event), closable: "", "mask-closable": "", + "close-on-esc": "", preset: "card", style: { "width": "85vw" }, title: "Image Info", @@ -348,8 +374,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(_sfc_main$1, { output: unref(global).state.imageBrowser.currentImageByte64, - card: false - }, null, 8, ["output"]) + card: false, + data: unref(global).state.imageBrowser.currentImageMetadata + }, null, 8, ["output", "data"]) ]), _: 1 }) @@ -371,13 +398,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(global).state.imageBrowser.currentImageMetadata, (item, key) => { return openBlock(), createBlock(unref(NDescriptionsItem), { - label: toDescriptionString(key.toString()), + label: unref(convertToTextString)(key.toString()), "content-style": "max-width: 100px; word-wrap: break-word;", style: { "margin": "4px" }, key: item.toString() }, { default: withCtx(() => [ - createTextVNode(toDisplayString(item), 1) + createTextVNode(toDisplayString(key.toString() === "scheduler" ? getNamedSampler(item.toString()) : item), 1) ]), _: 2 }, 1032, ["label"]); @@ -423,12 +450,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ ]) ], 512) ]) - ], 64); + ]); }; } }); -const ImageBrowserView_vue_vue_type_style_index_0_scoped_c0900653_lang = ""; -const ImageBrowserView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-c0900653"]]); +const ImageBrowserView_vue_vue_type_style_index_0_scoped_7b550fae_lang = ""; +const ImageBrowserView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7b550fae"]]); export { ImageBrowserView as default }; diff --git a/frontend/dist/assets/ImageOutput.vue_vue_type_script_setup_true_lang.js b/frontend/dist/assets/ImageOutput.vue_vue_type_script_setup_true_lang.js index b1101e22c..a8094f9c2 100644 --- a/frontend/dist/assets/ImageOutput.vue_vue_type_script_setup_true_lang.js +++ b/frontend/dist/assets/ImageOutput.vue_vue_type_script_setup_true_lang.js @@ -1,4 +1,4 @@ -import { d as defineComponent, E as ref, u as useState, e as openBlock, v as createBlock, w as withCtx, h as unref, N as NGi, g as createVNode, G as NIcon, m as createTextVNode, F as NButton, x as createCommentVNode, y as NGrid, c as computed, n as createBaseVNode, f as createElementBlock, J as Fragment, M as renderList, O as NScrollbar, i as NCard } from "./index.js"; +import { d as defineComponent, D as ref, u as useState, e as openBlock, v as createBlock, w as withCtx, h as unref, N as NGi, g as createVNode, F as NIcon, m as createTextVNode, E as NButton, r as createCommentVNode, y as NGrid, c as computed, n as createBaseVNode, f as createElementBlock, I as Fragment, L as renderList, M as NScrollbar, i as NCard } from "./index.js"; import { D as Download, _ as _sfc_main$2 } from "./SendOutputTo.vue_vue_type_script_setup_true_lang.js"; import { T as TrashBin, N as NImage } from "./TrashBin.js"; const _sfc_main$1 = /* @__PURE__ */ defineComponent({ @@ -107,6 +107,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ type: Array, required: false, default: () => [] + }, + data: { + type: Object, + required: false, + default: () => ({}) } }, emits: ["image-clicked"], @@ -164,8 +169,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }, null, 8, ["base64image"]), createVNode(_sfc_main$2, { output: props.currentImage, - card: false - }, null, 8, ["output"]) + card: false, + data: __props.data + }, null, 8, ["output", "data"]) ])) : createCommentVNode("", true) ]), _: 1 diff --git a/frontend/dist/assets/ExtraView.css b/frontend/dist/assets/ImageProcessingView.css similarity index 66% rename from frontend/dist/assets/ExtraView.css rename to frontend/dist/assets/ImageProcessingView.css index 905e51d6a..c1171f5e1 100644 --- a/frontend/dist/assets/ExtraView.css +++ b/frontend/dist/assets/ImageProcessingView.css @@ -1,11 +1,11 @@ -.image-container img[data-v-2a772a2b] { +.image-container img[data-v-3d34d51a] { width: 100%; height: 100%; object-fit: contain; overflow: hidden; } -.image-container[data-v-2a772a2b] { +.image-container[data-v-3d34d51a] { height: 70vh; width: 100%; display: flex; diff --git a/frontend/dist/assets/ImageProcessingView.js b/frontend/dist/assets/ImageProcessingView.js new file mode 100644 index 000000000..340d4fa90 --- /dev/null +++ b/frontend/dist/assets/ImageProcessingView.js @@ -0,0 +1,241 @@ +import { _ as _sfc_main$2 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; +import { _ as _sfc_main$3 } from "./ImageOutput.vue_vue_type_script_setup_true_lang.js"; +import { I as ImageUpload } from "./ImageUpload.js"; +import { d as defineComponent, u as useState, a as useSettings, b as useMessage, c as computed, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, N as NGi, i as NCard, j as NSpace, n as createBaseVNode, x as NSelect, q as NTooltip, m as createTextVNode, y as NGrid, K as upscalerOptions, s as serverUrl, A as pushScopeId, B as popScopeId, _ as _export_sfc, v as createBlock, G as NTabPane, H as NTabs } from "./index.js"; +import { N as NSlider } from "./Switch.js"; +import { N as NInputNumber } from "./InputNumber.js"; +import "./SendOutputTo.vue_vue_type_script_setup_true_lang.js"; +import "./TrashBin.js"; +import "./CloudUpload.js"; +const _withScopeId = (n) => (pushScopeId("data-v-3d34d51a"), n = n(), popScopeId(), n); +const _hoisted_1 = { style: { "margin": "0 12px" } }; +const _hoisted_2 = { class: "flex-container" }; +const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Model", -1)); +const _hoisted_4 = { class: "flex-container" }; +const _hoisted_5 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Scale Factor", -1)); +const _hoisted_6 = { class: "flex-container" }; +const _hoisted_7 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Tile Size", -1)); +const _hoisted_8 = { class: "flex-container" }; +const _hoisted_9 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Tile Padding", -1)); +const _sfc_main$1 = /* @__PURE__ */ defineComponent({ + __name: "Upscale", + setup(__props) { + const global = useState(); + const settings = useSettings(); + const messageHandler = useMessage(); + const imageSelectCallback = (base64Image) => { + settings.data.settings.upscale.image = base64Image; + }; + const upscalerOptionsFull = computed(() => { + const localModels = global.state.models.filter( + (model) => model.backend === "Upscaler" && !(upscalerOptions.map((option) => option.label).indexOf(model.name) !== -1) + ).map((model) => ({ + label: model.name, + value: model.path + })); + return [...upscalerOptions, ...localModels]; + }); + const generate = () => { + global.state.generating = true; + fetch(`${serverUrl}/api/generate/upscale`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + data: { + image: settings.data.settings.upscale.image, + upscale_factor: settings.data.settings.upscale.upscale_factor, + model: settings.data.settings.upscale.model, + tile_size: settings.data.settings.upscale.tile_size, + tile_padding: settings.data.settings.upscale.tile_padding + }, + model: settings.data.settings.upscale.model + }) + }).then((res) => { + global.state.generating = false; + res.json().then((data) => { + global.state.imageProcessing.images = [data.images]; + global.state.progress = 0; + global.state.total_steps = 0; + global.state.current_step = 0; + }); + }).catch((err) => { + global.state.generating = false; + messageHandler.error(err); + }); + }; + return (_ctx, _cache) => { + return openBlock(), createElementBlock("div", _hoisted_1, [ + createVNode(unref(NGrid), { + cols: "1 m:2", + "x-gap": "12", + responsive: "screen" + }, { + default: withCtx(() => [ + createVNode(unref(NGi), null, { + default: withCtx(() => [ + createVNode(ImageUpload, { + callback: imageSelectCallback, + preview: unref(settings).data.settings.upscale.image, + style: { "margin-bottom": "12px" }, + onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(settings).data.settings.upscale.image = $event) + }, null, 8, ["preview"]), + createVNode(unref(NCard), { + title: "Settings", + style: { "margin-bottom": "12px" } + }, { + default: withCtx(() => [ + createVNode(unref(NSpace), { + vertical: "", + class: "left-container" + }, { + default: withCtx(() => [ + createBaseVNode("div", _hoisted_2, [ + _hoisted_3, + createVNode(unref(NSelect), { + value: unref(settings).data.settings.upscale.model, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).data.settings.upscale.model = $event), + style: { "margin-right": "12px" }, + filterable: "", + tag: "", + options: upscalerOptionsFull.value + }, null, 8, ["value", "options"]) + ]), + createBaseVNode("div", _hoisted_4, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_5 + ]), + default: withCtx(() => [ + createTextVNode(" TODO ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.upscale.upscale_factor, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).data.settings.upscale.upscale_factor = $event), + min: 1, + max: 4, + step: 0.1, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.upscale.upscale_factor, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).data.settings.upscale.upscale_factor = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 1, + max: 4, + step: 0.1 + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_6, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_7 + ]), + default: withCtx(() => [ + createTextVNode(" How large each tile should be. Larger tiles will use more memory. 0 will disable tiling. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.upscale.tile_size, + "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(settings).data.settings.upscale.tile_size = $event), + min: 32, + max: 2048, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.upscale.tile_size, + "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(settings).data.settings.upscale.tile_size = $event), + size: "small", + min: 32, + max: 2048, + style: { "min-width": "96px", "width": "96px" } + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_8, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_9 + ]), + default: withCtx(() => [ + createTextVNode(" How much should tiles overlap. Larger padding will use more memory, but image should not have visible seams. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.upscale.tile_padding, + "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).data.settings.upscale.tile_padding = $event), + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.upscale.tile_padding, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).data.settings.upscale.tile_padding = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" } + }, null, 8, ["value"]) + ]) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }), + createVNode(unref(NGi), null, { + default: withCtx(() => [ + createVNode(_sfc_main$2, { + generate, + "do-not-disable-generate": "" + }), + createVNode(_sfc_main$3, { + "current-image": unref(global).state.imageProcessing.currentImage, + images: unref(global).state.imageProcessing.images, + onImageClicked: _cache[8] || (_cache[8] = ($event) => unref(global).state.imageProcessing.currentImage = $event) + }, null, 8, ["current-image", "images"]) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]); + }; + } +}); +const Upscale_vue_vue_type_style_index_0_scoped_3d34d51a_lang = ""; +const Upscale = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-3d34d51a"]]); +const _sfc_main = /* @__PURE__ */ defineComponent({ + __name: "ImageProcessingView", + setup(__props) { + const state = useState(); + return (_ctx, _cache) => { + return openBlock(), createBlock(unref(NTabs), { + type: "segment", + value: unref(state).state.imageProcessing.tab, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(state).state.imageProcessing.tab = $event) + }, { + default: withCtx(() => [ + createVNode(unref(NTabPane), { + tab: "Upscale", + name: "upscale" + }, { + default: withCtx(() => [ + createVNode(Upscale) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["value"]); + }; + } +}); +export { + _sfc_main as default +}; diff --git a/frontend/dist/assets/ImageUpload.css b/frontend/dist/assets/ImageUpload.css index 1f13ea614..b288d96de 100644 --- a/frontend/dist/assets/ImageUpload.css +++ b/frontend/dist/assets/ImageUpload.css @@ -1,14 +1,14 @@ -.hidden-input[data-v-4f5be896] { +.hidden-input[data-v-9ed1514f] { display: none; } -.image-container img[data-v-4f5be896] { +.image-container img[data-v-9ed1514f] { width: 100%; height: 100%; object-fit: contain; overflow: hidden; } -.image-container[data-v-4f5be896] { +.image-container[data-v-9ed1514f] { height: 70vh; width: 100%; display: flex; diff --git a/frontend/dist/assets/ImageUpload.js b/frontend/dist/assets/ImageUpload.js index dd372b66e..cb99ea6ef 100644 --- a/frontend/dist/assets/ImageUpload.js +++ b/frontend/dist/assets/ImageUpload.js @@ -1,6 +1,6 @@ -import { d as defineComponent, E as ref, c as computed, b9 as onMounted, e as openBlock, v as createBlock, w as withCtx, n as createBaseVNode, bM as withModifiers, f as createElementBlock, g as createVNode, h as unref, G as NIcon, t as toDisplayString, i as NCard, A as pushScopeId, B as popScopeId, _ as _export_sfc } from "./index.js"; +import { d as defineComponent, D as ref, c as computed, b9 as onMounted, e as openBlock, v as createBlock, w as withCtx, n as createBaseVNode, bU as withModifiers, f as createElementBlock, g as createVNode, h as unref, F as NIcon, t as toDisplayString, i as NCard, A as pushScopeId, B as popScopeId, _ as _export_sfc } from "./index.js"; import { C as CloudUpload } from "./CloudUpload.js"; -const _withScopeId = (n) => (pushScopeId("data-v-4f5be896"), n = n(), popScopeId(), n); +const _withScopeId = (n) => (pushScopeId("data-v-9ed1514f"), n = n(), popScopeId(), n); const _hoisted_1 = { class: "image-container" }; const _hoisted_2 = { for: "file-upload", @@ -30,11 +30,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ const image = ref(); const width = computed(() => { var _a; - return image.value ? (_a = image.value) == null ? void 0 : _a.width : 0; + return image.value ? (_a = image.value) == null ? void 0 : _a.naturalWidth : 0; }); const height = computed(() => { var _a; - return image.value ? (_a = image.value) == null ? void 0 : _a.height : 0; + return image.value ? (_a = image.value) == null ? void 0 : _a.naturalHeight : 0; }); function previewImage(event) { const input = event.target; @@ -59,9 +59,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ } } function onDrop(e) { - var _a, _b; - console.log((_a = e.dataTransfer) == null ? void 0 : _a.files); - if ((_b = e.dataTransfer) == null ? void 0 : _b.files) { + var _a; + if ((_a = e.dataTransfer) == null ? void 0 : _a.files) { const reader = new FileReader(); reader.onload = (e2) => { var _a2; @@ -134,8 +133,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }; } }); -const ImageUpload_vue_vue_type_style_index_0_scoped_4f5be896_lang = ""; -const ImageUpload = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-4f5be896"]]); +const ImageUpload_vue_vue_type_style_index_0_scoped_9ed1514f_lang = ""; +const ImageUpload = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-9ed1514f"]]); export { ImageUpload as I }; diff --git a/frontend/dist/assets/InputNumber.js b/frontend/dist/assets/InputNumber.js index ffb28add5..df9ed4299 100644 --- a/frontend/dist/assets/InputNumber.js +++ b/frontend/dist/assets/InputNumber.js @@ -1,4 +1,4 @@ -import { d as defineComponent, D as h, X as c, Y as cB, Q as useConfig, a5 as useTheme, ar as useLocale, R as useFormItem, E as ref, U as toRef, S as useMergedState, a4 as useMemo, K as watch, a7 as useRtl, c as computed, k as NInput, aB as resolveWrappedSlot, bO as inputNumberLight, ac as on, bP as rgba, at as resolveSlot, au as NBaseIcon, bQ as XButton, b0 as AddIcon, W as call, ah as nextTick } from "./index.js"; +import { d as defineComponent, C as h, aa as c, Q as cB, S as useConfig, T as useTheme, ad as useLocale, ar as useFormItem, D as ref, V as toRef, ae as useMergedState, as as useMemo, J as watch, ag as useRtl, c as computed, k as NInput, av as resolveWrappedSlot, bW as inputNumberLight, aD as on, bX as rgba, ah as resolveSlot, ai as NBaseIcon, bY as XButton, a$ as AddIcon, $ as call, a0 as nextTick } from "./index.js"; const RemoveIcon = defineComponent({ name: "Remove", render() { @@ -519,6 +519,10 @@ const NInputNumber = defineComponent({ blur: () => { var _a; return (_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.blur(); + }, + select: () => { + var _a; + return (_a = inputInstRef.value) === null || _a === void 0 ? void 0 : _a.select(); } }; const rtlEnabledRef = useRtl("InputNumber", mergedRtlRef, mergedClsPrefixRef); diff --git a/frontend/dist/assets/ModelPopup.vue_vue_type_script_setup_true_lang.js b/frontend/dist/assets/ModelPopup.vue_vue_type_script_setup_true_lang.js index 3ba34b761..0dd4894d8 100644 --- a/frontend/dist/assets/ModelPopup.vue_vue_type_script_setup_true_lang.js +++ b/frontend/dist/assets/ModelPopup.vue_vue_type_script_setup_true_lang.js @@ -1,4 +1,4 @@ -import { bh as upperFirst, bi as toString, bj as createCompounder, bk as cloneVNode, T as provide, V as createInjectionKey, a3 as inject, a_ as throwError, d as defineComponent, Q as useConfig, E as ref, bl as onBeforeUpdate, D as h, bm as indexMap, c as computed, b9 as onMounted, aH as onBeforeUnmount, Y as cB, Z as cE, X as c, $ as cM, S as useMergedState, U as toRef, as as watchEffect, bn as onUpdated, K as watch, a5 as useTheme, a9 as useThemeClass, aC as flatten, aP as VResizeObserver, bo as resolveSlotWithProps, bp as withDirectives, bq as vShow, aX as Transition, ak as keep, aI as off, ah as nextTick, br as carouselLight, ba as normalizeStyle, bs as getPreciseEventTarget, ac as on, aq as cNotM, R as useFormItem, M as renderList, au as NBaseIcon, bt as rateLight, a8 as createKey, bu as color2Class, W as call, b as useMessage, a as useSettings, b8 as reactive, e as openBlock, v as createBlock, w as withCtx, h as unref, g as createVNode, f as createElementBlock, H as NTabPane, y as NGrid, N as NGi, J as Fragment, n as createBaseVNode, i as NCard, m as createTextVNode, t as toDisplayString, bv as NTag, r as NSelect, F as NButton, I as NTabs, bd as NModal, s as serverUrl } from "./index.js"; +import { bj as upperFirst, bk as toString, bl as createCompounder, bm as cloneVNode, a3 as provide, P as createInjectionKey, R as inject, a_ as throwError, d as defineComponent, S as useConfig, D as ref, bn as onBeforeUpdate, C as h, bo as indexMap, c as computed, b9 as onMounted, aB as onBeforeUnmount, Q as cB, at as cE, aa as c, ab as cM, ae as useMergedState, V as toRef, af as watchEffect, bp as onUpdated, J as watch, T as useTheme, W as useThemeClass, aw as flatten, aL as VResizeObserver, bq as resolveSlotWithProps, br as withDirectives, bs as vShow, aX as Transition, a4 as keep, aC as off, a0 as nextTick, bt as carouselLight, ba as normalizeStyle, bu as getPreciseEventTarget, aD as on, ac as cNotM, ar as useFormItem, L as renderList, ai as NBaseIcon, bv as rateLight, al as createKey, bw as color2Class, $ as call, b as useMessage, a as useSettings, b8 as reactive, e as openBlock, v as createBlock, w as withCtx, h as unref, g as createVNode, f as createElementBlock, G as NTabPane, y as NGrid, N as NGi, I as Fragment, n as createBaseVNode, i as NCard, m as createTextVNode, t as toDisplayString, bx as NTag, x as NSelect, E as NButton, H as NTabs, bd as NModal, s as serverUrl } from "./index.js"; import { a as NDescriptions, N as NDescriptionsItem } from "./DescriptionsItem.js"; function capitalize(string) { return upperFirst(toString(string).toLowerCase()); diff --git a/frontend/dist/assets/ModelsView.css b/frontend/dist/assets/ModelsView.css index 68df8d970..d45d94eae 100644 --- a/frontend/dist/assets/ModelsView.css +++ b/frontend/dist/assets/ModelsView.css @@ -1,26 +1,26 @@ -.img-slider[data-v-07574b72] { +.img-slider[data-v-f55237b7] { aspect-ratio: 1/1; height: 182px; width: auto; } -.image-grid[data-v-07574b72] { +.image-grid[data-v-f55237b7] { display: grid; grid-template-columns: repeat( - var(--9fc29920), + var(--52455add), 1fr ); grid-gap: 8px; } -.top-bar[data-v-07574b72] { - background-color: var(--714c7f6d); +.top-bar[data-v-f55237b7] { + background-color: var(--5509ecf2); } -.image-column[data-v-07574b72] { +.image-column[data-v-f55237b7] { display: flex; flex-direction: column; } -.install[data-v-41346243] { +.install[data-v-b405f046] { width: 100%; padding: 10px 0px; } diff --git a/frontend/dist/assets/ModelsView.js b/frontend/dist/assets/ModelsView.js index 6f7d85932..b87daaedc 100644 --- a/frontend/dist/assets/ModelsView.js +++ b/frontend/dist/assets/ModelsView.js @@ -1,9 +1,9 @@ -import { d as defineComponent, D as h, P as replaceable, Q as useConfig, R as useFormItem, E as ref, c as computed, S as useMergedState, T as provide, U as toRef, V as createInjectionKey, W as call, X as c, Y as cB, Z as cE, $ as cM, a0 as iconSwitchTransition, a1 as insideModal, a2 as insidePopover, a3 as inject, a4 as useMemo, a5 as useTheme, a6 as checkboxLight, a7 as useRtl, a8 as createKey, a9 as useThemeClass, aa as createId, ab as NIconSwitchTransition, ac as on, ad as popselectLight, K as watch, ae as NInternalSelectMenu, af as createTreeMate, ag as happensIn, ah as nextTick, ai as keysOf, aj as createTmOptions, ak as keep, al as createRefSetter, am as mergeEventHandlers, an as omit, ao as NPopover, ap as popoverBaseProps, aq as cNotM, ar as useLocale, as as watchEffect, at as resolveSlot, k as NInput, r as NSelect, J as Fragment, au as NBaseIcon, av as useAdjustedTo, aw as paginationLight, ax as ellipsisLight, ay as onDeactivated, q as NTooltip, az as mergeProps, aA as radioLight, aB as resolveWrappedSlot, aC as flatten$1, aD as getSlot, aE as depx, aF as formatLength, F as NButton, aG as NScrollbar, aH as onBeforeUnmount, aI as off, aJ as ChevronDownIcon, aK as NDropdown, aL as pxfy, aM as get, aN as NBaseLoading, aO as ChevronRightIcon, o as onUnmounted, aP as VResizeObserver, aQ as warn, aR as cssrAnchorMetaName, aS as VVirtualList, aT as NEmpty, aU as repeat, aV as beforeNextFrameOnce, aW as fadeInScaleUpTransition, aX as Transition, aY as dataTableLight, aZ as loadingBarApiInjectionKey, a_ as throwError, a$ as isBrowser, b0 as AddIcon, b1 as NProgress, b2 as NFadeInExpandTransition, b3 as EyeIcon, b4 as fadeInHeightExpandTransition, b5 as Teleport, b6 as uploadLight, e as openBlock, f as createElementBlock, n as createBaseVNode, b7 as useCssVars, h as unref, a as useSettings, b8 as reactive, b9 as onMounted, g as createVNode, w as withCtx, G as NIcon, M as renderList, ba as normalizeStyle, m as createTextVNode, t as toDisplayString, bb as NText, _ as _export_sfc, u as useState, b as useMessage, bc as huggingfaceModelsFile, i as NCard, s as serverUrl, A as pushScopeId, B as popScopeId, j as NSpace, bd as NModal, N as NGi, y as NGrid, v as createBlock, H as NTabPane, I as NTabs } from "./index.js"; +import { d as defineComponent, C as h, O as replaceable, P as createInjectionKey, Q as cB, R as inject, S as useConfig, T as useTheme, U as popselectLight, c as computed, J as watch, V as toRef, W as useThemeClass, X as NInternalSelectMenu, Y as createTreeMate, Z as happensIn, $ as call, a0 as nextTick, a1 as keysOf, a2 as createTmOptions, D as ref, a3 as provide, a4 as keep, a5 as createRefSetter, a6 as mergeEventHandlers, a7 as omit, a8 as NPopover, a9 as popoverBaseProps, aa as c, ab as cM, ac as cNotM, ad as useLocale, ae as useMergedState, af as watchEffect, ag as useRtl, ah as resolveSlot, k as NInput, x as NSelect, I as Fragment, ai as NBaseIcon, aj as useAdjustedTo, ak as paginationLight, al as createKey, am as useMergedClsPrefix, an as ellipsisLight, ao as onDeactivated, q as NTooltip, ap as mergeProps, aq as useStyle, ar as useFormItem, as as useMemo, at as cE, au as radioLight, av as resolveWrappedSlot, aw as flatten$1, ax as getSlot, ay as depx, az as formatLength, E as NButton, aA as NScrollbar, aB as onBeforeUnmount, aC as off, aD as on, aE as ChevronDownIcon, aF as NDropdown, aG as pxfy, aH as get, aI as NIconSwitchTransition, aJ as NBaseLoading, aK as ChevronRightIcon, o as onUnmounted, aL as VResizeObserver, aM as warn, aN as cssrAnchorMetaName, aO as VVirtualList, aP as NEmpty, aQ as repeat, aR as beforeNextFrameOnce, aS as fadeInScaleUpTransition, aT as iconSwitchTransition, aU as insideModal, aV as insidePopover, aW as createId, aX as Transition, aY as dataTableLight, aZ as loadingBarApiInjectionKey, a_ as throwError, a$ as AddIcon, b0 as NProgress, b1 as NFadeInExpandTransition, b2 as EyeIcon, b3 as fadeInHeightExpandTransition, b4 as Teleport, b5 as uploadLight, e as openBlock, f as createElementBlock, n as createBaseVNode, b6 as useCssVars, h as unref, a as useSettings, b7 as themeOverridesKey, b8 as reactive, b9 as onMounted, g as createVNode, w as withCtx, F as NIcon, L as renderList, ba as normalizeStyle, m as createTextVNode, t as toDisplayString, bb as NText, _ as _export_sfc, u as useState, b as useMessage, bc as huggingfaceModelsFile, i as NCard, s as serverUrl, A as pushScopeId, B as popScopeId, j as NSpace, bd as NModal, v as createBlock, N as NGi, y as NGrid, be as NDivider, bf as Backends, G as NTabPane, H as NTabs } from "./index.js"; import { _ as _sfc_main$5, n as nsfwIndex } from "./ModelPopup.vue_vue_type_script_setup_true_lang.js"; import { G as GridOutline } from "./GridOutline.js"; -import { N as NSlider } from "./Slider.js"; -import { N as NSwitch } from "./Switch.js"; -import { N as NImage, a as NImageGroup, T as TrashBin } from "./TrashBin.js"; +import { N as NSlider, a as NSwitch } from "./Switch.js"; +import { N as NCheckboxGroup, a as NCheckbox, S as Settings } from "./Settings.js"; +import { g as getFilesFromEntries, i as isImageFile, N as NImage, d as download, a as NImageGroup, e as environmentSupportFile, c as createSettledFileInfo, m as matchType, b as createImageDataUrl, T as TrashBin } from "./TrashBin.js"; import { C as CloudUpload } from "./CloudUpload.js"; import "./DescriptionsItem.js"; function smallerSize(size) { @@ -184,526 +184,6 @@ const RetryIcon = replaceable("retry", h( h("path", { d: "M320,146s24.36-12-64-12A160,160,0,1,0,416,294", style: "fill: none; stroke: currentcolor; stroke-linecap: round; stroke-miterlimit: 10; stroke-width: 32px;" }), h("polyline", { points: "256 58 336 138 256 218", style: "fill: none; stroke: currentcolor; stroke-linecap: round; stroke-linejoin: round; stroke-width: 32px;" }) )); -const CheckMark = h( - "svg", - { viewBox: "0 0 64 64", class: "check-icon" }, - h("path", { d: "M50.42,16.76L22.34,39.45l-8.1-11.46c-1.12-1.58-3.3-1.96-4.88-0.84c-1.58,1.12-1.95,3.3-0.84,4.88l10.26,14.51 c0.56,0.79,1.42,1.31,2.38,1.45c0.16,0.02,0.32,0.03,0.48,0.03c0.8,0,1.57-0.27,2.2-0.78l30.99-25.03c1.5-1.21,1.74-3.42,0.52-4.92 C54.13,15.78,51.93,15.55,50.42,16.76z" }) -); -const LineMark = h( - "svg", - { viewBox: "0 0 100 100", class: "line-icon" }, - h("path", { d: "M80.2,55.5H21.4c-2.8,0-5.1-2.5-5.1-5.5l0,0c0-3,2.3-5.5,5.1-5.5h58.7c2.8,0,5.1,2.5,5.1,5.5l0,0C85.2,53.1,82.9,55.5,80.2,55.5z" }) -); -const checkboxGroupInjectionKey = createInjectionKey("n-checkbox-group"); -const checkboxGroupProps = { - min: Number, - max: Number, - size: String, - value: Array, - defaultValue: { - type: Array, - default: null - }, - disabled: { - type: Boolean, - default: void 0 - }, - "onUpdate:value": [Function, Array], - onUpdateValue: [Function, Array], - // deprecated - onChange: [Function, Array] -}; -const NCheckboxGroup = defineComponent({ - name: "CheckboxGroup", - props: checkboxGroupProps, - setup(props) { - const { mergedClsPrefixRef } = useConfig(props); - const formItem = useFormItem(props); - const { mergedSizeRef, mergedDisabledRef } = formItem; - const uncontrolledValueRef = ref(props.defaultValue); - const controlledValueRef = computed(() => props.value); - const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef); - const checkedCount = computed(() => { - var _a; - return ((_a = mergedValueRef.value) === null || _a === void 0 ? void 0 : _a.length) || 0; - }); - const valueSetRef = computed(() => { - if (Array.isArray(mergedValueRef.value)) { - return new Set(mergedValueRef.value); - } - return /* @__PURE__ */ new Set(); - }); - function toggleCheckbox(checked, checkboxValue) { - const { nTriggerFormInput, nTriggerFormChange } = formItem; - const { onChange, "onUpdate:value": _onUpdateValue, onUpdateValue } = props; - if (Array.isArray(mergedValueRef.value)) { - const groupValue = Array.from(mergedValueRef.value); - const index = groupValue.findIndex((value) => value === checkboxValue); - if (checked) { - if (!~index) { - groupValue.push(checkboxValue); - if (onUpdateValue) { - call(onUpdateValue, groupValue, { - actionType: "check", - value: checkboxValue - }); - } - if (_onUpdateValue) { - call(_onUpdateValue, groupValue, { - actionType: "check", - value: checkboxValue - }); - } - nTriggerFormInput(); - nTriggerFormChange(); - uncontrolledValueRef.value = groupValue; - if (onChange) - call(onChange, groupValue); - } - } else { - if (~index) { - groupValue.splice(index, 1); - if (onUpdateValue) { - call(onUpdateValue, groupValue, { - actionType: "uncheck", - value: checkboxValue - }); - } - if (_onUpdateValue) { - call(_onUpdateValue, groupValue, { - actionType: "uncheck", - value: checkboxValue - }); - } - if (onChange) - call(onChange, groupValue); - uncontrolledValueRef.value = groupValue; - nTriggerFormInput(); - nTriggerFormChange(); - } - } - } else { - if (checked) { - if (onUpdateValue) { - call(onUpdateValue, [checkboxValue], { - actionType: "check", - value: checkboxValue - }); - } - if (_onUpdateValue) { - call(_onUpdateValue, [checkboxValue], { - actionType: "check", - value: checkboxValue - }); - } - if (onChange) - call(onChange, [checkboxValue]); - uncontrolledValueRef.value = [checkboxValue]; - nTriggerFormInput(); - nTriggerFormChange(); - } else { - if (onUpdateValue) { - call(onUpdateValue, [], { - actionType: "uncheck", - value: checkboxValue - }); - } - if (_onUpdateValue) { - call(_onUpdateValue, [], { - actionType: "uncheck", - value: checkboxValue - }); - } - if (onChange) - call(onChange, []); - uncontrolledValueRef.value = []; - nTriggerFormInput(); - nTriggerFormChange(); - } - } - } - provide(checkboxGroupInjectionKey, { - checkedCountRef: checkedCount, - maxRef: toRef(props, "max"), - minRef: toRef(props, "min"), - valueSetRef, - disabledRef: mergedDisabledRef, - mergedSizeRef, - toggleCheckbox - }); - return { - mergedClsPrefix: mergedClsPrefixRef - }; - }, - render() { - return h("div", { class: `${this.mergedClsPrefix}-checkbox-group`, role: "group" }, this.$slots); - } -}); -const style$7 = c([ - cB("checkbox", ` - line-height: var(--n-label-line-height); - font-size: var(--n-font-size); - outline: none; - cursor: pointer; - display: inline-flex; - flex-wrap: nowrap; - align-items: flex-start; - word-break: break-word; - --n-merged-color-table: var(--n-color-table); - `, [c("&:hover", [cB("checkbox-box", [cE("border", { - border: "var(--n-border-checked)" - })])]), c("&:focus:not(:active)", [cB("checkbox-box", [cE("border", ` - border: var(--n-border-focus); - box-shadow: var(--n-box-shadow-focus); - `)])]), cM("inside-table", [cB("checkbox-box", ` - background-color: var(--n-merged-color-table); - `)]), cM("checked", [cB("checkbox-box", ` - background-color: var(--n-color-checked); - `, [cB("checkbox-icon", [ - // if not set width to 100%, safari & old chrome won't display the icon - c(".check-icon", ` - opacity: 1; - transform: scale(1); - `) - ])])]), cM("indeterminate", [cB("checkbox-box", [cB("checkbox-icon", [c(".check-icon", ` - opacity: 0; - transform: scale(.5); - `), c(".line-icon", ` - opacity: 1; - transform: scale(1); - `)])])]), cM("checked, indeterminate", [c("&:focus:not(:active)", [cB("checkbox-box", [cE("border", ` - border: var(--n-border-checked); - box-shadow: var(--n-box-shadow-focus); - `)])]), cB("checkbox-box", ` - background-color: var(--n-color-checked); - border-left: 0; - border-top: 0; - `, [cE("border", { - border: "var(--n-border-checked)" - })])]), cM("disabled", { - cursor: "not-allowed" - }, [cM("checked", [cB("checkbox-box", ` - background-color: var(--n-color-disabled-checked); - `, [cE("border", { - border: "var(--n-border-disabled-checked)" - }), cB("checkbox-icon", [c(".check-icon, .line-icon", { - fill: "var(--n-check-mark-color-disabled-checked)" - })])])]), cB("checkbox-box", ` - background-color: var(--n-color-disabled); - `, [cE("border", { - border: "var(--n-border-disabled)" - }), cB("checkbox-icon", [c(".check-icon, .line-icon", { - fill: "var(--n-check-mark-color-disabled)" - })])]), cE("label", { - color: "var(--n-text-color-disabled)" - })]), cB("checkbox-box-wrapper", ` - position: relative; - width: var(--n-size); - flex-shrink: 0; - flex-grow: 0; - user-select: none; - -webkit-user-select: none; - `), cB("checkbox-box", ` - position: absolute; - left: 0; - top: 50%; - transform: translateY(-50%); - height: var(--n-size); - width: var(--n-size); - display: inline-block; - box-sizing: border-box; - border-radius: var(--n-border-radius); - background-color: var(--n-color); - transition: background-color 0.3s var(--n-bezier); - `, [cE("border", ` - transition: - border-color .3s var(--n-bezier), - box-shadow .3s var(--n-bezier); - border-radius: inherit; - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - border: var(--n-border); - `), cB("checkbox-icon", ` - display: flex; - align-items: center; - justify-content: center; - position: absolute; - left: 1px; - right: 1px; - top: 1px; - bottom: 1px; - `, [c(".check-icon, .line-icon", ` - width: 100%; - fill: var(--n-check-mark-color); - opacity: 0; - transform: scale(0.5); - transform-origin: center; - transition: - fill 0.3s var(--n-bezier), - transform 0.3s var(--n-bezier), - opacity 0.3s var(--n-bezier), - border-color 0.3s var(--n-bezier); - `), iconSwitchTransition({ - left: "1px", - top: "1px" - })])]), cE("label", ` - color: var(--n-text-color); - transition: color .3s var(--n-bezier); - user-select: none; - -webkit-user-select: none; - padding: var(--n-label-padding); - font-weight: var(--n-label-font-weight); - `, [c("&:empty", { - display: "none" - })])]), - // modal table header checkbox - insideModal(cB("checkbox", ` - --n-merged-color-table: var(--n-color-table-modal); - `)), - // popover table header checkbox - insidePopover(cB("checkbox", ` - --n-merged-color-table: var(--n-color-table-popover); - `)) -]); -const checkboxProps = Object.assign(Object.assign({}, useTheme.props), { - size: String, - checked: { - type: [Boolean, String, Number], - default: void 0 - }, - defaultChecked: { - type: [Boolean, String, Number], - default: false - }, - value: [String, Number], - disabled: { - type: Boolean, - default: void 0 - }, - indeterminate: Boolean, - label: String, - focusable: { - type: Boolean, - default: true - }, - checkedValue: { - type: [Boolean, String, Number], - default: true - }, - uncheckedValue: { - type: [Boolean, String, Number], - default: false - }, - "onUpdate:checked": [Function, Array], - onUpdateChecked: [Function, Array], - // private - privateInsideTable: Boolean, - // deprecated - onChange: [Function, Array] -}); -const NCheckbox = defineComponent({ - name: "Checkbox", - props: checkboxProps, - setup(props) { - const selfRef = ref(null); - const { mergedClsPrefixRef, inlineThemeDisabled, mergedRtlRef } = useConfig(props); - const formItem = useFormItem(props, { - mergedSize(NFormItem) { - const { size } = props; - if (size !== void 0) - return size; - if (NCheckboxGroup2) { - const { value: mergedSize } = NCheckboxGroup2.mergedSizeRef; - if (mergedSize !== void 0) { - return mergedSize; - } - } - if (NFormItem) { - const { mergedSize } = NFormItem; - if (mergedSize !== void 0) - return mergedSize.value; - } - return "medium"; - }, - mergedDisabled(NFormItem) { - const { disabled } = props; - if (disabled !== void 0) - return disabled; - if (NCheckboxGroup2) { - if (NCheckboxGroup2.disabledRef.value) - return true; - const { maxRef: { value: max }, checkedCountRef } = NCheckboxGroup2; - if (max !== void 0 && checkedCountRef.value >= max && !renderedCheckedRef.value) { - return true; - } - const { minRef: { value: min } } = NCheckboxGroup2; - if (min !== void 0 && checkedCountRef.value <= min && renderedCheckedRef.value) { - return true; - } - } - if (NFormItem) { - return NFormItem.disabled.value; - } - return false; - } - }); - const { mergedDisabledRef, mergedSizeRef } = formItem; - const NCheckboxGroup2 = inject(checkboxGroupInjectionKey, null); - const uncontrolledCheckedRef = ref(props.defaultChecked); - const controlledCheckedRef = toRef(props, "checked"); - const mergedCheckedRef = useMergedState(controlledCheckedRef, uncontrolledCheckedRef); - const renderedCheckedRef = useMemo(() => { - if (NCheckboxGroup2) { - const groupValueSet = NCheckboxGroup2.valueSetRef.value; - if (groupValueSet && props.value !== void 0) { - return groupValueSet.has(props.value); - } - return false; - } else { - return mergedCheckedRef.value === props.checkedValue; - } - }); - const themeRef = useTheme("Checkbox", "-checkbox", style$7, checkboxLight, props, mergedClsPrefixRef); - function toggle(e) { - if (NCheckboxGroup2 && props.value !== void 0) { - NCheckboxGroup2.toggleCheckbox(!renderedCheckedRef.value, props.value); - } else { - const { onChange, "onUpdate:checked": _onUpdateCheck, onUpdateChecked } = props; - const { nTriggerFormInput, nTriggerFormChange } = formItem; - const nextChecked = renderedCheckedRef.value ? props.uncheckedValue : props.checkedValue; - if (_onUpdateCheck) { - call(_onUpdateCheck, nextChecked, e); - } - if (onUpdateChecked) { - call(onUpdateChecked, nextChecked, e); - } - if (onChange) - call(onChange, nextChecked, e); - nTriggerFormInput(); - nTriggerFormChange(); - uncontrolledCheckedRef.value = nextChecked; - } - } - function handleClick(e) { - if (!mergedDisabledRef.value) { - toggle(e); - } - } - function handleKeyUp(e) { - if (mergedDisabledRef.value) - return; - switch (e.key) { - case " ": - case "Enter": - toggle(e); - } - } - function handleKeyDown(e) { - switch (e.key) { - case " ": - e.preventDefault(); - } - } - const exposedMethods = { - focus: () => { - var _a; - (_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.focus(); - }, - blur: () => { - var _a; - (_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.blur(); - } - }; - const rtlEnabledRef = useRtl("Checkbox", mergedRtlRef, mergedClsPrefixRef); - const cssVarsRef = computed(() => { - const { value: mergedSize } = mergedSizeRef; - const { common: { cubicBezierEaseInOut }, self: { borderRadius, color, colorChecked, colorDisabled, colorTableHeader, colorTableHeaderModal, colorTableHeaderPopover, checkMarkColor, checkMarkColorDisabled, border, borderFocus, borderDisabled, borderChecked, boxShadowFocus, textColor, textColorDisabled, checkMarkColorDisabledChecked, colorDisabledChecked, borderDisabledChecked, labelPadding, labelLineHeight, labelFontWeight, [createKey("fontSize", mergedSize)]: fontSize, [createKey("size", mergedSize)]: size } } = themeRef.value; - return { - "--n-label-line-height": labelLineHeight, - "--n-label-font-weight": labelFontWeight, - "--n-size": size, - "--n-bezier": cubicBezierEaseInOut, - "--n-border-radius": borderRadius, - "--n-border": border, - "--n-border-checked": borderChecked, - "--n-border-focus": borderFocus, - "--n-border-disabled": borderDisabled, - "--n-border-disabled-checked": borderDisabledChecked, - "--n-box-shadow-focus": boxShadowFocus, - "--n-color": color, - "--n-color-checked": colorChecked, - "--n-color-table": colorTableHeader, - "--n-color-table-modal": colorTableHeaderModal, - "--n-color-table-popover": colorTableHeaderPopover, - "--n-color-disabled": colorDisabled, - "--n-color-disabled-checked": colorDisabledChecked, - "--n-text-color": textColor, - "--n-text-color-disabled": textColorDisabled, - "--n-check-mark-color": checkMarkColor, - "--n-check-mark-color-disabled": checkMarkColorDisabled, - "--n-check-mark-color-disabled-checked": checkMarkColorDisabledChecked, - "--n-font-size": fontSize, - "--n-label-padding": labelPadding - }; - }); - const themeClassHandle = inlineThemeDisabled ? useThemeClass("checkbox", computed(() => mergedSizeRef.value[0]), cssVarsRef, props) : void 0; - return Object.assign(formItem, exposedMethods, { - rtlEnabled: rtlEnabledRef, - selfRef, - mergedClsPrefix: mergedClsPrefixRef, - mergedDisabled: mergedDisabledRef, - renderedChecked: renderedCheckedRef, - mergedTheme: themeRef, - labelId: createId(), - handleClick, - handleKeyUp, - handleKeyDown, - cssVars: inlineThemeDisabled ? void 0 : cssVarsRef, - themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, - onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender - }); - }, - render() { - var _a; - const { $slots, renderedChecked, mergedDisabled, indeterminate, privateInsideTable, cssVars, labelId, label, mergedClsPrefix, focusable, handleKeyUp, handleKeyDown, handleClick } = this; - (_a = this.onRender) === null || _a === void 0 ? void 0 : _a.call(this); - return h( - "div", - { ref: "selfRef", class: [ - `${mergedClsPrefix}-checkbox`, - this.themeClass, - this.rtlEnabled && `${mergedClsPrefix}-checkbox--rtl`, - renderedChecked && `${mergedClsPrefix}-checkbox--checked`, - mergedDisabled && `${mergedClsPrefix}-checkbox--disabled`, - indeterminate && `${mergedClsPrefix}-checkbox--indeterminate`, - privateInsideTable && `${mergedClsPrefix}-checkbox--inside-table` - ], tabindex: mergedDisabled || !focusable ? void 0 : 0, role: "checkbox", "aria-checked": indeterminate ? "mixed" : renderedChecked, "aria-labelledby": labelId, style: cssVars, onKeyup: handleKeyUp, onKeydown: handleKeyDown, onClick: handleClick, onMousedown: () => { - on("selectstart", window, (e) => { - e.preventDefault(); - }, { - once: true - }); - } }, - h( - "div", - { class: `${mergedClsPrefix}-checkbox-box-wrapper` }, - " ", - h( - "div", - { class: `${mergedClsPrefix}-checkbox-box` }, - h(NIconSwitchTransition, null, { - default: () => this.indeterminate ? h("div", { key: "indeterminate", class: `${mergedClsPrefix}-checkbox-icon` }, LineMark) : h("div", { key: "check", class: `${mergedClsPrefix}-checkbox-icon` }, CheckMark) - }), - h("div", { class: `${mergedClsPrefix}-checkbox-box__border` }) - ) - ), - label !== null || $slots.default ? h("span", { class: `${mergedClsPrefix}-checkbox__label`, id: labelId }, $slots.default ? $slots.default() : label) : null - ); - } -}); const popselectInjectionKey = createInjectionKey("n-popselect"); const style$6 = cB("popselect-menu", ` box-shadow: var(--n-menu-box-shadow); @@ -1738,7 +1218,7 @@ const NEllipsis = defineComponent({ inheritAttrs: false, props: ellipsisProps, setup(props, { slots, attrs }) { - const { mergedClsPrefixRef } = useConfig(props); + const mergedClsPrefixRef = useMergedClsPrefix(); const mergedTheme = useTheme("Ellipsis", "-ellipsis", style$4, ellipsisLight, props, mergedClsPrefixRef); const triggerRef = ref(null); const triggerInnerRef = ref(null); @@ -1865,6 +1345,45 @@ const NEllipsis = defineComponent({ return renderTrigger(); } }); +const NPerformantEllipsis = defineComponent({ + name: "PerformantEllipsis", + props: ellipsisProps, + inheritAttrs: false, + setup(props, { attrs, slots }) { + const mouseEnteredRef = ref(false); + const mergedClsPrefixRef = useMergedClsPrefix(); + useStyle("-ellipsis", style$4, mergedClsPrefixRef); + const renderTrigger = () => { + const { lineClamp } = props; + const mergedClsPrefix = mergedClsPrefixRef.value; + return h("span", Object.assign({}, mergeProps(attrs, { + class: [ + `${mergedClsPrefix}-ellipsis`, + lineClamp !== void 0 ? createLineClampClass(mergedClsPrefix) : void 0, + props.expandTrigger === "click" ? createCursorClass(mergedClsPrefix, "pointer") : void 0 + ], + style: lineClamp === void 0 ? { + textOverflow: "ellipsis" + } : { + "-webkit-line-clamp": lineClamp + } + }), { onMouseenter: () => { + mouseEnteredRef.value = true; + } }), lineClamp ? slots : h("span", null, slots)); + }; + return { + mouseEntered: mouseEnteredRef, + renderTrigger + }; + }, + render() { + if (this.mouseEntered) { + return h(NEllipsis, mergeProps({}, this.$attrs, this.$props), this.$slots); + } else { + return this.renderTrigger(); + } + } +}); const RenderSorter = defineComponent({ name: "DataTableRenderSorter", props: { @@ -1879,8 +1398,8 @@ const RenderSorter = defineComponent({ } }, render() { - const { render: render5, order } = this; - return render5({ + const { render: render4, order } = this; + return render4({ order }); } @@ -2056,14 +1575,14 @@ const RenderFilter = defineComponent({ } }, render() { - const { render: render5, active, show } = this; - return render5({ + const { render: render4, active, show } = this; + return render4({ active, show }); } }); -const radioProps = { +const radioBaseProps = { name: String, value: { type: [String, Number, Boolean], @@ -2272,9 +1791,10 @@ const style$3 = cB("radio", ` }), cB("radio-input", ` cursor: not-allowed; `)])]); +const radioProps = Object.assign(Object.assign({}, useTheme.props), radioBaseProps); const NRadio = defineComponent({ name: "Radio", - props: Object.assign(Object.assign({}, useTheme.props), radioProps), + props: radioProps, setup(props) { const radio = setup(props); const themeRef = useTheme("Radio", "-radio", style$3, radioLight, props, radio.mergedClsPrefix); @@ -2951,6 +2471,7 @@ const ResizeButton = defineComponent({ } function handleMousedown(e) { var _a; + e.preventDefault(); const alreadyStarted = activeRef.value; startX = getMouseX(e); activeRef.value = true; @@ -3090,7 +2611,6 @@ const TableHeader = defineComponent({ checkOptionsRef, mergedSortStateRef, componentId, - scrollPartRef, mergedTableLayoutRef, headerCheckboxDisabledRef, onUnstableColumnResize, @@ -3123,12 +2643,6 @@ const TableHeader = defineComponent({ const nextSorter = createNextSorter(column, activeSorter); deriveNextSorter(nextSorter); } - function handleMouseenter() { - scrollPartRef.value = "head"; - } - function handleMouseleave() { - scrollPartRef.value = "body"; - } const resizeStartWidthMap = /* @__PURE__ */ new Map(); function handleColumnResizeStart(column) { resizeStartWidthMap.set(column.key, getCellActualWidth(column.key)); @@ -3160,8 +2674,6 @@ const TableHeader = defineComponent({ checkOptions: checkOptionsRef, mergedTableLayout: mergedTableLayoutRef, headerCheckboxDisabled: headerCheckboxDisabledRef, - handleMouseenter, - handleMouseleave, handleCheckboxUpdateChecked, handleColHeaderClick, handleTableHeaderScroll, @@ -3229,10 +2741,10 @@ const TableHeader = defineComponent({ if (!discrete) { return theadVNode; } - const { handleTableHeaderScroll, handleMouseenter, handleMouseleave, scrollX } = this; + const { handleTableHeaderScroll, scrollX } = this; return h( "div", - { class: `${mergedClsPrefix}-data-table-base-table-header`, onScroll: handleTableHeaderScroll, onMouseenter: handleMouseenter, onMouseleave: handleMouseleave }, + { class: `${mergedClsPrefix}-data-table-base-table-header`, onScroll: handleTableHeaderScroll }, h( "table", { ref: "body", class: `${mergedClsPrefix}-data-table-table`, style: { @@ -3274,9 +2786,9 @@ const Cell = defineComponent({ render() { const { isSummary, column, row, renderCell } = this; let cell; - const { render: render5, key, ellipsis } = column; - if (render5 && !isSummary) { - cell = render5(row, this.index); + const { render: render4, key, ellipsis } = column; + if (render4 && !isSummary) { + cell = render4(row, this.index); } else { if (isSummary) { cell = row[key].value; @@ -3287,6 +2799,9 @@ const Cell = defineComponent({ if (ellipsis) { if (typeof ellipsis === "object") { const { mergedTheme } = this; + if (column.ellipsisComponent === "performant-ellipsis") { + return h(NPerformantEllipsis, Object.assign({}, ellipsis, { theme: mergedTheme.peers.Ellipsis, themeOverrides: mergedTheme.peerOverrides.Ellipsis }), { default: () => cell }); + } return h(NEllipsis, Object.assign({}, ellipsis, { theme: mergedTheme.peers.Ellipsis, themeOverrides: mergedTheme.peerOverrides.Ellipsis }), { default: () => cell }); } else { return h("span", { class: `${this.clsPrefix}-data-table-td__ellipsis` }, cell); @@ -3319,7 +2834,9 @@ const ExpandTrigger = defineComponent({ { class: [ `${clsPrefix}-data-table-expand-trigger`, this.expanded && `${clsPrefix}-data-table-expand-trigger--expanded` - ], onClick: this.onClick }, + ], onClick: this.onClick, onMousedown: (e) => { + e.preventDefault(); + } }, h(NIconSwitchTransition, null, { default: () => { return this.loading ? h(NBaseLoading, { key: "loading", clsPrefix: this.clsPrefix, radius: 85, strokeWidth: 15, scale: 0.88 }) : this.renderExpandIcon ? this.renderExpandIcon({ @@ -3479,7 +2996,6 @@ const TableBody = defineComponent({ mergedSortStateRef, virtualScrollRef, componentId, - scrollPartRef, mergedTableLayoutRef, childTriggerColIndexRef, indentRef, @@ -3611,9 +3127,6 @@ const TableBody = defineComponent({ function handleMouseleaveTable() { hoverKeyRef.value = null; } - function handleMouseenterTable() { - scrollPartRef.value = "body"; - } function virtualListContainer() { const { value } = virtualListRef; return value === null || value === void 0 ? void 0 : value.listElRef; @@ -3759,7 +3272,6 @@ const TableBody = defineComponent({ renderExpandIcon: renderExpandIconRef, scrollbarProps: scrollbarPropsRef, setHeaderScrollLeft, - handleMouseenterTable, handleVirtualListScroll, handleVirtualListResize, handleMouseleaveTable, @@ -3786,7 +3298,7 @@ const TableBody = defineComponent({ default: () => { const cordToPass = {}; const cordKey = {}; - const { cols, paginatedDataAndInfo, mergedTheme: mergedTheme2, fixedColumnLeftMap, fixedColumnRightMap, currentPage, rowClassName, mergedSortState, mergedExpandedRowKeySet, stickyExpandedRows, componentId, childTriggerColIndex, expandable, rowProps, handleMouseenterTable, handleMouseleaveTable, renderExpand, summary, handleCheckboxUpdateChecked, handleRadioUpdateChecked, handleUpdateExpanded } = this; + const { cols, paginatedDataAndInfo, mergedTheme: mergedTheme2, fixedColumnLeftMap, fixedColumnRightMap, currentPage, rowClassName, mergedSortState, mergedExpandedRowKeySet, stickyExpandedRows, componentId, childTriggerColIndex, expandable, rowProps, handleMouseleaveTable, renderExpand, summary, handleCheckboxUpdateChecked, handleRadioUpdateChecked, handleUpdateExpanded } = this; const { length: colCount } = cols; let mergedData; const { data: paginatedData, hasChildren } = paginatedDataAndInfo; @@ -3846,7 +3358,7 @@ const TableBody = defineComponent({ const { tmNode: { key, rawNode } } = rowInfo; return h( "tr", - { class: `${mergedClsPrefix}-data-table-tr`, key: `${key}__expand` }, + { class: `${mergedClsPrefix}-data-table-tr ${mergedClsPrefix}-data-table-tr--expanded`, key: `${key}__expand` }, h("td", { class: [ `${mergedClsPrefix}-data-table-td`, `${mergedClsPrefix}-data-table-td--last-col`, @@ -3869,6 +3381,7 @@ const TableBody = defineComponent({ `${mergedClsPrefix}-data-table-tr`, isSummary && `${mergedClsPrefix}-data-table-tr--summary`, striped && `${mergedClsPrefix}-data-table-tr--striped`, + expanded && `${mergedClsPrefix}-data-table-tr--expanded`, mergedRowClassName ] }, props), cols.map((col, colIndex) => { var _a, _b, _c, _d, _e; @@ -3913,6 +3426,9 @@ const TableBody = defineComponent({ const hoverKey = isCrossRowTd ? this.hoverKey : null; const { cellProps } = column; const resolvedCellProps = cellProps === null || cellProps === void 0 ? void 0 : cellProps(rowData, actualRowIndex); + const indentOffsetStyle = { + "--indent-offset": "" + }; return h( "td", Object.assign({}, resolvedCellProps, { key: colKey, style: [ @@ -3921,6 +3437,7 @@ const TableBody = defineComponent({ left: pxfy((_c = fixedColumnLeftMap[colKey]) === null || _c === void 0 ? void 0 : _c.start), right: pxfy((_d = fixedColumnRightMap[colKey]) === null || _d === void 0 ? void 0 : _d.start) }, + indentOffsetStyle, (resolvedCellProps === null || resolvedCellProps === void 0 ? void 0 : resolvedCellProps.style) || "" ], colspan: mergedColSpan, rowspan: isVirtual ? void 0 : mergedRowSpan, "data-col-key": colKey, class: [ `${mergedClsPrefix}-data-table-td`, @@ -3936,7 +3453,7 @@ const TableBody = defineComponent({ isLastRow && `${mergedClsPrefix}-data-table-td--last-row` ] }), hasChildren && colIndex === childTriggerColIndex ? [ - repeat(isSummary ? 0 : rowInfo.tmNode.level, h("div", { class: `${mergedClsPrefix}-data-table-indent`, style: indentStyle })), + repeat(indentOffsetStyle["--indent-offset"] = isSummary ? 0 : rowInfo.tmNode.level, h("div", { class: `${mergedClsPrefix}-data-table-indent`, style: indentStyle })), isSummary || rowInfo.tmNode.isLeaf ? h("div", { class: `${mergedClsPrefix}-data-table-expand-placeholder` }) : h(ExpandTrigger, { class: `${mergedClsPrefix}-data-table-expand-trigger`, clsPrefix: mergedClsPrefix, expanded, renderExpandIcon: this.renderExpandIcon, loading: loadingKeySet.has(rowInfo.key), onClick: () => { handleUpdateExpanded(rowKey, rowInfo.tmNode); } }) @@ -3955,7 +3472,7 @@ const TableBody = defineComponent({ if (!virtualScroll) { return h( "table", - { class: `${mergedClsPrefix}-data-table-table`, onMouseleave: handleMouseleaveTable, onMouseenter: handleMouseenterTable, style: { + { class: `${mergedClsPrefix}-data-table-table`, onMouseleave: handleMouseleaveTable, style: { tableLayout: this.mergedTableLayout } }, h("colgroup", null, cols.map((col) => h("col", { key: col.key, style: col.style }))), @@ -3969,7 +3486,6 @@ const TableBody = defineComponent({ clsPrefix: mergedClsPrefix, id: componentId, cols, - onMouseenter: handleMouseenterTable, onMouseleave: handleMouseleaveTable }, showScrollbar: false, onResize: this.handleVirtualListResize, onScroll: this.handleVirtualListScroll, itemsStyle: contentStyle, itemResizable: true }, { default: ({ item, index }) => renderRow(item, index, true) @@ -4677,8 +4193,9 @@ function useTableData(props, { dataRelatedColsRef }) { sort }; } -function useScroll(props, { mainTableInstRef, mergedCurrentPageRef, bodyWidthRef, scrollPartRef }) { - let scrollLeft = 0; +function useScroll(props, { mainTableInstRef, mergedCurrentPageRef, bodyWidthRef }) { + let lastScrollLeft = 0; + const scrollPartRef = ref(); const leftActiveFixedColKeyRef = ref(null); const leftActiveFixedChildrenColKeysRef = ref([]); const rightActiveFixedColKeyRef = ref(null); @@ -4739,7 +4256,7 @@ function useScroll(props, { mainTableInstRef, mergedCurrentPageRef, bodyWidthRef let leftActiveFixedColKey = null; for (let i = 0; i < leftFixedColumns.length; ++i) { const key = getColKey(leftFixedColumns[i]); - if (scrollLeft > (((_a = fixedColumnLeftMap[key]) === null || _a === void 0 ? void 0 : _a.start) || 0) - leftWidth) { + if (lastScrollLeft > (((_a = fixedColumnLeftMap[key]) === null || _a === void 0 ? void 0 : _a.start) || 0) - leftWidth) { leftActiveFixedColKey = key; leftWidth = ((_b = fixedColumnLeftMap[key]) === null || _b === void 0 ? void 0 : _b.end) || 0; } else { @@ -4772,7 +4289,7 @@ function useScroll(props, { mainTableInstRef, mergedCurrentPageRef, bodyWidthRef const { value: fixedColumnRightMap } = fixedColumnRightMapRef; for (let i = rightFixedColumns.length - 1; i >= 0; --i) { const key = getColKey(rightFixedColumns[i]); - if (Math.round(scrollLeft + (((_a = fixedColumnRightMap[key]) === null || _a === void 0 ? void 0 : _a.start) || 0) + tableWidth - rightWidth) < scrollWidth) { + if (Math.round(lastScrollLeft + (((_a = fixedColumnRightMap[key]) === null || _a === void 0 ? void 0 : _a.start) || 0) + tableWidth - rightWidth) < scrollWidth) { rightActiveFixedColKey = key; rightWidth = ((_b = fixedColumnRightMap[key]) === null || _b === void 0 ? void 0 : _b.end) || 0; } else { @@ -4805,15 +4322,19 @@ function useScroll(props, { mainTableInstRef, mergedCurrentPageRef, bodyWidthRef } } function handleTableHeaderScroll() { - if (scrollPartRef.value === "head") { + if (scrollPartRef.value !== "body") { beforeNextFrameOnce(syncScrollState); + } else { + scrollPartRef.value = void 0; } } function handleTableBodyScroll(e) { var _a; (_a = props.onScroll) === null || _a === void 0 ? void 0 : _a.call(props, e); - if (scrollPartRef.value === "body") { + if (scrollPartRef.value !== "head") { beforeNextFrameOnce(syncScrollState); + } else { + scrollPartRef.value = void 0; } } function syncScrollState() { @@ -4823,19 +4344,20 @@ function useScroll(props, { mainTableInstRef, mergedCurrentPageRef, bodyWidthRef const { value: tableWidth } = bodyWidthRef; if (tableWidth === null) return; - const { value: scrollPart } = scrollPartRef; if (props.maxHeight || props.flexHeight) { if (!header) return; - if (scrollPart === "head") { - scrollLeft = header.scrollLeft; - body.scrollLeft = scrollLeft; + const directionHead = lastScrollLeft - header.scrollLeft; + scrollPartRef.value = directionHead !== 0 ? "head" : "body"; + if (scrollPartRef.value === "head") { + lastScrollLeft = header.scrollLeft; + body.scrollLeft = lastScrollLeft; } else { - scrollLeft = body.scrollLeft; - header.scrollLeft = scrollLeft; + lastScrollLeft = body.scrollLeft; + header.scrollLeft = lastScrollLeft; } } else { - scrollLeft = body.scrollLeft; + lastScrollLeft = body.scrollLeft; } deriveActiveLeftFixedColumn(); deriveActiveLeftFixedChildrenColumns(); @@ -5275,6 +4797,7 @@ const style$1 = c([cB("data-table", ` white-space: nowrap; max-width: 100%; vertical-align: bottom; + max-width: calc(100% - var(--indent-offset, -1.5) * 16px - 24px); `), cM("selection, expand", ` text-align: center; padding: 0; @@ -5438,10 +4961,6 @@ const NDataTable = defineComponent({ }); const themeRef = useTheme("DataTable", "-data-table", style$1, dataTableLight, props, mergedClsPrefixRef); const bodyWidthRef = ref(null); - const scrollPartRef = ref("body"); - onDeactivated(() => { - scrollPartRef.value = "body"; - }); const mainTableInstRef = ref(null); const { getResizableWidth, clearResizableWidth, doUpdateResizableWidth } = useResizable(); const { rowsRef, colsRef, dataRelatedColsRef, hasEllipsisRef } = useGroupHeader(props, getResizableWidth); @@ -5453,7 +4972,6 @@ const NDataTable = defineComponent({ }); const { stickyExpandedRowsRef, mergedExpandedRowKeysRef, renderExpandRef, expandableRef, doUpdateExpandedRowKeys } = useExpand(props, treeMateRef); const { handleTableBodyScroll, handleTableHeaderScroll, syncScrollState, setHeaderScrollLeft, leftActiveFixedColKeyRef, leftActiveFixedChildrenColKeysRef, rightActiveFixedColKeyRef, rightActiveFixedChildrenColKeysRef, leftFixedColumnsRef, rightFixedColumnsRef, fixedColumnLeftMapRef, fixedColumnRightMapRef } = useScroll(props, { - scrollPartRef, bodyWidthRef, mainTableInstRef, mergedCurrentPageRef @@ -5501,7 +5019,6 @@ const NDataTable = defineComponent({ mergedExpandedRowKeysRef, mergedInderminateRowKeySetRef, localeRef, - scrollPartRef, expandableRef, stickyExpandedRowsRef, rowKeyRef: toRef(props, "rowKey"), @@ -5687,171 +5204,6 @@ const NUploadDragger = defineComponent({ }; } }); -var __awaiter$2 = globalThis && globalThis.__awaiter || function(thisArg, _arguments, P, generator) { - function adopt(value) { - return value instanceof P ? value : new P(function(resolve) { - resolve(value); - }); - } - return new (P || (P = Promise))(function(resolve, reject) { - function fulfilled(value) { - try { - step(generator.next(value)); - } catch (e) { - reject(e); - } - } - function rejected(value) { - try { - step(generator["throw"](value)); - } catch (e) { - reject(e); - } - } - function step(result) { - result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); - } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -const isImageFileType = (type) => type.includes("image/"); -const getExtname = (url = "") => { - const temp = url.split("/"); - const filename = temp[temp.length - 1]; - const filenameWithoutSuffix = filename.split(/#|\?/)[0]; - return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [""])[0]; -}; -const imageExtensionRegex = /(webp|svg|png|gif|jpg|jpeg|jfif|bmp|dpg|ico)$/i; -const isImageFile = (file) => { - if (file.type) { - return isImageFileType(file.type); - } - const fileNameExtension = getExtname(file.name || ""); - if (imageExtensionRegex.test(fileNameExtension)) { - return true; - } - const url = file.thumbnailUrl || file.url || ""; - const urlExtension = getExtname(url); - if (/^data:image\//.test(url) || imageExtensionRegex.test(urlExtension)) { - return true; - } - return false; -}; -function createImageDataUrl(file) { - return __awaiter$2(this, void 0, void 0, function* () { - return yield new Promise((resolve) => { - if (!file.type || !isImageFileType(file.type)) { - resolve(""); - return; - } - resolve(window.URL.createObjectURL(file)); - }); - }); -} -const environmentSupportFile = isBrowser && window.FileReader && window.File; -function isFileSystemDirectoryEntry(item) { - return item.isDirectory; -} -function isFileSystemFileEntry(item) { - return item.isFile; -} -function getFilesFromEntries(entries, directory) { - return __awaiter$2(this, void 0, void 0, function* () { - const fileAndEntries = []; - let _resolve; - let requestCallbackCount = 0; - function lock() { - requestCallbackCount++; - } - function unlock() { - requestCallbackCount--; - if (!requestCallbackCount) { - _resolve(fileAndEntries); - } - } - function _getFilesFromEntries(entries2) { - entries2.forEach((entry) => { - if (!entry) - return; - lock(); - if (directory && isFileSystemDirectoryEntry(entry)) { - const directoryReader = entry.createReader(); - lock(); - directoryReader.readEntries((entries3) => { - _getFilesFromEntries(entries3); - unlock(); - }, () => { - unlock(); - }); - } else if (isFileSystemFileEntry(entry)) { - lock(); - entry.file((file) => { - fileAndEntries.push({ file, entry, source: "dnd" }); - unlock(); - }, () => { - unlock(); - }); - } - unlock(); - }); - } - yield new Promise((resolve) => { - _resolve = resolve; - _getFilesFromEntries(entries); - }); - return fileAndEntries; - }); -} -function createSettledFileInfo(fileInfo) { - const { id, name, percentage, status, url, file, thumbnailUrl, type, fullPath, batchId } = fileInfo; - return { - id, - name, - percentage: percentage !== null && percentage !== void 0 ? percentage : null, - status, - url: url !== null && url !== void 0 ? url : null, - file: file !== null && file !== void 0 ? file : null, - thumbnailUrl: thumbnailUrl !== null && thumbnailUrl !== void 0 ? thumbnailUrl : null, - type: type !== null && type !== void 0 ? type : null, - fullPath: fullPath !== null && fullPath !== void 0 ? fullPath : null, - batchId: batchId !== null && batchId !== void 0 ? batchId : null - }; -} -function matchType(name, mimeType, accept) { - name = name.toLowerCase(); - mimeType = mimeType.toLocaleLowerCase(); - accept = accept.toLocaleLowerCase(); - const acceptAtoms = accept.split(",").map((acceptAtom) => acceptAtom.trim()).filter(Boolean); - return acceptAtoms.some((acceptAtom) => { - if (acceptAtom.startsWith(".")) { - if (name.endsWith(acceptAtom)) - return true; - } else if (acceptAtom.includes("/")) { - const [type, subtype] = mimeType.split("/"); - const [acceptType, acceptSubtype] = acceptAtom.split("/"); - if (acceptType === "*" || type && acceptType && acceptType === type) { - if (acceptSubtype === "*" || subtype && acceptSubtype && acceptSubtype === subtype) { - return true; - } - } - } else { - return true; - } - return false; - }); -} -const download = (url, name) => { - if (!url) - return; - const a = document.createElement("a"); - a.href = url; - if (name !== void 0) { - a.download = name; - } - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); -}; const NUploadTrigger = defineComponent({ name: "UploadTrigger", props: { @@ -6735,7 +6087,7 @@ const uploadProps = Object.assign(Object.assign({}, useTheme.props), { imageGroupProps: Object, inputProps: Object, triggerStyle: [String, Object], - renderIcon: Object + renderIcon: Function }); const NUpload = defineComponent({ name: "Upload", @@ -7046,12 +6398,12 @@ const NUpload = defineComponent({ ); } }); -const _hoisted_1$7 = { +const _hoisted_1$6 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" }; -const _hoisted_2$7 = /* @__PURE__ */ createBaseVNode( +const _hoisted_2$6 = /* @__PURE__ */ createBaseVNode( "path", { d: "M261.56 101.28a8 8 0 0 0-11.06 0L66.4 277.15a8 8 0 0 0-2.47 5.79L63.9 448a32 32 0 0 0 32 32H192a16 16 0 0 0 16-16V328a8 8 0 0 1 8-8h80a8 8 0 0 1 8 8v136a16 16 0 0 0 16 16h96.06a32 32 0 0 0 32-32V282.94a8 8 0 0 0-2.47-5.79z", @@ -7061,7 +6413,7 @@ const _hoisted_2$7 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_3$7 = /* @__PURE__ */ createBaseVNode( +const _hoisted_3$6 = /* @__PURE__ */ createBaseVNode( "path", { d: "M490.91 244.15l-74.8-71.56V64a16 16 0 0 0-16-16h-48a16 16 0 0 0-16 16v32l-57.92-55.38C272.77 35.14 264.71 32 256 32c-8.68 0-16.72 3.14-22.14 8.63l-212.7 203.5c-6.22 6-7 15.87-1.34 22.37A16 16 0 0 0 43 267.56L250.5 69.28a8 8 0 0 1 11.06 0l207.52 198.28a16 16 0 0 0 22.59-.44c6.14-6.36 5.63-16.86-.76-22.97z", @@ -7071,19 +6423,19 @@ const _hoisted_3$7 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_4$7 = [_hoisted_2$7, _hoisted_3$7]; +const _hoisted_4$5 = [_hoisted_2$6, _hoisted_3$6]; const Home = defineComponent({ name: "Home", render: function render(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$7, _hoisted_4$7); + return openBlock(), createElementBlock("svg", _hoisted_1$6, _hoisted_4$5); } }); -const _hoisted_1$6 = { +const _hoisted_1$5 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" }; -const _hoisted_2$6 = /* @__PURE__ */ createBaseVNode( +const _hoisted_2$5 = /* @__PURE__ */ createBaseVNode( "path", { fill: "none", @@ -7097,7 +6449,7 @@ const _hoisted_2$6 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_3$6 = /* @__PURE__ */ createBaseVNode( +const _hoisted_3$5 = /* @__PURE__ */ createBaseVNode( "path", { fill: "none", @@ -7111,7 +6463,7 @@ const _hoisted_3$6 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_4$6 = /* @__PURE__ */ createBaseVNode( +const _hoisted_4$4 = /* @__PURE__ */ createBaseVNode( "path", { fill: "none", @@ -7125,19 +6477,19 @@ const _hoisted_4$6 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_5$3 = [_hoisted_2$6, _hoisted_3$6, _hoisted_4$6]; +const _hoisted_5$2 = [_hoisted_2$5, _hoisted_3$5, _hoisted_4$4]; const Menu = defineComponent({ name: "Menu", render: function render2(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$6, _hoisted_5$3); + return openBlock(), createElementBlock("svg", _hoisted_1$5, _hoisted_5$2); } }); -const _hoisted_1$5 = { +const _hoisted_1$4 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" }; -const _hoisted_2$5 = /* @__PURE__ */ createBaseVNode( +const _hoisted_2$4 = /* @__PURE__ */ createBaseVNode( "path", { d: "M221.09 64a157.09 157.09 0 1 0 157.09 157.09A157.1 157.1 0 0 0 221.09 64z", @@ -7150,7 +6502,7 @@ const _hoisted_2$5 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_3$5 = /* @__PURE__ */ createBaseVNode( +const _hoisted_3$4 = /* @__PURE__ */ createBaseVNode( "path", { fill: "none", @@ -7164,45 +6516,11 @@ const _hoisted_3$5 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_4$5 = [_hoisted_2$5, _hoisted_3$5]; +const _hoisted_4$3 = [_hoisted_2$4, _hoisted_3$4]; const SearchOutline = defineComponent({ name: "SearchOutline", render: function render3(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$5, _hoisted_4$5); - } -}); -const _hoisted_1$4 = { - xmlns: "http://www.w3.org/2000/svg", - "xmlns:xlink": "http://www.w3.org/1999/xlink", - viewBox: "0 0 512 512" -}; -const _hoisted_2$4 = /* @__PURE__ */ createBaseVNode( - "circle", - { - cx: "256", - cy: "256", - r: "48", - fill: "currentColor" - }, - null, - -1 - /* HOISTED */ -); -const _hoisted_3$4 = /* @__PURE__ */ createBaseVNode( - "path", - { - d: "M470.39 300l-.47-.38l-31.56-24.75a16.11 16.11 0 0 1-6.1-13.33v-11.56a16 16 0 0 1 6.11-13.22L469.92 212l.47-.38a26.68 26.68 0 0 0 5.9-34.06l-42.71-73.9a1.59 1.59 0 0 1-.13-.22A26.86 26.86 0 0 0 401 92.14l-.35.13l-37.1 14.93a15.94 15.94 0 0 1-14.47-1.29q-4.92-3.1-10-5.86a15.94 15.94 0 0 1-8.19-11.82l-5.59-39.59l-.12-.72A27.22 27.22 0 0 0 298.76 26h-85.52a26.92 26.92 0 0 0-26.45 22.39l-.09.56l-5.57 39.67a16 16 0 0 1-8.13 11.82a175.21 175.21 0 0 0-10 5.82a15.92 15.92 0 0 1-14.43 1.27l-37.13-15l-.35-.14a26.87 26.87 0 0 0-32.48 11.34l-.13.22l-42.77 73.95a26.71 26.71 0 0 0 5.9 34.1l.47.38l31.56 24.75a16.11 16.11 0 0 1 6.1 13.33v11.56a16 16 0 0 1-6.11 13.22L42.08 300l-.47.38a26.68 26.68 0 0 0-5.9 34.06l42.71 73.9a1.59 1.59 0 0 1 .13.22a26.86 26.86 0 0 0 32.45 11.3l.35-.13l37.07-14.93a15.94 15.94 0 0 1 14.47 1.29q4.92 3.11 10 5.86a15.94 15.94 0 0 1 8.19 11.82l5.56 39.59l.12.72A27.22 27.22 0 0 0 213.24 486h85.52a26.92 26.92 0 0 0 26.45-22.39l.09-.56l5.57-39.67a16 16 0 0 1 8.18-11.82c3.42-1.84 6.76-3.79 10-5.82a15.92 15.92 0 0 1 14.43-1.27l37.13 14.95l.35.14a26.85 26.85 0 0 0 32.48-11.34a2.53 2.53 0 0 1 .13-.22l42.71-73.89a26.7 26.7 0 0 0-5.89-34.11zm-134.48-40.24a80 80 0 1 1-83.66-83.67a80.21 80.21 0 0 1 83.66 83.67z", - fill: "currentColor" - }, - null, - -1 - /* HOISTED */ -); -const _hoisted_4$4 = [_hoisted_2$4, _hoisted_3$4]; -const Settings = defineComponent({ - name: "Settings", - render: function render4(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$4, _hoisted_4$4); + return openBlock(), createElementBlock("svg", _hoisted_1$4, _hoisted_4$3); } }); const _hoisted_1$3 = { @@ -7214,16 +6532,20 @@ const _hoisted_2$3 = { style: { "margin": "12px", "margin-top": "8px" } }; const _hoisted_3$3 = { class: "image-grid" }; -const _hoisted_4$3 = ["src", "onClick"]; -const _hoisted_5$2 = { style: { "position": "absolute", "width": "100%", "bottom": "0", "padding": "0 8px", "min-height": "32px", "overflow": "hidden", "box-sizing": "border-box", "backdrop-filter": "blur(12px)" } }; +const _hoisted_4$2 = ["src", "onClick"]; +const _hoisted_5$1 = { style: { "position": "absolute", "width": "100%", "bottom": "0", "padding": "0 8px", "min-height": "32px", "overflow": "hidden", "box-sizing": "border-box", "backdrop-filter": "blur(12px)" } }; const _sfc_main$4 = /* @__PURE__ */ defineComponent({ __name: "CivitAIDownload", setup(__props) { - useCssVars((_ctx) => ({ - "9fc29920": unref(settings).data.settings.frontend.image_browser_columns, - "714c7f6d": backgroundColor.value - })); + useCssVars((_ctx) => { + var _a, _b; + return { + "52455add": unref(settings).data.settings.frontend.image_browser_columns, + "5509ecf2": (_b = (_a = unref(theme)) == null ? void 0 : _a.Card) == null ? void 0 : _b.color + }; + }); const settings = useSettings(); + const theme = inject(themeOverridesKey); const loadingLock = ref(false); const currentPage = ref(1); const sortBy = ref("Most Downloaded"); @@ -7259,6 +6581,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ async function refreshImages() { currentPage.value = 1; modelData.splice(0, modelData.length); + loadingBar.start(); const url = new URL("https://civitai.com/api/v1/models"); url.searchParams.append("sort", sortBy.value); if (itemFilter.value !== "") { @@ -7271,6 +6594,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ data.items.forEach((item) => { modelData.push(item); }); + loadingBar.finish(); }); } const handleScroll = (e) => { @@ -7307,7 +6631,6 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ if (types.value) { url.searchParams.append("types", types.value); } - console.log("Fetching page: " + url.toString()); fetch(url).then((res) => res.json()).then((data) => { data.items.forEach((item) => { modelData.push(item); @@ -7368,13 +6691,6 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ }); }); refreshImages(); - const backgroundColor = computed(() => { - if (settings.data.settings.frontend.theme === "dark") { - return "#121215"; - } else { - return "#fff"; - } - }); return (_ctx, _cache) => { return openBlock(), createElementBlock(Fragment, null, [ createVNode(_sfc_main$5, { @@ -7492,8 +6808,8 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ filter: unref(nsfwIndex)(item.modelVersions[0].images[0].nsfw) > unref(settings).data.settings.frontend.nsfw_ok_threshold ? "blur(12px)" : "none" }), onClick: ($event) => imgClick(column_index, item_index) - }, null, 12, _hoisted_4$3), - createBaseVNode("div", _hoisted_5$2, [ + }, null, 12, _hoisted_4$2), + createBaseVNode("div", _hoisted_5$1, [ createVNode(unref(NText), { depth: 2 }, { default: withCtx(() => [ createTextVNode(toDisplayString(item.name), 1) @@ -7512,32 +6828,31 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ }; } }); -const CivitAIDownload_vue_vue_type_style_index_0_scoped_07574b72_lang = ""; -const CivitAIDownload = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-07574b72"]]); -const _withScopeId = (n) => (pushScopeId("data-v-41346243"), n = n(), popScopeId(), n); +const CivitAIDownload_vue_vue_type_style_index_0_scoped_f55237b7_lang = ""; +const CivitAIDownload = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-f55237b7"]]); +const _withScopeId = (n) => (pushScopeId("data-v-b405f046"), n = n(), popScopeId(), n); const _hoisted_1$2 = { style: { "margin": "18px" } }; const _hoisted_2$2 = { style: { "width": "100%", "display": "inline-flex", "justify-content": "space-between", "align-items": "center" } }; const _hoisted_3$2 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("div", null, "Install custom models from Hugging Face", -1)); -const _hoisted_4$2 = { style: { "display": "inline-flex", "align-items": "center" } }; +const _hoisted_4$1 = { style: { "display": "inline-flex", "align-items": "center" } }; const _sfc_main$3 = /* @__PURE__ */ defineComponent({ __name: "HuggingfaceDownload", setup(__props) { - const conf = useState(); + const settings = useState(); const message = useMessage(); const customModel = ref(""); function downloadModel(model) { const url = new URL(`${serverUrl}/api/models/download`); const modelName = typeof model === "string" ? model : model.value; url.searchParams.append("model", modelName); - console.log(url); - conf.state.downloading = true; + settings.state.downloading = true; customModel.value = ""; message.info(`Downloading model: ${modelName}`); fetch(url, { method: "POST" }).then(() => { - conf.state.downloading = false; + settings.state.downloading = false; message.success(`Downloaded model: ${modelName}`); }).catch(() => { - conf.state.downloading = false; + settings.state.downloading = false; message.error(`Failed to download model: ${modelName}`); }); } @@ -7590,7 +6905,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ round: true, block: true, bordered: false, - disabled: conf.state.downloading, + disabled: settings.state.downloading, onClick: () => { downloadModel(row.huggingface_id); } @@ -7609,7 +6924,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ { trigger: "hover", options: getPluginOptions(row), - disabled: conf.state.downloading + disabled: settings.state.downloading }, { default: renderIcon(Menu) } ); @@ -7643,7 +6958,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createBaseVNode("div", _hoisted_2$2, [ _hoisted_3$2, - createBaseVNode("div", _hoisted_4$2, [ + createBaseVNode("div", _hoisted_4$1, [ createVNode(unref(NInput), { value: customModel.value, "onUpdate:value": _cache[0] || (_cache[0] = ($event) => customModel.value = $event), @@ -7654,8 +6969,8 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ type: "primary", bordered: "", onClick: _cache[1] || (_cache[1] = ($event) => downloadModel(customModel.value)), - loading: unref(conf).state.downloading, - disabled: unref(conf).state.downloading || customModel.value === "", + loading: unref(settings).state.downloading, + disabled: unref(settings).state.downloading || customModel.value === "", secondary: "", style: { "margin-right": "16px", "margin-left": "4px" } }, { @@ -7696,15 +7011,15 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }; } }); -const HuggingfaceDownload_vue_vue_type_style_index_0_scoped_41346243_lang = ""; -const HuggingfaceDownload = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-41346243"]]); +const HuggingfaceDownload_vue_vue_type_style_index_0_scoped_b405f046_lang = ""; +const HuggingfaceDownload = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-b405f046"]]); const _hoisted_1$1 = { style: { "margin": "16px" } }; const _hoisted_2$1 = { class: "flex-container" }; const _hoisted_3$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "FP32", -1); -const _hoisted_4$1 = { class: "flex-container" }; -const _hoisted_5$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Output in safetensors format", -1); -const _hoisted_6$1 = { class: "flex-container" }; -const _hoisted_7$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Model", -1); +const _hoisted_4 = { class: "flex-container" }; +const _hoisted_5 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Output in safetensors format", -1); +const _hoisted_6 = { class: "flex-container" }; +const _hoisted_7 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Model", -1); const _sfc_main$2 = /* @__PURE__ */ defineComponent({ __name: "ModelConvert", setup(__props) { @@ -7773,15 +7088,15 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ "onUpdate:value": _cache[0] || (_cache[0] = ($event) => use_fp32.value = $event) }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_4$1, [ - _hoisted_5$1, + createBaseVNode("div", _hoisted_4, [ + _hoisted_5, createVNode(unref(NSwitch), { value: safetensors.value, "onUpdate:value": _cache[1] || (_cache[1] = ($event) => safetensors.value = $event) }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_6$1, [ - _hoisted_7$1, + createBaseVNode("div", _hoisted_6, [ + _hoisted_7, createVNode(unref(NSelect), { value: model.value, "onUpdate:value": _cache[2] || (_cache[2] = ($event) => model.value = $event), @@ -7833,19 +7148,25 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }; } }); -const _hoisted_1 = { style: { "padding": "12px" } }; +const _hoisted_1 = { style: { "padding": "0px 12px 12px 12px" } }; const _hoisted_2 = { style: { "margin-bottom": "12px", "display": "block" } }; const _hoisted_3 = { style: { "display": "inline-flex" } }; -const _hoisted_4 = { style: { "margin-bottom": "12px", "display": "block" } }; -const _hoisted_5 = { style: { "display": "inline-flex" } }; -const _hoisted_6 = { style: { "margin-bottom": "12px", "display": "block" } }; -const _hoisted_7 = { style: { "display": "inline-flex" } }; +const allowedExtensions = ".safetensors,.ckpt,.pth,.pt,.bin"; const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "ModelManager", setup(__props) { const global = useState(); const filter = ref(""); const message = useMessage(); + const modelTypes = { + PyTorch: "models", + LoRA: "lora", + LyCORIS: "lycoris", + "Textual Inversion": "textual-inversion", + VAE: "vae", + AITemplate: "aitemplate", + ONNX: "onnx" + }; const renderIcon = (icon) => { return () => { return h(NIcon, null, { @@ -7858,41 +7179,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ return model.path.toLowerCase().includes(filter.value.toLowerCase()) || filter.value === ""; }).sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1); }); - const pyTorchModels = computed(() => { - return filteredModels.value.filter((model) => { - return model.backend === "PyTorch" && model.valid === true; - }).sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1); - }); - const loraModels = computed(() => { - return filteredModels.value.filter((model) => { - return model.backend === "LoRA"; - }).sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1); - }); - const textualInversionModels = computed(() => { - return filteredModels.value.filter((model) => { - return model.backend === "Textual Inversion"; - }).sort((a, b) => a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1); - }); - function createPyTorchOptions(model_path) { - return [ - { - label: "Delete", - key: `delete:${model_path}`, - icon: renderIcon(TrashBin) - } - // { - // label: "Convert", - // key: `convert:${model_path}`, - // icon: renderIcon(GitCompare), - // }, - // { - // label: "Accelerate", - // key: `accelerate:${model_path}`, - // icon: renderIcon(PlayForward), - // }, - ]; - } - function createLoraOptions(model_path) { + function createOptions(model_path) { return [ { label: "Delete", @@ -7901,51 +7188,28 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ } ]; } - function createTextualInversionOptions(model_path) { - return [ - { - label: "Delete", - key: `delete:${model_path}`, - icon: renderIcon(TrashBin) - } - ]; - } - function deleteModel(model_path, model_type) { - fetch(`${serverUrl}/api/models/delete-model`, { - method: "DELETE", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ - model_path, - model_type - }) - }).then((response) => response.json()).then(() => { + async function deleteModel(model_path, model_type) { + try { + const res = await fetch(`${serverUrl}/api/models/delete-model`, { + method: "DELETE", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + model_path, + model_type + }) + }); + await res.json(); message.success("Model deleted"); - }).catch((error) => { + } catch (error) { message.error(error); - }); - } - function handlePyTorchModelAction(key) { - const [action, model_path] = key.split(":"); - if (action === "delete") { - deleteModel(model_path, "pytorch"); - } else if (action === "convert") { - message.success(key); - } else if (action === "accelerate") { - message.success(key); - } - } - function handleLoraModelAction(key) { - const [action, model_path] = key.split(":"); - if (action === "delete") { - deleteModel(model_path, "lora"); } } - function handleTextualInversionModelAction(key) { + async function handleAction(key, modelType, model) { const [action, model_path] = key.split(":"); if (action === "delete") { - deleteModel(model_path, "textual-inversion"); + await deleteModel(model_path, modelType); } } return (_ctx, _cache) => { @@ -7958,226 +7222,113 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ clearable: "" }, null, 8, ["value"]), createVNode(unref(NGrid), { - cols: "3", - "x-gap": "12" + cols: "1 600:2 900:3", + "x-gap": "8", + "y-gap": "8" }, { default: withCtx(() => [ - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(unref(NCard), { title: "Model" }, { - default: withCtx(() => [ - createVNode(unref(NUpload), { - multiple: "", - "directory-dnd": "", - action: `${unref(serverUrl)}/api/models/upload-model`, - max: 5, - accept: ".ckpt,.safetensors", - style: { "border-bottom": "1px solid rgb(66, 66, 71)", "padding-bottom": "12px" } - }, { - default: withCtx(() => [ - createVNode(unref(NUploadDragger), { style: { "display": "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center" } }, { - default: withCtx(() => [ - createBaseVNode("div", _hoisted_2, [ - createVNode(unref(NIcon), { - size: "48", - depth: 3 - }, { - default: withCtx(() => [ - createVNode(unref(CloudUpload)) - ]), - _: 1 - }) - ]), - createVNode(unref(NText), { style: { "font-size": "24px" } }, { - default: withCtx(() => [ - createTextVNode(" Model ") - ]), - _: 1 - }), - createVNode(unref(NText), { style: { "font-size": "16px" } }, { - default: withCtx(() => [ - createTextVNode(" Click or drag a model to this area to upload it to the server ") - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }, 8, ["action"]), - (openBlock(true), createElementBlock(Fragment, null, renderList(pyTorchModels.value, (model) => { - return openBlock(), createElementBlock("div", { - style: { "display": "inline-flex", "width": "100%", "align-items": "center", "justify-content": "space-between", "border-bottom": "1px solid rgb(66, 66, 71)" }, - key: model.path - }, [ - createBaseVNode("p", null, toDisplayString(model.name), 1), - createBaseVNode("div", _hoisted_3, [ - createVNode(unref(NDropdown), { - options: createPyTorchOptions(model.path), - placement: "right", - onSelect: handlePyTorchModelAction - }, { + (openBlock(true), createElementBlock(Fragment, null, renderList(Object.keys(modelTypes).filter((item) => item !== "AITemplate" && item !== "ONNX"), (key) => { + return openBlock(), createBlock(unref(NGi), null, { + default: withCtx(() => [ + createVNode(unref(NCard), { title: key }, { + default: withCtx(() => [ + createVNode(unref(NUpload), { + multiple: "", + "directory-dnd": "", + action: `${unref(serverUrl)}/api/models/upload-model?type=${modelTypes[key]}`, + accept: allowedExtensions + }, { + default: withCtx(() => [ + createVNode(unref(NUploadDragger), { style: { "display": "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center" } }, { default: withCtx(() => [ - createVNode(unref(NButton), { - "render-icon": renderIcon(unref(Settings)) - }, null, 8, ["render-icon"]) - ]), - _: 2 - }, 1032, ["options"]) - ]) - ]); - }), 128)) - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(unref(NCard), { title: "LoRA" }, { - default: withCtx(() => [ - createVNode(unref(NUpload), { - multiple: "", - "directory-dnd": "", - action: `${unref(serverUrl)}/api/models/upload-model?type=lora`, - max: 5, - accept: ".ckpt,.safetensors", - style: { "border-bottom": "1px solid rgb(66, 66, 71)", "padding-bottom": "12px" } - }, { - default: withCtx(() => [ - createVNode(unref(NUploadDragger), { style: { "display": "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center" } }, { - default: withCtx(() => [ - createBaseVNode("div", _hoisted_4, [ - createVNode(unref(NIcon), { - size: "48", - depth: 3 - }, { + createBaseVNode("div", _hoisted_2, [ + createVNode(unref(NIcon), { + size: "48", + depth: 3 + }, { + default: withCtx(() => [ + createVNode(unref(CloudUpload)) + ]), + _: 1 + }) + ]), + createVNode(unref(NText), { style: { "font-size": "24px" } }, { default: withCtx(() => [ - createVNode(unref(CloudUpload)) + createTextVNode(toDisplayString(key), 1) ]), - _: 1 - }) - ]), - createVNode(unref(NText), { style: { "font-size": "24px" } }, { - default: withCtx(() => [ - createTextVNode(" LoRA ") - ]), - _: 1 - }), - createVNode(unref(NText), { style: { "font-size": "16px" } }, { - default: withCtx(() => [ - createTextVNode(" Click or drag a model to this area to upload it to the server ") - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }, 8, ["action"]), - (openBlock(true), createElementBlock(Fragment, null, renderList(loraModels.value, (model) => { - return openBlock(), createElementBlock("div", { - style: { "display": "inline-flex", "width": "100%", "align-items": "center", "justify-content": "space-between", "border-bottom": "1px solid rgb(66, 66, 71)" }, - key: model.path - }, [ - createBaseVNode("p", null, toDisplayString(model.name), 1), - createBaseVNode("div", _hoisted_5, [ - createVNode(unref(NDropdown), { - options: createLoraOptions(model.path), - placement: "right", - onSelect: handleLoraModelAction - }, { - default: withCtx(() => [ - createVNode(unref(NButton), { - "render-icon": renderIcon(unref(Settings)) - }, null, 8, ["render-icon"]) - ]), - _: 2 - }, 1032, ["options"]) - ]) - ]); - }), 128)) - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(unref(NCard), { title: "Textual Inversion" }, { - default: withCtx(() => [ - createVNode(unref(NUpload), { - multiple: "", - "directory-dnd": "", - action: `${unref(serverUrl)}/api/models/upload-model?type=textual-inversion`, - max: 5, - accept: ".pt,.safetensors", - style: { "border-bottom": "1px solid rgb(66, 66, 71)", "padding-bottom": "12px" } - }, { - default: withCtx(() => [ - createVNode(unref(NUploadDragger), { style: { "display": "flex", "flex-direction": "column", "align-items": "center", "justify-content": "center" } }, { - default: withCtx(() => [ - createBaseVNode("div", _hoisted_6, [ - createVNode(unref(NIcon), { - size: "48", - depth: 3 - }, { + _: 2 + }, 1024), + createVNode(unref(NText), { style: { "font-size": "14px" } }, { default: withCtx(() => [ - createVNode(unref(CloudUpload)) + createTextVNode(" Click or Drag a model here ") ]), _: 1 }) ]), - createVNode(unref(NText), { style: { "font-size": "24px" } }, { - default: withCtx(() => [ - createTextVNode(" Textual Inversion ") - ]), - _: 1 - }), - createVNode(unref(NText), { style: { "font-size": "16px" } }, { + _: 2 + }, 1024) + ]), + _: 2 + }, 1032, ["action"]) + ]), + _: 2 + }, 1032, ["title"]) + ]), + _: 2 + }, 1024); + }), 256)) + ]), + _: 1 + }), + createVNode(unref(NDivider)), + createVNode(unref(NGrid), { + style: { "margin-top": "12px" }, + cols: "1 900:2 1100:3", + "x-gap": "12", + "y-gap": "12" + }, { + default: withCtx(() => [ + (openBlock(true), createElementBlock(Fragment, null, renderList(Object.keys(unref(Backends)).filter( + (item) => isNaN(Number(item)) + ), (modelType) => { + return openBlock(), createBlock(unref(NGi), null, { + default: withCtx(() => [ + createVNode(unref(NCard), { + title: modelType, + style: { "width": "100%" } + }, { + default: withCtx(() => [ + (openBlock(true), createElementBlock(Fragment, null, renderList(filteredModels.value.filter( + (item) => item.backend === modelType + ), (model) => { + return openBlock(), createElementBlock("div", { + style: { "display": "inline-flex", "width": "100%", "align-items": "center", "justify-content": "space-between", "border-bottom": "1px solid rgb(66, 66, 71)" }, + key: model.path + }, [ + createBaseVNode("p", null, toDisplayString(model.name), 1), + createBaseVNode("div", _hoisted_3, [ + createVNode(unref(NDropdown), { + options: createOptions(model.path), + placement: "right", + onSelect: (key) => handleAction(key, modelTypes[modelType], model) + }, { default: withCtx(() => [ - createTextVNode(" Click or drag a model to this area to upload it to the server ") + createVNode(unref(NButton), { + "render-icon": renderIcon(unref(Settings)) + }, null, 8, ["render-icon"]) ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - }, 8, ["action"]), - (openBlock(true), createElementBlock(Fragment, null, renderList(textualInversionModels.value, (model) => { - return openBlock(), createElementBlock("div", { - style: { "display": "inline-flex", "width": "100%", "align-items": "center", "justify-content": "space-between", "border-bottom": "1px solid rgb(66, 66, 71)" }, - key: model.path - }, [ - createBaseVNode("p", null, toDisplayString(model.name), 1), - createBaseVNode("div", _hoisted_7, [ - createVNode(unref(NDropdown), { - options: createTextualInversionOptions(model.path), - placement: "right", - onSelect: handleTextualInversionModelAction - }, { - default: withCtx(() => [ - createVNode(unref(NButton), { - "render-icon": renderIcon(unref(Settings)) - }, null, 8, ["render-icon"]) - ]), - _: 2 - }, 1032, ["options"]) - ]) - ]); - }), 128)) - ]), - _: 1 - }) - ]), - _: 1 - }) + _: 2 + }, 1032, ["options", "onSelect"]) + ]) + ]); + }), 128)) + ]), + _: 2 + }, 1032, ["title"]) + ]), + _: 2 + }, 1024); + }), 256)) ]), _: 1 }) diff --git a/frontend/dist/assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js b/frontend/dist/assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js new file mode 100644 index 000000000..d5a9906bb --- /dev/null +++ b/frontend/dist/assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js @@ -0,0 +1,222 @@ +import { d as defineComponent, a as useSettings, D as ref, c as computed, e as openBlock, f as createElementBlock, n as createBaseVNode, g as createVNode, w as withCtx, h as unref, i as NCard, I as Fragment, L as renderList, E as NButton, m as createTextVNode, t as toDisplayString, bC as convertToTextString, v as createBlock, bG as resolveDynamicComponent, bd as NModal, q as NTooltip, x as NSelect, F as NIcon, C as h } from "./index.js"; +import { S as Settings, a as NCheckbox } from "./Settings.js"; +import { N as NInputNumber } from "./InputNumber.js"; +import { N as NSlider } from "./Switch.js"; +const _hoisted_1 = { class: "flex-container" }; +const _hoisted_2 = { style: { "margin-left": "12px", "margin-right": "12px", "white-space": "nowrap" } }; +const _hoisted_3 = /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "100px" } }, "Sampler", -1); +const _hoisted_4 = /* @__PURE__ */ createBaseVNode("a", { + target: "_blank", + href: "https://docs.google.com/document/d/1n0YozLAUwLJWZmbsx350UD_bwAx3gZMnRuleIZt_R1w" +}, "Learn more", -1); +const _hoisted_5 = { class: "flex-container" }; +const _hoisted_6 = /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "94px" } }, "Sigmas", -1); +const _hoisted_7 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, 'Only "Default" and "Karras" sigmas work on diffusers samplers (and "Karras" are only applied to KDPM samplers)', -1); +const _sfc_main = /* @__PURE__ */ defineComponent({ + __name: "SamplerPicker", + props: { + type: { + type: String, + required: true + }, + target: { + type: String, + required: false, + default: "settings" + } + }, + setup(__props) { + const props = __props; + const settings = useSettings(); + const showModal = ref(false); + function getValue(param) { + const val = target.value.sampler_config[target.value[props.type].sampler][param]; + return val; + } + function setValue(param, value) { + target.value.sampler_config[target.value[props.type].sampler][param] = value; + } + function resolveComponent(settings2, param) { + switch (settings2.componentType) { + case "slider": + return h(NSlider, { + min: settings2.min, + max: settings2.max, + step: settings2.step, + value: getValue(param), + onUpdateValue: (value) => setValue(param, value) + }); + case "select": + return h(NSelect, { + options: settings2.options, + value: getValue(param), + onUpdateValue: (value) => setValue(param, value) + }); + case "boolean": + return h(NCheckbox, { + checked: getValue(param), + onUpdateChecked: (value) => setValue(param, value) + }); + case "number": + return h(NInputNumber, { + min: settings2.min, + max: settings2.max, + step: settings2.step, + value: getValue(param), + onUpdateValue: (value) => setValue(param, value) + }); + } + } + const target = computed(() => { + if (props.target === "settings") { + return settings.data.settings; + } + return settings.defaultSettings; + }); + const computedSettings = computed(() => { + return target.value.sampler_config[target.value[props.type].sampler] ?? {}; + }); + const sigmaOptions = computed(() => { + const karras = typeof target.value[props.type].sampler === "string"; + return [ + { + label: "Automatic", + value: "automatic" + }, + { + label: "Karras", + value: "karras" + }, + { + label: "Exponential", + value: "exponential", + disabled: !karras + }, + { + label: "Polyexponential", + value: "polyexponential", + disabled: !karras + }, + { + label: "VP", + value: "vp", + disabled: !karras + } + ]; + }); + const sigmaValidationStatus = computed(() => { + if (typeof target.value[props.type].sampler !== "string") { + if (!["automatic", "karras"].includes(target.value[props.type].sigmas)) { + return "error"; + } else { + return void 0; + } + } + return void 0; + }); + return (_ctx, _cache) => { + return openBlock(), createElementBlock(Fragment, null, [ + createBaseVNode("div", _hoisted_1, [ + createVNode(unref(NModal), { + show: showModal.value, + "onUpdate:show": _cache[1] || (_cache[1] = ($event) => showModal.value = $event), + "close-on-esc": "", + "mask-closable": "" + }, { + default: withCtx(() => [ + createVNode(unref(NCard), { + title: "Sampler settings", + style: { "max-width": "90vw", "max-height": "90vh" }, + closable: "", + onClose: _cache[0] || (_cache[0] = ($event) => showModal.value = false) + }, { + default: withCtx(() => [ + (openBlock(true), createElementBlock(Fragment, null, renderList(Object.keys(computedSettings.value), (param) => { + return openBlock(), createElementBlock("div", { + class: "flex-container", + key: param + }, [ + createVNode(unref(NButton), { + type: computedSettings.value[param] !== null ? "error" : "default", + ghost: "", + disabled: computedSettings.value[param] === null, + onClick: ($event) => setValue(param, null), + style: { "min-width": "100px" } + }, { + default: withCtx(() => [ + createTextVNode(toDisplayString(computedSettings.value[param] !== null ? "Reset" : "Disabled"), 1) + ]), + _: 2 + }, 1032, ["type", "disabled", "onClick"]), + createBaseVNode("p", _hoisted_2, toDisplayString(unref(convertToTextString)(param)), 1), + (openBlock(), createBlock(resolveDynamicComponent( + resolveComponent( + target.value.sampler_config["ui_settings"][param], + param + ) + ))) + ]); + }), 128)) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["show"]), + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_3 + ]), + default: withCtx(() => [ + createTextVNode(" The sampler is the method used to generate the image. Your result may vary drastically depending on the sampler you choose. "), + _hoisted_4 + ]), + _: 1 + }), + createVNode(unref(NSelect), { + options: unref(settings).scheduler_options, + filterable: "", + value: target.value[props.type].sampler, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => target.value[props.type].sampler = $event), + style: { "flex-grow": "1" } + }, null, 8, ["options", "value"]), + createVNode(unref(NButton), { + style: { "margin-left": "4px" }, + onClick: _cache[3] || (_cache[3] = ($event) => showModal.value = true) + }, { + default: withCtx(() => [ + createVNode(unref(NIcon), null, { + default: withCtx(() => [ + createVNode(unref(Settings)) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + createBaseVNode("div", _hoisted_5, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_6 + ]), + default: withCtx(() => [ + createTextVNode(" Changes the sigmas used in the diffusion process. Can change the quality of the output. "), + _hoisted_7 + ]), + _: 1 + }), + createVNode(unref(NSelect), { + options: sigmaOptions.value, + value: target.value[props.type].sigmas, + "onUpdate:value": _cache[4] || (_cache[4] = ($event) => target.value[props.type].sigmas = $event), + status: sigmaValidationStatus.value + }, null, 8, ["options", "value", "status"]) + ]) + ], 64); + }; + } +}); +export { + _sfc_main as _ +}; diff --git a/frontend/dist/assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js b/frontend/dist/assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js index 5f0c31833..a767d1f0d 100644 --- a/frontend/dist/assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js +++ b/frontend/dist/assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js @@ -1,10 +1,95 @@ -import { d as defineComponent, e as openBlock, f as createElementBlock, n as createBaseVNode, bD as useRouter, a as useSettings, u as useState, v as createBlock, w as withCtx, g as createVNode, h as unref, N as NGi, F as NButton, m as createTextVNode, y as NGrid, i as NCard, x as createCommentVNode } from "./index.js"; -const _hoisted_1 = { +import { d as defineComponent, e as openBlock, f as createElementBlock, n as createBaseVNode, bK as useRouter, a as useSettings, u as useState, D as ref, b8 as reactive, J as watch, c as computed, g as createVNode, w as withCtx, h as unref, i as NCard, E as NButton, m as createTextVNode, M as NScrollbar, I as Fragment, L as renderList, t as toDisplayString, be as NDivider, bd as NModal, v as createBlock, N as NGi, y as NGrid, r as createCommentVNode } from "./index.js"; +import { a as NSwitch } from "./Switch.js"; +const _hoisted_1$3 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" }; -const _hoisted_2 = /* @__PURE__ */ createBaseVNode( +const _hoisted_2$3 = /* @__PURE__ */ createBaseVNode( + "path", + { + fill: "none", + stroke: "currentColor", + "stroke-linecap": "round", + "stroke-linejoin": "round", + "stroke-width": "32", + d: "M368 368L144 144" + }, + null, + -1 + /* HOISTED */ +); +const _hoisted_3$3 = /* @__PURE__ */ createBaseVNode( + "path", + { + fill: "none", + stroke: "currentColor", + "stroke-linecap": "round", + "stroke-linejoin": "round", + "stroke-width": "32", + d: "M368 144L144 368" + }, + null, + -1 + /* HOISTED */ +); +const _hoisted_4$3 = [_hoisted_2$3, _hoisted_3$3]; +const CloseOutline = defineComponent({ + name: "CloseOutline", + render: function render(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$3, _hoisted_4$3); + } +}); +const _hoisted_1$2 = { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + viewBox: "0 0 512 512" +}; +const _hoisted_2$2 = /* @__PURE__ */ createBaseVNode( + "rect", + { + x: "128", + y: "128", + width: "336", + height: "336", + rx: "57", + ry: "57", + fill: "none", + stroke: "currentColor", + "stroke-linejoin": "round", + "stroke-width": "32" + }, + null, + -1 + /* HOISTED */ +); +const _hoisted_3$2 = /* @__PURE__ */ createBaseVNode( + "path", + { + d: "M383.5 128l.5-24a56.16 56.16 0 0 0-56-56H112a64.19 64.19 0 0 0-64 64v216a56.16 56.16 0 0 0 56 56h24", + fill: "none", + stroke: "currentColor", + "stroke-linecap": "round", + "stroke-linejoin": "round", + "stroke-width": "32" + }, + null, + -1 + /* HOISTED */ +); +const _hoisted_4$2 = [_hoisted_2$2, _hoisted_3$2]; +const CopyOutline = defineComponent({ + name: "CopyOutline", + render: function render2(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$2, _hoisted_4$2); + } +}); +const _hoisted_1$1 = { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + viewBox: "0 0 512 512" +}; +const _hoisted_2$1 = /* @__PURE__ */ createBaseVNode( "path", { d: "M376 160H272v153.37l52.69-52.68a16 16 0 0 1 22.62 22.62l-80 80a16 16 0 0 1-22.62 0l-80-80a16 16 0 0 1 22.62-22.62L240 313.37V160H136a56.06 56.06 0 0 0-56 56v208a56.06 56.06 0 0 0 56 56h240a56.06 56.06 0 0 0 56-56V216a56.06 56.06 0 0 0-56-56z", @@ -14,7 +99,7 @@ const _hoisted_2 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_3 = /* @__PURE__ */ createBaseVNode( +const _hoisted_3$1 = /* @__PURE__ */ createBaseVNode( "path", { d: "M272 48a16 16 0 0 0-32 0v112h32z", @@ -24,13 +109,18 @@ const _hoisted_3 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_4 = [_hoisted_2, _hoisted_3]; +const _hoisted_4$1 = [_hoisted_2$1, _hoisted_3$1]; const Download = defineComponent({ name: "Download", - render: function render(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1, _hoisted_4); + render: function render3(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$1, _hoisted_4$1); } }); +const _hoisted_1 = { style: { "display": "flex", "flex-direction": "row", "justify-content": "flex-end", "margin-bottom": "8px" } }; +const _hoisted_2 = { style: { "margin": "0 24px" } }; +const _hoisted_3 = { style: { "display": "flex", "flex-direction": "row", "justify-content": "space-between" } }; +const _hoisted_4 = { style: { "display": "flex", "flex-direction": "row", "justify-content": "flex-end" } }; +const _hoisted_5 = { key: 0 }; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "SendOutputTo", props: { @@ -41,209 +131,252 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ card: { type: Boolean, default: true + }, + data: { + type: Object, + required: false, + default: () => ({}) } }, setup(__props) { const props = __props; const router = useRouter(); - const conf = useSettings(); + const settings = useSettings(); const state = useState(); - async function toImg2Img() { - conf.data.settings.img2img.image = props.output; - state.state.img2img.tab = "Image to Image"; - await router.push("/image2image"); + const showModal = ref(false); + const maybeTarget = ref(null); + const targets = { + txt2img: "txt2img", + img2img: "img2img", + controlnet: "img2img", + inpainting: "img2img", + upscale: "imageProcessing", + tagger: "tagger" + }; + function handleClick(target) { + if (props.data) { + maybeTarget.value = target; + showModal.value = true; + } else { + toTarget(target); + } + } + function modalCopyClick() { + showModal.value = false; + if (maybeTarget.value) { + const tmp = maybeTarget.value; + maybeTarget.value = null; + toTarget(tmp); + } } - async function toControlNet() { - conf.data.settings.controlnet.image = props.output; - state.state.img2img.tab = "ControlNet"; - await router.push("/image2image"); + const valuesToCopy = reactive( + Object.fromEntries(Object.keys(props.data).map((key) => [key, false])) + ); + watch( + () => props.data, + (newData) => { + Object.keys(newData).forEach((key) => { + if (!valuesToCopy.hasOwnProperty(key)) { + valuesToCopy[key] = false; + } + }); + } + ); + const valuesToCopyFiltered = computed(() => { + return Object.keys(valuesToCopy).filter((key) => { + if (maybeTarget.value) { + return Object.keys(settings.data.settings[maybeTarget.value]).includes( + key + ); + } + }); + }); + async function toTarget(target) { + const targetPage = targets[target]; + if (target !== "txt2img") { + settings.data.settings[target].image = props.output; + } + if (targetPage !== "txt2img" && target !== "txt2img") { + state.state[targetPage].tab = target; + } + Object.keys(props.data).forEach((key) => { + if (valuesToCopy[key]) { + if (Object.keys(settings.data.settings[target]).includes(key)) { + settings.data.settings[target][key] = props.data[key]; + } + } + }); + await router.push("/" + targetPage); } - async function toInpainting() { - conf.data.settings.inpainting.image = props.output; - state.state.img2img.tab = "Inpainting"; - await router.push("/image2image"); + function capitalizeAndReplace(target) { + return target.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" "); } - async function toUpscale() { - conf.data.settings.upscale.image = props.output; - state.state.extra.tab = "Upscale"; - await router.push("/extra"); + function selectAll() { + for (const key in valuesToCopy) { + valuesToCopy[key] = true; + } } - async function toTagger() { - conf.data.settings.tagger.image = props.output; - await router.push("/tagger"); + function selectNone() { + for (const key in valuesToCopy) { + valuesToCopy[key] = false; + } } return (_ctx, _cache) => { - return __props.output && __props.card ? (openBlock(), createBlock(unref(NCard), { - key: 0, - style: { "margin": "12px 0" }, - title: "Send To" - }, { - default: withCtx(() => [ - createVNode(unref(NGrid), { - cols: "4", - "x-gap": "4", - "y-gap": "4" - }, { - default: withCtx(() => [ - createVNode(unref(NGi), null, { - default: withCtx(() => [ + return openBlock(), createElementBlock(Fragment, null, [ + createVNode(unref(NModal), { + show: showModal.value, + "onUpdate:show": _cache[1] || (_cache[1] = ($event) => showModal.value = $event), + "mask-closable": "", + "close-on-esc": "" + }, { + default: withCtx(() => [ + createVNode(unref(NCard), { + style: { "max-width": "700px" }, + title: "Copy additional properties" + }, { + default: withCtx(() => [ + createBaseVNode("div", _hoisted_1, [ createVNode(unref(NButton), { - type: "default", - onClick: toImg2Img, - style: { "width": "100%" }, - ghost: "" + type: "success", + ghost: "", + style: { "margin-right": "4px" }, + onClick: selectAll }, { default: withCtx(() => [ - createTextVNode("Img2Img") + createTextVNode("Select All") ]), _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ + }), createVNode(unref(NButton), { - type: "default", - onClick: toControlNet, - style: { "width": "100%" }, - ghost: "" + type: "warning", + ghost: "", + onClick: selectNone }, { default: withCtx(() => [ - createTextVNode("ControlNet") + createTextVNode("Select None") ]), _: 1 }) ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ + createVNode(unref(NScrollbar), { style: { "max-height": "70vh", "margin-bottom": "8px" } }, { + default: withCtx(() => [ + createBaseVNode("div", _hoisted_2, [ + (openBlock(true), createElementBlock(Fragment, null, renderList(valuesToCopyFiltered.value, (item) => { + return openBlock(), createElementBlock("div", { key: item }, [ + createBaseVNode("div", _hoisted_3, [ + createTextVNode(toDisplayString(capitalizeAndReplace(item)) + " ", 1), + createVNode(unref(NSwitch), { + value: valuesToCopy[item], + "onUpdate:value": (v) => valuesToCopy[item] = v + }, null, 8, ["value", "onUpdate:value"]) + ]), + createVNode(unref(NDivider), { style: { "margin": "12px 0" } }) + ]); + }), 128)) + ]) + ]), + _: 1 + }), + createBaseVNode("div", _hoisted_4, [ createVNode(unref(NButton), { type: "default", - onClick: toInpainting, - style: { "width": "100%" }, - ghost: "" + onClick: _cache[0] || (_cache[0] = () => showModal.value = false), + style: { "margin-right": "4px", "flex-grow": "1" } }, { + icon: withCtx(() => [ + createVNode(unref(CloseOutline)) + ]), default: withCtx(() => [ - createTextVNode("Inpainting") + createTextVNode(" Cancel ") ]), _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ + }), createVNode(unref(NButton), { - type: "default", - onClick: toUpscale, - style: { "width": "100%" }, - ghost: "" + type: "primary", + onClick: modalCopyClick, + style: { "flex-grow": "1" } }, { + icon: withCtx(() => [ + createVNode(unref(CopyOutline)) + ]), default: withCtx(() => [ - createTextVNode("Upscale") + createTextVNode(" Copy ") ]), _: 1 }) - ]), - _: 1 - }) - ]), - _: 1 - }) - ]), - _: 1 - })) : __props.output ? (openBlock(), createBlock(unref(NGrid), { - key: 1, - cols: "3", - "x-gap": "4", - "y-gap": "4" - }, { - default: withCtx(() => [ - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(unref(NButton), { - type: "default", - onClick: toImg2Img, - style: { "width": "100%" }, - ghost: "" - }, { - default: withCtx(() => [ - createTextVNode("Img2Img") - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(unref(NButton), { - type: "default", - onClick: toControlNet, - style: { "width": "100%" }, - ghost: "" - }, { - default: withCtx(() => [ - createTextVNode("ControlNet") - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { - default: withCtx(() => [ - createVNode(unref(NButton), { - type: "default", - onClick: toInpainting, - style: { "width": "100%" }, - ghost: "" - }, { - default: withCtx(() => [ - createTextVNode("Inpainting") - ]), - _: 1 - }) - ]), - _: 1 - }), - createVNode(unref(NGi), null, { + ]) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["show"]), + __props.output ? (openBlock(), createElementBlock("div", _hoisted_5, [ + __props.output && __props.card ? (openBlock(), createBlock(unref(NCard), { + key: 0, + style: { "margin": "12px 0" }, + title: "Send To" + }, { default: withCtx(() => [ - createVNode(unref(NButton), { - type: "default", - onClick: toUpscale, - style: { "width": "100%" }, - ghost: "" + createVNode(unref(NGrid), { + cols: "4", + "x-gap": "4", + "y-gap": "4" }, { default: withCtx(() => [ - createTextVNode("Upscale") + (openBlock(true), createElementBlock(Fragment, null, renderList(Object.keys(targets), (target) => { + return openBlock(), createBlock(unref(NGi), { key: target }, { + default: withCtx(() => [ + createVNode(unref(NButton), { + type: "default", + onClick: () => handleClick(target), + style: { "width": "100%" }, + ghost: "" + }, { + default: withCtx(() => [ + createTextVNode(toDisplayString(capitalizeAndReplace(target)), 1) + ]), + _: 2 + }, 1032, ["onClick"]) + ]), + _: 2 + }, 1024); + }), 128)) ]), _: 1 }) ]), _: 1 - }), - createVNode(unref(NGi), null, { + })) : (openBlock(), createBlock(unref(NGrid), { + key: 1, + cols: "3", + "x-gap": "4", + "y-gap": "4" + }, { default: withCtx(() => [ - createVNode(unref(NButton), { - type: "default", - onClick: toTagger, - style: { "width": "100%" }, - ghost: "" - }, { - default: withCtx(() => [ - createTextVNode("Tagger") - ]), - _: 1 - }) + (openBlock(true), createElementBlock(Fragment, null, renderList(Object.keys(targets), (target) => { + return openBlock(), createBlock(unref(NGi), { key: target }, { + default: withCtx(() => [ + createVNode(unref(NButton), { + type: "default", + onClick: () => handleClick(target), + style: { "width": "100%" }, + ghost: "" + }, { + default: withCtx(() => [ + createTextVNode("-> " + toDisplayString(capitalizeAndReplace(target)), 1) + ]), + _: 2 + }, 1032, ["onClick"]) + ]), + _: 2 + }, 1024); + }), 128)) ]), _: 1 - }) - ]), - _: 1 - })) : createCommentVNode("", true); + })) + ])) : createCommentVNode("", true) + ], 64); }; } }); diff --git a/frontend/dist/assets/Settings.js b/frontend/dist/assets/Settings.js new file mode 100644 index 000000000..346cfdb23 --- /dev/null +++ b/frontend/dist/assets/Settings.js @@ -0,0 +1,565 @@ +import { C as h, d as defineComponent, S as useConfig, ar as useFormItem, D as ref, c as computed, ae as useMergedState, a3 as provide, V as toRef, P as createInjectionKey, $ as call, aa as c, Q as cB, ab as cM, at as cE, aT as iconSwitchTransition, aU as insideModal, aV as insidePopover, R as inject, as as useMemo, T as useTheme, bH as checkboxLight, ag as useRtl, al as createKey, W as useThemeClass, aW as createId, av as resolveWrappedSlot, aI as NIconSwitchTransition, aD as on, e as openBlock, f as createElementBlock, n as createBaseVNode } from "./index.js"; +const CheckMark = h( + "svg", + { viewBox: "0 0 64 64", class: "check-icon" }, + h("path", { d: "M50.42,16.76L22.34,39.45l-8.1-11.46c-1.12-1.58-3.3-1.96-4.88-0.84c-1.58,1.12-1.95,3.3-0.84,4.88l10.26,14.51 c0.56,0.79,1.42,1.31,2.38,1.45c0.16,0.02,0.32,0.03,0.48,0.03c0.8,0,1.57-0.27,2.2-0.78l30.99-25.03c1.5-1.21,1.74-3.42,0.52-4.92 C54.13,15.78,51.93,15.55,50.42,16.76z" }) +); +const LineMark = h( + "svg", + { viewBox: "0 0 100 100", class: "line-icon" }, + h("path", { d: "M80.2,55.5H21.4c-2.8,0-5.1-2.5-5.1-5.5l0,0c0-3,2.3-5.5,5.1-5.5h58.7c2.8,0,5.1,2.5,5.1,5.5l0,0C85.2,53.1,82.9,55.5,80.2,55.5z" }) +); +const checkboxGroupInjectionKey = createInjectionKey("n-checkbox-group"); +const checkboxGroupProps = { + min: Number, + max: Number, + size: String, + value: Array, + defaultValue: { + type: Array, + default: null + }, + disabled: { + type: Boolean, + default: void 0 + }, + "onUpdate:value": [Function, Array], + onUpdateValue: [Function, Array], + // deprecated + onChange: [Function, Array] +}; +const NCheckboxGroup = defineComponent({ + name: "CheckboxGroup", + props: checkboxGroupProps, + setup(props) { + const { mergedClsPrefixRef } = useConfig(props); + const formItem = useFormItem(props); + const { mergedSizeRef, mergedDisabledRef } = formItem; + const uncontrolledValueRef = ref(props.defaultValue); + const controlledValueRef = computed(() => props.value); + const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef); + const checkedCount = computed(() => { + var _a; + return ((_a = mergedValueRef.value) === null || _a === void 0 ? void 0 : _a.length) || 0; + }); + const valueSetRef = computed(() => { + if (Array.isArray(mergedValueRef.value)) { + return new Set(mergedValueRef.value); + } + return /* @__PURE__ */ new Set(); + }); + function toggleCheckbox(checked, checkboxValue) { + const { nTriggerFormInput, nTriggerFormChange } = formItem; + const { onChange, "onUpdate:value": _onUpdateValue, onUpdateValue } = props; + if (Array.isArray(mergedValueRef.value)) { + const groupValue = Array.from(mergedValueRef.value); + const index = groupValue.findIndex((value) => value === checkboxValue); + if (checked) { + if (!~index) { + groupValue.push(checkboxValue); + if (onUpdateValue) { + call(onUpdateValue, groupValue, { + actionType: "check", + value: checkboxValue + }); + } + if (_onUpdateValue) { + call(_onUpdateValue, groupValue, { + actionType: "check", + value: checkboxValue + }); + } + nTriggerFormInput(); + nTriggerFormChange(); + uncontrolledValueRef.value = groupValue; + if (onChange) + call(onChange, groupValue); + } + } else { + if (~index) { + groupValue.splice(index, 1); + if (onUpdateValue) { + call(onUpdateValue, groupValue, { + actionType: "uncheck", + value: checkboxValue + }); + } + if (_onUpdateValue) { + call(_onUpdateValue, groupValue, { + actionType: "uncheck", + value: checkboxValue + }); + } + if (onChange) + call(onChange, groupValue); + uncontrolledValueRef.value = groupValue; + nTriggerFormInput(); + nTriggerFormChange(); + } + } + } else { + if (checked) { + if (onUpdateValue) { + call(onUpdateValue, [checkboxValue], { + actionType: "check", + value: checkboxValue + }); + } + if (_onUpdateValue) { + call(_onUpdateValue, [checkboxValue], { + actionType: "check", + value: checkboxValue + }); + } + if (onChange) + call(onChange, [checkboxValue]); + uncontrolledValueRef.value = [checkboxValue]; + nTriggerFormInput(); + nTriggerFormChange(); + } else { + if (onUpdateValue) { + call(onUpdateValue, [], { + actionType: "uncheck", + value: checkboxValue + }); + } + if (_onUpdateValue) { + call(_onUpdateValue, [], { + actionType: "uncheck", + value: checkboxValue + }); + } + if (onChange) + call(onChange, []); + uncontrolledValueRef.value = []; + nTriggerFormInput(); + nTriggerFormChange(); + } + } + } + provide(checkboxGroupInjectionKey, { + checkedCountRef: checkedCount, + maxRef: toRef(props, "max"), + minRef: toRef(props, "min"), + valueSetRef, + disabledRef: mergedDisabledRef, + mergedSizeRef, + toggleCheckbox + }); + return { + mergedClsPrefix: mergedClsPrefixRef + }; + }, + render() { + return h("div", { class: `${this.mergedClsPrefix}-checkbox-group`, role: "group" }, this.$slots); + } +}); +const style = c([ + cB("checkbox", ` + font-size: var(--n-font-size); + outline: none; + cursor: pointer; + display: inline-flex; + flex-wrap: nowrap; + align-items: flex-start; + word-break: break-word; + line-height: var(--n-size); + --n-merged-color-table: var(--n-color-table); + `, [cM("show-label", "line-height: var(--n-label-line-height);"), c("&:hover", [cB("checkbox-box", [cE("border", "border: var(--n-border-checked);")])]), c("&:focus:not(:active)", [cB("checkbox-box", [cE("border", ` + border: var(--n-border-focus); + box-shadow: var(--n-box-shadow-focus); + `)])]), cM("inside-table", [cB("checkbox-box", ` + background-color: var(--n-merged-color-table); + `)]), cM("checked", [cB("checkbox-box", ` + background-color: var(--n-color-checked); + `, [cB("checkbox-icon", [ + // if not set width to 100%, safari & old chrome won't display the icon + c(".check-icon", ` + opacity: 1; + transform: scale(1); + `) + ])])]), cM("indeterminate", [cB("checkbox-box", [cB("checkbox-icon", [c(".check-icon", ` + opacity: 0; + transform: scale(.5); + `), c(".line-icon", ` + opacity: 1; + transform: scale(1); + `)])])]), cM("checked, indeterminate", [c("&:focus:not(:active)", [cB("checkbox-box", [cE("border", ` + border: var(--n-border-checked); + box-shadow: var(--n-box-shadow-focus); + `)])]), cB("checkbox-box", ` + background-color: var(--n-color-checked); + border-left: 0; + border-top: 0; + `, [cE("border", { + border: "var(--n-border-checked)" + })])]), cM("disabled", { + cursor: "not-allowed" + }, [cM("checked", [cB("checkbox-box", ` + background-color: var(--n-color-disabled-checked); + `, [cE("border", { + border: "var(--n-border-disabled-checked)" + }), cB("checkbox-icon", [c(".check-icon, .line-icon", { + fill: "var(--n-check-mark-color-disabled-checked)" + })])])]), cB("checkbox-box", ` + background-color: var(--n-color-disabled); + `, [cE("border", ` + border: var(--n-border-disabled); + `), cB("checkbox-icon", [c(".check-icon, .line-icon", ` + fill: var(--n-check-mark-color-disabled); + `)])]), cE("label", ` + color: var(--n-text-color-disabled); + `)]), cB("checkbox-box-wrapper", ` + position: relative; + width: var(--n-size); + flex-shrink: 0; + flex-grow: 0; + user-select: none; + -webkit-user-select: none; + `), cB("checkbox-box", ` + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + height: var(--n-size); + width: var(--n-size); + display: inline-block; + box-sizing: border-box; + border-radius: var(--n-border-radius); + background-color: var(--n-color); + transition: background-color 0.3s var(--n-bezier); + `, [cE("border", ` + transition: + border-color .3s var(--n-bezier), + box-shadow .3s var(--n-bezier); + border-radius: inherit; + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + border: var(--n-border); + `), cB("checkbox-icon", ` + display: flex; + align-items: center; + justify-content: center; + position: absolute; + left: 1px; + right: 1px; + top: 1px; + bottom: 1px; + `, [c(".check-icon, .line-icon", ` + width: 100%; + fill: var(--n-check-mark-color); + opacity: 0; + transform: scale(0.5); + transform-origin: center; + transition: + fill 0.3s var(--n-bezier), + transform 0.3s var(--n-bezier), + opacity 0.3s var(--n-bezier), + border-color 0.3s var(--n-bezier); + `), iconSwitchTransition({ + left: "1px", + top: "1px" + })])]), cE("label", ` + color: var(--n-text-color); + transition: color .3s var(--n-bezier); + user-select: none; + -webkit-user-select: none; + padding: var(--n-label-padding); + font-weight: var(--n-label-font-weight); + `, [c("&:empty", { + display: "none" + })])]), + // modal table header checkbox + insideModal(cB("checkbox", ` + --n-merged-color-table: var(--n-color-table-modal); + `)), + // popover table header checkbox + insidePopover(cB("checkbox", ` + --n-merged-color-table: var(--n-color-table-popover); + `)) +]); +const checkboxProps = Object.assign(Object.assign({}, useTheme.props), { + size: String, + checked: { + type: [Boolean, String, Number], + default: void 0 + }, + defaultChecked: { + type: [Boolean, String, Number], + default: false + }, + value: [String, Number], + disabled: { + type: Boolean, + default: void 0 + }, + indeterminate: Boolean, + label: String, + focusable: { + type: Boolean, + default: true + }, + checkedValue: { + type: [Boolean, String, Number], + default: true + }, + uncheckedValue: { + type: [Boolean, String, Number], + default: false + }, + "onUpdate:checked": [Function, Array], + onUpdateChecked: [Function, Array], + // private + privateInsideTable: Boolean, + // deprecated + onChange: [Function, Array] +}); +const NCheckbox = defineComponent({ + name: "Checkbox", + props: checkboxProps, + setup(props) { + const selfRef = ref(null); + const { mergedClsPrefixRef, inlineThemeDisabled, mergedRtlRef } = useConfig(props); + const formItem = useFormItem(props, { + mergedSize(NFormItem) { + const { size } = props; + if (size !== void 0) + return size; + if (NCheckboxGroup2) { + const { value: mergedSize } = NCheckboxGroup2.mergedSizeRef; + if (mergedSize !== void 0) { + return mergedSize; + } + } + if (NFormItem) { + const { mergedSize } = NFormItem; + if (mergedSize !== void 0) + return mergedSize.value; + } + return "medium"; + }, + mergedDisabled(NFormItem) { + const { disabled } = props; + if (disabled !== void 0) + return disabled; + if (NCheckboxGroup2) { + if (NCheckboxGroup2.disabledRef.value) + return true; + const { maxRef: { value: max }, checkedCountRef } = NCheckboxGroup2; + if (max !== void 0 && checkedCountRef.value >= max && !renderedCheckedRef.value) { + return true; + } + const { minRef: { value: min } } = NCheckboxGroup2; + if (min !== void 0 && checkedCountRef.value <= min && renderedCheckedRef.value) { + return true; + } + } + if (NFormItem) { + return NFormItem.disabled.value; + } + return false; + } + }); + const { mergedDisabledRef, mergedSizeRef } = formItem; + const NCheckboxGroup2 = inject(checkboxGroupInjectionKey, null); + const uncontrolledCheckedRef = ref(props.defaultChecked); + const controlledCheckedRef = toRef(props, "checked"); + const mergedCheckedRef = useMergedState(controlledCheckedRef, uncontrolledCheckedRef); + const renderedCheckedRef = useMemo(() => { + if (NCheckboxGroup2) { + const groupValueSet = NCheckboxGroup2.valueSetRef.value; + if (groupValueSet && props.value !== void 0) { + return groupValueSet.has(props.value); + } + return false; + } else { + return mergedCheckedRef.value === props.checkedValue; + } + }); + const themeRef = useTheme("Checkbox", "-checkbox", style, checkboxLight, props, mergedClsPrefixRef); + function toggle(e) { + if (NCheckboxGroup2 && props.value !== void 0) { + NCheckboxGroup2.toggleCheckbox(!renderedCheckedRef.value, props.value); + } else { + const { onChange, "onUpdate:checked": _onUpdateCheck, onUpdateChecked } = props; + const { nTriggerFormInput, nTriggerFormChange } = formItem; + const nextChecked = renderedCheckedRef.value ? props.uncheckedValue : props.checkedValue; + if (_onUpdateCheck) { + call(_onUpdateCheck, nextChecked, e); + } + if (onUpdateChecked) { + call(onUpdateChecked, nextChecked, e); + } + if (onChange) + call(onChange, nextChecked, e); + nTriggerFormInput(); + nTriggerFormChange(); + uncontrolledCheckedRef.value = nextChecked; + } + } + function handleClick(e) { + if (!mergedDisabledRef.value) { + toggle(e); + } + } + function handleKeyUp(e) { + if (mergedDisabledRef.value) + return; + switch (e.key) { + case " ": + case "Enter": + toggle(e); + } + } + function handleKeyDown(e) { + switch (e.key) { + case " ": + e.preventDefault(); + } + } + const exposedMethods = { + focus: () => { + var _a; + (_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.focus(); + }, + blur: () => { + var _a; + (_a = selfRef.value) === null || _a === void 0 ? void 0 : _a.blur(); + } + }; + const rtlEnabledRef = useRtl("Checkbox", mergedRtlRef, mergedClsPrefixRef); + const cssVarsRef = computed(() => { + const { value: mergedSize } = mergedSizeRef; + const { common: { cubicBezierEaseInOut }, self: { borderRadius, color, colorChecked, colorDisabled, colorTableHeader, colorTableHeaderModal, colorTableHeaderPopover, checkMarkColor, checkMarkColorDisabled, border, borderFocus, borderDisabled, borderChecked, boxShadowFocus, textColor, textColorDisabled, checkMarkColorDisabledChecked, colorDisabledChecked, borderDisabledChecked, labelPadding, labelLineHeight, labelFontWeight, [createKey("fontSize", mergedSize)]: fontSize, [createKey("size", mergedSize)]: size } } = themeRef.value; + return { + "--n-label-line-height": labelLineHeight, + "--n-label-font-weight": labelFontWeight, + "--n-size": size, + "--n-bezier": cubicBezierEaseInOut, + "--n-border-radius": borderRadius, + "--n-border": border, + "--n-border-checked": borderChecked, + "--n-border-focus": borderFocus, + "--n-border-disabled": borderDisabled, + "--n-border-disabled-checked": borderDisabledChecked, + "--n-box-shadow-focus": boxShadowFocus, + "--n-color": color, + "--n-color-checked": colorChecked, + "--n-color-table": colorTableHeader, + "--n-color-table-modal": colorTableHeaderModal, + "--n-color-table-popover": colorTableHeaderPopover, + "--n-color-disabled": colorDisabled, + "--n-color-disabled-checked": colorDisabledChecked, + "--n-text-color": textColor, + "--n-text-color-disabled": textColorDisabled, + "--n-check-mark-color": checkMarkColor, + "--n-check-mark-color-disabled": checkMarkColorDisabled, + "--n-check-mark-color-disabled-checked": checkMarkColorDisabledChecked, + "--n-font-size": fontSize, + "--n-label-padding": labelPadding + }; + }); + const themeClassHandle = inlineThemeDisabled ? useThemeClass("checkbox", computed(() => mergedSizeRef.value[0]), cssVarsRef, props) : void 0; + return Object.assign(formItem, exposedMethods, { + rtlEnabled: rtlEnabledRef, + selfRef, + mergedClsPrefix: mergedClsPrefixRef, + mergedDisabled: mergedDisabledRef, + renderedChecked: renderedCheckedRef, + mergedTheme: themeRef, + labelId: createId(), + handleClick, + handleKeyUp, + handleKeyDown, + cssVars: inlineThemeDisabled ? void 0 : cssVarsRef, + themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, + onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender + }); + }, + render() { + var _a; + const { $slots, renderedChecked, mergedDisabled, indeterminate, privateInsideTable, cssVars, labelId, label, mergedClsPrefix, focusable, handleKeyUp, handleKeyDown, handleClick } = this; + (_a = this.onRender) === null || _a === void 0 ? void 0 : _a.call(this); + const labelNode = resolveWrappedSlot($slots.default, (children) => { + if (label || children) { + return h("span", { class: `${mergedClsPrefix}-checkbox__label`, id: labelId }, label || children); + } + return null; + }); + return h( + "div", + { ref: "selfRef", class: [ + `${mergedClsPrefix}-checkbox`, + this.themeClass, + this.rtlEnabled && `${mergedClsPrefix}-checkbox--rtl`, + renderedChecked && `${mergedClsPrefix}-checkbox--checked`, + mergedDisabled && `${mergedClsPrefix}-checkbox--disabled`, + indeterminate && `${mergedClsPrefix}-checkbox--indeterminate`, + privateInsideTable && `${mergedClsPrefix}-checkbox--inside-table`, + labelNode && `${mergedClsPrefix}-checkbox--show-label` + ], tabindex: mergedDisabled || !focusable ? void 0 : 0, role: "checkbox", "aria-checked": indeterminate ? "mixed" : renderedChecked, "aria-labelledby": labelId, style: cssVars, onKeyup: handleKeyUp, onKeydown: handleKeyDown, onClick: handleClick, onMousedown: () => { + on("selectstart", window, (e) => { + e.preventDefault(); + }, { + once: true + }); + } }, + h( + "div", + { class: `${mergedClsPrefix}-checkbox-box-wrapper` }, + " ", + h( + "div", + { class: `${mergedClsPrefix}-checkbox-box` }, + h(NIconSwitchTransition, null, { + default: () => this.indeterminate ? h("div", { key: "indeterminate", class: `${mergedClsPrefix}-checkbox-icon` }, LineMark) : h("div", { key: "check", class: `${mergedClsPrefix}-checkbox-icon` }, CheckMark) + }), + h("div", { class: `${mergedClsPrefix}-checkbox-box__border` }) + ) + ), + labelNode + ); + } +}); +const _hoisted_1 = { + xmlns: "http://www.w3.org/2000/svg", + "xmlns:xlink": "http://www.w3.org/1999/xlink", + viewBox: "0 0 512 512" +}; +const _hoisted_2 = /* @__PURE__ */ createBaseVNode( + "circle", + { + cx: "256", + cy: "256", + r: "48", + fill: "currentColor" + }, + null, + -1 + /* HOISTED */ +); +const _hoisted_3 = /* @__PURE__ */ createBaseVNode( + "path", + { + d: "M470.39 300l-.47-.38l-31.56-24.75a16.11 16.11 0 0 1-6.1-13.33v-11.56a16 16 0 0 1 6.11-13.22L469.92 212l.47-.38a26.68 26.68 0 0 0 5.9-34.06l-42.71-73.9a1.59 1.59 0 0 1-.13-.22A26.86 26.86 0 0 0 401 92.14l-.35.13l-37.1 14.93a15.94 15.94 0 0 1-14.47-1.29q-4.92-3.1-10-5.86a15.94 15.94 0 0 1-8.19-11.82l-5.59-39.59l-.12-.72A27.22 27.22 0 0 0 298.76 26h-85.52a26.92 26.92 0 0 0-26.45 22.39l-.09.56l-5.57 39.67a16 16 0 0 1-8.13 11.82a175.21 175.21 0 0 0-10 5.82a15.92 15.92 0 0 1-14.43 1.27l-37.13-15l-.35-.14a26.87 26.87 0 0 0-32.48 11.34l-.13.22l-42.77 73.95a26.71 26.71 0 0 0 5.9 34.1l.47.38l31.56 24.75a16.11 16.11 0 0 1 6.1 13.33v11.56a16 16 0 0 1-6.11 13.22L42.08 300l-.47.38a26.68 26.68 0 0 0-5.9 34.06l42.71 73.9a1.59 1.59 0 0 1 .13.22a26.86 26.86 0 0 0 32.45 11.3l.35-.13l37.07-14.93a15.94 15.94 0 0 1 14.47 1.29q4.92 3.11 10 5.86a15.94 15.94 0 0 1 8.19 11.82l5.56 39.59l.12.72A27.22 27.22 0 0 0 213.24 486h85.52a26.92 26.92 0 0 0 26.45-22.39l.09-.56l5.57-39.67a16 16 0 0 1 8.18-11.82c3.42-1.84 6.76-3.79 10-5.82a15.92 15.92 0 0 1 14.43-1.27l37.13 14.95l.35.14a26.85 26.85 0 0 0 32.48-11.34a2.53 2.53 0 0 1 .13-.22l42.71-73.89a26.7 26.7 0 0 0-5.89-34.11zm-134.48-40.24a80 80 0 1 1-83.66-83.67a80.21 80.21 0 0 1 83.66 83.67z", + fill: "currentColor" + }, + null, + -1 + /* HOISTED */ +); +const _hoisted_4 = [_hoisted_2, _hoisted_3]; +const Settings = defineComponent({ + name: "Settings", + render: function render(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1, _hoisted_4); + } +}); +export { + NCheckboxGroup as N, + Settings as S, + NCheckbox as a +}; diff --git a/frontend/dist/assets/SettingsView.js b/frontend/dist/assets/SettingsView.js index 5b51a3cda..028346c1b 100644 --- a/frontend/dist/assets/SettingsView.js +++ b/frontend/dist/assets/SettingsView.js @@ -1,7 +1,8 @@ -import { a3 as inject, bw as getCurrentInstance, K as watch, aH as onBeforeUnmount, Y as cB, $ as cM, X as c, V as createInjectionKey, d as defineComponent, Q as useConfig, a5 as useTheme, E as ref, T as provide, D as h, bx as formLight, ai as keysOf, c as computed, aF as formatLength, aM as get, by as commonVariables, Z as cE, U as toRef, aa as createId, bz as formItemInjectionKey, b9 as onMounted, a9 as useThemeClass, aX as Transition, aB as resolveWrappedSlot, a8 as createKey, aQ as warn, a as useSettings, u as useState, e as openBlock, v as createBlock, w as withCtx, g as createVNode, h as unref, k as NInput, r as NSelect, x as createCommentVNode, f as createElementBlock, n as createBaseVNode, i as NCard, H as NTabPane, I as NTabs, b as useMessage, bA as useNotification, o as onUnmounted, s as serverUrl, F as NButton, m as createTextVNode, bB as defaultSettings } from "./index.js"; -import { N as NSwitch } from "./Switch.js"; +import { R as inject, by as getCurrentInstance, J as watch, aB as onBeforeUnmount, Q as cB, ab as cM, aa as c, P as createInjectionKey, d as defineComponent, S as useConfig, T as useTheme, D as ref, a3 as provide, C as h, bz as formLight, a1 as keysOf, c as computed, az as formatLength, aH as get, bA as commonVariables, at as cE, V as toRef, aW as createId, bB as formItemInjectionKey, b9 as onMounted, W as useThemeClass, aX as Transition, av as resolveWrappedSlot, al as createKey, aM as warn, a as useSettings, u as useState, e as openBlock, v as createBlock, w as withCtx, g as createVNode, h as unref, x as NSelect, n as createBaseVNode, f as createElementBlock, L as renderList, bb as NText, m as createTextVNode, t as toDisplayString, I as Fragment, i as NCard, b8 as reactive, s as serverUrl, k as NInput, bC as convertToTextString, r as createCommentVNode, G as NTabPane, H as NTabs, bD as themeKey, E as NButton, q as NTooltip, b as useMessage, bE as useNotification, o as onUnmounted, bF as defaultSettings } from "./index.js"; +import { a as NSwitch, N as NSlider } from "./Switch.js"; import { N as NInputNumber } from "./InputNumber.js"; -import { N as NSlider } from "./Slider.js"; +import { _ as _sfc_main$h } from "./SamplerPicker.vue_vue_type_script_setup_true_lang.js"; +import "./Settings.js"; function useInjectionInstanceCollection(injectionName, collectionKey, registerKeyRef) { var _a; const injection = inject(injectionName, null); @@ -1839,578 +1840,133 @@ const NFormItem = defineComponent({ ); } }); -const _hoisted_1$4 = /* @__PURE__ */ createBaseVNode("h2", null, "Saving outputs", -1); -const _hoisted_2 = /* @__PURE__ */ createBaseVNode("h2", null, "CLIP settings", -1); -const _hoisted_3 = /* @__PURE__ */ createBaseVNode("h2", null, "Autoload", -1); -const _hoisted_4 = /* @__PURE__ */ createBaseVNode("h2", null, "Timings and Queue", -1); -const _hoisted_5 = /* @__PURE__ */ createBaseVNode("h2", null, "Optimizations", -1); -const _hoisted_6 = { - key: 1, - class: "flex-container" -}; -const _hoisted_7 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Subquadratic chunk size (affects VRAM usage)", -1); -const _hoisted_8 = /* @__PURE__ */ createBaseVNode("h2", null, "Device", -1); -const _hoisted_9 = /* @__PURE__ */ createBaseVNode("h2", null, "TomeSD", -1); -const _hoisted_10 = { key: 3 }; -const _hoisted_11 = /* @__PURE__ */ createBaseVNode("h2", null, "Torch Compile", -1); -const _hoisted_12 = { key: 4 }; -const _sfc_main$d = /* @__PURE__ */ defineComponent({ - __name: "APISettings", +const _hoisted_1$3 = { style: { "width": "100%" } }; +const _sfc_main$g = /* @__PURE__ */ defineComponent({ + __name: "AutoloadSettings", setup(__props) { const settings = useSettings(); const global = useState(); - const availableDtypes = computed(() => { - if (settings.defaultSettings.api.device_type == "cpu") { - return global.state.capabilities.supported_precisions_cpu.map((value) => { - var description = ""; - switch (value) { - case "float32": - description = "32-bit float"; - break; - case "float16": - description = "16-bit float"; - break; - default: - description = "16-bit bfloat"; - } - return { value, label: description }; - }); - } - return global.state.capabilities.supported_precisions_gpu.map((value) => { - var description = ""; - switch (value) { - case "float32": - description = "32-bit float"; - break; - case "float16": - description = "16-bit float"; - break; - default: - description = "16-bit bfloat"; - } - return { value, label: description }; + const textualInversions = computed(() => { + return global.state.models.filter((model) => { + return model.backend === "Textual Inversion"; }); }); - const availableBackends = computed(() => { - return global.state.capabilities.supported_backends.map((value) => { - switch (value) { - case "cuda": - return { value: "cuda", label: "CUDA/ROCm" }; - case "mps": - return { value: "mps", label: "MPS (Apple)" }; - case "xpu": - return { value: "intel", label: "Intel" }; - case "directml": - return { value: "directml", label: "DirectML" }; - default: - return { value: "cpu", label: "CPU" }; - } + const textualInversionOptions = computed(() => { + return textualInversions.value.map((model) => { + return { + value: model.path, + label: model.name + }; }); }); - const availableTorchCompileBackends = computed(() => { - return global.state.capabilities.supported_torch_compile_backends.map( - (value) => { - return { value, label: value }; - } - ); - }); - const availableAttentions = computed(() => { - return [ - ...global.state.capabilities.supports_xformers ? [{ value: "xformers", label: "xFormers" }] : [], - { - value: "sdpa", - label: "SDP Attention" - }, - { - value: "cross-attention", - label: "Cross-Attention" - }, - { - value: "subquadratic", - label: "Sub-quadratic Attention" - }, - { - value: "multihead", - label: "Multihead attention" - } - ]; - }); - const availableQuantizations = computed(() => { - return [ - { value: "full", label: "Full precision" }, - ...global.state.capabilities.supports_int8 ? [ - { value: "int8", label: "Quantized (int8)" }, - { value: "int4", label: "Quantized (int4)" } - ] : [] - ]; + const availableModels = computed(() => { + return global.state.models.filter((model) => { + return model.backend === "AITemplate" || model.backend === "PyTorch" || model.backend === "ONNX"; + }); }); - const textualInversions = computed(() => { + const availableVaes = computed(() => { return global.state.models.filter((model) => { - return model.backend === "Textual Inversion"; + return model.backend === "VAE"; }); }); - const textualInversionOptions = computed(() => { - return textualInversions.value.map((model) => { + const autoloadModelOptions = computed(() => { + return availableModels.value.map((model) => { return { value: model.path, label: model.name }; }); }); + const autoloadVaeOptions = computed(() => { + const arr = availableVaes.value.map((model) => { + return { + value: model.path, + label: model.name + }; + }); + arr.push({ value: "default", label: "Default" }); + return arr; + }); + const autoloadVaeValue = (model) => { + return computed({ + get: () => { + return settings.defaultSettings.api.autoloaded_vae[model] ?? "default"; + }, + set: (value) => { + if (!value || value === "default") { + delete settings.defaultSettings.api.autoloaded_vae[model]; + } else { + settings.defaultSettings.api.autoloaded_vae[model] = value; + } + } + }); + }; return (_ctx, _cache) => { - return openBlock(), createBlock(unref(NForm), null, { - default: withCtx(() => [ - _hoisted_1$4, - createVNode(unref(NFormItem), { label: "Template for saving outputs" }, { - default: withCtx(() => [ - createVNode(unref(NInput), { - value: unref(settings).defaultSettings.api.save_path_template, - "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.api.save_path_template = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Disable generating grid image" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.disable_grid, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.disable_grid = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Image extension" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - value: unref(settings).defaultSettings.api.image_extension, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).defaultSettings.api.image_extension = $event), - options: [ - { - label: "PNG", - value: "png" - }, - { - label: "WebP", - value: "webp" - }, - { - label: "JPEG", - value: "jpeg" - } - ] - }, null, 8, ["value"]) - ]), - _: 1 - }), - unref(settings).defaultSettings.api.image_extension != "png" ? (openBlock(), createBlock(unref(NFormItem), { - key: 0, - label: "Image quality (JPEG/WebP only)" - }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.image_quality, - "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).defaultSettings.api.image_quality = $event), - min: 0, - max: 100, - step: 1 - }, null, 8, ["value"]) - ]), - _: 1 - })) : createCommentVNode("", true), - _hoisted_2, - createVNode(unref(NFormItem), { label: "CLIP skip" }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.clip_skip, - "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(settings).defaultSettings.api.clip_skip = $event), - min: 1, - max: 11, - step: 1 - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Precision" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - value: unref(settings).defaultSettings.api.clip_quantization, - "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(settings).defaultSettings.api.clip_quantization = $event), - options: availableQuantizations.value, - disabled: !unref(global).state.capabilities.supports_int8 - }, null, 8, ["value", "options", "disabled"]) - ]), - _: 1 - }), - _hoisted_3, - createVNode(unref(NFormItem), { label: "Textual Inversions" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - multiple: "", - options: textualInversionOptions.value, - value: unref(settings).defaultSettings.api.autoloaded_textual_inversions, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).defaultSettings.api.autoloaded_textual_inversions = $event) - }, null, 8, ["options", "value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Huggingface-style prompting" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.huggingface_style_parsing, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.api.huggingface_style_parsing = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - _hoisted_4, - createVNode(unref(NFormItem), { label: "WebSocket Performance Monitor Interval" }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.websocket_perf_interval, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.api.websocket_perf_interval = $event), - min: 0.1, - step: 0.1 - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "WebSocket Sync Interval" }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.websocket_sync_interval, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.api.websocket_sync_interval = $event), - min: 1e-3, - step: 0.01 - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Image Preview Interval (seconds)" }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.image_preview_delay, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).defaultSettings.api.image_preview_delay = $event), - step: 0.1 - }, null, 8, ["value"]) - ]), - _: 1 - }), - _hoisted_5, - createVNode(unref(NFormItem), { label: "Autocast" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.autocast, - "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).defaultSettings.api.autocast = $event), - disabled: availableDtypes.value.length < 2 - }, null, 8, ["value", "disabled"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Attention Processor" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: availableAttentions.value, - value: unref(settings).defaultSettings.api.attention_processor, - "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).defaultSettings.api.attention_processor = $event) - }, null, 8, ["options", "value"]) - ]), - _: 1 - }), - unref(settings).defaultSettings.api.attention_processor == "subquadratic" ? (openBlock(), createElementBlock("div", _hoisted_6, [ - _hoisted_7, - createVNode(unref(NSlider), { - value: unref(settings).defaultSettings.api.subquadratic_size, - "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).defaultSettings.api.subquadratic_size = $event), - step: 64, - min: 64, - max: 8192, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.subquadratic_size, - "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).defaultSettings.api.subquadratic_size = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 64, - min: 64, - max: 8192 - }, null, 8, ["value"]) - ])) : createCommentVNode("", true), - createVNode(unref(NFormItem), { label: "Attention Slicing" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: [ - { - value: "disabled", - label: "None" - }, - { - value: "auto", - label: "Auto" - } - ], - value: unref(settings).defaultSettings.api.attention_slicing, - "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(settings).defaultSettings.api.attention_slicing = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Channels Last" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.channels_last, - "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(settings).defaultSettings.api.channels_last = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Deterministic generation" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.deterministic_generation, - "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(settings).defaultSettings.api.deterministic_generation = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Reduced Precision (RTX 30xx and newer cards)" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.reduced_precision, - "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(settings).defaultSettings.api.reduced_precision = $event), - disabled: !unref(global).state.capabilities.has_tensorfloat - }, null, 8, ["value", "disabled"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "CudNN Benchmark (big VRAM spikes - use on 8GB+ cards only)" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.cudnn_benchmark, - "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(settings).defaultSettings.api.cudnn_benchmark = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Clean Memory" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: [ - { - value: "always", - label: "Always" - }, - { - value: "never", - label: "Never" - }, - { - value: "after_disconnect", - label: "After disconnect" - } - ], - value: unref(settings).defaultSettings.api.clear_memory_policy, - "onUpdate:value": _cache[20] || (_cache[20] = ($event) => unref(settings).defaultSettings.api.clear_memory_policy = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "VAE Slicing" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.vae_slicing, - "onUpdate:value": _cache[21] || (_cache[21] = ($event) => unref(settings).defaultSettings.api.vae_slicing = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "VAE Tiling" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.vae_tiling, - "onUpdate:value": _cache[22] || (_cache[22] = ($event) => unref(settings).defaultSettings.api.vae_tiling = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Trace UNet" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.trace_model, - "onUpdate:value": _cache[23] || (_cache[23] = ($event) => unref(settings).defaultSettings.api.trace_model = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Offload" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: [ - { - value: "disabled", - label: "Disabled" - }, - { - value: "model", - label: "Offload the whole model to RAM when not used" - }, - { - value: "module", - label: "Offload individual modules to RAM when not used" - } - ], - value: unref(settings).defaultSettings.api.offload, - "onUpdate:value": _cache[24] || (_cache[24] = ($event) => unref(settings).defaultSettings.api.offload = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - _hoisted_8, - createVNode(unref(NFormItem), { label: "Device Type" }, { + return openBlock(), createBlock(unref(NForm), null, { + default: withCtx(() => [ + createVNode(unref(NFormItem), { + label: "Model", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSelect), { - options: availableBackends.value, - value: unref(settings).defaultSettings.api.device_type, - "onUpdate:value": _cache[25] || (_cache[25] = ($event) => unref(settings).defaultSettings.api.device_type = $event) + multiple: "", + filterable: "", + options: autoloadModelOptions.value, + value: unref(settings).defaultSettings.api.autoloaded_models, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.api.autoloaded_models = $event) }, null, 8, ["options", "value"]) ]), _: 1 }), - unref(settings).defaultSettings.api.device_type != "cpu" ? (openBlock(), createBlock(unref(NFormItem), { - key: 2, - label: "Device ID (GPU ID)" + createVNode(unref(NFormItem), { + label: "Textual Inversions", + "label-placement": "left" }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.device_id, - "onUpdate:value": _cache[26] || (_cache[26] = ($event) => unref(settings).defaultSettings.api.device_id = $event) - }, null, 8, ["value"]) - ]), - _: 1 - })) : createCommentVNode("", true), - createVNode(unref(NFormItem), { label: "Precision" }, { default: withCtx(() => [ createVNode(unref(NSelect), { - options: availableDtypes.value, - value: unref(settings).defaultSettings.api.data_type, - "onUpdate:value": _cache[27] || (_cache[27] = ($event) => unref(settings).defaultSettings.api.data_type = $event) + multiple: "", + filterable: "", + options: textualInversionOptions.value, + value: unref(settings).defaultSettings.api.autoloaded_textual_inversions, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.autoloaded_textual_inversions = $event) }, null, 8, ["options", "value"]) ]), _: 1 }), - _hoisted_9, - createVNode(unref(NFormItem), { label: "Use TomeSD" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.use_tomesd, - "onUpdate:value": _cache[28] || (_cache[28] = ($event) => unref(settings).defaultSettings.api.use_tomesd = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - unref(settings).defaultSettings.api.use_tomesd ? (openBlock(), createElementBlock("div", _hoisted_10, [ - createVNode(unref(NFormItem), { label: "TomeSD Ratio" }, { - default: withCtx(() => [ - createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.api.tomesd_ratio, - "onUpdate:value": _cache[29] || (_cache[29] = ($event) => unref(settings).defaultSettings.api.tomesd_ratio = $event), - min: 0.1, - max: 1 - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "TomeSD Downsample layers" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: [ - { - value: 1, - label: "1" - }, - { - value: 2, - label: "2" - }, - { - value: 4, - label: "4" - }, - { - value: 8, - label: "8" - } - ], - value: unref(settings).defaultSettings.api.tomesd_downsample_layers, - "onUpdate:value": _cache[30] || (_cache[30] = ($event) => unref(settings).defaultSettings.api.tomesd_downsample_layers = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }) - ])) : createCommentVNode("", true), - _hoisted_11, - createVNode(unref(NFormItem), { label: "Torch Compile" }, { + createVNode(unref(NCard), { title: "VAE" }, { default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.torch_compile, - "onUpdate:value": _cache[31] || (_cache[31] = ($event) => unref(settings).defaultSettings.api.torch_compile = $event) - }, null, 8, ["value"]) + createBaseVNode("div", _hoisted_1$3, [ + (openBlock(true), createElementBlock(Fragment, null, renderList(availableModels.value, (model) => { + return openBlock(), createElementBlock("div", { + key: model.name, + style: { "display": "flex", "flex-direction": "row", "margin-bottom": "4px" } + }, [ + createVNode(unref(NText), { style: { "width": "50%" } }, { + default: withCtx(() => [ + createTextVNode(toDisplayString(model.name), 1) + ]), + _: 2 + }, 1024), + createVNode(unref(NSelect), { + filterable: "", + options: autoloadVaeOptions.value, + value: autoloadVaeValue(model.path).value, + "onUpdate:value": ($event) => autoloadVaeValue(model.path).value = $event + }, null, 8, ["options", "value", "onUpdate:value"]) + ]); + }), 128)) + ]) ]), _: 1 - }), - unref(settings).defaultSettings.api.torch_compile ? (openBlock(), createElementBlock("div", _hoisted_12, [ - createVNode(unref(NFormItem), { label: "Fullgraph" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.torch_compile_fullgraph, - "onUpdate:value": _cache[32] || (_cache[32] = ($event) => unref(settings).defaultSettings.api.torch_compile_fullgraph = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Dynamic" }, { - default: withCtx(() => [ - createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.api.torch_compile_dynamic, - "onUpdate:value": _cache[33] || (_cache[33] = ($event) => unref(settings).defaultSettings.api.torch_compile_dynamic = $event) - }, null, 8, ["value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Backend" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - value: unref(settings).defaultSettings.api.torch_compile_backend, - "onUpdate:value": _cache[34] || (_cache[34] = ($event) => unref(settings).defaultSettings.api.torch_compile_backend = $event), - tag: "", - filterable: "", - options: availableTorchCompileBackends.value - }, null, 8, ["value", "options"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Compile Mode" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - value: unref(settings).defaultSettings.api.torch_compile_mode, - "onUpdate:value": _cache[35] || (_cache[35] = ($event) => unref(settings).defaultSettings.api.torch_compile_mode = $event), - options: [ - { value: "default", label: "Default" }, - { value: "reduce-overhead", label: "Reduce Overhead" }, - { value: "max-autotune", label: "Max Autotune" } - ] - }, null, 8, ["value"]) - ]), - _: 1 - }) - ])) : createCommentVNode("", true) + }) ]), _: 1 }); }; } }); -const _sfc_main$c = /* @__PURE__ */ defineComponent({ +const _sfc_main$f = /* @__PURE__ */ defineComponent({ __name: "BotSettings", setup(__props) { const settings = useSettings(); @@ -2419,7 +1975,10 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Default Scheduler" }, { + createVNode(unref(NFormItem), { + label: "Default Scheduler", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSelect), { options: unref(settings).scheduler_options, @@ -2429,7 +1988,10 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Use Default Negative Prompt" }, { + createVNode(unref(NFormItem), { + label: "Use Default Negative Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSwitch), { value: unref(settings).defaultSettings.bot.use_default_negative_prompt, @@ -2438,7 +2000,10 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Verbose" }, { + createVNode(unref(NFormItem), { + label: "Verbose", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSwitch), { value: unref(settings).defaultSettings.bot.verbose, @@ -2456,16 +2021,94 @@ const _sfc_main$c = /* @__PURE__ */ defineComponent({ }; } }); -const _sfc_main$b = /* @__PURE__ */ defineComponent({ - __name: "HiresSettings", +const _sfc_main$e = /* @__PURE__ */ defineComponent({ + __name: "ThemeSettings", setup(__props) { const settings = useSettings(); + const extraThemes = reactive([]); + const themeOptions = computed(() => { + return extraThemes.map((theme) => { + return { label: convertToTextString(theme), value: theme }; + }); + }); + const themesLoading = ref(true); + fetch(`${serverUrl}/api/general/themes`).then(async (res) => { + const data = await res.json(); + extraThemes.push(...data); + themesLoading.value = false; + }).catch((err) => { + console.error(err); + themesLoading.value = false; + }); + watch(settings.defaultSettings.frontend, () => { + settings.data.settings.frontend = settings.defaultSettings.frontend; + }); return (_ctx, _cache) => { return openBlock(), createBlock(unref(NCard), null, { default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Scale" }, { + createVNode(unref(NFormItem), { + label: "Theme", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: themeOptions.value, + value: unref(settings).defaultSettings.frontend.theme, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.frontend.theme = $event), + loading: themesLoading.value, + filterable: "" + }, null, 8, ["options", "value", "loading"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Background Image Override", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NInput), { + value: unref(settings).defaultSettings.frontend.background_image_override, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.frontend.background_image_override = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Enable Theme Editor", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.frontend.enable_theme_editor, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).defaultSettings.frontend.enable_theme_editor = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }); + }; + } +}); +const _sfc_main$d = /* @__PURE__ */ defineComponent({ + __name: "ExtraSettings", + setup(__props) { + const settings = useSettings(); + return (_ctx, _cache) => { + return openBlock(), createBlock(unref(NCard), { title: "Hi-res fix" }, { + default: withCtx(() => [ + createVNode(unref(NForm), null, { + default: withCtx(() => [ + createVNode(unref(NFormItem), { + label: "Scale", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.extra.highres.scale, @@ -2474,7 +2117,10 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Scaling Mode" }, { + createVNode(unref(NFormItem), { + label: "Scaling Mode", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSelect), { options: [ @@ -2513,7 +2159,10 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Strength" }, { + createVNode(unref(NFormItem), { + label: "Strength", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.extra.highres.strength, @@ -2522,7 +2171,10 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Steps" }, { + createVNode(unref(NFormItem), { + label: "Steps", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.extra.highres.steps, @@ -2531,7 +2183,10 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Antialiased" }, { + createVNode(unref(NFormItem), { + label: "Antialiased", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSwitch), { value: unref(settings).defaultSettings.extra.highres.antialiased, @@ -2549,25 +2204,86 @@ const _sfc_main$b = /* @__PURE__ */ defineComponent({ }; } }); -const _sfc_main$a = /* @__PURE__ */ defineComponent({ - __name: "ExtraSettings", +const _sfc_main$c = /* @__PURE__ */ defineComponent({ + __name: "FilesSettings", setup(__props) { + const settings = useSettings(); return (_ctx, _cache) => { - return openBlock(), createBlock(unref(NTabs), null, { + return openBlock(), createBlock(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NTabPane), { name: "Highres fix" }, { + createVNode(unref(NFormItem), { + label: "Template for saving outputs", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$b) + createVNode(unref(NInput), { + value: unref(settings).defaultSettings.api.save_path_template, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.api.save_path_template = $event) + }, null, 8, ["value"]) ]), _: 1 - }) + }), + createVNode(unref(NFormItem), { + label: "Disable generating grid image", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.disable_grid, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.disable_grid = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Image extension", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + value: unref(settings).defaultSettings.api.image_extension, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).defaultSettings.api.image_extension = $event), + options: [ + { + label: "PNG", + value: "png" + }, + { + label: "WebP", + value: "webp" + }, + { + label: "JPEG", + value: "jpeg" + } + ] + }, null, 8, ["value"]) + ]), + _: 1 + }), + unref(settings).defaultSettings.api.image_extension != "png" ? (openBlock(), createBlock(unref(NFormItem), { + key: 0, + label: "Image quality (JPEG/WebP only)", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.image_quality, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).defaultSettings.api.image_quality = $event), + min: 0, + max: 100, + step: 1 + }, null, 8, ["value"]) + ]), + _: 1 + })) : createCommentVNode("", true) ]), _: 1 }); }; } }); -const _sfc_main$9 = /* @__PURE__ */ defineComponent({ +const _sfc_main$b = /* @__PURE__ */ defineComponent({ __name: "ControlNetSettings", setup(__props) { const settings = useSettings(); @@ -2576,7 +2292,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Prompt" }, { + createVNode(unref(NFormItem), { + label: "Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.controlnet.prompt, @@ -2585,7 +2304,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Negative Prompt" }, { + createVNode(unref(NFormItem), { + label: "Negative Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.controlnet.negative_prompt, @@ -2594,7 +2316,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Count" }, { + createVNode(unref(NFormItem), { + label: "Batch Count", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.batch_count, @@ -2603,7 +2328,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Size" }, { + createVNode(unref(NFormItem), { + label: "Batch Size", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.batch_size, @@ -2612,7 +2340,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "CFG Scale" }, { + createVNode(unref(NFormItem), { + label: "CFG Scale", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.cfg_scale, @@ -2622,7 +2353,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Height" }, { + createVNode(unref(NFormItem), { + label: "Height", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.height, @@ -2632,7 +2366,10 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Width" }, { + createVNode(unref(NFormItem), { + label: "Width", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.width, @@ -2642,75 +2379,87 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Sampler" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: unref(settings).scheduler_options, - value: unref(settings).defaultSettings.controlnet.sampler, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.controlnet.sampler = $event) - }, null, 8, ["options", "value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "ControlNet" }, { + createVNode(unref(NFormItem), { + label: "ControlNet", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSelect), { options: unref(settings).controlnet_options, value: unref(settings).defaultSettings.controlnet.controlnet, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.controlnet.controlnet = $event), + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.controlnet.controlnet = $event), filterable: "", tag: "" }, null, 8, ["options", "value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Seed" }, { + createVNode(unref(NFormItem), { + label: "Seed", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.seed, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.controlnet.seed = $event), + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.controlnet.seed = $event), min: -1 }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Is Preprocessed" }, { + createVNode(unref(NFormItem), { + label: "Is Preprocessed", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSwitch), { value: unref(settings).defaultSettings.controlnet.is_preprocessed, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).defaultSettings.controlnet.is_preprocessed = $event) + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.controlnet.is_preprocessed = $event) }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Steps" }, { + createVNode(unref(NFormItem), { + label: "Steps", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.steps, - "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).defaultSettings.controlnet.steps = $event) + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).defaultSettings.controlnet.steps = $event) }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "ControlNet Conditioning Scale" }, { + createVNode(unref(NFormItem), { + label: "ControlNet Conditioning Scale", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.controlnet_conditioning_scale, - "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).defaultSettings.controlnet.controlnet_conditioning_scale = $event), + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).defaultSettings.controlnet.controlnet_conditioning_scale = $event), step: 0.1 }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Detection Resolution" }, { + createVNode(unref(NFormItem), { + label: "Detection Resolution", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.controlnet.detection_resolution, - "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).defaultSettings.controlnet.detection_resolution = $event), + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).defaultSettings.controlnet.detection_resolution = $event), step: 8 }, null, 8, ["value"]) ]), _: 1 + }), + createVNode(_sfc_main$h, { + type: "controlnet", + target: "defaultSettings" }) ]), _: 1 @@ -2721,18 +2470,19 @@ const _sfc_main$9 = /* @__PURE__ */ defineComponent({ }; } }); -const _hoisted_1$3 = /* @__PURE__ */ createBaseVNode("h2", null, "Default settings", -1); -const _sfc_main$8 = /* @__PURE__ */ defineComponent({ +const _sfc_main$a = /* @__PURE__ */ defineComponent({ __name: "ImageBrowserSettings", setup(__props) { const settings = useSettings(); return (_ctx, _cache) => { - return openBlock(), createBlock(unref(NCard), null, { + return openBlock(), createBlock(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NForm), null, { + createVNode(unref(NCard), null, { default: withCtx(() => [ - _hoisted_1$3, - createVNode(unref(NFormItem), { label: "Number of columns" }, { + createVNode(unref(NFormItem), { + label: "Number of columns", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.frontend.image_browser_columns, @@ -2750,7 +2500,7 @@ const _sfc_main$8 = /* @__PURE__ */ defineComponent({ }; } }); -const _sfc_main$7 = /* @__PURE__ */ defineComponent({ +const _sfc_main$9 = /* @__PURE__ */ defineComponent({ __name: "ImageToImageSettings", setup(__props) { const settings = useSettings(); @@ -2759,7 +2509,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Prompt" }, { + createVNode(unref(NFormItem), { + label: "Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.img2img.prompt, @@ -2768,7 +2521,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Negative Prompt" }, { + createVNode(unref(NFormItem), { + label: "Negative Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.img2img.negative_prompt, @@ -2777,7 +2533,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Count" }, { + createVNode(unref(NFormItem), { + label: "Batch Count", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.batch_count, @@ -2786,7 +2545,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Size" }, { + createVNode(unref(NFormItem), { + label: "Batch Size", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.batch_size, @@ -2795,7 +2557,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "CFG Scale" }, { + createVNode(unref(NFormItem), { + label: "CFG Scale", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.cfg_scale, @@ -2805,7 +2570,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Height" }, { + createVNode(unref(NFormItem), { + label: "Height", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.height, @@ -2815,7 +2583,10 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Width" }, { + createVNode(unref(NFormItem), { + label: "Width", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.width, @@ -2825,44 +2596,47 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Sampler" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: unref(settings).scheduler_options, - value: unref(settings).defaultSettings.img2img.sampler, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.img2img.sampler = $event) - }, null, 8, ["options", "value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Seed" }, { + createVNode(unref(NFormItem), { + label: "Seed", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.seed, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.img2img.seed = $event), + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.img2img.seed = $event), min: -1 }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Steps" }, { + createVNode(unref(NFormItem), { + label: "Steps", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.steps, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.img2img.steps = $event) + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.img2img.steps = $event) }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Denoising Strength" }, { + createVNode(unref(NFormItem), { + label: "Denoising Strength", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.img2img.denoising_strength, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).defaultSettings.img2img.denoising_strength = $event), + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.img2img.denoising_strength = $event), step: 0.1 }, null, 8, ["value"]) ]), _: 1 + }), + createVNode(_sfc_main$h, { + type: "img2img", + target: "defaultSettings" }) ]), _: 1 @@ -2873,7 +2647,7 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({ }; } }); -const _sfc_main$6 = /* @__PURE__ */ defineComponent({ +const _sfc_main$8 = /* @__PURE__ */ defineComponent({ __name: "InpaintingSettings", setup(__props) { const settings = useSettings(); @@ -2882,7 +2656,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Prompt" }, { + createVNode(unref(NFormItem), { + label: "Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.inpainting.prompt, @@ -2891,7 +2668,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Negative Prompt" }, { + createVNode(unref(NFormItem), { + label: "Negative Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.inpainting.negative_prompt, @@ -2900,7 +2680,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Count" }, { + createVNode(unref(NFormItem), { + label: "Batch Count", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.batch_count, @@ -2909,7 +2692,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Size" }, { + createVNode(unref(NFormItem), { + label: "Batch Size", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.batch_size, @@ -2918,7 +2704,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "CFG Scale" }, { + createVNode(unref(NFormItem), { + label: "CFG Scale", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.cfg_scale, @@ -2928,7 +2717,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Height" }, { + createVNode(unref(NFormItem), { + label: "Height", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.height, @@ -2938,7 +2730,10 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Width" }, { + createVNode(unref(NFormItem), { + label: "Width", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.width, @@ -2948,34 +2743,34 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Sampler" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: unref(settings).scheduler_options, - value: unref(settings).defaultSettings.inpainting.sampler, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.inpainting.sampler = $event) - }, null, 8, ["options", "value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Seed" }, { + createVNode(unref(NFormItem), { + label: "Seed", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.seed, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.inpainting.seed = $event), + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.inpainting.seed = $event), min: -1 }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Steps" }, { + createVNode(unref(NFormItem), { + label: "Steps", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.inpainting.steps, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.inpainting.steps = $event) + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.inpainting.steps = $event) }, null, 8, ["value"]) ]), _: 1 + }), + createVNode(_sfc_main$h, { + type: "inpainting", + target: "defaultSettings" }) ]), _: 1 @@ -2986,7 +2781,7 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({ }; } }); -const _sfc_main$5 = /* @__PURE__ */ defineComponent({ +const _sfc_main$7 = /* @__PURE__ */ defineComponent({ __name: "TextToImageSettings", setup(__props) { const settings = useSettings(); @@ -2995,7 +2790,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Prompt" }, { + createVNode(unref(NFormItem), { + label: "Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.txt2img.prompt, @@ -3004,7 +2802,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Negative Prompt" }, { + createVNode(unref(NFormItem), { + label: "Negative Prompt", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInput), { value: unref(settings).defaultSettings.txt2img.negative_prompt, @@ -3013,7 +2814,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Count" }, { + createVNode(unref(NFormItem), { + label: "Batch Count", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.batch_count, @@ -3022,7 +2826,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Batch Size" }, { + createVNode(unref(NFormItem), { + label: "Batch Size", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.batch_size, @@ -3031,7 +2838,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "CFG Scale" }, { + createVNode(unref(NFormItem), { + label: "CFG Scale", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.cfg_scale, @@ -3041,7 +2851,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Height" }, { + createVNode(unref(NFormItem), { + label: "Height", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.height, @@ -3051,7 +2864,10 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Width" }, { + createVNode(unref(NFormItem), { + label: "Width", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.width, @@ -3061,34 +2877,34 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Sampler" }, { - default: withCtx(() => [ - createVNode(unref(NSelect), { - options: unref(settings).scheduler_options, - value: unref(settings).defaultSettings.txt2img.sampler, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.txt2img.sampler = $event) - }, null, 8, ["options", "value"]) - ]), - _: 1 - }), - createVNode(unref(NFormItem), { label: "Seed" }, { + createVNode(unref(NFormItem), { + label: "Seed", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.seed, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.txt2img.seed = $event), + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.txt2img.seed = $event), min: -1 }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Steps" }, { + createVNode(unref(NFormItem), { + label: "Steps", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { value: unref(settings).defaultSettings.txt2img.steps, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.txt2img.steps = $event) + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.txt2img.steps = $event) }, null, 8, ["value"]) ]), _: 1 + }), + createVNode(_sfc_main$h, { + type: "txt2img", + target: "defaultSettings" }) ]), _: 1 @@ -3099,37 +2915,82 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ }; } }); -const _sfc_main$4 = /* @__PURE__ */ defineComponent({ - __name: "ThemeSettings", +const _sfc_main$6 = /* @__PURE__ */ defineComponent({ + __name: "FrontendSettings", + setup(__props) { + return (_ctx, _cache) => { + return openBlock(), createBlock(unref(NTabs), null, { + default: withCtx(() => [ + createVNode(unref(NTabPane), { name: "Text to Image" }, { + default: withCtx(() => [ + createVNode(_sfc_main$7) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Image to Image" }, { + default: withCtx(() => [ + createVNode(_sfc_main$9) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "ControlNet" }, { + default: withCtx(() => [ + createVNode(_sfc_main$b) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Inpainting" }, { + default: withCtx(() => [ + createVNode(_sfc_main$8) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Image Browser" }, { + default: withCtx(() => [ + createVNode(_sfc_main$a) + ]), + _: 1 + }) + ]), + _: 1 + }); + }; + } +}); +const _sfc_main$5 = /* @__PURE__ */ defineComponent({ + __name: "GeneralSettings", setup(__props) { const settings = useSettings(); - const themeOptions = [ - { label: "Dark", value: "dark" }, - { label: "Light", value: "light" } - ]; watch(settings.defaultSettings.frontend, () => { - settings.data.settings.frontend = settings.defaultSettings.frontend; + settings.data.settings.frontend.on_change_timer = settings.defaultSettings.frontend.on_change_timer; }); return (_ctx, _cache) => { - return openBlock(), createBlock(unref(NCard), null, { + return openBlock(), createBlock(unref(NCard), { title: "Timings" }, { default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NFormItem), { label: "Theme" }, { + createVNode(unref(NFormItem), { + label: "Continuous generation timeout (0 for disabled) [ms]", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(unref(NSelect), { - options: themeOptions, - value: unref(settings).defaultSettings.frontend.theme, - "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.frontend.theme = $event) + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.frontend.on_change_timer, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.frontend.on_change_timer = $event), + min: 0, + step: 50 }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NFormItem), { label: "Enable Theme Editor" }, { + createVNode(unref(NFormItem), { + label: "Enable sending logs to UI", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSwitch), { - value: unref(settings).defaultSettings.frontend.enable_theme_editor, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.frontend.enable_theme_editor = $event) + value: unref(settings).defaultSettings.api.enable_websocket_logging, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.enable_websocket_logging = $event) }, null, 8, ["value"]) ]), _: 1 @@ -3143,8 +3004,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ }; } }); -const _hoisted_1$2 = /* @__PURE__ */ createBaseVNode("h2", null, "NSFW", -1); -const _sfc_main$3 = /* @__PURE__ */ defineComponent({ +const _sfc_main$4 = /* @__PURE__ */ defineComponent({ __name: "NSFWSettings", setup(__props) { const settings = useSettings(); @@ -3159,8 +3019,10 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(unref(NForm), null, { default: withCtx(() => [ - _hoisted_1$2, - createVNode(unref(NFormItem), { label: "NSFW OK threshold (if you don't get the reference, select `I'm too young to die`)" }, { + createVNode(unref(NFormItem), { + label: "NSFW OK threshold (if you don't get the reference, select `I'm too young to die`)", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NSelect), { value: unref(settings).defaultSettings.frontend.nsfw_ok_threshold, @@ -3196,51 +3058,631 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }; } }); +const _hoisted_1$2 = { + key: 0, + class: "flex-container" +}; +const _hoisted_2$1 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Subquadratic chunk size (affects VRAM usage)", -1); +const _hoisted_3$1 = { "flex-direction": "row" }; +const _hoisted_4$1 = { key: 1 }; +const _sfc_main$3 = /* @__PURE__ */ defineComponent({ + __name: "OptimizationSettings", + setup(__props) { + const settings = useSettings(); + const global = useState(); + const theme = inject(themeKey); + const compileColor = computed(() => { + var _a; + if (settings.defaultSettings.api.torch_compile) + return (_a = theme == null ? void 0 : theme.value.Button.common) == null ? void 0 : _a.successColor; + return void 0; + }); + const traceColor = computed(() => { + var _a; + if (settings.defaultSettings.api.trace_model) + return (_a = theme == null ? void 0 : theme.value.Button.common) == null ? void 0 : _a.successColor; + return void 0; + }); + const disableColor = computed(() => { + var _a; + if (settings.defaultSettings.api.torch_compile || settings.defaultSettings.api.trace_model) + return void 0; + return (_a = theme == null ? void 0 : theme.value.Button.common) == null ? void 0 : _a.successColor; + }); + function change_compilation(a) { + settings.defaultSettings.api.torch_compile = a === "compile"; + settings.defaultSettings.api.trace_model = a === "trace"; + } + const availableTorchCompileBackends = computed(() => { + return global.state.capabilities.supported_torch_compile_backends.map( + (value) => { + return { value, label: value }; + } + ); + }); + const availableAttentions = computed(() => { + return [ + ...global.state.capabilities.supports_xformers ? [{ value: "xformers", label: "xFormers" }] : [], + { + value: "sdpa", + label: "SDP Attention" + }, + { + value: "cross-attention", + label: "Cross-Attention" + }, + { + value: "subquadratic", + label: "Sub-quadratic Attention" + }, + { + value: "multihead", + label: "Multihead attention" + } + ]; + }); + return (_ctx, _cache) => { + return openBlock(), createBlock(unref(NForm), null, { + default: withCtx(() => [ + createVNode(unref(NFormItem), { + label: "Autocast", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.autocast, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.api.autocast = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Attention processor", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: availableAttentions.value, + value: unref(settings).defaultSettings.api.attention_processor, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.attention_processor = $event) + }, null, 8, ["options", "value"]) + ]), + _: 1 + }), + unref(settings).defaultSettings.api.attention_processor == "subquadratic" ? (openBlock(), createElementBlock("div", _hoisted_1$2, [ + _hoisted_2$1, + createVNode(unref(NSlider), { + value: unref(settings).defaultSettings.api.subquadratic_size, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).defaultSettings.api.subquadratic_size = $event), + step: 64, + min: 64, + max: 8192, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.subquadratic_size, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).defaultSettings.api.subquadratic_size = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + step: 64, + min: 64, + max: 8192 + }, null, 8, ["value"]) + ])) : createCommentVNode("", true), + createVNode(unref(NFormItem), { + label: "Compilation method", + "label-placement": "left" + }, { + default: withCtx(() => [ + createBaseVNode("div", _hoisted_3$1, [ + createVNode(unref(NButton), { + onClick: _cache[4] || (_cache[4] = ($event) => change_compilation("disabled")), + color: disableColor.value + }, { + default: withCtx(() => [ + createTextVNode("Disabled") + ]), + _: 1 + }, 8, ["color"]), + createVNode(unref(NButton), { + onClick: _cache[5] || (_cache[5] = ($event) => change_compilation("trace")), + color: traceColor.value + }, { + default: withCtx(() => [ + createTextVNode("Trace UNet") + ]), + _: 1 + }, 8, ["color"]), + createVNode(unref(NButton), { + onClick: _cache[6] || (_cache[6] = ($event) => change_compilation("compile")), + color: compileColor.value + }, { + default: withCtx(() => [ + createTextVNode("torch.compile") + ]), + _: 1 + }, 8, ["color"]) + ]) + ]), + _: 1 + }), + unref(settings).defaultSettings.api.torch_compile ? (openBlock(), createElementBlock("div", _hoisted_4$1, [ + createVNode(unref(NFormItem), { + label: "Fullgraph compile", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.torch_compile_fullgraph, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.api.torch_compile_fullgraph = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Dynamic compile", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.torch_compile_dynamic, + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.api.torch_compile_dynamic = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Compilation backend", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: availableTorchCompileBackends.value, + value: unref(settings).defaultSettings.api.torch_compile_backend, + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.api.torch_compile_backend = $event) + }, null, 8, ["options", "value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Compilation mode", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: [ + { value: "default", label: "Default" }, + { value: "reduce-overhead", label: "Reduce Overhead" }, + { value: "max-autotune", label: "Max Autotune" } + ], + value: unref(settings).defaultSettings.api.torch_compile_mode, + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).defaultSettings.api.torch_compile_mode = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }) + ])) : createCommentVNode("", true), + createVNode(unref(NFormItem), { + label: "Attention Slicing", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: [ + { + value: "disabled", + label: "None" + }, + { + value: "auto", + label: "Auto" + } + ], + value: unref(settings).defaultSettings.api.attention_slicing, + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).defaultSettings.api.attention_slicing = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Channels Last", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.channels_last, + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).defaultSettings.api.channels_last = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Reduced Precision (RTX 30xx and newer cards)", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.reduced_precision, + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).defaultSettings.api.reduced_precision = $event), + disabled: !unref(global).state.capabilities.has_tensorfloat + }, null, 8, ["value", "disabled"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "CudNN Benchmark (big VRAM spikes - use on 8GB+ cards only)", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.cudnn_benchmark, + "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).defaultSettings.api.cudnn_benchmark = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Clean Memory", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: [ + { + value: "always", + label: "Always" + }, + { + value: "never", + label: "Never" + }, + { + value: "after_disconnect", + label: "After disconnect" + } + ], + value: unref(settings).defaultSettings.api.clear_memory_policy, + "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(settings).defaultSettings.api.clear_memory_policy = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "VAE Slicing", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.vae_slicing, + "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(settings).defaultSettings.api.vae_slicing = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "VAE Tiling", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.vae_tiling, + "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(settings).defaultSettings.api.vae_tiling = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Offload", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: [ + { + value: "disabled", + label: "Disabled" + }, + { + value: "model", + label: "Offload the whole model to RAM when not used" + }, + { + value: "module", + label: "Offload individual modules to RAM when not used" + } + ], + value: unref(settings).defaultSettings.api.offload, + "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(settings).defaultSettings.api.offload = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }) + ]), + _: 1 + }); + }; + } +}); +const _hoisted_1$1 = { key: 1 }; +const _hoisted_2 = { class: "flex-container" }; +const _hoisted_3 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Hypertile UNet chunk size", -1); +const _hoisted_4 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, 'PyTorch ONLY. Recommended sizes are 1/4th your desired resolution or plain "256."', -1); +const _hoisted_5 = /* @__PURE__ */ createBaseVNode("b", null, "LARGE (1024x1024+)", -1); +const _hoisted_6 = { key: 2 }; const _sfc_main$2 = /* @__PURE__ */ defineComponent({ - __name: "FrontendSettings", + __name: "ReproducibilitySettings", setup(__props) { + const settings = useSettings(); + const global = useState(); + const availableDtypes = computed(() => { + if (settings.defaultSettings.api.device.includes("cpu")) { + return global.state.capabilities.supported_precisions_cpu.map((value) => { + var description = ""; + switch (value) { + case "float32": + description = "32-bit float"; + break; + case "float16": + description = "16-bit float"; + break; + default: + description = "16-bit bfloat"; + } + return { value, label: description }; + }); + } + return global.state.capabilities.supported_precisions_gpu.map((value) => { + var description = ""; + switch (value) { + case "float32": + description = "32-bit float"; + break; + case "float16": + description = "16-bit float"; + break; + default: + description = "16-bit bfloat"; + } + return { value, label: description }; + }); + }); + const availableBackends = computed(() => { + return global.state.capabilities.supported_backends.map((l) => { + return { value: l[1], label: l[0] }; + }); + }); + const availableQuantizations = computed(() => { + return [ + { value: "full", label: "Full precision" }, + ...global.state.capabilities.supports_int8 ? [ + { value: "int8", label: "Quantized (int8)" }, + { value: "int4", label: "Quantized (int4)" } + ] : [] + ]; + }); return (_ctx, _cache) => { - return openBlock(), createBlock(unref(NTabs), null, { + return openBlock(), createBlock(unref(NForm), null, { default: withCtx(() => [ - createVNode(unref(NTabPane), { name: "Text to Image" }, { + createVNode(unref(NFormItem), { + label: "Device", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$5) + createVNode(unref(NSelect), { + options: availableBackends.value, + value: unref(settings).defaultSettings.api.device, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.api.device = $event) + }, null, 8, ["options", "value"]) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "Image to Image" }, { + createVNode(unref(NFormItem), { + label: "Data type", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$7) + createVNode(unref(NSelect), { + options: availableDtypes.value, + value: unref(settings).defaultSettings.api.data_type, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.data_type = $event) + }, null, 8, ["options", "value"]) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "ControlNet" }, { + createVNode(unref(NFormItem), { + label: "Deterministic generation", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$9) + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.deterministic_generation, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).defaultSettings.api.deterministic_generation = $event) + }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "Inpainting" }, { + createVNode(unref(NFormItem), { + label: "SGM Noise multiplier", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$6) + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.sgm_noise_multiplier, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).defaultSettings.api.sgm_noise_multiplier = $event) + }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "Image Browser" }, { + createVNode(unref(NFormItem), { + label: "Quantization in k-samplers", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$8) + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.kdiffusers_quantization, + "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(settings).defaultSettings.api.kdiffusers_quantization = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Generator", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + value: unref(settings).defaultSettings.api.generator, + "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(settings).defaultSettings.api.generator = $event), + options: [ + { value: "device", label: "On-Device" }, + { value: "cpu", label: "CPU" }, + { value: "philox", label: "CPU (device mock)" } + ] + }, null, 8, ["value", "options"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "CLIP skip", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.clip_skip, + "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).defaultSettings.api.clip_skip = $event), + min: 1, + max: 11, + step: 1 + }, null, 8, ["value"]) + ]), + _: 1 + }), + availableQuantizations.value.length != 1 ? (openBlock(), createBlock(unref(NFormItem), { + key: 0, + label: "CLIP quantization", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: availableQuantizations.value, + value: unref(settings).defaultSettings.api.clip_quantization, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).defaultSettings.api.clip_quantization = $event) + }, null, 8, ["options", "value"]) + ]), + _: 1 + })) : createCommentVNode("", true), + createVNode(unref(NFormItem), { + label: "Use HyperTile", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.hypertile, + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).defaultSettings.api.hypertile = $event) + }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "Theme" }, { + unref(settings).defaultSettings.api.hypertile ? (openBlock(), createElementBlock("div", _hoisted_1$1, [ + createBaseVNode("div", _hoisted_2, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_3 + ]), + default: withCtx(() => [ + _hoisted_4, + createTextVNode(" Internally splits up the generated image into a grid of this size and does work on them one by one. In practice, this can make generation up to 4x faster on "), + _hoisted_5, + createTextVNode(" images. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).defaultSettings.api.hypertile_unet_chunk, + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).defaultSettings.api.hypertile_unet_chunk = $event), + min: 128, + max: 1024, + step: 8, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.hypertile_unet_chunk, + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).defaultSettings.api.hypertile_unet_chunk = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + min: 128, + max: 1024, + step: 1 + }, null, 8, ["value"]) + ]) + ])) : createCommentVNode("", true), + createVNode(unref(NFormItem), { + label: "Use TomeSD", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$4) + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.use_tomesd, + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).defaultSettings.api.use_tomesd = $event) + }, null, 8, ["value"]) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "NSFW" }, { + unref(settings).defaultSettings.api.use_tomesd ? (openBlock(), createElementBlock("div", _hoisted_6, [ + createVNode(unref(NFormItem), { + label: "TomeSD Ratio", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.tomesd_ratio, + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).defaultSettings.api.tomesd_ratio = $event), + min: 0.1, + max: 1 + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "TomeSD Downsample layers", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + options: [ + { + value: 1, + label: "1" + }, + { + value: 2, + label: "2" + }, + { + value: 4, + label: "4" + }, + { + value: 8, + label: "8" + } + ], + value: unref(settings).defaultSettings.api.tomesd_downsample_layers, + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(settings).defaultSettings.api.tomesd_downsample_layers = $event) + }, null, 8, ["value"]) + ]), + _: 1 + }) + ])) : createCommentVNode("", true), + createVNode(unref(NFormItem), { + label: "Huggingface-style prompting", + "label-placement": "left" + }, { default: withCtx(() => [ - createVNode(_sfc_main$3) + createVNode(unref(NSwitch), { + value: unref(settings).defaultSettings.api.huggingface_style_parsing, + "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).defaultSettings.api.huggingface_style_parsing = $event) + }, null, 8, ["value"]) ]), _: 1 }) @@ -3250,25 +3692,67 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }; } }); -const _hoisted_1$1 = /* @__PURE__ */ createBaseVNode("h2", null, "Timers", -1); const _sfc_main$1 = /* @__PURE__ */ defineComponent({ - __name: "GeneralSettings", + __name: "UISettings", setup(__props) { const settings = useSettings(); - watch(settings.defaultSettings.frontend, () => { - settings.data.settings.frontend.on_change_timer = settings.defaultSettings.frontend.on_change_timer; - }); return (_ctx, _cache) => { return openBlock(), createBlock(unref(NForm), null, { default: withCtx(() => [ - _hoisted_1$1, - createVNode(unref(NFormItem), { label: "Continuous generation timeout (0 for disabled) [ms]" }, { + createVNode(unref(NFormItem), { + label: "Image Preview Interval (seconds)", + "label-placement": "left" + }, { default: withCtx(() => [ createVNode(unref(NInputNumber), { - value: unref(settings).defaultSettings.frontend.on_change_timer, - "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.frontend.on_change_timer = $event), - min: 0, - step: 50 + value: unref(settings).defaultSettings.api.live_preview_delay, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).defaultSettings.api.live_preview_delay = $event), + step: 0.1 + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "Image Preview Method", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NSelect), { + value: unref(settings).defaultSettings.api.live_preview_method, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).defaultSettings.api.live_preview_method = $event), + options: [ + { value: "disabled", label: "Disabled" }, + { value: "approximation", label: "Quick approximation (Default)" }, + { value: "taesd", label: "Tiny VAE" } + ] + }, null, 8, ["value", "options"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "WebSocket Performance Monitor Interval", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.websocket_perf_interval, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).defaultSettings.api.websocket_perf_interval = $event), + min: 0.1, + step: 0.1 + }, null, 8, ["value"]) + ]), + _: 1 + }), + createVNode(unref(NFormItem), { + label: "WebSocket Sync Interval", + "label-placement": "left" + }, { + default: withCtx(() => [ + createVNode(unref(NInputNumber), { + value: unref(settings).defaultSettings.api.websocket_sync_interval, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).defaultSettings.api.websocket_sync_interval = $event), + min: 1e-3, + step: 0.01 }, null, 8, ["value"]) ]), _: 1 @@ -3353,33 +3837,69 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }, 8, ["loading"]) ]), default: withCtx(() => [ - createVNode(unref(NTabPane), { name: "Frontend" }, { + createVNode(unref(NTabPane), { name: "Autoload" }, { + default: withCtx(() => [ + createVNode(_sfc_main$g) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Files & Saving" }, { + default: withCtx(() => [ + createVNode(_sfc_main$c) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Optimizations" }, { + default: withCtx(() => [ + createVNode(_sfc_main$3) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Reproducibility & Generation" }, { default: withCtx(() => [ createVNode(_sfc_main$2) ]), _: 1 }), - createVNode(unref(NTabPane), { name: "API" }, { + createVNode(unref(NTabPane), { name: "Live preview & UI" }, { default: withCtx(() => [ - createVNode(_sfc_main$d) + createVNode(_sfc_main$1) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Defaults" }, { + default: withCtx(() => [ + createVNode(_sfc_main$6) ]), _: 1 }), createVNode(unref(NTabPane), { name: "Bot" }, { default: withCtx(() => [ - createVNode(_sfc_main$c) + createVNode(_sfc_main$f) ]), _: 1 }), createVNode(unref(NTabPane), { name: "General" }, { default: withCtx(() => [ - createVNode(_sfc_main$1) + createVNode(_sfc_main$5) ]), _: 1 }), createVNode(unref(NTabPane), { name: "Extra" }, { default: withCtx(() => [ - createVNode(_sfc_main$a) + createVNode(_sfc_main$d) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "Theme" }, { + default: withCtx(() => [ + createVNode(_sfc_main$e) + ]), + _: 1 + }), + createVNode(unref(NTabPane), { name: "NSFW" }, { + default: withCtx(() => [ + createVNode(_sfc_main$4) ]), _: 1 }) diff --git a/frontend/dist/assets/Slider.js b/frontend/dist/assets/Slider.js deleted file mode 100644 index 4b5fdc38c..000000000 --- a/frontend/dist/assets/Slider.js +++ /dev/null @@ -1,727 +0,0 @@ -import { E as ref, bl as onBeforeUpdate, X as c, Y as cB, $ as cM, Z as cE, aW as fadeInScaleUpTransition, a1 as insideModal, a2 as insidePopover, d as defineComponent, Q as useConfig, a5 as useTheme, R as useFormItem, c as computed, U as toRef, S as useMergedState, K as watch, aH as onBeforeUnmount, a9 as useThemeClass, bG as isMounted, av as useAdjustedTo, D as h, bT as VBinder, bU as VTarget, at as resolveSlot, bV as VFollower, aX as Transition, bW as sliderLight, ac as on, aI as off, ah as nextTick, W as call } from "./index.js"; -function isTouchEvent(e) { - return window.TouchEvent && e instanceof window.TouchEvent; -} -function useRefs() { - const refs = ref(/* @__PURE__ */ new Map()); - const setRefs = (index) => (el) => { - refs.value.set(index, el); - }; - onBeforeUpdate(() => { - refs.value.clear(); - }); - return [refs, setRefs]; -} -const style = c([cB("slider", ` - display: block; - padding: calc((var(--n-handle-size) - var(--n-rail-height)) / 2) 0; - position: relative; - z-index: 0; - width: 100%; - cursor: pointer; - user-select: none; - -webkit-user-select: none; - `, [cM("reverse", [cB("slider-handles", [cB("slider-handle-wrapper", ` - transform: translate(50%, -50%); - `)]), cB("slider-dots", [cB("slider-dot", ` - transform: translateX(50%, -50%); - `)]), cM("vertical", [cB("slider-handles", [cB("slider-handle-wrapper", ` - transform: translate(-50%, -50%); - `)]), cB("slider-marks", [cB("slider-mark", ` - transform: translateY(calc(-50% + var(--n-dot-height) / 2)); - `)]), cB("slider-dots", [cB("slider-dot", ` - transform: translateX(-50%) translateY(0); - `)])])]), cM("vertical", ` - padding: 0 calc((var(--n-handle-size) - var(--n-rail-height)) / 2); - width: var(--n-rail-width-vertical); - height: 100%; - `, [cB("slider-handles", ` - top: calc(var(--n-handle-size) / 2); - right: 0; - bottom: calc(var(--n-handle-size) / 2); - left: 0; - `, [cB("slider-handle-wrapper", ` - top: unset; - left: 50%; - transform: translate(-50%, 50%); - `)]), cB("slider-rail", ` - height: 100%; - `, [cE("fill", ` - top: unset; - right: 0; - bottom: unset; - left: 0; - `)]), cM("with-mark", ` - width: var(--n-rail-width-vertical); - margin: 0 32px 0 8px; - `), cB("slider-marks", ` - top: calc(var(--n-handle-size) / 2); - right: unset; - bottom: calc(var(--n-handle-size) / 2); - left: 22px; - font-size: var(--n-mark-font-size); - `, [cB("slider-mark", ` - transform: translateY(50%); - white-space: nowrap; - `)]), cB("slider-dots", ` - top: calc(var(--n-handle-size) / 2); - right: unset; - bottom: calc(var(--n-handle-size) / 2); - left: 50%; - `, [cB("slider-dot", ` - transform: translateX(-50%) translateY(50%); - `)])]), cM("disabled", ` - cursor: not-allowed; - opacity: var(--n-opacity-disabled); - `, [cB("slider-handle", ` - cursor: not-allowed; - `)]), cM("with-mark", ` - width: 100%; - margin: 8px 0 32px 0; - `), c("&:hover", [cB("slider-rail", { - backgroundColor: "var(--n-rail-color-hover)" -}, [cE("fill", { - backgroundColor: "var(--n-fill-color-hover)" -})]), cB("slider-handle", { - boxShadow: "var(--n-handle-box-shadow-hover)" -})]), cM("active", [cB("slider-rail", { - backgroundColor: "var(--n-rail-color-hover)" -}, [cE("fill", { - backgroundColor: "var(--n-fill-color-hover)" -})]), cB("slider-handle", { - boxShadow: "var(--n-handle-box-shadow-hover)" -})]), cB("slider-marks", ` - position: absolute; - top: 18px; - left: calc(var(--n-handle-size) / 2); - right: calc(var(--n-handle-size) / 2); - `, [cB("slider-mark", ` - position: absolute; - transform: translateX(-50%); - white-space: nowrap; - `)]), cB("slider-rail", ` - width: 100%; - position: relative; - height: var(--n-rail-height); - background-color: var(--n-rail-color); - transition: background-color .3s var(--n-bezier); - border-radius: calc(var(--n-rail-height) / 2); - `, [cE("fill", ` - position: absolute; - top: 0; - bottom: 0; - border-radius: calc(var(--n-rail-height) / 2); - transition: background-color .3s var(--n-bezier); - background-color: var(--n-fill-color); - `)]), cB("slider-handles", ` - position: absolute; - top: 0; - right: calc(var(--n-handle-size) / 2); - bottom: 0; - left: calc(var(--n-handle-size) / 2); - `, [cB("slider-handle-wrapper", ` - outline: none; - position: absolute; - top: 50%; - transform: translate(-50%, -50%); - cursor: pointer; - display: flex; - `, [cB("slider-handle", ` - height: var(--n-handle-size); - width: var(--n-handle-size); - border-radius: 50%; - overflow: hidden; - transition: box-shadow .2s var(--n-bezier), background-color .3s var(--n-bezier); - background-color: var(--n-handle-color); - box-shadow: var(--n-handle-box-shadow); - `, [c("&:hover", ` - box-shadow: var(--n-handle-box-shadow-hover); - `)]), c("&:focus", [cB("slider-handle", ` - box-shadow: var(--n-handle-box-shadow-focus); - `, [c("&:hover", ` - box-shadow: var(--n-handle-box-shadow-active); - `)])])])]), cB("slider-dots", ` - position: absolute; - top: 50%; - left: calc(var(--n-handle-size) / 2); - right: calc(var(--n-handle-size) / 2); - `, [cM("transition-disabled", [cB("slider-dot", "transition: none;")]), cB("slider-dot", ` - transition: - border-color .3s var(--n-bezier), - box-shadow .3s var(--n-bezier), - background-color .3s var(--n-bezier); - position: absolute; - transform: translate(-50%, -50%); - height: var(--n-dot-height); - width: var(--n-dot-width); - border-radius: var(--n-dot-border-radius); - overflow: hidden; - box-sizing: border-box; - border: var(--n-dot-border); - background-color: var(--n-dot-color); - `, [cM("active", "border: var(--n-dot-border-active);")])])]), cB("slider-handle-indicator", ` - font-size: var(--n-font-size); - padding: 6px 10px; - border-radius: var(--n-indicator-border-radius); - color: var(--n-indicator-text-color); - background-color: var(--n-indicator-color); - box-shadow: var(--n-indicator-box-shadow); - `, [fadeInScaleUpTransition()]), cB("slider-handle-indicator", ` - font-size: var(--n-font-size); - padding: 6px 10px; - border-radius: var(--n-indicator-border-radius); - color: var(--n-indicator-text-color); - background-color: var(--n-indicator-color); - box-shadow: var(--n-indicator-box-shadow); - `, [cM("top", ` - margin-bottom: 12px; - `), cM("right", ` - margin-left: 12px; - `), cM("bottom", ` - margin-top: 12px; - `), cM("left", ` - margin-right: 12px; - `), fadeInScaleUpTransition()]), insideModal(cB("slider", [cB("slider-dot", "background-color: var(--n-dot-color-modal);")])), insidePopover(cB("slider", [cB("slider-dot", "background-color: var(--n-dot-color-popover);")]))]); -const eventButtonLeft = 0; -const sliderProps = Object.assign(Object.assign({}, useTheme.props), { to: useAdjustedTo.propTo, defaultValue: { - type: [Number, Array], - default: 0 -}, marks: Object, disabled: { - type: Boolean, - default: void 0 -}, formatTooltip: Function, keyboard: { - type: Boolean, - default: true -}, min: { - type: Number, - default: 0 -}, max: { - type: Number, - default: 100 -}, step: { - type: [Number, String], - default: 1 -}, range: Boolean, value: [Number, Array], placement: String, showTooltip: { - type: Boolean, - default: void 0 -}, tooltip: { - type: Boolean, - default: true -}, vertical: Boolean, reverse: Boolean, "onUpdate:value": [Function, Array], onUpdateValue: [Function, Array] }); -const NSlider = defineComponent({ - name: "Slider", - props: sliderProps, - setup(props) { - const { mergedClsPrefixRef, namespaceRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Slider", "-slider", style, sliderLight, props, mergedClsPrefixRef); - const handleRailRef = ref(null); - const [handleRefs, setHandleRefs] = useRefs(); - const [followerRefs, setFollowerRefs] = useRefs(); - const followerEnabledIndexSetRef = ref(/* @__PURE__ */ new Set()); - const formItem = useFormItem(props); - const { mergedDisabledRef } = formItem; - const precisionRef = computed(() => { - const { step } = props; - if (Number(step) <= 0 || step === "mark") - return 0; - const stepString = step.toString(); - let precision = 0; - if (stepString.includes(".")) { - precision = stepString.length - stepString.indexOf(".") - 1; - } - return precision; - }); - const uncontrolledValueRef = ref(props.defaultValue); - const controlledValueRef = toRef(props, "value"); - const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef); - const arrifiedValueRef = computed(() => { - const { value: mergedValue } = mergedValueRef; - return (props.range ? mergedValue : [mergedValue]).map(clampValue); - }); - const handleCountExceeds2Ref = computed(() => arrifiedValueRef.value.length > 2); - const mergedPlacementRef = computed(() => { - return props.placement === void 0 ? props.vertical ? "right" : "top" : props.placement; - }); - const markValuesRef = computed(() => { - const { marks } = props; - return marks ? Object.keys(marks).map(parseFloat) : null; - }); - const activeIndexRef = ref(-1); - const previousIndexRef = ref(-1); - const hoverIndexRef = ref(-1); - const draggingRef = ref(false); - const dotTransitionDisabledRef = ref(false); - const styleDirectionRef = computed(() => { - const { vertical, reverse } = props; - const left = reverse ? "right" : "left"; - const bottom = reverse ? "top" : "bottom"; - return vertical ? bottom : left; - }); - const fillStyleRef = computed(() => { - if (handleCountExceeds2Ref.value) - return; - const values = arrifiedValueRef.value; - const start = valueToPercentage(props.range ? Math.min(...values) : props.min); - const end = valueToPercentage(props.range ? Math.max(...values) : values[0]); - const { value: styleDirection } = styleDirectionRef; - return props.vertical ? { - [styleDirection]: `${start}%`, - height: `${end - start}%` - } : { - [styleDirection]: `${start}%`, - width: `${end - start}%` - }; - }); - const markInfosRef = computed(() => { - const mergedMarks = []; - const { marks } = props; - if (marks) { - const orderValues = arrifiedValueRef.value.slice(); - orderValues.sort((a, b) => a - b); - const { value: styleDirection } = styleDirectionRef; - const { value: handleCountExceeds2 } = handleCountExceeds2Ref; - const { range } = props; - const isActive = handleCountExceeds2 ? () => false : (num) => range ? num >= orderValues[0] && num <= orderValues[orderValues.length - 1] : num <= orderValues[0]; - for (const key of Object.keys(marks)) { - const num = Number(key); - mergedMarks.push({ - active: isActive(num), - label: marks[key], - style: { - [styleDirection]: `${valueToPercentage(num)}%` - } - }); - } - } - return mergedMarks; - }); - function getHandleStyle(value, index) { - const percentage = valueToPercentage(value); - const { value: styleDirection } = styleDirectionRef; - return { - [styleDirection]: `${percentage}%`, - zIndex: index === activeIndexRef.value ? 1 : 0 - }; - } - function isShowTooltip(index) { - return props.showTooltip || hoverIndexRef.value === index || activeIndexRef.value === index && draggingRef.value; - } - function shouldKeepTooltipTransition(index) { - if (!draggingRef.value) - return true; - return !(activeIndexRef.value === index && previousIndexRef.value === index); - } - function focusActiveHandle(index) { - var _a; - if (~index) { - activeIndexRef.value = index; - (_a = handleRefs.value.get(index)) === null || _a === void 0 ? void 0 : _a.focus(); - } - } - function syncPosition() { - followerRefs.value.forEach((inst, index) => { - if (isShowTooltip(index)) - inst.syncPosition(); - }); - } - function doUpdateValue(value) { - const { "onUpdate:value": _onUpdateValue, onUpdateValue } = props; - const { nTriggerFormInput, nTriggerFormChange } = formItem; - if (onUpdateValue) - call(onUpdateValue, value); - if (_onUpdateValue) - call(_onUpdateValue, value); - uncontrolledValueRef.value = value; - nTriggerFormInput(); - nTriggerFormChange(); - } - function dispatchValueUpdate(value) { - const { range } = props; - if (range) { - if (Array.isArray(value)) { - const { value: oldValues } = arrifiedValueRef; - if (value.join() !== oldValues.join()) { - doUpdateValue(value); - } - } - } else if (!Array.isArray(value)) { - const oldValue = arrifiedValueRef.value[0]; - if (oldValue !== value) { - doUpdateValue(value); - } - } - } - function doDispatchValue(value, index) { - if (props.range) { - const values = arrifiedValueRef.value.slice(); - values.splice(index, 1, value); - dispatchValueUpdate(values); - } else { - dispatchValueUpdate(value); - } - } - function sanitizeValue(value, currentValue, stepBuffer) { - const stepping = stepBuffer !== void 0; - if (!stepBuffer) { - stepBuffer = value - currentValue > 0 ? 1 : -1; - } - const markValues = markValuesRef.value || []; - const { step } = props; - if (step === "mark") { - const closestMark2 = getClosestMark(value, markValues.concat(currentValue), stepping ? stepBuffer : void 0); - return closestMark2 ? closestMark2.value : currentValue; - } - if (step <= 0) - return currentValue; - const { value: precision } = precisionRef; - let closestMark; - if (stepping) { - const currentStep = Number((currentValue / step).toFixed(precision)); - const actualStep = Math.floor(currentStep); - const leftStep = currentStep > actualStep ? actualStep : actualStep - 1; - const rightStep = currentStep < actualStep ? actualStep : actualStep + 1; - closestMark = getClosestMark(currentValue, [ - Number((leftStep * step).toFixed(precision)), - Number((rightStep * step).toFixed(precision)), - ...markValues - ], stepBuffer); - } else { - const roundValue = getRoundValue(value); - closestMark = getClosestMark(value, [...markValues, roundValue]); - } - return closestMark ? clampValue(closestMark.value) : currentValue; - } - function clampValue(value) { - return Math.min(props.max, Math.max(props.min, value)); - } - function valueToPercentage(value) { - const { max, min } = props; - return (value - min) / (max - min) * 100; - } - function percentageToValue(percentage) { - const { max, min } = props; - return min + (max - min) * percentage; - } - function getRoundValue(value) { - const { step, min } = props; - if (Number(step) <= 0 || step === "mark") - return value; - const newValue = Math.round((value - min) / step) * step + min; - return Number(newValue.toFixed(precisionRef.value)); - } - function getClosestMark(currentValue, markValues = markValuesRef.value, buffer) { - if (!(markValues === null || markValues === void 0 ? void 0 : markValues.length)) - return null; - let closestMark = null; - let index = -1; - while (++index < markValues.length) { - const diff = markValues[index] - currentValue; - const distance = Math.abs(diff); - if ( - // find marks in the same direction - (buffer === void 0 || diff * buffer > 0) && (closestMark === null || distance < closestMark.distance) - ) { - closestMark = { - index, - distance, - value: markValues[index] - }; - } - } - return closestMark; - } - function getPointValue(event) { - const railEl = handleRailRef.value; - if (!railEl) - return; - const touchEvent = isTouchEvent(event) ? event.touches[0] : event; - const railRect = railEl.getBoundingClientRect(); - let percentage; - if (props.vertical) { - percentage = (railRect.bottom - touchEvent.clientY) / railRect.height; - } else { - percentage = (touchEvent.clientX - railRect.left) / railRect.width; - } - if (props.reverse) { - percentage = 1 - percentage; - } - return percentageToValue(percentage); - } - function handleRailKeyDown(e) { - if (mergedDisabledRef.value || !props.keyboard) - return; - const { vertical, reverse } = props; - switch (e.key) { - case "ArrowUp": - e.preventDefault(); - handleStepValue(vertical && reverse ? -1 : 1); - break; - case "ArrowRight": - e.preventDefault(); - handleStepValue(!vertical && reverse ? -1 : 1); - break; - case "ArrowDown": - e.preventDefault(); - handleStepValue(vertical && reverse ? 1 : -1); - break; - case "ArrowLeft": - e.preventDefault(); - handleStepValue(!vertical && reverse ? 1 : -1); - break; - } - } - function handleStepValue(ratio) { - const activeIndex = activeIndexRef.value; - if (activeIndex === -1) - return; - const { step } = props; - const currentValue = arrifiedValueRef.value[activeIndex]; - const nextValue = Number(step) <= 0 || step === "mark" ? currentValue : currentValue + step * ratio; - doDispatchValue( - // Avoid the number of value does not change when `step` is null - sanitizeValue(nextValue, currentValue, ratio > 0 ? 1 : -1), - activeIndex - ); - } - function handleRailMouseDown(event) { - var _a, _b; - if (mergedDisabledRef.value) - return; - if (!isTouchEvent(event) && event.button !== eventButtonLeft) { - return; - } - const pointValue = getPointValue(event); - if (pointValue === void 0) - return; - const values = arrifiedValueRef.value.slice(); - const activeIndex = props.range ? (_b = (_a = getClosestMark(pointValue, values)) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : -1 : 0; - if (activeIndex !== -1) { - event.preventDefault(); - focusActiveHandle(activeIndex); - startDragging(); - doDispatchValue(sanitizeValue(pointValue, arrifiedValueRef.value[activeIndex]), activeIndex); - } - } - function startDragging() { - if (!draggingRef.value) { - draggingRef.value = true; - on("touchend", document, handleMouseUp); - on("mouseup", document, handleMouseUp); - on("touchmove", document, handleMouseMove); - on("mousemove", document, handleMouseMove); - } - } - function stopDragging() { - if (draggingRef.value) { - draggingRef.value = false; - off("touchend", document, handleMouseUp); - off("mouseup", document, handleMouseUp); - off("touchmove", document, handleMouseMove); - off("mousemove", document, handleMouseMove); - } - } - function handleMouseMove(event) { - const { value: activeIndex } = activeIndexRef; - if (!draggingRef.value || activeIndex === -1) { - stopDragging(); - return; - } - const pointValue = getPointValue(event); - doDispatchValue(sanitizeValue(pointValue, arrifiedValueRef.value[activeIndex]), activeIndex); - } - function handleMouseUp() { - stopDragging(); - } - function handleHandleFocus(index) { - activeIndexRef.value = index; - if (!mergedDisabledRef.value) { - hoverIndexRef.value = index; - } - } - function handleHandleBlur(index) { - if (activeIndexRef.value === index) { - activeIndexRef.value = -1; - stopDragging(); - } - if (hoverIndexRef.value === index) { - hoverIndexRef.value = -1; - } - } - function handleHandleMouseEnter(index) { - hoverIndexRef.value = index; - } - function handleHandleMouseLeave(index) { - if (hoverIndexRef.value === index) { - hoverIndexRef.value = -1; - } - } - watch(activeIndexRef, (_, previous) => void nextTick(() => previousIndexRef.value = previous)); - watch(mergedValueRef, () => { - if (props.marks) { - if (dotTransitionDisabledRef.value) - return; - dotTransitionDisabledRef.value = true; - void nextTick(() => { - dotTransitionDisabledRef.value = false; - }); - } - void nextTick(syncPosition); - }); - onBeforeUnmount(() => { - stopDragging(); - }); - const cssVarsRef = computed(() => { - const { self: { markFontSize, railColor, railColorHover, fillColor, fillColorHover, handleColor, opacityDisabled, dotColor, dotColorModal, handleBoxShadow, handleBoxShadowHover, handleBoxShadowActive, handleBoxShadowFocus, dotBorder, dotBoxShadow, railHeight, railWidthVertical, handleSize, dotHeight, dotWidth, dotBorderRadius, fontSize, dotBorderActive, dotColorPopover }, common: { cubicBezierEaseInOut } } = themeRef.value; - return { - "--n-bezier": cubicBezierEaseInOut, - "--n-dot-border": dotBorder, - "--n-dot-border-active": dotBorderActive, - "--n-dot-border-radius": dotBorderRadius, - "--n-dot-box-shadow": dotBoxShadow, - "--n-dot-color": dotColor, - "--n-dot-color-modal": dotColorModal, - "--n-dot-color-popover": dotColorPopover, - "--n-dot-height": dotHeight, - "--n-dot-width": dotWidth, - "--n-fill-color": fillColor, - "--n-fill-color-hover": fillColorHover, - "--n-font-size": fontSize, - "--n-handle-box-shadow": handleBoxShadow, - "--n-handle-box-shadow-active": handleBoxShadowActive, - "--n-handle-box-shadow-focus": handleBoxShadowFocus, - "--n-handle-box-shadow-hover": handleBoxShadowHover, - "--n-handle-color": handleColor, - "--n-handle-size": handleSize, - "--n-opacity-disabled": opacityDisabled, - "--n-rail-color": railColor, - "--n-rail-color-hover": railColorHover, - "--n-rail-height": railHeight, - "--n-rail-width-vertical": railWidthVertical, - "--n-mark-font-size": markFontSize - }; - }); - const themeClassHandle = inlineThemeDisabled ? useThemeClass("slider", void 0, cssVarsRef, props) : void 0; - const indicatorCssVarsRef = computed(() => { - const { self: { fontSize, indicatorColor, indicatorBoxShadow, indicatorTextColor, indicatorBorderRadius } } = themeRef.value; - return { - "--n-font-size": fontSize, - "--n-indicator-border-radius": indicatorBorderRadius, - "--n-indicator-box-shadow": indicatorBoxShadow, - "--n-indicator-color": indicatorColor, - "--n-indicator-text-color": indicatorTextColor - }; - }); - const indicatorThemeClassHandle = inlineThemeDisabled ? useThemeClass("slider-indicator", void 0, indicatorCssVarsRef, props) : void 0; - return { - mergedClsPrefix: mergedClsPrefixRef, - namespace: namespaceRef, - uncontrolledValue: uncontrolledValueRef, - mergedValue: mergedValueRef, - mergedDisabled: mergedDisabledRef, - mergedPlacement: mergedPlacementRef, - isMounted: isMounted(), - adjustedTo: useAdjustedTo(props), - dotTransitionDisabled: dotTransitionDisabledRef, - markInfos: markInfosRef, - isShowTooltip, - shouldKeepTooltipTransition, - handleRailRef, - setHandleRefs, - setFollowerRefs, - fillStyle: fillStyleRef, - getHandleStyle, - activeIndex: activeIndexRef, - arrifiedValues: arrifiedValueRef, - followerEnabledIndexSet: followerEnabledIndexSetRef, - handleRailMouseDown, - handleHandleFocus, - handleHandleBlur, - handleHandleMouseEnter, - handleHandleMouseLeave, - handleRailKeyDown, - indicatorCssVars: inlineThemeDisabled ? void 0 : indicatorCssVarsRef, - indicatorThemeClass: indicatorThemeClassHandle === null || indicatorThemeClassHandle === void 0 ? void 0 : indicatorThemeClassHandle.themeClass, - indicatorOnRender: indicatorThemeClassHandle === null || indicatorThemeClassHandle === void 0 ? void 0 : indicatorThemeClassHandle.onRender, - cssVars: inlineThemeDisabled ? void 0 : cssVarsRef, - themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, - onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender - }; - }, - render() { - var _a; - const { mergedClsPrefix, themeClass, formatTooltip } = this; - (_a = this.onRender) === null || _a === void 0 ? void 0 : _a.call(this); - return h( - "div", - { class: [ - `${mergedClsPrefix}-slider`, - themeClass, - { - [`${mergedClsPrefix}-slider--disabled`]: this.mergedDisabled, - [`${mergedClsPrefix}-slider--active`]: this.activeIndex !== -1, - [`${mergedClsPrefix}-slider--with-mark`]: this.marks, - [`${mergedClsPrefix}-slider--vertical`]: this.vertical, - [`${mergedClsPrefix}-slider--reverse`]: this.reverse - } - ], style: this.cssVars, onKeydown: this.handleRailKeyDown, onMousedown: this.handleRailMouseDown, onTouchstart: this.handleRailMouseDown }, - h( - "div", - { class: `${mergedClsPrefix}-slider-rail` }, - h("div", { class: `${mergedClsPrefix}-slider-rail__fill`, style: this.fillStyle }), - this.marks ? h("div", { class: [ - `${mergedClsPrefix}-slider-dots`, - this.dotTransitionDisabled && `${mergedClsPrefix}-slider-dots--transition-disabled` - ] }, this.markInfos.map((mark) => h("div", { key: mark.label, class: [ - `${mergedClsPrefix}-slider-dot`, - { - [`${mergedClsPrefix}-slider-dot--active`]: mark.active - } - ], style: mark.style }))) : null, - h("div", { ref: "handleRailRef", class: `${mergedClsPrefix}-slider-handles` }, this.arrifiedValues.map((value, index) => { - const showTooltip = this.isShowTooltip(index); - return h(VBinder, null, { - default: () => [ - h(VTarget, null, { - default: () => h("div", { ref: this.setHandleRefs(index), class: `${mergedClsPrefix}-slider-handle-wrapper`, tabindex: this.mergedDisabled ? -1 : 0, style: this.getHandleStyle(value, index), onFocus: () => { - this.handleHandleFocus(index); - }, onBlur: () => { - this.handleHandleBlur(index); - }, onMouseenter: () => { - this.handleHandleMouseEnter(index); - }, onMouseleave: () => { - this.handleHandleMouseLeave(index); - } }, resolveSlot(this.$slots.thumb, () => [ - h("div", { class: `${mergedClsPrefix}-slider-handle` }) - ])) - }), - this.tooltip && h(VFollower, { ref: this.setFollowerRefs(index), show: showTooltip, to: this.adjustedTo, enabled: this.showTooltip && !this.range || this.followerEnabledIndexSet.has(index), teleportDisabled: this.adjustedTo === useAdjustedTo.tdkey, placement: this.mergedPlacement, containerClass: this.namespace }, { - default: () => h(Transition, { name: "fade-in-scale-up-transition", appear: this.isMounted, css: this.shouldKeepTooltipTransition(index), onEnter: () => { - this.followerEnabledIndexSet.add(index); - }, onAfterLeave: () => { - this.followerEnabledIndexSet.delete(index); - } }, { - default: () => { - var _a2; - if (showTooltip) { - (_a2 = this.indicatorOnRender) === null || _a2 === void 0 ? void 0 : _a2.call(this); - return h("div", { class: [ - `${mergedClsPrefix}-slider-handle-indicator`, - this.indicatorThemeClass, - `${mergedClsPrefix}-slider-handle-indicator--${this.mergedPlacement}` - ], style: this.indicatorCssVars }, typeof formatTooltip === "function" ? formatTooltip(value) : value); - } - return null; - } - }) - }) - ] - }); - })), - this.marks ? h("div", { class: `${mergedClsPrefix}-slider-marks` }, this.markInfos.map((mark) => h("div", { key: mark.label, class: `${mergedClsPrefix}-slider-mark`, style: mark.style }, mark.label))) : null - ) - ); - } -}); -export { - NSlider as N -}; diff --git a/frontend/dist/assets/Switch.js b/frontend/dist/assets/Switch.js index 64acc7c16..b551fdd9d 100644 --- a/frontend/dist/assets/Switch.js +++ b/frontend/dist/assets/Switch.js @@ -1,4 +1,727 @@ -import { Y as cB, Z as cE, a0 as iconSwitchTransition, X as c, $ as cM, aq as cNotM, d as defineComponent, Q as useConfig, a5 as useTheme, R as useFormItem, E as ref, U as toRef, S as useMergedState, c as computed, a9 as useThemeClass, bR as isSlotEmpty, D as h, aB as resolveWrappedSlot, bS as switchLight, a8 as createKey, aL as pxfy, aE as depx, ab as NIconSwitchTransition, aN as NBaseLoading, W as call } from "./index.js"; +import { D as ref, bn as onBeforeUpdate, aa as c, Q as cB, ab as cM, at as cE, aS as fadeInScaleUpTransition, aU as insideModal, aV as insidePopover, d as defineComponent, S as useConfig, T as useTheme, ar as useFormItem, c as computed, V as toRef, ae as useMergedState, J as watch, aB as onBeforeUnmount, W as useThemeClass, bO as isMounted, aj as useAdjustedTo, C as h, bZ as VBinder, b_ as VTarget, ah as resolveSlot, b$ as VFollower, aX as Transition, c0 as sliderLight, aD as on, aC as off, a0 as nextTick, $ as call, aT as iconSwitchTransition, ac as cNotM, c1 as isSlotEmpty, av as resolveWrappedSlot, c2 as switchLight, al as createKey, aG as pxfy, ay as depx, aI as NIconSwitchTransition, aJ as NBaseLoading } from "./index.js"; +function isTouchEvent(e) { + return window.TouchEvent && e instanceof window.TouchEvent; +} +function useRefs() { + const refs = ref(/* @__PURE__ */ new Map()); + const setRefs = (index) => (el) => { + refs.value.set(index, el); + }; + onBeforeUpdate(() => { + refs.value.clear(); + }); + return [refs, setRefs]; +} +const style$1 = c([cB("slider", ` + display: block; + padding: calc((var(--n-handle-size) - var(--n-rail-height)) / 2) 0; + position: relative; + z-index: 0; + width: 100%; + cursor: pointer; + user-select: none; + -webkit-user-select: none; + `, [cM("reverse", [cB("slider-handles", [cB("slider-handle-wrapper", ` + transform: translate(50%, -50%); + `)]), cB("slider-dots", [cB("slider-dot", ` + transform: translateX(50%, -50%); + `)]), cM("vertical", [cB("slider-handles", [cB("slider-handle-wrapper", ` + transform: translate(-50%, -50%); + `)]), cB("slider-marks", [cB("slider-mark", ` + transform: translateY(calc(-50% + var(--n-dot-height) / 2)); + `)]), cB("slider-dots", [cB("slider-dot", ` + transform: translateX(-50%) translateY(0); + `)])])]), cM("vertical", ` + padding: 0 calc((var(--n-handle-size) - var(--n-rail-height)) / 2); + width: var(--n-rail-width-vertical); + height: 100%; + `, [cB("slider-handles", ` + top: calc(var(--n-handle-size) / 2); + right: 0; + bottom: calc(var(--n-handle-size) / 2); + left: 0; + `, [cB("slider-handle-wrapper", ` + top: unset; + left: 50%; + transform: translate(-50%, 50%); + `)]), cB("slider-rail", ` + height: 100%; + `, [cE("fill", ` + top: unset; + right: 0; + bottom: unset; + left: 0; + `)]), cM("with-mark", ` + width: var(--n-rail-width-vertical); + margin: 0 32px 0 8px; + `), cB("slider-marks", ` + top: calc(var(--n-handle-size) / 2); + right: unset; + bottom: calc(var(--n-handle-size) / 2); + left: 22px; + font-size: var(--n-mark-font-size); + `, [cB("slider-mark", ` + transform: translateY(50%); + white-space: nowrap; + `)]), cB("slider-dots", ` + top: calc(var(--n-handle-size) / 2); + right: unset; + bottom: calc(var(--n-handle-size) / 2); + left: 50%; + `, [cB("slider-dot", ` + transform: translateX(-50%) translateY(50%); + `)])]), cM("disabled", ` + cursor: not-allowed; + opacity: var(--n-opacity-disabled); + `, [cB("slider-handle", ` + cursor: not-allowed; + `)]), cM("with-mark", ` + width: 100%; + margin: 8px 0 32px 0; + `), c("&:hover", [cB("slider-rail", { + backgroundColor: "var(--n-rail-color-hover)" +}, [cE("fill", { + backgroundColor: "var(--n-fill-color-hover)" +})]), cB("slider-handle", { + boxShadow: "var(--n-handle-box-shadow-hover)" +})]), cM("active", [cB("slider-rail", { + backgroundColor: "var(--n-rail-color-hover)" +}, [cE("fill", { + backgroundColor: "var(--n-fill-color-hover)" +})]), cB("slider-handle", { + boxShadow: "var(--n-handle-box-shadow-hover)" +})]), cB("slider-marks", ` + position: absolute; + top: 18px; + left: calc(var(--n-handle-size) / 2); + right: calc(var(--n-handle-size) / 2); + `, [cB("slider-mark", ` + position: absolute; + transform: translateX(-50%); + white-space: nowrap; + `)]), cB("slider-rail", ` + width: 100%; + position: relative; + height: var(--n-rail-height); + background-color: var(--n-rail-color); + transition: background-color .3s var(--n-bezier); + border-radius: calc(var(--n-rail-height) / 2); + `, [cE("fill", ` + position: absolute; + top: 0; + bottom: 0; + border-radius: calc(var(--n-rail-height) / 2); + transition: background-color .3s var(--n-bezier); + background-color: var(--n-fill-color); + `)]), cB("slider-handles", ` + position: absolute; + top: 0; + right: calc(var(--n-handle-size) / 2); + bottom: 0; + left: calc(var(--n-handle-size) / 2); + `, [cB("slider-handle-wrapper", ` + outline: none; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + cursor: pointer; + display: flex; + `, [cB("slider-handle", ` + height: var(--n-handle-size); + width: var(--n-handle-size); + border-radius: 50%; + overflow: hidden; + transition: box-shadow .2s var(--n-bezier), background-color .3s var(--n-bezier); + background-color: var(--n-handle-color); + box-shadow: var(--n-handle-box-shadow); + `, [c("&:hover", ` + box-shadow: var(--n-handle-box-shadow-hover); + `)]), c("&:focus", [cB("slider-handle", ` + box-shadow: var(--n-handle-box-shadow-focus); + `, [c("&:hover", ` + box-shadow: var(--n-handle-box-shadow-active); + `)])])])]), cB("slider-dots", ` + position: absolute; + top: 50%; + left: calc(var(--n-handle-size) / 2); + right: calc(var(--n-handle-size) / 2); + `, [cM("transition-disabled", [cB("slider-dot", "transition: none;")]), cB("slider-dot", ` + transition: + border-color .3s var(--n-bezier), + box-shadow .3s var(--n-bezier), + background-color .3s var(--n-bezier); + position: absolute; + transform: translate(-50%, -50%); + height: var(--n-dot-height); + width: var(--n-dot-width); + border-radius: var(--n-dot-border-radius); + overflow: hidden; + box-sizing: border-box; + border: var(--n-dot-border); + background-color: var(--n-dot-color); + `, [cM("active", "border: var(--n-dot-border-active);")])])]), cB("slider-handle-indicator", ` + font-size: var(--n-font-size); + padding: 6px 10px; + border-radius: var(--n-indicator-border-radius); + color: var(--n-indicator-text-color); + background-color: var(--n-indicator-color); + box-shadow: var(--n-indicator-box-shadow); + `, [fadeInScaleUpTransition()]), cB("slider-handle-indicator", ` + font-size: var(--n-font-size); + padding: 6px 10px; + border-radius: var(--n-indicator-border-radius); + color: var(--n-indicator-text-color); + background-color: var(--n-indicator-color); + box-shadow: var(--n-indicator-box-shadow); + `, [cM("top", ` + margin-bottom: 12px; + `), cM("right", ` + margin-left: 12px; + `), cM("bottom", ` + margin-top: 12px; + `), cM("left", ` + margin-right: 12px; + `), fadeInScaleUpTransition()]), insideModal(cB("slider", [cB("slider-dot", "background-color: var(--n-dot-color-modal);")])), insidePopover(cB("slider", [cB("slider-dot", "background-color: var(--n-dot-color-popover);")]))]); +const eventButtonLeft = 0; +const sliderProps = Object.assign(Object.assign({}, useTheme.props), { to: useAdjustedTo.propTo, defaultValue: { + type: [Number, Array], + default: 0 +}, marks: Object, disabled: { + type: Boolean, + default: void 0 +}, formatTooltip: Function, keyboard: { + type: Boolean, + default: true +}, min: { + type: Number, + default: 0 +}, max: { + type: Number, + default: 100 +}, step: { + type: [Number, String], + default: 1 +}, range: Boolean, value: [Number, Array], placement: String, showTooltip: { + type: Boolean, + default: void 0 +}, tooltip: { + type: Boolean, + default: true +}, vertical: Boolean, reverse: Boolean, "onUpdate:value": [Function, Array], onUpdateValue: [Function, Array] }); +const NSlider = defineComponent({ + name: "Slider", + props: sliderProps, + setup(props) { + const { mergedClsPrefixRef, namespaceRef, inlineThemeDisabled } = useConfig(props); + const themeRef = useTheme("Slider", "-slider", style$1, sliderLight, props, mergedClsPrefixRef); + const handleRailRef = ref(null); + const [handleRefs, setHandleRefs] = useRefs(); + const [followerRefs, setFollowerRefs] = useRefs(); + const followerEnabledIndexSetRef = ref(/* @__PURE__ */ new Set()); + const formItem = useFormItem(props); + const { mergedDisabledRef } = formItem; + const precisionRef = computed(() => { + const { step } = props; + if (Number(step) <= 0 || step === "mark") + return 0; + const stepString = step.toString(); + let precision = 0; + if (stepString.includes(".")) { + precision = stepString.length - stepString.indexOf(".") - 1; + } + return precision; + }); + const uncontrolledValueRef = ref(props.defaultValue); + const controlledValueRef = toRef(props, "value"); + const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef); + const arrifiedValueRef = computed(() => { + const { value: mergedValue } = mergedValueRef; + return (props.range ? mergedValue : [mergedValue]).map(clampValue); + }); + const handleCountExceeds2Ref = computed(() => arrifiedValueRef.value.length > 2); + const mergedPlacementRef = computed(() => { + return props.placement === void 0 ? props.vertical ? "right" : "top" : props.placement; + }); + const markValuesRef = computed(() => { + const { marks } = props; + return marks ? Object.keys(marks).map(parseFloat) : null; + }); + const activeIndexRef = ref(-1); + const previousIndexRef = ref(-1); + const hoverIndexRef = ref(-1); + const draggingRef = ref(false); + const dotTransitionDisabledRef = ref(false); + const styleDirectionRef = computed(() => { + const { vertical, reverse } = props; + const left = reverse ? "right" : "left"; + const bottom = reverse ? "top" : "bottom"; + return vertical ? bottom : left; + }); + const fillStyleRef = computed(() => { + if (handleCountExceeds2Ref.value) + return; + const values = arrifiedValueRef.value; + const start = valueToPercentage(props.range ? Math.min(...values) : props.min); + const end = valueToPercentage(props.range ? Math.max(...values) : values[0]); + const { value: styleDirection } = styleDirectionRef; + return props.vertical ? { + [styleDirection]: `${start}%`, + height: `${end - start}%` + } : { + [styleDirection]: `${start}%`, + width: `${end - start}%` + }; + }); + const markInfosRef = computed(() => { + const mergedMarks = []; + const { marks } = props; + if (marks) { + const orderValues = arrifiedValueRef.value.slice(); + orderValues.sort((a, b) => a - b); + const { value: styleDirection } = styleDirectionRef; + const { value: handleCountExceeds2 } = handleCountExceeds2Ref; + const { range } = props; + const isActive = handleCountExceeds2 ? () => false : (num) => range ? num >= orderValues[0] && num <= orderValues[orderValues.length - 1] : num <= orderValues[0]; + for (const key of Object.keys(marks)) { + const num = Number(key); + mergedMarks.push({ + active: isActive(num), + label: marks[key], + style: { + [styleDirection]: `${valueToPercentage(num)}%` + } + }); + } + } + return mergedMarks; + }); + function getHandleStyle(value, index) { + const percentage = valueToPercentage(value); + const { value: styleDirection } = styleDirectionRef; + return { + [styleDirection]: `${percentage}%`, + zIndex: index === activeIndexRef.value ? 1 : 0 + }; + } + function isShowTooltip(index) { + return props.showTooltip || hoverIndexRef.value === index || activeIndexRef.value === index && draggingRef.value; + } + function shouldKeepTooltipTransition(index) { + if (!draggingRef.value) + return true; + return !(activeIndexRef.value === index && previousIndexRef.value === index); + } + function focusActiveHandle(index) { + var _a; + if (~index) { + activeIndexRef.value = index; + (_a = handleRefs.value.get(index)) === null || _a === void 0 ? void 0 : _a.focus(); + } + } + function syncPosition() { + followerRefs.value.forEach((inst, index) => { + if (isShowTooltip(index)) + inst.syncPosition(); + }); + } + function doUpdateValue(value) { + const { "onUpdate:value": _onUpdateValue, onUpdateValue } = props; + const { nTriggerFormInput, nTriggerFormChange } = formItem; + if (onUpdateValue) + call(onUpdateValue, value); + if (_onUpdateValue) + call(_onUpdateValue, value); + uncontrolledValueRef.value = value; + nTriggerFormInput(); + nTriggerFormChange(); + } + function dispatchValueUpdate(value) { + const { range } = props; + if (range) { + if (Array.isArray(value)) { + const { value: oldValues } = arrifiedValueRef; + if (value.join() !== oldValues.join()) { + doUpdateValue(value); + } + } + } else if (!Array.isArray(value)) { + const oldValue = arrifiedValueRef.value[0]; + if (oldValue !== value) { + doUpdateValue(value); + } + } + } + function doDispatchValue(value, index) { + if (props.range) { + const values = arrifiedValueRef.value.slice(); + values.splice(index, 1, value); + dispatchValueUpdate(values); + } else { + dispatchValueUpdate(value); + } + } + function sanitizeValue(value, currentValue, stepBuffer) { + const stepping = stepBuffer !== void 0; + if (!stepBuffer) { + stepBuffer = value - currentValue > 0 ? 1 : -1; + } + const markValues = markValuesRef.value || []; + const { step } = props; + if (step === "mark") { + const closestMark2 = getClosestMark(value, markValues.concat(currentValue), stepping ? stepBuffer : void 0); + return closestMark2 ? closestMark2.value : currentValue; + } + if (step <= 0) + return currentValue; + const { value: precision } = precisionRef; + let closestMark; + if (stepping) { + const currentStep = Number((currentValue / step).toFixed(precision)); + const actualStep = Math.floor(currentStep); + const leftStep = currentStep > actualStep ? actualStep : actualStep - 1; + const rightStep = currentStep < actualStep ? actualStep : actualStep + 1; + closestMark = getClosestMark(currentValue, [ + Number((leftStep * step).toFixed(precision)), + Number((rightStep * step).toFixed(precision)), + ...markValues + ], stepBuffer); + } else { + const roundValue = getRoundValue(value); + closestMark = getClosestMark(value, [...markValues, roundValue]); + } + return closestMark ? clampValue(closestMark.value) : currentValue; + } + function clampValue(value) { + return Math.min(props.max, Math.max(props.min, value)); + } + function valueToPercentage(value) { + const { max, min } = props; + return (value - min) / (max - min) * 100; + } + function percentageToValue(percentage) { + const { max, min } = props; + return min + (max - min) * percentage; + } + function getRoundValue(value) { + const { step, min } = props; + if (Number(step) <= 0 || step === "mark") + return value; + const newValue = Math.round((value - min) / step) * step + min; + return Number(newValue.toFixed(precisionRef.value)); + } + function getClosestMark(currentValue, markValues = markValuesRef.value, buffer) { + if (!(markValues === null || markValues === void 0 ? void 0 : markValues.length)) + return null; + let closestMark = null; + let index = -1; + while (++index < markValues.length) { + const diff = markValues[index] - currentValue; + const distance = Math.abs(diff); + if ( + // find marks in the same direction + (buffer === void 0 || diff * buffer > 0) && (closestMark === null || distance < closestMark.distance) + ) { + closestMark = { + index, + distance, + value: markValues[index] + }; + } + } + return closestMark; + } + function getPointValue(event) { + const railEl = handleRailRef.value; + if (!railEl) + return; + const touchEvent = isTouchEvent(event) ? event.touches[0] : event; + const railRect = railEl.getBoundingClientRect(); + let percentage; + if (props.vertical) { + percentage = (railRect.bottom - touchEvent.clientY) / railRect.height; + } else { + percentage = (touchEvent.clientX - railRect.left) / railRect.width; + } + if (props.reverse) { + percentage = 1 - percentage; + } + return percentageToValue(percentage); + } + function handleRailKeyDown(e) { + if (mergedDisabledRef.value || !props.keyboard) + return; + const { vertical, reverse } = props; + switch (e.key) { + case "ArrowUp": + e.preventDefault(); + handleStepValue(vertical && reverse ? -1 : 1); + break; + case "ArrowRight": + e.preventDefault(); + handleStepValue(!vertical && reverse ? -1 : 1); + break; + case "ArrowDown": + e.preventDefault(); + handleStepValue(vertical && reverse ? 1 : -1); + break; + case "ArrowLeft": + e.preventDefault(); + handleStepValue(!vertical && reverse ? 1 : -1); + break; + } + } + function handleStepValue(ratio) { + const activeIndex = activeIndexRef.value; + if (activeIndex === -1) + return; + const { step } = props; + const currentValue = arrifiedValueRef.value[activeIndex]; + const nextValue = Number(step) <= 0 || step === "mark" ? currentValue : currentValue + step * ratio; + doDispatchValue( + // Avoid the number of value does not change when `step` is null + sanitizeValue(nextValue, currentValue, ratio > 0 ? 1 : -1), + activeIndex + ); + } + function handleRailMouseDown(event) { + var _a, _b; + if (mergedDisabledRef.value) + return; + if (!isTouchEvent(event) && event.button !== eventButtonLeft) { + return; + } + const pointValue = getPointValue(event); + if (pointValue === void 0) + return; + const values = arrifiedValueRef.value.slice(); + const activeIndex = props.range ? (_b = (_a = getClosestMark(pointValue, values)) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : -1 : 0; + if (activeIndex !== -1) { + event.preventDefault(); + focusActiveHandle(activeIndex); + startDragging(); + doDispatchValue(sanitizeValue(pointValue, arrifiedValueRef.value[activeIndex]), activeIndex); + } + } + function startDragging() { + if (!draggingRef.value) { + draggingRef.value = true; + on("touchend", document, handleMouseUp); + on("mouseup", document, handleMouseUp); + on("touchmove", document, handleMouseMove); + on("mousemove", document, handleMouseMove); + } + } + function stopDragging() { + if (draggingRef.value) { + draggingRef.value = false; + off("touchend", document, handleMouseUp); + off("mouseup", document, handleMouseUp); + off("touchmove", document, handleMouseMove); + off("mousemove", document, handleMouseMove); + } + } + function handleMouseMove(event) { + const { value: activeIndex } = activeIndexRef; + if (!draggingRef.value || activeIndex === -1) { + stopDragging(); + return; + } + const pointValue = getPointValue(event); + doDispatchValue(sanitizeValue(pointValue, arrifiedValueRef.value[activeIndex]), activeIndex); + } + function handleMouseUp() { + stopDragging(); + } + function handleHandleFocus(index) { + activeIndexRef.value = index; + if (!mergedDisabledRef.value) { + hoverIndexRef.value = index; + } + } + function handleHandleBlur(index) { + if (activeIndexRef.value === index) { + activeIndexRef.value = -1; + stopDragging(); + } + if (hoverIndexRef.value === index) { + hoverIndexRef.value = -1; + } + } + function handleHandleMouseEnter(index) { + hoverIndexRef.value = index; + } + function handleHandleMouseLeave(index) { + if (hoverIndexRef.value === index) { + hoverIndexRef.value = -1; + } + } + watch(activeIndexRef, (_, previous) => void nextTick(() => previousIndexRef.value = previous)); + watch(mergedValueRef, () => { + if (props.marks) { + if (dotTransitionDisabledRef.value) + return; + dotTransitionDisabledRef.value = true; + void nextTick(() => { + dotTransitionDisabledRef.value = false; + }); + } + void nextTick(syncPosition); + }); + onBeforeUnmount(() => { + stopDragging(); + }); + const cssVarsRef = computed(() => { + const { self: { markFontSize, railColor, railColorHover, fillColor, fillColorHover, handleColor, opacityDisabled, dotColor, dotColorModal, handleBoxShadow, handleBoxShadowHover, handleBoxShadowActive, handleBoxShadowFocus, dotBorder, dotBoxShadow, railHeight, railWidthVertical, handleSize, dotHeight, dotWidth, dotBorderRadius, fontSize, dotBorderActive, dotColorPopover }, common: { cubicBezierEaseInOut } } = themeRef.value; + return { + "--n-bezier": cubicBezierEaseInOut, + "--n-dot-border": dotBorder, + "--n-dot-border-active": dotBorderActive, + "--n-dot-border-radius": dotBorderRadius, + "--n-dot-box-shadow": dotBoxShadow, + "--n-dot-color": dotColor, + "--n-dot-color-modal": dotColorModal, + "--n-dot-color-popover": dotColorPopover, + "--n-dot-height": dotHeight, + "--n-dot-width": dotWidth, + "--n-fill-color": fillColor, + "--n-fill-color-hover": fillColorHover, + "--n-font-size": fontSize, + "--n-handle-box-shadow": handleBoxShadow, + "--n-handle-box-shadow-active": handleBoxShadowActive, + "--n-handle-box-shadow-focus": handleBoxShadowFocus, + "--n-handle-box-shadow-hover": handleBoxShadowHover, + "--n-handle-color": handleColor, + "--n-handle-size": handleSize, + "--n-opacity-disabled": opacityDisabled, + "--n-rail-color": railColor, + "--n-rail-color-hover": railColorHover, + "--n-rail-height": railHeight, + "--n-rail-width-vertical": railWidthVertical, + "--n-mark-font-size": markFontSize + }; + }); + const themeClassHandle = inlineThemeDisabled ? useThemeClass("slider", void 0, cssVarsRef, props) : void 0; + const indicatorCssVarsRef = computed(() => { + const { self: { fontSize, indicatorColor, indicatorBoxShadow, indicatorTextColor, indicatorBorderRadius } } = themeRef.value; + return { + "--n-font-size": fontSize, + "--n-indicator-border-radius": indicatorBorderRadius, + "--n-indicator-box-shadow": indicatorBoxShadow, + "--n-indicator-color": indicatorColor, + "--n-indicator-text-color": indicatorTextColor + }; + }); + const indicatorThemeClassHandle = inlineThemeDisabled ? useThemeClass("slider-indicator", void 0, indicatorCssVarsRef, props) : void 0; + return { + mergedClsPrefix: mergedClsPrefixRef, + namespace: namespaceRef, + uncontrolledValue: uncontrolledValueRef, + mergedValue: mergedValueRef, + mergedDisabled: mergedDisabledRef, + mergedPlacement: mergedPlacementRef, + isMounted: isMounted(), + adjustedTo: useAdjustedTo(props), + dotTransitionDisabled: dotTransitionDisabledRef, + markInfos: markInfosRef, + isShowTooltip, + shouldKeepTooltipTransition, + handleRailRef, + setHandleRefs, + setFollowerRefs, + fillStyle: fillStyleRef, + getHandleStyle, + activeIndex: activeIndexRef, + arrifiedValues: arrifiedValueRef, + followerEnabledIndexSet: followerEnabledIndexSetRef, + handleRailMouseDown, + handleHandleFocus, + handleHandleBlur, + handleHandleMouseEnter, + handleHandleMouseLeave, + handleRailKeyDown, + indicatorCssVars: inlineThemeDisabled ? void 0 : indicatorCssVarsRef, + indicatorThemeClass: indicatorThemeClassHandle === null || indicatorThemeClassHandle === void 0 ? void 0 : indicatorThemeClassHandle.themeClass, + indicatorOnRender: indicatorThemeClassHandle === null || indicatorThemeClassHandle === void 0 ? void 0 : indicatorThemeClassHandle.onRender, + cssVars: inlineThemeDisabled ? void 0 : cssVarsRef, + themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, + onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender + }; + }, + render() { + var _a; + const { mergedClsPrefix, themeClass, formatTooltip } = this; + (_a = this.onRender) === null || _a === void 0 ? void 0 : _a.call(this); + return h( + "div", + { class: [ + `${mergedClsPrefix}-slider`, + themeClass, + { + [`${mergedClsPrefix}-slider--disabled`]: this.mergedDisabled, + [`${mergedClsPrefix}-slider--active`]: this.activeIndex !== -1, + [`${mergedClsPrefix}-slider--with-mark`]: this.marks, + [`${mergedClsPrefix}-slider--vertical`]: this.vertical, + [`${mergedClsPrefix}-slider--reverse`]: this.reverse + } + ], style: this.cssVars, onKeydown: this.handleRailKeyDown, onMousedown: this.handleRailMouseDown, onTouchstart: this.handleRailMouseDown }, + h( + "div", + { class: `${mergedClsPrefix}-slider-rail` }, + h("div", { class: `${mergedClsPrefix}-slider-rail__fill`, style: this.fillStyle }), + this.marks ? h("div", { class: [ + `${mergedClsPrefix}-slider-dots`, + this.dotTransitionDisabled && `${mergedClsPrefix}-slider-dots--transition-disabled` + ] }, this.markInfos.map((mark) => h("div", { key: mark.label, class: [ + `${mergedClsPrefix}-slider-dot`, + { + [`${mergedClsPrefix}-slider-dot--active`]: mark.active + } + ], style: mark.style }))) : null, + h("div", { ref: "handleRailRef", class: `${mergedClsPrefix}-slider-handles` }, this.arrifiedValues.map((value, index) => { + const showTooltip = this.isShowTooltip(index); + return h(VBinder, null, { + default: () => [ + h(VTarget, null, { + default: () => h("div", { ref: this.setHandleRefs(index), class: `${mergedClsPrefix}-slider-handle-wrapper`, tabindex: this.mergedDisabled ? -1 : 0, style: this.getHandleStyle(value, index), onFocus: () => { + this.handleHandleFocus(index); + }, onBlur: () => { + this.handleHandleBlur(index); + }, onMouseenter: () => { + this.handleHandleMouseEnter(index); + }, onMouseleave: () => { + this.handleHandleMouseLeave(index); + } }, resolveSlot(this.$slots.thumb, () => [ + h("div", { class: `${mergedClsPrefix}-slider-handle` }) + ])) + }), + this.tooltip && h(VFollower, { ref: this.setFollowerRefs(index), show: showTooltip, to: this.adjustedTo, enabled: this.showTooltip && !this.range || this.followerEnabledIndexSet.has(index), teleportDisabled: this.adjustedTo === useAdjustedTo.tdkey, placement: this.mergedPlacement, containerClass: this.namespace }, { + default: () => h(Transition, { name: "fade-in-scale-up-transition", appear: this.isMounted, css: this.shouldKeepTooltipTransition(index), onEnter: () => { + this.followerEnabledIndexSet.add(index); + }, onAfterLeave: () => { + this.followerEnabledIndexSet.delete(index); + } }, { + default: () => { + var _a2; + if (showTooltip) { + (_a2 = this.indicatorOnRender) === null || _a2 === void 0 ? void 0 : _a2.call(this); + return h("div", { class: [ + `${mergedClsPrefix}-slider-handle-indicator`, + this.indicatorThemeClass, + `${mergedClsPrefix}-slider-handle-indicator--${this.mergedPlacement}` + ], style: this.indicatorCssVars }, typeof formatTooltip === "function" ? formatTooltip(value) : value); + } + return null; + } + }) + }) + ] + }); + })), + this.marks ? h("div", { class: `${mergedClsPrefix}-slider-marks` }, this.markInfos.map((mark) => h("div", { key: mark.label, class: `${mergedClsPrefix}-slider-mark`, style: mark.style }, mark.label))) : null + ) + ); + } +}); const style = cB("switch", ` height: var(--n-height); min-width: var(--n-width); @@ -354,5 +1077,6 @@ const NSwitch = defineComponent({ } }); export { - NSwitch as N + NSlider as N, + NSwitch as a }; diff --git a/frontend/dist/assets/TaggerView.css b/frontend/dist/assets/TaggerView.css index ea6025ed3..e5d6b99f2 100644 --- a/frontend/dist/assets/TaggerView.css +++ b/frontend/dist/assets/TaggerView.css @@ -1,11 +1,11 @@ -.image-container img[data-v-e404f3d8] { +.image-container img[data-v-95e673e5] { width: 100%; height: 100%; object-fit: contain; overflow: hidden; } -.image-container[data-v-e404f3d8] { +.image-container[data-v-95e673e5] { height: 70vh; width: 100%; display: flex; diff --git a/frontend/dist/assets/TaggerView.js b/frontend/dist/assets/TaggerView.js index 3ce021525..06bb41574 100644 --- a/frontend/dist/assets/TaggerView.js +++ b/frontend/dist/assets/TaggerView.js @@ -1,12 +1,11 @@ import { _ as _sfc_main$1 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; import { I as ImageUpload } from "./ImageUpload.js"; -import { d as defineComponent, u as useState, a as useSettings, b as useMessage, E as ref, c as computed, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, N as NGi, i as NCard, j as NSpace, n as createBaseVNode, r as NSelect, q as NTooltip, m as createTextVNode, k as NInput, t as toDisplayString, y as NGrid, s as serverUrl, z as spaceRegex, A as pushScopeId, B as popScopeId, _ as _export_sfc } from "./index.js"; +import { d as defineComponent, u as useState, a as useSettings, b as useMessage, D as ref, c as computed, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, N as NGi, i as NCard, j as NSpace, n as createBaseVNode, x as NSelect, q as NTooltip, m as createTextVNode, k as NInput, t as toDisplayString, y as NGrid, s as serverUrl, z as spaceRegex, A as pushScopeId, B as popScopeId, _ as _export_sfc } from "./index.js"; import { v as v4 } from "./v4.js"; -import { N as NSlider } from "./Slider.js"; +import { N as NSlider, a as NSwitch } from "./Switch.js"; import { N as NInputNumber } from "./InputNumber.js"; -import { N as NSwitch } from "./Switch.js"; import "./CloudUpload.js"; -const _withScopeId = (n) => (pushScopeId("data-v-e404f3d8"), n = n(), popScopeId(), n); +const _withScopeId = (n) => (pushScopeId("data-v-95e673e5"), n = n(), popScopeId(), n); const _hoisted_1 = { class: "main-container" }; const _hoisted_2 = { class: "flex-container" }; const _hoisted_3 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Sampler", -1)); @@ -18,10 +17,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "TaggerView", setup(__props) { const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const messageHandler = useMessage(); const imageSelectCallback = (base64Image) => { - conf.data.settings.tagger.image = base64Image; + settings.data.settings.tagger.image = base64Image; }; const generate = () => { global.state.generating = true; @@ -32,11 +31,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }, body: JSON.stringify({ data: { - image: conf.data.settings.tagger.image, + image: settings.data.settings.tagger.image, id: v4(), - threshold: conf.data.settings.tagger.threshold + threshold: settings.data.settings.tagger.threshold }, - model: conf.data.settings.tagger.model + model: settings.data.settings.tagger.model }) }).then((res) => { if (!res.ok) { @@ -47,13 +46,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ (data) => { global.state.tagger.positivePrompt = data.positive; global.state.tagger.negativePrompt = data.negative; - console.log(data); } ); }).catch((err) => { global.state.generating = false; messageHandler.error(err); - console.log(err); }); }; const weighted = ref(false); @@ -107,9 +104,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ default: withCtx(() => [ createVNode(ImageUpload, { callback: imageSelectCallback, - preview: unref(conf).data.settings.tagger.image, + preview: unref(settings).data.settings.tagger.image, style: { "margin-bottom": "12px" }, - onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(conf).data.settings.tagger.image = $event) + onFileDropped: _cache[0] || (_cache[0] = ($event) => unref(settings).data.settings.tagger.image = $event) }, null, 8, ["preview"]), createVNode(unref(NCard), { title: "Settings", @@ -138,8 +135,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ value: "flamingo" } ], - value: unref(conf).data.settings.tagger.model, - "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(conf).data.settings.tagger.model = $event), + value: unref(settings).data.settings.tagger.model, + "onUpdate:value": _cache[1] || (_cache[1] = ($event) => unref(settings).data.settings.tagger.model = $event), style: { "flex-grow": "1" } }, null, 8, ["value"]) ]), @@ -154,16 +151,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.tagger.threshold, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(conf).data.settings.tagger.threshold = $event), + value: unref(settings).data.settings.tagger.threshold, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).data.settings.tagger.threshold = $event), min: 0.1, max: 1, style: { "margin-right": "12px" }, step: 0.025 }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.tagger.threshold, - "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(conf).data.settings.tagger.threshold = $event), + value: unref(settings).data.settings.tagger.threshold, + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => unref(settings).data.settings.tagger.threshold = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 0.1, @@ -232,8 +229,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }; } }); -const TaggerView_vue_vue_type_style_index_0_scoped_e404f3d8_lang = ""; -const TaggerView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-e404f3d8"]]); +const TaggerView_vue_vue_type_style_index_0_scoped_95e673e5_lang = ""; +const TaggerView = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-95e673e5"]]); export { TaggerView as default }; diff --git a/frontend/dist/assets/TestView.js b/frontend/dist/assets/TestView.js index a9cea9ea4..6214a3cee 100644 --- a/frontend/dist/assets/TestView.js +++ b/frontend/dist/assets/TestView.js @@ -1,5 +1,5 @@ import { _ as _sfc_main$1 } from "./ModelPopup.vue_vue_type_script_setup_true_lang.js"; -import { d as defineComponent, E as ref, e as openBlock, v as createBlock } from "./index.js"; +import { d as defineComponent, D as ref, e as openBlock, v as createBlock } from "./index.js"; import "./DescriptionsItem.js"; const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "TestView", diff --git a/frontend/dist/assets/TextToImageView.js b/frontend/dist/assets/TextToImageView.js index eb522bad6..55dc016e9 100644 --- a/frontend/dist/assets/TextToImageView.js +++ b/frontend/dist/assets/TextToImageView.js @@ -1,65 +1,58 @@ -import { _ as _sfc_main$3 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; -import { _ as _sfc_main$4 } from "./ImageOutput.vue_vue_type_script_setup_true_lang.js"; -import { B as BurnerClock, _ as _sfc_main$1, a as _sfc_main$2, b as _sfc_main$5 } from "./clock.js"; -import { d as defineComponent, u as useState, a as useSettings, b as useMessage, c as computed, o as onUnmounted, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, s as serverUrl, N as NGi, i as NCard, j as NSpace, k as NInput, p as promptHandleKeyUp, l as promptHandleKeyDown, m as createTextVNode, t as toDisplayString, n as createBaseVNode, q as NTooltip, r as NSelect, v as createBlock, x as createCommentVNode, y as NGrid, z as spaceRegex } from "./index.js"; +import { _ as _sfc_main$4 } from "./GenerateSection.vue_vue_type_script_setup_true_lang.js"; +import { _ as _sfc_main$5 } from "./ImageOutput.vue_vue_type_script_setup_true_lang.js"; +import { B as BurnerClock, _ as _sfc_main$2, a as _sfc_main$3, b as _sfc_main$6 } from "./clock.js"; +import { _ as _sfc_main$1 } from "./SamplerPicker.vue_vue_type_script_setup_true_lang.js"; +import { d as defineComponent, u as useState, a as useSettings, b as useMessage, c as computed, o as onUnmounted, e as openBlock, f as createElementBlock, g as createVNode, w as withCtx, h as unref, s as serverUrl, N as NGi, i as NCard, j as NSpace, k as NInput, p as promptHandleKeyUp, l as promptHandleKeyDown, m as createTextVNode, t as toDisplayString, n as createBaseVNode, q as NTooltip, r as createCommentVNode, v as createBlock, x as NSelect, y as NGrid, z as spaceRegex } from "./index.js"; import { v as v4 } from "./v4.js"; -import { N as NSwitch } from "./Switch.js"; -import { N as NSlider } from "./Slider.js"; +import { N as NSlider, a as NSwitch } from "./Switch.js"; import { N as NInputNumber } from "./InputNumber.js"; import "./SendOutputTo.vue_vue_type_script_setup_true_lang.js"; import "./TrashBin.js"; import "./DescriptionsItem.js"; +import "./Settings.js"; const _hoisted_1 = { class: "main-container" }; const _hoisted_2 = { class: "flex-container" }; -const _hoisted_3 = /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "100px" } }, "Sampler", -1); -const _hoisted_4 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using DPMSolverMultistep for the best results . ", -1); -const _hoisted_5 = /* @__PURE__ */ createBaseVNode("a", { - target: "_blank", - href: "https://docs.google.com/document/d/1n0YozLAUwLJWZmbsx350UD_bwAx3gZMnRuleIZt_R1w" -}, "Learn more", -1); -const _hoisted_6 = { class: "flex-container" }; -const _hoisted_7 = /* @__PURE__ */ createBaseVNode("p", { style: { "width": "120px" } }, "Karras Sigmas", -1); -const _hoisted_8 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "Works only with KDPM samplers. Ignored by other samplers.", -1); -const _hoisted_9 = { class: "flex-container" }; -const _hoisted_10 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1); -const _hoisted_11 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1); +const _hoisted_3 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1); +const _hoisted_4 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1); +const _hoisted_5 = { class: "flex-container" }; +const _hoisted_6 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1); +const _hoisted_7 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1); +const _hoisted_8 = { + key: 0, + class: "flex-container" +}; +const _hoisted_9 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Self Attention Scale", -1); +const _hoisted_10 = { class: "flex-container" }; +const _hoisted_11 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1); const _hoisted_12 = { class: "flex-container" }; -const _hoisted_13 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "CFG Scale", -1); -const _hoisted_14 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 3-15 for most images.", -1); +const _hoisted_13 = /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "75px" } }, "Seed", -1); +const _hoisted_14 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1); const _hoisted_15 = { class: "flex-container" }; -const _hoisted_16 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Self Attention Scale", -1); -const _hoisted_17 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "PyTorch ONLY.", -1); -const _hoisted_18 = { class: "flex-container" }; -const _hoisted_19 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Batch Count", -1); -const _hoisted_20 = { class: "flex-container" }; -const _hoisted_21 = /* @__PURE__ */ createBaseVNode("p", { style: { "margin-right": "12px", "width": "75px" } }, "Seed", -1); -const _hoisted_22 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "For random seed use -1.", -1); -const _hoisted_23 = { class: "flex-container" }; -const _hoisted_24 = /* @__PURE__ */ createBaseVNode("div", { class: "slider-label" }, [ +const _hoisted_16 = /* @__PURE__ */ createBaseVNode("div", { class: "slider-label" }, [ /* @__PURE__ */ createBaseVNode("p", null, "Enabled") ], -1); -const _hoisted_25 = { class: "flex-container" }; -const _hoisted_26 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1); -const _hoisted_27 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1); -const _hoisted_28 = { class: "flex-container" }; -const _hoisted_29 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Scale", -1); -const _hoisted_30 = { class: "flex-container" }; -const _hoisted_31 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Strength", -1); -const _hoisted_32 = { class: "flex-container" }; -const _hoisted_33 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Antialiased", -1); -const _hoisted_34 = { class: "flex-container" }; -const _hoisted_35 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Latent Mode", -1); +const _hoisted_17 = { class: "flex-container" }; +const _hoisted_18 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Steps", -1); +const _hoisted_19 = /* @__PURE__ */ createBaseVNode("b", { class: "highlight" }, "We recommend using 20-50 steps for most images.", -1); +const _hoisted_20 = { class: "flex-container" }; +const _hoisted_21 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Scale", -1); +const _hoisted_22 = { class: "flex-container" }; +const _hoisted_23 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Strength", -1); +const _hoisted_24 = { class: "flex-container" }; +const _hoisted_25 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Antialiased", -1); +const _hoisted_26 = { class: "flex-container" }; +const _hoisted_27 = /* @__PURE__ */ createBaseVNode("p", { class: "slider-label" }, "Latent Mode", -1); const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "TextToImageView", setup(__props) { const global = useState(); - const conf = useSettings(); + const settings = useSettings(); const messageHandler = useMessage(); const promptCount = computed(() => { - return conf.data.settings.txt2img.prompt.split(spaceRegex).length - 1; + return settings.data.settings.txt2img.prompt.split(spaceRegex).length - 1; }); const negativePromptCount = computed(() => { - return conf.data.settings.txt2img.negative_prompt.split(spaceRegex).length - 1; + return settings.data.settings.txt2img.negative_prompt.split(spaceRegex).length - 1; }); const checkSeed = (seed) => { if (seed === -1) { @@ -69,12 +62,12 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }; const generate = () => { var _a; - if (conf.data.settings.txt2img.seed === null) { + if (settings.data.settings.txt2img.seed === null) { messageHandler.error("Please set a seed"); return; } global.state.generating = true; - const seed = checkSeed(conf.data.settings.txt2img.seed); + const seed = checkSeed(settings.data.settings.txt2img.seed); fetch(`${serverUrl}/api/generate/txt2img`, { method: "POST", headers: { @@ -83,29 +76,30 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ body: JSON.stringify({ data: { id: v4(), - prompt: conf.data.settings.txt2img.prompt, - negative_prompt: conf.data.settings.txt2img.negative_prompt, - width: conf.data.settings.txt2img.width, - height: conf.data.settings.txt2img.height, - steps: conf.data.settings.txt2img.steps, - guidance_scale: conf.data.settings.txt2img.cfg_scale, + prompt: settings.data.settings.txt2img.prompt, + negative_prompt: settings.data.settings.txt2img.negative_prompt, + width: settings.data.settings.txt2img.width, + height: settings.data.settings.txt2img.height, + steps: settings.data.settings.txt2img.steps, + guidance_scale: settings.data.settings.txt2img.cfg_scale, seed, - batch_size: conf.data.settings.txt2img.batch_size, - batch_count: conf.data.settings.txt2img.batch_count, - scheduler: conf.data.settings.txt2img.sampler, - self_attention_scale: conf.data.settings.txt2img.self_attention_scale, - use_karras_sigmas: conf.data.settings.txt2img.use_karras_sigmas + batch_size: settings.data.settings.txt2img.batch_size, + batch_count: settings.data.settings.txt2img.batch_count, + scheduler: settings.data.settings.txt2img.sampler, + self_attention_scale: settings.data.settings.txt2img.self_attention_scale, + sigmas: settings.data.settings.txt2img.sigmas, + sampler_settings: settings.data.settings.sampler_config[settings.data.settings.txt2img.sampler] }, - model: (_a = conf.data.settings.model) == null ? void 0 : _a.name, + model: (_a = settings.data.settings.model) == null ? void 0 : _a.name, backend: "PyTorch", autoload: false, flags: global.state.txt2img.highres ? { highres_fix: { - scale: conf.data.settings.extra.highres.scale, - latent_scale_mode: conf.data.settings.extra.highres.latent_scale_mode, - strength: conf.data.settings.extra.highres.strength, - steps: conf.data.settings.extra.highres.steps, - antialiased: conf.data.settings.extra.highres.antialiased + scale: settings.data.settings.extra.highres.scale, + latent_scale_mode: settings.data.settings.extra.highres.latent_scale_mode, + strength: settings.data.settings.extra.highres.strength, + steps: settings.data.settings.extra.highres.steps, + antialiased: settings.data.settings.extra.highres.antialiased } } : {} }) @@ -128,14 +122,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }).catch((err) => { global.state.generating = false; messageHandler.error(err); - console.log(err); }); }; - const isSelectedModelPyTorch = computed(() => { - var _a; - return ((_a = conf.data.settings.model) == null ? void 0 : _a.backend) === "PyTorch"; - }); - const burner = new BurnerClock(conf.data.settings.txt2img, conf, generate); + const burner = new BurnerClock( + settings.data.settings.txt2img, + settings, + generate + ); onUnmounted(() => { burner.cleanup(); }); @@ -155,209 +148,176 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ vertical: "", class: "left-container" }, { - default: withCtx(() => [ - createVNode(unref(NInput), { - value: unref(conf).data.settings.txt2img.prompt, - "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(conf).data.settings.txt2img.prompt = $event), - type: "textarea", - placeholder: "Prompt", - "show-count": "", - onKeyup: _cache[1] || (_cache[1] = ($event) => unref(promptHandleKeyUp)( - $event, - unref(conf).data.settings.txt2img, - "prompt", - unref(global) - )), - onKeydown: unref(promptHandleKeyDown) - }, { - count: withCtx(() => [ - createTextVNode(toDisplayString(promptCount.value), 1) - ]), - _: 1 - }, 8, ["value", "onKeydown"]), - createVNode(unref(NInput), { - value: unref(conf).data.settings.txt2img.negative_prompt, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(conf).data.settings.txt2img.negative_prompt = $event), - type: "textarea", - placeholder: "Negative prompt", - "show-count": "", - onKeyup: _cache[3] || (_cache[3] = ($event) => unref(promptHandleKeyUp)( - $event, - unref(conf).data.settings.txt2img, - "negative_prompt", - unref(global) - )), - onKeydown: unref(promptHandleKeyDown) - }, { - count: withCtx(() => [ - createTextVNode(toDisplayString(negativePromptCount.value), 1) - ]), - _: 1 - }, 8, ["value", "onKeydown"]), - createBaseVNode("div", _hoisted_2, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_3 - ]), - default: withCtx(() => [ - createTextVNode(" The sampler is the method used to generate the image. Your result may vary drastically depending on the sampler you choose. "), - _hoisted_4, - _hoisted_5 - ]), - _: 1 - }), - createVNode(unref(NSelect), { - options: unref(conf).scheduler_options, - value: unref(conf).data.settings.txt2img.sampler, - "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(conf).data.settings.txt2img.sampler = $event), - style: { "flex-grow": "1" } - }, null, 8, ["options", "value"]) - ]), - createBaseVNode("div", _hoisted_6, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_7 - ]), - default: withCtx(() => [ - createTextVNode(" Changes the sigmas used in the Karras diffusion process. Might provide better results for some images. "), - _hoisted_8 - ]), - _: 1 - }), - createVNode(unref(NSwitch), { - value: unref(conf).data.settings.txt2img.use_karras_sigmas, - "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(conf).data.settings.txt2img.use_karras_sigmas = $event), - style: { "justify-self": "flex-end" } - }, null, 8, ["value"]) - ]), - createVNode(_sfc_main$1, { - "dimensions-object": unref(conf).data.settings.txt2img - }, null, 8, ["dimensions-object"]), - createBaseVNode("div", _hoisted_9, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_10 - ]), - default: withCtx(() => [ - createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), - _hoisted_11 + default: withCtx(() => { + var _a; + return [ + createVNode(unref(NInput), { + value: unref(settings).data.settings.txt2img.prompt, + "onUpdate:value": _cache[0] || (_cache[0] = ($event) => unref(settings).data.settings.txt2img.prompt = $event), + type: "textarea", + placeholder: "Prompt", + "show-count": "", + onKeyup: _cache[1] || (_cache[1] = ($event) => unref(promptHandleKeyUp)( + $event, + unref(settings).data.settings.txt2img, + "prompt", + unref(global) + )), + onKeydown: unref(promptHandleKeyDown) + }, { + count: withCtx(() => [ + createTextVNode(toDisplayString(promptCount.value), 1) ]), _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.txt2img.steps, - "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(conf).data.settings.txt2img.steps = $event), - min: 5, - max: 300, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.steps, - "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(conf).data.settings.txt2img.steps = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" } - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_12, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_13 - ]), - default: withCtx(() => [ - createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), - _hoisted_14 + }, 8, ["value", "onKeydown"]), + createVNode(unref(NInput), { + value: unref(settings).data.settings.txt2img.negative_prompt, + "onUpdate:value": _cache[2] || (_cache[2] = ($event) => unref(settings).data.settings.txt2img.negative_prompt = $event), + type: "textarea", + placeholder: "Negative prompt", + "show-count": "", + onKeyup: _cache[3] || (_cache[3] = ($event) => unref(promptHandleKeyUp)( + $event, + unref(settings).data.settings.txt2img, + "negative_prompt", + unref(global) + )), + onKeydown: unref(promptHandleKeyDown) + }, { + count: withCtx(() => [ + createTextVNode(toDisplayString(negativePromptCount.value), 1) ]), _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.txt2img.cfg_scale, - "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(conf).data.settings.txt2img.cfg_scale = $event), - min: 1, - max: 30, - step: 0.5, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.cfg_scale, - "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(conf).data.settings.txt2img.cfg_scale = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 0.5 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_15, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_16 - ]), - default: withCtx(() => [ - _hoisted_17, - createTextVNode(" If self attention is >0, SAG will guide the model and improve the quality of the image at the cost of speed. Higher values will follow the guidance more closely, which can lead to better, more sharp and detailed outputs. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.txt2img.self_attention_scale, - "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(conf).data.settings.txt2img.self_attention_scale = $event), - min: 0, - max: 1, - step: 0.05, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.self_attention_scale, - "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(conf).data.settings.txt2img.self_attention_scale = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" }, - step: 0.05 - }, null, 8, ["value"]) - ]), - createBaseVNode("div", _hoisted_18, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_19 - ]), - default: withCtx(() => [ - createTextVNode(" Number of images to generate after each other. ") - ]), - _: 1 - }), - createVNode(unref(NSlider), { - value: unref(conf).data.settings.txt2img.batch_count, - "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(conf).data.settings.txt2img.batch_count = $event), - min: 1, - max: 9, - style: { "margin-right": "12px" } - }, null, 8, ["value"]), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.batch_count, - "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(conf).data.settings.txt2img.batch_count = $event), - size: "small", - style: { "min-width": "96px", "width": "96px" } - }, null, 8, ["value"]) - ]), - createVNode(_sfc_main$2, { - "batch-size-object": unref(conf).data.settings.txt2img - }, null, 8, ["batch-size-object"]), - createBaseVNode("div", _hoisted_20, [ - createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { - trigger: withCtx(() => [ - _hoisted_21 - ]), - default: withCtx(() => [ - createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), - _hoisted_22 - ]), - _: 1 - }), - createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.txt2img.seed, - "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(conf).data.settings.txt2img.seed = $event), - size: "small", - style: { "flex-grow": "1" } - }, null, 8, ["value"]) - ]) - ]), + }, 8, ["value", "onKeydown"]), + createVNode(_sfc_main$1, { type: "txt2img" }), + createVNode(_sfc_main$2, { + "dimensions-object": unref(settings).data.settings.txt2img + }, null, 8, ["dimensions-object"]), + createBaseVNode("div", _hoisted_2, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_3 + ]), + default: withCtx(() => [ + createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), + _hoisted_4 + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.txt2img.steps, + "onUpdate:value": _cache[4] || (_cache[4] = ($event) => unref(settings).data.settings.txt2img.steps = $event), + min: 5, + max: 300, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.txt2img.steps, + "onUpdate:value": _cache[5] || (_cache[5] = ($event) => unref(settings).data.settings.txt2img.steps = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" } + }, null, 8, ["value"]) + ]), + createBaseVNode("div", _hoisted_5, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_6 + ]), + default: withCtx(() => [ + createTextVNode(' Guidance scale indicates how much should model stay close to the prompt. Higher values might be exactly what you want, but generated images might have some artefacts. Lower values indicates that model can "dream" about this prompt more. '), + _hoisted_7 + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.txt2img.cfg_scale, + "onUpdate:value": _cache[6] || (_cache[6] = ($event) => unref(settings).data.settings.txt2img.cfg_scale = $event), + min: 1, + max: 30, + step: 0.5, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.txt2img.cfg_scale, + "onUpdate:value": _cache[7] || (_cache[7] = ($event) => unref(settings).data.settings.txt2img.cfg_scale = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + step: 0.5 + }, null, 8, ["value"]) + ]), + Number.isInteger(unref(settings).data.settings.txt2img.sampler) && ((_a = unref(settings).data.settings.model) == null ? void 0 : _a.backend) === "PyTorch" ? (openBlock(), createElementBlock("div", _hoisted_8, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_9 + ]), + default: withCtx(() => [ + createTextVNode(" If self attention is >0, SAG will guide the model and improve the quality of the image at the cost of speed. Higher values will follow the guidance more closely, which can lead to better, more sharp and detailed outputs. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.txt2img.self_attention_scale, + "onUpdate:value": _cache[8] || (_cache[8] = ($event) => unref(settings).data.settings.txt2img.self_attention_scale = $event), + min: 0, + max: 1, + step: 0.05, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.txt2img.self_attention_scale, + "onUpdate:value": _cache[9] || (_cache[9] = ($event) => unref(settings).data.settings.txt2img.self_attention_scale = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" }, + step: 0.05 + }, null, 8, ["value"]) + ])) : createCommentVNode("", true), + createBaseVNode("div", _hoisted_10, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_11 + ]), + default: withCtx(() => [ + createTextVNode(" Number of images to generate after each other. ") + ]), + _: 1 + }), + createVNode(unref(NSlider), { + value: unref(settings).data.settings.txt2img.batch_count, + "onUpdate:value": _cache[10] || (_cache[10] = ($event) => unref(settings).data.settings.txt2img.batch_count = $event), + min: 1, + max: 9, + style: { "margin-right": "12px" } + }, null, 8, ["value"]), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.txt2img.batch_count, + "onUpdate:value": _cache[11] || (_cache[11] = ($event) => unref(settings).data.settings.txt2img.batch_count = $event), + size: "small", + style: { "min-width": "96px", "width": "96px" } + }, null, 8, ["value"]) + ]), + createVNode(_sfc_main$3, { + "batch-size-object": unref(settings).data.settings.txt2img + }, null, 8, ["batch-size-object"]), + createBaseVNode("div", _hoisted_12, [ + createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { + trigger: withCtx(() => [ + _hoisted_13 + ]), + default: withCtx(() => [ + createTextVNode(" Seed is a number that represents the starting canvas of your image. If you want to create the same image as your friend, you can use the same settings and seed to do so. "), + _hoisted_14 + ]), + _: 1 + }), + createVNode(unref(NInputNumber), { + value: unref(settings).data.settings.txt2img.seed, + "onUpdate:value": _cache[12] || (_cache[12] = ($event) => unref(settings).data.settings.txt2img.seed = $event), + size: "small", + style: { "flex-grow": "1" } + }, null, 8, ["value"]) + ]) + ]; + }), _: 1 }) ]), @@ -368,76 +328,75 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ style: { "margin-top": "12px", "margin-bottom": "12px" } }, { default: withCtx(() => [ - createBaseVNode("div", _hoisted_23, [ - _hoisted_24, + createBaseVNode("div", _hoisted_15, [ + _hoisted_16, createVNode(unref(NSwitch), { value: unref(global).state.txt2img.highres, - "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(global).state.txt2img.highres = $event), - disabled: !isSelectedModelPyTorch.value - }, null, 8, ["value", "disabled"]) + "onUpdate:value": _cache[13] || (_cache[13] = ($event) => unref(global).state.txt2img.highres = $event) + }, null, 8, ["value"]) ]), - unref(global).state.txt2img.highres && isSelectedModelPyTorch.value ? (openBlock(), createBlock(unref(NSpace), { + unref(global).state.txt2img.highres ? (openBlock(), createBlock(unref(NSpace), { key: 0, vertical: "", class: "left-container" }, { default: withCtx(() => [ - createBaseVNode("div", _hoisted_25, [ + createBaseVNode("div", _hoisted_17, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ - _hoisted_26 + _hoisted_18 ]), default: withCtx(() => [ createTextVNode(" Number of steps to take in the diffusion process. Higher values will result in more detailed images but will take longer to generate. There is also a point of diminishing returns around 100 steps. "), - _hoisted_27 + _hoisted_19 ]), _: 1 }), createVNode(unref(NSlider), { - value: unref(conf).data.settings.extra.highres.steps, - "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(conf).data.settings.extra.highres.steps = $event), + value: unref(settings).data.settings.extra.highres.steps, + "onUpdate:value": _cache[14] || (_cache[14] = ($event) => unref(settings).data.settings.extra.highres.steps = $event), min: 5, max: 300, style: { "margin-right": "12px" } }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.extra.highres.steps, - "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(conf).data.settings.extra.highres.steps = $event), + value: unref(settings).data.settings.extra.highres.steps, + "onUpdate:value": _cache[15] || (_cache[15] = ($event) => unref(settings).data.settings.extra.highres.steps = $event), size: "small", style: { "min-width": "96px", "width": "96px" } }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_28, [ - _hoisted_29, + createBaseVNode("div", _hoisted_20, [ + _hoisted_21, createVNode(unref(NSlider), { - value: unref(conf).data.settings.extra.highres.scale, - "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(conf).data.settings.extra.highres.scale = $event), + value: unref(settings).data.settings.extra.highres.scale, + "onUpdate:value": _cache[16] || (_cache[16] = ($event) => unref(settings).data.settings.extra.highres.scale = $event), min: 1, max: 8, step: 0.1, style: { "margin-right": "12px" } }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.extra.highres.scale, - "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(conf).data.settings.extra.highres.scale = $event), + value: unref(settings).data.settings.extra.highres.scale, + "onUpdate:value": _cache[17] || (_cache[17] = ($event) => unref(settings).data.settings.extra.highres.scale = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, step: 0.1 }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_30, [ - _hoisted_31, + createBaseVNode("div", _hoisted_22, [ + _hoisted_23, createVNode(unref(NSlider), { - value: unref(conf).data.settings.extra.highres.strength, - "onUpdate:value": _cache[20] || (_cache[20] = ($event) => unref(conf).data.settings.extra.highres.strength = $event), + value: unref(settings).data.settings.extra.highres.strength, + "onUpdate:value": _cache[18] || (_cache[18] = ($event) => unref(settings).data.settings.extra.highres.strength = $event), min: 0.1, max: 0.9, step: 0.05, style: { "margin-right": "12px" } }, null, 8, ["value"]), createVNode(unref(NInputNumber), { - value: unref(conf).data.settings.extra.highres.strength, - "onUpdate:value": _cache[21] || (_cache[21] = ($event) => unref(conf).data.settings.extra.highres.strength = $event), + value: unref(settings).data.settings.extra.highres.strength, + "onUpdate:value": _cache[19] || (_cache[19] = ($event) => unref(settings).data.settings.extra.highres.strength = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, min: 0.1, @@ -445,18 +404,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ step: 0.05 }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_32, [ - _hoisted_33, + createBaseVNode("div", _hoisted_24, [ + _hoisted_25, createVNode(unref(NSwitch), { - value: unref(conf).data.settings.extra.highres.antialiased, - "onUpdate:value": _cache[22] || (_cache[22] = ($event) => unref(conf).data.settings.extra.highres.antialiased = $event) + value: unref(settings).data.settings.extra.highres.antialiased, + "onUpdate:value": _cache[20] || (_cache[20] = ($event) => unref(settings).data.settings.extra.highres.antialiased = $event) }, null, 8, ["value"]) ]), - createBaseVNode("div", _hoisted_34, [ - _hoisted_35, + createBaseVNode("div", _hoisted_26, [ + _hoisted_27, createVNode(unref(NSelect), { - value: unref(conf).data.settings.extra.highres.latent_scale_mode, - "onUpdate:value": _cache[23] || (_cache[23] = ($event) => unref(conf).data.settings.extra.highres.latent_scale_mode = $event), + value: unref(settings).data.settings.extra.highres.latent_scale_mode, + "onUpdate:value": _cache[21] || (_cache[21] = ($event) => unref(settings).data.settings.extra.highres.latent_scale_mode = $event), size: "small", style: { "flex-grow": "1" }, options: [ @@ -487,13 +446,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }), createVNode(unref(NGi), null, { default: withCtx(() => [ - createVNode(_sfc_main$3, { generate }), - createVNode(_sfc_main$4, { + createVNode(_sfc_main$4, { generate }), + createVNode(_sfc_main$5, { "current-image": unref(global).state.txt2img.currentImage, images: unref(global).state.txt2img.images, - onImageClicked: _cache[24] || (_cache[24] = ($event) => unref(global).state.txt2img.currentImage = $event) - }, null, 8, ["current-image", "images"]), - createVNode(_sfc_main$5, { + data: unref(settings).data.settings.txt2img, + onImageClicked: _cache[22] || (_cache[22] = ($event) => unref(global).state.txt2img.currentImage = $event) + }, null, 8, ["current-image", "images", "data"]), + createVNode(_sfc_main$6, { style: { "margin-top": "12px" }, "gen-data": unref(global).state.txt2img.genData }, null, 8, ["gen-data"]) diff --git a/frontend/dist/assets/TrashBin.js b/frontend/dist/assets/TrashBin.js index 4136c55b8..4e4a4d9cf 100644 --- a/frontend/dist/assets/TrashBin.js +++ b/frontend/dist/assets/TrashBin.js @@ -1,4 +1,4 @@ -import { P as replaceable, D as h, d as defineComponent, a$ as isBrowser, a5 as useTheme, V as createInjectionKey, X as c, Y as cB, bE as fadeInTransition, aW as fadeInScaleUpTransition, aq as cNotM, U as toRef, bF as imageLight, E as ref, ar as useLocale, K as watch, aH as onBeforeUnmount, aI as off, a3 as inject, c as computed, Q as useConfig, a9 as useThemeClass, bG as isMounted, bH as LazyTeleport, bp as withDirectives, bI as zindexable, aX as Transition, J as Fragment, au as NBaseIcon, bq as vShow, ac as on, ba as normalizeStyle, bJ as kebabCase, q as NTooltip, aV as beforeNextFrameOnce, aa as createId, T as provide, bw as getCurrentInstance, b9 as onMounted, as as watchEffect, e as openBlock, f as createElementBlock, n as createBaseVNode } from "./index.js"; +import { O as replaceable, C as h, d as defineComponent, bL as isBrowser, T as useTheme, P as createInjectionKey, aa as c, Q as cB, bM as fadeInTransition, aS as fadeInScaleUpTransition, ac as cNotM, V as toRef, bN as imageLight, D as ref, ad as useLocale, J as watch, aB as onBeforeUnmount, aC as off, R as inject, c as computed, S as useConfig, W as useThemeClass, bO as isMounted, bP as LazyTeleport, br as withDirectives, bQ as zindexable, aX as Transition, I as Fragment, ai as NBaseIcon, bs as vShow, aD as on, ba as normalizeStyle, bR as kebabCase, q as NTooltip, aR as beforeNextFrameOnce, aW as createId, a3 as provide, by as getCurrentInstance, b9 as onMounted, af as watchEffect, e as openBlock, f as createElementBlock, n as createBaseVNode } from "./index.js"; const RotateClockwiseIcon = replaceable("rotateClockwise", h( "svg", { viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, @@ -113,8 +113,173 @@ const observeIntersection = (el, options, shouldStartLoadingRef) => { shouldStartLoadingRefMap.set(el, shouldStartLoadingRef); return unobserve; }; -const imagePreviewSharedProps = Object.assign(Object.assign({}, useTheme.props), { showToolbar: { type: Boolean, default: true }, showToolbarTooltip: Boolean }); +const imagePreviewSharedProps = Object.assign(Object.assign({}, useTheme.props), { onPreviewPrev: Function, onPreviewNext: Function, showToolbar: { type: Boolean, default: true }, showToolbarTooltip: Boolean }); const imageContextKey = createInjectionKey("n-image"); +var __awaiter = globalThis && globalThis.__awaiter || function(thisArg, _arguments, P, generator) { + function adopt(value) { + return value instanceof P ? value : new P(function(resolve) { + resolve(value); + }); + } + return new (P || (P = Promise))(function(resolve, reject) { + function fulfilled(value) { + try { + step(generator.next(value)); + } catch (e) { + reject(e); + } + } + function rejected(value) { + try { + step(generator["throw"](value)); + } catch (e) { + reject(e); + } + } + function step(result) { + result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); + } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const isImageFileType = (type) => type.includes("image/"); +const getExtname = (url = "") => { + const temp = url.split("/"); + const filename = temp[temp.length - 1]; + const filenameWithoutSuffix = filename.split(/#|\?/)[0]; + return (/\.[^./\\]*$/.exec(filenameWithoutSuffix) || [""])[0]; +}; +const imageExtensionRegex = /(webp|svg|png|gif|jpg|jpeg|jfif|bmp|dpg|ico)$/i; +const isImageFile = (file) => { + if (file.type) { + return isImageFileType(file.type); + } + const fileNameExtension = getExtname(file.name || ""); + if (imageExtensionRegex.test(fileNameExtension)) { + return true; + } + const url = file.thumbnailUrl || file.url || ""; + const urlExtension = getExtname(url); + if (/^data:image\//.test(url) || imageExtensionRegex.test(urlExtension)) { + return true; + } + return false; +}; +function createImageDataUrl(file) { + return __awaiter(this, void 0, void 0, function* () { + return yield new Promise((resolve) => { + if (!file.type || !isImageFileType(file.type)) { + resolve(""); + return; + } + resolve(window.URL.createObjectURL(file)); + }); + }); +} +const environmentSupportFile = isBrowser && window.FileReader && window.File; +function isFileSystemDirectoryEntry(item) { + return item.isDirectory; +} +function isFileSystemFileEntry(item) { + return item.isFile; +} +function getFilesFromEntries(entries, directory) { + return __awaiter(this, void 0, void 0, function* () { + const fileAndEntries = []; + let _resolve; + let requestCallbackCount = 0; + function lock() { + requestCallbackCount++; + } + function unlock() { + requestCallbackCount--; + if (!requestCallbackCount) { + _resolve(fileAndEntries); + } + } + function _getFilesFromEntries(entries2) { + entries2.forEach((entry) => { + if (!entry) + return; + lock(); + if (directory && isFileSystemDirectoryEntry(entry)) { + const directoryReader = entry.createReader(); + lock(); + directoryReader.readEntries((entries3) => { + _getFilesFromEntries(entries3); + unlock(); + }, () => { + unlock(); + }); + } else if (isFileSystemFileEntry(entry)) { + lock(); + entry.file((file) => { + fileAndEntries.push({ file, entry, source: "dnd" }); + unlock(); + }, () => { + unlock(); + }); + } + unlock(); + }); + } + yield new Promise((resolve) => { + _resolve = resolve; + _getFilesFromEntries(entries); + }); + return fileAndEntries; + }); +} +function createSettledFileInfo(fileInfo) { + const { id, name, percentage, status, url, file, thumbnailUrl, type, fullPath, batchId } = fileInfo; + return { + id, + name, + percentage: percentage !== null && percentage !== void 0 ? percentage : null, + status, + url: url !== null && url !== void 0 ? url : null, + file: file !== null && file !== void 0 ? file : null, + thumbnailUrl: thumbnailUrl !== null && thumbnailUrl !== void 0 ? thumbnailUrl : null, + type: type !== null && type !== void 0 ? type : null, + fullPath: fullPath !== null && fullPath !== void 0 ? fullPath : null, + batchId: batchId !== null && batchId !== void 0 ? batchId : null + }; +} +function matchType(name, mimeType, accept) { + name = name.toLowerCase(); + mimeType = mimeType.toLocaleLowerCase(); + accept = accept.toLocaleLowerCase(); + const acceptAtoms = accept.split(",").map((acceptAtom) => acceptAtom.trim()).filter(Boolean); + return acceptAtoms.some((acceptAtom) => { + if (acceptAtom.startsWith(".")) { + if (name.endsWith(acceptAtom)) + return true; + } else if (acceptAtom.includes("/")) { + const [type, subtype] = mimeType.split("/"); + const [acceptType, acceptSubtype] = acceptAtom.split("/"); + if (acceptType === "*" || type && acceptType && acceptType === type) { + if (acceptSubtype === "*" || subtype && acceptSubtype && acceptSubtype === subtype) { + return true; + } + } + } else { + return true; + } + return false; + }); +} +const download = (url, name) => { + if (!url) + return; + const a = document.createElement("a"); + a.href = url; + if (name !== void 0) { + a.download = name; + } + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); +}; const prevIcon = h( "svg", { viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, @@ -130,6 +295,11 @@ const closeIcon = h( { viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M4.089 4.216l.057-.07a.5.5 0 0 1 .638-.057l.07.057L10 9.293l5.146-5.147a.5.5 0 0 1 .638-.057l.07.057a.5.5 0 0 1 .057.638l-.057.07L10.707 10l5.147 5.146a.5.5 0 0 1 .057.638l-.057.07a.5.5 0 0 1-.638.057l-.07-.057L10 10.707l-5.146 5.147a.5.5 0 0 1-.638.057l-.07-.057a.5.5 0 0 1-.057-.638l.057-.07L9.293 10L4.146 4.854a.5.5 0 0 1-.057-.638l.057-.07l-.057.07z", fill: "currentColor" }) ); +const downloadIcon = h( + "svg", + { xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 1024 1024" }, + h("path", { fill: "currentColor", d: "M505.7 661a8 8 0 0 0 12.6 0l112-141.7c4.1-5.2.4-12.9-6.3-12.9h-74.1V168c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v338.3H400c-6.7 0-10.4 7.7-6.3 12.9l112 141.8zM878 626h-60c-4.4 0-8 3.6-8 8v154H214V634c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v198c0 17.7 14.3 32 32 32h684c17.7 0 32-14.3 32-32V634c0-4.4-3.6-8-8-8z" }) +); const style = c([c("body >", [cB("image-container", "position: fixed;")]), cB("image-preview-container", ` position: fixed; left: 0; @@ -418,6 +588,12 @@ const NImagePreview = defineComponent({ derivePreviewStyle(); } } + function handleDownloadClick() { + const src = previewSrcRef.value; + if (src) { + download(src, void 0); + } + } function derivePreviewStyle(transition = true) { var _a; const { value: preview } = previewRef; @@ -514,6 +690,7 @@ const NImagePreview = defineComponent({ }, zoomIn, zoomOut, + handleDownloadClick, rotateCounterclockwise, rotateClockwise, handleSwitchPrev, @@ -575,6 +752,7 @@ const NImagePreview = defineComponent({ }), "tipOriginalSize"), withTooltip(h(NBaseIcon, { clsPrefix, onClick: this.zoomOut }, { default: () => h(ZoomOutIcon, null) }), "tipZoomOut"), withTooltip(h(NBaseIcon, { clsPrefix, onClick: this.zoomIn }, { default: () => h(ZoomInIcon, null) }), "tipZoomIn"), + withTooltip(h(NBaseIcon, { clsPrefix, onClick: this.handleDownloadClick }, { default: () => downloadIcon }), "tipDownload"), withTooltip(h(NBaseIcon, { clsPrefix, onClick: this.toggleShow }, { default: () => closeIcon }), "tipClose") ); } @@ -623,6 +801,7 @@ const NImageGroup = defineComponent({ (_a = previewInstRef.value) === null || _a === void 0 ? void 0 : _a.setPreviewSrc(src); }; function go(step) { + var _a, _b; if (!(vm === null || vm === void 0 ? void 0 : vm.proxy)) return; const container = vm.proxy.$el.parentElement; @@ -635,6 +814,7 @@ const NImageGroup = defineComponent({ } else { setPreviewSrc(imgs[0].dataset.previewSrc); } + step === 1 ? (_a = props.onPreviewNext) === null || _a === void 0 ? void 0 : _a.call(props) : (_b = props.onPreviewPrev) === null || _b === void 0 ? void 0 : _b.call(props); } provide(imageGroupInjectionKey, { mergedClsPrefixRef, @@ -835,5 +1015,12 @@ const TrashBin = defineComponent({ export { NImage as N, TrashBin as T, - NImageGroup as a + NImageGroup as a, + createImageDataUrl as b, + createSettledFileInfo as c, + download as d, + environmentSupportFile as e, + getFilesFromEntries as g, + isImageFile as i, + matchType as m }; diff --git a/frontend/dist/assets/clock.js b/frontend/dist/assets/clock.js index 012d5af39..65958e757 100644 --- a/frontend/dist/assets/clock.js +++ b/frontend/dist/assets/clock.js @@ -5,8 +5,8 @@ var __publicField = (obj, key, value) => { return value; }; import { N as NDescriptionsItem, a as NDescriptions } from "./DescriptionsItem.js"; -import { d as defineComponent, e as openBlock, v as createBlock, w as withCtx, g as createVNode, h as unref, m as createTextVNode, t as toDisplayString, i as NCard, x as createCommentVNode, a as useSettings, f as createElementBlock, q as NTooltip, n as createBaseVNode, J as Fragment, K as watch, E as ref, s as serverUrl } from "./index.js"; -import { N as NSlider } from "./Slider.js"; +import { d as defineComponent, e as openBlock, v as createBlock, w as withCtx, g as createVNode, h as unref, m as createTextVNode, t as toDisplayString, i as NCard, r as createCommentVNode, a as useSettings, f as createElementBlock, q as NTooltip, n as createBaseVNode, I as Fragment, J as watch, D as ref, s as serverUrl } from "./index.js"; +import { N as NSlider } from "./Switch.js"; import { N as NInputNumber } from "./InputNumber.js"; const _sfc_main$2 = /* @__PURE__ */ defineComponent({ __name: "OutputStats", @@ -66,9 +66,9 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ }, setup(__props) { const props = __props; - const conf = useSettings(); + const settings = useSettings(); return (_ctx, _cache) => { - return unref(conf).data.settings.aitDim.batch_size ? (openBlock(), createElementBlock("div", _hoisted_1$1, [ + return unref(settings).data.settings.aitDim.batch_size ? (openBlock(), createElementBlock("div", _hoisted_1$1, [ createVNode(unref(NTooltip), { style: { "max-width": "600px" } }, { trigger: withCtx(() => [ _hoisted_2$1 @@ -81,16 +81,16 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({ createVNode(unref(NSlider), { value: props.batchSizeObject.batch_size, "onUpdate:value": _cache[0] || (_cache[0] = ($event) => props.batchSizeObject.batch_size = $event), - min: unref(conf).data.settings.aitDim.batch_size[0], - max: unref(conf).data.settings.aitDim.batch_size[1], + min: unref(settings).data.settings.aitDim.batch_size[0], + max: unref(settings).data.settings.aitDim.batch_size[1], style: { "margin-right": "12px" } }, null, 8, ["value", "min", "max"]), createVNode(unref(NInputNumber), { value: props.batchSizeObject.batch_size, "onUpdate:value": _cache[1] || (_cache[1] = ($event) => props.batchSizeObject.batch_size = $event), size: "small", - min: unref(conf).data.settings.aitDim.batch_size[0], - max: unref(conf).data.settings.aitDim.batch_size[1], + min: unref(settings).data.settings.aitDim.batch_size[0], + max: unref(settings).data.settings.aitDim.batch_size[1], style: { "min-width": "96px", "width": "96px" } }, null, 8, ["value", "min", "max"]) ])) : (openBlock(), createElementBlock("div", _hoisted_3$1, [ @@ -150,16 +150,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ }, setup(__props) { const props = __props; - const conf = useSettings(); + const settings = useSettings(); return (_ctx, _cache) => { return openBlock(), createElementBlock(Fragment, null, [ - unref(conf).data.settings.aitDim.width ? (openBlock(), createElementBlock("div", _hoisted_1, [ + unref(settings).data.settings.aitDim.width ? (openBlock(), createElementBlock("div", _hoisted_1, [ _hoisted_2, createVNode(unref(NSlider), { value: props.dimensionsObject.width, "onUpdate:value": _cache[0] || (_cache[0] = ($event) => props.dimensionsObject.width = $event), - min: unref(conf).data.settings.aitDim.width[0], - max: unref(conf).data.settings.aitDim.width[1], + min: unref(settings).data.settings.aitDim.width[0], + max: unref(settings).data.settings.aitDim.width[1], step: 64, style: { "margin-right": "12px" } }, null, 8, ["value", "min", "max"]), @@ -168,8 +168,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ "onUpdate:value": _cache[1] || (_cache[1] = ($event) => props.dimensionsObject.width = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, - min: unref(conf).data.settings.aitDim.width[0], - max: unref(conf).data.settings.aitDim.width[1], + min: unref(settings).data.settings.aitDim.width[0], + max: unref(settings).data.settings.aitDim.width[1], step: 64 }, null, 8, ["value", "min", "max"]) ])) : (openBlock(), createElementBlock("div", _hoisted_3, [ @@ -190,13 +190,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ step: 1 }, null, 8, ["value"]) ])), - unref(conf).data.settings.aitDim.height ? (openBlock(), createElementBlock("div", _hoisted_5, [ + unref(settings).data.settings.aitDim.height ? (openBlock(), createElementBlock("div", _hoisted_5, [ _hoisted_6, createVNode(unref(NSlider), { value: props.dimensionsObject.height, "onUpdate:value": _cache[4] || (_cache[4] = ($event) => props.dimensionsObject.height = $event), - min: unref(conf).data.settings.aitDim.height[0], - max: unref(conf).data.settings.aitDim.height[1], + min: unref(settings).data.settings.aitDim.height[0], + max: unref(settings).data.settings.aitDim.height[1], step: 64, style: { "margin-right": "12px" } }, null, 8, ["value", "min", "max"]), @@ -205,8 +205,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({ "onUpdate:value": _cache[5] || (_cache[5] = ($event) => props.dimensionsObject.height = $event), size: "small", style: { "min-width": "96px", "width": "96px" }, - min: unref(conf).data.settings.aitDim.height[0], - max: unref(conf).data.settings.aitDim.height[1], + min: unref(settings).data.settings.aitDim.height[0], + max: unref(settings).data.settings.aitDim.height[1], step: 64 }, null, 8, ["value", "min", "max"]) ])) : (openBlock(), createElementBlock("div", _hoisted_7, [ diff --git a/frontend/dist/assets/index.css b/frontend/dist/assets/index.css index 17b1d102c..48eb30289 100644 --- a/frontend/dist/assets/index.css +++ b/frontend/dist/assets/index.css @@ -7,15 +7,14 @@ z-index: 2; } -.progress-container[data-v-2cf1de9c] { +.progress-container[data-v-44d84e0e] { margin: 12px; flex-grow: 1; width: 400px; } -.top-bar[data-v-2cf1de9c] { +.top-bar[data-v-44d84e0e] { display: inline-flex; align-items: center; - border-bottom: #505050 1px solid; padding-top: 10px; padding-bottom: 10px; width: calc(100% - 64px); @@ -23,16 +22,11 @@ position: fixed; top: 0; z-index: 1; - background-color: var(--67e22a10); } -.logo[data-v-2cf1de9c] { +.logo[data-v-44d84e0e] { margin-right: 16px; margin-left: 16px; } - -.main { - background-color: var(--16223ad6); -} .autocomplete { position: relative; display: inline-block; @@ -40,24 +34,53 @@ .autocomplete-items { position: absolute; z-index: 99; - background-color: var(--1fedac06); - border-radius: var(--ba9033c6); + background-color: var(--2441c648); + border-radius: var(--521efb30); padding: 2px; } .autocomplete-items div { padding: 8px; cursor: pointer; - border-radius: var(--ba9033c6); + border-radius: var(--521efb30); } .autocomplete-active { - background-color: var(--3119c2a0) !important; - color: var(--aca37748) !important; + background-color: var(--65525eeb); + color: var(--0c729ef1); +} +#autocomplete-list { + max-height: min(600px, 70vh); + overflow-y: auto; +} +.n-card { + backdrop-filter: var(--15a84ddb); +} +.navbar .n-layout { + backdrop-filter: var(--15a84ddb); +} +.navbar .n-layout-toggle-button { + backdrop-filter: var(--15a84ddb); } -body { - min-height: 100vh; +.top-bar { + backdrop-filter: var(--15a84ddb); + background-color: var(--2259b162); +} +.navbar { + backdrop-filter: var(--15a84ddb); +} +#background { + width: 100vw; + height: 100vh; + position: fixed; + background-image: var(--f8e7ba4e); + background-size: cover; + background-position: center; + background-attachment: fixed; + top: 0; + left: 0; + z-index: -99; +}body { margin-left: 64px; color: #fff; - background-color: rgb(18, 18, 21); font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; diff --git a/frontend/dist/assets/index.js b/frontend/dist/assets/index.js index a3a112bf9..c470004b0 100644 --- a/frontend/dist/assets/index.js +++ b/frontend/dist/assets/index.js @@ -1552,7 +1552,7 @@ function renderComponentRoot(instance) { slots, attrs, emit: emit2, - render: render16, + render: render15, renderCache, data, setupState, @@ -1566,7 +1566,7 @@ function renderComponentRoot(instance) { if (vnode.shapeFlag & 4) { const proxyToUse = withProxy || proxy; result = normalizeVNode( - render16.call( + render15.call( proxyToUse, proxyToUse, renderCache, @@ -2396,6 +2396,13 @@ function resolveComponent(name, maybeSelfReference) { return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name; } const NULL_DYNAMIC_COMPONENT = Symbol.for("v-ndc"); +function resolveDynamicComponent(component) { + if (isString$1(component)) { + return resolveAsset(COMPONENTS, component, false) || component; + } else { + return component || NULL_DYNAMIC_COMPONENT; + } +} function resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) { const instance = currentRenderingInstance || currentInstance; if (instance) { @@ -2661,7 +2668,7 @@ function applyOptions(instance) { beforeUnmount, destroyed, unmounted, - render: render16, + render: render15, renderTracked, renderTriggered, errorCaptured, @@ -2760,8 +2767,8 @@ function applyOptions(instance) { instance.exposed = {}; } } - if (render16 && instance.render === NOOP) { - instance.render = render16; + if (render15 && instance.render === NOOP) { + instance.render = render15; } if (inheritAttrs != null) { instance.inheritAttrs = inheritAttrs; @@ -2993,7 +3000,7 @@ function createAppContext() { }; } let uid$1 = 0; -function createAppAPI(render16, hydrate) { +function createAppAPI(render15, hydrate) { return function createApp2(rootComponent, rootProps = null) { if (!isFunction$2(rootComponent)) { rootComponent = extend({}, rootComponent); @@ -3062,7 +3069,7 @@ function createAppAPI(render16, hydrate) { if (isHydrate && hydrate) { hydrate(vnode, rootContainer); } else { - render16(vnode, rootContainer, isSVG2); + render15(vnode, rootContainer, isSVG2); } isMounted2 = true; app2._container = rootContainer; @@ -3072,7 +3079,7 @@ function createAppAPI(render16, hydrate) { }, unmount() { if (isMounted2) { - render16(null, app2._container); + render15(null, app2._container); delete app2._container.__vue_app__; } }, @@ -4787,7 +4794,7 @@ function baseCreateRenderer(options, createHydrationFns) { } return hostNextSibling(vnode.anchor || vnode.el); }; - const render16 = (vnode, container, isSVG2) => { + const render15 = (vnode, container, isSVG2) => { if (vnode == null) { if (container._vnode) { unmount2(container._vnode, null, null, true); @@ -4819,9 +4826,9 @@ function baseCreateRenderer(options, createHydrationFns) { ); } return { - render: render16, + render: render15, hydrate, - createApp: createAppAPI(render16, hydrate) + createApp: createAppAPI(render15, hydrate) }; } function toggleRecurse({ effect, update }, allowed) { @@ -6607,12 +6614,12 @@ function normalizeContainer(container) { } var isVue2 = false; /*! - * pinia v2.1.3 - * (c) 2023 Eduardo San Martin Morote - * @license MIT - */ + * pinia v2.1.7 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */ let activePinia; -const setActivePinia = (pinia2) => activePinia = pinia2; +const setActivePinia = (pinia) => activePinia = pinia; const piniaSymbol = ( /* istanbul ignore next */ Symbol() @@ -6631,13 +6638,13 @@ function createPinia() { const state = scope.run(() => ref({})); let _p = []; let toBeInstalled = []; - const pinia2 = markRaw({ + const pinia = markRaw({ install(app2) { - setActivePinia(pinia2); + setActivePinia(pinia); { - pinia2._a = app2; - app2.provide(piniaSymbol, pinia2); - app2.config.globalProperties.$pinia = pinia2; + pinia._a = app2; + app2.provide(piniaSymbol, pinia); + app2.config.globalProperties.$pinia = pinia; toBeInstalled.forEach((plugin2) => _p.push(plugin2)); toBeInstalled = []; } @@ -6658,7 +6665,7 @@ function createPinia() { _s: /* @__PURE__ */ new Map(), state }); - return pinia2; + return pinia; } const noop$2 = () => { }; @@ -6713,30 +6720,30 @@ const { assign: assign$1 } = Object; function isComputed(o) { return !!(isRef(o) && o.effect); } -function createOptionsStore(id, options, pinia2, hot) { +function createOptionsStore(id, options, pinia, hot) { const { state, actions, getters } = options; - const initialState = pinia2.state.value[id]; + const initialState = pinia.state.value[id]; let store; function setup() { if (!initialState && true) { { - pinia2.state.value[id] = state ? state() : {}; + pinia.state.value[id] = state ? state() : {}; } } - const localState = toRefs(pinia2.state.value[id]); + const localState = toRefs(pinia.state.value[id]); return assign$1(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => { computedGetters[name] = markRaw(computed(() => { - setActivePinia(pinia2); - const store2 = pinia2._s.get(id); + setActivePinia(pinia); + const store2 = pinia._s.get(id); return getters[name].call(store2, store2); })); return computedGetters; }, {})); } - store = createSetupStore(id, setup, options, pinia2, hot, true); + store = createSetupStore(id, setup, options, pinia, hot, true); return store; } -function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) { +function createSetupStore($id, setup, options = {}, pinia, hot, isOptionsStore) { let scope; const optionsForPlugin = assign$1({ actions: {} }, options); const $subscribeOptions = { @@ -6748,10 +6755,10 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) let subscriptions = []; let actionSubscriptions = []; let debuggerEvents; - const initialState = pinia2.state.value[$id]; + const initialState = pinia.state.value[$id]; if (!isOptionsStore && !initialState && true) { { - pinia2.state.value[$id] = {}; + pinia.state.value[$id] = {}; } } ref({}); @@ -6760,14 +6767,14 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) let subscriptionMutation; isListening = isSyncListening = false; if (typeof partialStateOrMutator === "function") { - partialStateOrMutator(pinia2.state.value[$id]); + partialStateOrMutator(pinia.state.value[$id]); subscriptionMutation = { type: MutationType.patchFunction, storeId: $id, events: debuggerEvents }; } else { - mergeReactiveObjects(pinia2.state.value[$id], partialStateOrMutator); + mergeReactiveObjects(pinia.state.value[$id], partialStateOrMutator); subscriptionMutation = { type: MutationType.patchObject, payload: partialStateOrMutator, @@ -6782,7 +6789,7 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) } }); isSyncListening = true; - triggerSubscriptions(subscriptions, subscriptionMutation, pinia2.state.value[$id]); + triggerSubscriptions(subscriptions, subscriptionMutation, pinia.state.value[$id]); } const $reset = isOptionsStore ? function $reset2() { const { state } = options; @@ -6798,11 +6805,11 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) scope.stop(); subscriptions = []; actionSubscriptions = []; - pinia2._s.delete($id); + pinia._s.delete($id); } function wrapAction(name, action) { return function() { - setActivePinia(pinia2); + setActivePinia(pinia); const args = Array.from(arguments); const afterCallbackList = []; const onErrorCallbackList = []; @@ -6840,7 +6847,7 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) }; } const partialStore = { - _p: pinia2, + _p: pinia, // _s: scope, $id, $onAction: addSubscription.bind(null, actionSubscriptions), @@ -6848,7 +6855,7 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) $reset, $subscribe(callback, options2 = {}) { const removeSubscription = addSubscription(subscriptions, callback, options2.detached, () => stopWatcher()); - const stopWatcher = scope.run(() => watch(() => pinia2.state.value[$id], (state) => { + const stopWatcher = scope.run(() => watch(() => pinia.state.value[$id], (state) => { if (options2.flush === "sync" ? isSyncListening : isListening) { callback({ storeId: $id, @@ -6862,12 +6869,9 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) $dispose }; const store = reactive(partialStore); - pinia2._s.set($id, store); - const runWithContext = pinia2._a && pinia2._a.runWithContext || fallbackRunWithContext; - const setupStore = pinia2._e.run(() => { - scope = effectScope(); - return runWithContext(() => scope.run(setup)); - }); + pinia._s.set($id, store); + const runWithContext = pinia._a && pinia._a.runWithContext || fallbackRunWithContext; + const setupStore = runWithContext(() => pinia._e.run(() => (scope = effectScope()).run(setup))); for (const key in setupStore) { const prop = setupStore[key]; if (isRef(prop) && !isComputed(prop) || isReactive(prop)) { @@ -6880,7 +6884,7 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) } } { - pinia2.state.value[$id][key] = prop; + pinia.state.value[$id][key] = prop; } } } else if (typeof prop === "function") { @@ -6897,19 +6901,19 @@ function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) assign$1(toRaw(store), setupStore); } Object.defineProperty(store, "$state", { - get: () => pinia2.state.value[$id], + get: () => pinia.state.value[$id], set: (state) => { $patch(($state) => { assign$1($state, state); }); } }); - pinia2._p.forEach((extender) => { + pinia._p.forEach((extender) => { { assign$1(store, scope.run(() => extender({ store, - app: pinia2._a, - pinia: pinia2, + app: pinia._a, + pinia, options: optionsForPlugin }))); } @@ -6932,27 +6936,29 @@ function defineStore(idOrOptions, setup, setupOptions) { options = idOrOptions; id = idOrOptions.id; } - function useStore(pinia2, hot) { + function useStore(pinia, hot) { const hasContext = hasInjectionContext(); - pinia2 = // in test mode, ignore the argument provided as we can always retrieve a + pinia = // in test mode, ignore the argument provided as we can always retrieve a // pinia instance with getActivePinia() - pinia2 || (hasContext ? inject(piniaSymbol, null) : null); - if (pinia2) - setActivePinia(pinia2); - pinia2 = activePinia; - if (!pinia2._s.has(id)) { + pinia || (hasContext ? inject(piniaSymbol, null) : null); + if (pinia) + setActivePinia(pinia); + pinia = activePinia; + if (!pinia._s.has(id)) { if (isSetupStore) { - createSetupStore(id, setup, options, pinia2); + createSetupStore(id, setup, options, pinia); } else { - createOptionsStore(id, options, pinia2); + createOptionsStore(id, options, pinia); } } - const store = pinia2._s.get(id); + const store = pinia._s.get(id); return store; } useStore.$id = id; return useStore; } +const themeOverridesKey = Symbol("themeOverrides"); +const themeKey = Symbol("theme"); let onceCbs = []; const paramsMap = /* @__PURE__ */ new WeakMap(); function flushOnceCallbacks() { @@ -9824,7 +9830,7 @@ function getOffset(placement, offsetRect, targetRect, offsetTopToStandardPlaceme }; } } -const style$A = c([ +const style$C = c([ c(".v-binder-follower-container", { position: "absolute", left: "0", @@ -9906,7 +9912,7 @@ const VFollower = defineComponent({ } }); const ssrAdapter2 = useSsrAdapter(); - style$A.mount({ + style$C.mount({ id: "vueuc/binder", head: true, anchorMetaName: cssrAnchorMetaName$1, @@ -11101,7 +11107,7 @@ const VXScroll = defineComponent({ } }); const hiddenAttr = "v-hidden"; -const style$z = c("[v-hidden]", { +const style$B = c("[v-hidden]", { display: "none!important" }); const VOverflow = defineComponent({ @@ -11191,7 +11197,7 @@ const VOverflow = defineComponent({ } } const ssrAdapter2 = useSsrAdapter(); - style$z.mount({ + style$B.mount({ id: "vueuc/overflow", head: true, anchorMetaName: cssrAnchorMetaName$1, @@ -13732,13 +13738,14 @@ function useConfig(props = {}, options = { return bordered; return (_b = (_a2 = NConfigProvider2 === null || NConfigProvider2 === void 0 ? void 0 : NConfigProvider2.mergedBorderedRef.value) !== null && _a2 !== void 0 ? _a2 : options.defaultBordered) !== null && _b !== void 0 ? _b : true; }), - mergedClsPrefixRef: computed(() => { - const clsPrefix = NConfigProvider2 === null || NConfigProvider2 === void 0 ? void 0 : NConfigProvider2.mergedClsPrefixRef.value; - return clsPrefix || defaultClsPrefix; - }), + mergedClsPrefixRef: NConfigProvider2 ? NConfigProvider2.mergedClsPrefixRef : shallowRef(defaultClsPrefix), namespaceRef: computed(() => NConfigProvider2 === null || NConfigProvider2 === void 0 ? void 0 : NConfigProvider2.mergedNamespaceRef.value) }; } +function useMergedClsPrefix() { + const NConfigProvider2 = inject(configProviderInjectionKey, null); + return NConfigProvider2 ? NConfigProvider2.mergedClsPrefixRef : shallowRef(defaultClsPrefix); +} const enUS = { name: "en-US", global: { @@ -13854,6 +13861,7 @@ const enUS = { tipClockwise: "Clockwise", tipZoomOut: "Zoom out", tipZoomIn: "Zoom in", + tipDownload: "Download", tipClose: "Close (Esc)", // TODO: translation tipOriginalSize: "Zoom to original size" @@ -14339,7 +14347,7 @@ function useStyle(mountId, style2, clsPrefixRef) { const ssrAdapter2 = useSsrAdapter(); const NConfigProvider2 = inject(configProviderInjectionKey, null); const mountStyle = () => { - const clsPrefix = clsPrefixRef === null || clsPrefixRef === void 0 ? void 0 : clsPrefixRef.value; + const clsPrefix = clsPrefixRef.value; style2.mount({ id: clsPrefix === void 0 ? mountId : clsPrefix + mountId, head: true, @@ -14364,6 +14372,12 @@ function useStyle(mountId, style2, clsPrefixRef) { onBeforeMount(mountStyle); } } +function useHljs(props, shouldHighlightRef) { + const NConfigProvider2 = inject(configProviderInjectionKey, null); + return computed(() => { + return props.hljs || (NConfigProvider2 === null || NConfigProvider2 === void 0 ? void 0 : NConfigProvider2.mergedHljsRef.value); + }); +} function useThemeClass(componentName, hashRef, cssVarsRef, props) { var _a2; if (!cssVarsRef) @@ -14751,7 +14765,7 @@ const NFadeInExpandTransition = defineComponent({ }; } }); -const style$y = cB("base-icon", ` +const style$A = cB("base-icon", ` height: 1em; width: 1em; line-height: 1em; @@ -14786,13 +14800,13 @@ const NBaseIcon = defineComponent({ onMouseup: Function }, setup(props) { - useStyle("-base-icon", style$y, toRef(props, "clsPrefix")); + useStyle("-base-icon", style$A, toRef(props, "clsPrefix")); }, render() { return h("i", { class: `${this.clsPrefix}-base-icon`, onClick: this.onClick, onMousedown: this.onMousedown, onMouseup: this.onMouseup, role: this.role, "aria-label": this.ariaLabel, "aria-hidden": this.ariaHidden, "aria-disabled": this.ariaDisabled }, this.$slots); } }); -const style$x = cB("base-close", ` +const style$z = cB("base-close", ` display: flex; align-items: center; justify-content: center; @@ -14861,7 +14875,7 @@ const NBaseClose = defineComponent({ absolute: Boolean }, setup(props) { - useStyle("-base-close", style$x, toRef(props, "clsPrefix")); + useStyle("-base-close", style$z, toRef(props, "clsPrefix")); return () => { const { clsPrefix, disabled, absolute, round, isButtonTag } = props; const Tag = isButtonTag ? "button" : "div"; @@ -14920,7 +14934,7 @@ function iconSwitchTransition({ transition })]; } -const style$w = c$1([c$1("@keyframes loading-container-rotate", ` +const style$y = c$1([c$1("@keyframes loading-container-rotate", ` to { -webkit-transform: rotate(360deg); transform: rotate(360deg); @@ -15083,7 +15097,7 @@ const NBaseLoading = defineComponent({ default: 100 } }, exposedLoadingProps), setup(props) { - useStyle("-base-loading", style$w, toRef(props, "clsPrefix")); + useStyle("-base-loading", style$y, toRef(props, "clsPrefix")); }, render() { const { clsPrefix, radius, strokeWidth, stroke, scale } = this; @@ -16113,7 +16127,7 @@ const emptyDark = { self: self$1d }; const emptyDark$1 = emptyDark; -const style$v = cB("empty", ` +const style$x = cB("empty", ` display: flex; flex-direction: column; align-items: center; @@ -16152,7 +16166,7 @@ const NEmpty = defineComponent({ props: emptyProps, setup(props) { const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Empty", "-empty", style$v, emptyLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Empty", "-empty", style$x, emptyLight$1, props, mergedClsPrefixRef); const { localeRef } = useLocale("Empty"); const NConfigProvider2 = inject(configProviderInjectionKey, null); const mergedDescriptionRef = computed(() => { @@ -16243,7 +16257,7 @@ function fadeInTransition({ opacity: 1 })]; } -const style$u = cB("scrollbar", ` +const style$w = cB("scrollbar", ` overflow: hidden; position: relative; z-index: auto; @@ -16253,6 +16267,7 @@ const style$u = cB("scrollbar", ` width: 100%; overflow: scroll; height: 100%; + min-height: inherit; max-height: inherit; scrollbar-width: none; `, [c$1("&::-webkit-scrollbar, &::-webkit-scrollbar-track-piece, &::-webkit-scrollbar-thumb", ` @@ -16766,7 +16781,7 @@ const Scrollbar$1 = defineComponent({ off("mousemove", window, handleYScrollMouseMove, true); off("mouseup", window, handleYScrollMouseUp, true); }); - const themeRef = useTheme("Scrollbar", "-scrollbar", style$u, scrollbarLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Scrollbar", "-scrollbar", style$w, scrollbarLight$1, props, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { common: { cubicBezierEaseInOut: cubicBezierEaseInOut2, scrollbarBorderRadius, scrollbarHeight, scrollbarWidth }, self: { color, colorHover } } = themeRef.value; return { @@ -16821,11 +16836,11 @@ const Scrollbar$1 = defineComponent({ if (!this.scrollable) return (_a2 = $slots.default) === null || _a2 === void 0 ? void 0 : _a2.call($slots); const triggerIsNone = this.trigger === "none"; - const createYRail = () => { + const createYRail = (style2) => { return h("div", { ref: "yRailRef", class: [ `${mergedClsPrefix}-scrollbar-rail`, `${mergedClsPrefix}-scrollbar-rail--vertical` - ], "data-scrollbar-rail": true, style: this.verticalRailStyle, "aria-hidden": true }, h(triggerIsNone ? Wrapper : Transition, triggerIsNone ? null : { name: "fade-in-transition" }, { + ], "data-scrollbar-rail": true, style: [style2 || "", this.verticalRailStyle], "aria-hiddens": true }, h(triggerIsNone ? Wrapper : Transition, triggerIsNone ? null : { name: "fade-in-transition" }, { default: () => this.needYBar && this.isShowYBar && !this.isIos ? h("div", { class: `${mergedClsPrefix}-scrollbar-rail__scrollbar`, style: { height: this.yBarSizePx, top: this.yBarTopPx @@ -16865,7 +16880,7 @@ const Scrollbar$1 = defineComponent({ ] }, $slots) }) ), - internalHoistYRail ? null : createYRail(), + internalHoistYRail ? null : createYRail(void 0), this.xScrollable && h("div", { ref: "xRailRef", class: [ `${mergedClsPrefix}-scrollbar-rail`, `${mergedClsPrefix}-scrollbar-rail--horizontal` @@ -16886,7 +16901,7 @@ const Scrollbar$1 = defineComponent({ Fragment, null, scrollbarNode, - createYRail() + createYRail(this.cssVars) ); } else { return scrollbarNode; @@ -17112,7 +17127,7 @@ function fadeInScaleUpTransition({ transform: `${originalTransform} scale(1)` })]; } -const style$t = cB("base-select-menu", ` +const style$v = cB("base-select-menu", ` line-height: 1.5; outline: none; z-index: 0; @@ -17281,7 +17296,7 @@ const NInternalSelectMenu = defineComponent({ onToggle: Function }), setup(props) { - const themeRef = useTheme("InternalSelectMenu", "-internal-select-menu", style$t, internalSelectMenuLight$1, props, toRef(props, "clsPrefix")); + const themeRef = useTheme("InternalSelectMenu", "-internal-select-menu", style$v, internalSelectMenuLight$1, props, toRef(props, "clsPrefix")); const selfRef = ref(null); const virtualListRef = ref(null); const scrollbarRef = ref(null); @@ -17570,7 +17585,7 @@ const NInternalSelectMenu = defineComponent({ paddingBottom: this.padding.bottom } }, this.flattenedNodes.map((tmNode) => tmNode.isGroup ? h(NSelectGroupHeader, { key: tmNode.key, clsPrefix, tmNode }) : h(NSelectOption, { clsPrefix, key: tmNode.key, tmNode }))); } - }) : h("div", { class: `${clsPrefix}-base-select-menu__empty`, "data-empty": true }, resolveSlot($slots.empty, () => [ + }) : h("div", { class: `${clsPrefix}-base-select-menu__empty`, "data-empty": true, "data-action": true }, resolveSlot($slots.empty, () => [ h(NEmpty, { theme: mergedTheme.peers.Empty, themeOverrides: mergedTheme.peerOverrides.Empty }) ])), resolveWrappedSlot($slots.action, (children) => children && [ @@ -17580,7 +17595,7 @@ const NInternalSelectMenu = defineComponent({ ); } }); -const style$s = cB("base-wave", ` +const style$u = cB("base-wave", ` position: absolute; left: 0; right: 0; @@ -17597,7 +17612,7 @@ const NBaseWave = defineComponent({ } }, setup(props) { - useStyle("-base-wave", style$s, toRef(props, "clsPrefix")); + useStyle("-base-wave", style$u, toRef(props, "clsPrefix")); const selfRef = ref(null); const activeRef = ref(false); let animationTimerId = null; @@ -17673,7 +17688,7 @@ const oppositePlacement = { right: "left" }; const arrowSize = "var(--n-arrow-height) * 1.414"; -const style$r = c$1([cB("popover", ` +const style$t = c$1([cB("popover", ` transition: box-shadow .3s var(--n-bezier), background-color .3s var(--n-bezier), @@ -17865,7 +17880,7 @@ const NPopoverBody = defineComponent({ props: popoverBodyProps, setup(props, { slots, attrs }) { const { namespaceRef, mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Popover", "-popover", style$r, popoverLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Popover", "-popover", style$t, popoverLight$1, props, mergedClsPrefixRef); const followerRef = ref(null); const NPopover2 = inject("NPopover"); const bodyRef = ref(null); @@ -18410,7 +18425,7 @@ const NPopover = defineComponent({ doUpdateShow(false); } }); - return { + const returned = { binderInstRef, positionManually: positionManuallyRef, mergedShowConsideringDisabledProp: mergedShowConsideringDisabledPropRef, @@ -18426,6 +18441,7 @@ const NPopover = defineComponent({ handleBlur, syncPosition }; + return returned; }, render() { var _a2; @@ -18747,7 +18763,7 @@ const commonProps = { default: void 0 } }; -const style$q = cB("tag", ` +const style$s = cB("tag", ` white-space: nowrap; position: relative; box-sizing: border-box; @@ -18850,7 +18866,7 @@ const NTag = defineComponent({ setup(props) { const contentRef = ref(null); const { mergedBorderedRef, mergedClsPrefixRef, inlineThemeDisabled, mergedRtlRef } = useConfig(props); - const themeRef = useTheme("Tag", "-tag", style$q, tagLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Tag", "-tag", style$s, tagLight$1, props, mergedClsPrefixRef); provide(tagInjectionKey, { roundRef: toRef(props, "round") }); @@ -18981,7 +18997,7 @@ const NTag = defineComponent({ ); } }); -const style$p = cB("base-clear", ` +const style$r = cB("base-clear", ` flex-shrink: 0; height: 1em; width: 1em; @@ -19021,10 +19037,12 @@ const NBaseClear = defineComponent({ onClear: Function }, setup(props) { - useStyle("-base-clear", style$p, toRef(props, "clsPrefix")); + useStyle("-base-clear", style$r, toRef(props, "clsPrefix")); return { handleMouseDown(e) { + var _a2; e.preventDefault(); + (_a2 = props.onClear) === null || _a2 === void 0 ? void 0 : _a2.call(props, e); } }; }, @@ -19241,7 +19259,7 @@ const internalSelectionDark = { } }; const internalSelectionDark$1 = internalSelectionDark; -const style$o = c$1([cB("base-selection", ` +const style$q = c$1([cB("base-selection", ` position: relative; z-index: auto; box-shadow: none; @@ -19481,7 +19499,7 @@ const NInternalSelection = defineComponent({ const showTagsPopoverRef = ref(false); const patternInputFocusedRef = ref(false); const hoverRef = ref(false); - const themeRef = useTheme("InternalSelection", "-internal-selection", style$o, internalSelectionLight$1, props, toRef(props, "clsPrefix")); + const themeRef = useTheme("InternalSelection", "-internal-selection", style$q, internalSelectionLight$1, props, toRef(props, "clsPrefix")); const mergedClearableRef = computed(() => { return props.clearable && !props.disabled && (hoverRef.value || props.active); }); @@ -19699,7 +19717,7 @@ const NInternalSelection = defineComponent({ window.clearTimeout(enterTimerId); } function handleMouseEnterCounter() { - if (props.disabled || props.active) + if (props.active) return; clearEnterTimer(); enterTimerId = window.setTimeout(() => { @@ -19727,7 +19745,11 @@ const NInternalSelection = defineComponent({ const patternInputWrapperEl = patternInputWrapperRef.value; if (!patternInputWrapperEl) return; - patternInputWrapperEl.tabIndex = props.disabled || patternInputFocusedRef.value ? -1 : 0; + if (props.disabled) { + patternInputWrapperEl.removeAttribute("tabindex"); + } else { + patternInputWrapperEl.tabIndex = patternInputFocusedRef.value ? -1 : 0; + } }); }); useOnResize(selfRef, props.onResize); @@ -20286,7 +20308,7 @@ function fadeInHeightExpandTransition({ ${originalTransition ? "," + originalTransition : ""} `)]; } -const style$n = cB("alert", ` +const style$p = cB("alert", ` line-height: var(--n-line-height); border-radius: var(--n-border-radius); position: relative; @@ -20294,7 +20316,8 @@ const style$n = cB("alert", ` background-color: var(--n-color); text-align: start; word-break: break-word; -`, [cE("border", ` +`, [ + cE("border", ` border-radius: inherit; position: absolute; left: 0; @@ -20304,25 +20327,30 @@ const style$n = cB("alert", ` transition: border-color .3s var(--n-bezier); border: var(--n-border); pointer-events: none; - `), cM("closable", [cB("alert-body", [cE("title", ` + `), + cM("closable", [cB("alert-body", [cE("title", ` padding-right: 24px; - `)])]), cE("icon", { - color: "var(--n-icon-color)" -}), cB("alert-body", { - padding: "var(--n-padding)" -}, [cE("title", { - color: "var(--n-title-text-color)" -}), cE("content", { - color: "var(--n-content-text-color)" -})]), fadeInHeightExpandTransition({ - originalTransition: "transform .3s var(--n-bezier)", - enterToProps: { - transform: "scale(1)" - }, - leaveToProps: { - transform: "scale(0.9)" - } -}), cE("icon", ` + `)])]), + cE("icon", { + color: "var(--n-icon-color)" + }), + cB("alert-body", { + padding: "var(--n-padding)" + }, [cE("title", { + color: "var(--n-title-text-color)" + }), cE("content", { + color: "var(--n-content-text-color)" + })]), + fadeInHeightExpandTransition({ + originalTransition: "transform .3s var(--n-bezier)", + enterToProps: { + transform: "scale(1)" + }, + leaveToProps: { + transform: "scale(0.9)" + } + }), + cE("icon", ` position: absolute; left: 0; top: 0; @@ -20333,7 +20361,8 @@ const style$n = cB("alert", ` height: var(--n-icon-size); font-size: var(--n-icon-size); margin: var(--n-icon-margin); - `), cE("close", ` + `), + cE("close", ` transition: color .3s var(--n-bezier), background-color .3s var(--n-bezier); @@ -20341,9 +20370,15 @@ const style$n = cB("alert", ` right: 0; top: 0; margin: var(--n-close-margin); - `), cM("show-icon", [cB("alert-body", { - paddingLeft: "calc(var(--n-icon-margin-left) + var(--n-icon-size) + var(--n-icon-margin-right))" -})]), cB("alert-body", ` + `), + cM("show-icon", [cB("alert-body", { + paddingLeft: "calc(var(--n-icon-margin-left) + var(--n-icon-size) + var(--n-icon-margin-right))" + })]), + // fix: https://github.com/tusen-ai/naive-ui/issues/4588 + cM("right-adjust", [cB("alert-body", { + paddingRight: "calc(var(--n-close-size) + var(--n-padding) + 2px)" + })]), + cB("alert-body", ` border-radius: var(--n-border-radius); transition: border-color .3s var(--n-bezier); `, [cE("title", ` @@ -20352,13 +20387,15 @@ const style$n = cB("alert", ` line-height: 19px; font-weight: var(--n-title-font-weight); `, [c$1("& +", [cE("content", { - marginTop: "9px" -})])]), cE("content", { - transition: "color .3s var(--n-bezier)", - fontSize: "var(--n-font-size)" -})]), cE("icon", { - transition: "color .3s var(--n-bezier)" -})]); + marginTop: "9px" + })])]), cE("content", { + transition: "color .3s var(--n-bezier)", + fontSize: "var(--n-font-size)" + })]), + cE("icon", { + transition: "color .3s var(--n-bezier)" + }) +]); const alertProps = Object.assign(Object.assign({}, useTheme.props), { title: String, showIcon: { @@ -20385,7 +20422,7 @@ const NAlert = defineComponent({ props: alertProps, setup(props) { const { mergedClsPrefixRef, mergedBorderedRef, inlineThemeDisabled, mergedRtlRef } = useConfig(props); - const themeRef = useTheme("Alert", "-alert", style$n, alertLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Alert", "-alert", style$p, alertLight$1, props, mergedClsPrefixRef); const rtlEnabledRef = useRtl("Alert", mergedRtlRef, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { common: { cubicBezierEaseInOut: cubicBezierEaseInOut2 }, self: self2 } = themeRef.value; @@ -20472,6 +20509,8 @@ const NAlert = defineComponent({ this.themeClass, this.closable && `${mergedClsPrefix}-alert--closable`, this.showIcon && `${mergedClsPrefix}-alert--show-icon`, + // fix: https://github.com/tusen-ai/naive-ui/issues/4588 + !this.title && this.closable && `${mergedClsPrefix}-alert--right-adjust`, this.rtlEnabled && `${mergedClsPrefix}-alert--rtl` ], style: this.cssVars, @@ -20852,7 +20891,7 @@ const WordCount = defineComponent({ }; } }); -const style$m = cB("input", ` +const style$o = cB("input", ` max-width: 100%; cursor: text; line-height: 1.5; @@ -20983,6 +21022,7 @@ const style$m = cB("input", ` margin: 0; resize: none; white-space: pre-wrap; + scroll-padding-block-end: var(--n-padding-vertical); `), cE("textarea-mirror", ` width: 100%; pointer-events: none; @@ -21173,7 +21213,7 @@ const inputProps = Object.assign(Object.assign({}, useTheme.props), { renderCount: Function, onMousedown: Function, onKeydown: Function, - onKeyup: Function, + onKeyup: [Function, Array], onInput: [Function, Array], onFocus: [Function, Array], onBlur: [Function, Array], @@ -21198,7 +21238,10 @@ const inputProps = Object.assign(Object.assign({}, useTheme.props), { onWrapperBlur: [Function, Array], internalDeactivateOnEnter: Boolean, internalForceFocus: Boolean, - internalLoadingBeforeSuffix: Boolean, + internalLoadingBeforeSuffix: { + type: Boolean, + default: true + }, /** deprecated */ showPasswordToggle: Boolean }); @@ -21207,7 +21250,7 @@ const NInput = defineComponent({ props: inputProps, setup(props) { const { mergedClsPrefixRef, mergedBorderedRef, inlineThemeDisabled, mergedRtlRef } = useConfig(props); - const themeRef = useTheme("Input", "-input", style$m, inputLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Input", "-input", style$o, inputLight$1, props, mergedClsPrefixRef); if (isSafari) { useStyle("-input-safari", safariStyle, mergedClsPrefixRef); } @@ -21613,9 +21656,13 @@ const NInput = defineComponent({ }; on("mouseup", document, hidePassword); } + function handleWrapperKeyup(e) { + if (props.onKeyup) + call(props.onKeyup, e); + } function handleWrapperKeydown(e) { - var _a2; - (_a2 = props.onKeydown) === null || _a2 === void 0 ? void 0 : _a2.call(props, e); + if (props.onKeydown) + call(props.onKeydown, e); switch (e.key) { case "Escape": handleWrapperKeydownEsc(); @@ -21884,6 +21931,7 @@ const NInput = defineComponent({ handlePasswordToggleClick, handlePasswordToggleMousedown, handleWrapperKeydown, + handleWrapperKeyup, handleTextAreaMirrorResize, getTextareaScrollContainer: () => { return textareaElRef.value; @@ -21916,7 +21964,7 @@ const NInput = defineComponent({ [`${mergedClsPrefix}-input--focus`]: this.mergedFocus, [`${mergedClsPrefix}-input--stateful`]: this.stateful } - ], style: this.cssVars, tabindex: !this.mergedDisabled && this.passivelyActivated && !this.activated ? 0 : void 0, onFocus: this.handleWrapperFocus, onBlur: this.handleWrapperBlur, onClick: this.handleClick, onMousedown: this.handleMouseDown, onMouseenter: this.handleMouseEnter, onMouseleave: this.handleMouseLeave, onCompositionstart: this.handleCompositionStart, onCompositionend: this.handleCompositionEnd, onKeyup: this.onKeyup, onKeydown: this.handleWrapperKeydown }, + ], style: this.cssVars, tabindex: !this.mergedDisabled && this.passivelyActivated && !this.activated ? 0 : void 0, onFocus: this.handleWrapperFocus, onBlur: this.handleWrapperBlur, onClick: this.handleClick, onMousedown: this.handleMouseDown, onMouseenter: this.handleMouseEnter, onMouseleave: this.handleMouseLeave, onCompositionstart: this.handleCompositionStart, onCompositionend: this.handleCompositionEnd, onKeyup: this.handleWrapperKeyup, onKeydown: this.handleWrapperKeydown }, h( "div", { class: `${mergedClsPrefix}-input-wrapper` }, @@ -22052,7 +22100,7 @@ const NInput = defineComponent({ ); } }); -const style$l = cB("input-group", ` +const style$n = cB("input-group", ` display: inline-flex; width: 100%; flex-wrap: nowrap; @@ -22114,7 +22162,7 @@ const NInputGroup = defineComponent({ props: inputGroupProps, setup(props) { const { mergedClsPrefixRef } = useConfig(props); - useStyle("-input-group", style$l, mergedClsPrefixRef); + useStyle("-input-group", style$n, mergedClsPrefixRef); return { mergedClsPrefix: mergedClsPrefixRef }; @@ -22524,7 +22572,7 @@ const buttonDark = { } }; const buttonDark$1 = buttonDark; -const style$k = c$1([cB("button", ` +const style$m = c$1([cB("button", ` margin: 0; font-weight: var(--n-font-weight); line-height: 1; @@ -22781,7 +22829,7 @@ const Button = defineComponent({ enterPressedRef.value = false; }; const { inlineThemeDisabled, mergedClsPrefixRef, mergedRtlRef } = useConfig(props); - const themeRef = useTheme("Button", "-button", style$k, buttonLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Button", "-button", style$m, buttonLight$1, props, mergedClsPrefixRef); const rtlEnabledRef = useRtl("Button", mergedRtlRef, mergedClsPrefixRef); const cssVarsRef = computed(() => { const theme = themeRef.value; @@ -23949,7 +23997,7 @@ const ColorPreview = defineComponent({ ); } }); -const style$j = c$1([cB("color-picker", ` +const style$l = c$1([cB("color-picker", ` display: inline-block; box-sizing: border-box; height: var(--n-height); @@ -24160,7 +24208,7 @@ const NColorPicker = defineComponent({ const { mergedSizeRef, mergedDisabledRef } = formItem; const { localeRef } = useLocale("global"); const { mergedClsPrefixRef, namespaceRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("ColorPicker", "-color-picker", style$j, colorPickerLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("ColorPicker", "-color-picker", style$l, colorPickerLight$1, props, mergedClsPrefixRef); provide(colorPickerInjectionKey, { themeRef, renderLabelRef: toRef(props, "renderLabel"), @@ -24618,7 +24666,7 @@ const cardDark = { } }; const cardDark$1 = cardDark; -const style$i = c$1([cB("card", ` +const style$k = c$1([cB("card", ` font-size: var(--n-font-size); line-height: var(--n-line-height); display: flex; @@ -24763,7 +24811,7 @@ const NCard = defineComponent({ call(onClose); }; const { inlineThemeDisabled, mergedClsPrefixRef, mergedRtlRef } = useConfig(props); - const themeRef = useTheme("Card", "-card", style$i, cardLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Card", "-card", style$k, cardLight$1, props, mergedClsPrefixRef); const rtlEnabledRef = useRtl("Card", mergedRtlRef, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { size: size2 } = props; @@ -25024,6 +25072,255 @@ const codeLight = { self: self$T }; const codeLight$1 = codeLight; +const style$j = c$1([cB("code", ` + font-size: var(--n-font-size); + font-family: var(--n-font-family); + `, [cM("show-line-numbers", ` + display: flex; + `), cE("line-numbers", ` + user-select: none; + padding-right: 12px; + text-align: right; + transition: color .3s var(--n-bezier); + color: var(--n-line-number-text-color); + `), cM("word-wrap", [c$1("pre", ` + white-space: pre-wrap; + word-break: break-all; + `)]), c$1("pre", ` + margin: 0; + line-height: inherit; + font-size: inherit; + font-family: inherit; + `), c$1("[class^=hljs]", ` + color: var(--n-text-color); + transition: + color .3s var(--n-bezier), + background-color .3s var(--n-bezier); + `)]), ({ + props +}) => { + const codeClass = `${props.bPrefix}code`; + return [`${codeClass} .hljs-comment, + ${codeClass} .hljs-quote { + color: var(--n-mono-3); + font-style: italic; + }`, `${codeClass} .hljs-doctag, + ${codeClass} .hljs-keyword, + ${codeClass} .hljs-formula { + color: var(--n-hue-3); + }`, `${codeClass} .hljs-section, + ${codeClass} .hljs-name, + ${codeClass} .hljs-selector-tag, + ${codeClass} .hljs-deletion, + ${codeClass} .hljs-subst { + color: var(--n-hue-5); + }`, `${codeClass} .hljs-literal { + color: var(--n-hue-1); + }`, `${codeClass} .hljs-string, + ${codeClass} .hljs-regexp, + ${codeClass} .hljs-addition, + ${codeClass} .hljs-attribute, + ${codeClass} .hljs-meta-string { + color: var(--n-hue-4); + }`, `${codeClass} .hljs-built_in, + ${codeClass} .hljs-class .hljs-title { + color: var(--n-hue-6-2); + }`, `${codeClass} .hljs-attr, + ${codeClass} .hljs-variable, + ${codeClass} .hljs-template-variable, + ${codeClass} .hljs-type, + ${codeClass} .hljs-selector-class, + ${codeClass} .hljs-selector-attr, + ${codeClass} .hljs-selector-pseudo, + ${codeClass} .hljs-number { + color: var(--n-hue-6); + }`, `${codeClass} .hljs-symbol, + ${codeClass} .hljs-bullet, + ${codeClass} .hljs-link, + ${codeClass} .hljs-meta, + ${codeClass} .hljs-selector-id, + ${codeClass} .hljs-title { + color: var(--n-hue-2); + }`, `${codeClass} .hljs-emphasis { + font-style: italic; + }`, `${codeClass} .hljs-strong { + font-weight: var(--n-font-weight-strong); + }`, `${codeClass} .hljs-link { + text-decoration: underline; + }`]; +}]); +const codeProps = Object.assign(Object.assign({}, useTheme.props), { + language: String, + code: { + type: String, + default: "" + }, + trim: { + type: Boolean, + default: true + }, + hljs: Object, + uri: Boolean, + inline: Boolean, + wordWrap: Boolean, + showLineNumbers: Boolean, + // In n-log, we only need to mount code's style for highlight + internalFontSize: Number, + internalNoHighlight: Boolean +}); +const NCode = defineComponent({ + name: "Code", + props: codeProps, + setup(props, { slots }) { + const { internalNoHighlight } = props; + const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(); + const codeRef = ref(null); + const hljsRef = internalNoHighlight ? { value: void 0 } : useHljs(props); + const createCodeHtml = (language, code, trim) => { + const { value: hljs } = hljsRef; + if (!hljs) { + return null; + } + if (!(language && hljs.getLanguage(language))) { + return null; + } + return hljs.highlight(trim ? code.trim() : code, { + language + }).value; + }; + const mergedShowLineNumbersRef = computed(() => { + if (props.inline || props.wordWrap) + return false; + return props.showLineNumbers; + }); + const setCode = () => { + if (slots.default) + return; + const { value: codeEl } = codeRef; + if (!codeEl) + return; + const { language } = props; + const code = props.uri ? window.decodeURIComponent(props.code) : props.code; + if (language) { + const html = createCodeHtml(language, code, props.trim); + if (html !== null) { + if (props.inline) { + codeEl.innerHTML = html; + } else { + const prevPreEl = codeEl.querySelector(".__code__"); + if (prevPreEl) + codeEl.removeChild(prevPreEl); + const preEl = document.createElement("pre"); + preEl.className = "__code__"; + preEl.innerHTML = html; + codeEl.appendChild(preEl); + } + return; + } + } + if (props.inline) { + codeEl.textContent = code; + return; + } + const maybePreEl = codeEl.querySelector(".__code__"); + if (maybePreEl) { + maybePreEl.textContent = code; + } else { + const wrap = document.createElement("pre"); + wrap.className = "__code__"; + wrap.textContent = code; + codeEl.innerHTML = ""; + codeEl.appendChild(wrap); + } + }; + onMounted(setCode); + watch(toRef(props, "language"), setCode); + watch(toRef(props, "code"), setCode); + if (!internalNoHighlight) + watch(hljsRef, setCode); + const themeRef = useTheme("Code", "-code", style$j, codeLight$1, props, mergedClsPrefixRef); + const cssVarsRef = computed(() => { + const { common: { cubicBezierEaseInOut: cubicBezierEaseInOut2, fontFamilyMono }, self: { + textColor, + fontSize: fontSize2, + fontWeightStrong, + lineNumberTextColor, + // extracted from hljs atom-one-light.scss + "mono-3": $1, + "hue-1": $2, + "hue-2": $3, + "hue-3": $4, + "hue-4": $5, + "hue-5": $6, + "hue-5-2": $7, + "hue-6": $8, + "hue-6-2": $9 + } } = themeRef.value; + const { internalFontSize } = props; + return { + "--n-font-size": internalFontSize ? `${internalFontSize}px` : fontSize2, + "--n-font-family": fontFamilyMono, + "--n-font-weight-strong": fontWeightStrong, + "--n-bezier": cubicBezierEaseInOut2, + "--n-text-color": textColor, + "--n-mono-3": $1, + "--n-hue-1": $2, + "--n-hue-2": $3, + "--n-hue-3": $4, + "--n-hue-4": $5, + "--n-hue-5": $6, + "--n-hue-5-2": $7, + "--n-hue-6": $8, + "--n-hue-6-2": $9, + "--n-line-number-text-color": lineNumberTextColor + }; + }); + const themeClassHandle = inlineThemeDisabled ? useThemeClass("code", computed(() => { + return `${props.internalFontSize || "a"}`; + }), cssVarsRef, props) : void 0; + return { + mergedClsPrefix: mergedClsPrefixRef, + codeRef, + mergedShowLineNumbers: mergedShowLineNumbersRef, + lineNumbers: computed(() => { + let number = 1; + const numbers = []; + let lastIsLineWrap = false; + for (const char of props.code) { + if (char === "\n") { + lastIsLineWrap = true; + numbers.push(number++); + } else { + lastIsLineWrap = false; + } + } + if (!lastIsLineWrap) { + numbers.push(number++); + } + return numbers.join("\n"); + }), + cssVars: inlineThemeDisabled ? void 0 : cssVarsRef, + themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, + onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender + }; + }, + render() { + var _a2, _b; + const { mergedClsPrefix, wordWrap, mergedShowLineNumbers, onRender } = this; + onRender === null || onRender === void 0 ? void 0 : onRender(); + return h( + "code", + { class: [ + `${mergedClsPrefix}-code`, + this.themeClass, + wordWrap && `${mergedClsPrefix}-code--word-wrap`, + mergedShowLineNumbers && `${mergedClsPrefix}-code--show-line-numbers` + ], style: this.cssVars, ref: "codeRef" }, + mergedShowLineNumbers ? h("pre", { class: `${mergedClsPrefix}-code__line-numbers` }, this.lineNumbers) : null, + (_b = (_a2 = this.$slots).default) === null || _b === void 0 ? void 0 : _b.call(_a2) + ); + } +}); const self$S = (vars) => { const { fontWeight, textColor1, textColor2, textColorDisabled, dividerColor, fontSize: fontSize2 } = vars; return { @@ -25052,7 +25349,7 @@ const collapseDark = { self: self$S }; const collapseDark$1 = collapseDark; -const style$h = cB("collapse", "width: 100%;", [cB("collapse-item", ` +const style$i = cB("collapse", "width: 100%;", [cB("collapse-item", ` font-size: var(--n-font-size); color: var(--n-text-color); transition: @@ -25135,7 +25432,7 @@ const NCollapse = defineComponent({ const uncontrolledExpandedNamesRef = ref(props.defaultExpandedNames); const controlledExpandedNamesRef = computed(() => props.expandedNames); const mergedExpandedNamesRef = useMergedState(controlledExpandedNamesRef, uncontrolledExpandedNamesRef); - const themeRef = useTheme("Collapse", "-collapse", style$h, collapseLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Collapse", "-collapse", style$i, collapseLight$1, props, mergedClsPrefixRef); function doUpdateExpandedNames(names) { const { "onUpdate:expandedNames": _onUpdateExpandedNames, onUpdateExpandedNames, onExpandedNamesChange } = props; if (onUpdateExpandedNames) { @@ -25384,7 +25681,7 @@ const configProviderProps = { type: Boolean, default: void 0 }, - clsPrefix: String, + clsPrefix: { type: String, default: defaultClsPrefix }, locale: Object, dateLocale: Object, namespace: String, @@ -25465,7 +25762,9 @@ const NConfigProvider = defineComponent({ const { clsPrefix } = props; if (clsPrefix !== void 0) return clsPrefix; - return NConfigProvider2 === null || NConfigProvider2 === void 0 ? void 0 : NConfigProvider2.mergedClsPrefixRef.value; + if (NConfigProvider2) + return NConfigProvider2.mergedClsPrefixRef.value; + return defaultClsPrefix; }); const mergedRtlRef = computed(() => { var _a2; @@ -25606,7 +25905,7 @@ const selectDark = { self: self$P }; const selectDark$1 = selectDark; -const style$g = c$1([cB("select", ` +const style$h = c$1([cB("select", ` z-index: auto; outline: none; width: 100%; @@ -25735,7 +26034,7 @@ const NSelect = defineComponent({ props: selectProps, setup(props) { const { mergedClsPrefixRef, mergedBorderedRef, namespaceRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Select", "-select", style$g, selectLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Select", "-select", style$h, selectLight$1, props, mergedClsPrefixRef); const uncontrolledValueRef = ref(props.defaultValue); const controlledValueRef = toRef(props, "value"); const mergedValueRef = useMergedState(controlledValueRef, uncontrolledValueRef); @@ -26102,8 +26401,12 @@ const NSelect = defineComponent({ } const { onCreate } = props; const optionBeingCreated = onCreate ? onCreate(value) : { [props.labelField]: value, [props.valueField]: value }; - const { valueField } = props; - if (compitableOptionsRef.value.some((option) => option[valueField] === optionBeingCreated[valueField]) || createdOptionsRef.value.some((option) => option[valueField] === optionBeingCreated[valueField])) { + const { valueField, labelField } = props; + if (compitableOptionsRef.value.some((option) => { + return option[valueField] === optionBeingCreated[valueField] || option[labelField] === optionBeingCreated[labelField]; + }) || createdOptionsRef.value.some((option) => { + return option[valueField] === optionBeingCreated[valueField] || option[labelField] === optionBeingCreated[labelField]; + })) { beingCreatedOptionsRef.value = emptyArray; } else { beingCreatedOptionsRef.value = [optionBeingCreated]; @@ -26223,9 +26526,17 @@ const NSelect = defineComponent({ var _a2; (_a2 = triggerRef.value) === null || _a2 === void 0 ? void 0 : _a2.focus(); }, + focusInput: () => { + var _a2; + (_a2 = triggerRef.value) === null || _a2 === void 0 ? void 0 : _a2.focusInput(); + }, blur: () => { var _a2; (_a2 = triggerRef.value) === null || _a2 === void 0 ? void 0 : _a2.blur(); + }, + blurInput: () => { + var _a2; + (_a2 = triggerRef.value) === null || _a2 === void 0 ? void 0 : _a2.blurInput(); } }; const cssVarsRef = computed(() => { @@ -26813,7 +27124,7 @@ const iconDark$1 = { self: self$J }; const iconDark$2 = iconDark$1; -const style$f = cB("icon", ` +const style$g = cB("icon", ` height: 1em; width: 1em; line-height: 1em; @@ -26841,7 +27152,7 @@ const NIcon = defineComponent({ props: iconProps, setup(props) { const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Icon", "-icon", style$f, iconLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Icon", "-icon", style$g, iconLight$1, props, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { depth } = props; const { common: { cubicBezierEaseInOut: cubicBezierEaseInOut2 }, self: self2 } = themeRef.value; @@ -27245,8 +27556,8 @@ const NDropdownRenderOption = defineComponent({ } }, render() { - const { rawNode: { render: render16, props } } = this.tmNode; - return h("div", props, [render16 === null || render16 === void 0 ? void 0 : render16()]); + const { rawNode: { render: render15, props } } = this.tmNode; + return h("div", props, [render15 === null || render15 === void 0 ? void 0 : render15()]); } }); const NDropdownMenu = defineComponent({ @@ -27335,7 +27646,7 @@ const NDropdownMenu = defineComponent({ ); } }); -const style$e = cB("dropdown-menu", ` +const style$f = cB("dropdown-menu", ` transform-origin: var(--v-transform-origin); background-color: var(--n-color); border-radius: var(--n-border-radius); @@ -27563,7 +27874,7 @@ const NDropdown = defineComponent({ } }, keyboardEnabledRef); const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Dropdown", "-dropdown", style$e, dropdownLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Dropdown", "-dropdown", style$f, dropdownLight$1, props, mergedClsPrefixRef); provide(dropdownInjectionKey, { labelFieldRef: toRef(props, "labelField"), childrenFieldRef: toRef(props, "childrenField"), @@ -28049,7 +28360,7 @@ const dialogProps = { onClose: Function }; const dialogPropKeys = keysOf(dialogProps); -const style$d = c$1([cB("dialog", ` +const style$e = c$1([cB("dialog", ` word-break: break-word; line-height: var(--n-line-height); position: relative; @@ -28157,7 +28468,7 @@ const NDialog = defineComponent({ if (onClose) onClose(); } - const themeRef = useTheme("Dialog", "-dialog", style$d, dialogLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Dialog", "-dialog", style$e, dialogLight$1, props, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { type } = props; const iconPlacement = mergedIconPlacementRef.value; @@ -28483,7 +28794,7 @@ const NModalBodyWrapper = defineComponent({ ]) : null; } }); -const style$c = c$1([cB("modal-container", ` +const style$d = c$1([cB("modal-container", ` position: fixed; left: 0; top: 0; @@ -28575,7 +28886,7 @@ const NModal = defineComponent({ setup(props) { const containerRef = ref(null); const { mergedClsPrefixRef, namespaceRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Modal", "-modal", style$c, modalLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Modal", "-modal", style$d, modalLight$1, props, mergedClsPrefixRef); const clickedRef = useClicked(64); const clickedPositionRef = useClickPosition(); const isMountedRef = isMounted(); @@ -28765,7 +29076,7 @@ const dividerDark = { self: self$D }; const dividerDark$1 = dividerDark; -const style$b = cB("divider", ` +const style$c = cB("divider", ` position: relative; display: flex; width: 100%; @@ -28826,7 +29137,7 @@ const NDivider = defineComponent({ props: dividerProps, setup(props) { const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Divider", "-divider", style$b, dividerLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Divider", "-divider", style$c, dividerLight$1, props, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { common: { cubicBezierEaseInOut: cubicBezierEaseInOut2 }, self: { color, textColor, fontWeight } } = themeRef.value; return { @@ -28949,6 +29260,10 @@ const NDrawerBodyWrapper = defineComponent({ type: [Boolean, String], required: true }, + maxWidth: Number, + maxHeight: Number, + minWidth: Number, + minHeight: Number, resizable: Boolean, onClickoutside: Function, onAfterLeave: Function, @@ -28999,6 +29314,24 @@ const NDrawerBodyWrapper = defineComponent({ isHoverOnResizeTriggerRef.value = false; }; const { doUpdateHeight, doUpdateWidth } = NDrawer2; + const regulateWidth = (size2) => { + const { maxWidth } = props; + if (maxWidth && size2 > maxWidth) + return maxWidth; + const { minWidth } = props; + if (minWidth && size2 < minWidth) + return minWidth; + return size2; + }; + const regulateHeight = (size2) => { + const { maxHeight } = props; + if (maxHeight && size2 > maxHeight) + return maxHeight; + const { minHeight } = props; + if (minHeight && size2 < minHeight) + return minHeight; + return size2; + }; const handleBodyMousemove = (e) => { var _a2, _b; if (isDraggingRef.value) { @@ -29006,12 +29339,14 @@ const NDrawerBodyWrapper = defineComponent({ let height = ((_a2 = bodyRef.value) === null || _a2 === void 0 ? void 0 : _a2.offsetHeight) || 0; const increment = startPosition - e.clientY; height += props.placement === "bottom" ? increment : -increment; + height = regulateHeight(height); doUpdateHeight(height); startPosition = e.clientY; } else { let width = ((_b = bodyRef.value) === null || _b === void 0 ? void 0 : _b.offsetWidth) || 0; const increment = startPosition - e.clientX; width += props.placement === "right" ? increment : -increment; + width = regulateWidth(width); doUpdateWidth(width); startPosition = e.clientX; } @@ -29090,7 +29425,7 @@ const NDrawerBodyWrapper = defineComponent({ const { $slots, mergedClsPrefix } = this; return this.displayDirective === "show" || this.displayed || this.show ? withDirectives( /* Keep the wrapper dom. Make sure the drawer has a host. - Nor the detached content will disappear without transition */ + Nor the detached content will disappear without transition */ h( "div", { role: "none" }, @@ -29222,7 +29557,7 @@ function slideInFromBottomTransition({ duration = "0.3s", leaveDuration = "0.2s" }) ]; } -const style$a = c$1([cB("drawer", ` +const style$b = c$1([cB("drawer", ` word-break: break-word; line-height: var(--n-line-height); position: absolute; @@ -29401,6 +29736,10 @@ const drawerProps = Object.assign(Object.assign({}, useTheme.props), { type: Boolean, default: true }, + maxWidth: Number, + maxHeight: Number, + minWidth: Number, + minHeight: Number, resizable: Boolean, defaultWidth: { type: [Number, String], @@ -29432,7 +29771,7 @@ const NDrawer = defineComponent({ setup(props) { const { mergedClsPrefixRef, namespaceRef, inlineThemeDisabled } = useConfig(props); const isMountedRef = isMounted(); - const themeRef = useTheme("Drawer", "-drawer", style$a, drawerLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Drawer", "-drawer", style$b, drawerLight$1, props, mergedClsPrefixRef); const uncontrolledWidthRef = ref(props.defaultWidth); const uncontrolledHeightRef = ref(props.defaultHeight); const mergedWidthRef = useMergedState(toRef(props, "width"), uncontrolledWidthRef); @@ -29569,7 +29908,7 @@ const NDrawer = defineComponent({ this.showMask === "transparent" && `${mergedClsPrefix}-drawer-mask--invisible` ], onClick: this.handleMaskClick }) : null }) : null, - h(NDrawerBodyWrapper, Object.assign({}, this.$attrs, { class: [this.drawerClass, this.$attrs.class], style: [this.mergedBodyStyle, this.$attrs.style], blockScroll: this.blockScroll, contentStyle: this.contentStyle, placement: this.placement, scrollbarProps: this.scrollbarProps, show: this.show, displayDirective: this.displayDirective, nativeScrollbar: this.nativeScrollbar, onAfterEnter: this.onAfterEnter, onAfterLeave: this.onAfterLeave, trapFocus: this.trapFocus, autoFocus: this.autoFocus, resizable: this.resizable, showMask: this.showMask, onEsc: this.handleEsc, onClickoutside: this.handleMaskClick }), this.$slots) + h(NDrawerBodyWrapper, Object.assign({}, this.$attrs, { class: [this.drawerClass, this.$attrs.class], style: [this.mergedBodyStyle, this.$attrs.style], blockScroll: this.blockScroll, contentStyle: this.contentStyle, placement: this.placement, scrollbarProps: this.scrollbarProps, show: this.show, displayDirective: this.displayDirective, nativeScrollbar: this.nativeScrollbar, onAfterEnter: this.onAfterEnter, onAfterLeave: this.onAfterLeave, trapFocus: this.trapFocus, autoFocus: this.autoFocus, resizable: this.resizable, maxHeight: this.maxHeight, minHeight: this.minHeight, maxWidth: this.maxWidth, minWidth: this.minWidth, showMask: this.showMask, onEsc: this.handleEsc, onClickoutside: this.handleMaskClick }), this.$slots) ), [[zindexable$1, { zIndex: this.zIndex, enabled: this.show }]]); } }); @@ -30182,7 +30521,7 @@ const NGrid = defineComponent({ const childrenAndRawSpan = []; const { collapsed, collapsedRows, responsiveCols, responsiveQuery } = this; rawChildren.forEach((child) => { - var _a3, _b2, _c2, _d2; + var _a3, _b2, _c2, _d2, _e2; if (((_a3 = child === null || child === void 0 ? void 0 : child.type) === null || _a3 === void 0 ? void 0 : _a3.__GRID_ITEM__) !== true) return; if (isNodeVShowFalse(child)) { @@ -30199,8 +30538,11 @@ const NGrid = defineComponent({ return; } child.dirs = ((_b2 = child.dirs) === null || _b2 === void 0 ? void 0 : _b2.filter(({ dir }) => dir !== vShow)) || null; + if (((_c2 = child.dirs) === null || _c2 === void 0 ? void 0 : _c2.length) === 0) { + child.dirs = null; + } const clonedChild = cloneVNode(child); - const rawChildSpan = Number((_d2 = parseResponsivePropValue((_c2 = clonedChild.props) === null || _c2 === void 0 ? void 0 : _c2.span, responsiveQuery)) !== null && _d2 !== void 0 ? _d2 : defaultSpan$1); + const rawChildSpan = Number((_e2 = parseResponsivePropValue((_d2 = clonedChild.props) === null || _d2 === void 0 ? void 0 : _d2.span, responsiveQuery)) !== null && _e2 !== void 0 ? _e2 : defaultSpan$1); if (rawChildSpan === 0) return; childrenAndRawSpan.push({ @@ -30213,7 +30555,7 @@ const NGrid = defineComponent({ if (maybeSuffixNode === null || maybeSuffixNode === void 0 ? void 0 : maybeSuffixNode.props) { const suffixPropValue = (_b = maybeSuffixNode.props) === null || _b === void 0 ? void 0 : _b.suffix; if (suffixPropValue !== void 0 && suffixPropValue !== false) { - suffixSpan = (_d = (_c = maybeSuffixNode.props) === null || _c === void 0 ? void 0 : _c.span) !== null && _d !== void 0 ? _d : defaultSpan$1; + suffixSpan = Number((_d = parseResponsivePropValue((_c = maybeSuffixNode.props) === null || _c === void 0 ? void 0 : _c.span, responsiveQuery)) !== null && _d !== void 0 ? _d : defaultSpan$1); maybeSuffixNode.props.privateSpan = suffixSpan; maybeSuffixNode.props.privateColStart = responsiveCols + 1 - suffixSpan; maybeSuffixNode.props.privateShow = (_e = maybeSuffixNode.props.privateShow) !== null && _e !== void 0 ? _e : true; @@ -31416,6 +31758,9 @@ const self$7 = (vars) => { const { borderRadiusSmall, hoverColor, pressedColor, primaryColor, textColor3, textColor2, textColorDisabled, fontSize: fontSize2 } = vars; return { fontSize: fontSize2, + lineHeight: "1.5", + nodeHeight: "30px", + nodeWrapperPadding: "3px 0", nodeBorderRadius: borderRadiusSmall, nodeColorHover: hoverColor, nodeColorPressed: pressedColor, @@ -31636,7 +31981,7 @@ const positionProp = { type: String, default: "static" }; -const style$9 = cB("layout", ` +const style$a = cB("layout", ` color: var(--n-text-color); background-color: var(--n-color); box-sizing: border-box; @@ -31687,7 +32032,7 @@ function createLayoutComponent(isContent) { const scrollableElRef = ref(null); const scrollbarInstRef = ref(null); const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Layout", "-layout", style$9, layoutLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Layout", "-layout", style$a, layoutLight$1, props, mergedClsPrefixRef); function scrollTo(options, y) { if (props.nativeScrollbar) { const { value: scrollableEl } = scrollableElRef; @@ -31772,7 +32117,7 @@ function createLayoutComponent(isContent) { }); } const NLayout = createLayoutComponent(false); -const style$8 = cB("layout-sider", ` +const style$9 = cB("layout-sider", ` flex-shrink: 0; box-sizing: border-box; position: relative; @@ -32076,7 +32421,7 @@ const NLayoutSider = defineComponent({ collapseModeRef: toRef(props, "collapseMode") }); const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); - const themeRef = useTheme("Layout", "-layout-sider", style$8, layoutLight$1, props, mergedClsPrefixRef); + const themeRef = useTheme("Layout", "-layout-sider", style$9, layoutLight$1, props, mergedClsPrefixRef); function handleTransitionend(e) { var _a2, _b; if (e.propertyName === "max-width") { @@ -32260,7 +32605,7 @@ const transferLight = createTheme({ const legacyTransferLight = transferLight; const loadingBarProviderInjectionKey = createInjectionKey("n-loading-bar"); const loadingBarApiInjectionKey = createInjectionKey("n-loading-bar-api"); -const style$7 = cB("loading-bar-container", ` +const style$8 = cB("loading-bar-container", ` z-index: 5999; position: fixed; top: 0; @@ -32357,9 +32702,11 @@ const NLoadingBar = defineComponent({ } function start(fromProgress = 0, toProgress = 80, status = "starting") { return __awaiter(this, void 0, void 0, function* () { + startedRef.value = true; yield init2(); + if (finishing) + return; loadingRef.value = true; - startedRef.value = true; yield nextTick(); const el = loadingBarRef.value; if (!el) @@ -32373,16 +32720,21 @@ const NLoadingBar = defineComponent({ }); } function finish() { - if (finishing || erroringRef.value || !loadingRef.value) - return; - finishing = true; - const el = loadingBarRef.value; - if (!el) - return; - el.className = createClassName("finishing", mergedClsPrefixRef.value); - el.style.maxWidth = "100%"; - void el.offsetWidth; - loadingRef.value = false; + return __awaiter(this, void 0, void 0, function* () { + if (finishing || erroringRef.value) + return; + if (startedRef.value) { + yield nextTick(); + } + finishing = true; + const el = loadingBarRef.value; + if (!el) + return; + el.className = createClassName("finishing", mergedClsPrefixRef.value); + el.style.maxWidth = "100%"; + void el.offsetWidth; + loadingRef.value = false; + }); } function error() { if (finishing || erroringRef.value) @@ -32419,7 +32771,7 @@ const NLoadingBar = defineComponent({ yield init2(); }); } - const themeRef = useTheme("LoadingBar", "-loading-bar", style$7, loadingBarLight$1, providerProps, mergedClsPrefixRef); + const themeRef = useTheme("LoadingBar", "-loading-bar", style$8, loadingBarLight$1, providerProps, mergedClsPrefixRef); const cssVarsRef = computed(() => { const { self: { height, colorError, colorLoading } } = themeRef.value; return { @@ -32550,6 +32902,314 @@ const NLoadingBarProvider = defineComponent({ ); } }); +const NLogLoader = defineComponent({ + name: "LogLoader", + props: { + clsPrefix: { + type: String, + required: true + } + }, + setup() { + return { + locale: useLocale("Log").localeRef + }; + }, + render() { + const { clsPrefix } = this; + return h( + "div", + { class: `${clsPrefix}-log-loader` }, + h(NBaseLoading, { clsPrefix, strokeWidth: 24, scale: 0.85 }), + h("span", { class: `${clsPrefix}-log-loader__content` }, this.locale.loading) + ); + } +}); +const logInjectionKey = createInjectionKey("n-log"); +const NLogLine = defineComponent({ + props: { + line: { + type: String, + default: "" + } + }, + setup(props) { + const { trimRef, highlightRef, languageRef, mergedHljsRef } = ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + inject(logInjectionKey) + ); + const selfRef = ref(null); + const maybeTrimmedLinesRef = computed(() => { + return trimRef.value ? props.line.trim() : props.line; + }); + function setInnerHTML() { + if (selfRef.value) { + selfRef.value.innerHTML = generateCodeHTML(languageRef.value, maybeTrimmedLinesRef.value); + } + } + function generateCodeHTML(language, code) { + const { value: hljs } = mergedHljsRef; + if (hljs) { + if (language && hljs.getLanguage(language)) { + return hljs.highlight(code, { language }).value; + } + } + return code; + } + onMounted(() => { + if (highlightRef.value) { + setInnerHTML(); + } + }); + watch(toRef(props, "line"), () => { + if (highlightRef.value) { + setInnerHTML(); + } + }); + return { + highlight: highlightRef, + selfRef, + maybeTrimmedLines: maybeTrimmedLinesRef + }; + }, + render() { + const { highlight, maybeTrimmedLines } = this; + return h("pre", { ref: "selfRef" }, highlight ? null : maybeTrimmedLines); + } +}); +const style$7 = cB("log", ` + position: relative; + box-sizing: border-box; + transition: border-color .3s var(--n-bezier); +`, [c$1("pre", ` + white-space: pre-wrap; + word-break: break-word; + margin: 0; + `), cB("log-loader", ` + transition: + color .3s var(--n-bezier), + background-color .3s var(--n-bezier), + border-color .3s var(--n-bezier); + box-sizing: border-box; + position: absolute; + right: 16px; + top: 8px; + height: 34px; + border-radius: 17px; + line-height: 34px; + white-space: nowrap; + overflow: hidden; + border: var(--n-loader-border); + color: var(--n-loader-text-color); + background-color: var(--n-loader-color); + font-size: var(--n-loader-font-size); + `, [fadeInScaleUpTransition(), cE("content", ` + display: inline-block; + vertical-align: bottom; + line-height: 34px; + padding-left: 40px; + padding-right: 20px; + white-space: nowrap; + `), cB("base-loading", ` + color: var(--n-loading-color); + position: absolute; + left: 12px; + top: calc(50% - 10px); + font-size: 20px; + width: 20px; + height: 20px; + display: inline-block; + `)])]); +const logProps = Object.assign(Object.assign({}, useTheme.props), { loading: Boolean, trim: Boolean, log: String, fontSize: { + type: Number, + default: 14 +}, lines: { + type: Array, + default: () => [] +}, lineHeight: { + type: Number, + default: 1.25 +}, language: String, rows: { + type: Number, + default: 15 +}, offsetTop: { + type: Number, + default: 0 +}, offsetBottom: { + type: Number, + default: 0 +}, hljs: Object, onReachTop: Function, onReachBottom: Function, onRequireMore: Function }); +const NLog = defineComponent({ + name: "Log", + props: logProps, + setup(props) { + const { mergedClsPrefixRef, inlineThemeDisabled } = useConfig(props); + const silentRef = ref(false); + const highlightRef = computed(() => { + return props.language !== void 0; + }); + const styleHeightRef = computed(() => { + return `calc(${Math.round(props.rows * props.lineHeight * props.fontSize)}px)`; + }); + const mergedLinesRef = computed(() => { + const { log } = props; + if (log) { + return log.split("\n"); + } + return props.lines; + }); + const scrollbarRef = ref(null); + const themeRef = useTheme("Log", "-log", style$7, logLight$1, props, mergedClsPrefixRef); + function handleScroll(e) { + const container = e.target; + const content = container.firstElementChild; + if (silentRef.value) { + void nextTick(() => { + silentRef.value = false; + }); + return; + } + const containerHeight = container.offsetHeight; + const containerScrollTop = container.scrollTop; + const contentHeight = content.offsetHeight; + const scrollTop = containerScrollTop; + const scrollBottom = contentHeight - containerScrollTop - containerHeight; + if (scrollTop <= props.offsetTop) { + const { onReachTop, onRequireMore } = props; + if (onRequireMore) + onRequireMore("top"); + if (onReachTop) + onReachTop(); + } + if (scrollBottom <= props.offsetBottom) { + const { onReachBottom, onRequireMore } = props; + if (onRequireMore) + onRequireMore("bottom"); + if (onReachBottom) + onReachBottom(); + } + } + const handleWheel = throttle(_handleWheel, 300); + function _handleWheel(e) { + if (silentRef.value) { + void nextTick(() => { + silentRef.value = false; + }); + return; + } + if (scrollbarRef.value) { + const { containerRef, contentRef } = scrollbarRef.value; + if (containerRef && contentRef) { + const containerHeight = containerRef.offsetHeight; + const containerScrollTop = containerRef.scrollTop; + const contentHeight = contentRef.offsetHeight; + const scrollTop = containerScrollTop; + const scrollBottom = contentHeight - containerScrollTop - containerHeight; + const deltaY = e.deltaY; + if (scrollTop === 0 && deltaY < 0) { + const { onRequireMore } = props; + if (onRequireMore) + onRequireMore("top"); + } + if (scrollBottom <= 0 && deltaY > 0) { + const { onRequireMore } = props; + if (onRequireMore) + onRequireMore("bottom"); + } + } + } + } + function scrollTo(options) { + const { value: scrollbarInst } = scrollbarRef; + if (!scrollbarInst) + return; + const { silent, top, position } = options; + if (silent) { + silentRef.value = true; + } + if (top !== void 0) { + scrollbarInst.scrollTo({ left: 0, top }); + } else if (position === "bottom" || position === "top") { + scrollbarInst.scrollTo({ position }); + } + } + function scrollToTop(silent = false) { + warn$2("log", "`scrollToTop` is deprecated, please use `scrollTo({ position: 'top'})` instead."); + scrollTo({ + position: "top", + silent + }); + } + function scrollToBottom(silent = false) { + warn$2("log", "`scrollToTop` is deprecated, please use `scrollTo({ position: 'bottom'})` instead."); + scrollTo({ + position: "bottom", + silent + }); + } + provide(logInjectionKey, { + languageRef: toRef(props, "language"), + mergedHljsRef: useHljs(props), + trimRef: toRef(props, "trim"), + highlightRef + }); + const exportedMethods = { + scrollTo + }; + const cssVarsRef = computed(() => { + const { self: { loaderFontSize, loaderTextColor, loaderColor, loaderBorder, loadingColor }, common: { cubicBezierEaseInOut: cubicBezierEaseInOut2 } } = themeRef.value; + return { + "--n-bezier": cubicBezierEaseInOut2, + "--n-loader-font-size": loaderFontSize, + "--n-loader-border": loaderBorder, + "--n-loader-color": loaderColor, + "--n-loader-text-color": loaderTextColor, + "--n-loading-color": loadingColor + }; + }); + const themeClassHandle = inlineThemeDisabled ? useThemeClass("log", void 0, cssVarsRef, props) : void 0; + return Object.assign(Object.assign({}, exportedMethods), { + mergedClsPrefix: mergedClsPrefixRef, + scrollbarRef, + mergedTheme: themeRef, + styleHeight: styleHeightRef, + mergedLines: mergedLinesRef, + scrollToTop, + scrollToBottom, + handleWheel, + handleScroll, + cssVars: inlineThemeDisabled ? void 0 : cssVarsRef, + themeClass: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.themeClass, + onRender: themeClassHandle === null || themeClassHandle === void 0 ? void 0 : themeClassHandle.onRender + }); + }, + render() { + const { mergedClsPrefix, mergedTheme, onRender } = this; + onRender === null || onRender === void 0 ? void 0 : onRender(); + return h("div", { + class: [`${mergedClsPrefix}-log`, this.themeClass], + style: [ + { + lineHeight: this.lineHeight, + height: this.styleHeight + }, + this.cssVars + ], + onWheelPassive: this.handleWheel + }, [ + h(NScrollbar$1, { ref: "scrollbarRef", theme: mergedTheme.peers.Scrollbar, themeOverrides: mergedTheme.peerOverrides.Scrollbar, onScroll: this.handleScroll }, { + default: () => h(NCode, { internalNoHighlight: true, internalFontSize: this.fontSize, theme: mergedTheme.peers.Code, themeOverrides: mergedTheme.peerOverrides.Code }, { + default: () => this.mergedLines.map((line, index) => { + return h(NLogLine, { key: index, line }); + }) + }) + }), + h(Transition, { name: "fade-in-scale-up-transition" }, { + default: () => this.loading ? h(NLogLoader, { clsPrefix: mergedClsPrefix }) : null + }) + ]); + } +}); const menuInjectionKey = createInjectionKey("n-menu"); const submenuInjectionKey = createInjectionKey("n-submenu"); const menuItemGroupInjectionKey = createInjectionKey("n-menu-item-group"); @@ -34904,14 +35564,9 @@ const Line = defineComponent({ height: styleHeightRef.value, lineHeight: styleHeightRef.value, borderRadius: styleFillBorderRadiusRef.value - } }, indicatorPlacement === "inside" ? h( - "div", - { class: `${clsPrefix}-progress-graph-line-indicator`, style: { - color: indicatorTextColor - } }, - percentage, - unit - ) : null) + } }, indicatorPlacement === "inside" ? h("div", { class: `${clsPrefix}-progress-graph-line-indicator`, style: { + color: indicatorTextColor + } }, slots.default ? slots.default() : `${percentage}${unit}`) : null) ) ) ), @@ -36109,6 +36764,18 @@ const NTabs = defineComponent({ if (tabsPaneWrapperEl) { tabsPaneWrapperEl.style.maxHeight = ""; tabsPaneWrapperEl.style.height = ""; + const { paneWrapperStyle } = props; + if (typeof paneWrapperStyle === "string") { + tabsPaneWrapperEl.style.cssText = paneWrapperStyle; + } else if (paneWrapperStyle) { + const { maxHeight, height } = paneWrapperStyle; + if (maxHeight !== void 0) { + tabsPaneWrapperEl.style.maxHeight = maxHeight; + } + if (height !== void 0) { + tabsPaneWrapperEl.style.height = height; + } + } } } const renderNameListRef = { value: [] }; @@ -37044,10 +37711,10 @@ const NThemeEditor = defineComponent({ const compNamePatternLower = compNamePattern.toLowerCase(); const varNamePatternLower = varNamePattern.toLowerCase(); let filteredItemsCount = 0; - const collapsedItems = themeKeys.filter((themeKey) => { - return themeKey.toLowerCase().includes(compNamePatternLower); - }).map((themeKey) => { - const componentTheme = themeKey === "common" ? this.themeCommonDefault : theme[themeKey]; + const collapsedItems = themeKeys.filter((themeKey2) => { + return themeKey2.toLowerCase().includes(compNamePatternLower); + }).map((themeKey2) => { + const componentTheme = themeKey2 === "common" ? this.themeCommonDefault : theme[themeKey2]; if (componentTheme === void 0) { return null; } @@ -37058,7 +37725,7 @@ const NThemeEditor = defineComponent({ return null; } filteredItemsCount += 1; - return h(NCollapseItem, { title: themeKey, name: themeKey }, { + return h(NCollapseItem, { title: themeKey2, name: themeKey2 }, { default: () => h(NGrid, { xGap: 32, yGap: 16, responsive: "screen", cols: this.isMaximized ? "1 xs:1 s:2 m:3 l:4" : 1 }, { default: () => varKeys.map((varKey) => h(NGi, null, { default: () => { @@ -37069,21 +37736,21 @@ const NThemeEditor = defineComponent({ h("div", { key: `${varKey}Label`, style: { wordBreak: "break-word" } }, varKey), - showColorPicker(varKey) ? h(NColorPicker, { key: varKey, modes: ["rgb", "hex"], value: ((_b2 = (_a3 = this.tempOverrides) === null || _a3 === void 0 ? void 0 : _a3[themeKey]) === null || _b2 === void 0 ? void 0 : _b2[varKey]) || componentTheme[varKey], onComplete: this.applyTempOverrides, onUpdateValue: (value) => { - this.setTempOverrides(themeKey, varKey, value); + showColorPicker(varKey) ? h(NColorPicker, { key: varKey, modes: ["rgb", "hex"], value: ((_b2 = (_a3 = this.tempOverrides) === null || _a3 === void 0 ? void 0 : _a3[themeKey2]) === null || _b2 === void 0 ? void 0 : _b2[varKey]) || componentTheme[varKey], onComplete: this.applyTempOverrides, onUpdateValue: (value) => { + this.setTempOverrides(themeKey2, varKey, value); } }, { action: () => { var _a4, _b3; - return h(NButton, { size: "small", disabled: componentTheme[varKey] === ((_b3 = (_a4 = this.tempOverrides) === null || _a4 === void 0 ? void 0 : _a4[themeKey]) === null || _b3 === void 0 ? void 0 : _b3[varKey]), onClick: () => { - this.setTempOverrides(themeKey, varKey, componentTheme[varKey]); + return h(NButton, { size: "small", disabled: componentTheme[varKey] === ((_b3 = (_a4 = this.tempOverrides) === null || _a4 === void 0 ? void 0 : _a4[themeKey2]) === null || _b3 === void 0 ? void 0 : _b3[varKey]), onClick: () => { + this.setTempOverrides(themeKey2, varKey, componentTheme[varKey]); this.applyTempOverrides(); } }, { default: () => this.locale.restore }); } }) : h(NInput, { key: varKey, onChange: this.applyTempOverrides, onUpdateValue: (value) => { - this.setTempOverrides(themeKey, varKey, value); - }, value: ((_d = (_c = this.tempOverrides) === null || _c === void 0 ? void 0 : _c[themeKey]) === null || _d === void 0 ? void 0 : _d[varKey]) || "", placeholder: componentTheme[varKey] }) + this.setTempOverrides(themeKey2, varKey, value); + }, value: ((_d = (_c = this.tempOverrides) === null || _c === void 0 ? void 0 : _c[themeKey2]) === null || _d === void 0 ? void 0 : _d[varKey]) || "", placeholder: componentTheme[varKey] }) ); } })) @@ -37108,7 +37775,7 @@ const _hoisted_1$h = { "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" }; -const _hoisted_2$g = /* @__PURE__ */ createBaseVNode( +const _hoisted_2$f = /* @__PURE__ */ createBaseVNode( "path", { d: "M368 96H144a16 16 0 0 1 0-32h224a16 16 0 0 1 0 32z", @@ -37118,7 +37785,7 @@ const _hoisted_2$g = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_3$f = /* @__PURE__ */ createBaseVNode( +const _hoisted_3$e = /* @__PURE__ */ createBaseVNode( "path", { d: "M400 144H112a16 16 0 0 1 0-32h288a16 16 0 0 1 0 32z", @@ -37138,11 +37805,11 @@ const _hoisted_4$b = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_5$8 = [_hoisted_2$g, _hoisted_3$f, _hoisted_4$b]; +const _hoisted_5$7 = [_hoisted_2$f, _hoisted_3$e, _hoisted_4$b]; const Albums = defineComponent({ name: "Albums", render: function render2(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$h, _hoisted_5$8); + return openBlock(), createElementBlock("svg", _hoisted_1$h, _hoisted_5$7); } }); const _hoisted_1$g = { @@ -37150,28 +37817,6 @@ const _hoisted_1$g = { "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" }; -const _hoisted_2$f = /* @__PURE__ */ createBaseVNode( - "path", - { - d: "M256 32C132.29 32 32 132.29 32 256s100.29 224 224 224s224-100.29 224-224S379.71 32 256 32zM128.72 383.28A180 180 0 0 1 256 76v360a178.82 178.82 0 0 1-127.28-52.72z", - fill: "currentColor" - }, - null, - -1 - /* HOISTED */ -); -const _hoisted_3$e = [_hoisted_2$f]; -const ContrastSharp = defineComponent({ - name: "ContrastSharp", - render: function render3(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$g, _hoisted_3$e); - } -}); -const _hoisted_1$f = { - xmlns: "http://www.w3.org/2000/svg", - "xmlns:xlink": "http://www.w3.org/1999/xlink", - viewBox: "0 0 512 512" -}; const _hoisted_2$e = /* @__PURE__ */ createBaseVNode( "path", { @@ -37202,14 +37847,14 @@ const _hoisted_4$a = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_5$7 = [_hoisted_2$e, _hoisted_3$d, _hoisted_4$a]; +const _hoisted_5$6 = [_hoisted_2$e, _hoisted_3$d, _hoisted_4$a]; const Create = defineComponent({ name: "Create", - render: function render4(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$f, _hoisted_5$7); + render: function render3(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$g, _hoisted_5$6); } }); -const _hoisted_1$e = { +const _hoisted_1$f = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37244,14 +37889,14 @@ const _hoisted_4$9 = /* @__PURE__ */ createBaseVNode( -1 /* HOISTED */ ); -const _hoisted_5$6 = [_hoisted_2$d, _hoisted_3$c, _hoisted_4$9]; +const _hoisted_5$5 = [_hoisted_2$d, _hoisted_3$c, _hoisted_4$9]; const Cube = defineComponent({ name: "Cube", - render: function render5(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$e, _hoisted_5$6); + render: function render4(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$f, _hoisted_5$5); } }); -const _hoisted_1$d = { +const _hoisted_1$e = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37259,7 +37904,7 @@ const _hoisted_1$d = { const _hoisted_2$c = /* @__PURE__ */ createBaseVNode( "path", { - d: "M48 170v196.92L240 480V284L48 170z", + d: "M428 224H288a48 48 0 0 1-48-48V36a4 4 0 0 0-4-4h-92a64 64 0 0 0-64 64v320a64 64 0 0 0 64 64h224a64 64 0 0 0 64-64V228a4 4 0 0 0-4-4zm-92 160H176a16 16 0 0 1 0-32h160a16 16 0 0 1 0 32zm0-80H176a16 16 0 0 1 0-32h160a16 16 0 0 1 0 32z", fill: "currentColor" }, null, @@ -37269,31 +37914,21 @@ const _hoisted_2$c = /* @__PURE__ */ createBaseVNode( const _hoisted_3$b = /* @__PURE__ */ createBaseVNode( "path", { - d: "M272 480l192-113.08V170L272 284zm176-122.36z", + d: "M419.22 188.59L275.41 44.78a2 2 0 0 0-3.41 1.41V176a16 16 0 0 0 16 16h129.81a2 2 0 0 0 1.41-3.41z", fill: "currentColor" }, null, -1 /* HOISTED */ ); -const _hoisted_4$8 = /* @__PURE__ */ createBaseVNode( - "path", - { - d: "M448 144L256 32L64 144l192 112l192-112z", - fill: "currentColor" - }, - null, - -1 - /* HOISTED */ -); -const _hoisted_5$5 = [_hoisted_2$c, _hoisted_3$b, _hoisted_4$8]; -const CubeSharp = defineComponent({ - name: "CubeSharp", - render: function render6(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$d, _hoisted_5$5); +const _hoisted_4$8 = [_hoisted_2$c, _hoisted_3$b]; +const DocumentText = defineComponent({ + name: "DocumentText", + render: function render5(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$e, _hoisted_4$8); } }); -const _hoisted_1$c = { +const _hoisted_1$d = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37321,11 +37956,11 @@ const _hoisted_3$a = /* @__PURE__ */ createBaseVNode( const _hoisted_4$7 = [_hoisted_2$b, _hoisted_3$a]; const Duplicate = defineComponent({ name: "Duplicate", - render: function render7(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$c, _hoisted_4$7); + render: function render6(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$d, _hoisted_4$7); } }); -const _hoisted_1$b = { +const _hoisted_1$c = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37343,11 +37978,11 @@ const _hoisted_2$a = /* @__PURE__ */ createBaseVNode( const _hoisted_3$9 = [_hoisted_2$a]; const Image$1 = defineComponent({ name: "Image", - render: function render8(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$b, _hoisted_3$9); + render: function render7(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$c, _hoisted_3$9); } }); -const _hoisted_1$a = { +const _hoisted_1$b = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37375,11 +38010,11 @@ const _hoisted_3$8 = /* @__PURE__ */ createBaseVNode( const _hoisted_4$6 = [_hoisted_2$9, _hoisted_3$8]; const Images = defineComponent({ name: "Images", - render: function render9(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$a, _hoisted_4$6); + render: function render8(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$b, _hoisted_4$6); } }); -const _hoisted_1$9 = { +const _hoisted_1$a = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37407,11 +38042,11 @@ const _hoisted_3$7 = /* @__PURE__ */ createBaseVNode( const _hoisted_4$5 = [_hoisted_2$8, _hoisted_3$7]; const PowerSharp = defineComponent({ name: "PowerSharp", - render: function render10(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$9, _hoisted_4$5); + render: function render9(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$a, _hoisted_4$5); } }); -const _hoisted_1$8 = { +const _hoisted_1$9 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37429,11 +38064,11 @@ const _hoisted_2$7 = /* @__PURE__ */ createBaseVNode( const _hoisted_3$6 = [_hoisted_2$7]; const SettingsSharp = defineComponent({ name: "SettingsSharp", - render: function render11(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$8, _hoisted_3$6); + render: function render10(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$9, _hoisted_3$6); } }); -const _hoisted_1$7 = { +const _hoisted_1$8 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37451,11 +38086,11 @@ const _hoisted_2$6 = /* @__PURE__ */ createBaseVNode( const _hoisted_3$5 = [_hoisted_2$6]; const Speedometer = defineComponent({ name: "Speedometer", - render: function render12(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$7, _hoisted_3$5); + render: function render11(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$8, _hoisted_3$5); } }); -const _hoisted_1$6 = { +const _hoisted_1$7 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37503,11 +38138,11 @@ const _hoisted_5$4 = /* @__PURE__ */ createBaseVNode( const _hoisted_6$2 = [_hoisted_2$5, _hoisted_3$4, _hoisted_4$4, _hoisted_5$4]; const StatsChart = defineComponent({ name: "StatsChart", - render: function render13(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$6, _hoisted_6$2); + render: function render12(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$7, _hoisted_6$2); } }); -const _hoisted_1$5 = { +const _hoisted_1$6 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37557,11 +38192,11 @@ const _hoisted_4$3 = /* @__PURE__ */ createBaseVNode( const _hoisted_5$3 = [_hoisted_2$4, _hoisted_3$3, _hoisted_4$3]; const SyncSharp = defineComponent({ name: "SyncSharp", - render: function render14(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$5, _hoisted_5$3); + render: function render13(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$6, _hoisted_5$3); } }); -const _hoisted_1$4 = { +const _hoisted_1$5 = { xmlns: "http://www.w3.org/2000/svg", "xmlns:xlink": "http://www.w3.org/1999/xlink", viewBox: "0 0 512 512" @@ -37569,12 +38204,8 @@ const _hoisted_1$4 = { const _hoisted_2$3 = /* @__PURE__ */ createBaseVNode( "path", { - d: "M332.69 320a115 115 0 0 0-152.8 0", - fill: "none", - stroke: "currentColor", - "stroke-linecap": "square", - "stroke-linejoin": "round", - "stroke-width": "42" + d: "M346.65 304.3a136 136 0 0 0-180.71 0a21 21 0 1 0 27.91 31.38a94 94 0 0 1 124.89 0a21 21 0 0 0 27.91-31.4z", + fill: "currentColor" }, null, -1 @@ -37583,12 +38214,8 @@ const _hoisted_2$3 = /* @__PURE__ */ createBaseVNode( const _hoisted_3$2 = /* @__PURE__ */ createBaseVNode( "path", { - d: "M393.74 259a201.26 201.26 0 0 0-274.92 0", - fill: "none", - stroke: "currentColor", - "stroke-linecap": "square", - "stroke-linejoin": "round", - "stroke-width": "42" + d: "M256.28 183.7a221.47 221.47 0 0 0-151.8 59.92a21 21 0 1 0 28.68 30.67a180.28 180.28 0 0 1 246.24 0a21 21 0 1 0 28.68-30.67a221.47 221.47 0 0 0-151.8-59.92z", + fill: "currentColor" }, null, -1 @@ -37597,21 +38224,19 @@ const _hoisted_3$2 = /* @__PURE__ */ createBaseVNode( const _hoisted_4$2 = /* @__PURE__ */ createBaseVNode( "path", { - d: "M448 191.52a288 288 0 0 0-383.44 0", - fill: "none", - stroke: "currentColor", - "stroke-linecap": "square", - "stroke-linejoin": "round", - "stroke-width": "42" + d: "M462 175.86a309 309 0 0 0-411.44 0a21 21 0 1 0 28 31.29a267 267 0 0 1 355.43 0a21 21 0 0 0 28-31.31z", + fill: "currentColor" }, null, -1 /* HOISTED */ ); const _hoisted_5$2 = /* @__PURE__ */ createBaseVNode( - "path", + "circle", { - d: "M300.67 384L256 433l-44.34-49a56.73 56.73 0 0 1 88.92 0z", + cx: "256.28", + cy: "393.41", + r: "32", fill: "currentColor" }, null, @@ -37619,14 +38244,14 @@ const _hoisted_5$2 = /* @__PURE__ */ createBaseVNode( /* HOISTED */ ); const _hoisted_6$1 = [_hoisted_2$3, _hoisted_3$2, _hoisted_4$2, _hoisted_5$2]; -const WifiSharp = defineComponent({ - name: "WifiSharp", - render: function render15(_ctx, _cache) { - return openBlock(), createElementBlock("svg", _hoisted_1$4, _hoisted_6$1); +const Wifi = defineComponent({ + name: "Wifi", + render: function render14(_ctx, _cache) { + return openBlock(), createElementBlock("svg", _hoisted_1$5, _hoisted_6$1); } }); /*! - * vue-router v4.2.2 + * vue-router v4.2.5 * (c) 2023 Eduardo San Martin Morote * @license MIT */ @@ -38511,7 +39136,7 @@ function normalizeRecordProps(record) { propsObject.default = props; } else { for (const name in record.components) - propsObject[name] = typeof props === "boolean" ? props : props[name]; + propsObject[name] = typeof props === "object" ? props[name] : props; } return propsObject; } @@ -38650,7 +39275,7 @@ function useCallbacks() { } return { add: add2, - list: () => handlers, + list: () => handlers.slice(), reset }; } @@ -39164,8 +39789,8 @@ function createRouter(options) { return runGuardQueue(guards); }).then(() => { guards = []; - for (const record of to.matched) { - if (record.beforeEnter && !from.matched.includes(record)) { + for (const record of enteringRecords) { + if (record.beforeEnter) { if (isArray(record.beforeEnter)) { for (const beforeEnter of record.beforeEnter) guards.push(guardToPromiseFn(beforeEnter, to, from)); @@ -39195,9 +39820,7 @@ function createRouter(options) { ) ? err : Promise.reject(err)); } function triggerAfterEach(to, from, failure) { - for (const guard of afterGuards.list()) { - runWithContext(() => guard(to, from, failure)); - } + afterGuards.list().forEach((guard) => runWithContext(() => guard(to, from, failure))); } function finalizeNavigation(toLocation, from, isPush, replace2, data) { const error = checkCanceledNavigation(toLocation, from); @@ -39296,11 +39919,11 @@ function createRouter(options) { }); } let readyHandlers = useCallbacks(); - let errorHandlers = useCallbacks(); + let errorListeners = useCallbacks(); let ready; function triggerError(error, to, from) { markAsReady(error); - const list = errorHandlers.list(); + const list = errorListeners.list(); if (list.length) { list.forEach((handler) => handler(error, to, from)); } else { @@ -39351,7 +39974,7 @@ function createRouter(options) { beforeEach: beforeGuards.add, beforeResolve: beforeResolveGuards.add, afterEach: afterGuards.add, - onError: errorHandlers.add, + onError: errorListeners.add, isReady, install(app2) { const router3 = this; @@ -39371,10 +39994,13 @@ function createRouter(options) { } const reactiveRoute = {}; for (const key in START_LOCATION_NORMALIZED) { - reactiveRoute[key] = computed(() => currentRoute.value[key]); + Object.defineProperty(reactiveRoute, key, { + get: () => currentRoute.value[key], + enumerable: true + }); } app2.provide(routerKey, router3); - app2.provide(routeLocationKey, reactive(reactiveRoute)); + app2.provide(routeLocationKey, shallowReactive(reactiveRoute)); app2.provide(routerViewLocationKey, currentRoute); const unmountApp = app2.unmount; installedApps.add(app2); @@ -39422,8 +40048,8 @@ function extractChangingRecords(to, from) { function useRouter() { return inject(routerKey); } -const _hoisted_1$3 = { class: "navbar" }; -const _sfc_main$5 = /* @__PURE__ */ defineComponent({ +const _hoisted_1$4 = { class: "navbar" }; +const _sfc_main$8 = /* @__PURE__ */ defineComponent({ __name: "CollapsibleNavbar", setup(__props) { function renderIcon(icon) { @@ -39436,17 +40062,17 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ icon: renderIcon(Image$1) }, { - label: () => h( - RouterLink, - { to: "/image2image" }, - { default: () => "Image to Image" } - ), - key: "image2image", + label: () => h(RouterLink, { to: "/img2img" }, { default: () => "Image to Image" }), + key: "img2img", icon: renderIcon(Images) }, { - label: () => h(RouterLink, { to: "/extra" }, { default: () => "Extra" }), - key: "extra", + label: () => h( + RouterLink, + { to: "/imageProcessing" }, + { default: () => "Image Processing" } + ), + key: "imageProcessing", icon: renderIcon(Duplicate) }, { @@ -39473,6 +40099,11 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ key: "plugins", icon: renderIcon(Speedometer) }, + // { + // label: () => h(RouterLink, { to: "/extra" }, { default: () => "Extra" }), + // key: "extra", + // icon: renderIcon(Archive), + // }, { label: () => h(RouterLink, { to: "/settings" }, { default: () => "Settings" }), key: "settings", @@ -39481,7 +40112,7 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({ ]; let collapsed = ref(true); return (_ctx, _cache) => { - return openBlock(), createElementBlock("div", _hoisted_1$3, [ + return openBlock(), createElementBlock("div", _hoisted_1$4, [ createVNode(unref(NLayout), { style: { "height": "100%", "overflow": "visible" }, "has-sider": "", @@ -39539,7 +40170,7 @@ const serverUrl = loc.protocol + "//" + loc.host; const webSocketUrl = new_uri + "//" + loc.host; const huggingfaceModelsFile = "https://raw.githubusercontent.com/VoltaML/voltaML-fast-stable-diffusion/experimental/static/huggingface-models.json"; const defaultCapabilities = { - supported_backends: ["cpu"], + supported_backends: [["CPU", "cpu"]], supported_precisions_cpu: ["float32"], supported_precisions_gpu: ["float32"], supported_torch_compile_backends: ["inductor"], @@ -39592,7 +40223,7 @@ const useState = defineStore("state", () => { img2img: { images: [], currentImage: "", - tab: "Image to Image", + tab: "img2img", genData: { time_taken: null, seed: null @@ -39622,22 +40253,18 @@ const useState = defineStore("state", () => { seed: null } }, - sd_upscale: { + imageProcessing: { images: [], currentImage: "", - genData: { - time_taken: null, - seed: null - } + tab: "upscale" }, extra: { - images: [], - currentImage: "", - tab: "Upscale" + tab: "dependencies" }, tagger: { positivePrompt: /* @__PURE__ */ new Map(), - negativePrompt: /* @__PURE__ */ new Map() + negativePrompt: /* @__PURE__ */ new Map(), + tab: "tagger" }, current_step: 0, total_steps: 0, @@ -39648,32 +40275,106 @@ const useState = defineStore("state", () => { time: 0 }, currentImageByte64: "", - currentImageMetadata: /* @__PURE__ */ new Map() + currentImageMetadata: {} }, perf_drawer: { enabled: false, gpus: [] }, + log_drawer: { + enabled: false, + logs: [] + }, models: [], selected_model: ref(null), secrets: { huggingface: "ok" }, autofill: [], + autofill_special: [], capabilities: defaultCapabilities // Should get replaced at runtime }); async function fetchCapabilites() { state.capabilities = await getCapabilities(); } - return { state, fetchCapabilites }; + async function fetchAutofill() { + fetch(`${serverUrl}/api/autofill`).then(async (response) => { + if (response.status === 200) { + const arr = await response.json(); + state.autofill = arr; + console.log("Autofill data successfully fetched from the server"); + } else { + console.error("Failed to fetch autofill data"); + } + }); + } + return { state, fetchCapabilites, fetchAutofill }; }); -const _hoisted_1$2 = { style: { "width": "100%", "display": "inline-flex", "align-items": "center" } }; +const _sfc_main$7 = /* @__PURE__ */ defineComponent({ + __name: "InitHandler", + setup(__props) { + console.log( + ` + ██╗ ██╗ █████╗ ██╗ ████████╗ █████╗ ███╗ ███╗██╗ + ██║ ██║██╔══██╗██║ ╚══██╔══╝██╔══██╗████╗ ████║██║ + ╚██╗ ██╔╝██║ ██║██║ ██║ ███████║██╔████╔██║██║ + ╚████╔╝ ██║ ██║██║ ██║ ██╔══██║██║╚██╔╝██║██║ + ╚██╔╝ ╚█████╔╝███████╗ ██║ ██║ ██║██║ ╚═╝ ██║███████╗ + ╚═╝ ╚════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ + ` + ); + const global2 = useState(); + global2.fetchCapabilites().then(() => { + console.log("Capabilities successfully fetched from the server"); + }); + global2.fetchAutofill(); + return (_ctx, _cache) => { + return null; + }; + } +}); +const _sfc_main$6 = /* @__PURE__ */ defineComponent({ + __name: "LogDrawer", + setup(__props) { + const glob = useState(); + const log = computed(() => glob.state.log_drawer.logs.join("\n")); + return (_ctx, _cache) => { + return openBlock(), createBlock(unref(NDrawer), { + placement: "bottom", + show: unref(glob).state.log_drawer.enabled, + "onUpdate:show": _cache[0] || (_cache[0] = ($event) => unref(glob).state.log_drawer.enabled = $event), + "auto-focus": false, + "show-mask": true, + height: "70vh" + }, { + default: withCtx(() => [ + createVNode(unref(NDrawerContent), { + closable: "", + title: "Log - 500 latest messages" + }, { + default: withCtx(() => [ + createVNode(unref(NLog), { + ref: "logRef", + log: log.value, + trim: "", + style: { "height": "100%" } + }, null, 8, ["log"]) + ]), + _: 1 + }) + ]), + _: 1 + }, 8, ["show"]); + }; + } +}); +const _hoisted_1$3 = { style: { "width": "100%", "display": "inline-flex", "align-items": "center" } }; const _hoisted_2$2 = /* @__PURE__ */ createBaseVNode("p", { style: { "width": "108px" } }, "Utilization", -1); const _hoisted_3$1 = { style: { "width": "100%", "display": "inline-flex", "align-items": "center" } }; const _hoisted_4$1 = /* @__PURE__ */ createBaseVNode("p", { style: { "width": "108px" } }, "Memory", -1); const _hoisted_5$1 = { style: { "align-self": "flex-end", "margin-left": "12px" } }; -const _sfc_main$4 = /* @__PURE__ */ defineComponent({ +const _sfc_main$5 = /* @__PURE__ */ defineComponent({ __name: "PerformanceDrawer", setup(__props) { const global2 = useState(); @@ -39710,7 +40411,7 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ ]), _: 2 }, 1024), - createBaseVNode("div", _hoisted_1$2, [ + createBaseVNode("div", _hoisted_1$3, [ _hoisted_2$2, createVNode(unref(NProgress), { percentage: gpu.utilization, @@ -39743,12 +40444,12 @@ const _sfc_main$4 = /* @__PURE__ */ defineComponent({ }; } }); -const _hoisted_1$1 = /* @__PURE__ */ createBaseVNode("a", { +const _hoisted_1$2 = /* @__PURE__ */ createBaseVNode("a", { target: "_blank", href: "https://huggingface.co/settings/tokens" }, "this page", -1); const _hoisted_2$1 = { style: { "margin-top": "8px", "width": "100%", "display": "flex", "justify-content": "end" } }; -const _sfc_main$3 = /* @__PURE__ */ defineComponent({ +const _sfc_main$4 = /* @__PURE__ */ defineComponent({ __name: "SecretsHandler", setup(__props) { const message = useMessage(); @@ -39789,7 +40490,7 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ createVNode(unref(NText), null, { default: withCtx(() => [ createTextVNode(" API does not have a HuggingFace token. Please enter a valid token to continue. You can get a token from "), - _hoisted_1$1 + _hoisted_1$2 ]), _: 1 }), @@ -39820,127 +40521,13 @@ const _sfc_main$3 = /* @__PURE__ */ defineComponent({ }; } }); -function progressForward(progress, global2) { - if (progress === 0) { - return 0; - } else if (global2.state.progress <= progress) { - return progress; - } else { - return global2.state.progress; - } -} -function currentStepForward(currentStep, global2) { - if (currentStep === 0) { - return 0; - } else if (global2.state.current_step <= currentStep) { - return currentStep; - } else { - return global2.state.current_step; - } -} -function processWebSocket(message, global2, notificationProvider) { - switch (message.type) { - case "test": { - break; - } - case "progress": { - global2.state.progress = message.data.progress; - break; - } - case "txt2img": { - global2.state.txt2img.currentImage = message.data.image ? message.data.image : global2.state.txt2img.currentImage; - global2.state.progress = progressForward(message.data.progress, global2); - global2.state.current_step = currentStepForward( - message.data.current_step, - global2 - ); - global2.state.total_steps = message.data.total_steps; - break; - } - case "img2img": { - global2.state.img2img.currentImage = message.data.image ? message.data.image : global2.state.img2img.currentImage; - global2.state.progress = progressForward(message.data.progress, global2); - global2.state.current_step = currentStepForward( - message.data.current_step, - global2 - ); - global2.state.total_steps = message.data.total_steps; - break; - } - case "inpainting": { - global2.state.inpainting.currentImage = message.data.image ? message.data.image : global2.state.inpainting.currentImage; - global2.state.progress = progressForward(message.data.progress, global2); - global2.state.current_step = currentStepForward( - message.data.current_step, - global2 - ); - global2.state.total_steps = message.data.total_steps; - break; - } - case "controlnet": { - global2.state.controlnet.currentImage = message.data.image ? message.data.image : global2.state.controlnet.currentImage; - global2.state.progress = progressForward(message.data.progress, global2); - global2.state.current_step = currentStepForward( - message.data.current_step, - global2 - ); - global2.state.total_steps = message.data.total_steps; - break; - } - case "notification": { - message.data.timeout = message.data.timeout || 5e3; - notificationProvider.create({ - type: message.data.severity, - title: message.data.title, - content: message.data.message, - duration: message.data.timeout - }); - break; - } - case "aitemplate_compile": { - global2.state.aitBuildStep = { - ...global2.state.aitBuildStep, - ...message.data - }; - break; - } - case "onnx_compile": { - global2.state.onnxBuildStep = { - ...global2.state.onnxBuildStep, - ...message.data - }; - break; - } - case "cluster_stats": { - global2.state.perf_drawer.gpus = message.data; - break; - } - case "token": { - if (message.data.huggingface === "missing") { - global2.state.secrets.huggingface = "missing"; - } - break; - } - case "refresh_capabilities": { - global2.fetchCapabilites().then(() => { - console.log("Capabilities refreshed"); - }).catch((error) => { - console.error(error); - }); - break; - } - default: { - console.log(message); - } - } -} var _a; const isClient = typeof window !== "undefined"; const isFunction = (val) => typeof val === "function"; const isString = (val) => typeof val === "string"; const noop = () => { }; -isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && /iP(ad|hone|od)/.test(window.navigator.userAgent); +const isIOS = isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && /iP(ad|hone|od)/.test(window.navigator.userAgent); function resolveUnref(r) { return typeof r === "function" ? r() : unref(r); } @@ -40047,6 +40634,55 @@ function useEventListener(...args) { tryOnScopeDispose(stop); return stop; } +let _iOSWorkaround = false; +function onClickOutside(target, handler, options = {}) { + const { window: window2 = defaultWindow, ignore = [], capture = true, detectIframe = false } = options; + if (!window2) + return; + if (isIOS && !_iOSWorkaround) { + _iOSWorkaround = true; + Array.from(window2.document.body.children).forEach((el) => el.addEventListener("click", noop)); + } + let shouldListen = true; + const shouldIgnore = (event) => { + return ignore.some((target2) => { + if (typeof target2 === "string") { + return Array.from(window2.document.querySelectorAll(target2)).some((el) => el === event.target || event.composedPath().includes(el)); + } else { + const el = unrefElement(target2); + return el && (event.target === el || event.composedPath().includes(el)); + } + }); + }; + const listener = (event) => { + const el = unrefElement(target); + if (!el || el === event.target || event.composedPath().includes(el)) + return; + if (event.detail === 0) + shouldListen = !shouldIgnore(event); + if (!shouldListen) { + shouldListen = true; + return; + } + handler(event); + }; + const cleanup = [ + useEventListener(window2, "click", listener, { passive: true, capture }), + useEventListener(window2, "pointerdown", (e) => { + const el = unrefElement(target); + if (el) + shouldListen = !e.composedPath().includes(el) && !shouldIgnore(e); + }, { passive: true }), + detectIframe && useEventListener(window2, "blur", (event) => { + var _a2; + const el = unrefElement(target); + if (((_a2 = window2.document.activeElement) == null ? void 0 : _a2.tagName) === "IFRAME" && !(el == null ? void 0 : el.contains(window2.document.activeElement))) + handler(event); + }) + ].filter(Boolean); + const stop = () => cleanup.forEach((fn) => fn()); + return stop; +} const _global = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {}; const globalKey = "__vueuse_ssr_handlers__"; _global[globalKey] = _global[globalKey] || {}; @@ -40242,6 +40878,131 @@ function useWebSocket(url, options = {}) { ws: wsRef }; } +function progressForward(progress, global2) { + if (progress === 0) { + return 0; + } else if (global2.state.progress <= progress) { + return progress; + } else { + return global2.state.progress; + } +} +function currentStepForward(currentStep, global2) { + if (currentStep === 0) { + return 0; + } else if (global2.state.current_step <= currentStep) { + return currentStep; + } else { + return global2.state.current_step; + } +} +function processWebSocket(message, global2, notificationProvider) { + switch (message.type) { + case "test": { + break; + } + case "progress": { + global2.state.progress = message.data.progress; + break; + } + case "txt2img": { + global2.state.txt2img.currentImage = message.data.image ? message.data.image : global2.state.txt2img.currentImage; + global2.state.progress = progressForward(message.data.progress, global2); + global2.state.current_step = currentStepForward( + message.data.current_step, + global2 + ); + global2.state.total_steps = message.data.total_steps; + break; + } + case "img2img": { + global2.state.img2img.currentImage = message.data.image ? message.data.image : global2.state.img2img.currentImage; + global2.state.progress = progressForward(message.data.progress, global2); + global2.state.current_step = currentStepForward( + message.data.current_step, + global2 + ); + global2.state.total_steps = message.data.total_steps; + break; + } + case "inpainting": { + global2.state.inpainting.currentImage = message.data.image ? message.data.image : global2.state.inpainting.currentImage; + global2.state.progress = progressForward(message.data.progress, global2); + global2.state.current_step = currentStepForward( + message.data.current_step, + global2 + ); + global2.state.total_steps = message.data.total_steps; + break; + } + case "controlnet": { + global2.state.controlnet.currentImage = message.data.image ? message.data.image : global2.state.controlnet.currentImage; + global2.state.progress = progressForward(message.data.progress, global2); + global2.state.current_step = currentStepForward( + message.data.current_step, + global2 + ); + global2.state.total_steps = message.data.total_steps; + break; + } + case "notification": { + message.data.timeout = message.data.timeout || 5e3; + console.log(message.data.message); + notificationProvider.create({ + type: message.data.severity, + title: message.data.title, + content: message.data.message, + duration: message.data.timeout + }); + break; + } + case "aitemplate_compile": { + global2.state.aitBuildStep = { + ...global2.state.aitBuildStep, + ...message.data + }; + break; + } + case "onnx_compile": { + global2.state.onnxBuildStep = { + ...global2.state.onnxBuildStep, + ...message.data + }; + break; + } + case "cluster_stats": { + global2.state.perf_drawer.gpus = message.data; + break; + } + case "token": { + if (message.data.huggingface === "missing") { + global2.state.secrets.huggingface = "missing"; + } + break; + } + case "refresh_capabilities": { + global2.fetchCapabilites().then(() => { + console.log("Capabilities refreshed"); + }).catch((error) => { + console.error(error); + }); + break; + } + case "log": { + const messages = message.data.message.split("\n"); + for (const msg2 of messages) { + global2.state.log_drawer.logs.splice(0, 0, msg2); + if (global2.state.log_drawer.logs.length > 500) { + global2.state.log_drawer.logs.pop(); + } + } + break; + } + default: { + console.log(message); + } + } +} const useWebsocket = defineStore("websocket", () => { const notificationProvider = useNotification(); const messageProvider = useMessage(); @@ -40315,6 +41076,10 @@ const useWebsocket = defineStore("websocket", () => { const spaceRegex = new RegExp("[\\s,]+"); const arrowKeys = [38, 40]; let currentFocus = -1; +function convertToTextString(str) { + const upper = str.charAt(0).toUpperCase() + str.slice(1); + return upper.replace(/_/g, " "); +} function addActive(x) { if (!x) return false; @@ -40364,7 +41129,7 @@ async function startWebsocket(messageProvider) { } function getTextBoundaries(elem) { if (elem === null) { - console.log("Element is null"); + console.error("Element is null"); return [0, 0]; } if (elem.tagName === "INPUT" && elem.type === "text" || elem.tagName === "TEXTAREA") { @@ -40373,7 +41138,7 @@ function getTextBoundaries(elem) { elem.selectionEnd === null ? 0 : elem.selectionEnd ]; } - console.log("Element is not input"); + console.error("Element is not input"); return [0, 0]; } function promptHandleKeyUp(e, data, key, globalState) { @@ -40465,19 +41230,38 @@ function promptHandleKeyUp(e, data, key, globalState) { return false; } const toAppend = []; - for (let i = 0; i < globalState.state.autofill.length; i++) { - if (globalState.state.autofill[i].toUpperCase().includes(currentTokenStripped.toUpperCase())) { + for (let i = 0; i < globalState.state.autofill_special.length; i++) { + if (globalState.state.autofill_special[i].toLowerCase().includes(currentTokenStripped.toLowerCase())) { const b = document.createElement("DIV"); - b.innerText = globalState.state.autofill[i]; - b.innerHTML += ""; + b.innerText = globalState.state.autofill_special[i]; + b.innerHTML += ""; b.addEventListener("click", function() { - input.value = text.substring(0, text.lastIndexOf(",") + 1) + globalState.state.autofill[i]; + input.value = text.substring(0, text.lastIndexOf(",") + 1) + globalState.state.autofill_special[i]; data[key] = input.value; closeAllLists(void 0, input); }); toAppend.push(b); } } + const lowercaseStrippedToken = currentTokenStripped.toLowerCase(); + if (lowercaseStrippedToken.length >= 3) { + for (let i = 0; i < globalState.state.autofill.length; i++) { + if (globalState.state.autofill[i].toLowerCase().includes(lowercaseStrippedToken)) { + if (toAppend.length >= 30) { + break; + } + const b = document.createElement("DIV"); + b.innerText = globalState.state.autofill[i]; + b.innerHTML += ""; + b.addEventListener("click", function() { + input.value = text.substring(0, text.lastIndexOf(",") + 1) + globalState.state.autofill[i]; + data[key] = input.value; + closeAllLists(void 0, input); + }); + toAppend.push(b); + } + } + } if (toAppend.length === 0) { return false; } @@ -40488,6 +41272,9 @@ function promptHandleKeyUp(e, data, key, globalState) { for (let i = 0; i < toAppend.length; i++) { div.appendChild(toAppend[i]); } + onClickOutside(div, () => { + closeAllLists(void 0, input); + }); const autocompleteList = document.getElementById("autocomplete-list"); const x = autocompleteList == null ? void 0 : autocompleteList.getElementsByTagName("div"); if (e.key === "ArrowDown") { @@ -40524,6 +41311,18 @@ function urlFromPath(path) { const url = new URL(path, serverUrl); return url.href; } +var Backends = /* @__PURE__ */ ((Backends2) => { + Backends2[Backends2["PyTorch"] = 0] = "PyTorch"; + Backends2[Backends2["AITemplate"] = 1] = "AITemplate"; + Backends2[Backends2["ONNX"] = 2] = "ONNX"; + Backends2[Backends2["unknown"] = 3] = "unknown"; + Backends2[Backends2["LoRA"] = 4] = "LoRA"; + Backends2[Backends2["LyCORIS"] = 5] = "LyCORIS"; + Backends2[Backends2["VAE"] = 6] = "VAE"; + Backends2[Backends2["Textual Inversion"] = 7] = "Textual Inversion"; + Backends2[Backends2["Upscaler"] = 8] = "Upscaler"; + return Backends2; +})(Backends || {}); var ControlNetType = /* @__PURE__ */ ((ControlNetType2) => { ControlNetType2["CANNY"] = "lllyasviel/sd-controlnet-canny"; ControlNetType2["DEPTH"] = "lllyasviel/sd-controlnet-depth"; @@ -40565,7 +41364,7 @@ const defaultSettings = { batch_size: 1, negative_prompt: "", self_attention_scale: 0, - use_karras_sigmas: false + sigmas: "automatic" }, img2img: { width: 512, @@ -40581,7 +41380,7 @@ const defaultSettings = { denoising_strength: 0.6, image: "", self_attention_scale: 0, - use_karras_sigmas: false + sigmas: "automatic" }, inpainting: { prompt: "", @@ -40597,7 +41396,7 @@ const defaultSettings = { batch_size: 1, sampler: 8, self_attention_scale: 0, - use_karras_sigmas: false + sigmas: "automatic" }, controlnet: { prompt: "", @@ -40617,7 +41416,7 @@ const defaultSettings = { is_preprocessed: false, save_preprocessed: false, return_preprocessed: true, - use_karras_sigmas: false + sigmas: "automatic" }, upscale: { image: "", @@ -40634,7 +41433,7 @@ const defaultSettings = { api: { websocket_sync_interval: 0.02, websocket_perf_interval: 1, - image_preview_delay: 2, + enable_websocket_logging: true, clip_skip: 1, clip_quantization: "full", autocast: true, @@ -40647,8 +41446,7 @@ const defaultSettings = { trace_model: false, cudnn_benchmark: false, offload: "disabled", - device_id: 0, - device_type: "cuda", + device: "cuda:0", data_type: "float16", use_tomesd: true, tomesd_ratio: 0.4, @@ -40658,6 +41456,8 @@ const defaultSettings = { clear_memory_policy: "always", huggingface_style_parsing: false, autoloaded_textual_inversions: [], + autoloaded_models: [], + autoloaded_vae: {}, save_path_template: "{folder}/{prompt}/{id}-{index}.{extension}", image_extension: "png", image_quality: 95, @@ -40666,7 +41466,14 @@ const defaultSettings = { torch_compile_fullgraph: false, torch_compile_dynamic: false, torch_compile_backend: "inductor", - torch_compile_mode: "default" + torch_compile_mode: "default", + hypertile: false, + hypertile_unet_chunk: 256, + sgm_noise_multiplier: false, + kdiffusers_quantization: true, + generator: "device", + live_preview_method: "approximation", + live_preview_delay: 2 }, aitemplate: { num_threads: 8 @@ -40691,8 +41498,10 @@ const defaultSettings = { enable_theme_editor: false, image_browser_columns: 5, on_change_timer: 2e3, - nsfw_ok_threshold: 0 - } + nsfw_ok_threshold: 0, + background_image_override: "" + }, + sampler_config: {} }; let rSettings = JSON.parse(JSON.stringify(defaultSettings)); try { @@ -40716,6 +41525,22 @@ class Settings { return JSON.stringify(this.settings); } } +const diffusersSchedulerTuple = { + DDIM: 1, + DDPM: 2, + PNDM: 3, + LMSD: 4, + EulerDiscrete: 5, + HeunDiscrete: 6, + EulerAncestralDiscrete: 7, + DPMSolverMultistep: 8, + DPMSolverSinglestep: 9, + KDPM2Discrete: 10, + KDPM2AncestralDiscrete: 11, + DEISMultistep: 12, + UniPCMultistep: 13, + DPMSolverSDEScheduler: 14 +}; const upscalerOptions = [ { label: "RealESRGAN_x4plus", @@ -40741,56 +41566,38 @@ const upscalerOptions = [ function getSchedulerOptions() { const scheduler_options = [ { - label: "DDIM", - value: 1 - }, - { - label: "DDPM", - value: 2 - }, - { - label: "PNDM", - value: 3 - }, - { - label: "LMSD", - value: 4 - }, - { - label: "EulerDiscrete", - value: 5 - }, - { - label: "HeunDiscrete", - value: 6 - }, - { - label: "EulerAncestralDiscrete", - value: 7 - }, - { - label: "DPMSolverMultistep", - value: 8 - }, - { - label: "DPMSolverSinglestep", - value: 9 - }, - { - label: "KDPM2Discrete", - value: 10 - }, - { - label: "KDPM2AncestralDiscrete", - value: 11 - }, - { - label: "DEISMultistep", - value: 12 + type: "group", + label: "k-diffusion", + key: "K-Diffusion", + children: [ + { label: "Euler a", value: "euler_a" }, + { label: "Euler", value: "euler" }, + { label: "LMS", value: "lms" }, + { label: "Heun", value: "heun" }, + { label: "DPM Fast", value: "dpm_fast" }, + { label: "DPM Adaptive", value: "dpm_adaptive" }, + { label: "DPM2", value: "dpm2" }, + { label: "DPM2 a", value: "dpm2_a" }, + { label: "DPM++ 2S a", value: "dpmpp_2s_a" }, + { label: "DPM++ 2M", value: "dpmpp_2m" }, + { label: "DPM++ 2M Sharp", value: "dpmpp_2m_sharp" }, + { label: "DPM++ SDE", value: "dpmpp_sde" }, + { label: "DPM++ 2M SDE", value: "dpmpp_2m_sde" }, + { label: "DPM++ 3M SDE", value: "dpmpp_3m_sde" }, + { label: "UniPC Multistep", value: "unipc_multistep" }, + { label: "Restart", value: "restart" } + ] }, { - label: "UniPCMultistep", - value: 13 + type: "group", + label: "Diffusers", + key: "diffusers", + children: Object.keys(diffusersSchedulerTuple).map((key) => { + return { + label: key, + value: diffusersSchedulerTuple[key] + }; + }) } ]; return scheduler_options; @@ -40919,8 +41726,8 @@ const useSettings = defineStore("settings", () => { resetSettings }; }); -const _withScopeId = (n) => (pushScopeId("data-v-2cf1de9c"), n = n(), popScopeId(), n); -const _hoisted_1 = { class: "top-bar" }; +const _withScopeId = (n) => (pushScopeId("data-v-44d84e0e"), n = n(), popScopeId(), n); +const _hoisted_1$1 = { class: "top-bar" }; const _hoisted_2 = { key: 0 }; const _hoisted_3 = { key: 1 }; const _hoisted_4 = { key: 2 }; @@ -40935,16 +41742,13 @@ const _hoisted_12 = { style: { "display": "inline-flex" } }; const _hoisted_13 = { key: 1 }; const _hoisted_14 = { class: "progress-container" }; const _hoisted_15 = { style: { "display": "inline-flex", "align-items": "center" } }; -const _sfc_main$2 = /* @__PURE__ */ defineComponent({ +const _sfc_main$3 = /* @__PURE__ */ defineComponent({ __name: "TopBar", setup(__props) { - useCssVars((_ctx) => ({ - "67e22a10": backgroundColor.value - })); const router2 = useRouter(); const websocketState = useWebsocket(); const global2 = useState(); - const conf = useSettings(); + const settings = useSettings(); const modelsLoading = ref(false); const filter = ref(""); const filteredModels = computed(() => { @@ -40956,21 +41760,39 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ return filteredModels.value.filter((model) => { return model.backend === "PyTorch" && model.valid === true; }).sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; + } else { + return a.name.localeCompare(b.name); + } }); }); const aitModels = computed(() => { return filteredModels.value.filter((model) => { return model.backend === "AITemplate"; }).sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; + } else { + return a.name.localeCompare(b.name); + } }); }); const onnxModels = computed(() => { return filteredModels.value.filter((model) => { return model.backend === "ONNX"; }).sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; + } else { + return a.name.localeCompare(b.name); + } }); }); const vaeModels = computed(() => { @@ -40984,6 +41806,24 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ vae: "default", textual_inversions: [] }, + { + name: "Tiny VAE (fast)", + path: "madebyollin/taesd", + backend: "VAE", + valid: true, + state: "not loaded", + vae: "madebyollin/taesd", + textual_inversions: [] + }, + { + name: "Asymmetric VAE", + path: "cross-attention/asymmetric-autoencoder-kl-x-1-5", + backend: "VAE", + valid: true, + state: "not loaded", + vae: "cross-attention/asymmetric-autoencoder-kl-x-1-5", + textual_inversions: [] + }, ...filteredModels.value.filter((model) => { return model.backend === "VAE"; }).sort((a, b) => { @@ -40995,7 +41835,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ return filteredModels.value.filter((model) => { return model.backend === "Textual Inversion"; }).sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; + } else { + return a.name.localeCompare(b.name); + } }); }); function refreshModels() { @@ -41015,13 +41861,13 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }).then(() => { fetch(`${serverUrl}/api/models/loaded`).then((res) => { res.json().then((data) => { - if (conf.data.settings.model) { + if (settings.data.settings.model) { if (!data.find((model) => { var _a2; - return model.path === ((_a2 = conf.data.settings.model) == null ? void 0 : _a2.path); + return model.path === ((_a2 = settings.data.settings.model) == null ? void 0 : _a2.path); })) { console.log("Current model is not loaded anymore"); - conf.data.settings.model = null; + settings.data.settings.model = null; } } data.forEach((loadedModel) => { @@ -41032,7 +41878,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ Object.assign(model, loadedModel); } }); - if (!conf.data.settings.model) { + if (!settings.data.settings.model) { const allLoaded = [ ...loadedPyTorchModels.value, ...loadedAitModels.value, @@ -41041,29 +41887,28 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ ]; console.log("All loaded models: ", allLoaded); if (allLoaded.length > 0) { - conf.data.settings.model = allLoaded[0]; + settings.data.settings.model = allLoaded[0]; console.log( - "Set current model to first available model: ", - conf.data.settings.model + "Setting current model to first available model: ", + settings.data.settings.model ); } else { - console.log("No models available"); - conf.data.settings.model = null; + console.log("No models available, setting current model to null"); + settings.data.settings.model = null; } } try { - if (conf.data.settings.model) { - const spl = conf.data.settings.model.name.split("__")[1]; + if (settings.data.settings.model) { + const spl = settings.data.settings.model.name.split("__")[1]; const regex = /([\d]+-[\d]+)x([\d]+-[\d]+)x([\d]+-[\d]+)/g; const matches = regex.exec(spl); - console.log("Match: ", matches); if (matches) { const width = matches[1].split("-").map((x) => parseInt(x)); const height = matches[2].split("-").map((x) => parseInt(x)); const batch_size = matches[3].split("-").map((x) => parseInt(x)); - conf.data.settings.aitDim.width = width; - conf.data.settings.aitDim.height = height; - conf.data.settings.aitDim.batch_size = batch_size; + settings.data.settings.aitDim.width = width; + settings.data.settings.aitDim.height = height; + settings.data.settings.aitDim.batch_size = batch_size; } else { throw new Error("Invalid model name for AIT dimensions parser"); } @@ -41071,10 +41916,9 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ throw new Error("No model, cannot parse AIT dimensions"); } } catch (e) { - console.warn(e); - conf.data.settings.aitDim.width = void 0; - conf.data.settings.aitDim.height = void 0; - conf.data.settings.aitDim.batch_size = void 0; + settings.data.settings.aitDim.width = void 0; + settings.data.settings.aitDim.height = void 0; + settings.data.settings.aitDim.batch_size = void 0; } const autofillKeys = []; for (const model of global2.state.models) { @@ -41082,7 +41926,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ autofillKeys.push(``); } } - global2.state.autofill = autofillKeys; + global2.state.autofill_special = autofillKeys; }); }); }).catch((e) => { @@ -41177,22 +42021,22 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ return model2.path === modelName && model2.backend === modelBackend; }); if (model) { - conf.data.settings.model = model; + settings.data.settings.model = model; } else { message.error("Model not found"); } try { - if (conf.data.settings.model) { - const spl = conf.data.settings.model.name.split("__")[1]; + if (settings.data.settings.model) { + const spl = settings.data.settings.model.name.split("__")[1]; const regex = /([\d]+-[\d]+)x([\d]+-[\d]+)x([\d]+-[\d]+)/g; const match2 = spl.match(regex); if (match2) { const width = match2[0].split("-").map((x) => parseInt(x)); const height = match2[1].split("-").map((x) => parseInt(x)); const batch_size = match2[2].split("-").map((x) => parseInt(x)); - conf.data.settings.aitDim.width = width; - conf.data.settings.aitDim.height = height; - conf.data.settings.aitDim.batch_size = batch_size; + settings.data.settings.aitDim.width = width; + settings.data.settings.aitDim.height = height; + settings.data.settings.aitDim.batch_size = batch_size; } else { throw new Error("Invalid model name for AIT dimensions parser"); } @@ -41201,21 +42045,15 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ } } catch (e) { console.warn(e); - conf.data.settings.aitDim.width = void 0; - conf.data.settings.aitDim.height = void 0; - conf.data.settings.aitDim.batch_size = void 0; + settings.data.settings.aitDim.width = void 0; + settings.data.settings.aitDim.height = void 0; + settings.data.settings.aitDim.batch_size = void 0; } } function resetModels() { global2.state.models.splice(0, global2.state.models.length); console.log("Reset models"); } - const perfIcon = () => { - return h(StatsChart); - }; - const themeIcon = () => { - return h(ContrastSharp); - }; websocketState.onConnectedCallbacks.push(() => { refreshModels(); }); @@ -41324,6 +42162,16 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }; }; const dropdownOptions = [ + { + label: "Log", + key: "log", + icon: renderIcon(DocumentText) + }, + { + label: "Performance", + key: "performance", + icon: renderIcon(StatsChart) + }, { label: "Reconnect", key: "reconnect", @@ -41341,41 +42189,44 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ } ]; async function dropdownSelected(key) { - if (key === "reconnect") { - await startWebsocket(message); - } else if (key === "settings") { - router2.push("/settings"); - } else if (key === "shutdown") { - await fetch(`${serverUrl}/api/general/shutdown`, { - method: "POST" - }); + switch (key) { + case "reconnect": + await startWebsocket(message); + break; + case "settings": + router2.push("/settings"); + break; + case "shutdown": + await fetch(`${serverUrl}/api/general/shutdown`, { + method: "POST" + }); + break; + case "performance": + global2.state.perf_drawer.enabled = true; + break; + case "log": + global2.state.log_drawer.enabled = true; + break; } } startWebsocket(message); - const backgroundColor = computed(() => { - if (conf.data.settings.frontend.theme === "dark") { - return "#121215"; - } else { - return "#fff"; - } - }); return (_ctx, _cache) => { var _a2; - return openBlock(), createElementBlock("div", _hoisted_1, [ + return openBlock(), createElementBlock("div", _hoisted_1$1, [ createVNode(unref(NSelect), { style: { "max-width": "250px", "padding-left": "12px", "padding-right": "12px" }, options: generatedModelOptions.value, "onUpdate:value": onModelChange, loading: modelsLoading.value, placeholder: "", - value: unref(conf).data.settings.model !== null ? (_a2 = unref(conf).data.settings.model) == null ? void 0 : _a2.name : "", + value: unref(settings).data.settings.model !== null ? (_a2 = unref(settings).data.settings.model) == null ? void 0 : _a2.name : "", "consistent-menu-width": false, filterable: "" }, null, 8, ["options", "loading", "value"]), createVNode(unref(NButton), { onClick: _cache[0] || (_cache[0] = ($event) => showModal.value = true), loading: modelsLoading.value, - type: unref(conf).data.settings.model ? "default" : "success" + type: unref(settings).data.settings.model ? "default" : "success" }, { default: withCtx(() => [ createTextVNode(" Load Model") @@ -41384,7 +42235,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ }, 8, ["loading", "type"]), createVNode(unref(NModal), { show: showModal.value, - "onUpdate:show": _cache[3] || (_cache[3] = ($event) => showModal.value = $event), + "onUpdate:show": _cache[4] || (_cache[4] = ($event) => showModal.value = $event), closable: "", "mask-closable": "", preset: "card", @@ -41417,12 +42268,19 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ createVNode(unref(NResult), { title: "No models found", description: "Click on this icon in the LEFT MENU to access the model download page", - style: { "height": "70vh", "display": "flex", "align-items": "center", "justify-content": "center", "flex-direction": "column" } + style: { "height": "70vh", "display": "flex", "align-items": "center", "justify-content": "center", "flex-direction": "column" }, + status: "404" }, { - icon: withCtx(() => [ - createVNode(unref(NIcon), { size: "64" }, { + footer: withCtx(() => [ + createVNode(unref(NButton), { + type: "success", + onClick: _cache[2] || (_cache[2] = () => { + unref(router2).push("/models"); + showModal.value = false; + }) + }, { default: withCtx(() => [ - createVNode(unref(CubeSharp)) + createTextVNode("Get model") ]), _: 1 }) @@ -41433,7 +42291,7 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ createBaseVNode("div", _hoisted_5, [ createVNode(unref(NInput), { value: filter.value, - "onUpdate:value": _cache[2] || (_cache[2] = ($event) => filter.value = $event), + "onUpdate:value": _cache[3] || (_cache[3] = ($event) => filter.value = $event), clearable: "", placeholder: "Filter Models" }, null, 8, ["value"]), @@ -41800,39 +42658,19 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({ type: unref(websocketState).color, quaternary: "", "icon-placement": "left", - "render-icon": renderIcon(unref(WifiSharp)), + "render-icon": renderIcon(unref(Wifi)), loading: unref(websocketState).loading, - onClick: _cache[4] || (_cache[4] = ($event) => unref(startWebsocket)(unref(message))) - }, { - default: withCtx(() => [ - createTextVNode(toDisplayString(unref(websocketState).text), 1) - ]), - _: 1 - }, 8, ["type", "render-icon", "loading"]) + onClick: _cache[5] || (_cache[5] = ($event) => unref(startWebsocket)(unref(message))) + }, null, 8, ["type", "render-icon", "loading"]) ]), _: 1 - }), - createVNode(unref(NButton), { - type: "success", - quaternary: "", - "icon-placement": "left", - "render-icon": perfIcon, - onClick: _cache[5] || (_cache[5] = ($event) => unref(global2).state.perf_drawer.enabled = true), - disabled: unref(global2).state.perf_drawer.enabled - }, null, 8, ["disabled"]), - createVNode(unref(NButton), { - quaternary: "", - "icon-placement": "left", - "render-icon": themeIcon, - style: { "margin-right": "8px" }, - onClick: _cache[6] || (_cache[6] = ($event) => unref(conf).data.settings.frontend.theme = unref(conf).data.settings.frontend.theme === "dark" ? "light" : "dark") }) ]) ]); }; } }); -const TopBar_vue_vue_type_style_index_0_scoped_2cf1de9c_lang = ""; +const TopBar_vue_vue_type_style_index_0_scoped_44d84e0e_lang = ""; const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { @@ -41840,84 +42678,117 @@ const _export_sfc = (sfc, props) => { } return target; }; -const TopBarVue = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-2cf1de9c"]]); -const _sfc_main$1 = {}; +const TopBarVue = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-44d84e0e"]]); +const _sfc_main$2 = {}; function _sfc_render(_ctx, _cache) { const _component_RouterView = resolveComponent("RouterView"); return openBlock(), createBlock(_component_RouterView); } -const routerContainerVue = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render]]); +const routerContainerVue = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render]]); +const _hoisted_1 = /* @__PURE__ */ createBaseVNode("div", { id: "background" }, null, -1); +const _sfc_main$1 = /* @__PURE__ */ defineComponent({ + __name: "Content", + setup(__props) { + return (_ctx, _cache) => { + return openBlock(), createBlock(unref(NNotificationProvider), { + placement: "bottom-right", + max: 3 + }, { + default: withCtx(() => [ + createVNode(unref(NLoadingBarProvider), null, { + default: withCtx(() => [ + createVNode(unref(NMessageProvider), null, { + default: withCtx(() => [ + _hoisted_1, + createVNode(_sfc_main$4), + createVNode(_sfc_main$8), + createVNode(TopBarVue), + createVNode(_sfc_main$7), + createVNode(routerContainerVue, { style: { "margin-top": "52px" } }), + createVNode(_sfc_main$5), + createVNode(_sfc_main$6) + ]), + _: 1 + }) + ]), + _: 1 + }) + ]), + _: 1 + }); + }; + } +}); const _sfc_main = /* @__PURE__ */ defineComponent({ __name: "App", setup(__props) { - useCssVars((_ctx) => ({ - "16223ad6": backgroundColor.value, - "1fedac06": theme.value.common.popoverColor, - "ba9033c6": theme.value.common.borderRadius, - "3119c2a0": theme.value.common.pressedColor, - "aca37748": theme.value.common.primaryColorHover - })); - const settings = useSettings(); - const global2 = useState(); - global2.fetchCapabilites().then(() => { - console.log("Capabilities successfully fetched from the server"); + useCssVars((_ctx) => { + var _a2, _b, _c; + return { + "2441c648": theme.value.common.popoverColor, + "521efb30": theme.value.common.borderRadius, + "65525eeb": theme.value.common.pressedColor, + "0c729ef1": theme.value.common.primaryColorHover, + "15a84ddb": blur.value, + "2259b162": ((_b = (_a2 = overrides.value) == null ? void 0 : _a2.Card) == null ? void 0 : _b.color) ?? ((_c = theme.value.Card.common) == null ? void 0 : _c.cardColor), + "f8e7ba4e": backgroundImage.value + }; }); + const settings = useSettings(); + const overrides = ref(null); const theme = computed(() => { - if (settings.data.settings.frontend.theme === "dark") { - document.body.style.backgroundColor = "#121215"; - return darkTheme; - } else { - document.body.style.backgroundColor = "white"; + var _a2, _b; + if (((_b = (_a2 = overrides.value) == null ? void 0 : _a2.volta) == null ? void 0 : _b.base) === "light") { return lightTheme; + } else { + return darkTheme; } }); - const backgroundColor = computed(() => { - if (settings.data.settings.frontend.theme === "dark") { - return "#121215"; - } else { - return "#fff"; + provide(themeOverridesKey, overrides); + provide(themeKey, theme); + function updateTheme() { + fetch(`${serverUrl}/themes/${settings.data.settings.frontend.theme}.json`).then((res) => res.json()).then((data) => { + overrides.value = data; + }); + } + updateTheme(); + watch(() => settings.data.settings.frontend.theme, updateTheme); + const backgroundImage = computed(() => { + var _a2, _b, _c, _d; + if (settings.data.settings.frontend.background_image_override) { + return `url(${settings.data.settings.frontend.background_image_override})`; + } else if ((_b = (_a2 = overrides.value) == null ? void 0 : _a2.volta) == null ? void 0 : _b.backgroundImage) { + return `url(${(_d = (_c = overrides.value) == null ? void 0 : _c.volta) == null ? void 0 : _d.backgroundImage})`; } + return void 0; + }); + const blur = computed(() => { + var _a2, _b; + return `blur(${((_b = (_a2 = overrides.value) == null ? void 0 : _a2.volta) == null ? void 0 : _b.blur) ?? "6px"})`; }); - const overrides = { - common: { - fontSize: "15px", - fontWeight: "600" + watch( + () => overrides.value, + () => { + var _a2, _b; + document.body.style.backgroundColor = ((_b = (_a2 = overrides.value) == null ? void 0 : _a2.common) == null ? void 0 : _b.baseColor) ?? theme.value.common.baseColor; } - }; + ); return (_ctx, _cache) => { return openBlock(), createBlock(unref(NConfigProvider), { theme: theme.value, - "theme-overrides": overrides, + "theme-overrides": overrides.value, class: "main" }, { default: withCtx(() => [ - unref(settings).data.settings.frontend.enable_theme_editor ? (openBlock(), createBlock(unref(NThemeEditor), { key: 0 })) : createCommentVNode("", true), - createVNode(unref(NNotificationProvider), { - placement: "bottom-right", - max: 3 - }, { + unref(settings).data.settings.frontend.enable_theme_editor ? (openBlock(), createBlock(unref(NThemeEditor), { key: 0 }, { default: withCtx(() => [ - createVNode(unref(NLoadingBarProvider), null, { - default: withCtx(() => [ - createVNode(unref(NMessageProvider), null, { - default: withCtx(() => [ - createVNode(_sfc_main$3), - createVNode(_sfc_main$5), - createVNode(TopBarVue), - createVNode(routerContainerVue, { style: { "margin-top": "52px" } }), - createVNode(_sfc_main$4) - ]), - _: 1 - }) - ]), - _: 1 - }) + createVNode(_sfc_main$1) ]), _: 1 - }) + })) : (openBlock(), createBlock(_sfc_main$1, { key: 1 })) ]), _: 1 - }, 8, ["theme"]); + }, 8, ["theme", "theme-overrides"]); }; } }); @@ -41964,30 +42835,42 @@ const __vitePreload = function preload(baseModule, deps, importerUrl) { link.addEventListener("error", () => rej(new Error(`Unable to preload CSS for ${dep}`))); }); } - })).then(() => baseModule()); + })).then(() => baseModule()).catch((err) => { + const e = new Event("vite:preloadError", { cancelable: true }); + e.payload = err; + window.dispatchEvent(e); + if (!e.defaultPrevented) { + throw err; + } + }); }; const router = createRouter({ history: createWebHistory("/"), routes: [ { path: "/", - name: "text2image", - component: () => __vitePreload(() => import("./TextToImageView.js"), true ? ["assets/TextToImageView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/TrashBin.js","assets/clock.js","assets/DescriptionsItem.js","assets/Slider.js","assets/InputNumber.js","assets/v4.js","assets/Switch.js"] : void 0) + name: "home", + component: () => __vitePreload(() => import("./TextToImageView.js"), true ? ["assets/TextToImageView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/Switch.js","assets/TrashBin.js","assets/clock.js","assets/DescriptionsItem.js","assets/InputNumber.js","assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js","assets/Settings.js","assets/v4.js"] : void 0) }, { - path: "/image2image", - name: "image2image", - component: () => __vitePreload(() => import("./Image2ImageView.js"), true ? ["assets/Image2ImageView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/clock.js","assets/DescriptionsItem.js","assets/Slider.js","assets/InputNumber.js","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/TrashBin.js","assets/ImageUpload.js","assets/CloudUpload.js","assets/ImageUpload.css","assets/v4.js","assets/Switch.js","assets/Image2ImageView.css"] : void 0) + path: "/txt2img", + name: "txt2img", + component: () => __vitePreload(() => import("./TextToImageView.js"), true ? ["assets/TextToImageView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/Switch.js","assets/TrashBin.js","assets/clock.js","assets/DescriptionsItem.js","assets/InputNumber.js","assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js","assets/Settings.js","assets/v4.js"] : void 0) }, { - path: "/extra", - name: "extra", - component: () => __vitePreload(() => import("./ExtraView.js"), true ? ["assets/ExtraView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/TrashBin.js","assets/ImageUpload.js","assets/CloudUpload.js","assets/ImageUpload.css","assets/Slider.js","assets/InputNumber.js","assets/ExtraView.css"] : void 0) + path: "/img2img", + name: "img2img", + component: () => __vitePreload(() => import("./Image2ImageView.js"), true ? ["assets/Image2ImageView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/clock.js","assets/DescriptionsItem.js","assets/Switch.js","assets/InputNumber.js","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/TrashBin.js","assets/ImageUpload.js","assets/CloudUpload.js","assets/ImageUpload.css","assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js","assets/Settings.js","assets/v4.js","assets/Image2ImageView.css"] : void 0) + }, + { + path: "/imageProcessing", + name: "imageProcessing", + component: () => __vitePreload(() => import("./ImageProcessingView.js"), true ? ["assets/ImageProcessingView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageOutput.vue_vue_type_script_setup_true_lang.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/Switch.js","assets/TrashBin.js","assets/ImageUpload.js","assets/CloudUpload.js","assets/ImageUpload.css","assets/InputNumber.js","assets/ImageProcessingView.css"] : void 0) }, { path: "/models", name: "models", - component: () => __vitePreload(() => import("./ModelsView.js"), true ? ["assets/ModelsView.js","assets/ModelPopup.vue_vue_type_script_setup_true_lang.js","assets/DescriptionsItem.js","assets/GridOutline.js","assets/Slider.js","assets/Switch.js","assets/TrashBin.js","assets/CloudUpload.js","assets/ModelsView.css"] : void 0) + component: () => __vitePreload(() => import("./ModelsView.js"), true ? ["assets/ModelsView.js","assets/ModelPopup.vue_vue_type_script_setup_true_lang.js","assets/DescriptionsItem.js","assets/GridOutline.js","assets/Switch.js","assets/Settings.js","assets/TrashBin.js","assets/CloudUpload.js","assets/ModelsView.css"] : void 0) }, { path: "/about", @@ -41997,7 +42880,12 @@ const router = createRouter({ { path: "/accelerate", name: "accelerate", - component: () => __vitePreload(() => import("./AccelerateView.js"), true ? ["assets/AccelerateView.js","assets/Slider.js","assets/InputNumber.js","assets/Switch.js"] : void 0) + component: () => __vitePreload(() => import("./AccelerateView.js"), true ? ["assets/AccelerateView.js","assets/Switch.js","assets/InputNumber.js"] : void 0) + }, + { + path: "/extra", + name: "extra", + component: () => __vitePreload(() => import("./ExtraView.js"), true ? [] : void 0) }, { path: "/test", @@ -42007,181 +42895,194 @@ const router = createRouter({ { path: "/settings", name: "settings", - component: () => __vitePreload(() => import("./SettingsView.js"), true ? ["assets/SettingsView.js","assets/Switch.js","assets/InputNumber.js","assets/Slider.js"] : void 0) + component: () => __vitePreload(() => import("./SettingsView.js"), true ? ["assets/SettingsView.js","assets/Switch.js","assets/InputNumber.js","assets/SamplerPicker.vue_vue_type_script_setup_true_lang.js","assets/Settings.js"] : void 0) }, { path: "/imageBrowser", name: "imageBrowser", - component: () => __vitePreload(() => import("./ImageBrowserView.js"), true ? ["assets/ImageBrowserView.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/GridOutline.js","assets/TrashBin.js","assets/Slider.js","assets/DescriptionsItem.js","assets/ImageBrowserView.css"] : void 0) + component: () => __vitePreload(() => import("./ImageBrowserView.js"), true ? ["assets/ImageBrowserView.js","assets/SendOutputTo.vue_vue_type_script_setup_true_lang.js","assets/Switch.js","assets/GridOutline.js","assets/TrashBin.js","assets/DescriptionsItem.js","assets/ImageBrowserView.css"] : void 0) }, { path: "/tagger", name: "tagger", - component: () => __vitePreload(() => import("./TaggerView.js"), true ? ["assets/TaggerView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageUpload.js","assets/CloudUpload.js","assets/ImageUpload.css","assets/v4.js","assets/Slider.js","assets/InputNumber.js","assets/Switch.js","assets/TaggerView.css"] : void 0) + component: () => __vitePreload(() => import("./TaggerView.js"), true ? ["assets/TaggerView.js","assets/GenerateSection.vue_vue_type_script_setup_true_lang.js","assets/GenerateSection.css","assets/ImageUpload.js","assets/CloudUpload.js","assets/ImageUpload.css","assets/v4.js","assets/Switch.js","assets/InputNumber.js","assets/TaggerView.css"] : void 0) + }, + { + path: "/:pathMatch(.*)", + name: "notFound", + component: () => __vitePreload(() => import("./404View.js"), true ? [] : void 0) } ] }); const main = ""; -const pinia = createPinia(); const app = createApp(_sfc_main); -app.use(pinia); +app.use(createPinia()); app.use(router); app.mount("#app"); export { - cM as $, + call as $, pushScopeId as A, popScopeId as B, - resolveComponent as C, - h as D, - ref as E, - NButton as F, - NIcon as G, - NTabPane as H, - NTabs as I, - Fragment as J, - watch as K, - upscalerOptions as L, - renderList as M, + h as C, + ref as D, + NButton as E, + NIcon as F, + NTabPane as G, + NTabs as H, + Fragment as I, + watch as J, + upscalerOptions as K, + renderList as L, + NScrollbar as M, NGi as N, - NScrollbar as O, - replaceable as P, - useConfig as Q, - useFormItem as R, - useMergedState as S, - provide as T, - toRef as U, - createInjectionKey as V, - call as W, - c$1 as X, - cB as Y, - cE as Z, + replaceable as O, + createInjectionKey as P, + cB as Q, + inject as R, + useConfig as S, + useTheme as T, + popselectLight$1 as U, + toRef as V, + useThemeClass as W, + NInternalSelectMenu as X, + createTreeMate as Y, + happensIn as Z, _export_sfc as _, useSettings as a, - isBrowser$3 as a$, - iconSwitchTransition as a0, - insideModal as a1, - insidePopover as a2, - inject as a3, - useMemo as a4, - useTheme as a5, - checkboxLight$1 as a6, - useRtl as a7, - createKey as a8, - useThemeClass as a9, - radioLight$1 as aA, - resolveWrappedSlot as aB, - flatten$2 as aC, - getSlot$1 as aD, - depx as aE, - formatLength as aF, - NScrollbar$1 as aG, - onBeforeUnmount as aH, - off as aI, - ChevronDownIcon as aJ, - NDropdown as aK, - pxfy as aL, - get as aM, - NBaseLoading as aN, - ChevronRightIcon as aO, - VResizeObserver as aP, - warn$2 as aQ, - cssrAnchorMetaName as aR, - VVirtualList as aS, - NEmpty as aT, - repeat as aU, - beforeNextFrameOnce as aV, - fadeInScaleUpTransition as aW, + AddIcon as a$, + nextTick as a0, + keysOf as a1, + createTmOptions as a2, + provide as a3, + keep as a4, + createRefSetter as a5, + mergeEventHandlers as a6, + omit as a7, + NPopover as a8, + popoverBaseProps as a9, + NScrollbar$1 as aA, + onBeforeUnmount as aB, + off as aC, + on as aD, + ChevronDownIcon as aE, + NDropdown as aF, + pxfy as aG, + get as aH, + NIconSwitchTransition as aI, + NBaseLoading as aJ, + ChevronRightIcon as aK, + VResizeObserver as aL, + warn$2 as aM, + cssrAnchorMetaName as aN, + VVirtualList as aO, + NEmpty as aP, + repeat as aQ, + beforeNextFrameOnce as aR, + fadeInScaleUpTransition as aS, + iconSwitchTransition as aT, + insideModal as aU, + insidePopover as aV, + createId as aW, Transition as aX, dataTableLight$1 as aY, loadingBarApiInjectionKey as aZ, throwError as a_, - createId as aa, - NIconSwitchTransition as ab, - on as ac, - popselectLight$1 as ad, - NInternalSelectMenu as ae, - createTreeMate as af, - happensIn as ag, - nextTick as ah, - keysOf as ai, - createTmOptions as aj, - keep as ak, - createRefSetter as al, - mergeEventHandlers as am, - omit as an, - NPopover as ao, - popoverBaseProps as ap, - cNotM as aq, - useLocale as ar, - watchEffect as as, - resolveSlot as at, - NBaseIcon as au, - useAdjustedTo as av, - paginationLight$1 as aw, - ellipsisLight$1 as ax, - onDeactivated as ay, - mergeProps as az, + c$1 as aa, + cM as ab, + cNotM as ac, + useLocale as ad, + useMergedState as ae, + watchEffect as af, + useRtl as ag, + resolveSlot as ah, + NBaseIcon as ai, + useAdjustedTo as aj, + paginationLight$1 as ak, + createKey as al, + useMergedClsPrefix as am, + ellipsisLight$1 as an, + onDeactivated as ao, + mergeProps as ap, + useStyle as aq, + useFormItem as ar, + useMemo as as, + cE as at, + radioLight$1 as au, + resolveWrappedSlot as av, + flatten$2 as aw, + getSlot$1 as ax, + depx as ay, + formatLength as az, useMessage as b, - AddIcon as b0, - NProgress as b1, - NFadeInExpandTransition as b2, - EyeIcon as b3, - fadeInHeightExpandTransition as b4, - Teleport as b5, - uploadLight$1 as b6, - useCssVars as b7, + VFollower as b$, + NProgress as b0, + NFadeInExpandTransition as b1, + EyeIcon as b2, + fadeInHeightExpandTransition as b3, + Teleport as b4, + uploadLight$1 as b5, + useCssVars as b6, + themeOverridesKey as b7, reactive as b8, onMounted as b9, - useNotification as bA, - defaultSettings as bB, - urlFromPath as bC, - useRouter as bD, - fadeInTransition as bE, - imageLight as bF, - isMounted as bG, - LazyTeleport as bH, - zindexable$1 as bI, - kebabCase$1 as bJ, - useCompitable as bK, - descriptionsLight$1 as bL, - withModifiers as bM, - NAlert as bN, - inputNumberLight$1 as bO, - rgba as bP, - XButton as bQ, - isSlotEmpty as bR, - switchLight$1 as bS, - VBinder as bT, - VTarget as bU, - VFollower as bV, - sliderLight$1 as bW, + commonVariables$m as bA, + formItemInjectionKey as bB, + convertToTextString as bC, + themeKey as bD, + useNotification as bE, + defaultSettings as bF, + resolveDynamicComponent as bG, + checkboxLight$1 as bH, + urlFromPath as bI, + diffusersSchedulerTuple as bJ, + useRouter as bK, + isBrowser$3 as bL, + fadeInTransition as bM, + imageLight as bN, + isMounted as bO, + LazyTeleport as bP, + zindexable$1 as bQ, + kebabCase$1 as bR, + useCompitable as bS, + descriptionsLight$1 as bT, + withModifiers as bU, + NAlert as bV, + inputNumberLight$1 as bW, + rgba as bX, + XButton as bY, + VBinder as bZ, + VTarget as b_, normalizeStyle as ba, NText as bb, huggingfaceModelsFile as bc, NModal as bd, - stepsLight$1 as be, - FinishedIcon as bf, - ErrorIcon$1 as bg, - upperFirst$1 as bh, - toString as bi, - createCompounder as bj, - cloneVNode as bk, - onBeforeUpdate as bl, - indexMap as bm, - onUpdated as bn, - resolveSlotWithProps as bo, - withDirectives as bp, - vShow as bq, - carouselLight$1 as br, - getPreciseEventTarget as bs, - rateLight as bt, - color2Class as bu, - NTag as bv, - getCurrentInstance as bw, - formLight$1 as bx, - commonVariables$m as by, - formItemInjectionKey as bz, + NDivider as be, + Backends as bf, + stepsLight$1 as bg, + FinishedIcon as bh, + ErrorIcon$1 as bi, + upperFirst$1 as bj, + toString as bk, + createCompounder as bl, + cloneVNode as bm, + onBeforeUpdate as bn, + indexMap as bo, + onUpdated as bp, + resolveSlotWithProps as bq, + withDirectives as br, + vShow as bs, + carouselLight$1 as bt, + getPreciseEventTarget as bu, + rateLight as bv, + color2Class as bw, + NTag as bx, + getCurrentInstance as by, + formLight$1 as bz, computed as c, + sliderLight$1 as c0, + isSlotEmpty as c1, + switchLight$1 as c2, + NResult as c3, defineComponent as d, openBlock as e, createElementBlock as f, @@ -42196,13 +43097,13 @@ export { onUnmounted as o, promptHandleKeyUp as p, NTooltip as q, - NSelect as r, + createCommentVNode as r, serverUrl as s, toDisplayString as t, useState as u, createBlock as v, withCtx as w, - createCommentVNode as x, + NSelect as x, NGrid as y, spaceRegex as z }; diff --git a/frontend/dist/assets/v4.js b/frontend/dist/assets/v4.js index 9bdd881c8..1f7c62045 100644 --- a/frontend/dist/assets/v4.js +++ b/frontend/dist/assets/v4.js @@ -14,7 +14,7 @@ for (let i = 0; i < 256; ++i) { byteToHex.push((i + 256).toString(16).slice(1)); } function unsafeStringify(arr, offset = 0) { - return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); + return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]; } const randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto); const native = { diff --git a/frontend/package.json b/frontend/package.json index 0a51903c4..be7c3bcd4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,6 +37,7 @@ "naive-ui": "^2.34.3", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", + "sass": "^1.69.4", "typescript": "~4.7.4", "vite": "^4.0.0", "vue-tsc": "^1.0.12" diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 692ac1e00..a1d19bdcb 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,78 +1,69 @@ +const blur = computed(() => `blur(${overrides.value?.volta?.blur ?? "6px"})`); - diff --git a/frontend/src/Content.vue b/frontend/src/Content.vue new file mode 100644 index 000000000..d33dc25d7 --- /dev/null +++ b/frontend/src/Content.vue @@ -0,0 +1,32 @@ + + + + diff --git a/frontend/src/assets/main.css b/frontend/src/assets/main.css index d8e3a0fcc..18e9b89e9 100644 --- a/frontend/src/assets/main.css +++ b/frontend/src/assets/main.css @@ -1,8 +1,6 @@ body { - min-height: 100vh; margin-left: 64px; color: #fff; - background-color: rgb(18, 18, 21); font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; diff --git a/frontend/src/components/CollapsibleNavbar.vue b/frontend/src/components/CollapsibleNavbar.vue index 9f4ba0f8a..a440c7abc 100644 --- a/frontend/src/components/CollapsibleNavbar.vue +++ b/frontend/src/components/CollapsibleNavbar.vue @@ -65,17 +65,18 @@ const menuOptionsMain: MenuOption[] = [ }, { label: () => - h( - RouterLink, - { to: "/image2image" }, - { default: () => "Image to Image" } - ), - key: "image2image", + h(RouterLink, { to: "/img2img" }, { default: () => "Image to Image" }), + key: "img2img", icon: renderIcon(Images), }, { - label: () => h(RouterLink, { to: "/extra" }, { default: () => "Extra" }), - key: "extra", + label: () => + h( + RouterLink, + { to: "/imageProcessing" }, + { default: () => "Image Processing" } + ), + key: "imageProcessing", icon: renderIcon(Duplicate), }, { @@ -104,6 +105,11 @@ const menuOptionsMain: MenuOption[] = [ key: "plugins", icon: renderIcon(Speedometer), }, + // { + // label: () => h(RouterLink, { to: "/extra" }, { default: () => "Extra" }), + // key: "extra", + // icon: renderIcon(Archive), + // }, { label: () => h(RouterLink, { to: "/settings" }, { default: () => "Settings" }), diff --git a/frontend/src/components/GenerateSection.vue b/frontend/src/components/GenerateSection.vue index ca682e786..49814e6ec 100644 --- a/frontend/src/components/GenerateSection.vue +++ b/frontend/src/components/GenerateSection.vue @@ -10,8 +10,8 @@ :disabled=" !props.doNotDisableGenerate && (global.state.generating || - conf.data.settings.model?.name === '' || - conf.data.settings.model?.name === undefined) + settings.data.settings.model?.name === '' || + settings.data.settings.model?.name === undefined) " :loading="global.state.generating" style="width: 100%" @@ -44,8 +44,8 @@ style="margin-top: 12px" v-if=" !props.doNotDisableGenerate && - (conf.data.settings.model?.name === '' || - conf.data.settings.model?.name === undefined) + (settings.data.settings.model?.name === '' || + settings.data.settings.model?.name === undefined) " type="warning" title="No model loaded" @@ -65,7 +65,7 @@ import type { MaybeArray } from "naive-ui/es/_utils"; import { onMounted, onUnmounted, ref, type PropType } from "vue"; const global = useState(); -const conf = useSettings(); +const settings = useSettings(); const generateButton = ref(null); diff --git a/frontend/src/components/ImageOutput.vue b/frontend/src/components/ImageOutput.vue index 45f934489..9eb9b4c54 100644 --- a/frontend/src/components/ImageOutput.vue +++ b/frontend/src/components/ImageOutput.vue @@ -37,7 +37,7 @@ style="margin-bottom: 4px" /> - + @@ -58,6 +58,11 @@ const props = defineProps({ required: false, default: () => [], }, + data: { + type: Object, + required: false, + default: () => ({}), + }, }); defineEmits(["image-clicked"]); diff --git a/frontend/src/components/ImageUpload.vue b/frontend/src/components/ImageUpload.vue index 4f48c2e5c..bb3c6c508 100644 --- a/frontend/src/components/ImageUpload.vue +++ b/frontend/src/components/ImageUpload.vue @@ -67,8 +67,8 @@ const props = defineProps({ }); const image = ref(); -const width = computed(() => (image.value ? image.value?.width : 0)); -const height = computed(() => (image.value ? image.value?.height : 0)); +const width = computed(() => (image.value ? image.value?.naturalWidth : 0)); +const height = computed(() => (image.value ? image.value?.naturalHeight : 0)); function previewImage(event: Event) { const input = event.target as HTMLInputElement; @@ -95,8 +95,6 @@ function previewImage(event: Event) { const emit = defineEmits(["file-dropped"]); function onDrop(e: DragEvent) { - console.log(e.dataTransfer?.files); - // Emit file as string if (e.dataTransfer?.files) { const reader = new FileReader(); diff --git a/frontend/src/components/InitHandler.vue b/frontend/src/components/InitHandler.vue new file mode 100644 index 000000000..f892eb81d --- /dev/null +++ b/frontend/src/components/InitHandler.vue @@ -0,0 +1,25 @@ + + + + diff --git a/frontend/src/components/LogDrawer.vue b/frontend/src/components/LogDrawer.vue new file mode 100644 index 000000000..09507e6da --- /dev/null +++ b/frontend/src/components/LogDrawer.vue @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/components/SendOutputTo.vue b/frontend/src/components/SendOutputTo.vue index 34b6b30cd..95932e376 100644 --- a/frontend/src/components/SendOutputTo.vue +++ b/frontend/src/components/SendOutputTo.vue @@ -1,68 +1,128 @@ + Cancel + + + + Copy + + + + + +
+ + + + {{ capitalizeAndReplace(target) }} + + + + + + -> {{ capitalizeAndReplace(target) }} - - - - Img2Img - - - ControlNet - - - Inpainting - - - Upscale - - - Tagger - - +
diff --git a/frontend/src/components/TopBar.vue b/frontend/src/components/TopBar.vue index 0a0ca8ec0..b4dd24b90 100644 --- a/frontend/src/components/TopBar.vue +++ b/frontend/src/components/TopBar.vue @@ -7,7 +7,9 @@ :loading="modelsLoading" placeholder="" :value=" - conf.data.settings.model !== null ? conf.data.settings.model?.name : '' + settings.data.settings.model !== null + ? settings.data.settings.model?.name + : '' " :consistent-menu-width="false" filterable @@ -15,7 +17,7 @@ Load Model @@ -59,11 +61,19 @@ justify-content: center; flex-direction: column; " + status="404" > - @@ -396,13 +387,12 @@ import { serverUrl } from "@/env"; import { startWebsocket } from "@/functions"; import { useWebsocket } from "@/store/websockets"; import { - ContrastSharp, - CubeSharp, + DocumentText, PowerSharp, SettingsSharp, StatsChart, SyncSharp, - WifiSharp, + Wifi, } from "@vicons/ionicons5"; import { NAlert, NButton, NProgress, NResult, useMessage } from "naive-ui"; import type { SelectMixedOption } from "naive-ui/es/select/src/interface"; @@ -414,7 +404,7 @@ const router = useRouter(); const websocketState = useWebsocket(); const global = useState(); -const conf = useSettings(); +const settings = useSettings(); const modelsLoading = ref(false); const filter = ref(""); @@ -434,7 +424,14 @@ const pyTorchModels = computed(() => { return model.backend === "PyTorch" && model.valid === true; }) .sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; // a should come before b + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; // b should come before a + } else { + // If 'state' is the same, sort alphabetically by 'name' + return a.name.localeCompare(b.name); + } }); }); @@ -444,7 +441,14 @@ const aitModels = computed(() => { return model.backend === "AITemplate"; }) .sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; // a should come before b + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; // b should come before a + } else { + // If 'state' is the same, sort alphabetically by 'name' + return a.name.localeCompare(b.name); + } }); }); @@ -454,7 +458,14 @@ const onnxModels = computed(() => { return model.backend === "ONNX"; }) .sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; // a should come before b + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; // b should come before a + } else { + // If 'state' is the same, sort alphabetically by 'name' + return a.name.localeCompare(b.name); + } }); }); @@ -469,6 +480,24 @@ const vaeModels = computed(() => { vae: "default", textual_inversions: [], } as ModelEntry, + { + name: "Tiny VAE (fast)", + path: "madebyollin/taesd", + backend: "VAE", + valid: true, + state: "not loaded", + vae: "madebyollin/taesd", + textual_inversions: [], + } as ModelEntry, + { + name: "Asymmetric VAE", + path: "cross-attention/asymmetric-autoencoder-kl-x-1-5", + backend: "VAE", + valid: true, + state: "not loaded", + vae: "cross-attention/asymmetric-autoencoder-kl-x-1-5", + textual_inversions: [], + } as ModelEntry, ...filteredModels.value .filter((model) => { return model.backend === "VAE"; @@ -485,7 +514,14 @@ const textualInversionModels = computed(() => { return model.backend === "Textual Inversion"; }) .sort((a, b) => { - return a.name.localeCompare(b.name); + if (a.state === "loaded" && b.state !== "loaded") { + return -1; // a should come before b + } else if (a.state !== "loaded" && b.state === "loaded") { + return 1; // b should come before a + } else { + // If 'state' is the same, sort alphabetically by 'name' + return a.name.localeCompare(b.name); + } }); }); @@ -510,14 +546,14 @@ function refreshModels() { fetch(`${serverUrl}/api/models/loaded`).then((res) => { res.json().then((data: Array) => { // Check if the current model is still loaded, if not, set it to null - if (conf.data.settings.model) { + if (settings.data.settings.model) { if ( !data.find((model) => { - return model.path === conf.data.settings.model?.path; + return model.path === settings.data.settings.model?.path; }) ) { console.log("Current model is not loaded anymore"); - conf.data.settings.model = null; + settings.data.settings.model = null; } } @@ -533,7 +569,7 @@ function refreshModels() { }); // Set the current model to the first available model if it was null - if (!conf.data.settings.model) { + if (!settings.data.settings.model) { const allLoaded = [ ...loadedPyTorchModels.value, ...loadedAitModels.value, @@ -544,25 +580,23 @@ function refreshModels() { console.log("All loaded models: ", allLoaded); if (allLoaded.length > 0) { - conf.data.settings.model = allLoaded[0]; + settings.data.settings.model = allLoaded[0]; console.log( - "Set current model to first available model: ", - conf.data.settings.model + "Setting current model to first available model: ", + settings.data.settings.model ); } else { - console.log("No models available"); - conf.data.settings.model = null; + console.log("No models available, setting current model to null"); + settings.data.settings.model = null; } } try { - if (conf.data.settings.model) { - const spl = conf.data.settings.model.name.split("__")[1]; + if (settings.data.settings.model) { + const spl = settings.data.settings.model.name.split("__")[1]; const regex = /([\d]+-[\d]+)x([\d]+-[\d]+)x([\d]+-[\d]+)/g; const matches = regex.exec(spl); - console.log("Match: ", matches); - if (matches) { const width = matches[1].split("-").map((x) => parseInt(x)); const height = matches[2].split("-").map((x) => parseInt(x)); @@ -570,9 +604,9 @@ function refreshModels() { .split("-") .map((x) => parseInt(x)); - conf.data.settings.aitDim.width = width; - conf.data.settings.aitDim.height = height; - conf.data.settings.aitDim.batch_size = batch_size; + settings.data.settings.aitDim.width = width; + settings.data.settings.aitDim.height = height; + settings.data.settings.aitDim.batch_size = batch_size; } else { throw new Error("Invalid model name for AIT dimensions parser"); } @@ -580,10 +614,9 @@ function refreshModels() { throw new Error("No model, cannot parse AIT dimensions"); } } catch (e) { - console.warn(e); - conf.data.settings.aitDim.width = undefined; - conf.data.settings.aitDim.height = undefined; - conf.data.settings.aitDim.batch_size = undefined; + settings.data.settings.aitDim.width = undefined; + settings.data.settings.aitDim.height = undefined; + settings.data.settings.aitDim.batch_size = undefined; } const autofillKeys = []; @@ -596,7 +629,7 @@ function refreshModels() { }*/ } - global.state.autofill = autofillKeys; + global.state.autofill_special = autofillKeys; }); }); }) @@ -705,14 +738,14 @@ async function onModelChange(modelStr: string) { }); if (model) { - conf.data.settings.model = model; + settings.data.settings.model = model; } else { message.error("Model not found"); } try { - if (conf.data.settings.model) { - const spl = conf.data.settings.model.name.split("__")[1]; + if (settings.data.settings.model) { + const spl = settings.data.settings.model.name.split("__")[1]; const regex = /([\d]+-[\d]+)x([\d]+-[\d]+)x([\d]+-[\d]+)/g; const match = spl.match(regex); @@ -722,9 +755,9 @@ async function onModelChange(modelStr: string) { const height = match[1].split("-").map((x) => parseInt(x)); const batch_size = match[2].split("-").map((x) => parseInt(x)); - conf.data.settings.aitDim.width = width; - conf.data.settings.aitDim.height = height; - conf.data.settings.aitDim.batch_size = batch_size; + settings.data.settings.aitDim.width = width; + settings.data.settings.aitDim.height = height; + settings.data.settings.aitDim.batch_size = batch_size; } else { throw new Error("Invalid model name for AIT dimensions parser"); } @@ -733,9 +766,9 @@ async function onModelChange(modelStr: string) { } } catch (e) { console.warn(e); - conf.data.settings.aitDim.width = undefined; - conf.data.settings.aitDim.height = undefined; - conf.data.settings.aitDim.batch_size = undefined; + settings.data.settings.aitDim.width = undefined; + settings.data.settings.aitDim.height = undefined; + settings.data.settings.aitDim.batch_size = undefined; } } @@ -744,14 +777,6 @@ function resetModels() { console.log("Reset models"); } -const perfIcon = () => { - return h(StatsChart); -}; - -const themeIcon = () => { - return h(ContrastSharp); -}; - websocketState.onConnectedCallbacks.push(() => { refreshModels(); }); @@ -879,6 +904,16 @@ const renderIcon = (icon: Component) => { }; const dropdownOptions: DropdownOption[] = [ + { + label: "Log", + key: "log", + icon: renderIcon(DocumentText), + }, + { + label: "Performance", + key: "performance", + icon: renderIcon(StatsChart), + }, { label: "Reconnect", key: "reconnect", @@ -897,26 +932,28 @@ const dropdownOptions: DropdownOption[] = [ ]; async function dropdownSelected(key: string) { - if (key === "reconnect") { - await startWebsocket(message); - } else if (key === "settings") { - router.push("/settings"); - } else if (key === "shutdown") { - await fetch(`${serverUrl}/api/general/shutdown`, { - method: "POST", - }); + switch (key) { + case "reconnect": + await startWebsocket(message); + break; + case "settings": + router.push("/settings"); + break; + case "shutdown": + await fetch(`${serverUrl}/api/general/shutdown`, { + method: "POST", + }); + break; + case "performance": + global.state.perf_drawer.enabled = true; + break; + case "log": + global.state.log_drawer.enabled = true; + break; } } startWebsocket(message); - -const backgroundColor = computed(() => { - if (conf.data.settings.frontend.theme === "dark") { - return "#121215"; - } else { - return "#fff"; - } -});