diff --git a/src/wwwroot/css/genpage.css b/src/wwwroot/css/genpage.css index e63a69333..b9ab6ef03 100644 --- a/src/wwwroot/css/genpage.css +++ b/src/wwwroot/css/genpage.css @@ -1079,6 +1079,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; @@ -1115,6 +1116,10 @@ body { border: 1px solid black; cursor: pointer; } +.alt-prompt-image-container.image-drop-replace-target .alt-prompt-image { + outline: 4px solid var(--emphasis); + 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..428773722 100644 --- a/src/wwwroot/js/genpage/main.js +++ b/src/wwwroot/js/genpage/main.js @@ -524,30 +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 (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 replaceTarget = promptImageReplaceTarget; + setPromptImageReplaceTarget(null); + let existingImage = replaceTarget ? replaceTarget.querySelector('.alt-prompt-image') : null; + if (replaceTarget && !existingImage) { + replaceTarget = null; + } 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); + 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); @@ -574,6 +614,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();