Compare commits

..

10 Commits

Author SHA1 Message Date
Dominik Reh
abb5625e55 Cache workaround
Force reload by querying with current time added to the URL
2022-11-01 13:56:03 +01:00
Dominik Reh
d5de786d07 Don't use unnecessary onUIUpdate 2022-11-01 13:54:38 +01:00
Dominik Reh
f8a9223c29 Add config option to hide autocomplete UI options
Implements #57
2022-11-01 13:50:34 +01:00
Dominik Reh
61a97175a7 Fix parentheses regression
Fixes #59
2022-11-01 13:03:03 +01:00
Dominik Reh
92a08205d0 Remove unnecessary path check
Simplifies getting the tag base path. Also fixes #55
2022-10-31 11:04:36 +01:00
DominikDoom
372a499615 Merge pull request #52 from Kinsmir/main 2022-10-30 17:14:10 +01:00
Dominik Reh
ca717948a4 Add config & UI option for appending commas
Closes #49
2022-10-30 17:10:31 +01:00
Dominik Reh
6c6999d5f1 Fix diff check for negative tag count changes
Now properly closes the popup if the last letter of a tag gets deleted.
2022-10-30 16:10:55 +01:00
Joris Neuteboom
f7f5101f62 Removed wildcard comments
https://github.com/Klokinator/UnivAICharGen uses # for comments within the wildcards
2022-10-30 16:10:38 +01:00
Dominik Reh
e49862d422 Fix Regex for non-ascii tags
Also separates the regex for simplification. Fixes #51.
2022-10-30 16:08:13 +01:00
3 changed files with 74 additions and 46 deletions

View File

@@ -1,5 +1,6 @@
var acConfig = null;
var acActive = true;
var acAppendComma = false;
// Style for new elements. Gets appended to the Gradio root.
let autocompleteCSS_dark = `
@@ -185,7 +186,7 @@ function createResultsDiv(textArea) {
}
// Create the checkbox to enable/disable autocomplete
function createCheckbox() {
function createCheckbox(text) {
let label = document.createElement("label");
let input = document.createElement("input");
let span = document.createElement("span");
@@ -196,7 +197,7 @@ function createCheckbox() {
input.setAttribute('class', 'gr-check-radio gr-checkbox')
span.setAttribute('class', 'ml-2');
span.textContent = "Enable Autocomplete";
span.textContent = text;
label.appendChild(input);
label.appendChild(span);
@@ -229,7 +230,8 @@ function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
const TAG_REGEX = /[([]([^,()[\]:| ]+)(?::(?:\d+(?:\.\d+)?|\.\d+))?[)\]]|((?:\b|<)[^,|\n\r ]*(?:>|\b)?)/g
const WEIGHT_REGEX = /[([]([^,()[\]:| ]+)(?::(?:\d+(?:\.\d+)?|\.\d+))?[)\]]/g;
const TAG_REGEX = /([^\s,|]+)/g
let hideBlocked = false;
// On click, insert the tag into the prompt textbox with respect to the cursor position
@@ -269,7 +271,7 @@ function insertTextAtCursor(textArea, result, tagword) {
let afterInsertCursorPos = editStart + match.index + sanitizedText.length;
var optionalComma = "";
if (tagType !== "wildcardFile") {
if (acAppendComma && tagType !== "wildcardFile") {
optionalComma = surrounding.match(new RegExp(`${escapeRegExp(tagword)}[,:]`, "i")) !== null ? "" : ", ";
}
@@ -287,9 +289,13 @@ function insertTextAtCursor(textArea, result, tagword) {
textArea.dispatchEvent(new Event("input", { bubbles: true }));
// Update previous tags with the edited prompt to prevent re-searching the same term
let tags = [...newPrompt.matchAll(TAG_REGEX)]
.filter(x => x[2] !== undefined ? x[2].length > 0 : x)
.flatMap(x => x[1] || x[2])
let weightedTags = [...newPrompt.matchAll(WEIGHT_REGEX)]
.map(match => match[1]);
let tags = newPrompt.match(TAG_REGEX)
if (weightedTags !== null) {
tags = tags.filter(tag => !weightedTags.some(weighted => tag.includes(weighted)))
.concat(weightedTags);
}
previousTags = tags;
// Hide results after inserting
@@ -396,15 +402,21 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
if (fixedTag === null) {
// Match tags with RegEx to get the last edited one
// We also match for the weighting format (e.g. "tag:1.0") here, and flatMap it to just the tag part if it exists
let tags = [...prompt.matchAll(TAG_REGEX)]
.filter(x => x[2] !== undefined ? x[2].length > 0 : x)
.flatMap(x => x[1] || x[2])
let diff = difference(tags, previousTags)
// We also match for the weighting format (e.g. "tag:1.0") here, and combine the two to get the full tag word set
let weightedTags = [...prompt.matchAll(WEIGHT_REGEX)]
.map(match => match[1]);
let tags = prompt.match(TAG_REGEX)
if (weightedTags !== null) {
tags = tags.filter(tag => !weightedTags.some(weighted => tag.includes(weighted)))
.concat(weightedTags);
}
let tagCountChange = tags.length - previousTags.length;
let diff = difference(tags, previousTags);
previousTags = tags;
// Guard for no difference / only whitespace remaining
if (diff === null || diff.length === 0) {
// Guard for no difference / only whitespace remaining / last edited tag was fully removed
if (diff === null || diff.length === 0 || (diff.length === 1 && tagCountChange < 0)) {
if (!hideBlocked) hideResults(textArea);
return;
}
@@ -436,8 +448,8 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
else // Look in extensions wildcard files
wcPair = wildcardExtFiles.find(x => x[1].toLowerCase() === wcFile);
let wildcards = (await readFile(`file/${wcPair[0]}/${wcPair[1]}.txt`)).split("\n")
.filter(x => x.trim().length > 0); // Remove empty lines
let wildcards = (await readFile(`file/${wcPair[0]}/${wcPair[1]}.txt?${new Date().getTime()}`)).split("\n")
.filter(x => x.trim().length > 0 && !x.startsWith('#')); // Remove empty lines and comments
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
@@ -574,15 +586,15 @@ function navigateInList(textArea, event) {
event.stopPropagation();
}
var styleAdded = false;
onUiUpdate(async function () {
// One-time setup
document.addEventListener("DOMContentLoaded", async () => {
// Get our tag base path from the temp file
let tagBasePath = await readFile("file/tmp/tagAutocompletePath.txt");
let tagBasePath = await readFile(`file/tmp/tagAutocompletePath.txt?${new Date().getTime()}`);
// Load config
if (acConfig === null) {
try {
acConfig = JSON.parse(await readFile(`file/${tagBasePath}/config.json`));
acConfig = JSON.parse(await readFile(`file/${tagBasePath}/config.json?${new Date().getTime()}`));
if (acConfig.translation.onlyShowTranslation) {
acConfig.translation.searchByTranslation = true; // if only show translation, enable search by translation is necessary
}
@@ -594,7 +606,7 @@ onUiUpdate(async function () {
// Load main tags and translations
if (allTags.length === 0) {
try {
allTags = await loadCSV(`file/${tagBasePath}/${acConfig.tagFile}`);
allTags = await loadCSV(`file/${tagBasePath}/${acConfig.tagFile}?${new Date().getTime()}`);
} catch (e) {
console.error("Error loading tags file: " + e);
return;
@@ -628,16 +640,16 @@ onUiUpdate(async function () {
}
}
// Load wildcards
if (wildcardFiles.length === 0 && acConfig.useWildcards) {
if (acConfig.useWildcards && wildcardFiles.length === 0) {
try {
let wcFileArr = (await readFile(`file/${tagBasePath}/temp/wc.txt`)).split("\n");
let wcFileArr = (await readFile(`file/${tagBasePath}/temp/wc.txt?${new Date().getTime()}`)).split("\n");
let wcBasePath = wcFileArr[0].trim(); // First line should be the base path
wildcardFiles = wcFileArr.slice(1)
.filter(x => x.trim().length > 0) // Remove empty lines
.map(x => [wcBasePath, x.trim().replace(".txt", "")]); // Remove file extension & newlines
// To support multiple sources, we need to separate them using the provided "-----" strings
let wcExtFileArr = (await readFile(`file/${tagBasePath}/temp/wce.txt`)).split("\n");
let wcExtFileArr = (await readFile(`file/${tagBasePath}/temp/wce.txt?${new Date().getTime()}`)).split("\n");
let splitIndices = [];
for (let index = 0; index < wcExtFileArr.length; index++) {
if (wcExtFileArr[index].trim() === "-----") {
@@ -664,9 +676,9 @@ onUiUpdate(async function () {
}
}
// Load embeddings
if (embeddings.length === 0 && acConfig.useEmbeddings) {
if (acConfig.useEmbeddings && embeddings.length === 0) {
try {
embeddings = (await readFile(`file/${tagBasePath}/temp/emb.txt`)).split("\n")
embeddings = (await readFile(`file/${tagBasePath}/temp/emb.txt?${new Date().getTime()}`)).split("\n")
.filter(x => x.trim().length > 0) // Remove empty lines
.map(x => x.replace(".bin", "").replace(".pt", "").replace(".png", "")); // Remove file extensions
} catch (e) {
@@ -695,7 +707,6 @@ onUiUpdate(async function () {
}
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"))
@@ -724,17 +735,42 @@ onUiUpdate(async function () {
}
});
if (gradioApp().querySelector("#acActiveCheckbox") === null) {
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 cb = createCheckbox();
cb.querySelector("input").checked = acActive;
cb.querySelector("input").addEventListener("change", (e) => {
let cbActive = createCheckbox("Enable Autocomplete");
cbActive.querySelector("input").checked = acActive;
cbActive.querySelector("input").addEventListener("change", (e) => {
acActive = e.target.checked;
});
quicksettings.parentNode.insertBefore(cb, quicksettings.nextSibling);
}
// Add comma switch
let cbComma = createCheckbox("Append commas");
cbComma.querySelector("input").checked = acAppendComma;
cbComma.querySelector("input").addEventListener("change", (e) => {
acAppendComma = e.target.checked;
});
if (styleAdded) return;
// Add options to optionsDiv
optionsInner.appendChild(cbActive);
optionsInner.appendChild(cbComma);
optionsDiv.appendChild(optionsInner);
// Add options div to DOM
quicksettings.parentNode.insertBefore(optionsDiv, quicksettings.nextSibling);
}
// Add style to dom
let acStyle = document.createElement('style');
@@ -746,4 +782,4 @@ onUiUpdate(async function () {
}
gradioApp().appendChild(acStyle);
styleAdded = true;
});
});

View File

@@ -11,17 +11,7 @@ FILE_DIR = Path().absolute()
EXT_PATH = FILE_DIR.joinpath('extensions')
# Tags base path
def get_tags_base_path():
script_path = Path(scripts.basedir())
if (script_path.is_relative_to(EXT_PATH)):
return script_path.joinpath('tags')
else:
return FILE_DIR.joinpath('tags')
TAGS_PATH = get_tags_base_path()
TAGS_PATH = Path(scripts.basedir()).joinpath('tags')
# The path to the folder containing the wildcards and embeddings
WILDCARD_PATH = FILE_DIR.joinpath('scripts/wildcards')

View File

@@ -5,6 +5,7 @@
"img2img": true,
"negativePrompts": true
},
"hideUIOptions": false,
"maxResults": 5,
"resultStepLength": 500,
"delayTime": 100,
@@ -12,6 +13,7 @@
"useLeftRightArrowKeys": false,
"replaceUnderscores": true,
"escapeParentheses": true,
"appendComma": true,
"useWildcards": true,
"useEmbeddings": true,
"translation": {