mirror of
https://github.com/DominikDoom/a1111-sd-webui-tagcomplete.git
synced 2026-03-07 22:30:04 +00:00
Isolate textarea utils in sub-namespace
This commit is contained in:
@@ -1,204 +1,218 @@
|
||||
// Utility functions to select text areas the script should work on,
|
||||
// including third party options.
|
||||
// Supported third party options so far:
|
||||
// - Dataset Tag Editor
|
||||
|
||||
// Core text area selectors
|
||||
const core = [
|
||||
"#txt2img_prompt > label > textarea",
|
||||
"#img2img_prompt > label > textarea",
|
||||
"#txt2img_neg_prompt > label > textarea",
|
||||
"#img2img_neg_prompt > label > textarea",
|
||||
".prompt > label > textarea",
|
||||
"#txt2img_edit_style_prompt > label > textarea",
|
||||
"#txt2img_edit_style_neg_prompt > label > textarea",
|
||||
"#img2img_edit_style_prompt > label > textarea",
|
||||
"#img2img_edit_style_neg_prompt > label > textarea"
|
||||
];
|
||||
TAC.TextAreas = new (function () {
|
||||
// Core text area selectors
|
||||
const core = [
|
||||
"#txt2img_prompt > label > textarea",
|
||||
"#img2img_prompt > label > textarea",
|
||||
"#txt2img_neg_prompt > label > textarea",
|
||||
"#img2img_neg_prompt > label > textarea",
|
||||
".prompt > label > textarea",
|
||||
"#txt2img_edit_style_prompt > label > textarea",
|
||||
"#txt2img_edit_style_neg_prompt > label > textarea",
|
||||
"#img2img_edit_style_prompt > label > textarea",
|
||||
"#img2img_edit_style_neg_prompt > label > textarea",
|
||||
];
|
||||
|
||||
// Third party text area selectors
|
||||
const thirdParty = {
|
||||
"dataset-tag-editor": {
|
||||
"base": "#tab_dataset_tag_editor_interface",
|
||||
"hasIds": false,
|
||||
"selectors": [
|
||||
"Caption of Selected Image",
|
||||
"Interrogate Result",
|
||||
"Edit Caption",
|
||||
"Edit Tags"
|
||||
]
|
||||
},
|
||||
"image browser": {
|
||||
"base": "#tab_image_browser",
|
||||
"hasIds": false,
|
||||
"selectors": [
|
||||
"Filename keyword search",
|
||||
"EXIF keyword search"
|
||||
]
|
||||
},
|
||||
"tab_tagger": {
|
||||
"base": "#tab_tagger",
|
||||
"hasIds": false,
|
||||
"selectors": [
|
||||
"Additional tags (split by comma)",
|
||||
"Exclude tags (split by comma)"
|
||||
]
|
||||
},
|
||||
"tiled-diffusion-t2i": {
|
||||
"base": "#txt2img_script_container",
|
||||
"hasIds": true,
|
||||
"onDemand": true,
|
||||
"selectors": [
|
||||
"[id^=MD-t2i][id$=prompt] textarea",
|
||||
"[id^=MD-t2i][id$=prompt] input[type='text']"
|
||||
]
|
||||
},
|
||||
"tiled-diffusion-i2i": {
|
||||
"base": "#img2img_script_container",
|
||||
"hasIds": true,
|
||||
"onDemand": true,
|
||||
"selectors": [
|
||||
"[id^=MD-i2i][id$=prompt] textarea",
|
||||
"[id^=MD-i2i][id$=prompt] input[type='text']"
|
||||
]
|
||||
},
|
||||
"adetailer-t2i": {
|
||||
"base": "#txt2img_script_container",
|
||||
"hasIds": true,
|
||||
"onDemand": true,
|
||||
"selectors": [
|
||||
"[id^=script_txt2img_adetailer_ad_prompt] textarea",
|
||||
"[id^=script_txt2img_adetailer_ad_negative_prompt] textarea"
|
||||
]
|
||||
},
|
||||
"adetailer-i2i": {
|
||||
"base": "#img2img_script_container",
|
||||
"hasIds": true,
|
||||
"onDemand": true,
|
||||
"selectors": [
|
||||
"[id^=script_img2img_adetailer_ad_prompt] textarea",
|
||||
"[id^=script_img2img_adetailer_ad_negative_prompt] textarea"
|
||||
]
|
||||
},
|
||||
"deepdanbooru-object-recognition": {
|
||||
"base": "#tab_deepdanboru_object_recg_tab",
|
||||
"hasIds": false,
|
||||
"selectors": [
|
||||
"Found tags",
|
||||
]
|
||||
},
|
||||
"TIPO": {
|
||||
"base": "#tab_txt2img",
|
||||
"hasIds": false,
|
||||
"selectors": [
|
||||
"Tag Prompt"
|
||||
]
|
||||
// Third party text area selectors
|
||||
const thirdParty = {
|
||||
"dataset-tag-editor": {
|
||||
base: "#tab_dataset_tag_editor_interface",
|
||||
hasIds: false,
|
||||
selectors: [
|
||||
"Caption of Selected Image",
|
||||
"Interrogate Result",
|
||||
"Edit Caption",
|
||||
"Edit Tags",
|
||||
],
|
||||
},
|
||||
"image browser": {
|
||||
base: "#tab_image_browser",
|
||||
hasIds: false,
|
||||
selectors: ["Filename keyword search", "EXIF keyword search"],
|
||||
},
|
||||
tab_tagger: {
|
||||
base: "#tab_tagger",
|
||||
hasIds: false,
|
||||
selectors: ["Additional tags (split by comma)", "Exclude tags (split by comma)"],
|
||||
},
|
||||
"tiled-diffusion-t2i": {
|
||||
base: "#txt2img_script_container",
|
||||
hasIds: true,
|
||||
onDemand: true,
|
||||
selectors: [
|
||||
"[id^=MD-t2i][id$=prompt] textarea",
|
||||
"[id^=MD-t2i][id$=prompt] input[type='text']",
|
||||
],
|
||||
},
|
||||
"tiled-diffusion-i2i": {
|
||||
base: "#img2img_script_container",
|
||||
hasIds: true,
|
||||
onDemand: true,
|
||||
selectors: [
|
||||
"[id^=MD-i2i][id$=prompt] textarea",
|
||||
"[id^=MD-i2i][id$=prompt] input[type='text']",
|
||||
],
|
||||
},
|
||||
"adetailer-t2i": {
|
||||
base: "#txt2img_script_container",
|
||||
hasIds: true,
|
||||
onDemand: true,
|
||||
selectors: [
|
||||
"[id^=script_txt2img_adetailer_ad_prompt] textarea",
|
||||
"[id^=script_txt2img_adetailer_ad_negative_prompt] textarea",
|
||||
],
|
||||
},
|
||||
"adetailer-i2i": {
|
||||
base: "#img2img_script_container",
|
||||
hasIds: true,
|
||||
onDemand: true,
|
||||
selectors: [
|
||||
"[id^=script_img2img_adetailer_ad_prompt] textarea",
|
||||
"[id^=script_img2img_adetailer_ad_negative_prompt] textarea",
|
||||
],
|
||||
},
|
||||
"deepdanbooru-object-recognition": {
|
||||
base: "#tab_deepdanboru_object_recg_tab",
|
||||
hasIds: false,
|
||||
selectors: ["Found tags"],
|
||||
},
|
||||
TIPO: {
|
||||
base: "#tab_txt2img",
|
||||
hasIds: false,
|
||||
selectors: ["Tag Prompt"],
|
||||
},
|
||||
};
|
||||
|
||||
this.getTextAreas = function () {
|
||||
// First get all core text areas
|
||||
let textAreas = [...gradioApp().querySelectorAll(core.join(", "))];
|
||||
|
||||
for (const [key, entry] of Object.entries(thirdParty)) {
|
||||
if (entry.hasIds) {
|
||||
// If the entry has proper ids, we can just select them
|
||||
textAreas = textAreas.concat([
|
||||
...gradioApp().querySelectorAll(entry.selectors.join(", ")),
|
||||
]);
|
||||
} else {
|
||||
// Otherwise, we have to find the text areas by their adjacent labels
|
||||
let base = gradioApp().querySelector(entry.base);
|
||||
|
||||
// Safety check
|
||||
if (!base) continue;
|
||||
|
||||
let allTextAreas = [...base.querySelectorAll("textarea, input[type='text']")];
|
||||
|
||||
// Filter the text areas where the adjacent label matches one of the selectors
|
||||
let matchingTextAreas = allTextAreas.filter((ta) =>
|
||||
[...ta.parentElement.childNodes].some((x) =>
|
||||
entry.selectors.includes(x.innerText)
|
||||
)
|
||||
);
|
||||
textAreas = textAreas.concat(matchingTextAreas);
|
||||
}
|
||||
}
|
||||
|
||||
return textAreas;
|
||||
}
|
||||
}
|
||||
|
||||
function getTextAreas() {
|
||||
// First get all core text areas
|
||||
let textAreas = [...gradioApp().querySelectorAll(core.join(", "))];
|
||||
this.addOnDemandObservers = function (setupFunction) {
|
||||
for (const [key, entry] of Object.entries(thirdParty)) {
|
||||
if (!entry.onDemand) continue;
|
||||
|
||||
for (const [key, entry] of Object.entries(thirdParty)) {
|
||||
if (entry.hasIds) { // If the entry has proper ids, we can just select them
|
||||
textAreas = textAreas.concat([...gradioApp().querySelectorAll(entry.selectors.join(", "))]);
|
||||
} else { // Otherwise, we have to find the text areas by their adjacent labels
|
||||
let base = gradioApp().querySelector(entry.base);
|
||||
|
||||
// Safety check
|
||||
if (!base) continue;
|
||||
|
||||
let allTextAreas = [...base.querySelectorAll("textarea, input[type='text']")];
|
||||
let accordions = [...base?.querySelectorAll(".gradio-accordion")];
|
||||
if (!accordions) continue;
|
||||
|
||||
// Filter the text areas where the adjacent label matches one of the selectors
|
||||
let matchingTextAreas = allTextAreas.filter(ta => [...ta.parentElement.childNodes].some(x => entry.selectors.includes(x.innerText)));
|
||||
textAreas = textAreas.concat(matchingTextAreas);
|
||||
}
|
||||
};
|
||||
|
||||
return textAreas;
|
||||
}
|
||||
|
||||
function addOnDemandObservers(setupFunction) {
|
||||
for (const [key, entry] of Object.entries(thirdParty)) {
|
||||
if (!entry.onDemand) continue;
|
||||
|
||||
let base = gradioApp().querySelector(entry.base);
|
||||
if (!base) continue;
|
||||
|
||||
let accordions = [...base?.querySelectorAll(".gradio-accordion")];
|
||||
if (!accordions) continue;
|
||||
|
||||
accordions.forEach(acc => {
|
||||
let accObserver = new MutationObserver((mutationList, observer) => {
|
||||
for (const mutation of mutationList) {
|
||||
if (mutation.type === "childList") {
|
||||
let newChildren = mutation.addedNodes;
|
||||
if (!newChildren) {
|
||||
accObserver.disconnect();
|
||||
continue;
|
||||
}
|
||||
|
||||
newChildren.forEach(child => {
|
||||
if (child.classList.contains("gradio-accordion") || child.querySelector(".gradio-accordion")) {
|
||||
let newAccordions = [...child.querySelectorAll(".gradio-accordion")];
|
||||
newAccordions.forEach(nAcc => accObserver.observe(nAcc, { childList: true }));
|
||||
accordions.forEach((acc) => {
|
||||
let accObserver = new MutationObserver((mutationList, observer) => {
|
||||
for (const mutation of mutationList) {
|
||||
if (mutation.type === "childList") {
|
||||
let newChildren = mutation.addedNodes;
|
||||
if (!newChildren) {
|
||||
accObserver.disconnect();
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
if (entry.hasIds) { // If the entry has proper ids, we can just select them
|
||||
[...gradioApp().querySelectorAll(entry.selectors.join(", "))].forEach(x => setupFunction(x));
|
||||
} else { // Otherwise, we have to find the text areas by their adjacent labels
|
||||
let base = gradioApp().querySelector(entry.base);
|
||||
newChildren.forEach((child) => {
|
||||
if (
|
||||
child.classList.contains("gradio-accordion") ||
|
||||
child.querySelector(".gradio-accordion")
|
||||
) {
|
||||
let newAccordions = [
|
||||
...child.querySelectorAll(".gradio-accordion"),
|
||||
];
|
||||
newAccordions.forEach((nAcc) =>
|
||||
accObserver.observe(nAcc, { childList: true })
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Safety check
|
||||
if (!base) continue;
|
||||
if (entry.hasIds) {
|
||||
// If the entry has proper ids, we can just select them
|
||||
[
|
||||
...gradioApp().querySelectorAll(entry.selectors.join(", ")),
|
||||
].forEach((x) => setupFunction(x));
|
||||
} else {
|
||||
// Otherwise, we have to find the text areas by their adjacent labels
|
||||
let base = gradioApp().querySelector(entry.base);
|
||||
|
||||
let allTextAreas = [...base.querySelectorAll("textarea, input[type='text']")];
|
||||
// Safety check
|
||||
if (!base) continue;
|
||||
|
||||
// Filter the text areas where the adjacent label matches one of the selectors
|
||||
let matchingTextAreas = allTextAreas.filter(ta => [...ta.parentElement.childNodes].some(x => entry.selectors.includes(x.innerText)));
|
||||
matchingTextAreas.forEach(x => setupFunction(x));
|
||||
let allTextAreas = [
|
||||
...base.querySelectorAll("textarea, input[type='text']"),
|
||||
];
|
||||
|
||||
// Filter the text areas where the adjacent label matches one of the selectors
|
||||
let matchingTextAreas = allTextAreas.filter((ta) =>
|
||||
[...ta.parentElement.childNodes].some((x) =>
|
||||
entry.selectors.includes(x.innerText)
|
||||
)
|
||||
);
|
||||
matchingTextAreas.forEach((x) => setupFunction(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
accObserver.observe(acc, { childList: true });
|
||||
});
|
||||
accObserver.observe(acc, { childList: true });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const thirdPartyIdSet = new Set();
|
||||
// Get the identifier for the text area to differentiate between positive and negative
|
||||
function getTextAreaIdentifier(textArea) {
|
||||
let txt2img_p = gradioApp().querySelector('#txt2img_prompt > label > textarea');
|
||||
let txt2img_n = gradioApp().querySelector('#txt2img_neg_prompt > label > textarea');
|
||||
let img2img_p = gradioApp().querySelector('#img2img_prompt > label > textarea');
|
||||
let img2img_n = gradioApp().querySelector('#img2img_neg_prompt > label > textarea');
|
||||
|
||||
let modifier = "";
|
||||
switch (textArea) {
|
||||
case txt2img_p:
|
||||
modifier = ".txt2img.p";
|
||||
break;
|
||||
case txt2img_n:
|
||||
modifier = ".txt2img.n";
|
||||
break;
|
||||
case img2img_p:
|
||||
modifier = ".img2img.p";
|
||||
break;
|
||||
case img2img_n:
|
||||
modifier = ".img2img.n";
|
||||
break;
|
||||
default:
|
||||
// If the text area is not a core text area, it must be a third party text area
|
||||
// Add it to the set of third party text areas and get its index as a unique identifier
|
||||
if (!thirdPartyIdSet.has(textArea))
|
||||
thirdPartyIdSet.add(textArea);
|
||||
|
||||
modifier = `.thirdParty.ta${[...thirdPartyIdSet].indexOf(textArea)}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return modifier;
|
||||
}
|
||||
|
||||
const thirdPartyIdSet = new Set();
|
||||
// Get the identifier for the text area to differentiate between positive and negative
|
||||
this.getTextAreaIdentifier = function (textArea) {
|
||||
let txt2img_p = gradioApp().querySelector("#txt2img_prompt > label > textarea");
|
||||
let txt2img_n = gradioApp().querySelector("#txt2img_neg_prompt > label > textarea");
|
||||
let img2img_p = gradioApp().querySelector("#img2img_prompt > label > textarea");
|
||||
let img2img_n = gradioApp().querySelector("#img2img_neg_prompt > label > textarea");
|
||||
|
||||
let modifier = "";
|
||||
switch (textArea) {
|
||||
case txt2img_p:
|
||||
modifier = ".txt2img.p";
|
||||
break;
|
||||
case txt2img_n:
|
||||
modifier = ".txt2img.n";
|
||||
break;
|
||||
case img2img_p:
|
||||
modifier = ".img2img.p";
|
||||
break;
|
||||
case img2img_n:
|
||||
modifier = ".img2img.n";
|
||||
break;
|
||||
default:
|
||||
// If the text area is not a core text area, it must be a third party text area
|
||||
// Add it to the set of third party text areas and get its index as a unique identifier
|
||||
if (!thirdPartyIdSet.has(textArea)) thirdPartyIdSet.add(textArea);
|
||||
|
||||
modifier = `.thirdParty.ta${[...thirdPartyIdSet].indexOf(textArea)}`;
|
||||
break;
|
||||
}
|
||||
return modifier;
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -332,7 +332,7 @@ function createResultsDiv(textArea) {
|
||||
let sideDiv = document.createElement("div");
|
||||
let sideDivImg = document.createElement("img");
|
||||
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let typeClass = textAreaId.replaceAll(".", " ");
|
||||
|
||||
parentDiv.setAttribute("class", `autocompleteParent${typeClass}`);
|
||||
@@ -354,12 +354,12 @@ function createResultsDiv(textArea) {
|
||||
|
||||
// Show or hide the results div
|
||||
function isVisible(textArea) {
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let parentDiv = gradioApp().querySelector('.autocompleteParent' + textAreaId);
|
||||
return parentDiv.style.display === "flex";
|
||||
}
|
||||
function showResults(textArea) {
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let parentDiv = gradioApp().querySelector('.autocompleteParent' + textAreaId);
|
||||
parentDiv.style.display = "flex";
|
||||
|
||||
@@ -383,7 +383,7 @@ function showResults(textArea) {
|
||||
previewDiv.style.display = "none";
|
||||
}
|
||||
function hideResults(textArea) {
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let resultsDiv = gradioApp().querySelector('.autocompleteParent' + textAreaId);
|
||||
|
||||
if (!resultsDiv) return;
|
||||
@@ -513,7 +513,7 @@ async function insertTextAtCursor(textArea, result, tagword, tabCompletedWithout
|
||||
|
||||
if (name && name.length > 0) {
|
||||
// Check if it's a negative prompt
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let isNegative = textAreaId.includes("n");
|
||||
// Sanitize name for API call
|
||||
name = encodeURIComponent(name)
|
||||
@@ -675,7 +675,7 @@ async function insertTextAtCursor(textArea, result, tagword, tabCompletedWithout
|
||||
}
|
||||
|
||||
function addResultsToList(textArea, results, tagword, resetList) {
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let resultDiv = gradioApp().querySelector('.autocompleteResults' + textAreaId);
|
||||
let resultsList = resultDiv.querySelector('ul');
|
||||
|
||||
@@ -950,7 +950,7 @@ function addResultsToList(textArea, results, tagword, resetList) {
|
||||
}
|
||||
|
||||
async function updateSelectionStyle(textArea, newIndex, oldIndex, scroll = true) {
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let resultDiv = gradioApp().querySelector('.autocompleteResults' + textAreaId);
|
||||
let resultsList = resultDiv.querySelector('ul');
|
||||
let items = resultsList.getElementsByTagName('li');
|
||||
@@ -999,9 +999,9 @@ function updateRuby(textArea, prompt) {
|
||||
if (!TAC.CFG.translation.liveTranslation) return;
|
||||
if (!TAC.CFG.translation.translationFile || TAC.CFG.translation.translationFile === "None") return;
|
||||
|
||||
let ruby = gradioApp().querySelector('.acRuby' + getTextAreaIdentifier(textArea));
|
||||
let ruby = gradioApp().querySelector('.acRuby' + TAC.TextAreas.getTextAreaIdentifier(textArea));
|
||||
if (!ruby) {
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let typeClass = textAreaId.replaceAll(".", " ");
|
||||
ruby = document.createElement("div");
|
||||
ruby.setAttribute("class", `acRuby${typeClass} notranslate`);
|
||||
@@ -1309,7 +1309,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
||||
});
|
||||
|
||||
// Check if it's a negative prompt
|
||||
let textAreaId = getTextAreaIdentifier(textArea);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(textArea);
|
||||
let isNegative = textAreaId.includes("n");
|
||||
|
||||
// Request use counts from the DB
|
||||
@@ -1492,7 +1492,7 @@ async function refreshEmbeddings() {
|
||||
|
||||
function addAutocompleteToArea(area) {
|
||||
// Return if autocomplete is disabled for the current area type in config
|
||||
let textAreaId = getTextAreaIdentifier(area);
|
||||
let textAreaId = TAC.TextAreas.getTextAreaIdentifier(area);
|
||||
if ((!TAC.CFG.activeIn.img2img && textAreaId.includes("img2img"))
|
||||
|| (!TAC.CFG.activeIn.txt2img && textAreaId.includes("txt2img"))
|
||||
|| (!TAC.CFG.activeIn.negativePrompts && textAreaId.includes("n"))
|
||||
@@ -1550,10 +1550,10 @@ async function setup() {
|
||||
await TacUtils.processQueue(TAC.Ext.QUEUE_FILE_LOAD, null);
|
||||
|
||||
// Find all textareas
|
||||
let textAreas = getTextAreas();
|
||||
let textAreas = TAC.TextAreas.getTextAreas();
|
||||
|
||||
// Add mutation observer to accordions inside a base that has onDemand set to true
|
||||
addOnDemandObservers(addAutocompleteToArea);
|
||||
TAC.TextAreas.addOnDemandObservers(addAutocompleteToArea);
|
||||
|
||||
// Add event listener to apply settings button so we can mirror the changes to our internal config
|
||||
let applySettingsButton = gradioApp().querySelector("#tab_settings #settings_submit") || gradioApp().querySelector("#tab_settings > div > .gr-button-primary");
|
||||
|
||||
Reference in New Issue
Block a user