From 1c0579828cf03389db8b71a3f21e37381af0647a Mon Sep 17 00:00:00 2001 From: Glen Date: Sat, 11 Apr 2026 16:59:14 -0600 Subject: [PATCH 1/4] refactor per comments --- src/wwwroot/css/genpage.css | 5 ++ src/wwwroot/js/genpage/main.js | 102 +++++++++++++++++++++++++-------- src/wwwroot/js/util.js | 12 ++++ 3 files changed, 96 insertions(+), 23 deletions(-) diff --git a/src/wwwroot/css/genpage.css b/src/wwwroot/css/genpage.css index 60732e247..a05b1e13a 100644 --- a/src/wwwroot/css/genpage.css +++ b/src/wwwroot/css/genpage.css @@ -1075,6 +1075,7 @@ body { position: relative; background-color: color-mix(in srgb, transparent 80%, var(--background)); transition: background-color 0.2s ease-out; + margin: 0 2px; } .alt-prompt-image { max-height: 128px; @@ -1111,6 +1112,10 @@ body { border: 1px solid black; cursor: pointer; } +.alt-prompt-image-container.image-drop-replace-target .alt-prompt-image { + outline: 2px solid var(--box-selected-border-stronger); + border-radius: 5px; +} .image-clear-button { vertical-align: middle; display: inline-block; diff --git a/src/wwwroot/js/genpage/main.js b/src/wwwroot/js/genpage/main.js index 0f26edd2b..dd01b9098 100644 --- a/src/wwwroot/js/genpage/main.js +++ b/src/wwwroot/js/genpage/main.js @@ -524,33 +524,70 @@ function autoRevealRevision() { } } +let promptImageReplaceTarget = null; + +function setPromptImageReplaceTarget(target) { + if (promptImageReplaceTarget) { + promptImageReplaceTarget.classList.remove('image-drop-replace-target'); + } + promptImageReplaceTarget = target; + if (promptImageReplaceTarget) { + promptImageReplaceTarget.classList.add('image-drop-replace-target'); + } +} + +function getPromptImageDropReplaceTarget(e) { + if (!e || !e.dataTransfer || !e.target || !e.target.closest || uiImprover.getFileList(e.dataTransfer, e).length == 0) { + return null; + } + let target = e.target.closest('.alt-prompt-image-container'); + if (!target || !target.querySelector('.alt-prompt-image')) { + return null; + } + return target; +} + function imagePromptAddImage(file) { - let clearButton = getRequiredElementById('alt_prompt_image_clear_button'); - let promptImageArea = getRequiredElementById('alt_prompt_image_area'); - let reader = new FileReader(); - reader.onload = (e) => { - let data = e.target.result; - let imageContainer = createDiv(null, 'alt-prompt-image-container'); - let imageRemoveButton = createSpan(null, 'alt-prompt-image-container-remove-button', '×'); - imageRemoveButton.addEventListener('click', (e) => { - imageContainer.remove(); - autoRevealRevision(); - genTabLayout.altPromptSizeHandle(); - }); - imageRemoveButton.title = 'Remove this image'; - imageContainer.appendChild(imageRemoveButton); - let imageObject = new Image(); - imageObject.src = data; - imageObject.height = 128; - imageObject.className = 'alt-prompt-image'; - imageObject.dataset.filedata = data; - imageContainer.appendChild(imageObject); + let replaceTarget = promptImageReplaceTarget; + setPromptImageReplaceTarget(null); + let existingImage = replaceTarget ? replaceTarget.querySelector('.alt-prompt-image') : null; + if (replaceTarget && !existingImage) { + replaceTarget = null; + } + readFileAsDataURL(file, (data) => { + if (replaceTarget && !replaceTarget.isConnected) { + imagePromptAddImage(file); + return; + } + if (existingImage) { + existingImage.src = data; + existingImage.height = 128; + existingImage.dataset.filedata = data; + } + else { + let promptImageArea = getRequiredElementById('alt_prompt_image_area'); + let imageContainer = createDiv(null, 'alt-prompt-image-container'); + let imageRemoveButton = createSpan(null, 'alt-prompt-image-container-remove-button', '×'); + imageRemoveButton.addEventListener('click', () => { + imageContainer.remove(); + autoRevealRevision(); + genTabLayout.altPromptSizeHandle(); + }); + imageRemoveButton.title = 'Remove this image'; + imageContainer.appendChild(imageRemoveButton); + let imageObject = new Image(); + imageObject.src = data; + imageObject.height = 128; + imageObject.className = 'alt-prompt-image'; + imageObject.dataset.filedata = data; + imageContainer.appendChild(imageObject); + promptImageArea.appendChild(imageContainer); + } + let clearButton = getRequiredElementById('alt_prompt_image_clear_button'); clearButton.style.display = ''; showRevisionInputs(true); - promptImageArea.appendChild(imageContainer); genTabLayout.altPromptSizeHandle(); - }; - reader.readAsDataURL(file); + }); } function imagePromptInputHandler() { @@ -574,6 +611,25 @@ function imagePromptInputHandler() { } } }); + let updateReplaceTarget = (e) => { + setPromptImageReplaceTarget(getPromptImageDropReplaceTarget(e)); + }; + dragArea.addEventListener('dragenter', updateReplaceTarget, true); + dragArea.addEventListener('dragover', updateReplaceTarget, true); + dragArea.addEventListener('dragleave', (e) => { + if (!dragArea.contains(e.relatedTarget)) { + setPromptImageReplaceTarget(null); + } + }, true); + dragArea.addEventListener('drop', (e) => { + setPromptImageReplaceTarget(getPromptImageDropReplaceTarget(e)); + }, true); + document.addEventListener('drop', () => { + setPromptImageReplaceTarget(null); + }, true); + document.addEventListener('dragend', () => { + setPromptImageReplaceTarget(null); + }, true); } imagePromptInputHandler(); diff --git a/src/wwwroot/js/util.js b/src/wwwroot/js/util.js index f6bede703..2dfdd5683 100644 --- a/src/wwwroot/js/util.js +++ b/src/wwwroot/js/util.js @@ -744,6 +744,18 @@ function readFileText(file, handler) { reader.readAsText(file); } +/** Reads the given file as a data URL and passes the result to the given handler. Ignores null file inputs. */ +function readFileAsDataURL(file, handler) { + if (!file) { + return; + } + let reader = new FileReader(); + reader.onload = (e) => { + handler(e.target.result); + }; + reader.readAsDataURL(file); +} + /** Converts a number to a string of letters, where 1=a, 2=b, 3=c, ..., 26=aa, 27=ab, etc. */ function numberToLetters(id) { if (id > 26) { From 41aaf8e9c4b274a376b19cdc42c15307d9f81a32 Mon Sep 17 00:00:00 2001 From: Glen Date: Sat, 11 Apr 2026 20:48:56 -0600 Subject: [PATCH 2/4] make outline larger --- src/wwwroot/css/genpage.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wwwroot/css/genpage.css b/src/wwwroot/css/genpage.css index a05b1e13a..8c122a614 100644 --- a/src/wwwroot/css/genpage.css +++ b/src/wwwroot/css/genpage.css @@ -1113,7 +1113,7 @@ body { cursor: pointer; } .alt-prompt-image-container.image-drop-replace-target .alt-prompt-image { - outline: 2px solid var(--box-selected-border-stronger); + outline: 4px solid var(--emphasis); border-radius: 5px; } .image-clear-button { From eaf1beffead79da8536f28021ed1b77f90c2ccb1 Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 12 Apr 2026 15:15:46 -0600 Subject: [PATCH 3/4] remove utility function and keep existing file reader code --- src/wwwroot/js/genpage/main.js | 7 +++++-- src/wwwroot/js/util.js | 12 ------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/wwwroot/js/genpage/main.js b/src/wwwroot/js/genpage/main.js index dd01b9098..173752531 100644 --- a/src/wwwroot/js/genpage/main.js +++ b/src/wwwroot/js/genpage/main.js @@ -554,7 +554,9 @@ function imagePromptAddImage(file) { if (replaceTarget && !existingImage) { replaceTarget = null; } - readFileAsDataURL(file, (data) => { + let reader = new FileReader(); + reader.onload = (e) => { + let data = e.target.result; if (replaceTarget && !replaceTarget.isConnected) { imagePromptAddImage(file); return; @@ -587,7 +589,8 @@ function imagePromptAddImage(file) { clearButton.style.display = ''; showRevisionInputs(true); genTabLayout.altPromptSizeHandle(); - }); + }; + reader.readAsDataURL(file); } function imagePromptInputHandler() { diff --git a/src/wwwroot/js/util.js b/src/wwwroot/js/util.js index df405b701..d13898078 100644 --- a/src/wwwroot/js/util.js +++ b/src/wwwroot/js/util.js @@ -744,18 +744,6 @@ function readFileText(file, handler) { reader.readAsText(file); } -/** Reads the given file as a data URL and passes the result to the given handler. Ignores null file inputs. */ -function readFileAsDataURL(file, handler) { - if (!file) { - return; - } - let reader = new FileReader(); - reader.onload = (e) => { - handler(e.target.result); - }; - reader.readAsDataURL(file); -} - /** Converts a number to a string of letters, where 1=a, 2=b, 3=c, ..., 26=aa, 27=ab, etc. */ function numberToLetters(id) { if (id > 26) { From f24706b06cbde6e7283d1829d23a27176c3c5d5c Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 12 Apr 2026 15:17:36 -0600 Subject: [PATCH 4/4] remove unnecessary event checkers --- src/wwwroot/js/genpage/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wwwroot/js/genpage/main.js b/src/wwwroot/js/genpage/main.js index 173752531..428773722 100644 --- a/src/wwwroot/js/genpage/main.js +++ b/src/wwwroot/js/genpage/main.js @@ -537,7 +537,7 @@ function setPromptImageReplaceTarget(target) { } function getPromptImageDropReplaceTarget(e) { - if (!e || !e.dataTransfer || !e.target || !e.target.closest || uiImprover.getFileList(e.dataTransfer, e).length == 0) { + if (uiImprover.getFileList(e.dataTransfer, e).length == 0) { return null; } let target = e.target.closest('.alt-prompt-image-container');