diff --git a/docs/nodes/communityNodes.md b/docs/nodes/communityNodes.md index 7dbcef3836..fa17b42411 100644 --- a/docs/nodes/communityNodes.md +++ b/docs/nodes/communityNodes.md @@ -14,6 +14,7 @@ To use a community workflow, download the the `.json` node graph file and load i - Community Nodes + [Adapters-Linked](#adapters-linked-nodes) + + [Autostereogram](#autostereogram-nodes) + [Average Images](#average-images) + [Clean Image Artifacts After Cut](#clean-image-artifacts-after-cut) + [Close Color Mask](#close-color-mask) @@ -67,6 +68,17 @@ Note: These are inherited from the core nodes so any update to the core nodes sh **Node Link:** https://github.com/skunkworxdark/adapters-linked-nodes +-------------------------------- +### Autostereogram Nodes + +**Description:** Generate autostereogram images from a depth map. This is not a very practically useful node but more a 90s nostalgic indulgence as I used to love these images as a kid. + +**Node Link:** https://github.com/skunkworxdark/autostereogram_nodes + +**Example Usage:** +
+ -> -> + -------------------------------- ### Average Images diff --git a/invokeai/app/services/config/config_default.py b/invokeai/app/services/config/config_default.py index f1650f1ee9..b161ea18d6 100644 --- a/invokeai/app/services/config/config_default.py +++ b/invokeai/app/services/config/config_default.py @@ -173,10 +173,10 @@ from __future__ import annotations import os from pathlib import Path -from typing import Any, ClassVar, Dict, List, Literal, Optional, get_type_hints +from typing import Any, ClassVar, Dict, List, Literal, Optional, Union from omegaconf import DictConfig, OmegaConf -from pydantic import Field, TypeAdapter +from pydantic import Field from pydantic.config import JsonDict from pydantic_settings import SettingsConfigDict @@ -339,13 +339,9 @@ class InvokeAIAppConfig(InvokeAISettings): super().parse_args(argv) if self.singleton_init and not clobber: - hints = get_type_hints(self.__class__) - for k in self.singleton_init: - setattr( - self, - k, - TypeAdapter(hints[k]).validate_python(self.singleton_init[k]), - ) + # When setting values in this way, set validate_assignment to true if you want to validate the value. + for k, v in self.singleton_init.items(): + setattr(self, k, v) @classmethod def get_config(cls, **kwargs: Any) -> InvokeAIAppConfig: diff --git a/invokeai/app/services/invocation_stats/invocation_stats_default.py b/invokeai/app/services/invocation_stats/invocation_stats_default.py index 7f3ef3023d..501a4c04e5 100644 --- a/invokeai/app/services/invocation_stats/invocation_stats_default.py +++ b/invokeai/app/services/invocation_stats/invocation_stats_default.py @@ -9,6 +9,7 @@ import torch import invokeai.backend.util.logging as logger from invokeai.app.invocations.baseinvocation import BaseInvocation from invokeai.app.services.invoker import Invoker +from invokeai.app.services.item_storage.item_storage_common import ItemNotFoundError from invokeai.backend.model_management.model_cache import CacheStats from .invocation_stats_base import InvocationStatsServiceBase @@ -63,7 +64,7 @@ class InvocationStatsService(InvocationStatsServiceBase): finally: # Record state after the invocation. node_stats = NodeExecutionStats( - invocation_type=invocation.type, + invocation_type=invocation.get_type(), start_time=start_time, end_time=time.time(), start_ram_gb=start_ram / GB, @@ -78,11 +79,11 @@ class InvocationStatsService(InvocationStatsServiceBase): This shouldn't be necessary, but we don't have totally robust upstream handling of graph completions/errors, so for now we call this function periodically to prevent them from accumulating. """ - to_prune = [] + to_prune: list[str] = [] for graph_execution_state_id in self._stats: try: graph_execution_state = self._invoker.services.graph_execution_manager.get(graph_execution_state_id) - except Exception: + except ItemNotFoundError: # TODO(ryand): What would cause this? Should this exception just be allowed to propagate? logger.warning(f"Failed to get graph state for {graph_execution_state_id}.") continue diff --git a/invokeai/app/services/item_storage/item_storage_base.py b/invokeai/app/services/item_storage/item_storage_base.py index bbe1b3dcff..c93edf5188 100644 --- a/invokeai/app/services/item_storage/item_storage_base.py +++ b/invokeai/app/services/item_storage/item_storage_base.py @@ -20,17 +20,26 @@ class ItemStorageABC(ABC, Generic[T]): @abstractmethod def get(self, item_id: str) -> T: - """Gets the item, parsing it into a Pydantic model""" + """ + Gets the item. + :param item_id: the id of the item to get + :raises ItemNotFoundError: if the item is not found + """ pass @abstractmethod def set(self, item: T) -> None: - """Sets the item""" + """ + Sets the item. The id will be extracted based on id_field. + :param item: the item to set + """ pass @abstractmethod def delete(self, item_id: str) -> None: - """Deletes the item""" + """ + Deletes the item, if it exists. + """ pass def on_changed(self, on_changed: Callable[[T], None]) -> None: diff --git a/invokeai/app/services/item_storage/item_storage_common.py b/invokeai/app/services/item_storage/item_storage_common.py new file mode 100644 index 0000000000..8fd677c71b --- /dev/null +++ b/invokeai/app/services/item_storage/item_storage_common.py @@ -0,0 +1,5 @@ +class ItemNotFoundError(KeyError): + """Raised when an item is not found in storage""" + + def __init__(self, item_id: str) -> None: + super().__init__(f"Item with id {item_id} not found") diff --git a/invokeai/app/services/item_storage/item_storage_memory.py b/invokeai/app/services/item_storage/item_storage_memory.py index 028b4808cf..d8dd0e0664 100644 --- a/invokeai/app/services/item_storage/item_storage_memory.py +++ b/invokeai/app/services/item_storage/item_storage_memory.py @@ -1,15 +1,16 @@ from collections import OrderedDict from contextlib import suppress -from typing import Generic, Optional, TypeVar +from typing import Generic, TypeVar from pydantic import BaseModel from invokeai.app.services.item_storage.item_storage_base import ItemStorageABC +from invokeai.app.services.item_storage.item_storage_common import ItemNotFoundError T = TypeVar("T", bound=BaseModel) -class ItemStorageMemory(ItemStorageABC, Generic[T]): +class ItemStorageMemory(ItemStorageABC[T], Generic[T]): """ Provides a simple in-memory storage for items, with a maximum number of items to store. The storage uses the LRU strategy to evict items from storage when the max has been reached. @@ -25,12 +26,13 @@ class ItemStorageMemory(ItemStorageABC, Generic[T]): self._items: OrderedDict[str, T] = OrderedDict() self._max_items = max_items - def get(self, item_id: str) -> Optional[T]: + def get(self, item_id: str) -> T: # If the item exists, move it to the end of the OrderedDict. item = self._items.pop(item_id, None) - if item is not None: - self._items[item_id] = item - return self._items.get(item_id) + if item is None: + raise ItemNotFoundError(item_id) + self._items[item_id] = item + return item def set(self, item: T) -> None: item_id = getattr(item, self._id_field) diff --git a/invokeai/backend/model_management/convert_ckpt_to_diffusers.py b/invokeai/backend/model_management/convert_ckpt_to_diffusers.py index 1cecfb1a72..6878218f67 100644 --- a/invokeai/backend/model_management/convert_ckpt_to_diffusers.py +++ b/invokeai/backend/model_management/convert_ckpt_to_diffusers.py @@ -42,8 +42,7 @@ from diffusers.schedulers import ( PNDMScheduler, UnCLIPScheduler, ) -from diffusers.utils import is_accelerate_available, is_omegaconf_available -from diffusers.utils.import_utils import BACKENDS_MAPPING +from diffusers.utils import is_accelerate_available from picklescan.scanner import scan_file_path from transformers import ( AutoFeatureExtractor, @@ -1211,9 +1210,6 @@ def download_from_original_stable_diffusion_ckpt( if prediction_type == "v-prediction": prediction_type = "v_prediction" - if not is_omegaconf_available(): - raise ValueError(BACKENDS_MAPPING["omegaconf"][1]) - if from_safetensors: from safetensors.torch import load_file as safe_load @@ -1647,11 +1643,6 @@ def download_controlnet_from_original_ckpt( cross_attention_dim: Optional[bool] = None, scan_needed: bool = False, ) -> DiffusionPipeline: - if not is_omegaconf_available(): - raise ValueError(BACKENDS_MAPPING["omegaconf"][1]) - - from omegaconf import OmegaConf - if from_safetensors: from safetensors import safe_open diff --git a/invokeai/backend/model_management/lora.py b/invokeai/backend/model_management/lora.py index d72f55794d..3613dec897 100644 --- a/invokeai/backend/model_management/lora.py +++ b/invokeai/backend/model_management/lora.py @@ -424,6 +424,7 @@ class ONNXModelPatcher: blended_loras = {} for lora, lora_weight in loras: + print(f'DEBUG: lora type = {type(lora)}') for layer_key, layer in lora.layers.items(): if not layer_key.startswith(prefix): continue diff --git a/invokeai/frontend/web/public/locales/ar.json b/invokeai/frontend/web/public/locales/ar.json index 15d5c9c36d..23dc12d9fc 100644 --- a/invokeai/frontend/web/public/locales/ar.json +++ b/invokeai/frontend/web/public/locales/ar.json @@ -7,7 +7,6 @@ "img2img": "صورة إلى صورة", "unifiedCanvas": "لوحة موحدة", "nodes": "عقد", - "langArabic": "العربية", "nodesDesc": "نظام مبني على العقد لإنتاج الصور قيد التطوير حاليًا. تبقى على اتصال مع تحديثات حول هذه الميزة المذهلة.", "postProcessing": "معالجة بعد الإصدار", "postProcessDesc1": "Invoke AI توفر مجموعة واسعة من ميزات المعالجة بعد الإصدار. تحسين الصور واستعادة الوجوه متاحين بالفعل في واجهة الويب. يمكنك الوصول إليهم من الخيارات المتقدمة في قائمة الخيارات في علامة التبويب Text To Image و Image To Image. يمكن أيضًا معالجة الصور مباشرةً باستخدام أزرار الإجراء على الصورة فوق عرض الصورة الحالي أو في العارض.", diff --git a/invokeai/frontend/web/public/locales/az.json b/invokeai/frontend/web/public/locales/az.json new file mode 100644 index 0000000000..54c65ff291 --- /dev/null +++ b/invokeai/frontend/web/public/locales/az.json @@ -0,0 +1,5 @@ +{ + "accessibility": { + "about": "Haqqında" + } +} diff --git a/invokeai/frontend/web/public/locales/de.json b/invokeai/frontend/web/public/locales/de.json index 1444acba3a..e1ef94f06d 100644 --- a/invokeai/frontend/web/public/locales/de.json +++ b/invokeai/frontend/web/public/locales/de.json @@ -5,7 +5,6 @@ "settingsLabel": "Einstellungen", "img2img": "Bild zu Bild", "nodes": "Knoten Editor", - "langGerman": "Deutsch", "nodesDesc": "Ein knotenbasiertes System, für die Erzeugung von Bildern, ist derzeit in der Entwicklung. Bleiben Sie gespannt auf Updates zu dieser fantastischen Funktion.", "postProcessing": "Nachbearbeitung", "postProcessDesc1": "InvokeAI bietet eine breite Palette von Nachbearbeitungsfunktionen. Bildhochskalierung und Gesichtsrekonstruktion sind bereits in der WebUI verfügbar. Sie können sie über das Menü Erweiterte Optionen der Reiter Text in Bild und Bild in Bild aufrufen. Sie können Bilder auch direkt bearbeiten, indem Sie die Schaltflächen für Bildaktionen oberhalb der aktuellen Bildanzeige oder im Viewer verwenden.", @@ -41,24 +40,11 @@ "cancel": "Abbrechen", "accept": "Annehmen", "back": "Zurück", - "langEnglish": "Englisch", - "langDutch": "Niederländisch", - "langFrench": "Französisch", - "langItalian": "Italienisch", - "langPortuguese": "Portugiesisch", - "langRussian": "Russisch", - "langUkranian": "Ukrainisch", "hotkeysLabel": "Tastenkombinationen", "githubLabel": "Github", "discordLabel": "Discord", "txt2img": "Text zu Bild", "postprocessing": "Nachbearbeitung", - "langPolish": "Polnisch", - "langJapanese": "Japanisch", - "langArabic": "Arabisch", - "langKorean": "Koreanisch", - "langHebrew": "Hebräisch", - "langSpanish": "Spanisch", "t2iAdapter": "T2I Adapter", "communityLabel": "Gemeinschaft", "dontAskMeAgain": "Frag mich nicht nochmal", @@ -69,7 +55,6 @@ "on": "An", "nodeEditor": "Knoten Editor", "statusMergingModels": "Modelle zusammenführen", - "langSimplifiedChinese": "Vereinfachtes Chinesisch", "ipAdapter": "IP Adapter", "controlAdapter": "Control Adapter", "auto": "Automatisch", @@ -77,7 +62,6 @@ "imageFailedToLoad": "Kann Bild nicht laden", "statusModelConverted": "Model konvertiert", "modelManager": "Model Manager", - "lightMode": "Heller Modus", "generate": "Erstellen", "learnMore": "Mehr lernen", "darkMode": "Dunkler Modus", @@ -85,7 +69,6 @@ "random": "Zufall", "batch": "Stapel-Manager", "advanced": "Erweitert", - "langBrPortuguese": "Portugiesisch (Brasilien)", "unifiedCanvas": "Einheitliche Leinwand", "openInNewTab": "In einem neuem Tab öffnen", "statusProcessing": "wird bearbeitet", diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 41d6dd3572..e204057fe5 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -118,24 +118,7 @@ "inpaint": "inpaint", "input": "Input", "installed": "Installed", - "langArabic": "العربية", - "langBrPortuguese": "Português do Brasil", - "langDutch": "Nederlands", - "langEnglish": "English", - "langFrench": "Français", - "langGerman": "German", - "langHebrew": "Hebrew", - "langItalian": "Italiano", - "langJapanese": "日本語", - "langKorean": "한국어", - "langPolish": "Polski", - "langPortuguese": "Português", - "langRussian": "Русский", - "langSimplifiedChinese": "简体中文", - "langSpanish": "Español", "languagePickerLabel": "Language", - "langUkranian": "Украї́нська", - "lightMode": "Light Mode", "linear": "Linear", "load": "Load", "loading": "Loading", diff --git a/invokeai/frontend/web/public/locales/es.json b/invokeai/frontend/web/public/locales/es.json index 9f2fda6089..f85cd89721 100644 --- a/invokeai/frontend/web/public/locales/es.json +++ b/invokeai/frontend/web/public/locales/es.json @@ -7,7 +7,6 @@ "img2img": "Imagen a Imagen", "unifiedCanvas": "Lienzo Unificado", "nodes": "Editor del flujo de trabajo", - "langSpanish": "Español", "nodesDesc": "Un sistema de generación de imágenes basado en nodos, actualmente se encuentra en desarrollo. Mantente pendiente a nuestras actualizaciones acerca de esta fabulosa funcionalidad.", "postProcessing": "Post-procesamiento", "postProcessDesc1": "Invoke AI ofrece una gran variedad de funciones de post-procesamiento, El aumento de tamaño y Restauración de Rostros ya se encuentran disponibles en la interfaz web, puedes acceder desde el menú de Opciones Avanzadas en las pestañas de Texto a Imagen y de Imagen a Imagen. También puedes acceder a estas funciones directamente mediante el botón de acciones en el menú superior de la imagen actual o en el visualizador.", @@ -43,25 +42,10 @@ "statusMergedModels": "Modelos combinados", "githubLabel": "Github", "discordLabel": "Discord", - "langEnglish": "Inglés", - "langDutch": "Holandés", - "langFrench": "Francés", - "langGerman": "Alemán", - "langItalian": "Italiano", - "langArabic": "Árabe", - "langJapanese": "Japones", - "langPolish": "Polaco", - "langBrPortuguese": "Portugués brasileño", - "langRussian": "Ruso", - "langSimplifiedChinese": "Chino simplificado", - "langUkranian": "Ucraniano", "back": "Atrás", "statusConvertingModel": "Convertir el modelo", "statusModelConverted": "Modelo adaptado", "statusMergingModels": "Fusionar modelos", - "langPortuguese": "Portugués", - "langKorean": "Coreano", - "langHebrew": "Hebreo", "loading": "Cargando", "loadingInvokeAI": "Cargando invocar a la IA", "postprocessing": "Tratamiento posterior", @@ -77,7 +61,6 @@ "imagePrompt": "Indicación de imagen", "batch": "Administrador de lotes", "darkMode": "Modo oscuro", - "lightMode": "Modo claro", "modelManager": "Administrador de modelos", "communityLabel": "Comunidad" }, diff --git a/invokeai/frontend/web/public/locales/fi.json b/invokeai/frontend/web/public/locales/fi.json index cf7fc6701b..10bbddd72f 100644 --- a/invokeai/frontend/web/public/locales/fi.json +++ b/invokeai/frontend/web/public/locales/fi.json @@ -27,22 +27,12 @@ "statusModelChanged": "Malli vaihdettu", "statusConvertingModel": "Muunnetaan mallia", "statusModelConverted": "Malli muunnettu", - "langFrench": "Ranska", - "langItalian": "Italia", "languagePickerLabel": "Kielen valinta", "hotkeysLabel": "Pikanäppäimet", "reportBugLabel": "Raportoi Bugista", - "langPolish": "Puola", - "langDutch": "Hollanti", "settingsLabel": "Asetukset", "githubLabel": "Github", - "langGerman": "Saksa", - "langPortuguese": "Portugali", "discordLabel": "Discord", - "langEnglish": "Englanti", - "langRussian": "Venäjä", - "langUkranian": "Ukraina", - "langSpanish": "Espanja", "upload": "Lataa", "statusMergedModels": "Mallit yhdistelty", "img2img": "Kuva kuvaksi", diff --git a/invokeai/frontend/web/public/locales/fr.json b/invokeai/frontend/web/public/locales/fr.json index adbfbcfc30..dce2574c38 100644 --- a/invokeai/frontend/web/public/locales/fr.json +++ b/invokeai/frontend/web/public/locales/fr.json @@ -7,7 +7,6 @@ "img2img": "Image en image", "unifiedCanvas": "Canvas unifié", "nodes": "Nœuds", - "langFrench": "Français", "nodesDesc": "Un système basé sur les nœuds pour la génération d'images est actuellement en développement. Restez à l'écoute pour des mises à jour à ce sujet.", "postProcessing": "Post-traitement", "postProcessDesc1": "Invoke AI offre une grande variété de fonctionnalités de post-traitement. Le redimensionnement d'images et la restauration de visages sont déjà disponibles dans la WebUI. Vous pouvez y accéder à partir du menu 'Options avancées' des onglets 'Texte vers image' et 'Image vers image'. Vous pouvez également traiter les images directement en utilisant les boutons d'action d'image au-dessus de l'affichage d'image actuel ou dans le visualiseur.", @@ -47,7 +46,6 @@ "statusMergingModels": "Mélange des modèles", "loadingInvokeAI": "Chargement de Invoke AI", "cancel": "Annuler", - "langEnglish": "Anglais", "statusConvertingModel": "Conversion du modèle", "statusModelConverted": "Modèle converti", "loading": "Chargement", diff --git a/invokeai/frontend/web/public/locales/he.json b/invokeai/frontend/web/public/locales/he.json index aee10f839b..9920053fe8 100644 --- a/invokeai/frontend/web/public/locales/he.json +++ b/invokeai/frontend/web/public/locales/he.json @@ -111,17 +111,6 @@ "githubLabel": "גיטהאב", "discordLabel": "דיסקורד", "settingsLabel": "הגדרות", - "langEnglish": "אנגלית", - "langDutch": "הולנדית", - "langArabic": "ערבית", - "langFrench": "צרפתית", - "langGerman": "גרמנית", - "langJapanese": "יפנית", - "langBrPortuguese": "פורטוגזית", - "langRussian": "רוסית", - "langSimplifiedChinese": "סינית", - "langUkranian": "אוקראינית", - "langSpanish": "ספרדית", "img2img": "תמונה לתמונה", "unifiedCanvas": "קנבס מאוחד", "nodes": "צמתים", @@ -152,9 +141,7 @@ "statusMergedModels": "מודלים מוזגו", "hotkeysLabel": "מקשים חמים", "reportBugLabel": "דווח באג", - "langItalian": "איטלקית", "upload": "העלאה", - "langPolish": "פולנית", "training": "אימון", "load": "טעינה", "back": "אחורה", diff --git a/invokeai/frontend/web/public/locales/hu.json b/invokeai/frontend/web/public/locales/hu.json index aa959bc890..7c19af599e 100644 --- a/invokeai/frontend/web/public/locales/hu.json +++ b/invokeai/frontend/web/public/locales/hu.json @@ -34,7 +34,6 @@ "delete": "Törlés", "data": "Adat", "discordLabel": "Discord", - "folder": "Mappa", - "langEnglish": "Angol" + "folder": "Mappa" } } diff --git a/invokeai/frontend/web/public/locales/it.json b/invokeai/frontend/web/public/locales/it.json index c363ba1faf..fe5667cfef 100644 --- a/invokeai/frontend/web/public/locales/it.json +++ b/invokeai/frontend/web/public/locales/it.json @@ -7,7 +7,6 @@ "img2img": "Immagine a Immagine", "unifiedCanvas": "Tela unificata", "nodes": "Editor del flusso di lavoro", - "langItalian": "Italiano", "nodesDesc": "Attualmente è in fase di sviluppo un sistema basato su nodi per la generazione di immagini. Resta sintonizzato per gli aggiornamenti su questa fantastica funzionalità.", "postProcessing": "Post-elaborazione", "postProcessDesc1": "Invoke AI offre un'ampia varietà di funzionalità di post-elaborazione. Ampliamento Immagine e Restaura Volti sono già disponibili nell'interfaccia Web. È possibile accedervi dal menu 'Opzioni avanzate' delle schede 'Testo a Immagine' e 'Immagine a Immagine'. È inoltre possibile elaborare le immagini direttamente, utilizzando i pulsanti di azione dell'immagine sopra la visualizzazione dell'immagine corrente o nel visualizzatore.", @@ -43,26 +42,11 @@ "statusModelChanged": "Modello cambiato", "githubLabel": "GitHub", "discordLabel": "Discord", - "langArabic": "Arabo", - "langEnglish": "Inglese", - "langFrench": "Francese", - "langGerman": "Tedesco", - "langJapanese": "Giapponese", - "langPolish": "Polacco", - "langBrPortuguese": "Portoghese Basiliano", - "langRussian": "Russo", - "langUkranian": "Ucraino", - "langSpanish": "Spagnolo", "statusMergingModels": "Fusione Modelli", "statusMergedModels": "Modelli fusi", - "langSimplifiedChinese": "Cinese semplificato", - "langDutch": "Olandese", "statusModelConverted": "Modello Convertito", "statusConvertingModel": "Conversione Modello", - "langKorean": "Coreano", - "langPortuguese": "Portoghese", "loading": "Caricamento in corso", - "langHebrew": "Ebraico", "loadingInvokeAI": "Caricamento Invoke AI", "postprocessing": "Post Elaborazione", "txt2img": "Testo a Immagine", @@ -76,7 +60,6 @@ "dontAskMeAgain": "Non chiedermelo più", "imagePrompt": "Prompt Immagine", "darkMode": "Modalità scura", - "lightMode": "Modalità chiara", "batch": "Gestione Lotto", "modelManager": "Gestore modello", "communityLabel": "Comunità", diff --git a/invokeai/frontend/web/public/locales/ja.json b/invokeai/frontend/web/public/locales/ja.json index 0fee501e11..e92b5320f3 100644 --- a/invokeai/frontend/web/public/locales/ja.json +++ b/invokeai/frontend/web/public/locales/ja.json @@ -3,7 +3,6 @@ "languagePickerLabel": "言語", "reportBugLabel": "バグ報告", "settingsLabel": "設定", - "langJapanese": "日本語", "nodesDesc": "現在、画像生成のためのノードベースシステムを開発中です。機能についてのアップデートにご期待ください。", "postProcessing": "後処理", "postProcessDesc1": "Invoke AIは、多彩な後処理の機能を備えています。アップスケーリングと顔修復は、すでにWebUI上で利用可能です。これらは、[Text To Image]および[Image To Image]タブの[詳細オプション]メニューからアクセスできます。また、現在の画像表示の上やビューア内の画像アクションボタンを使って、画像を直接処理することもできます。", @@ -36,11 +35,6 @@ "statusModelChanged": "モデルを変更", "cancel": "キャンセル", "accept": "同意", - "langBrPortuguese": "Português do Brasil", - "langRussian": "Русский", - "langSimplifiedChinese": "简体中文", - "langUkranian": "Украї́нська", - "langSpanish": "Español", "img2img": "img2img", "unifiedCanvas": "Unified Canvas", "statusMergingModels": "モデルのマージ", @@ -54,18 +48,8 @@ "statusMergedModels": "マージ済モデル", "githubLabel": "Github", "hotkeysLabel": "ホットキー", - "langHebrew": "עברית", "discordLabel": "Discord", - "langItalian": "Italiano", - "langEnglish": "English", - "langArabic": "アラビア語", - "langDutch": "Nederlands", - "langFrench": "Français", - "langGerman": "Deutsch", - "langPortuguese": "Português", "nodes": "ワークフローエディター", - "langKorean": "한국어", - "langPolish": "Polski", "txt2img": "txt2img", "postprocessing": "Post Processing", "t2iAdapter": "T2I アダプター", @@ -84,7 +68,6 @@ "imageFailedToLoad": "画像が読み込めません", "imagePrompt": "画像プロンプト", "modelManager": "モデルマネージャー", - "lightMode": "ライトモード", "generate": "生成", "learnMore": "もっと学ぶ", "darkMode": "ダークモード", diff --git a/invokeai/frontend/web/public/locales/ko.json b/invokeai/frontend/web/public/locales/ko.json index 476c421fa3..cf37e24d7b 100644 --- a/invokeai/frontend/web/public/locales/ko.json +++ b/invokeai/frontend/web/public/locales/ko.json @@ -4,17 +4,7 @@ "reportBugLabel": "버그 리포트", "githubLabel": "Github", "settingsLabel": "설정", - "langArabic": "العربية", - "langEnglish": "English", - "langDutch": "Nederlands", "unifiedCanvas": "통합 캔버스", - "langFrench": "Français", - "langGerman": "Deutsch", - "langItalian": "Italiano", - "langJapanese": "日本語", - "langBrPortuguese": "Português do Brasil", - "langRussian": "Русский", - "langSpanish": "Español", "nodes": "Workflow Editor", "nodesDesc": "이미지 생성을 위한 노드 기반 시스템은 현재 개발 중입니다. 이 놀라운 기능에 대한 업데이트를 계속 지켜봐 주세요.", "postProcessing": "후처리", @@ -31,7 +21,6 @@ "statusDisconnected": "연결 끊김", "statusError": "에러", "statusPreparing": "준비 중", - "langSimplifiedChinese": "简体中文", "statusGenerating": "생성 중", "statusGeneratingTextToImage": "텍스트->이미지 생성", "statusGeneratingInpainting": "인페인팅 생성", @@ -51,9 +40,7 @@ "hotkeysLabel": "단축키 설정", "img2img": "이미지->이미지", "discordLabel": "Discord", - "langPolish": "Polski", "postProcessDesc1": "Invoke AI는 다양한 후처리 기능을 제공합니다. 이미지 업스케일링 및 얼굴 복원은 이미 Web UI에서 사용할 수 있습니다. 텍스트->이미지 또는 이미지->이미지 탭의 고급 옵션 메뉴에서 사용할 수 있습니다. 또한 현재 이미지 표시 위, 또는 뷰어에서 액션 버튼을 사용하여 이미지를 직접 처리할 수도 있습니다.", - "langUkranian": "Украї́нська", "statusProcessingCanceled": "처리 취소됨", "statusGeneratingImageToImage": "이미지->이미지 생성", "statusProcessingComplete": "처리 완료", @@ -73,7 +60,6 @@ "updated": "업데이트 됨", "on": "켜기", "save": "저장", - "langPortuguese": "Português", "created": "생성됨", "nodeEditor": "Node Editor", "error": "에러", @@ -100,10 +86,8 @@ "somethingWentWrong": "뭔가 잘못됐어요", "imagePrompt": "이미지 프롬프트", "modelManager": "Model Manager", - "lightMode": "라이트 모드", "safetensors": "Safetensors", "outpaint": "outpaint", - "langKorean": "한국어", "orderBy": "정렬 기준", "generate": "생성", "copyError": "$t(gallery.copy) 에러", @@ -113,7 +97,6 @@ "darkMode": "다크 모드", "loading": "불러오는 중", "random": "랜덤", - "langHebrew": "Hebrew", "batch": "Batch 매니저", "postprocessing": "후처리", "advanced": "고급", diff --git a/invokeai/frontend/web/public/locales/nl.json b/invokeai/frontend/web/public/locales/nl.json index 195fe4dc06..8290c8904f 100644 --- a/invokeai/frontend/web/public/locales/nl.json +++ b/invokeai/frontend/web/public/locales/nl.json @@ -7,7 +7,6 @@ "img2img": "Afbeelding naar afbeelding", "unifiedCanvas": "Centraal canvas", "nodes": "Werkstroom-editor", - "langDutch": "Nederlands", "nodesDesc": "Een op knooppunten gebaseerd systeem voor het genereren van afbeeldingen is momenteel in ontwikkeling. Blijf op de hoogte voor nieuws over deze verbluffende functie.", "postProcessing": "Naverwerking", "postProcessDesc1": "Invoke AI biedt een breed scala aan naverwerkingsfuncties. Afbeeldingsopschaling en Gezichtsherstel zijn al beschikbaar in de web-UI. Je kunt ze openen via het menu Uitgebreide opties in de tabbladen Tekst naar afbeelding en Afbeelding naar afbeelding. Je kunt een afbeelding ook direct verwerken via de afbeeldingsactieknoppen boven de weergave van de huidigde afbeelding of in de Viewer.", @@ -41,18 +40,6 @@ "statusModelChanged": "Model gewijzigd", "githubLabel": "Github", "discordLabel": "Discord", - "langArabic": "Arabisch", - "langEnglish": "Engels", - "langFrench": "Frans", - "langGerman": "Duits", - "langItalian": "Italiaans", - "langJapanese": "Japans", - "langPolish": "Pools", - "langBrPortuguese": "Portugees (Brazilië)", - "langRussian": "Russisch", - "langSimplifiedChinese": "Chinees (vereenvoudigd)", - "langUkranian": "Oekraïens", - "langSpanish": "Spaans", "training": "Training", "back": "Terug", "statusConvertingModel": "Omzetten van model", @@ -61,11 +48,8 @@ "statusMergedModels": "Modellen samengevoegd", "cancel": "Annuleer", "accept": "Akkoord", - "langPortuguese": "Portugees", "loading": "Bezig met laden", "loadingInvokeAI": "Bezig met laden van Invoke AI", - "langHebrew": "עברית", - "langKorean": "한국어", "txt2img": "Tekst naar afbeelding", "postprocessing": "Naverwerking", "dontAskMeAgain": "Vraag niet opnieuw", @@ -78,7 +62,6 @@ "batch": "Seriebeheer", "modelManager": "Modelbeheer", "darkMode": "Donkere modus", - "lightMode": "Lichte modus", "communityLabel": "Gemeenschap", "t2iAdapter": "T2I-adapter", "on": "Aan", diff --git a/invokeai/frontend/web/public/locales/pl.json b/invokeai/frontend/web/public/locales/pl.json index 35300596f4..7178bd1487 100644 --- a/invokeai/frontend/web/public/locales/pl.json +++ b/invokeai/frontend/web/public/locales/pl.json @@ -7,7 +7,6 @@ "img2img": "Obraz na obraz", "unifiedCanvas": "Tryb uniwersalny", "nodes": "Węzły", - "langPolish": "Polski", "nodesDesc": "W tym miejscu powstanie graficzny system generowania obrazów oparty na węzłach. Jest na co czekać!", "postProcessing": "Przetwarzanie końcowe", "postProcessDesc1": "Invoke AI oferuje wiele opcji przetwarzania końcowego. Z poziomu przeglądarki dostępne jest już zwiększanie rozdzielczości oraz poprawianie twarzy. Znajdziesz je wśród ustawień w trybach \"Tekst na obraz\" oraz \"Obraz na obraz\". Są również obecne w pasku menu wyświetlanym nad podglądem wygenerowanego obrazu.", @@ -42,8 +41,7 @@ "statusModelChanged": "Zmieniono model", "githubLabel": "GitHub", "discordLabel": "Discord", - "darkMode": "Tryb ciemny", - "lightMode": "Tryb jasny" + "darkMode": "Tryb ciemny" }, "gallery": { "generations": "Wygenerowane", diff --git a/invokeai/frontend/web/public/locales/pt.json b/invokeai/frontend/web/public/locales/pt.json index 9dd020f7e2..b44db81b49 100644 --- a/invokeai/frontend/web/public/locales/pt.json +++ b/invokeai/frontend/web/public/locales/pt.json @@ -1,22 +1,9 @@ { "common": { - "langArabic": "العربية", "reportBugLabel": "Reportar Bug", "settingsLabel": "Configurações", - "langBrPortuguese": "Português do Brasil", "languagePickerLabel": "Seletor de Idioma", - "langDutch": "Nederlands", - "langEnglish": "English", "hotkeysLabel": "Hotkeys", - "langPolish": "Polski", - "langFrench": "Français", - "langGerman": "Deutsch", - "langItalian": "Italiano", - "langJapanese": "日本語", - "langSimplifiedChinese": "简体中文", - "langSpanish": "Espanhol", - "langRussian": "Русский", - "langUkranian": "Украї́нська", "img2img": "Imagem para Imagem", "unifiedCanvas": "Tela Unificada", "nodes": "Nós", @@ -60,8 +47,7 @@ "statusMergingModels": "Mesclando Modelos", "statusMergedModels": "Modelos Mesclados", "loading": "A carregar", - "loadingInvokeAI": "A carregar Invoke AI", - "langPortuguese": "Português" + "loadingInvokeAI": "A carregar Invoke AI" }, "gallery": { "galleryImageResetSize": "Resetar Imagem", diff --git a/invokeai/frontend/web/public/locales/pt_BR.json b/invokeai/frontend/web/public/locales/pt_BR.json index ac2d36839b..6d0f12f660 100644 --- a/invokeai/frontend/web/public/locales/pt_BR.json +++ b/invokeai/frontend/web/public/locales/pt_BR.json @@ -7,7 +7,6 @@ "img2img": "Imagem Para Imagem", "unifiedCanvas": "Tela Unificada", "nodes": "Nódulos", - "langBrPortuguese": "Português do Brasil", "nodesDesc": "Um sistema baseado em nódulos para geração de imagens está em contrução. Fique ligado para atualizações sobre essa funcionalidade incrível.", "postProcessing": "Pós-processamento", "postProcessDesc1": "Invoke AI oferece uma variedade e funcionalidades de pós-processamento. Redimensionador de Imagem e Restauração Facial já estão disponíveis na interface. Você pode acessar elas no menu de Opções Avançadas na aba de Texto para Imagem e Imagem para Imagem. Você também pode processar imagens diretamente, usando os botões de ação de imagem acima da atual tela de imagens ou visualizador.", @@ -42,23 +41,11 @@ "statusModelChanged": "Modelo Alterado", "githubLabel": "Github", "discordLabel": "Discord", - "langArabic": "Árabe", - "langEnglish": "Inglês", - "langDutch": "Holandês", - "langFrench": "Francês", - "langGerman": "Alemão", - "langItalian": "Italiano", - "langJapanese": "Japonês", - "langPolish": "Polonês", - "langSimplifiedChinese": "Chinês", - "langUkranian": "Ucraniano", "back": "Voltar", "statusConvertingModel": "Convertendo Modelo", "statusModelConverted": "Modelo Convertido", "statusMergingModels": "Mesclando Modelos", "statusMergedModels": "Modelos Mesclados", - "langRussian": "Russo", - "langSpanish": "Espanhol", "loadingInvokeAI": "Carregando Invoke AI", "loading": "Carregando" }, diff --git a/invokeai/frontend/web/public/locales/ru.json b/invokeai/frontend/web/public/locales/ru.json index db4f7552cb..9643699333 100644 --- a/invokeai/frontend/web/public/locales/ru.json +++ b/invokeai/frontend/web/public/locales/ru.json @@ -7,7 +7,6 @@ "img2img": "Изображение в изображение (img2img)", "unifiedCanvas": "Единый холст", "nodes": "Редактор рабочего процесса", - "langRussian": "Русский", "nodesDesc": "Cистема генерации изображений на основе нодов (узлов) уже разрабатывается. Следите за новостями об этой замечательной функции.", "postProcessing": "Постобработка", "postProcessDesc1": "Invoke AI предлагает широкий спектр функций постобработки. Увеличение изображения (upscale) и восстановление лиц уже доступны в интерфейсе. Получите доступ к ним из меню 'Дополнительные параметры' на вкладках 'Текст в изображение' и 'Изображение в изображение'. Обрабатывайте изображения напрямую, используя кнопки действий с изображениями над текущим изображением или в режиме просмотра.", @@ -51,23 +50,8 @@ "statusConvertingModel": "Конвертация модели", "cancel": "Отменить", "accept": "Принять", - "langUkranian": "Украї́нська", - "langEnglish": "English", "postprocessing": "Постобработка", - "langArabic": "العربية", - "langSpanish": "Español", - "langSimplifiedChinese": "简体中文", - "langDutch": "Nederlands", - "langFrench": "Français", - "langGerman": "German", - "langHebrew": "Hebrew", - "langItalian": "Italiano", - "langJapanese": "日本語", - "langKorean": "한국어", - "langPolish": "Polski", - "langPortuguese": "Português", "txt2img": "Текст в изображение (txt2img)", - "langBrPortuguese": "Português do Brasil", "linear": "Линейная обработка", "dontAskMeAgain": "Больше не спрашивать", "areYouSure": "Вы уверены?", @@ -76,7 +60,6 @@ "openInNewTab": "Открыть в новой вкладке", "imagePrompt": "Запрос", "communityLabel": "Сообщество", - "lightMode": "Светлая тема", "batch": "Пакетный менеджер", "modelManager": "Менеджер моделей", "darkMode": "Темная тема", diff --git a/invokeai/frontend/web/public/locales/sv.json b/invokeai/frontend/web/public/locales/sv.json index ac2c3661e6..b292b08753 100644 --- a/invokeai/frontend/web/public/locales/sv.json +++ b/invokeai/frontend/web/public/locales/sv.json @@ -26,24 +26,8 @@ "githubLabel": "Github", "discordLabel": "Discord", "settingsLabel": "Inställningar", - "langEnglish": "Engelska", - "langDutch": "Nederländska", - "langFrench": "Franska", - "langGerman": "Tyska", - "langItalian": "Italienska", - "langArabic": "العربية", - "langHebrew": "עברית", - "langPolish": "Polski", - "langPortuguese": "Português", - "langBrPortuguese": "Português do Brasil", - "langSimplifiedChinese": "简体中文", - "langJapanese": "日本語", - "langKorean": "한국어", - "langRussian": "Русский", "unifiedCanvas": "Förenad kanvas", "nodesDesc": "Ett nodbaserat system för bildgenerering är under utveckling. Håll utkik för uppdateringar om denna fantastiska funktion.", - "langUkranian": "Украї́нська", - "langSpanish": "Español", "postProcessDesc2": "Ett dedikerat användargränssnitt kommer snart att släppas för att underlätta mer avancerade arbetsflöden av efterbehandling.", "trainingDesc1": "Ett dedikerat arbetsflöde för träning av dina egna inbäddningar och kontrollpunkter genom Textual Inversion eller Dreambooth från webbgränssnittet.", "trainingDesc2": "InvokeAI stöder redan träning av anpassade inbäddningar med hjälp av Textual Inversion genom huvudscriptet.", diff --git a/invokeai/frontend/web/public/locales/tr.json b/invokeai/frontend/web/public/locales/tr.json index fae92daa3a..a7b7d998ef 100644 --- a/invokeai/frontend/web/public/locales/tr.json +++ b/invokeai/frontend/web/public/locales/tr.json @@ -2,7 +2,7 @@ "accessibility": { "invokeProgressBar": "Invoke durum çubuğu", "nextImage": "Sonraki Görsel", - "useThisParameter": "Bu ayarları kullan", + "useThisParameter": "Bu seçenekleri kullan", "copyMetadataJson": "Üstveriyi kopyala (JSON)", "exitViewer": "Görüntüleyiciden Çık", "zoomIn": "Yakınlaştır", @@ -11,7 +11,7 @@ "rotateClockwise": "Saat yönüne döndür", "flipHorizontally": "Yatay Çevir", "flipVertically": "Dikey Çevir", - "modifyConfig": "Ayarları Değiştir", + "modifyConfig": "Yapılandırmayı Değiştir", "toggleAutoscroll": "Otomatik kaydırmayı Aç-Kapat", "toggleLogViewer": "Günlüğü Aç-Kapat", "showOptionsPanel": "Yan Paneli Göster", @@ -33,21 +33,7 @@ "reportBugLabel": "Sorun Bildir", "githubLabel": "Github", "discordLabel": "Discord", - "settingsLabel": "Ayarlar", - "langArabic": "Arapça", - "langEnglish": "İngilizce", - "langDutch": "Hollandaca", - "langFrench": "Fransızca", - "langGerman": "Almanca", - "langItalian": "İtalyanca", - "langJapanese": "Japonca", - "langPolish": "Lehçe", - "langPortuguese": "Portekizce", - "langBrPortuguese": "Portekizce (Brezilya)", - "langRussian": "Rusça", - "langSimplifiedChinese": "Çince (Basit)", - "langUkranian": "Ukraynaca", - "langSpanish": "İspanyolca", + "settingsLabel": "Seçenekler", "txt2img": "Yazıdan Görsel", "img2img": "Görselden Görsel", "linear": "Doğrusal", @@ -56,10 +42,9 @@ "postProcessing": "Rötuş", "postProcessDesc2": "Daha gelişmiş iş akışlarına olanak sağlayacak özel bir arayüz yakında yayınlanacaktır.", "postProcessDesc3": "Invoke AI Komut Satırı Arayüzü, içlerinde Embiggen da bulunan birçok özellik sunmaktadır.", - "langKorean": "Korece", "unifiedCanvas": "Tuval", "nodesDesc": "Görsel oluşturmaya yardımcı çizge tabanlı sistem şimdilik geliştirme aşamasındadır. Bu süper özellik hakkındaki gelişmeler için kulağınız bizde olsun.", - "postProcessDesc1": "Invoke AI birçok rötuş (post-process) aracı sağlar. Görsel büyütme ve yüz iyileştirme WebUI üzerinden kullanıma uygun durumdadır. Bunlara Yazıdan Görsel ve Görselden Görsel sekmelerindeki Gelişmiş Ayarlar menüsünden ulaşabilirsiniz. Ayrıca var olan görseli üzerindeki düğmeler yardımıyla düzenleyebilirsiniz.", + "postProcessDesc1": "Invoke AI birçok rötuş (post-process) aracı sağlar. Görsel büyütme ve yüz iyileştirme WebUI üzerinden kullanıma uygun durumdadır. Bunlara Yazıdan Görsel ve Görselden Görsel sekmelerindeki Gelişmiş Seçenekler menüsünden ulaşabilirsiniz. Ayrıca var olan görseli üzerindeki düğmeler yardımıyla düzenleyebilirsiniz.", "batch": "Toplu İş Yöneticisi", "accept": "Onayla", "cancel": "Vazgeç", @@ -68,7 +53,7 @@ "on": "Açık", "or": "ya da", "aboutDesc": "Invoke'u iş için mi kullanıyorsunuz? Şuna bir göz atın:", - "advancedOptions": "Gelişmiş Ayarlar", + "advancedOptions": "Gelişmiş Seçenekler", "ai": "yapay zeka", "close": "Kapat", "auto": "Otomatik", @@ -78,7 +63,6 @@ "notInstalled": "$t(common.installed) Değil", "openInNewTab": "Yeni Sekmede Aç", "aboutHeading": "Yaratıcı Gücünüzün Sahibi Olun", - "lightMode": "Açık Tema", "load": "Yükle", "loading": "Yükleniyor", "loadingInvokeAI": "Invoke AI Yükleniyor", @@ -88,7 +72,6 @@ "orderBy": "Sırala", "outpaint": "dışboyama", "outputs": "Çıktılar", - "langHebrew": "İbranice", "learnMore": "Bilgi Edin", "nodeEditor": "Çizge Düzenleyici", "save": "Kaydet", @@ -153,7 +136,11 @@ "input": "Giriş", "copy": "Kopyala", "created": "Yaratma", - "updated": "Güncelleme" + "updated": "Güncelleme", + "ipAdapter": "IP Aracı", + "t2iAdapter": "T2I Aracı", + "controlAdapter": "Yönetim Aracı", + "controlNet": "ControlNet" }, "accordions": { "generation": { @@ -173,7 +160,9 @@ "infillTab": "Doldurma" }, "control": { - "ipTab": "Görsel İstemleri" + "ipTab": "Görsel İstemleri", + "title": "Yönetim", + "controlAdaptersTab": "Yönetim Araçları" } }, "boards": { @@ -221,7 +210,7 @@ "none": "Hiçbiri", "noneDescription": "Hiçbir işlem uygulanmamış", "selectModel": "Model seçin", - "showAdvanced": "Gelişmiş Ayarları Göster", + "showAdvanced": "Gelişmiş Seçenekleri Göster", "controlNetT2IMutexDesc": "$t(common.controlNet) ve $t(common.t2iAdapter)'nün birlikte kullanımı şimdilik desteklenmiyor.", "canny": "Canny", "colorMapDescription": "Görselden renk haritası oluşturur", @@ -240,12 +229,55 @@ "fill": "Doldur", "highThreshold": "Üst Eşik", "imageResolution": "Görsel Çözünürlüğü", - "colorMapTileSize": "Karo Boyutu", + "colorMapTileSize": "Döşeme Boyutu", "importImageFromCanvas": "Tuvaldeki Görseli Al", "importMaskFromCanvas": "Tuvalden Maskeyi İçe Aktar", "lowThreshold": "Alt Eşik", "base": "Taban", - "depthAnythingDescription": "Depth Anything yöntemi ile derinlik haritası oluşturma" + "depthAnythingDescription": "Depth Anything yöntemi ile derinlik haritası oluşturma", + "controlAdapter_one": "Yönetim Aracı", + "controlAdapter_other": "Yönetim Aracı", + "beginEndStepPercent": "Başlangıç / Bitiş Yüzdesi", + "control": "Yönetim", + "controlnet": "{{number}}. $t(controlnet.controlAdapter_one) ($t(common.controlNet))", + "amult": "a_mult", + "hideAdvanced": "Gelişmiş Seçenekleri Gizle", + "enableControlnet": "ControlNet'i Etkinleştir", + "hed": "HED", + "ip_adapter": "{{number}}. $t(controlnet.controlAdapter_one) ($t(common.ipAdapter))", + "t2i_adapter": "{{number}}. $t(controlnet.controlAdapter_one) ($t(common.t2iAdapter))", + "autoConfigure": "Aracı otomatik yapılandır", + "bgth": "bg_th", + "coarse": "İri", + "controlMode": "Yönetim Kipi", + "depthAnything": "Depth Anything", + "hedDescription": "Holistically-Nested Edge Detection (Bütünsel Yuvalanmış Kenar Tespiti)", + "lineartAnime": "Çizim (Anime)", + "lineartAnimeDescription": "Anime stili çizim işleme", + "minConfidence": "Özgüven Alt Limiti", + "pidiDescription": "PIDI görsel işleme", + "mediapipeFace": "Mediapipe Yüz", + "megaControl": "Aşırı Yönetim", + "mlsd": "M-LSD", + "openPoseDescription": "Openpose kullanarak poz belirleme", + "setControlImageDimensions": "Yönetim Görseli Boyutlarını En/Boydan Al", + "pidi": "PIDI", + "scribble": "çiziktirme", + "ipAdapterModel": "Araç Modeli", + "resetIPAdapterImage": "IP Aracı Görselini Kaldır", + "mediapipeFaceDescription": "Mediapipe kullanarak yüz algılama", + "saveControlImage": "Yönetim Görselini Kaydet", + "w": "En", + "lineartDescription": "Görseli çizime dönüştürür", + "maxFaces": "Yüz Üst Limiti", + "mlsdDescription": "Minimalist Line Segment Detector (Kolay Çizgi Parçası Algılama)", + "normalBae": "Normal BAE", + "normalBaeDescription": "Normal BAE işleme", + "openPose": "Openpose", + "resetControlImage": "Yönetim Görselini Kaldır", + "enableIPAdapter": "IP Aracını Etkinleştir", + "lineart": "Çizim", + "ipAdapterImageFallback": "IP Aracı Görseli Seçilmemiş" }, "queue": { "queuedCount": "{{pending}} Sırada", @@ -304,7 +336,9 @@ "batchQueued": "Toplu İş Sıraya Alındı", "notReady": "Sıraya Alınamadı", "batchFieldValues": "Toplu İş Değişkenleri", - "queueMaxExceeded": "Sıra sınırı {{max_queue_size}} aşıldı, {{skip}} atlanıyor" + "queueMaxExceeded": "Sıra sınırı {{max_queue_size}} aşıldı, {{skip}} atlanıyor", + "graphFailedToQueue": "Çizge sıraya alınamadı", + "graphQueued": "Çizge sıraya alındı" }, "invocationCache": { "cacheSize": "Önbellek Boyutu", @@ -331,7 +365,7 @@ "unstarImage": "Yıldızı Kaldır", "uploads": "Yüklemeler", "problemDeletingImagesDesc": "Bir ya da daha çok görsel silinemedi", - "gallerySettings": "Galeri Ayarları", + "gallerySettings": "Galeri Düzeni", "image": "görsel", "galleryImageSize": "Görsel Boyutu", "allImagesLoaded": "Tüm Görseller Yüklendi", @@ -349,13 +383,16 @@ "singleColumnLayout": "Tek Sütun Düzen", "generations": "Çıktılar", "showUploads": "Yüklenenleri Göster", - "showGenerations": "Çıktıları Göster" + "showGenerations": "Çıktıları Göster", + "dropOrUpload": "$t(gallery.drop) ya da Yükle", + "dropToUpload": "Yüklemek için $t(gallery.drop)", + "drop": "Bırak" }, "hrf": { "hrf": "Yüksek Çözünürlük Kürü", "enableHrf": "Yüksek Çözünürlük Kürünü Aç", - "hrfStrength": "Yüksek Çözünürlük Kürü Etkisi", - "strengthTooltip": "Düşük değerler daha az detaya neden olsa da olası bozuklukları önleyebilir.", + "hrfStrength": "Yüksek Çözünürlük Kürü Ölçüsü", + "strengthTooltip": "Düşük değerler daha az ayrıntıya neden olsa da olası bozuklukları önleyebilir.", "metadata": { "enabled": "Yüksek Çözünürlük Kürü Açık", "strength": "Yüksek Çözünürlük Kürü Etkisi", @@ -390,12 +427,12 @@ "desc": "Galerideki sonraki görseli göster" }, "maximizeWorkSpace": { - "desc": "Panelleri kapat ve çalışma alanını genişlet", + "desc": "Panelleri kapatıp çalışma alanını genişlet", "title": "Çalışma Alanını Genişlet" }, "pinOptions": { - "desc": "Ayar panelini iğnele", - "title": "Ayarları İğnele" + "desc": "Seçenekler panelini iğnele", + "title": "Seçenekleri İğnele" }, "nodesHotkeys": "Çizgeler", "quickToggleMove": { @@ -500,8 +537,8 @@ "title": "Silgiyi Kullan" }, "toggleOptions": { - "desc": "Ayarlar panelini aç-kapat", - "title": "Ayarları Aç-Kapat" + "desc": "Seçenekler panelini aç-kapat", + "title": "Seçenekleri Aç-Kapat" }, "copyToClipboard": { "desc": "Tuval içeriğini kopyala", @@ -534,8 +571,8 @@ "title": "Önceki Görsel" }, "toggleOptionsAndGallery": { - "title": "Ayarları ve Galeriyi Aç-Kapat", - "desc": "Ayarlar ve galeri panellerini aç-kapat" + "title": "Seçenekleri ve Galeriyi Aç-Kapat", + "desc": "Seçenekler ve galeri panellerini aç-kapat" }, "toggleSnap": { "desc": "Kılavuza Uydur", @@ -554,8 +591,8 @@ "desc": "Tuval fırçasının/silgisinin boyutunu düşürür" }, "resetOptionsAndGallery": { - "desc": "Ayarlar ve galeri panellerini resetler", - "title": "Ayarları ve Galeriyi Resetle" + "desc": "Seçenekler ve galeri panellerini resetler", + "title": "Seçenekleri ve Galeriyi Resetle" }, "remixImage": { "desc": "Seçili görselin tohumu hariç tüm değişkenlerini kullan", @@ -581,6 +618,10 @@ "selectBrush": { "desc": "Tuval fırçasını kullan", "title": "Fırçayı Kullan" + }, + "restoreFaces": { + "title": "Yüzleri Onar", + "desc": "Geçerli görseli onar" } }, "embedding": { @@ -606,7 +647,7 @@ "currentImageDescription": "İşlemdeki görseli Çizge Düzenleyicide gösterir", "workflowAuthor": "Yaratıcı", "workflowName": "Ad", - "workflowSettings": "İş Akışı Düzenleyici Ayarları", + "workflowSettings": "İş Akışı Düzenleyici Seçenekleri", "currentImage": "İşlemdeki Görsel", "noWorkflow": "İş Akışı Yok", "newWorkflowDesc": "Yeni iş akışı?", @@ -617,7 +658,22 @@ "unableToGetWorkflowVersion": "İş akışı sürümüne ulaşılamadı", "unrecognizedWorkflowVersion": "Tanınmayan iş akışı sürümü {{version}}", "newWorkflowDesc2": "Geçerli iş akışında kaydedilmemiş değişiklikler var.", - "unableToLoadWorkflow": "İş Akışı Yüklenemedi" + "unableToLoadWorkflow": "İş Akışı Yüklenemedi", + "boardField": "Pano", + "boardFieldDescription": "Galeri panosu", + "booleanCollection": "Boole Değeri Yığını", + "booleanCollectionDescription": "Bir yığın boole değeri.", + "booleanDescription": "Boole değerleri doğru ya da yanlıştır.", + "cannotConnectInputToInput": "Giriş girişe bağlanamaz", + "zoomInNodes": "Yakınlaştır", + "boolean": "Boole Değeri", + "edge": "Uç", + "zoomOutNodes": "Uzaklaştır", + "cannotConnectOutputToOutput": "Çıkış çıkışa bağlanamaz", + "colorField": "Renk", + "colorFieldDescription": "RGBA rengi.", + "cannotConnectToSelf": "Kendisine bağlanamaz", + "cannotDuplicateConnection": "Kopya bağlantılar yaratılamaz" }, "workflows": { "searchWorkflows": "İş Akışlarında Ara", @@ -659,20 +715,264 @@ }, "parameters": { "invoke": { - "noPrompts": "İstem oluşturulmadı" - } + "noPrompts": "İstem oluşturulmadı", + "systemBusy": "Sistem kullanımda", + "noModelSelected": "Model seçilmedi", + "incompatibleBaseModelForControlAdapter": "{{number}}. yönetim aracı, ana model ile uyumlu değil.", + "systemDisconnected": "Sistem bağlantısı kesildi", + "invoke": "Invoke" + }, + "clipSkip": "CLIP Atlama", + "randomizeSeed": "Rastgele Tohum", + "cfgScale": "CFG Ölçeği", + "coherenceStrength": "Etki", + "controlNetControlMode": "Yönetim Kipi", + "general": "Genel", + "img2imgStrength": "Görselden Görsel Ölçüsü", + "seamlessYAxis": "Dikişsiz Döşeme Y Ekseni", + "seamLowThreshold": "Alt", + "isAllowedToUpscale": { + "tooLarge": "Görsel, büyütme işlemi için çok büyük, daha küçük bir boyut seçin", + "useX2Model": "Görsel 4 kat büyütme işlemi için çok geniş, 2 kat büyütmeyi kullanın" + }, + "maskAdjustmentsHeader": "Maske Düzenleme", + "maskBlur": "Bulandırma", + "aspectRatio": "En-Boy Oranı", + "images": "Görseller", + "hidePreview": "Önizlemeyi Gizle", + "info": "Bilgi", + "positivePromptPlaceholder": "Olumlu İstem", + "scaledHeight": "Ölçekli Boy", + "aspectRatioFree": "Serbest", + "lockAspectRatio": "En-Boy Oranını Koru", + "swapDimensions": "Çevir", + "setToOptimalSize": "Modele göre en uygun boyut", + "copyImage": "Görseli Kopyala", + "faceRestoration": "Yüz Onarma", + "gaussianBlur": "Çan Eğrisi Bulanıklık", + "height": "Boy", + "imageSize": "Görsel Boyutu", + "initialImage": "Öngörsel", + "otherOptions": "Diğer Seçenekler", + "manualSeed": "Özel Tohum", + "randomSeed": "Rastgele Tohum", + "seamCorrectionHeader": "Dikiş Düzeltme", + "width": "En", + "showPreview": "Önizlemeyi Göster", + "upscale": "Büyüt (Shift + U)", + "unmasked": "Maskesiz", + "useSize": "Boyutu Kullan", + "boundingBoxWidth": "Sınırlayıcı Kutu Eni", + "boundingBoxHeight": "Sınırlayıcı Kutu Boyu", + "boundingBoxHeader": "Sınırlayıcı Kutu", + "imageToImage": "Görselden Görsel", + "seamlessY": "Dikişsiz Y", + "seamlessX&Y": "Dikişsiz X & Y", + "seedWeights": "Tohum Ağırlıkları", + "symmetry": "Bakışım", + "tileSize": "Döşeme Boyutu", + "strength": "Güç", + "gpuNoise": "GPU Gürültüsü", + "useAll": "Hepsini Kullan", + "boxBlur": "Kutu Bulanıklık", + "clipSkipWithLayerCount": "CLIP Atla {{layerCount}}", + "denoisingStrength": "Arındırma Ölçüsü", + "imageFit": "Öngörseli Çıktı Boyutuna Sığdır", + "maskEdge": "Maske Kenarı", + "noiseThreshold": "Gürültü Eşiği", + "openInViewer": "Görüntüleyicide Aç", + "seed": "Tohum", + "imageActions": "Görsel İşlemleri", + "sendTo": "Şuraya Aktar", + "sendToImg2Img": "Görselden Görsele Aktar", + "sendToUnifiedCanvas": "Tuvale Aktar", + "showOptionsPanel": "Yan Paneli Göster (O ya da T)", + "shuffle": "Kar", + "usePrompt": "İstemi Kullan", + "upscaleImage": "Görseli Büyüt", + "setToOptimalSizeTooSmall": "$t(parameters.setToOptimalSize) (çok küçük olabilir)", + "setToOptimalSizeTooLarge": "$t(parameters.setToOptimalSize) (çok büyük olabilir)", + "cfgRescaleMultiplier": "CFG Rescale Çarpanı", + "cfgRescale": "CFG Rescale", + "coherencePassHeader": "Uyum Geçişi", + "coherenceSteps": "Adım", + "infillMethod": "Doldurma Yöntemi", + "maskBlurMethod": "Bulandırma Yöntemi", + "steps": "Adım", + "upscaling": "Büyütülüyor", + "cpuNoise": "CPU Gürültüsü", + "useSeed": "Tohumu Kullan", + "vSymmetryStep": "Dikey Bakışım Adımı", + "compositingSettingsHeader": "Birleştirme Seçenekleri", + "infillScalingHeader": "Doldurma ve Ölçek", + "scheduler": "Planlayıcı", + "coherenceMode": "Kip", + "closeViewer": "Görüntüleyiciyi Kapat", + "restoreFaces": "Yüzleri Onar", + "useCpuNoise": "CPU Gürültüsü Kullan", + "negativePromptPlaceholder": "Olumsuz İstem", + "patchmatchDownScaleSize": "Küçült", + "perlinNoise": "Perlin Gürültüsü", + "scaledWidth": "Ölçekli En", + "seamHighThreshold": "Üst", + "seamlessTiling": "Dikişsiz Döşeme", + "seamlessXAxis": "Dikişsiz Döşeme X Ekseni", + "downloadImage": "Görseli İndir", + "seamlessX": "Dikişsiz X", + "noiseSettings": "Gürültü", + "type": "Tür" }, "modelManager": { - "baseModel": "Ana Model" + "baseModel": "Ana Model", + "addManually": "Kendin Ekle", + "addNew": "Yeni Ekle", + "active": "etkin", + "addSelected": "Seçilenleri Ekle", + "addNewModel": "Yeni Model Ekle", + "configFile": "Yapılandırma Dosyası", + "deleteConfig": "Yapılandırmayı Sil", + "availableModels": "Kullanılabilir Modeller", + "customConfigFileLocation": "Özel Yapılandırma Dosyası Konumu", + "advanced": "Gelişmiş", + "allModels": "Tüm Modeller", + "alpha": "Alfa", + "configValidationMsg": "Modelin yapılandırma dosyasının yeri.", + "cannotUseSpaces": "Boşluk Kullanılamaz", + "config": "Yapılandırma", + "pathToCustomConfig": "Özel Yapılandırma Konumu", + "customConfig": "Özel Yapılandırma", + "addModel": "Model Ekle", + "useCustomConfig": "Özel Yapılandırma Kullan", + "height": "Boy", + "modelsMergeFailed": "Model Birleştirme Başarısız", + "mergedModelName": "Birleşmiş Model Adı", + "mergedModelSaveLocation": "Saklama Yeri", + "mergeModels": "Modelleri Birleştir", + "modelDeleted": "Model Kaldırıldı", + "notLoaded": "yüklü değil", + "selectAndAdd": "Aşağıda Gösterilen Modellerden Seçin ve Ekleyin", + "v1": "v1", + "vaePrecision": "VAE Kesinliği", + "weightedSum": "Ağırlıklı Toplam", + "convertToDiffusersHelpText6": "Bu modeli dönüştürmek istiyor musunuz?", + "modelAdded": "Model Eklendi", + "modelThree": "3. Model", + "deleteMsg1": "Bu modeli InvokeAI'dan silmek istediğinize emin misiniz?", + "customSaveLocation": "Özel Saklama Konumu", + "formMessageDiffusersModelLocationDesc": "En az bir tane girin.", + "findModels": "Model Bul", + "importModels": "Modelleri İçe Aktar", + "inpainting": "v1 İçboyama", + "invokeRoot": "InvokeAI klasörü", + "modelSyncFailed": "Modeller Senkronize Edilemedi", + "modelOne": "1. Model", + "modelsFound": "Bulunan Modeller", + "modelsMerged": "Modeller Birleştirildi", + "noModelsFound": "Model Bulunamadı", + "quickAdd": "Hızlıca Ekle", + "settings": "Seçenekler", + "syncModelsDesc": "Modelleriniz arka uçla senkronize değilse bu tuşa basarak yenileyebilirsiniz. Uygulama başladıktan sonra kendiniz models.yaml dosyasını güncellediğinizde ya da InvokeAI ana klasörüne model eklediğinizde kullanışlıdır.", + "v2_768": "v2 (768pks)", + "vae": "VAE", + "width": "En", + "delete": "Sil", + "convert": "Dönüştür", + "formMessageDiffusersVAELocation": "VAE konumu", + "deselectAll": "Seçimi Kaldır", + "syncModels": "Modelleri Senkronize Et", + "variant": "Tür", + "widthValidationMsg": "Modelinizin varsayılan eni.", + "custom": "Özel", + "closeAdvanced": "Gelişmiş Seçenekleri Kapat", + "convertingModelBegin": "Model Dönüştürülüyor. Lütfen bekleyiniz.", + "none": "hiçbiri", + "pickModelType": "Model Türü Seçin", + "search": "Ara", + "formMessageDiffusersVAELocationDesc": "VAE konumu girilmezse InvokeAI yukarıda girilen model konumunu arayacaktır.", + "heightValidationMsg": "Modelinizin varsayılan boyu.", + "interpolationType": "Aradeğerleme Türü", + "mergedModelCustomSaveLocation": "Özel Konum", + "model": "Model", + "modelEntryDeleted": "Model Girdisi Kaldırıldı", + "modelMergeHeaderHelp1": "Üç ayrı modeli birleştirip isteğinize uygun yeni bir karışım yaratabilirsiniz.", + "modelTwo": "2. Model", + "modelType": "Model Türü", + "modelUpdated": "Model Güncellendi", + "modelUpdateFailed": "Model Güncellenemedi", + "name": "Ad", + "noCustomLocationProvided": "Özel Konum Belirtilmedi", + "noModels": "Model Yok", + "sameFolder": "Aynı klasör", + "scanAgain": "Yeniden Tara", + "selectAll": "Hepsini Seç", + "selected": "Seçili", + "convertToDiffusersHelpText5": "Lütfen yeterli depolama alanınız olduğundan emin olun. Modeller çoğunlukla 2-7 GB boyutundadır.", + "modelLocation": "Model Konumu", + "modelManager": "Model Yöneticisi", + "modelMergeInterpAddDifferenceHelp": "Bu kipte ilk olarak 3. model 2. modelden çıkarılır. Bu sonuç, yukarıda belirlenen alfa değerine göre 1. model ile birleştirilir.", + "updateModel": "Modeli Güncelle", + "vaeLocation": "VAE Konumu", + "convertToDiffusersHelpText4": "Bu işlem yalnızca bir kez yapılır, bilgisayarınızın özelliklerine bağlı olarak yaklaşık 30-60 saniye sürebilir.", + "convertToDiffusersSaveLocation": "Saklama Yeri", + "deleteModel": "Modeli Sil", + "deleteMsg2": "Model InvokeAI ana klasöründeyse bilgisayarınızdan silinir, bu klasör dışındaysa bilgisayarınızdan silinmeyecektir.", + "descriptionValidationMsg": "Modeliniz için bir tanım girin", + "invokeAIFolder": "Invoke AI Klasörü", + "load": "Yükle", + "modelDeleteFailed": "Model kaldırılamadı", + "loraModels": "LoRAlar", + "modelsSynced": "Modeller Senkronize Edildi", + "noModelSelected": "Model Seçilmedi", + "predictionType": "Saptama Türü", + "selectFolder": "Klasör Seç", + "selectModel": "Model Seç", + "showExisting": "Var Olanları Göster", + "v2_base": "v2 (512pks)", + "conversionNotSupported": "Dönüşüm Desteklenmiyor", + "cached": "önbellekte", + "modelConversionFailed": "Model Dönüşümü Başarısız", + "modelConverted": "Model Dönüştürüldü", + "modelExists": "Model Var", + "modelMergeAlphaHelp": "Alfa, karışım şiddetini belirler. Düşük alfa değerleri, ikinci modelin etkisini azaltır.", + "scanForModels": "Modelleri Tara", + "ignoreMismatch": "Seçilen Modeller Arasındaki Uyumsuzluğu Yoksay", + "merge": "Birleştir", + "description": "Tanım", + "nameValidationMsg": "Modelinize bir ad verin", + "statusConverting": "Dönüştürülüyor", + "vaeLocationValidationMsg": "VAE dosyası konumu." }, "dynamicPrompts": { - "loading": "Devimsel İstemler Oluşturuluyor...", - "combinatorial": "Birleşimsel Oluşturma" + "combinatorial": "Birleşimsel Oluşturma", + "promptsWithCount_one": "{{count}} İstem", + "promptsWithCount_other": "{{count}} İstem" }, "models": { - "incompatibleBaseModel": "Uyumsuz ana model" + "incompatibleBaseModel": "Uyumsuz ana model", + "selectLoRA": "LoRA Seçin", + "addLora": "LoRA Ekle", + "esrganModel": "ESRGAN Modeli", + "defaultVAE": "Varsayılan VAE", + "lora": "LoRA", + "noMainModelSelected": "Ana model seçilmedi", + "noLoRAsAvailable": "LoRA yok", + "noLoRAsLoaded": "LoRA Yok", + "noModelsAvailable": "Model yok", + "noMatchingLoRAs": "Uygun LoRA Yok", + "noMatchingModels": "Uygun Model Yok", + "allLoRAsAdded": "Tüm LoRAlar eklendi", + "loraAlreadyAdded": "LoRA yüklü", + "loading": "yükleniyor", + "selectModel": "Model Seçin", + "noLoRAsInstalled": "LoRA Yok" }, "settings": { "generation": "Oluşturma" + }, + "sdxl": { + "cfgScale": "CFG Ölçeği", + "loading": "Yükleniyor...", + "denoisingStrength": "Arındırma Ölçüsü", + "concatPromptStyle": "İstem ve Stili Bitiştir" } } diff --git a/invokeai/frontend/web/public/locales/uk.json b/invokeai/frontend/web/public/locales/uk.json index 92f6a88282..bbf2daf099 100644 --- a/invokeai/frontend/web/public/locales/uk.json +++ b/invokeai/frontend/web/public/locales/uk.json @@ -7,7 +7,6 @@ "img2img": "Зображення із зображення (img2img)", "unifiedCanvas": "Універсальне полотно", "nodes": "Вузли", - "langUkranian": "Украї́нська", "nodesDesc": "Система генерації зображень на основі нодів (вузлів) вже розробляється. Слідкуйте за новинами про цю чудову функцію.", "postProcessing": "Постобробка", "postProcessDesc1": "Invoke AI пропонує широкий спектр функцій постобробки. Збільшення зображення (upscale) та відновлення облич вже доступні в інтерфейсі. Отримайте доступ до них з меню 'Додаткові параметри' на вкладках 'Зображення із тексту' та 'Зображення із зображення'. Обробляйте зображення безпосередньо, використовуючи кнопки дій із зображеннями над поточним зображенням або в режимі перегляду.", @@ -48,24 +47,9 @@ "statusMergingModels": "Злиття моделей", "loading": "Завантаження", "loadingInvokeAI": "Завантаження Invoke AI", - "langHebrew": "Іврит", - "langKorean": "Корейська", - "langPortuguese": "Португальська", - "langArabic": "Арабська", - "langSimplifiedChinese": "Китайська (спрощена)", - "langSpanish": "Іспанська", - "langEnglish": "Англійська", - "langGerman": "Німецька", - "langItalian": "Італійська", - "langJapanese": "Японська", - "langPolish": "Польська", - "langBrPortuguese": "Португальська (Бразилія)", - "langRussian": "Російська", "githubLabel": "Github", "txt2img": "Текст в зображення (txt2img)", "discordLabel": "Discord", - "langDutch": "Голландська", - "langFrench": "Французька", "statusMergedModels": "Моделі об'єднані", "statusConvertingModel": "Конвертація моделі", "linear": "Лінійна обробка" diff --git a/invokeai/frontend/web/public/locales/zh_CN.json b/invokeai/frontend/web/public/locales/zh_CN.json index 746f859668..24bda1efa7 100644 --- a/invokeai/frontend/web/public/locales/zh_CN.json +++ b/invokeai/frontend/web/public/locales/zh_CN.json @@ -7,7 +7,6 @@ "img2img": "图生图", "unifiedCanvas": "统一画布", "nodes": "工作流编辑器", - "langSimplifiedChinese": "简体中文", "nodesDesc": "一个基于节点的图像生成系统目前正在开发中。请持续关注关于这一功能的更新。", "postProcessing": "后期处理", "postProcessDesc1": "Invoke AI 提供各种各样的后期处理功能。图像放大和面部修复在网页界面中已经可用。你可以从文生图和图生图页面的高级选项菜单中访问它们。你也可以直接使用图像显示上方或查看器中的图像操作按钮处理图像。", @@ -45,12 +44,9 @@ "dontAskMeAgain": "不要再次询问", "areYouSure": "你确认吗?", "imagePrompt": "图片提示词", - "langKorean": "朝鲜语", - "langPortuguese": "葡萄牙语", "random": "随机", "generate": "生成", "openInNewTab": "在新的标签页打开", - "langUkranian": "乌克兰语", "back": "返回", "statusMergedModels": "模型已合并", "statusConvertingModel": "转换模型中", @@ -58,18 +54,6 @@ "statusMergingModels": "合并模型", "githubLabel": "GitHub", "discordLabel": "Discord", - "langPolish": "波兰语", - "langBrPortuguese": "葡萄牙语(巴西)", - "langDutch": "荷兰语", - "langFrench": "法语", - "langRussian": "俄语", - "langGerman": "德语", - "langHebrew": "希伯来语", - "langItalian": "意大利语", - "langJapanese": "日语", - "langSpanish": "西班牙语", - "langEnglish": "英语", - "langArabic": "阿拉伯语", "txt2img": "文生图", "postprocessing": "后期处理", "loading": "加载中", @@ -81,7 +65,6 @@ "nodeEditor": "节点编辑器", "statusProcessing": "处理中", "imageFailedToLoad": "无法加载图像", - "lightMode": "浅色模式", "learnMore": "了解更多", "darkMode": "深色模式", "advanced": "高级", diff --git a/invokeai/frontend/web/public/locales/zh_Hant.json b/invokeai/frontend/web/public/locales/zh_Hant.json index fe51856117..b15333edf6 100644 --- a/invokeai/frontend/web/public/locales/zh_Hant.json +++ b/invokeai/frontend/web/public/locales/zh_Hant.json @@ -2,37 +2,22 @@ "common": { "nodes": "節點", "img2img": "圖片轉圖片", - "langSimplifiedChinese": "簡體中文", "statusError": "錯誤", "statusDisconnected": "已中斷連線", "statusConnected": "已連線", "back": "返回", "load": "載入", "close": "關閉", - "langEnglish": "英語", "settingsLabel": "設定", "upload": "上傳", - "langArabic": "阿拉伯語", "discordLabel": "Discord", "nodesDesc": "使用Node生成圖像的系統正在開發中。敬請期待有關於這項功能的更新。", "reportBugLabel": "回報錯誤", "githubLabel": "GitHub", - "langKorean": "韓語", - "langPortuguese": "葡萄牙語", "hotkeysLabel": "快捷鍵", "languagePickerLabel": "切換語言", - "langDutch": "荷蘭語", - "langFrench": "法語", - "langGerman": "德語", - "langItalian": "義大利語", - "langJapanese": "日語", - "langPolish": "波蘭語", - "langBrPortuguese": "巴西葡萄牙語", - "langRussian": "俄語", - "langSpanish": "西班牙語", "unifiedCanvas": "統一畫布", "cancel": "取消", - "langHebrew": "希伯來語", "txt2img": "文字轉圖片" }, "accessibility": { diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index 81088ffcbb..e25e1351eb 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -3,49 +3,28 @@ import { autoBatchEnhancer, combineReducers, configureStore } from '@reduxjs/too import { logger } from 'app/logging/logger'; import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver'; import { errorHandler } from 'app/store/enhancers/reduxRemember/errors'; -import { canvasPersistDenylist } from 'features/canvas/store/canvasPersistDenylist'; -import canvasReducer, { initialCanvasState, migrateCanvasState } from 'features/canvas/store/canvasSlice'; -import changeBoardModalReducer from 'features/changeBoardModal/store/slice'; -import { controlAdaptersPersistDenylist } from 'features/controlAdapters/store/controlAdaptersPersistDenylist'; -import controlAdaptersReducer, { - initialControlAdaptersState, - migrateControlAdaptersState, +import { canvasPersistConfig, canvasSlice } from 'features/canvas/store/canvasSlice'; +import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice'; +import { + controlAdaptersPersistConfig, + controlAdaptersSlice, } from 'features/controlAdapters/store/controlAdaptersSlice'; -import deleteImageModalReducer from 'features/deleteImageModal/store/slice'; -import { dynamicPromptsPersistDenylist } from 'features/dynamicPrompts/store/dynamicPromptsPersistDenylist'; -import dynamicPromptsReducer, { - initialDynamicPromptsState, - migrateDynamicPromptsState, -} from 'features/dynamicPrompts/store/dynamicPromptsSlice'; -import { galleryPersistDenylist } from 'features/gallery/store/galleryPersistDenylist'; -import galleryReducer, { initialGalleryState, migrateGalleryState } from 'features/gallery/store/gallerySlice'; -import hrfReducer, { initialHRFState, migrateHRFState } from 'features/hrf/store/hrfSlice'; -import loraReducer, { initialLoraState, migrateLoRAState } from 'features/lora/store/loraSlice'; -import modelmanagerReducer, { - initialModelManagerState, - migrateModelManagerState, -} from 'features/modelManager/store/modelManagerSlice'; -import { nodesPersistDenylist } from 'features/nodes/store/nodesPersistDenylist'; -import nodesReducer, { initialNodesState, migrateNodesState } from 'features/nodes/store/nodesSlice'; -import nodeTemplatesReducer from 'features/nodes/store/nodeTemplatesSlice'; -import workflowReducer, { initialWorkflowState, migrateWorkflowState } from 'features/nodes/store/workflowSlice'; -import { generationPersistDenylist } from 'features/parameters/store/generationPersistDenylist'; -import generationReducer, { - initialGenerationState, - migrateGenerationState, -} from 'features/parameters/store/generationSlice'; -import { postprocessingPersistDenylist } from 'features/parameters/store/postprocessingPersistDenylist'; -import postprocessingReducer, { - initialPostprocessingState, - migratePostprocessingState, -} from 'features/parameters/store/postprocessingSlice'; -import queueReducer from 'features/queue/store/queueSlice'; -import sdxlReducer, { initialSDXLState, migrateSDXLState } from 'features/sdxl/store/sdxlSlice'; -import configReducer from 'features/system/store/configSlice'; -import { systemPersistDenylist } from 'features/system/store/systemPersistDenylist'; -import systemReducer, { initialSystemState, migrateSystemState } from 'features/system/store/systemSlice'; -import { uiPersistDenylist } from 'features/ui/store/uiPersistDenylist'; -import uiReducer, { initialUIState, migrateUIState } from 'features/ui/store/uiSlice'; +import { deleteImageModalSlice } from 'features/deleteImageModal/store/slice'; +import { dynamicPromptsPersistConfig, dynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice'; +import { galleryPersistConfig, gallerySlice } from 'features/gallery/store/gallerySlice'; +import { hrfPersistConfig, hrfSlice } from 'features/hrf/store/hrfSlice'; +import { loraPersistConfig, loraSlice } from 'features/lora/store/loraSlice'; +import { modelManagerPersistConfig, modelManagerSlice } from 'features/modelManager/store/modelManagerSlice'; +import { nodesPersistConfig, nodesSlice } from 'features/nodes/store/nodesSlice'; +import { nodesTemplatesSlice } from 'features/nodes/store/nodeTemplatesSlice'; +import { workflowPersistConfig, workflowSlice } from 'features/nodes/store/workflowSlice'; +import { generationPersistConfig, generationSlice } from 'features/parameters/store/generationSlice'; +import { postprocessingPersistConfig, postprocessingSlice } from 'features/parameters/store/postprocessingSlice'; +import { queueSlice } from 'features/queue/store/queueSlice'; +import { sdxlPersistConfig, sdxlSlice } from 'features/sdxl/store/sdxlSlice'; +import { configSlice } from 'features/system/store/configSlice'; +import { systemPersistConfig, systemSlice } from 'features/system/store/systemSlice'; +import { uiPersistConfig, uiSlice } from 'features/ui/store/uiSlice'; import { diff } from 'jsondiffpatch'; import { defaultsDeep, keys, omit, pick } from 'lodash-es'; import dynamicMiddlewares from 'redux-dynamic-middlewares'; @@ -61,26 +40,27 @@ import { actionSanitizer } from './middleware/devtools/actionSanitizer'; import { actionsDenylist } from './middleware/devtools/actionsDenylist'; import { stateSanitizer } from './middleware/devtools/stateSanitizer'; import { listenerMiddleware } from './middleware/listenerMiddleware'; + const allReducers = { - canvas: canvasReducer, - gallery: galleryReducer, - generation: generationReducer, - nodes: nodesReducer, - nodeTemplates: nodeTemplatesReducer, - postprocessing: postprocessingReducer, - system: systemReducer, - config: configReducer, - ui: uiReducer, - controlAdapters: controlAdaptersReducer, - dynamicPrompts: dynamicPromptsReducer, - deleteImageModal: deleteImageModalReducer, - changeBoardModal: changeBoardModalReducer, - lora: loraReducer, - modelmanager: modelmanagerReducer, - sdxl: sdxlReducer, - queue: queueReducer, - workflow: workflowReducer, - hrf: hrfReducer, + [canvasSlice.name]: canvasSlice.reducer, + [gallerySlice.name]: gallerySlice.reducer, + [generationSlice.name]: generationSlice.reducer, + [nodesSlice.name]: nodesSlice.reducer, + [nodesTemplatesSlice.name]: nodesTemplatesSlice.reducer, + [postprocessingSlice.name]: postprocessingSlice.reducer, + [systemSlice.name]: systemSlice.reducer, + [configSlice.name]: configSlice.reducer, + [uiSlice.name]: uiSlice.reducer, + [controlAdaptersSlice.name]: controlAdaptersSlice.reducer, + [dynamicPromptsSlice.name]: dynamicPromptsSlice.reducer, + [deleteImageModalSlice.name]: deleteImageModalSlice.reducer, + [changeBoardModalSlice.name]: changeBoardModalSlice.reducer, + [loraSlice.name]: loraSlice.reducer, + [modelManagerSlice.name]: modelManagerSlice.reducer, + [sdxlSlice.name]: sdxlSlice.reducer, + [queueSlice.name]: queueSlice.reducer, + [workflowSlice.name]: workflowSlice.reducer, + [hrfSlice.name]: hrfSlice.reducer, [api.reducerPath]: api.reducer, }; @@ -88,75 +68,53 @@ const rootReducer = combineReducers(allReducers); const rememberedRootReducer = rememberReducer(rootReducer); -const rememberedKeys = [ - 'canvas', - 'gallery', - 'generation', - 'sdxl', - 'nodes', - 'workflow', - 'postprocessing', - 'system', - 'ui', - 'controlAdapters', - 'dynamicPrompts', - 'lora', - 'modelmanager', - 'hrf', -] satisfies (keyof typeof allReducers)[]; - -type SliceConfig = { - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - initialState: any; - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - migrate: (state: any) => any; +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +export type PersistConfig = { + /** + * The name of the slice. + */ + name: keyof typeof allReducers; + /** + * The initial state of the slice. + */ + initialState: T; + /** + * Migrate the state to the current version during rehydration. + * @param state The rehydrated state. + * @returns A correctly-shaped state. + */ + migrate: (state: unknown) => T; + /** + * Keys to omit from the persisted state. + */ + persistDenylist: (keyof T)[]; }; -const sliceConfigs: { - [key in (typeof rememberedKeys)[number]]: SliceConfig; -} = { - canvas: { initialState: initialCanvasState, migrate: migrateCanvasState }, - gallery: { initialState: initialGalleryState, migrate: migrateGalleryState }, - generation: { - initialState: initialGenerationState, - migrate: migrateGenerationState, - }, - nodes: { initialState: initialNodesState, migrate: migrateNodesState }, - postprocessing: { - initialState: initialPostprocessingState, - migrate: migratePostprocessingState, - }, - system: { initialState: initialSystemState, migrate: migrateSystemState }, - workflow: { - initialState: initialWorkflowState, - migrate: migrateWorkflowState, - }, - ui: { initialState: initialUIState, migrate: migrateUIState }, - controlAdapters: { - initialState: initialControlAdaptersState, - migrate: migrateControlAdaptersState, - }, - dynamicPrompts: { - initialState: initialDynamicPromptsState, - migrate: migrateDynamicPromptsState, - }, - sdxl: { initialState: initialSDXLState, migrate: migrateSDXLState }, - lora: { initialState: initialLoraState, migrate: migrateLoRAState }, - modelmanager: { - initialState: initialModelManagerState, - migrate: migrateModelManagerState, - }, - hrf: { initialState: initialHRFState, migrate: migrateHRFState }, +const persistConfigs: { [key in keyof typeof allReducers]?: PersistConfig } = { + [canvasPersistConfig.name]: canvasPersistConfig, + [galleryPersistConfig.name]: galleryPersistConfig, + [generationPersistConfig.name]: generationPersistConfig, + [nodesPersistConfig.name]: nodesPersistConfig, + [postprocessingPersistConfig.name]: postprocessingPersistConfig, + [systemPersistConfig.name]: systemPersistConfig, + [workflowPersistConfig.name]: workflowPersistConfig, + [uiPersistConfig.name]: uiPersistConfig, + [controlAdaptersPersistConfig.name]: controlAdaptersPersistConfig, + [dynamicPromptsPersistConfig.name]: dynamicPromptsPersistConfig, + [sdxlPersistConfig.name]: sdxlPersistConfig, + [loraPersistConfig.name]: loraPersistConfig, + [modelManagerPersistConfig.name]: modelManagerPersistConfig, + [hrfPersistConfig.name]: hrfPersistConfig, }; const unserialize: UnserializeFunction = (data, key) => { const log = logger('system'); - const config = sliceConfigs[key as keyof typeof sliceConfigs]; - if (!config) { - throw new Error(`No unserialize config for slice "${key}"`); + const persistConfig = persistConfigs[key as keyof typeof persistConfigs]; + if (!persistConfig) { + throw new Error(`No persist config for slice "${key}"`); } try { - const { initialState, migrate } = config; + const { initialState, migrate } = persistConfig; const parsed = JSON.parse(data); // strip out old keys const stripped = pick(parsed, keys(initialState)); @@ -176,26 +134,16 @@ const unserialize: UnserializeFunction = (data, key) => { return transformed; } catch (err) { log.warn({ error: serializeError(err) }, `Error rehydrating slice "${key}", falling back to default initial state`); - return config.initialState; + return persistConfig.initialState; } }; -const serializationDenylist: { - [key in (typeof rememberedKeys)[number]]?: string[]; -} = { - canvas: canvasPersistDenylist, - gallery: galleryPersistDenylist, - generation: generationPersistDenylist, - nodes: nodesPersistDenylist, - postprocessing: postprocessingPersistDenylist, - system: systemPersistDenylist, - ui: uiPersistDenylist, - controlAdapters: controlAdaptersPersistDenylist, - dynamicPrompts: dynamicPromptsPersistDenylist, -}; - export const serialize: SerializeFunction = (data, key) => { - const result = omit(data, serializationDenylist[key as keyof typeof serializationDenylist] ?? []); + const persistConfig = persistConfigs[key as keyof typeof persistConfigs]; + if (!persistConfig) { + throw new Error(`No persist config for slice "${key}"`); + } + const result = omit(data, persistConfig.persistDenylist); return JSON.stringify(result); }; @@ -215,7 +163,7 @@ export const createStore = (uniqueStoreKey?: string, persist = true) => const _enhancers = getDefaultEnhancers().concat(autoBatchEnhancer()); if (persist) { _enhancers.push( - rememberEnhancer(idbKeyValDriver, rememberedKeys, { + rememberEnhancer(idbKeyValDriver, keys(persistConfigs), { persistDebounce: 300, serialize, unserialize, diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts b/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts deleted file mode 100644 index 9394b685ca..0000000000 --- a/invokeai/frontend/web/src/features/canvas/store/canvasPersistDenylist.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { CanvasState } from './canvasTypes'; - -/** - * Canvas slice persist denylist - */ -export const canvasPersistDenylist: (keyof CanvasState)[] = []; diff --git a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts index a049c13419..cd734d3f00 100644 --- a/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts +++ b/invokeai/frontend/web/src/features/canvas/store/canvasSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { roundDownToMultiple, roundToMultiple } from 'common/util/roundDownToMultiple'; import calculateCoordinates from 'features/canvas/util/calculateCoordinates'; import calculateScale from 'features/canvas/util/calculateScale'; @@ -719,8 +719,6 @@ export const { scaledBoundingBoxDimensionsReset, } = canvasSlice.actions; -export default canvasSlice.reducer; - export const selectCanvasSlice = (state: RootState) => state.canvas; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -731,3 +729,10 @@ export const migrateCanvasState = (state: any): any => { } return state; }; + +export const canvasPersistConfig: PersistConfig = { + name: canvasSlice.name, + initialState: initialCanvasState, + migrate: migrateCanvasState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts b/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts index b585b2b697..4b374b066a 100644 --- a/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts +++ b/invokeai/frontend/web/src/features/changeBoardModal/store/slice.ts @@ -5,7 +5,7 @@ import type { ImageDTO } from 'services/api/types'; import { initialState } from './initialState'; -const changeBoardModal = createSlice({ +export const changeBoardModalSlice = createSlice({ name: 'changeBoardModal', initialState, reducers: { @@ -22,8 +22,6 @@ const changeBoardModal = createSlice({ }, }); -export const { isModalOpenChanged, imagesToChangeSelected, changeBoardReset } = changeBoardModal.actions; - -export default changeBoardModal.reducer; +export const { isModalOpenChanged, imagesToChangeSelected, changeBoardReset } = changeBoardModalSlice.actions; export const selectChangeBoardModalSlice = (state: RootState) => state.changeBoardModal; diff --git a/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersPersistDenylist.ts b/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersPersistDenylist.ts deleted file mode 100644 index ff8fcd1594..0000000000 --- a/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersPersistDenylist.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { ControlAdaptersState } from './types'; - -/** - * ControlNet slice persist denylist - */ -export const controlAdaptersPersistDenylist: (keyof ControlAdaptersState)[] = ['pendingControlImages']; diff --git a/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts b/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts index fd9a70ef57..49b07f16a1 100644 --- a/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts +++ b/invokeai/frontend/web/src/features/controlAdapters/store/controlAdaptersSlice.ts @@ -1,7 +1,7 @@ import type { PayloadAction, Update } from '@reduxjs/toolkit'; import { createEntityAdapter, createSlice, isAnyOf } from '@reduxjs/toolkit'; import { getSelectorsOptions } from 'app/store/createMemoizedSelector'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { buildControlAdapter } from 'features/controlAdapters/util/buildControlAdapter'; import type { ParameterControlNetModel, @@ -424,8 +424,6 @@ export const { controlAdapterModelCleared, } = controlAdaptersSlice.actions; -export default controlAdaptersSlice.reducer; - export const isAnyControlAdapterAdded = isAnyOf( controlAdapterAdded, controlAdapterAddedFromImage, @@ -441,3 +439,10 @@ export const migrateControlAdaptersState = (state: any): any => { } return state; }; + +export const controlAdaptersPersistConfig: PersistConfig = { + name: controlAdaptersSlice.name, + initialState: initialControlAdaptersState, + migrate: migrateControlAdaptersState, + persistDenylist: ['pendingControlImages'], +}; diff --git a/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts b/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts index 268602c350..9efb7c395f 100644 --- a/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts +++ b/invokeai/frontend/web/src/features/deleteImageModal/store/slice.ts @@ -5,7 +5,7 @@ import type { ImageDTO } from 'services/api/types'; import { initialDeleteImageState } from './initialState'; -const deleteImageModal = createSlice({ +export const deleteImageModalSlice = createSlice({ name: 'deleteImageModal', initialState: initialDeleteImageState, reducers: { @@ -22,8 +22,6 @@ const deleteImageModal = createSlice({ }, }); -export const { isModalOpenChanged, imagesToDeleteSelected, imageDeletionCanceled } = deleteImageModal.actions; - -export default deleteImageModal.reducer; +export const { isModalOpenChanged, imagesToDeleteSelected, imageDeletionCanceled } = deleteImageModalSlice.actions; export const selectDeleteImageModalSlice = (state: RootState) => state.deleteImageModal; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsPersistDenylist.ts b/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsPersistDenylist.ts deleted file mode 100644 index a411b0beec..0000000000 --- a/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsPersistDenylist.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { initialDynamicPromptsState } from './dynamicPromptsSlice'; - -export const dynamicPromptsPersistDenylist: (keyof typeof initialDynamicPromptsState)[] = ['prompts']; diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts b/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts index 450dbb9f23..7bb0b29659 100644 --- a/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts +++ b/invokeai/frontend/web/src/features/dynamicPrompts/store/dynamicPromptsSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { z } from 'zod'; export const zSeedBehaviour = z.enum(['PER_ITERATION', 'PER_PROMPT']); @@ -74,8 +74,6 @@ export const { seedBehaviourChanged, } = dynamicPromptsSlice.actions; -export default dynamicPromptsSlice.reducer; - export const selectDynamicPromptsSlice = (state: RootState) => state.dynamicPrompts; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -85,3 +83,10 @@ export const migrateDynamicPromptsState = (state: any): any => { } return state; }; + +export const dynamicPromptsPersistConfig: PersistConfig = { + name: dynamicPromptsSlice.name, + initialState: initialDynamicPromptsState, + migrate: migrateDynamicPromptsState, + persistDenylist: ['prompts'], +}; diff --git a/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts b/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts deleted file mode 100644 index e39ace1577..0000000000 --- a/invokeai/frontend/web/src/features/gallery/store/galleryPersistDenylist.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { initialGalleryState } from './gallerySlice'; - -/** - * Gallery slice persist denylist - */ -export const galleryPersistDenylist: (keyof typeof initialGalleryState)[] = [ - 'selection', - 'selectedBoardId', - 'galleryView', - 'offset', - 'limit', -]; diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 6adad0be70..f351d91339 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { uniqBy } from 'lodash-es'; import { boardsApi } from 'services/api/endpoints/boards'; import { imagesApi } from 'services/api/endpoints/images'; @@ -109,8 +109,6 @@ export const { moreImagesLoaded, } = gallerySlice.actions; -export default gallerySlice.reducer; - const isAnyBoardDeleted = isAnyOf( imagesApi.endpoints.deleteBoard.matchFulfilled, imagesApi.endpoints.deleteBoardAndImages.matchFulfilled @@ -125,3 +123,10 @@ export const migrateGalleryState = (state: any): any => { } return state; }; + +export const galleryPersistConfig: PersistConfig = { + name: gallerySlice.name, + initialState: initialGalleryState, + migrate: migrateGalleryState, + persistDenylist: ['selection', 'selectedBoardId', 'galleryView', 'offset', 'limit'], +}; diff --git a/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts b/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts index ac846ec58e..4c5769550b 100644 --- a/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts +++ b/invokeai/frontend/web/src/features/hrf/store/hrfSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import type { ParameterHRFMethod, ParameterStrength } from 'features/parameters/types/parameterSchemas'; export interface HRFState { @@ -37,8 +37,6 @@ export const hrfSlice = createSlice({ export const { setHrfEnabled, setHrfStrength, setHrfMethod } = hrfSlice.actions; -export default hrfSlice.reducer; - export const selectHrfSlice = (state: RootState) => state.hrf; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -48,3 +46,10 @@ export const migrateHRFState = (state: any): any => { } return state; }; + +export const hrfPersistConfig: PersistConfig = { + name: hrfSlice.name, + initialState: initialHRFState, + migrate: migrateHRFState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts index 7906443d7f..b3c63f2e0f 100644 --- a/invokeai/frontend/web/src/features/lora/store/loraSlice.ts +++ b/invokeai/frontend/web/src/features/lora/store/loraSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import type { ParameterLoRAModel } from 'features/parameters/types/parameterSchemas'; import type { LoRAModelConfigEntity } from 'services/api/endpoints/models'; @@ -81,8 +81,6 @@ export const { loraRecalled, } = loraSlice.actions; -export default loraSlice.reducer; - export const selectLoraSlice = (state: RootState) => state.lora; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -92,3 +90,10 @@ export const migrateLoRAState = (state: any): any => { } return state; }; + +export const loraPersistConfig: PersistConfig = { + name: loraSlice.name, + initialState: initialLoraState, + migrate: migrateLoRAState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts b/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts index 5da08d7f54..c450e64b3c 100644 --- a/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts +++ b/invokeai/frontend/web/src/features/modelManager/store/modelManagerSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; type ModelManagerState = { _version: 1; @@ -29,8 +29,6 @@ export const modelManagerSlice = createSlice({ export const { setSearchFolder, setAdvancedAddScanModel } = modelManagerSlice.actions; -export default modelManagerSlice.reducer; - export const selectModelManagerSlice = (state: RootState) => state.modelmanager; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -40,3 +38,10 @@ export const migrateModelManagerState = (state: any): any => { } return state; }; + +export const modelManagerPersistConfig: PersistConfig = { + name: modelManagerSlice.name, + initialState: initialModelManagerState, + migrate: migrateModelManagerState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx index 716c922939..147289772f 100644 --- a/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx +++ b/invokeai/frontend/web/src/features/nodes/components/flow/nodes/Invocation/fields/inputs/ColorFieldInputComponent.tsx @@ -1,31 +1,47 @@ import { useAppDispatch } from 'app/store/storeHooks'; import { fieldColorValueChanged } from 'features/nodes/store/nodesSlice'; import type { ColorFieldInputInstance, ColorFieldInputTemplate } from 'features/nodes/types/field'; -import { memo, useCallback } from 'react'; +import { memo, useCallback, useMemo } from 'react'; import type { RgbaColor } from 'react-colorful'; import { RgbaColorPicker } from 'react-colorful'; import type { FieldComponentProps } from './types'; +const FALLBACK_COLOR: RgbaColor = { r: 0, g: 0, b: 0, a: 255 }; + const ColorFieldInputComponent = (props: FieldComponentProps) => { const { nodeId, field } = props; const dispatch = useAppDispatch(); + const color = useMemo(() => { + // For better or worse, zColorFieldValue is typed as optional. This means that `field.value` and `fieldTemplate.default` + // can be undefined. Rather than changing the schema (which could have other consequences), we can just provide a fallback. + if (!field.value) { + return FALLBACK_COLOR; + } + const { r, g, b, a } = field.value; + // We need to divide by 255 to convert from 0-255 to 0-1, which is what the UI component needs + return { r, g, b, a: a / 255 }; + }, [field.value]); + const handleValueChanged = useCallback( (value: RgbaColor) => { + // We need to multiply by 255 to convert from 0-1 to 0-255, which is what the backend needs + const { r, g, b, a: _a } = value; + const a = Math.round(_a * 255); dispatch( fieldColorValueChanged({ nodeId, fieldName: field.name, - value, + value: { r, g, b, a }, }) ); }, [dispatch, field.name, nodeId] ); - return ; + return ; }; export default memo(ColorFieldInputComponent); diff --git a/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts index ac67b19e43..c211131aab 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodeTemplatesSlice.ts @@ -9,7 +9,7 @@ export const initialNodeTemplatesState: NodeTemplatesState = { templates: {}, }; -const nodesTemplatesSlice = createSlice({ +export const nodesTemplatesSlice = createSlice({ name: 'nodeTemplates', initialState: initialNodeTemplatesState, reducers: { @@ -21,6 +21,4 @@ const nodesTemplatesSlice = createSlice({ export const { nodeTemplatesBuilt } = nodesTemplatesSlice.actions; -export default nodesTemplatesSlice.reducer; - export const selectNodeTemplatesSlice = (state: RootState) => state.nodeTemplates; diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts b/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts deleted file mode 100644 index 5e3947314a..0000000000 --- a/invokeai/frontend/web/src/features/nodes/store/nodesPersistDenylist.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { NodesState } from './types'; - -/** - * Nodes slice persist denylist - */ -export const nodesPersistDenylist: (keyof NodesState)[] = [ - 'connectionStartParams', - 'connectionStartFieldType', - 'selectedNodes', - 'selectedEdges', - 'isReady', - 'nodesToCopy', - 'edgesToCopy', - 'connectionMade', - 'modifyingEdge', - 'addNewNodePosition', -]; diff --git a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts index a0074c1e47..63d2d32360 100644 --- a/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/nodesSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { workflowLoaded } from 'features/nodes/store/actions'; import { nodeTemplatesBuilt } from 'features/nodes/store/nodeTemplatesSlice'; import { SHARED_NODE_PROPERTIES } from 'features/nodes/types/constants'; @@ -139,7 +139,7 @@ const fieldValueReducer = ( input.value = result.data; }; -const nodesSlice = createSlice({ +export const nodesSlice = createSlice({ name: 'nodes', initialState: initialNodesState, reducers: { @@ -852,8 +852,6 @@ export const isAnyNodeOrEdgeMutation = isAnyOf( edgeAdded ); -export default nodesSlice.reducer; - export const selectNodesSlice = (state: RootState) => state.nodes; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -863,3 +861,21 @@ export const migrateNodesState = (state: any): any => { } return state; }; + +export const nodesPersistConfig: PersistConfig = { + name: nodesSlice.name, + initialState: initialNodesState, + migrate: migrateNodesState, + persistDenylist: [ + 'connectionStartParams', + 'connectionStartFieldType', + 'selectedNodes', + 'selectedEdges', + 'isReady', + 'nodesToCopy', + 'edgesToCopy', + 'connectionMade', + 'modifyingEdge', + 'addNewNodePosition', + ], +}; diff --git a/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts b/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts index f6ffa20f13..73802da54e 100644 --- a/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts +++ b/invokeai/frontend/web/src/features/nodes/store/workflowSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { workflowLoaded } from 'features/nodes/store/actions'; import { isAnyNodeOrEdgeMutation, nodeEditorReset, nodesDeleted } from 'features/nodes/store/nodesSlice'; import type { WorkflowsState as WorkflowState } from 'features/nodes/store/types'; @@ -27,7 +27,7 @@ export const initialWorkflowState: WorkflowState = { ...blankWorkflow, }; -const workflowSlice = createSlice({ +export const workflowSlice = createSlice({ name: 'workflow', initialState: initialWorkflowState, reducers: { @@ -119,8 +119,6 @@ export const { workflowSaved, } = workflowSlice.actions; -export default workflowSlice.reducer; - export const selectWorkflowSlice = (state: RootState) => state.workflow; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -130,3 +128,10 @@ export const migrateWorkflowState = (state: any): any => { } return state; }; + +export const workflowPersistConfig: PersistConfig = { + name: workflowSlice.name, + initialState: initialWorkflowState, + migrate: migrateWorkflowState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts b/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts index 3dc8c8f735..582aa6b217 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/buildNodesGraph.ts @@ -1,8 +1,7 @@ import type { NodesState } from 'features/nodes/store/types'; import type { FieldInputInstance } from 'features/nodes/types/field'; -import { isColorFieldInputInstance } from 'features/nodes/types/field'; import { isInvocationNode } from 'features/nodes/types/invocation'; -import { cloneDeep, omit, reduce } from 'lodash-es'; +import { omit, reduce } from 'lodash-es'; import type { Graph } from 'services/api/types'; import type { AnyInvocation } from 'services/events/types'; import { v4 as uuidv4 } from 'uuid'; @@ -11,21 +10,7 @@ import { v4 as uuidv4 } from 'uuid'; * We need to do special handling for some fields */ export const parseFieldValue = (field: FieldInputInstance) => { - if (isColorFieldInputInstance(field)) { - if (field.value) { - const clonedValue = cloneDeep(field.value); - - const { r, g, b, a } = field.value; - - // scale alpha value to PIL's desired range 0-255 - const scaledAlpha = Math.max(0, Math.min(a * 255, 255)); - const transformedColor = { r, g, b, a: scaledAlpha }; - - Object.assign(clonedValue, transformedColor); - return clonedValue; - } - } - + // Currently, no special handling is needed. return field.value; }; diff --git a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts b/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts index b2ada08368..3e8278ea6a 100644 --- a/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts +++ b/invokeai/frontend/web/src/features/nodes/util/schema/buildFieldInputTemplate.ts @@ -373,6 +373,8 @@ const buildEnumFieldInputTemplate: FieldInputTemplateBuilder state.generation; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -311,3 +309,10 @@ export const migrateGenerationState = (state: any): GenerationState => { } return state; }; + +export const generationPersistConfig: PersistConfig = { + name: generationSlice.name, + initialState: initialGenerationState, + migrate: migrateGenerationState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts b/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts deleted file mode 100644 index 014a2bbeaf..0000000000 --- a/invokeai/frontend/web/src/features/parameters/store/postprocessingPersistDenylist.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { PostprocessingState } from './postprocessingSlice'; - -/** - * Postprocessing slice persist denylist - */ -export const postprocessingPersistDenylist: (keyof PostprocessingState)[] = []; diff --git a/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts b/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts index bb74c988a0..e014f60570 100644 --- a/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts +++ b/invokeai/frontend/web/src/features/parameters/store/postprocessingSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { z } from 'zod'; export const zParamESRGANModelName = z.enum([ @@ -35,8 +35,6 @@ export const postprocessingSlice = createSlice({ export const { esrganModelNameChanged } = postprocessingSlice.actions; -export default postprocessingSlice.reducer; - export const selectPostprocessingSlice = (state: RootState) => state.postprocessing; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -46,3 +44,10 @@ export const migratePostprocessingState = (state: any): any => { } return state; }; + +export const postprocessingPersistConfig: PersistConfig = { + name: postprocessingSlice.name, + initialState: initialPostprocessingState, + migrate: migratePostprocessingState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/queue/store/queueSlice.ts b/invokeai/frontend/web/src/features/queue/store/queueSlice.ts index 43baa57bb2..340dff5104 100644 --- a/invokeai/frontend/web/src/features/queue/store/queueSlice.ts +++ b/invokeai/frontend/web/src/features/queue/store/queueSlice.ts @@ -53,6 +53,4 @@ export const { resumeProcessorOnEnqueueChanged, } = queueSlice.actions; -export default queueSlice.reducer; - export const selectQueueSlice = (state: RootState) => state.queue; diff --git a/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts b/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts index a8f4b45359..efa5f1c8bd 100644 --- a/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts +++ b/invokeai/frontend/web/src/features/sdxl/store/sdxlSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import type { ParameterNegativeStylePromptSDXL, ParameterPositiveStylePromptSDXL, @@ -36,7 +36,7 @@ export const initialSDXLState: SDXLState = { refinerStart: 0.8, }; -const sdxlSlice = createSlice({ +export const sdxlSlice = createSlice({ name: 'sdxl', initialState: initialSDXLState, reducers: { @@ -86,8 +86,6 @@ export const { setRefinerStart, } = sdxlSlice.actions; -export default sdxlSlice.reducer; - export const selectSdxlSlice = (state: RootState) => state.sdxl; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -97,3 +95,10 @@ export const migrateSDXLState = (state: any): any => { } return state; }; + +export const sdxlPersistConfig: PersistConfig = { + name: sdxlSlice.name, + initialState: initialSDXLState, + migrate: migrateSDXLState, + persistDenylist: [], +}; diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx index c43c147e6a..eceba85b5a 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsLanguageSelect.tsx @@ -3,41 +3,46 @@ import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui-library'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus'; import { languageChanged } from 'features/system/store/systemSlice'; +import type { Language } from 'features/system/store/types'; import { isLanguage } from 'features/system/store/types'; +import { map } from 'lodash-es'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +const optionsObject: Record = { + ar: 'العربية', + az: 'Azərbaycan dili', + de: 'Deutsch', + en: 'English', + es: 'Español', + fi: 'Suomi', + fr: 'Français', + he: 'עִבְֿרִית', + hu: 'Magyar Nyelv', + it: 'Italiano', + ja: '日本語', + ko: '한국어', + nl: 'Nederlands', + pl: 'Polski', + pt: 'Português', + pt_BR: 'Português do Brasil', + ru: 'Русский', + sv: 'Svenska', + tr: 'Türkçe', + ua: 'Украї́нська', + zh_CN: '简体中文', + zh_Hant: '漢語', +}; + +const options = map(optionsObject, (label, value) => ({ label, value })); + export const SettingsLanguageSelect = memo(() => { const { t } = useTranslation(); const dispatch = useAppDispatch(); const language = useAppSelector((s) => s.system.language); - const options = useMemo( - () => [ - { label: t('common.langArabic', { lng: 'ar' }), value: 'ar' }, - { label: t('common.langDutch', { lng: 'nl' }), value: 'nl' }, - { label: t('common.langEnglish', { lng: 'en' }), value: 'en' }, - { label: t('common.langFrench', { lng: 'fr' }), value: 'fr' }, - { label: t('common.langGerman', { lng: 'de' }), value: 'de' }, - { label: t('common.langHebrew', { lng: 'he' }), value: 'he' }, - { label: t('common.langItalian', { lng: 'it' }), value: 'it' }, - { label: t('common.langJapanese', { lng: 'ja' }), value: 'ja' }, - { label: t('common.langKorean', { lng: 'ko' }), value: 'ko' }, - { label: t('common.langPolish', { lng: 'pl' }), value: 'pl' }, - { label: t('common.langBrPortuguese', { lng: 'pt_BR' }), value: 'pt_BR' }, - { label: t('common.langPortuguese', { lng: 'pt' }), value: 'pt' }, - { label: t('common.langRussian', { lng: 'ru' }), value: 'ru' }, - { - label: t('common.langSimplifiedChinese', { lng: 'zh_CN' }), - value: 'zh_CN', - }, - { label: t('common.langSpanish', { lng: 'es' }), value: 'es' }, - { label: t('common.langUkranian', { lng: 'ua' }), value: 'ua' }, - ], - [t] - ); const isLocalizationEnabled = useFeatureStatus('localization').isFeatureEnabled; - const value = useMemo(() => options.find((o) => o.value === language), [language, options]); + const value = useMemo(() => options.find((o) => o.value === language), [language]); const onChange = useCallback( (v) => { diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index 1590ec762f..cc937170a3 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -81,9 +81,10 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { hasPendingItems, intermediatesCount, isLoading: isLoadingClearIntermediates, + refetchIntermediatesCount, } = useClearIntermediates(shouldShowClearIntermediates); - const { isOpen: isSettingsModalOpen, onOpen: onSettingsModalOpen, onClose: onSettingsModalClose } = useDisclosure(); + const { isOpen: isSettingsModalOpen, onOpen: _onSettingsModalOpen, onClose: onSettingsModalClose } = useDisclosure(); const { isOpen: isRefreshModalOpen, onOpen: onRefreshModalOpen, onClose: onRefreshModalClose } = useDisclosure(); @@ -99,6 +100,11 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { const clearStorage = useClearStorage(); + const handleOpenSettingsModel = useCallback(() => { + refetchIntermediatesCount(); + _onSettingsModalOpen(); + }, [_onSettingsModalOpen, refetchIntermediatesCount]); + const handleClickResetWebUI = useCallback(() => { clearStorage(); onSettingsModalClose(); @@ -171,7 +177,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { return ( <> {cloneElement(children, { - onClick: onSettingsModalOpen, + onClick: handleOpenSettingsModel, })} diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts b/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts index 6d953c3c11..96d3b0f7ee 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/useClearIntermediates.ts @@ -12,13 +12,14 @@ export type UseClearIntermediatesReturn = { clearIntermediates: () => void; isLoading: boolean; hasPendingItems: boolean; + refetchIntermediatesCount: () => void; }; export const useClearIntermediates = (shouldShowClearIntermediates: boolean): UseClearIntermediatesReturn => { const { t } = useTranslation(); const dispatch = useAppDispatch(); - const { data: intermediatesCount } = useGetIntermediatesCountQuery(undefined, { + const { data: intermediatesCount, refetch: refetchIntermediatesCount } = useGetIntermediatesCountQuery(undefined, { refetchOnMountOrArgChange: true, skip: !shouldShowClearIntermediates, }); @@ -58,5 +59,5 @@ export const useClearIntermediates = (shouldShowClearIntermediates: boolean): Us }); }, [t, _clearIntermediates, dispatch, hasPendingItems]); - return { intermediatesCount, clearIntermediates, isLoading, hasPendingItems }; + return { intermediatesCount, clearIntermediates, isLoading, hasPendingItems, refetchIntermediatesCount }; }; diff --git a/invokeai/frontend/web/src/features/system/store/configSlice.ts b/invokeai/frontend/web/src/features/system/store/configSlice.ts index 948634e41b..1cf62e89c8 100644 --- a/invokeai/frontend/web/src/features/system/store/configSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/configSlice.ts @@ -177,6 +177,4 @@ export const configSlice = createSlice({ export const { configChanged } = configSlice.actions; -export default configSlice.reducer; - export const selectConfigSlice = (state: RootState) => state.config; diff --git a/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts b/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts deleted file mode 100644 index 75da6f63a9..0000000000 --- a/invokeai/frontend/web/src/features/system/store/systemPersistDenylist.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { SystemState } from './types'; - -export const systemPersistDenylist: (keyof SystemState)[] = ['isConnected', 'denoiseProgress', 'status']; diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 4f31ca80f5..589397e951 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -1,7 +1,7 @@ import type { UseToastOptions } from '@invoke-ai/ui-library'; import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice, isAnyOf } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { calculateStepPercentage } from 'features/system/util/calculateStepPercentage'; import { makeToast } from 'features/system/util/makeToast'; import { t } from 'i18next'; @@ -194,8 +194,6 @@ export const { setShouldEnableInformationalPopovers, } = systemSlice.actions; -export default systemSlice.reducer; - const isAnyServerError = isAnyOf(socketInvocationError, socketSessionRetrievalError, socketInvocationRetrievalError); export const selectSystemSlice = (state: RootState) => state.system; @@ -207,3 +205,10 @@ export const migrateSystemState = (state: any): any => { } return state; }; + +export const systemPersistConfig: PersistConfig = { + name: systemSlice.name, + initialState: initialSystemState, + migrate: migrateSystemState, + persistDenylist: ['isConnected', 'denoiseProgress', 'status'], +}; diff --git a/invokeai/frontend/web/src/features/system/store/types.ts b/invokeai/frontend/web/src/features/system/store/types.ts index 09d5fdcd6f..3575501d86 100644 --- a/invokeai/frontend/web/src/features/system/store/types.ts +++ b/invokeai/frontend/web/src/features/system/store/types.ts @@ -17,21 +17,27 @@ export type DenoiseProgress = { export const zLanguage = z.enum([ 'ar', - 'nl', - 'en', - 'fr', + 'az', 'de', + 'en', + 'es', + 'fi', + 'fr', 'he', + 'hu', 'it', 'ja', 'ko', + 'nl', 'pl', - 'pt_BR', 'pt', + 'pt_BR', 'ru', + 'sv', + 'tr', + 'ua', 'zh_CN', - 'es', - 'uk', + 'zh_Hant', ]); export type Language = z.infer; export const isLanguage = (v: unknown): v is Language => zLanguage.safeParse(v).success; diff --git a/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts b/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts deleted file mode 100644 index 815f8dfca5..0000000000 --- a/invokeai/frontend/web/src/features/ui/store/uiPersistDenylist.ts +++ /dev/null @@ -1,6 +0,0 @@ -import type { UIState } from './uiTypes'; - -/** - * UI slice persist denylist - */ -export const uiPersistDenylist: (keyof UIState)[] = ['shouldShowImageDetails']; diff --git a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts index acca0ad67b..32eb26673a 100644 --- a/invokeai/frontend/web/src/features/ui/store/uiSlice.ts +++ b/invokeai/frontend/web/src/features/ui/store/uiSlice.ts @@ -1,6 +1,6 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from 'app/store/store'; +import type { PersistConfig, RootState } from 'app/store/store'; import { initialImageChanged } from 'features/parameters/store/generationSlice'; import type { InvokeTabName } from './tabMap'; @@ -57,8 +57,6 @@ export const { expanderStateChanged, } = uiSlice.actions; -export default uiSlice.reducer; - export const selectUiSlice = (state: RootState) => state.ui; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -68,3 +66,10 @@ export const migrateUIState = (state: any): any => { } return state; }; + +export const uiPersistConfig: PersistConfig = { + name: uiSlice.name, + initialState: initialUIState, + migrate: migrateUIState, + persistDenylist: ['shouldShowImageDetails'], +}; diff --git a/pyproject.toml b/pyproject.toml index ee55720283..b2505ffe97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "clip_anytorch==2.5.2", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip", "compel==2.0.2", "controlnet-aux==0.0.7", - "diffusers[torch]==0.26.0", + "diffusers[torch]==0.26.1", "invisible-watermark==0.2.0", # needed to install SDXL base and refiner using their repo_ids "mediapipe==0.10.7", # needed for "mediapipeface" controlnet model "numpy==1.26.3", # >1.24.0 is needed to use the 'strict' argument to np.testing.assert_array_equal() @@ -57,7 +57,7 @@ dependencies = [ # Core application dependencies, pinned for reproducible builds. "fastapi-events==0.10.0", "fastapi==0.108.0", - "huggingface-hub==0.20.2", + "huggingface-hub==0.20.3", "pydantic-settings==2.1.0", "pydantic==2.5.3", "python-socketio==5.11.0", diff --git a/tests/test_item_storage_memory.py b/tests/test_item_storage_memory.py index cfd1ffb834..15758094d7 100644 --- a/tests/test_item_storage_memory.py +++ b/tests/test_item_storage_memory.py @@ -3,6 +3,7 @@ import re import pytest from pydantic import BaseModel +from invokeai.app.services.item_storage.item_storage_common import ItemNotFoundError from invokeai.app.services.item_storage.item_storage_memory import ItemStorageMemory @@ -17,19 +18,19 @@ def item_storage_memory(): def test_item_storage_memory_initializes(): - item_storage_memory = ItemStorageMemory() + item_storage_memory = ItemStorageMemory[MockItemModel]() assert item_storage_memory._items == {} assert item_storage_memory._id_field == "id" assert item_storage_memory._max_items == 10 - item_storage_memory = ItemStorageMemory(id_field="bananas", max_items=20) + item_storage_memory = ItemStorageMemory[MockItemModel](id_field="bananas", max_items=20) assert item_storage_memory._id_field == "bananas" assert item_storage_memory._max_items == 20 with pytest.raises(ValueError, match=re.escape("max_items must be at least 1")): - item_storage_memory = ItemStorageMemory(max_items=0) + item_storage_memory = ItemStorageMemory[MockItemModel](max_items=0) with pytest.raises(ValueError, match=re.escape("id_field must not be empty")): - item_storage_memory = ItemStorageMemory(id_field="") + item_storage_memory = ItemStorageMemory[MockItemModel](id_field="") def test_item_storage_memory_sets(item_storage_memory: ItemStorageMemory[MockItemModel]): @@ -58,8 +59,8 @@ def test_item_storage_memory_gets(item_storage_memory: ItemStorageMemory[MockIte item = item_storage_memory.get("2") assert item == item_2 - item = item_storage_memory.get("3") - assert item is None + with pytest.raises(ItemNotFoundError, match=re.escape("Item with id 3 not found")): + item_storage_memory.get("3") def test_item_storage_memory_deletes(item_storage_memory: ItemStorageMemory[MockItemModel]): @@ -73,7 +74,7 @@ def test_item_storage_memory_deletes(item_storage_memory: ItemStorageMemory[Mock def test_item_storage_memory_respects_max(): - item_storage_memory = ItemStorageMemory(max_items=3) + item_storage_memory = ItemStorageMemory[MockItemModel](max_items=3) for i in range(10): item_storage_memory.set(MockItemModel(id=str(i), value=i)) assert item_storage_memory._items == {