Compare commits

..

4 Commits
2.5.1 ... 2.6.0

Author SHA1 Message Date
DominikDoom
d7075c5468 Update README.md
Added info for new setting about partial wildcard completion
2023-06-20 18:50:39 +02:00
DominikDoom
4923c8a177 Add first-difference based wildcard completion
along with an option to choose the wanted mode
2023-06-20 18:22:08 +02:00
DominikDoom
9632909f72 Add safeguards so a wrong translation file format setting doesn't break the script
This should also prevent the issue in #189
2023-06-20 10:00:28 +02:00
DominikDoom
7be3066d77 Partial wildcard completion & color coding for subfolders
As proposed in #190
2023-06-20 09:58:57 +02:00
3 changed files with 87 additions and 5 deletions

View File

@@ -285,6 +285,29 @@ Depending on the last setting, tag autocomplete will append a comma and space af
![insertEscape](https://github.com/DominikDoom/a1111-sd-webui-tagcomplete/assets/34448969/d28557be-6c75-43fd-bf17-c0609223b384)
</details>
<!-- Wildcard path mode -->
<details>
<summary>Wildcard path completion</summary>
Some collections of wildcards are organized in nested subfolders.
They are listed with the full path to the file, like "hair/colors/light/..." or "clothing/male/casual/..." etc.
In these cases it is often hard to type the full path manually, but you still want to narrow the selection before further scrolling in the list.
For this, a partial completion of the path can be triggered with <kbd>Tab</kbd> (or the custom hotkey for `ChooseSelectedOrFirst`) if the results to choose from are all paths.
This setting determines the mode it should use for completion:
- To next folder level:
- Completes until the next `/` in the path, preferring your selection as the way forward
- If you want to directly choose an option, <kbd>Enter</kbd> / the `ChooseSelected` hotkey will skip it and fully complete.
- To first difference:
- Completes until the first difference in the results and waits for additional info
- E.g. if you have "/sub/folder_a/..." and "/sub/folder_b/...", completing after typing "su" will insert everything up to "/sub/folder_" and stop there until you type a or b to clarify.
- If you selected something with the arrow keys (regardless of pressing Enter or Tab), it will skip it and fully complete.
- Always fully:
- As the name suggests, disables the partial completion behavior and inserts the full path under all circumstances like with normal tags.
![insertWildcardPath](https://github.com/DominikDoom/a1111-sd-webui-tagcomplete/assets/34448969/ed354bd1-3f23-4fb1-a638-ac3b7a213fc5)
</details>
<!-- Alias -->
<details>
<summary>Alias settings</summary>
@@ -495,4 +518,4 @@ to force-reload the site without cache if e.g. a new feature doesn't appear for
[multidiffusion-url]: https://github.com/pkuliyi2015/multidiffusion-upscaler-for-automatic1111
[tag-editor-url]: https://github.com/toshiaki1729/stable-diffusion-webui-dataset-tag-editor
[wd-tagger-url]: https://github.com/toriato/stable-diffusion-webui-wd14-tagger
[umi-url]: https://github.com/Klokinator/Umi-AI
[umi-url]: https://github.com/Klokinator/Umi-AI

View File

@@ -111,6 +111,15 @@ const autocompleteCSS = `
font-size: 1rem;
color: var(--live-translation-rt);
}
.acListItem .acPathPart:nth-child(3n+1) {
color: var(--live-translation-color-1);
}
.acListItem .acPathPart:nth-child(3n+2) {
color: var(--live-translation-color-2);
}
.acListItem .acPathPart:nth-child(3n+3) {
color: var(--live-translation-color-3);
}
`;
async function loadTags(c) {
@@ -146,10 +155,12 @@ async function loadTranslations(c) {
try {
let tArray = await loadCSV(`${tagBasePath}/${c.translation.translationFile}`);
tArray.forEach(t => {
if (c.translation.oldFormat)
if (c.translation.oldFormat && t[2]) // if 2 doesn't exist, it's probably a new format file and the setting is on by mistake
translations.set(t[0], t[2]);
else
else if (t[1])
translations.set(t[0], t[1]);
else
translations.set(t[0], "Not found");
});
} catch (e) {
console.error("Error loading translations file: " + e);
@@ -188,6 +199,7 @@ async function syncOptions() {
replaceUnderscores: opts["tac_replaceUnderscores"],
escapeParentheses: opts["tac_escapeParentheses"],
appendComma: opts["tac_appendComma"],
wildcardCompletionMode: opts["tac_wildcardCompletionMode"],
// Alias settings
alias: {
searchByAlias: opts["tac_alias.searchByAlias"],
@@ -336,7 +348,7 @@ const RUBY_TAG_REGEX = /[\w\d<][\w\d' \-?!/$%]{2,}>?/g;
const TAG_REGEX = new RegExp(`${POINTY_REGEX.source}|${COMPLETED_WILDCARD_REGEX.source}|${NORMAL_TAG_REGEX.source}`, "g");
// On click, insert the tag into the prompt textbox with respect to the cursor position
async function insertTextAtCursor(textArea, result, tagword) {
async function insertTextAtCursor(textArea, result, tagword, tabCompletedWithoutChoice = false) {
let text = result.text;
let tagType = result.type;
@@ -360,6 +372,40 @@ async function insertTextAtCursor(textArea, result, tagword) {
}
}
if (tagType === ResultType.wildcardFile
&& tabCompletedWithoutChoice
&& TAC_CFG.wildcardCompletionMode !== "Always fully"
&& sanitizedText.includes("/")) {
if (TAC_CFG.wildcardCompletionMode === "To next folder level") {
let regexMatch = sanitizedText.match(new RegExp(`${escapeRegExp(tagword)}([^/]*\\/?)`, "i"));
if (regexMatch) {
let pathPart = regexMatch[0];
// In case the completion would have just added a slash, try again one level deeper
if (pathPart === `${tagword}/`) {
pathPart = sanitizedText.match(new RegExp(`${escapeRegExp(tagword)}\\/([^/]*\\/?)`, "i"))[0];
}
sanitizedText = pathPart;
}
} else if (TAC_CFG.wildcardCompletionMode === "To first difference") {
let firstDifference = 0;
let longestResult = results.map(x => x.text.length).reduce((a, b) => Math.max(a, b));
// Compare the results to each other to find the first point where they differ
for (let i = 0; i < longestResult; i++) {
let char = results[0].text[i];
if (results.every(x => x.text[i] === char)) {
firstDifference++;
} else {
break;
}
}
// Don't cut off the __ at the end if it is already the full path
if (firstDifference < longestResult) {
// +2 because the sanitized text already has the __ at the start but the matched text doesn't
sanitizedText = sanitizedText.substring(0, firstDifference + 2);
}
}
}
var prompt = textArea.value;
// Edit prompt text
@@ -488,6 +534,14 @@ function addResultsToList(textArea, results, tagword, resetList) {
// Print search term bolded in result
itemText.innerHTML = displayText.replace(tagword, `<b>${tagword}</b>`);
if (result.type === ResultType.wildcardFile && itemText.innerHTML.includes("/")) {
let parts = itemText.innerHTML.split("/");
let lastPart = parts[parts.length - 1];
parts = parts.slice(0, parts.length - 1);
itemText.innerHTML = "<span class='acPathPart'>" + parts.join("</span><span class='acPathPart'>/") + "</span>" + "/" + lastPart;
}
// Add wiki link if the setting is enabled and a supported tag set loaded
if (TAC_CFG.showWikiLinks
&& (result.type === ResultType.tag)
@@ -936,10 +990,14 @@ function navigateInList(textArea, event) {
}
break;
case keys["ChooseFirstOrSelected"]:
let withoutChoice = false;
if (selectedTag === null) {
selectedTag = 0;
withoutChoice = true;
} else if (TAC_CFG.wildcardCompletionMode === "To next folder level") {
withoutChoice = true;
}
insertTextAtCursor(textArea, results[selectedTag], tagword);
insertTextAtCursor(textArea, results[selectedTag], tagword, withoutChoice);
break;
case keys["Close"]:
hideResults(textArea);

View File

@@ -318,6 +318,7 @@ def on_ui_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))
shared.opts.add_option("tac_wildcardCompletionMode", shared.OptionInfo("To next folder level", "How to complete nested wildcard paths (e.g. \"hair/colours/light/...\")", gr.Dropdown, lambda: {"choices": ["To next folder level","To first difference","Always fully"]}, 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))