|
|
|
|
@@ -70,6 +70,13 @@ const autocompleteCSS = `
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
color: var(--meta-text-color);
|
|
|
|
|
}
|
|
|
|
|
.acWikiLink {
|
|
|
|
|
padding: 0.5rem;
|
|
|
|
|
margin: -0.5rem 0 -0.5rem -0.5rem;
|
|
|
|
|
}
|
|
|
|
|
.acWikiLink:hover {
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
}
|
|
|
|
|
.acListItem.acEmbeddingV1 {
|
|
|
|
|
color: var(--embedding-v1-color);
|
|
|
|
|
}
|
|
|
|
|
@@ -158,7 +165,9 @@ async function syncOptions() {
|
|
|
|
|
txt2img: opts["tac_activeIn.txt2img"],
|
|
|
|
|
img2img: opts["tac_activeIn.img2img"],
|
|
|
|
|
negativePrompts: opts["tac_activeIn.negativePrompts"],
|
|
|
|
|
thirdParty: opts["tac_activeIn.thirdParty"]
|
|
|
|
|
thirdParty: opts["tac_activeIn.thirdParty"],
|
|
|
|
|
modelList: opts["tac_activeIn.modelList"],
|
|
|
|
|
modelListMode: opts["tac_activeIn.modelListMode"]
|
|
|
|
|
},
|
|
|
|
|
// Results related settings
|
|
|
|
|
maxResults: opts["tac_maxResults"],
|
|
|
|
|
@@ -167,6 +176,9 @@ async function syncOptions() {
|
|
|
|
|
delayTime: opts["tac_delayTime"],
|
|
|
|
|
useWildcards: opts["tac_useWildcards"],
|
|
|
|
|
useEmbeddings: opts["tac_useEmbeddings"],
|
|
|
|
|
useHypernetworks: opts["tac_useHypernetworks"],
|
|
|
|
|
useLoras: opts["tac_useLoras"],
|
|
|
|
|
showWikiLinks: opts["tac_showWikiLinks"],
|
|
|
|
|
// Insertion related settings
|
|
|
|
|
replaceUnderscores: opts["tac_replaceUnderscores"],
|
|
|
|
|
escapeParentheses: opts["tac_escapeParentheses"],
|
|
|
|
|
@@ -186,7 +198,9 @@ async function syncOptions() {
|
|
|
|
|
extra: {
|
|
|
|
|
extraFile: opts["tac_extra.extraFile"],
|
|
|
|
|
onlyAliasExtraFile: opts["tac_extra.onlyAliasExtraFile"]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// Settings not from tac but still used by the script
|
|
|
|
|
extraNetworksDefaultMultiplier: opts["extra_networks_default_multiplier"]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CFG && CFG.colors) {
|
|
|
|
|
@@ -256,6 +270,30 @@ function hideResults(textArea) {
|
|
|
|
|
selectedTag = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var currentModelHash = "";
|
|
|
|
|
var currentModelName = "";
|
|
|
|
|
// Function to check activation criteria
|
|
|
|
|
function isEnabled() {
|
|
|
|
|
if (CFG.activeIn.global) {
|
|
|
|
|
let modelList = CFG.activeIn.modelList
|
|
|
|
|
.split(",")
|
|
|
|
|
.map(x => x.trim())
|
|
|
|
|
.filter(x => x.length > 0);
|
|
|
|
|
|
|
|
|
|
let shortHash = currentModelHash.substring(0, 10);
|
|
|
|
|
if (CFG.activeIn.modelListMode.toLowerCase() === "blacklist") {
|
|
|
|
|
// If the current model is in the blacklist, disable
|
|
|
|
|
return modelList.filter(x => x === currentModelName || x === currentModelHash || x === shortHash).length === 0;
|
|
|
|
|
} else {
|
|
|
|
|
// If the current model is in the whitelist, enable.
|
|
|
|
|
// An empty whitelist is ignored.
|
|
|
|
|
return modelList.length === 0 || modelList.filter(x => x === currentModelName || x === currentModelHash || x === shortHash).length > 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const WEIGHT_REGEX = /[([]([^,()[\]:| ]+)(?::(?:\d+(?:\.\d+)?|\.\d+))?[)\]]/g;
|
|
|
|
|
const TAG_REGEX = /(<[^\t\n\r,>]+>?|[^\s,|<>]+|<)/g
|
|
|
|
|
const WC_REGEX = /\b__([^, ]+)__([^, ]*)\b/g;
|
|
|
|
|
@@ -280,11 +318,15 @@ function insertTextAtCursor(textArea, result, tagword) {
|
|
|
|
|
sanitizedText = text.replaceAll("_", " "); // Replace underscores only if the yaml tag is not using them
|
|
|
|
|
} else if (tagType === ResultType.embedding) {
|
|
|
|
|
sanitizedText = `${text.replace(/^.*?: /g, "")}`;
|
|
|
|
|
} else if (tagType === ResultType.hypernetwork) {
|
|
|
|
|
sanitizedText = `<hypernet:${text}:${CFG.extraNetworksDefaultMultiplier}>`;
|
|
|
|
|
} else if(tagType === ResultType.lora) {
|
|
|
|
|
sanitizedText = `<lora:${text}:${CFG.extraNetworksDefaultMultiplier}>`;
|
|
|
|
|
} else {
|
|
|
|
|
sanitizedText = CFG.replaceUnderscores ? text.replaceAll("_", " ") : text;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CFG.escapeParentheses) {
|
|
|
|
|
if (CFG.escapeParentheses && tagType === ResultType.tag) {
|
|
|
|
|
sanitizedText = sanitizedText
|
|
|
|
|
.replaceAll("(", "\\(")
|
|
|
|
|
.replaceAll(")", "\\)")
|
|
|
|
|
@@ -383,7 +425,6 @@ function addResultsToList(textArea, results, tagword, resetList) {
|
|
|
|
|
|
|
|
|
|
let itemText = document.createElement("div");
|
|
|
|
|
itemText.classList.add("acListItem");
|
|
|
|
|
flexDiv.appendChild(itemText);
|
|
|
|
|
|
|
|
|
|
let displayText = "";
|
|
|
|
|
// If the tag matches the tagword, we don't need to display the alias
|
|
|
|
|
@@ -421,43 +462,68 @@ function addResultsToList(textArea, results, tagword, resetList) {
|
|
|
|
|
// Print search term bolded in result
|
|
|
|
|
itemText.innerHTML = displayText.replace(tagword, `<b>${tagword}</b>`);
|
|
|
|
|
|
|
|
|
|
// Add wiki link if the setting is enabled and a supported tag set loaded
|
|
|
|
|
if (CFG.showWikiLinks
|
|
|
|
|
&& (result.type === ResultType.tag)
|
|
|
|
|
&& (tagFileName.toLowerCase().startsWith("danbooru") || tagFileName.toLowerCase().startsWith("e621"))) {
|
|
|
|
|
let wikiLink = document.createElement("a");
|
|
|
|
|
wikiLink.classList.add("acWikiLink");
|
|
|
|
|
wikiLink.innerText = "?";
|
|
|
|
|
|
|
|
|
|
let linkPart = displayText;
|
|
|
|
|
// Only use alias result if it is one
|
|
|
|
|
if (displayText.includes("➝"))
|
|
|
|
|
linkPart = displayText.split(" ➝ ")[1];
|
|
|
|
|
|
|
|
|
|
// Set link based on selected file
|
|
|
|
|
let tagFileNameLower = tagFileName.toLowerCase();
|
|
|
|
|
if (tagFileNameLower.startsWith("danbooru")) {
|
|
|
|
|
wikiLink.href = `https://danbooru.donmai.us/wiki_pages/${linkPart}`;
|
|
|
|
|
} else if (tagFileNameLower.startsWith("e621")) {
|
|
|
|
|
wikiLink.href = `https://e621.net/wiki_pages/${linkPart}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wikiLink.target = "_blank";
|
|
|
|
|
flexDiv.appendChild(wikiLink);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flexDiv.appendChild(itemText);
|
|
|
|
|
|
|
|
|
|
// Add post count & color if it's a tag
|
|
|
|
|
// Wildcards & Embeds have no tag category
|
|
|
|
|
if (![ResultType.wildcardFile, ResultType.wildcardTag, ResultType.embedding].includes(result.type)) {
|
|
|
|
|
if (result.category) {
|
|
|
|
|
// Set the color of the tag
|
|
|
|
|
let cat = result.category;
|
|
|
|
|
let colorGroup = tagColors[tagFileName];
|
|
|
|
|
// Default to danbooru scheme if no matching one is found
|
|
|
|
|
if (!colorGroup)
|
|
|
|
|
colorGroup = tagColors["danbooru"];
|
|
|
|
|
if (result.category) {
|
|
|
|
|
// Set the color of the tag
|
|
|
|
|
let cat = result.category;
|
|
|
|
|
let colorGroup = tagColors[tagFileName];
|
|
|
|
|
// Default to danbooru scheme if no matching one is found
|
|
|
|
|
if (!colorGroup)
|
|
|
|
|
colorGroup = tagColors["danbooru"];
|
|
|
|
|
|
|
|
|
|
// Set tag type to invalid if not found
|
|
|
|
|
if (!colorGroup[cat])
|
|
|
|
|
cat = "-1";
|
|
|
|
|
// Set tag type to invalid if not found
|
|
|
|
|
if (!colorGroup[cat])
|
|
|
|
|
cat = "-1";
|
|
|
|
|
|
|
|
|
|
itemText.style = `color: ${colorGroup[cat][mode]};`;
|
|
|
|
|
}
|
|
|
|
|
flexDiv.style = `color: ${colorGroup[cat][mode]};`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Post count
|
|
|
|
|
if (result.count && !isNaN(result.count)) {
|
|
|
|
|
let postCount = result.count;
|
|
|
|
|
let formatter;
|
|
|
|
|
// Post count
|
|
|
|
|
if (result.count && !isNaN(result.count)) {
|
|
|
|
|
let postCount = result.count;
|
|
|
|
|
let formatter;
|
|
|
|
|
|
|
|
|
|
// Danbooru formats numbers with a padded fraction for 1M or 1k, but not for 10/100k
|
|
|
|
|
if (postCount >= 1000000 || (postCount >= 1000 && postCount < 10000))
|
|
|
|
|
formatter = Intl.NumberFormat("en", { notation: "compact", minimumFractionDigits: 1, maximumFractionDigits: 1 });
|
|
|
|
|
else
|
|
|
|
|
formatter = Intl.NumberFormat("en", {notation: "compact"});
|
|
|
|
|
|
|
|
|
|
let formattedCount = formatter.format(postCount);
|
|
|
|
|
|
|
|
|
|
let countDiv = document.createElement("div");
|
|
|
|
|
countDiv.textContent = formattedCount;
|
|
|
|
|
countDiv.classList.add("acMetaText");
|
|
|
|
|
flexDiv.appendChild(countDiv);
|
|
|
|
|
}
|
|
|
|
|
} else if (result.meta) { // Check if it is an embedding we have version info for
|
|
|
|
|
// Danbooru formats numbers with a padded fraction for 1M or 1k, but not for 10/100k
|
|
|
|
|
if (postCount >= 1000000 || (postCount >= 1000 && postCount < 10000))
|
|
|
|
|
formatter = Intl.NumberFormat("en", { notation: "compact", minimumFractionDigits: 1, maximumFractionDigits: 1 });
|
|
|
|
|
else
|
|
|
|
|
formatter = Intl.NumberFormat("en", {notation: "compact"});
|
|
|
|
|
|
|
|
|
|
let formattedCount = formatter.format(postCount);
|
|
|
|
|
|
|
|
|
|
let countDiv = document.createElement("div");
|
|
|
|
|
countDiv.textContent = formattedCount;
|
|
|
|
|
countDiv.classList.add("acMetaText");
|
|
|
|
|
flexDiv.appendChild(countDiv);
|
|
|
|
|
} else if (result.meta) { // Check if there is meta info to display
|
|
|
|
|
let metaDiv = document.createElement("div");
|
|
|
|
|
metaDiv.textContent = result.meta;
|
|
|
|
|
metaDiv.classList.add("acMetaText");
|
|
|
|
|
@@ -508,17 +574,21 @@ var wildcardExtFiles = [];
|
|
|
|
|
var yamlWildcards = [];
|
|
|
|
|
var umiPreviousTags = [];
|
|
|
|
|
var embeddings = [];
|
|
|
|
|
var hypernetworks = [];
|
|
|
|
|
var loras = [];
|
|
|
|
|
var results = [];
|
|
|
|
|
var tagword = "";
|
|
|
|
|
var originalTagword = "";
|
|
|
|
|
var resultCount = 0;
|
|
|
|
|
async function autocomplete(textArea, prompt, fixedTag = null) {
|
|
|
|
|
// Return if the function is deactivated in the UI
|
|
|
|
|
if (!CFG.activeIn.global) return;
|
|
|
|
|
if (!isEnabled()) return;
|
|
|
|
|
|
|
|
|
|
// Guard for empty prompt
|
|
|
|
|
if (prompt.length === 0) {
|
|
|
|
|
hideResults(textArea);
|
|
|
|
|
previousTags = [];
|
|
|
|
|
tagword = "";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -533,6 +603,14 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
|
|
|
|
.concat(weightedTags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Guard for no tags
|
|
|
|
|
if (!tags || tags.length === 0) {
|
|
|
|
|
previousTags = [];
|
|
|
|
|
tagword = "";
|
|
|
|
|
hideResults(textArea);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let tagCountChange = tags.length - previousTags.length;
|
|
|
|
|
let diff = difference(tags, previousTags);
|
|
|
|
|
previousTags = tags;
|
|
|
|
|
@@ -761,11 +839,11 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
|
|
|
|
originalTagword = tagword;
|
|
|
|
|
tagword = "";
|
|
|
|
|
}
|
|
|
|
|
} else if (CFG.useEmbeddings && tagword.match(/<[^,> ]*>?/g)) {
|
|
|
|
|
} else if (CFG.useEmbeddings && tagword.match(/<e:[^,> ]*>?/g)) {
|
|
|
|
|
// Show embeddings
|
|
|
|
|
let tempResults = [];
|
|
|
|
|
if (tagword !== "<") {
|
|
|
|
|
let searchTerm = tagword.replace("<", "")
|
|
|
|
|
if (tagword !== "<e:") {
|
|
|
|
|
let searchTerm = tagword.replace("<e:", "")
|
|
|
|
|
let versionString;
|
|
|
|
|
if (searchTerm.startsWith("v1") || searchTerm.startsWith("v2")) {
|
|
|
|
|
versionString = searchTerm.slice(0, 2);
|
|
|
|
|
@@ -778,6 +856,73 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
|
|
|
|
} else {
|
|
|
|
|
tempResults = embeddings;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add final results
|
|
|
|
|
tempResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t[0].trim(), ResultType.embedding)
|
|
|
|
|
result.meta = t[1] + " Embedding";
|
|
|
|
|
results.push(result);
|
|
|
|
|
});
|
|
|
|
|
} else if(CFG.useHypernetworks && tagword.match(/<h:[^,> ]*>?/g)) {
|
|
|
|
|
// Show hypernetworks
|
|
|
|
|
let tempResults = [];
|
|
|
|
|
if (tagword !== "<h:") {
|
|
|
|
|
let searchTerm = tagword.replace("<h:", "")
|
|
|
|
|
tempResults = hypernetworks.filter(x => x.toLowerCase().includes(searchTerm)); // Filter by tagword
|
|
|
|
|
} else {
|
|
|
|
|
tempResults = hypernetworks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add final results
|
|
|
|
|
tempResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t.trim(), ResultType.hypernetwork)
|
|
|
|
|
result.meta = "Hypernetwork";
|
|
|
|
|
results.push(result);
|
|
|
|
|
});
|
|
|
|
|
} else if(CFG.useLoras && tagword.match(/<l:[^,> ]*>?/g)){
|
|
|
|
|
// Show lora
|
|
|
|
|
let tempResults = [];
|
|
|
|
|
if (tagword !== "<l:") {
|
|
|
|
|
let searchTerm = tagword.replace("<l:", "")
|
|
|
|
|
tempResults = loras.filter(x => x.toLowerCase().includes(searchTerm)); // Filter by tagword
|
|
|
|
|
} else {
|
|
|
|
|
tempResults = loras;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add final results
|
|
|
|
|
tempResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t.trim(), ResultType.lora)
|
|
|
|
|
result.meta = "Lora";
|
|
|
|
|
results.push(result);
|
|
|
|
|
});
|
|
|
|
|
} else if ((CFG.useEmbeddings || CFG.useHypernetworks || CFG.useLoras) && tagword.match(/<[^,> ]*>?/g)) {
|
|
|
|
|
// Embeddings, lora, wildcards all together with generic options
|
|
|
|
|
let tempEmbResults = [];
|
|
|
|
|
let tempHypResults = [];
|
|
|
|
|
let tempLoraResults = [];
|
|
|
|
|
if (tagword !== "<") {
|
|
|
|
|
let searchTerm = tagword.replace("<", "")
|
|
|
|
|
|
|
|
|
|
let versionString;
|
|
|
|
|
if (searchTerm.startsWith("v1") || searchTerm.startsWith("v2")) {
|
|
|
|
|
versionString = searchTerm.slice(0, 2);
|
|
|
|
|
searchTerm = searchTerm.slice(2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (versionString && CFG.useEmbeddings) {
|
|
|
|
|
// Version string is only for embeddings atm, so we don't search the other lists here.
|
|
|
|
|
tempEmbResults = embeddings.filter(x => x[0].toLowerCase().includes(searchTerm) && x[1] && x[1] === versionString); // Filter by tagword
|
|
|
|
|
} else {
|
|
|
|
|
tempEmbResults = embeddings.filter(x => x[0].toLowerCase().includes(searchTerm)); // Filter by tagword
|
|
|
|
|
tempHypResults = hypernetworks.filter(x => x.toLowerCase().includes(searchTerm)); // Filter by tagword
|
|
|
|
|
tempLoraResults = loras.filter(x => x.toLowerCase().includes(searchTerm)); // Filter by tagword
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
tempEmbResults = embeddings;
|
|
|
|
|
tempHypResults = hypernetworks;
|
|
|
|
|
tempLoraResults = loras;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since some tags are kaomoji, we have to still get the normal results first.
|
|
|
|
|
// Create escaped search regex with support for * as a start placeholder
|
|
|
|
|
let searchRegex;
|
|
|
|
|
@@ -790,11 +935,32 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
|
|
|
|
let genericResults = allTags.filter(x => x[0].toLowerCase().search(searchRegex) > -1).slice(0, CFG.maxResults);
|
|
|
|
|
|
|
|
|
|
// Add final results
|
|
|
|
|
tempResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t[0].trim(), ResultType.embedding)
|
|
|
|
|
result.meta = t[1] + " Embedding";
|
|
|
|
|
results.push(result);
|
|
|
|
|
});
|
|
|
|
|
let mixedResults = [];
|
|
|
|
|
if (CFG.useEmbeddings) {
|
|
|
|
|
tempEmbResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t[0].trim(), ResultType.embedding)
|
|
|
|
|
result.meta = t[1] + " Embedding";
|
|
|
|
|
mixedResults.push(result);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (CFG.useHypernetworks) {
|
|
|
|
|
tempHypResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t.trim(), ResultType.hypernetwork)
|
|
|
|
|
result.meta = "Hypernetwork";
|
|
|
|
|
mixedResults.push(result);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (CFG.useLoras) {
|
|
|
|
|
tempLoraResults.forEach(t => {
|
|
|
|
|
let result = new AutocompleteResult(t.trim(), ResultType.lora)
|
|
|
|
|
result.meta = "Lora";
|
|
|
|
|
mixedResults.push(result);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add all mixed results to the final results, sorted by name so that they aren't after one another.
|
|
|
|
|
results = mixedResults.sort((a, b) => a.text.localeCompare(b.text));
|
|
|
|
|
|
|
|
|
|
genericResults.forEach(g => {
|
|
|
|
|
let result = new AutocompleteResult(g[0].trim(), ResultType.tag)
|
|
|
|
|
result.category = g[1];
|
|
|
|
|
@@ -859,8 +1025,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
|
|
|
|
|
|
|
|
|
var oldSelectedTag = null;
|
|
|
|
|
function navigateInList(textArea, event) {
|
|
|
|
|
// Return if the function is deactivated in the UI
|
|
|
|
|
if (!CFG.activeIn.global) return;
|
|
|
|
|
// Return if the function is deactivated in the UI or the current model is excluded due to white/blacklist settings
|
|
|
|
|
if (!isEnabled()) return;
|
|
|
|
|
|
|
|
|
|
validKeys = ["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", "Enter", "Tab", "Escape"];
|
|
|
|
|
|
|
|
|
|
@@ -1010,13 +1176,31 @@ async function setup() {
|
|
|
|
|
console.error("Error loading embeddings.txt: " + e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Load hypernetworks
|
|
|
|
|
if (hypernetworks.length === 0) {
|
|
|
|
|
try {
|
|
|
|
|
hypernetworks = (await readFile(`${tagBasePath}/temp/hyp.txt?${new Date().getTime()}`)).split("\n")
|
|
|
|
|
.filter(x => x.trim().length > 0) //Remove empty lines
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Error loading hypernetworks.txt: " + e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Load lora
|
|
|
|
|
if (loras.length === 0) {
|
|
|
|
|
try {
|
|
|
|
|
loras = (await readFile(`${tagBasePath}/temp/lora.txt?${new Date().getTime()}`)).split("\n")
|
|
|
|
|
.filter(x => x.trim().length > 0) // Remove empty lines
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Error loading lora.txt: " + e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find all textareas
|
|
|
|
|
let textAreas = getTextAreas();
|
|
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
|
applySettingsButton.addEventListener("click", () => {
|
|
|
|
|
applySettingsButton?.addEventListener("click", () => {
|
|
|
|
|
// Wait 500ms to make sure the settings have been applied to the webui opts object
|
|
|
|
|
setTimeout(async () => {
|
|
|
|
|
await syncOptions();
|
|
|
|
|
@@ -1025,7 +1209,7 @@ async function setup() {
|
|
|
|
|
// Add change listener to our quicksettings to change our internal config without the apply button for them
|
|
|
|
|
let quicksettings = gradioApp().querySelector('#quicksettings');
|
|
|
|
|
let commonQueryPart = "[id^=setting_tac] > label >";
|
|
|
|
|
quicksettings.querySelectorAll(`${commonQueryPart} input, ${commonQueryPart} textarea, ${commonQueryPart} select`).forEach(e => {
|
|
|
|
|
quicksettings?.querySelectorAll(`${commonQueryPart} input, ${commonQueryPart} textarea, ${commonQueryPart} select`).forEach(e => {
|
|
|
|
|
e.addEventListener("change", () => {
|
|
|
|
|
setTimeout(async () => {
|
|
|
|
|
await syncOptions();
|
|
|
|
|
@@ -1033,6 +1217,28 @@ async function setup() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Add change listener to model dropdown to react to model changes
|
|
|
|
|
let modelDropdown = gradioApp().querySelector("#setting_sd_model_checkpoint select");
|
|
|
|
|
currentModelName = modelDropdown.value;
|
|
|
|
|
modelDropdown?.addEventListener("change", () => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
currentModelName = modelDropdown.value;
|
|
|
|
|
}, 100);
|
|
|
|
|
});
|
|
|
|
|
// Add mutation observer for the model hash text to also allow hash-based blacklist again
|
|
|
|
|
let modelHashText = gradioApp().querySelector("#sd_checkpoint_hash");
|
|
|
|
|
if (modelHashText) {
|
|
|
|
|
currentModelHash = modelHashText.title
|
|
|
|
|
let modelHashObserver = new MutationObserver((mutationList, observer) => {
|
|
|
|
|
for (const mutation of mutationList) {
|
|
|
|
|
if (mutation.type === "attributes" && mutation.attributeName === "title") {
|
|
|
|
|
currentModelHash = mutation.target.title;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
modelHashObserver.observe(modelHashText, { attributes: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not found, we're on a page without prompt textareas
|
|
|
|
|
if (textAreas.every(v => v === null || v === undefined)) return;
|
|
|
|
|
// Already added or unnecessary to add
|
|
|
|
|
|