mirror of
https://github.com/DominikDoom/a1111-sd-webui-tagcomplete.git
synced 2026-01-26 19:19:57 +00:00
Compare commits
10 Commits
3.3.0
...
feature-fu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ec1bc3424 | ||
|
|
cfe026f366 | ||
|
|
c594156d30 | ||
|
|
2126a5d217 | ||
|
|
a4a656ae23 | ||
|
|
c59ffed049 | ||
|
|
625298bed6 | ||
|
|
8c0c308a6f | ||
|
|
fa4a3bc036 | ||
|
|
a8f175925f |
@@ -20,6 +20,15 @@ var lycos = [];
|
||||
var modelKeywordDict = new Map();
|
||||
var chants = [];
|
||||
var styleNames = [];
|
||||
// uFuzzy haystacks
|
||||
var tacHaystacks = {
|
||||
"tag": [],
|
||||
"extra": [],
|
||||
"tagAlias": [],
|
||||
"extraAlias": [],
|
||||
"translationKeys": [],
|
||||
"translationValues": []
|
||||
}
|
||||
|
||||
// Selected model info for black/whitelisting
|
||||
var currentModelHash = "";
|
||||
|
||||
@@ -30,6 +30,9 @@ class AutocompleteResult {
|
||||
meta = null;
|
||||
hash = null;
|
||||
sortKey = null;
|
||||
// uFuzzy specific
|
||||
highlightedText = null;
|
||||
matchSource = null;
|
||||
|
||||
// Constructor
|
||||
constructor(text, type) {
|
||||
|
||||
250
javascript/_uFuzzy.js
Normal file
250
javascript/_uFuzzy.js
Normal file
File diff suppressed because one or more lines are too long
@@ -5,14 +5,17 @@ class LoraParser extends BaseTagParser {
|
||||
parse() {
|
||||
// Show lora
|
||||
let tempResults = [];
|
||||
let searchTerm = tagword;
|
||||
if (tagword !== "<" && tagword !== "<l:" && tagword !== "<lora:") {
|
||||
let searchTerm = tagword.replace("<lora:", "").replace("<l:", "").replace("<", "");
|
||||
searchTerm = tagword.replace("<lora:", "").replace("<l:", "").replace("<", "");
|
||||
let filterCondition = x => {
|
||||
let regex = new RegExp(escapeRegExp(searchTerm, true), 'i');
|
||||
return regex.test(x.toLowerCase()) || regex.test(x.toLowerCase().replaceAll(" ", "_"));
|
||||
};
|
||||
filterCondition = (x) => TacFuzzy.check(x, searchTerm);
|
||||
tempResults = loras.filter(x => filterCondition(x[0])); // Filter by tagword
|
||||
} else {
|
||||
searchTerm = null;
|
||||
tempResults = loras;
|
||||
}
|
||||
|
||||
@@ -25,9 +28,12 @@ class LoraParser extends BaseTagParser {
|
||||
let name = text.substring(lastSlash + 1, lastDot);
|
||||
|
||||
let result = new AutocompleteResult(name, ResultType.lora)
|
||||
result.highlightedText = TacFuzzy.manualHighlight(name, searchTerm);
|
||||
result.matchSource = "base";
|
||||
result.meta = "Lora";
|
||||
result.sortKey = t[1];
|
||||
result.hash = t[2];
|
||||
|
||||
finalResults.push(result);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"--live-translation-color-1": ["lightskyblue", "#2d89ef"],
|
||||
"--live-translation-color-2": ["palegoldenrod", "#eb5700"],
|
||||
"--live-translation-color-3": ["darkseagreen", "darkgreen"],
|
||||
"--match-filter": ["brightness(1.2) drop-shadow(1px 1px 6px black)", "brightness(0.8)"]
|
||||
}
|
||||
const browserVars = {
|
||||
"--results-overflow-y": {
|
||||
@@ -90,6 +91,9 @@ const autocompleteCSS = `
|
||||
content: "✨";
|
||||
margin-right: 2px;
|
||||
}
|
||||
.acMatchHighlight {
|
||||
filter: var(--match-filter);
|
||||
}
|
||||
.acWikiLink {
|
||||
padding: 0.5rem;
|
||||
margin: -0.5rem 0 -0.5rem -0.5rem;
|
||||
@@ -158,6 +162,14 @@ async function loadTags(c) {
|
||||
}
|
||||
}
|
||||
await loadExtraTags(c);
|
||||
|
||||
// Assign uFuzzy haystacks
|
||||
tacHaystacks.tag = allTags.map(x => x[0]);
|
||||
tacHaystacks.extra = extras.map(x => x[0]);
|
||||
tacHaystacks.tagAlias = allTags.map(x => x[3] || "");
|
||||
tacHaystacks.extraAlias = extras.map(e => e[3] || "");
|
||||
tacHaystacks.translationKeys = Array.from(translations.keys());
|
||||
tacHaystacks.translationValues = Array.from(translations.values());
|
||||
}
|
||||
|
||||
async function loadExtraTags(c) {
|
||||
@@ -714,7 +726,31 @@ function addResultsToList(textArea, results, tagword, resetList) {
|
||||
displayText += `[${translations.get(result.text)}]`;
|
||||
|
||||
// Print search term bolded in result
|
||||
itemText.innerHTML = displayText.replace(tagword, `<b>${tagword}</b>`);
|
||||
if (result.highlightedText) {
|
||||
switch (result.matchSource) {
|
||||
case "base":
|
||||
itemText.innerHTML = result.highlightedText;
|
||||
break;
|
||||
case "alias":
|
||||
let aliases = result.highlightedText.split(",");
|
||||
let matchingAlias = aliases.find(a => a.includes("<b class=\"acMatchHighlight\">"));
|
||||
itemText.innerHTML = matchingAlias + " ➝ " + result.text;
|
||||
break;
|
||||
case "translatedBase":
|
||||
itemText.innerHTML = `${result.text}[${result.highlightedText}]`
|
||||
break;
|
||||
case "translatedAlias":
|
||||
let tAliases = result.aliases.split(",");
|
||||
let tMatchingAlias = tAliases.find(a => a.includes("<b class=\"acMatchHighlight\">"));
|
||||
let baseTranslation = `[${translations.get(result.text)}];` || "";
|
||||
itemText.innerHTML = `${tMatchingAlias}[${result.highlightedText}] ➝ ${result.text}${baseTranslation}`;
|
||||
default:
|
||||
itemText.innerHTML = displayText;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
itemText.innerHTML = displayText;
|
||||
}
|
||||
|
||||
const splitTypes = [ResultType.wildcardFile, ResultType.yamlWildcard]
|
||||
if (splitTypes.includes(result.type) && itemText.innerHTML.includes("/")) {
|
||||
@@ -1125,6 +1161,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
||||
else
|
||||
fil = (x) => baseFilter(x);
|
||||
|
||||
/*
|
||||
// Add final results
|
||||
allTags.filter(fil).forEach(t => {
|
||||
let result = new AutocompleteResult(t[0].trim(), ResultType.tag)
|
||||
@@ -1133,25 +1170,78 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
||||
result.aliases = t[3];
|
||||
results.push(result);
|
||||
});
|
||||
*/
|
||||
|
||||
// Add extras
|
||||
if (TAC_CFG.extra.extraFile) {
|
||||
let extraResults = [];
|
||||
let tagIndexSet = new Set()
|
||||
let extraIndexSet = new Set();
|
||||
// Here we store the values instead of the index, as the same translation can apply to different keys
|
||||
// For searching, only the first result is relevant (assuming it is also the best match)
|
||||
let translatedValueSet = new Set();
|
||||
|
||||
extras.filter(fil).forEach(e => {
|
||||
let result = new AutocompleteResult(e[0].trim(), ResultType.extra)
|
||||
result.category = e[1] || 0; // If no category is given, use 0 as the default
|
||||
result.meta = e[2] || "Custom tag";
|
||||
result.aliases = e[3] || "";
|
||||
extraResults.push(result);
|
||||
});
|
||||
|
||||
if (TAC_CFG.extra.addMode === "Insert before") {
|
||||
results = extraResults.concat(results);
|
||||
} else {
|
||||
results = results.concat(extraResults);
|
||||
}
|
||||
let tagOut = [];
|
||||
let extraOut = [];
|
||||
// Base text search
|
||||
TacFuzzy.assignResults(TacFuzzy.search(tacHaystacks.tag, tagword),"base", "tag", tagIndexSet, tagOut)
|
||||
TacFuzzy.assignResults(TacFuzzy.search(tacHaystacks.extra, tagword), "base", "extra", extraIndexSet, extraOut)
|
||||
// Alias search
|
||||
if (TAC_CFG.alias.searchByAlias) {
|
||||
TacFuzzy.assignResults(TacFuzzy.search(tacHaystacks.tagAlias, tagword), "alias", "tag", tagIndexSet, tagOut)
|
||||
TacFuzzy.assignResults(TacFuzzy.search(tacHaystacks.extraAlias, tagword), "alias", "extra", extraIndexSet, extraOut)
|
||||
}
|
||||
// Translation search
|
||||
if (TAC_CFG.translation.searchByTranslation) {
|
||||
// Translations need special treatment as they can belong to both tags and extras and need lookup based on their keys.
|
||||
// We also use unicode search here, slower but needed for non-latin translations.
|
||||
TacFuzzy.search(tacHaystacks.translationValues, tagword, true).forEach(pair => {
|
||||
const idx = pair[0];
|
||||
const orderIdx = pair[1];
|
||||
const translationKey = tacHaystacks.translationKeys[idx];
|
||||
|
||||
// Placeholder to make sure we never access an index of null if no matching key was found
|
||||
const notFound = [null, null, null, null];
|
||||
|
||||
// Check if the translation belongs to a tag or its alias. Only search alias if no base text found.
|
||||
const translatedTagBase = allTags.find(t => t[0] === translationKey);
|
||||
const translatedTagAlias = !translatedTagBase
|
||||
? allTags.find(t => t[3]?.split(",").some(a => a === translationKey))
|
||||
: null;
|
||||
const translatedTag = translatedTagBase || translatedTagAlias || notFound; // Combined result for easier checks later
|
||||
// Check if the translation belongs to an extra or its alias. Only search alias if no base text found.
|
||||
const translatedExtraBase = extras.find(e => e[0] === translationKey);
|
||||
const translatedExtraAlias = !translatedExtraBase
|
||||
? extras.find(e => e[3]?.split(",").some(a => a === translationKey))
|
||||
: null;
|
||||
const translatedExtra = translatedExtraBase || translatedExtraAlias || notFound; // Combined result for easier checks later
|
||||
|
||||
// For translations, we can sadly only exit early after making sure we don't have a duplicate (which is most of the work).
|
||||
// This is a side effect of translations mapping to multiple keys for the search direction, eg. different aliases.
|
||||
if (translatedValueSet.has(translatedTag[0] || translatedExtra[0])) return;
|
||||
|
||||
const resultType = translatedTag[0] ? ResultType.tag : ResultType.extra;
|
||||
const result = new AutocompleteResult(translatedTag[0] || translatedExtra[0], resultType);
|
||||
result.highlightedText = TacFuzzy.toStr(orderIdx);
|
||||
|
||||
result.matchSource = (translatedTagBase || translatedExtraBase) ? "translatedBase" : "translatedAlias";
|
||||
result.category = translatedTag[1] || translatedExtra[1] || 0;
|
||||
|
||||
if (translatedTag[0])
|
||||
result.count = translatedTag[2] || 0;
|
||||
else if (translatedExtra[0])
|
||||
result.meta = translatedExtra[2] || "Custom tag";
|
||||
|
||||
result.aliases = translatedTag[3] || translatedExtra[3] || "";
|
||||
|
||||
if (translatedTag[0]) {
|
||||
tagOut.push(result);
|
||||
} else if (translatedExtra[0]) {
|
||||
extraOut.push(result);
|
||||
}
|
||||
translatedValueSet.add(translatedTag[0] || translatedExtra[0]);
|
||||
});
|
||||
}
|
||||
|
||||
// Append results for each set
|
||||
results = results.concat([...extraOut]).concat([...tagOut]);
|
||||
}
|
||||
|
||||
// Guard for empty results
|
||||
@@ -1173,7 +1263,7 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
|
||||
const name = r.type === ResultType.chant ? r.aliases : r.text;
|
||||
// Add to alias list or tag list depending on if the name includes the tagword
|
||||
// (the same criteria is used in the filter in calculateUsageBias)
|
||||
if (aliasTypes.includes(r.type) && !name.includes(tagword)) {
|
||||
if (aliasTypes.includes(r.type) && r.matchSource === "alias") {
|
||||
aliasNames.push(name);
|
||||
} else {
|
||||
tagNames.push(name);
|
||||
|
||||
Reference in New Issue
Block a user