From 3dc62a88574f0f40a0400368fc934a7b4574c995 Mon Sep 17 00:00:00 2001 From: JeLuF <jf@mormo.org> Date: Thu, 22 Jun 2023 23:48:55 +0200 Subject: [PATCH 1/5] Basic embeddings support --- ui/easydiffusion/model_manager.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/easydiffusion/model_manager.py b/ui/easydiffusion/model_manager.py index de2c10ac..a9b42594 100644 --- a/ui/easydiffusion/model_manager.py +++ b/ui/easydiffusion/model_manager.py @@ -27,6 +27,7 @@ MODEL_EXTENSIONS = { "realesrgan": [".pth"], "lora": [".ckpt", ".safetensors"], "codeformer": [".pth"], + "embeddings": [".pt", ".bin", ".safetensors"], } DEFAULT_MODELS = { "stable-diffusion": [ @@ -58,6 +59,9 @@ def init(): def load_default_models(context: Context): set_vram_optimizations(context) + config = app.getConfig() + context.embeddings_path = os.path.join(app.MODELS_DIR, "embeddings") + # init default model paths for model_type in MODELS_TO_LOAD_ON_START: context.model_paths[model_type] = resolve_model_to_use(model_type=model_type, fail_if_not_found=False) @@ -318,6 +322,7 @@ def getModels(): "hypernetwork": [], "lora": [], "codeformer": ["codeformer"], + "embeddings": [], }, } @@ -374,6 +379,7 @@ def getModels(): listModels(model_type="hypernetwork") listModels(model_type="gfpgan") listModels(model_type="lora") + listModels(model_type="embeddings") if models_scanned > 0: log.info(f"[green]Scanned {models_scanned} models. Nothing infected[/]") From 75c57f646d7d70640232bc028fccb277c1cc915c Mon Sep 17 00:00:00 2001 From: JeLuF <jf@mormo.org> Date: Fri, 30 Jun 2023 08:41:15 +0200 Subject: [PATCH 2/5] embedding support popup --- ui/index.html | 23 ++++++++++++++ ui/media/css/main.css | 27 ++++++++++++++++ ui/media/js/main.js | 71 +++++++++++++++++++++++++++++++++++++++++++ ui/media/js/utils.js | 16 ++++++++++ 4 files changed, 137 insertions(+) diff --git a/ui/index.html b/ui/index.html index 9565c726..89f62944 100644 --- a/ui/index.html +++ b/ui/index.html @@ -244,6 +244,9 @@ <td><label for="hypernetwork_strength_slider">Hypernetwork Strength:</label></td> <td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td> </tr> + <tr class="pl-5"><td><label for="embeddings-button">Embedding:</label></td><td> + <button id="embeddings-button" class="tertiaryButton">Add embedding to prompt</button> + </td></tr> <tr id="tiling_container" class="pl-5"><td><label for="tiling">Seamless Tiling:</label></td><td> <select id="tiling" name="tiling"> <option value="none" selected>None</option> @@ -556,6 +559,26 @@ </div> </dialog> + <dialog id="embeddings-dialog"> + <div id="embeddings-dialog-header" class="dialog-header"> + <div id="embeddings-dialog-header-left" class="dialog-header-left"> + <h4>Embeddings</h4> + <span>Add embeddings to the prompt (click) or negative prompt (shift-click)</span> + </div> + <div id="embeddings-dialog-header-right"> + <i id="embeddings-dialog-close-button" class="fa-solid fa-xmark fa-lg"></i> + </div> + </div> + <div> + <i class="fa-solid fa-magnifying-glass"></i> + <input id="embeddings-search-box" type="text" spellcheck="false" autocomplete="off"> + <span style="float:right;"><label>Mode:</label> <select id="embeddings-mode"><option value="insert">Insert at cursor position</option><option value="append">Append at the end</option></select> + </div> + <div id="embeddings-list"> + </div> + </div> + </dialog> + <div id="image-editor" class="popup image-editor-popup"> <div> <i class="close-button fa-solid fa-xmark"></i> diff --git a/ui/media/css/main.css b/ui/media/css/main.css index 922aabff..3104fd2d 100644 --- a/ui/media/css/main.css +++ b/ui/media/css/main.css @@ -1631,3 +1631,30 @@ body.wait-pause { bottom: 0; } } + +#embeddings-button { + background-color: var(--background-color3); +} + +#embeddings-button:hover { + background-color: var(--button-hover-background); +} + +#embeddings-dialog { + overflow: clip; +} + +#embeddings-list { + height: 70vH; + width: 40vW; + overflow-y: scroll; +} + +#embeddings-list button { + margin-top: 2px; + margin-bottom: 2px; +} + +#embeddings-list::-webkit-scrollbar-thumb { + background: var(--background-color3); +} diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 20299b10..48080281 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -116,6 +116,12 @@ let streamImageProgressField = document.querySelector("#stream_image_progress") let thumbnailSizeField = document.querySelector("#thumbnail_size-input") let autoscrollBtn = document.querySelector("#auto_scroll_btn") let autoScroll = document.querySelector("#auto_scroll") +let embeddingsButton = document.querySelector("#embeddings-button") +let embeddingsDialog = document.querySelector("#embeddings-dialog") +let embeddingsDialogCloseBtn = embeddingsDialog.querySelector("#embeddings-dialog-close-button") +let embeddingsSearchBox = document.querySelector("#embeddings-search-box") +let embeddingsList = document.querySelector("#embeddings-list") +let embeddingsModeField = document.querySelector("#embeddings-mode") let makeImageBtn = document.querySelector("#makeImage") let stopImageBtn = document.querySelector("#stopImage") @@ -2129,6 +2135,71 @@ document.getElementById("toggle-cloudflare-tunnel").addEventListener("click", as console.log(`Cloudflare tunnel ${command} result:`, res) }) +/* Embeddings */ + +function updateEmbeddingsList(filter="") { + function html(model, prefix="", filter="") { + filter = filter.toLowerCase() + let toplevel="" + let folders="" + + model?.forEach( m => { + if (typeof(m) == "string") { + if (m.toLowerCase().search(filter)!=-1) { + toplevel += `<button data-embedding="${m}">${m}</button> ` + } + } else { + let subdir = html(m[1], prefix+m[0]+"/", filter) + if (subdir != "") { + folders += `<h4>${prefix}${m[0]}</h4>` + subdir + } + } + }) + return toplevel + folders + } + + function onButtonClick(e) { + let text = e.target.dataset["embedding"] + console.log(e.shiftKey, text) + + if (embeddingsModeField.value == "insert") { + if (e.shiftKey) { + insertAtCursor(negativePromptField, text) + } else { + insertAtCursor(promptField, text) + } + } else { + let pad="" + if (e.shiftKey) { + if (!negativePromptField.value.endsWith(" ")) { + pad = " " + } + negativePromptField += pad + text + } else { + if (!promptField.value.endsWith(" ")) { + pad = " " + } + promptField += pad + text + } + } + } + + embeddingsList.innerHTML = html(modelsOptions.embeddings, "", filter) + embeddingsList.querySelectorAll("button").forEach( (b) => { b.addEventListener("click", onButtonClick)}) +} + +embeddingsButton.addEventListener("click", () => { + updateEmbeddingsList() + embeddingsSearchBox.value="" + embeddingsDialog.showModal() +}) +embeddingsDialogCloseBtn.addEventListener("click", (e) => { + embeddingsDialog.close() +}) +embeddingsSearchBox.addEventListener("input", (e) => { + updateEmbeddingsList(embeddingsSearchBox.value) +}) + /* Pause function */ document.querySelectorAll(".tab").forEach(linkTabContents) diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js index 4c65bd00..cabbc9eb 100644 --- a/ui/media/js/utils.js +++ b/ui/media/js/utils.js @@ -997,6 +997,22 @@ async function getStorageData(key) { }); } +function insertAtCursor(field, text) { + if (field.selectionStart || field.selectionStart == "0") { + var startPos = field.selectionStart + var endPos = field.selectionEnd + var before = field.value.substring(0, startPos) + var after = field.value.substring(endPos, field.value.length) + + if (!before.endsWith(" ")) { before += " " } + if (!after.startsWith(" ")) { after = " "+after } + + field.value = before + text + after + } else { + field.value += text + } +} + // indexedDB debug functions async function getAllKeys() { return openDB().then(db => { From fa6716345d56464bcc809136e57785dd1082ee18 Mon Sep 17 00:00:00 2001 From: JeLuF <jf@mormo.org> Date: Fri, 30 Jun 2023 20:17:46 +0200 Subject: [PATCH 3/5] hide embeddings UI if not test_diffusers --- ui/index.html | 2 +- ui/media/js/main.js | 4 ++++ ui/media/js/parameters.js | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index 5b6c5300..3ae54e0e 100644 --- a/ui/index.html +++ b/ui/index.html @@ -244,7 +244,7 @@ <td><label for="hypernetwork_strength_slider">Hypernetwork Strength:</label></td> <td> <input id="hypernetwork_strength_slider" name="hypernetwork_strength_slider" class="editor-slider" value="100" type="range" min="0" max="100"> <input id="hypernetwork_strength" name="hypernetwork_strength" size="4" pattern="^[0-9\.]+$" onkeypress="preventNonNumericalInput(event)"><br/></td> </tr> - <tr class="pl-5"><td><label for="embeddings-button">Embedding:</label></td><td> + <tr id="embeddings-container" class="pl-5 displayNone"><td><label for="embeddings-button">Embedding:</label></td><td> <button id="embeddings-button" class="tertiaryButton">Add embedding to prompt</button> </td></tr> <tr id="tiling_container" class="pl-5"><td><label for="tiling">Seamless Tiling:</label></td><td> diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 48080281..c5f5bc52 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2200,6 +2200,10 @@ embeddingsSearchBox.addEventListener("input", (e) => { updateEmbeddingsList(embeddingsSearchBox.value) }) +if (testDiffusers.checked) { + document.getElementById("embeddings-container").classList.remove("displayNone") +} + /* Pause function */ document.querySelectorAll(".tab").forEach(linkTabContents) diff --git a/ui/media/js/parameters.js b/ui/media/js/parameters.js index cd86be63..cfe4f3a8 100644 --- a/ui/media/js/parameters.js +++ b/ui/media/js/parameters.js @@ -441,6 +441,7 @@ async function getAppConfig() { option.disabled = true }) document.querySelector("#clip_skip_config").classList.remove("displayNone") + document.querySelector("#embeddings-container").classList.remove("displayNone") } console.log("get config status response", config) From 0d9d01c9f5efd6335b8d4c63d518ed064b4de5e3 Mon Sep 17 00:00:00 2001 From: JeLuF <jf@mormo.org> Date: Fri, 30 Jun 2023 23:28:24 +0200 Subject: [PATCH 4/5] backdrop and drag handlers --- ui/index.html | 2 +- ui/media/js/main.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/index.html b/ui/index.html index ced88a08..979a7de9 100644 --- a/ui/index.html +++ b/ui/index.html @@ -577,7 +577,7 @@ </div> <div> <i class="fa-solid fa-magnifying-glass"></i> - <input id="embeddings-search-box" type="text" spellcheck="false" autocomplete="off"> + <input id="embeddings-search-box" type="text" spellcheck="false" autocomplete="off" placeholder="Search..."> <span style="float:right;"><label>Mode:</label> <select id="embeddings-mode"><option value="insert">Insert at cursor position</option><option value="append">Append at the end</option></select> </div> <div id="embeddings-list"> diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 0cf6c075..9798c0e9 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2202,6 +2202,10 @@ embeddingsSearchBox.addEventListener("input", (e) => { updateEmbeddingsList(embeddingsSearchBox.value) }) +modalDialogCloseOnBackdropClick(embeddingsDialog) +makeDialogDraggable(embeddingsDialog) + + if (testDiffusers.checked) { document.getElementById("embeddings-container").classList.remove("displayNone") } From 21946ff824192a9c83edf92a7c7ab79e7af90f2d Mon Sep 17 00:00:00 2001 From: JeLuF <jf@mormo.org> Date: Fri, 7 Jul 2023 22:49:21 +0200 Subject: [PATCH 5/5] Update ui/media/js/main.js Co-authored-by: rbertus2000 <91765399+rbertus2000@users.noreply.github.com> --- ui/media/js/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/media/js/main.js b/ui/media/js/main.js index 9798c0e9..eee65cb8 100644 --- a/ui/media/js/main.js +++ b/ui/media/js/main.js @@ -2176,12 +2176,12 @@ function updateEmbeddingsList(filter="") { if (!negativePromptField.value.endsWith(" ")) { pad = " " } - negativePromptField += pad + text + negativePromptField.value += pad + text } else { if (!promptField.value.endsWith(" ")) { pad = " " } - promptField += pad + text + promptField.value += pad + text } } }