Initial changes for settings migration

This commit is contained in:
Dominik Reh
2022-11-21 19:11:20 +01:00
parent e4a8ee7439
commit def6ebb798
2 changed files with 164 additions and 94 deletions

View File

@@ -1,6 +1,56 @@
var acConfig = null;
var acActive = true;
var acAppendComma = false;
var CFG = null;
function syncOptions() {
let newCFG = {
// Main tag file
tagFile: opts["tac_tagFile"],
// Active in settings
activeIn: {
global: opts["tac_active"],
txt2img: opts["tac_activeIn.txt2img"],
img2img: opts["tac_activeIn.img2img"],
negativePrompts: opts["tac_activeIn.negativePrompts"]
},
// Results related settings
maxResults: opts["tac_maxResults"],
showAllResults: opts["tac_showAllResults"],
resultStepLength: opts["tac_resultStepLength"],
delayTime: opts["tac_delayTime"],
useWildcards: opts["tac_useWildcards"],
useEmbeddings: opts["tac_useEmbeddings"],
useLeftRightArrowKeys: opts["tac_useLeftRightArrowKeys"],
// Insertion related settings
replaceUnderscores: opts["tac_replaceUnderscores"],
escapeParentheses: opts["tac_escapeParentheses"],
appendComma: opts["tac_appendComma"],
// Alias settings
alias: {
searchByAlias: opts["tac_alias.searchByAlias"],
onlyShowAlias: opts["tac_alias.onlyShowAlias"]
},
// Translation settings
translation: {
translationFile: opts["tac_translation.translationFile"],
oldFormat: opts["tac_translation.oldFormat"],
searchByTranslation: opts["tac_translation.searchByTranslation"],
},
// Extra file settings
extra: {
extraFile: opts["tac_extra.extraFile"],
onlyAliasExtraFile: opts["tac_extra.onlyAliasExtraFile"]
}
}
if (CFG && CFG.colors) {
newCFG["colors"] = CFG.colors;
}
if (newCFG.alias.onlyShowAlias) {
newCFG.alias.searchByAlias = true; // if only show translation, enable search by translation is necessary
}
// Apply changes
CFG = newCFG;
}
const styleColors = {
"--results-bg": ["#0b0f19", "#ffffff"],
@@ -18,7 +68,13 @@ const browserVars = {
}
}
// Style for new elements. Gets appended to the Gradio root.
let autocompleteCSS = `
const autocompleteCSS = `
#quicksettings [id^=setting_tac] {
background-color: transparent;
min-width: fit-content;
align-self: center;
margin: 0 5px;
}
.autocompleteResults {
position: absolute;
z-index: 999;
@@ -180,7 +236,7 @@ function createResultsDiv(textArea) {
let textAreaId = getTextAreaIdentifier(textArea);
let typeClass = textAreaId.replaceAll(".", " ");
resultsDiv.style.setProperty("max-height", acConfig.maxResults * 50 + "px");
resultsDiv.style.setProperty("max-height", CFG.maxResults * 50 + "px");
resultsDiv.setAttribute('class', `autocompleteResults ${typeClass}`);
resultsList.setAttribute('class', 'autocompleteResultsList');
resultsDiv.appendChild(resultsList);
@@ -258,10 +314,10 @@ function insertTextAtCursor(textArea, result, tagword) {
} else if (tagType === "embedding") {
sanitizedText = `<${text.replace(/^.*?: /g, "")}>`;
} else {
sanitizedText = acConfig.replaceUnderscores ? text.replaceAll("_", " ") : text;
sanitizedText = CFG.replaceUnderscores ? text.replaceAll("_", " ") : text;
}
if (acConfig.escapeParentheses) {
if (CFG.escapeParentheses) {
sanitizedText = sanitizedText
.replaceAll("(", "\\(")
.replaceAll(")", "\\)")
@@ -279,7 +335,7 @@ function insertTextAtCursor(textArea, result, tagword) {
let afterInsertCursorPos = editStart + match.index + sanitizedText.length;
var optionalComma = "";
if (acAppendComma && tagType !== "wildcardFile") {
if (CFG.appendComma && tagType !== "wildcardFile") {
optionalComma = surrounding.match(new RegExp(`${escapeRegExp(tagword)}[,:]`, "i")) !== null ? "" : ", ";
}
@@ -331,10 +387,10 @@ function addResultsToList(textArea, results, tagword, resetList) {
}
// Find right colors from config
let tagFileName = acConfig.tagFile.split(".")[0];
let tagColors = acConfig.colors;
let tagFileName = CFG.tagFile.split(".")[0];
let tagColors = CFG.colors;
let mode = gradioApp().querySelector('.dark') ? 0 : 1;
let nextLength = Math.min(results.length, resultCount + acConfig.resultStepLength);
let nextLength = Math.min(results.length, resultCount + CFG.resultStepLength);
for (let i = resultCount; i < nextLength; i++) {
let result = results[i];
@@ -371,7 +427,7 @@ function addResultsToList(textArea, results, tagword, resetList) {
if (translations.has(bestAlias) && translations.get(bestAlias) !== bestAlias && bestAlias !== result[0])
displayText += `[${translations.get(bestAlias)}]`;
if (!acConfig.alias.onlyShowAlias && result[0] !== bestAlias)
if (!CFG.alias.onlyShowAlias && result[0] !== bestAlias)
displayText += " ➝ " + result[0];
} else { // No alias
displayText = escapeHTML(result[0]);
@@ -444,7 +500,7 @@ function updateSelectionStyle(textArea, newIndex, oldIndex) {
}
// Set scrolltop to selected item if we are showing more than max results
if (items.length > acConfig.maxResults) {
if (items.length > CFG.maxResults) {
let selected = items[newIndex];
resultDiv.scrollTop = selected.offsetTop - resultDiv.offsetTop;
}
@@ -460,7 +516,7 @@ var tagword = "";
var resultCount = 0;
async function autocomplete(textArea, prompt, fixedTag = null) {
// Return if the function is deactivated in the UI
if (!acActive) return;
if (!CFG.activeIn.global) return;
// Guard for empty prompt
if (prompt.length === 0) {
@@ -502,7 +558,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
tagword = tagword.toLowerCase().replace(/[\n\r]/g, "");
if (acConfig.useWildcards && [...tagword.matchAll(/\b__([^, ]+)__([^, ]*)\b/g)].length > 0) {
if (CFG.useWildcards && [...tagword.matchAll(/\b__([^, ]+)__([^, ]*)\b/g)].length > 0) {
// Show wildcards from a file with that name
wcMatch = [...tagword.matchAll(/\b__([^, ]+)__([^, ]*)\b/g)]
let wcFile = wcMatch[0][1];
@@ -521,7 +577,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
results = wildcards.filter(x => (wcWord !== null && wcWord.length > 0) ? x.toLowerCase().includes(wcWord) : x) // Filter by tagword
.map(x => [wcFile + ": " + x.trim(), "wildcardTag"]); // Mark as wildcard
} else if (acConfig.useWildcards && (tagword.startsWith("__") && !tagword.endsWith("__") || tagword === "__")) {
} else if (CFG.useWildcards && (tagword.startsWith("__") && !tagword.endsWith("__") || tagword === "__")) {
// Show available wildcard files
let tempResults = [];
if (tagword !== "__") {
@@ -531,7 +587,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
tempResults = wildcardFiles.concat(wildcardExtFiles);
}
results = tempResults.map(x => ["Wildcards: " + x[1].trim(), "wildcardFile"]); // Mark as wildcard
} else if (acConfig.useEmbeddings && tagword.match(/<[^,> ]*>?/g)) {
} else if (CFG.useEmbeddings && tagword.match(/<[^,> ]*>?/g)) {
// Show embeddings
let tempResults = [];
if (tagword !== "<") {
@@ -548,7 +604,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
} else {
searchRegex = new RegExp(`(^|[^a-zA-Z])${escapeRegExp(tagword)}`, 'i');
}
genericResults = allTags.filter(x => x[0].toLowerCase().search(searchRegex) > -1).slice(0, acConfig.maxResults);
genericResults = allTags.filter(x => x[0].toLowerCase().search(searchRegex) > -1).slice(0, CFG.maxResults);
results = genericResults.concat(tempResults.map(x => ["Embeddings: " + x.trim(), "embedding"])); // Mark as embedding
} else {
// Create escaped search regex with support for * as a start placeholder
@@ -560,7 +616,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
searchRegex = new RegExp(`(^|[^a-zA-Z])${escapeRegExp(tagword)}`, 'i');
}
// If onlyShowAlias is enabled, we don't need to include normal results
if (acConfig.alias.onlyShowAlias) {
if (CFG.alias.onlyShowAlias) {
results = allTags.filter(x => x[3] && x[3].toLowerCase().search(searchRegex) >- 1);
} else {
// Else both normal tags and aliases/translations are included depending on the config
@@ -570,11 +626,11 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|| x[3] && x[3].split(",").some(y => translations.has(y) && translations.get(y).toLowerCase().search(searchRegex) >- 1);
let fil;
if (acConfig.alias.searchByAlias && acConfig.translation.searchByTranslation)
if (CFG.alias.searchByAlias && CFG.translation.searchByTranslation)
fil = (x) => baseFilter(x) || aliasFilter(x) || translationFilter(x);
else if (acConfig.alias.searchByAlias && !acConfig.translation.searchByTranslation)
else if (CFG.alias.searchByAlias && !CFG.translation.searchByTranslation)
fil = (x) => baseFilter(x) || aliasFilter(x);
else if (acConfig.translation.searchByTranslation && !acConfig.alias.searchByAlias)
else if (CFG.translation.searchByTranslation && !CFG.alias.searchByAlias)
fil = (x) => baseFilter(x) || translationFilter(x);
else
fil = (x) => baseFilter(x);
@@ -582,8 +638,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
results = allTags.filter(fil);
}
// Slice if the user has set a max result count
if (!acConfig.showAllResults) {
results = results.slice(0, acConfig.maxResults);
if (!CFG.showAllResults) {
results = results.slice(0, CFG.maxResults);
}
}
@@ -600,10 +656,10 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
var oldSelectedTag = null;
function navigateInList(textArea, event) {
// Return if the function is deactivated in the UI
if (!acActive) return;
if (!CFG.activeIn.global) return;
validKeys = ["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", "Enter", "Tab", "Escape"];
if (acConfig.useLeftRightArrowKeys)
if (CFG.useLeftRightArrowKeys)
validKeys.push("ArrowLeft", "ArrowRight");
if (!validKeys.includes(event.key)) return;
@@ -682,35 +738,36 @@ function navigateInList(textArea, event) {
event.stopPropagation();
}
onUiUpdate(() => {
if (Object.keys(opts).length === 0) return;
if (CFG) return;
syncOptions();
// Rest of setup
setup();
});
// One-time setup
document.addEventListener("DOMContentLoaded", async () => {
async function setup() {
// Get our tag base path from the temp file
let tagBasePath = await readFile(`tmp/tagAutocompletePath.txt?${new Date().getTime()}`);
// Load config
if (acConfig === null) {
try {
acConfig = await readFile(`${tagBasePath}/config.json?${new Date().getTime()}`, true);
if (acConfig.alias.onlyShowAlias) {
acConfig.alias.searchByAlias = true; // if only show translation, enable search by translation is necessary
}
} catch (e) {
console.error("Error loading config.json: " + e);
return;
}
}
// Load colors
CFG["colors"] = (await readFile(`${tagBasePath}/config.json?${new Date().getTime()}`, true)).colors;
// Load main tags and aliases
if (allTags.length === 0) {
try {
allTags = await loadCSV(`${tagBasePath}/${acConfig.tagFile}?${new Date().getTime()}`);
allTags = await loadCSV(`${tagBasePath}/${CFG.tagFile}?${new Date().getTime()}`);
} catch (e) {
console.error("Error loading tags file: " + e);
return;
}
if (acConfig.extra.extraFile) {
if (CFG.extra.extraFile) {
try {
extras = await loadCSV(`${tagBasePath}/${acConfig.extra.extraFile}?${new Date().getTime()}`);
if (acConfig.extra.onlyAliasExtraFile) {
extras = await loadCSV(`${tagBasePath}/${CFG.extra.extraFile}?${new Date().getTime()}`);
if (CFG.extra.onlyAliasExtraFile) {
// This works purely on index, so it's not very robust. But a lot faster.
for (let i = 0, n = extras.length; i < n; i++) {
if (extras[i][0]) {
@@ -746,11 +803,11 @@ document.addEventListener("DOMContentLoaded", async () => {
}
}
// Load translations
if (acConfig.translation.translationFile) {
if (CFG.translation.translationFile) {
try {
let tArray = await loadCSV(`${tagBasePath}/${acConfig.translation.translationFile}?${new Date().getTime()}`);
let tArray = await loadCSV(`${tagBasePath}/${CFG.translation.translationFile}?${new Date().getTime()}`);
tArray.forEach(t => {
if (acConfig.translation.oldFormat)
if (CFG.translation.oldFormat)
translations.set(t[0], t[2]);
else
translations.set(t[0], t[1]);
@@ -761,7 +818,7 @@ document.addEventListener("DOMContentLoaded", async () => {
}
}
// Load wildcards
if (acConfig.useWildcards && wildcardFiles.length === 0) {
if (wildcardFiles.length === 0) {
try {
let wcFileArr = (await readFile(`${tagBasePath}/temp/wc.txt?${new Date().getTime()}`)).split("\n");
let wcBasePath = wcFileArr[0].trim(); // First line should be the base path
@@ -797,7 +854,7 @@ document.addEventListener("DOMContentLoaded", async () => {
}
}
// Load embeddings
if (acConfig.useEmbeddings && embeddings.length === 0) {
if (embeddings.length === 0) {
try {
embeddings = (await readFile(`${tagBasePath}/temp/emb.txt?${new Date().getTime()}`)).split("\n")
.filter(x => x.trim().length > 0) // Remove empty lines
@@ -814,25 +871,41 @@ document.addEventListener("DOMContentLoaded", async () => {
let img2imgTextArea_n = gradioApp().querySelector('#img2img_neg_prompt > label > textarea');
let textAreas = [txt2imgTextArea, img2imgTextArea, txt2imgTextArea_n, img2imgTextArea_n];
// Add event listener to apply settings button so we can mirror the changes to our internal config
let applySettingsButton = gradioApp().querySelector("#tab_settings > div > .gr-button-primary");
applySettingsButton.addEventListener("click", () => {
// Wait 500ms to make sure the settings have been applied to the webui opts object
setTimeout(async () => {
syncOptions();
}, 500);
});
// Add change listener to our quicksettings to change our internal config without the apply button for them
let quicksettings = gradioApp().querySelector('#quicksettings');
quicksettings.querySelectorAll("[id^=setting_tac] > label > input, [id^=setting_tac] > label > textarea").forEach(e => {
e.addEventListener("change", () => {
setTimeout(async () => {
syncOptions();
}, 500);
});
});
// 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
if (gradioApp().querySelector('.autocompleteResults.p')) {
if (gradioApp().querySelector('.autocompleteResults.n') || !acConfig.activeIn.negativePrompts) {
if (gradioApp().querySelector('.autocompleteResults.n') || !CFG.activeIn.negativePrompts) {
return;
}
} else if (!acConfig.activeIn.txt2img && !acConfig.activeIn.img2img) {
} else if (!CFG.activeIn.txt2img && !CFG.activeIn.img2img) {
return;
}
textAreas.forEach(area => {
// Return if autocomplete is disabled for the current area type in config
let textAreaId = getTextAreaIdentifier(area);
if ((!acConfig.activeIn.img2img && textAreaId.includes("img2img"))
|| (!acConfig.activeIn.txt2img && textAreaId.includes("txt2img"))
|| (!acConfig.activeIn.negativePrompts && textAreaId.includes("n"))) {
if ((!CFG.activeIn.img2img && textAreaId.includes("img2img"))
|| (!CFG.activeIn.txt2img && textAreaId.includes("txt2img"))
|| (!CFG.activeIn.negativePrompts && textAreaId.includes("n"))) {
return;
}
@@ -845,7 +918,7 @@ document.addEventListener("DOMContentLoaded", async () => {
hideResults(area);
// Add autocomplete event listener
area.addEventListener('input', debounce(() => autocomplete(area, area.value), acConfig.delayTime));
area.addEventListener('input', debounce(() => autocomplete(area, area.value), CFG.delayTime));
// Add focusout event listener
area.addEventListener('focusout', debounce(() => hideResults(area), 400));
// Add up and down arrow event listener
@@ -862,44 +935,6 @@ document.addEventListener("DOMContentLoaded", async () => {
}
});
acAppendComma = acConfig.appendComma;
// Add our custom options elements
if (!acConfig.hideUIOptions && gradioApp().querySelector("#tagAutocompleteOptions") === null) {
let optionsDiv = document.createElement("div");
optionsDiv.id = "tagAutocompleteOptions";
optionsDiv.classList.add("flex", "flex-col", "p-1", "px-1", "relative", "text-sm");
let optionsInner = document.createElement("div");
optionsInner.classList.add("flex", "flex-row", "p-1", "gap-4", "text-gray-700");
// Add label
let title = document.createElement("p");
title.textContent = "Autocomplete options";
optionsDiv.appendChild(title);
// Add toggle switch
let cbActive = createCheckbox("Enable Autocomplete");
cbActive.querySelector("input").checked = acActive;
cbActive.querySelector("input").addEventListener("change", (e) => {
acActive = e.target.checked;
});
// Add comma switch
let cbComma = createCheckbox("Append commas");
cbComma.querySelector("input").checked = acAppendComma;
cbComma.querySelector("input").addEventListener("change", (e) => {
acAppendComma = e.target.checked;
});
// Add options to optionsDiv
optionsInner.appendChild(cbActive);
optionsInner.appendChild(cbComma);
optionsDiv.appendChild(optionsInner);
// Add options div to DOM
//quicksettings.parentNode.insertBefore(optionsDiv, quicksettings.nextSibling);
quicksettings.appendChild(optionsDiv);
}
// Add style to dom
let acStyle = document.createElement('style');
//let css = gradioApp().querySelector('.dark') ? autocompleteCSS_dark : autocompleteCSS_light;
@@ -922,4 +957,4 @@ document.addEventListener("DOMContentLoaded", async () => {
acStyle.appendChild(document.createTextNode(css));
}
gradioApp().appendChild(acStyle);
});
}

View File

@@ -2,7 +2,7 @@
# to a temporary file to expose it to the javascript side
from pathlib import Path
from modules import scripts, shared
from modules import scripts, script_callbacks, shared
# Webui root path
FILE_DIR = Path().absolute()
@@ -103,3 +103,38 @@ if EMB_PATH.exists():
embeddings = get_embeddings()
if embeddings:
write_to_temp_file('emb.txt', embeddings)
# Register autocomplete options
def on_ui_settings():
TAC_SECTION = ("tac", "Tag Autocomplete")
# Main tag file
shared.opts.add_option("tac_tagFile", shared.OptionInfo("danbooru.csv", "Tag file name (Requires restart)", section=TAC_SECTION))
# Active in settings
shared.opts.add_option("tac_active", shared.OptionInfo(True, "Enable Tag Autocompletion", section=TAC_SECTION))
shared.opts.add_option("tac_activeIn.txt2img", shared.OptionInfo(True, "Active in txt2img (Requires restart)", section=TAC_SECTION))
shared.opts.add_option("tac_activeIn.img2img", shared.OptionInfo(True, "Active in img2img (Requires restart)", section=TAC_SECTION))
shared.opts.add_option("tac_activeIn.negativePrompts", shared.OptionInfo(True, "Active in negative prompts (Requires restart)", section=TAC_SECTION))
# Results related settings
shared.opts.add_option("tac_maxResults", shared.OptionInfo(5, "Maximum results", section=TAC_SECTION))
shared.opts.add_option("tac_showAllResults", shared.OptionInfo(False, "Show all results", section=TAC_SECTION))
shared.opts.add_option("tac_resultStepLength", shared.OptionInfo(100, "How many results to load at once", section=TAC_SECTION))
shared.opts.add_option("tac_delayTime", shared.OptionInfo(100, "Time in ms to wait before triggering completion again (Requires restart)", section=TAC_SECTION))
shared.opts.add_option("tac_useWildcards", shared.OptionInfo(True, "Search for wildcards", section=TAC_SECTION))
shared.opts.add_option("tac_useEmbeddings", shared.OptionInfo(True, "Search for embeddings", section=TAC_SECTION))
shared.opts.add_option("tac_useLeftRightArrowKeys", shared.OptionInfo(False, "Use left/rigt arrows to jump to the start/end of the list", section=TAC_SECTION))
# Insertion related settings
shared.opts.add_option("tac_replaceUnderscores", shared.OptionInfo(True, "Replace underscores with spaces on insertion", section=TAC_SECTION))
shared.opts.add_option("tac_escapeParentheses", shared.OptionInfo(True, "Escape parentheses on insertion", section=TAC_SECTION))
shared.opts.add_option("tac_appendComma", shared.OptionInfo(True, "Append comma on tag autocompletion", section=TAC_SECTION))
# Alias settings
shared.opts.add_option("tac_alias.searchByAlias", shared.OptionInfo(True, "Search by alias", section=TAC_SECTION))
shared.opts.add_option("tac_alias.onlyShowAlias", shared.OptionInfo(False, "Only show alias", section=TAC_SECTION))
# Translation settings
shared.opts.add_option("tac_translation.translationFile", shared.OptionInfo("", "Translation file name (Requires restart)", section=TAC_SECTION))
shared.opts.add_option("tac_translation.oldFormat", shared.OptionInfo(False, "File uses the old 3-column translation format instead of the new 2-column one (Requires restart)", section=TAC_SECTION))
shared.opts.add_option("tac_translation.searchByTranslation", shared.OptionInfo(True, "Search by translation", section=TAC_SECTION))
# Extra file settings
shared.opts.add_option("tac_extra.extraFile", shared.OptionInfo("", "Extra file name (Requires restart)", section=TAC_SECTION))
shared.opts.add_option("tac_extra.onlyAliasExtraFile", shared.OptionInfo(False, "Extra file in alias only format (Requires restart)", section=TAC_SECTION))
script_callbacks.on_ui_settings(on_ui_settings)