diff --git a/ui/easydiffusion/bucket_manager.py b/ui/easydiffusion/bucket_manager.py
index f587bc7f..60a4ed6c 100644
--- a/ui/easydiffusion/bucket_manager.py
+++ b/ui/easydiffusion/bucket_manager.py
@@ -69,9 +69,8 @@ def init():
bucket = crud.get_bucket_by_path(db, path)
if bucket == None:
- bucket_id = crud.create_bucket(db=db, bucket=schemas.BucketCreate(path=path))
- else:
- bucket_id = bucket.id
+ bucket = crud.create_bucket(db=db, bucket=schemas.BucketCreate(path=path))
+ bucket_id = bucket.id
bucketfile = schemas.BucketFileCreate(filename=filename, data=file)
result = crud.create_bucketfile(db=db, bucketfile=bucketfile, bucket_id=bucket_id)
diff --git a/ui/index.html b/ui/index.html
index faee0ff2..f69efd0b 100644
--- a/ui/index.html
+++ b/ui/index.html
@@ -704,13 +704,13 @@
-
Use the thumbnail for …
-
-
-
+
+
diff --git a/ui/media/css/main.css b/ui/media/css/main.css
index be3e92f0..4d68b9db 100644
--- a/ui/media/css/main.css
+++ b/ui/media/css/main.css
@@ -1650,6 +1650,35 @@ body.wait-pause {
}
}
+.spinner-container {
+ width: 80px;
+ height: 100px;
+ margin: 100px auto;
+ margin-top: 30vH;
+}
+
+.spinner-block {
+ position: relative;
+ box-sizing: border-box;
+ float: left;
+ margin: 0 10px 10px 0;
+ width: 12px;
+ height: 12px;
+ border-radius: 3px;
+ background: var(--accent-color);
+}
+
+.spinner-block:nth-child(4n+1) { animation: spinner-wave 2s ease .0s infinite; }
+.spinner-block:nth-child(4n+2) { animation: spinner-wave 2s ease .2s infinite; }
+.spinner-block:nth-child(4n+3) { animation: spinner-wave 2s ease .4s infinite; }
+.spinner-block:nth-child(4n+4) { animation: spinner-wave 2s ease .6s infinite; margin-right: 0; }
+
+@keyframes spinner-wave {
+ 0% { top: 0; opacity: 1; }
+ 50% { top: 30px; opacity: .2; }
+ 100% { top: 0; opacity: 1; }
+}
+
#embeddings-dialog {
overflow: clip;
}
@@ -1748,7 +1777,7 @@ body.wait-pause {
grid-auto-flow: row;
grid-template-areas:
"uat-preview uat-select"
- "uat-buttons uat-buttons";
+ "uat-preview uat-buttons";
}
.use-as-thumb-preview {
@@ -1857,4 +1886,4 @@ div#enlarge-buttons {
/* hack for fixing Image Modifier Improvements plugin */
#imageTagPopupContainer {
position: absolute;
-}
\ No newline at end of file
+}
diff --git a/ui/media/js/main.js b/ui/media/js/main.js
index f340a79c..30ed6012 100644
--- a/ui/media/js/main.js
+++ b/ui/media/js/main.js
@@ -171,8 +171,11 @@ let saveAllJSONToggle = document.querySelector("#json_toggle")
let saveAllFoldersOption = document.querySelector("#download-add-folders")
let splashScreenPopup = document.querySelector("#splash-screen")
let useAsThumbDialog = document.querySelector("#use-as-thumb-dialog")
+let useAsThumbDialogCloseBtn = document.querySelector("#use-as-thumb-dialog-close-button")
let useAsThumbImageContainer = document.querySelector("#use-as-thumb-img-container")
let useAsThumbSelect = document.querySelector("#use-as-thumb-select")
+let useAsThumbSaveBtn = document.querySelector("#use-as-thumb-save")
+let useAsThumbCancelBtn = document.querySelector("#use-as-thumb-cancel")
let maskSetting = document.querySelector("#enable_mask")
@@ -703,8 +706,8 @@ function onUseAsThumbnailClick(req, img) {
let scale = 1
let targetWidth = img.naturalWidth
let targetHeight = img.naturalHeight
-
let resize = false
+ onUseAsThumbnailClick.img = img
if ( typeof(onUseAsThumbnailClick.croppr) == 'undefined' ) {
onUseAsThumbnailClick.croppr = new Croppr("#use-as-thumb-image", { aspectRatio: 1, minSize: [384,384,'px'], startSize: [512, 512, 'px'], returnMode:"real" })
@@ -725,6 +728,10 @@ function onUseAsThumbnailClick(req, img) {
resize = true
}
}
+
+ onUseAsThumbnailClick.croppr.options.minSize = {width: 384*scale>>>0, height: 384*scale>>>0}
+ onUseAsThumbnailClick.croppr.options.startSize = {width: 512*scale>>>0, height: 512*scale>>>0}
+
if (resize) {
const canvas = document.createElement('canvas')
canvas.width = targetWidth
@@ -732,9 +739,6 @@ function onUseAsThumbnailClick(req, img) {
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, targetWidth, targetHeight)
- onUseAsThumbnailClick.croppr.options.minSize = {width: 384*scale>>>0, height: 384*scale>>>0}
- onUseAsThumbnailClick.croppr.options.startSize = {width: 512*scale>>>0, height: 512*scale>>>0}
-
onUseAsThumbnailClick.croppr.setImage(canvas.toDataURL('image/png'))
} else {
onUseAsThumbnailClick.croppr.setImage(img.src)
@@ -759,21 +763,51 @@ function onUseAsThumbnailClick(req, img) {
useAsThumbSelect.replaceChildren(optgroup)
useAsThumbDialog.showModal()
+ onUseAsThumbnailClick.scale = scale
-
-// fetch(img.src)
-// .then(response => response.blob())
-// .then(async function(blob) {
-// const formData = new FormData()
-// formData.append("file", blob)
-// const response = await fetch(`bucket/embeddings/${embedding}.jpg`, { method: 'POST', body: formData });
-// console.log(response)
-// })
}
modalDialogCloseOnBackdropClick(useAsThumbDialog)
makeDialogDraggable(useAsThumbDialog)
+useAsThumbDialogCloseBtn.addEventListener("click", () => {
+ useAsThumbDialog.close()
+})
+
+useAsThumbCancelBtn.addEventListener("click", () => {
+ useAsThumbDialog.close()
+})
+
+useAsThumbSaveBtn.addEventListener("click", (e) => {
+ let scale = 1/onUseAsThumbnailClick.scale
+ let crop = onUseAsThumbnailClick.croppr.getValue()
+
+ let len = Math.max(crop.width*scale, 384)
+ let profileName = profileNameField.value
+
+ cropImageDataUrl(onUseAsThumbnailClick.img.src, crop.x*scale, crop.y*scale, len, len)
+ .then(thumb => fetch(thumb))
+ .then(response => response.blob())
+ .then(async function(blob) {
+ const formData = new FormData()
+ formData.append("file", blob)
+ let options = useAsThumbSelect.selectedOptions
+ let promises = []
+ console.log(options)
+ for (let embedding of options) {
+ console.log(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`)
+ promises.push(fetch(`bucket/${profileName}/${embedding.dataset["type"]}/${embedding.value}.png`, { method: 'POST', body: formData }))
+ }
+ return Promise.all(promises)
+ }).then(() => {
+ useAsThumbDialog.close()
+ })
+ .catch(error => {
+ console.error(error)
+ showToast("Couldn't save thumbnail.
"+error)
+ })
+})
+
function enqueueImageVariationTask(req, img, reqDiff) {
const imageSeed = img.getAttribute("data-seed")
@@ -2672,6 +2706,16 @@ function updateEmbeddingsList(filter = "") {
}
}
+ // Usually the rendering of the Embeddings HTML takes less than a second. In case it takes longer, show a spinner
+ embeddingsList.innerHTML = `
+
+ `
+
// Remove after fixing https://github.com/huggingface/diffusers/issues/3922
let warning = ""
if (vramUsageLevelField.value == "low") {
@@ -2682,9 +2726,11 @@ function updateEmbeddingsList(filter = "") {
}
// END of remove block
- fetch("/bucket/embeddings/")
+ let profileName = profileNameField.value
+ fetch(`/bucket/${profileName}/embeddings/`)
.then(response => response.status==200 ? response.json(): [])
- .then(iconlist => {
+ .then(async function(iconlist) {
+
embeddingsList.innerHTML = warning + html(modelsOptions.embeddings, iconlist, "", filter)
embeddingsList.querySelectorAll("button").forEach((b) => {
b.addEventListener("click", onButtonClick)
diff --git a/ui/media/js/utils.js b/ui/media/js/utils.js
index 9db0d1cc..673ba3dd 100644
--- a/ui/media/js/utils.js
+++ b/ui/media/js/utils.js
@@ -1097,6 +1097,30 @@ async function deleteKeys(keyToDelete) {
}
}
+function cropImageDataUrl(dataUrl, x, y, width, height) {
+ return new Promise((resolve, reject) => {
+ const image = new Image()
+ image.src = dataUrl
+
+ image.onload = () => {
+ const canvas = document.createElement('canvas')
+ canvas.width = width
+ canvas.height = height
+
+ const ctx = canvas.getContext('2d')
+ ctx.drawImage(image, x, y, width, height, 0, 0, width, height)
+
+ const croppedDataUrl = canvas.toDataURL('image/png')
+ resolve(croppedDataUrl)
+ }
+
+ image.onerror = (error) => {
+ reject(error)
+ }
+ })
+}
+
+
function modalDialogCloseOnBackdropClick(dialog) {
dialog.addEventListener('mousedown', function (event) {
// Firefox creates an event with clientX|Y = 0|0 when choosing an