This commit is contained in:
Haoming 2026-05-04 10:50:09 +08:00
parent addf9dcc79
commit 7e1ca521e3
3 changed files with 32 additions and 85 deletions

View File

@ -1,25 +1,16 @@
// allows drag-dropping files into gradio image elements, and also pasting images from clipboard
// allows drag-and-drop files into prompt fields, and also pasting images from clipboard
function isValidImageList(files) {
return (
files &&
files?.length === 1 &&
["image/png", "image/gif", "image/jpeg"].includes(files[0].type)
);
return files != null && files.length === 1 && files[0].type.startsWith("image");
}
function dropReplaceImage(imgWrap, files) {
if (!isValidImageList(files)) {
return;
}
if (!isValidImageList(files)) return;
const tmpFile = files[0];
imgWrap
.querySelector(
".modify-upload button + button, .touch-none + div button + button",
)
?.click();
imgWrap.querySelector(".modify-upload button + button, .touch-none + div button + button")?.click();
const callback = () => {
const fileInput = imgWrap.querySelector('input[type="file"]');
if (fileInput) {
@ -40,8 +31,7 @@ function dropReplaceImage(imgWrap, files) {
function eventHasFiles(e) {
if (!e.dataTransfer || !e.dataTransfer.files) return false;
if (e.dataTransfer.files.length > 0) return true;
if (e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind == "file")
return true;
if (e.dataTransfer.items.length > 0 && e.dataTransfer.items[0].kind == "file") return true;
return false;
}
@ -56,10 +46,8 @@ function isURL(url) {
}
function dragDropTargetIsPrompt(target) {
if (target?.placeholder && target?.placeholder.indexOf("Prompt") >= 0)
return true;
if (target?.parentNode?.parentNode?.className?.indexOf("prompt") > 0)
return true;
if (target?.placeholder && target?.placeholder.indexOf("Prompt") >= 0) return true;
if (target?.parentNode?.parentNode?.className?.indexOf("prompt") > 0) return true;
return false;
}
@ -67,7 +55,7 @@ window.document.addEventListener("dragover", (e) => {
const target = e.composedPath()[0];
if (!eventHasFiles(e)) return;
let targetImage = target.closest('[data-testid="image"]');
const targetImage = target.closest('[data-testid="image"]');
if (!dragDropTargetIsPrompt(target) && !targetImage) return;
e.stopPropagation();
@ -77,9 +65,7 @@ window.document.addEventListener("dragover", (e) => {
window.document.addEventListener("drop", async (e) => {
const target = e.composedPath()[0];
const url =
e.dataTransfer.getData("text/uri-list") ||
e.dataTransfer.getData("text/plain");
const url = e.dataTransfer.getData("text/uri-list") || e.dataTransfer.getData("text/plain");
if (!eventHasFiles(e) && !isURL(url)) return;
if (dragDropTargetIsPrompt(target)) {
@ -87,9 +73,7 @@ window.document.addEventListener("drop", async (e) => {
e.preventDefault();
const isImg2img = get_tab_index("tabs") == 1;
let prompt_image_target = isImg2img
? "img2img_prompt_image"
: "txt2img_prompt_image";
const prompt_image_target = isImg2img ? "img2img_prompt_image" : "txt2img_prompt_image";
const imgParent = gradioApp().getElementById(prompt_image_target);
const files = e.dataTransfer.files;
@ -115,7 +99,7 @@ window.document.addEventListener("drop", async (e) => {
}
}
let targetImage = target.closest('[data-testid="image"]');
const targetImage = target.closest('[data-testid="image"]');
if (targetImage) {
e.stopPropagation();
e.preventDefault();
@ -127,28 +111,18 @@ window.document.addEventListener("drop", async (e) => {
window.addEventListener("paste", (e) => {
const files = e.clipboardData.files;
if (!isValidImageList(files)) {
return;
}
if (!isValidImageList(files)) return;
const visibleImageFields = [
...gradioApp().querySelectorAll('[data-testid="image"]'),
]
const visibleImageFields = [...gradioApp().querySelectorAll('[data-testid="image"]')]
.filter((el) => uiElementIsVisible(el))
.sort((a, b) => uiElementInSight(b) - uiElementInSight(a));
if (!visibleImageFields.length) {
return;
}
if (!visibleImageFields.length) return;
const firstFreeImageField = visibleImageFields.filter(
(el) => !el.querySelector("img"),
)?.[0];
const firstFreeImageField = visibleImageFields.filter((el) => !el.querySelector("img"))?.[0];
dropReplaceImage(
firstFreeImageField
? firstFreeImageField
: visibleImageFields[visibleImageFields.length - 1],
firstFreeImageField ? firstFreeImageField : visibleImageFields[visibleImageFields.length - 1],
files,
);
});

View File

@ -1,55 +1,27 @@
(function () {
function extractImageFromHTML(html) {
if (!html) return null;
const match = html.match(/<img[^>]+src="([^">]+)"/i);
return match ? match[1] : null;
}
/** @param {HTMLDivElement} gradioImage */
function patchDragAndDrop(gradioImage) {
let isDroppingImage = false;
gradioImage.addEventListener("dragover", (e) => {
const dt = e.dataTransfer;
isDroppingImage = dt.types.includes("text/uri-list") || dt.types.includes("text/html");
if (isDroppingImage) e.preventDefault();
});
gradioImage.addEventListener("drop", async (e) => {
const isDroppingImage = dt.types.includes("text/uri-list") || dt.types.includes("text/html");
if (!isDroppingImage) return;
const dt = e.dataTransfer;
const url = dt.getData("text/uri-list") || extractImageFromHTML(dt.getData("text/html"));
if (!url) return;
e.preventDefault();
try {
const closeButton = gradioImage.querySelector('button[aria-label="Remove Image"]');
closeButton.click();
const res = await fetch(url);
const blob = await res.blob();
const file = new File([blob], "dropped-image", {
type: blob.type || "image/png",
});
const newDT = new DataTransfer();
newDT.items.add(file);
const fileInput = gradioImage.querySelector('input[type="file"]');
fileInput.files = newDT.files;
fileInput.dispatchEvent(new Event("change", { bubbles: true }));
} catch {
console.log("Failed to replace image...");
}
const closeButton = gradioImage.querySelector('button[aria-label="Remove Image"]');
if (closeButton) closeButton.click();
});
}
function setup() {
if (opts.remove_image_on_hover === false) return;
if (opts.remove_image_on_hover === undefined) {
setTimeout(setup, 50);
return;
}
for (const id of ["extras_image", "pnginfo_image"]) patchDragAndDrop(document.getElementById(id));
}
onUiLoaded(() => {
for (const id of ["extras_image", "pnginfo_image"]) patchDragAndDrop(document.getElementById(id));
setTimeout(setup, 100);
});
})();

View File

@ -425,6 +425,7 @@ options_templates.update(
"ctrl_enter_interrupt": OptionInfo(False, "Revert [Ctrl + Enter] to only interrupt the generation").info('the current "intended" behavior is to interrupt the current generation then immediately start a new one'),
"quicksettings_accordion": OptionInfo(False, "Place the Quicksettings under an Accordion").needs_reload_ui(),
"quicksettings_accordion_starts_closed": OptionInfo(False, "Close the Accordion on startup").info("for the above option").needs_reload_ui(),
"remove_image_on_hover": OptionInfo(True, "For image inputs in Extras and PNG Info, remove the current image when dragging another image over it").info("allow you to drag-and-drop images onto the input similar to AUTOMATIC1111 behavior").needs_reload_ui(),
"forbidden_knowledge": OptionInfo(False, "Forbidden Knowledge").info('replace "<b>DPM++ 2s a RF</b>" with "<b>Flux Realistic</b>"').needs_restart(),
"div_prompt": OptionDiv(),
"prompt_box_style": OptionInfo("Default", "Prompt Layout", gr.Radio, {"choices": ("Default", "Compact", "Scrollable", "Accordion")}).html(f"""