Compare commits

...

10 Commits

Author SHA1 Message Date
Dominik Reh
ca8a0c433e Fix result text cutoff in Firefox
Fixes #65
2022-11-06 14:33:11 +01:00
Dominik Reh
535c2a6753 Safety checks for translations
Should prevent list getting cut off if no translation or alias matches
2022-11-06 14:16:56 +01:00
Dominik Reh
e86c604903 Fix for translations failing to match sometimes
Fixes #62 (again)
2022-11-06 14:00:34 +01:00
Dominik Reh
4eabf00f01 Remove max width for resutls
Fixes #65
2022-11-05 18:27:53 +01:00
Dominik Reh
a39b0d0742 Include deprecated danbooru tags
Since many of the deprecated tags have large post counts, it makes sense to include them, even if better alternatives are available now.
Fixes #64
2022-11-05 17:51:34 +01:00
DominikDoom
ecc71902cd Update README.md 2022-11-05 16:46:24 +01:00
DominikDoom
2dc1dfea86 Update README.md 2022-11-05 16:21:38 +01:00
Dominik Reh
18556c6115 Rework translation to be separate from aliases
Enables two-way translation where the translation is always visible.
Closes #62.
2022-11-05 15:54:26 +01:00
DominikDoom
82355cdb60 Update README.md 2022-11-04 16:10:59 +01:00
Dominik Reh
2c6b6e7f13 Count and Alias support
The data and script have been updated to include post count and common aliases for tags.
Aliases are added the same way as translations.
Closes #60.
2022-11-04 15:50:27 +01:00
5 changed files with 200257 additions and 175903 deletions

137
README.md
View File

@@ -9,33 +9,26 @@ This custom script serves as a drop-in extension for the popular [AUTOMATIC1111
It displays autocompletion hints for recognized tags from "image booru" boards such as Danbooru, which are primarily used for browsing Anime-style illustrations.
Since some Stable Diffusion models were trained using this information, for example [Waifu Diffusion](https://github.com/harubaru/waifu-diffusion), using exact tags in prompts can often improve composition and help to achieve a wanted look.
I created this script as a convenience tool since it reduces the need of switching back and forth between the web UI and a booru site to copy-paste tags.
You can either clone / download the files manually as described [below](#installation), or use a pre-packaged version from [Releases](https://github.com/DominikDoom/a1111-sd-webui-tagcomplete/releases).
## Common Problems & Known Issues:
- The browser might cache old versions of the script, config, or embedding/wildcard lists. Try hitting `CTRL+F5` to clear the cache.
- If `replaceUnderscores` is active, the script will currently only partly replace edited tags containing multiple words in brackets.
- The browser might cache old versions of the script, config, or embedding/wildcard lists. Try hitting `CTRL+F5` to clear the cache if you have issues.
- If `replaceUnderscores` is active, the script will currently only partially replace edited tags containing multiple words in brackets.
For example, editing `atago (azur lane)`, it would be replaced with e.g. `taihou (azur lane), lane)`, since the script currently doesn't see the second part of the bracket as the same tag. So in those cases you should delete the old tag beforehand.
### Wildcard & Embedding support
Autocompletion also works with wildcard files used by [this script](https://github.com/jtkelm2/stable-diffusion-webui-1/blob/master/scripts/wildcards.py) of the same name (demo video further down). This enables you to either insert categories to be replaced by the script, or even replace them with the actual wildcard file content in the same step. Wildcards are searched for in every extension folder as well as the `scripts/wildcards` folder to support legacy versions. This means that you can combine wildcards from multiple extensions. Nested folders are also supported if you have grouped your wildcards in that way.
It also scans the embeddings folder and displays completion hints for the names of all .pt and .bin files inside if you start typing `<`. Note that some normal tags also use < in Kaomoji (like ">_<" for example), so the results will contain both.
## Screenshots
Demo video (with keyboard navigation):
https://user-images.githubusercontent.com/34448969/195344430-2b5f9945-b98b-4943-9fbc-82cf633321b1.mp4
https://user-images.githubusercontent.com/34448969/200128020-10d9a8b2-cea6-4e3f-bcd2-8c40c8c73233.mp4
Wildcard script support:
https://user-images.githubusercontent.com/34448969/195632461-49d226ae-d393-453d-8f04-1e44b073234c.mp4
https://user-images.githubusercontent.com/34448969/200128031-22dd7c33-71d1-464f-ae36-5f6c8fd49df0.mp4
Dark and Light mode supported, including tag colors:
![tagtypes](https://user-images.githubusercontent.com/34448969/195177127-f63949f8-271d-4767-bccd-f1b5e818a7f8.png)
![tagtypes_light](https://user-images.githubusercontent.com/34448969/195180061-ceebcc25-9e4c-424f-b0c9-ba8e8f4f17f4.png)
![results_dark](https://user-images.githubusercontent.com/34448969/200128214-3b6f21b4-9dda-4acf-820e-5df0285c30d6.png)
![results_light](https://user-images.githubusercontent.com/34448969/200128217-bfac8b60-6673-447b-90fd-dc6326f1618c.png)
## Installation
### As an extension (recommended)
@@ -58,6 +51,11 @@ After scanning for embeddings and wildcards, the script will also create a `temp
### Important:
The script needs **all three folders** to work properly.
## Wildcard & Embedding support
Autocompletion also works with wildcard files used by [this script](https://github.com/jtkelm2/stable-diffusion-webui-1/blob/master/scripts/wildcards.py) of the same name or other similar scripts/extensions. This enables you to either insert categories to be replaced by the script, or even replace them with the actual wildcard file content in the same step. Wildcards are searched for in every extension folder as well as the `scripts/wildcards` folder to support legacy versions. This means that you can combine wildcards from multiple extensions. Nested folders are also supported if you have grouped your wildcards in that way.
It also scans the embeddings folder and displays completion hints for the names of all .pt and .bin files inside if you start typing `<`. Note that some normal tags also use < in Kaomoji (like ">_<" for example), so the results will contain both.
## Config
The config contains the following settings and defaults:
```json
@@ -68,24 +66,33 @@ The config contains the following settings and defaults:
"img2img": true,
"negativePrompts": true
},
"hideUIOptions": false,
"maxResults": 5,
"resultStepLength": 500,
"delayTime": 100,
"showAllResults": false,
"useLeftRightArrowKeys": false,
"replaceUnderscores": true,
"escapeParentheses": true,
"appendComma": true,
"useWildcards": true,
"useEmbeddings": true,
"alias": {
"searchByAlias": true,
"onlyShowAlias": false
},
"translation": {
"searchByTranslation": true,
"onlyShowTranslation": false
"translationFile": "",
"oldFormat": false,
"searchByTranslation": true
},
"extra": {
"extraFile": "",
"onlyTranslationExtraFile": false
"onlyAliasExtraFile": false
},
"colors": {
"danbooru": {
"-1": ["red", "maroon"],
"0": ["lightblue", "dodgerblue"],
"1": ["indianred", "firebrick"],
"3": ["violet", "darkorchid"],
@@ -110,74 +117,92 @@ The config contains the following settings and defaults:
|---------|-------------|
| tagFile | Specifies the tag file to use. You can provide a custom tag database of your liking, but since the script was developed with Danbooru tags in mind, it might not work properly with other configurations.|
| activeIn | Allows to selectively (de)activate the script for txt2img, img2img, and the negative prompts for both. |
| hideUIOptions | Allows to hide the added GUI options at the top of the page to adjust active and comma settings without restarting. |
| maxResults | How many results to show max. For the default tag set, the results are ordered by occurence count. For embeddings and wildcards it will show all results in a scrollable list. |
| resultStepLength | Allows to load results in smaller batches of the specified size for better performance in long lists or if showAllResults is true. |
| delayTime | Specifies how much to wait in milliseconds before triggering autocomplete. Helps prevent too frequent updates while typing. |
| showAllResults | If true, will ignore maxResults and show all results in a scrollable list. **Warning:** can lag your browser for long lists. |
| useLeftRightArrowKeys | If true, left and right arrows will select the first/last result in the popup instead of moving the cursor in the textbox. |
| replaceUnderscores | If true, undescores are replaced with spaces on clicking a tag. Might work better for some models. |
| escapeParentheses | If true, escapes tags containing () so they don't contribute to the web UI's prompt weighting functionality. |
| appendComma | Specifies the starting value of the "Append commas" UI switch. If UI options are disabled, this will always be used. |
| useWildcards | Used to toggle the wildcard completion functionality. |
| useEmbeddings | Used to toggle the embedding completion functionality. |
| translation | Options for translating tags. More info in the section below. |
| extras | Options for additional tag files / translations. More info in the section below. |
| alias | Options for aliases. More info in the section below. |
| translation | Options for translations. More info in the section below. |
| extras | Options for additional tag files / aliases / translations. More info in the section below. |
| colors | Contains customizable colors for the tag types, you can add new ones here for custom tag files (same name as filename, without the .csv). The first value is for dark, the second for light mode. Color names and hex codes should both work.|
### Translations & Extra tags
With the recent update it is now possible to add translations to the tags. These will be searchable / shown according to the settings in `config.json`:
- `searchByTranslation` - Whether to search for the translated term as well or only the English tag.
- `onlyShowTranslation` - Replaces the English tag with its translation if it has one. Only for displaying, the inserted text at the end is still the English tag.
### Aliases, Translations & Extra tags
#### Aliases
Like on Booru sites, tags can have one or multiple aliases which redirect to the actual value on completion. These will be searchable / shown according to the settings in `config.json`:
- `searchByAlias` - Whether to also search for the alias or only the actual tag.
- `onlyShowAlias` - Shows only the alias instead of `alias -> actual`. Only for displaying, the inserted text at the end is still the actual tag.
Example with full and partial chinese tag sets:
#### Translations
An additional file can be added in the translation section, which will be used to translate both tags and aliases and also enables searching by translation.
This file needs to be a CSV in the format `<English tag/alias>,<Translation>`, but for backwards compatibility with older extra files that used a three column format, you can turn on `oldFormat` to use that instead.
![translation](https://user-images.githubusercontent.com/34448969/196175839-8aaacb26-5c90-48e3-be65-647a0b444ead.png)
![translation_mixed](https://user-images.githubusercontent.com/34448969/196176233-76d4cb5f-16cf-4800-a69b-adb64a79ca8b.png)
Example with chinese translation:
Translations can be added in multiple ways, which is where the "Extra" file comes into play.
1. Directly in the main tag file. Simply add a third value, separated by comma, containing the translation for the tag in that row.
2. As an extra file containing only the translated tag rows (so still including the english Tag name and tag type). Will be matched to the English tags in the main file based on the name & type, so might be slow for large translation files.
3. As an extra file with `onlyTranslationExtraFile` true. With this configuration, the extra file has to include *only* the translation itself. That means it is purely index based, assigning the translations to the main tags is really fast but also needs the lines to match (including empty lines). If the order or amount in the main file changes, the translations will potentially not match anymore.
![IME-input](https://user-images.githubusercontent.com/34448969/200126551-2264e9cc-abb2-4450-9afa-43f362a77ab0.png)
![english-input](https://user-images.githubusercontent.com/34448969/200126513-bf6b3940-6e22-41b0-a369-f2b4640f87d6.png)
**Important**
As of a recent update, translations added in the old Extra file way will only work as an alias and not be visible anymore if typing the English tag for that translation.
#### Extra file
Aliases can be added in multiple ways, which is where the "Extra" file comes into play.
1. As an extra file containing tag, category, optional count and the new alias. Will be matched to the English tags in the main file based on the name & type, so might be slow for large files.
2. As an extra file with `onlyAliasExtraFile` true. With this configuration, the extra file has to include *only* the alias itself. That means it is purely index based, assigning the aliases to the main tags is really fast but also needs the lines to match (including empty lines). If the order or amount in the main file changes, the translations will potentially not match anymore. Not recommended.
So your CSV values would look like this for each method:
| | 1 | 2 | 3 |
|------------|---------------------|--------------------|---------------|
| Main file | `tag,0,translation` | `tag,0` | `tag,0` |
| Extra file | - | `tag,0,translation`| `translation` |
| | 1 | 2 |
|------------|--------------------------|--------------------------|
| Main file | `tag,type,count,(alias)` | `tag,type,count,(alias)` |
| Extra file | `tag,type,(count),alias` | `alias` |
Methods 1 & 2 can also be mixed, in which case translations in the extra file will have priority over those in the main file if they translate the same tag.
Count in the extra file is optional, since there isn't always a post count for custom tag sets.
The extra files can also be used to just add new / custom tags not included in the main set, provided `onlyTranslationExtraFile` is false.
If an extra tag doesn't match any existing tag, it will be added to the list as a new tag instead.
The extra files can also be used to just add new / custom tags not included in the main set, provided `onlyAliasExtraFile` is false.
If an extra tag doesn't match any existing tag, it will be added to the list as a new tag instead. For this, it will need to include the post count and alias columns even if they don't contain anything, so it could be in the form of `tag,type,,`.
## CSV tag data
The script expects a CSV file with tags saved in the following way:
```csv
1girl,0
solo,0
highres,5
long_hair,0
<name>,<type>,<postCount>,"<aliases>"
```
Notably, it does not expect column names in the first row.
The first value needs to be the tag name, while the second value specifies the tag type. An optional third value will be interpreted as a translation as described in the section above.
Example:
```csv
1girl,0,4114588,"1girls,sole_female"
solo,0,3426446,"female_solo,solo_female"
highres,5,3008413,"high_res,high_resolution,hires"
long_hair,0,2898315,longhair
commentary_request,5,2610959,
```
Notably, it does not expect column names in the first row and both count and aliases are technically optional,
although count is always included in the default data. Multiple aliases need to be comma separated as well, but encased in string quotes to not break the CSV parsing.
The numbering system follows the [tag API docs](https://danbooru.donmai.us/wiki_pages/api%3Atags) of Danbooru:
| Value | Description |
|-------|-------------|
|0 | General |
|1 | Artist |
|3 | Copyright |
|4 | Character |
|5 | Meta |
|0 | General |
|1 | Artist |
|3 | Copyright |
|4 | Character |
|5 | Meta |
or of e621:
| Value | Description |
|-------|-------------|
|-1 | Invalid |
|0 | General |
|1 | Artist |
|3 | Copyright |
|4 | Character |
|5 | Species |
|6 | Invalid |
|7 | Meta |
|8 | Lore |
|-1 | Invalid |
|0 | General |
|1 | Artist |
|3 | Copyright |
|4 | Character |
|5 | Species |
|6 | Invalid |
|7 | Meta |
|8 | Lore |
The tag type is used for coloring entries in the result list.

View File

@@ -12,6 +12,7 @@ let autocompleteCSS_dark = `
border: 1px solid #4b5563 !important;
border-radius: 12px !important;
overflow-y: auto;
scrollbar-gutter: stable;
}
.autocompleteResultsList > li:nth-child(odd) {
background-color: #111827;
@@ -27,6 +28,20 @@ let autocompleteCSS_dark = `
.autocompleteResultsList > li.selected {
background-color: #374151;
}
.resultsFlexContainer {
display: flex;
}
.acListItem {
overflow: hidden;
white-space: nowrap;
}
.acPostCount {
position: relative;
text-align: end;
padding: 0 0 0 15px;
flex-grow: 1;
color: #6b6f7b;
}
`;
let autocompleteCSS_light = `
.autocompleteResults {
@@ -37,6 +52,7 @@ let autocompleteCSS_light = `
border: 1.5px solid #e5e7eb !important;
border-radius: 12px !important;
overflow-y: auto;
scrollbar-gutter: stable;
}
.autocompleteResultsList > li:nth-child(odd) {
background-color: #f9fafb;
@@ -52,6 +68,20 @@ let autocompleteCSS_light = `
.autocompleteResultsList > li.selected {
background-color: #e5e7eb;
}
.resultsFlexContainer {
display: flex;
}
.acListItem {
overflow: hidden;
white-space: nowrap;
}
.acPostCount {
position: relative;
text-align: end;
padding: 0 0 0 15px;
flex-grow: 1;
color: #a2a9b4;
}
`;
// Parse the CSV file into a 2D array. Doesn't use regex, so it is very lightweight.
@@ -229,6 +259,11 @@ function hideResults(textArea) {
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
function escapeHTML(unsafeText) {
let div = document.createElement('div');
div.textContent = unsafeText;
return div.innerHTML;
}
const WEIGHT_REGEX = /[([]([^,()[\]:| ]+)(?::(?:\d+(?:\.\d+)?|\.\d+))?[)\]]/g;
const TAG_REGEX = /([^\s,|]+)/g
@@ -332,25 +367,84 @@ function addResultsToList(textArea, results, tagword, resetList) {
let result = results[i];
let li = document.createElement("li");
//suppost only show the translation to result
if (result[2]) {
li.textContent = result[2];
if (!acConfig.translation.onlyShowTranslation) {
li.textContent += " >> " + result[0];
let flexDiv = document.createElement("div");
flexDiv.classList.add("resultsFlexContainer");
li.appendChild(flexDiv);
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
if (result[3] && !result[0].includes(tagword)) { // Alias
let splitAliases = result[3].split(",");
let bestAlias = splitAliases.find(a => a.toLowerCase().includes(tagword));
// search in translations if no alias matches
if (!bestAlias) {
let tagOrAlias = pair => pair[0] === result[0] || result[3].split(",").includes(pair[0]);
var tArray = [...translations];
if (tArray) {
var translationKey = [...translations].find(pair => tagOrAlias(pair) && pair[1].includes(tagword));
if (translationKey)
bestAlias = translationKey[0];
}
}
} else {
li.textContent = result[0];
displayText = escapeHTML(bestAlias);
// Append translation for alias if it exists and is not what the user typed
if (translations.has(bestAlias) && translations.get(bestAlias) !== bestAlias && bestAlias !== result[0])
displayText += `[${translations.get(bestAlias)}]`;
if (!acConfig.alias.onlyShowAlias && result[0] !== bestAlias)
displayText += " ➝ " + result[0];
} else { // No alias
displayText = escapeHTML(result[0]);
}
// Append translation for result if it exists
if (translations.has(result[0]))
displayText += `[${translations.get(result[0])}]`;
// Print search term bolded in result
itemText.innerHTML = displayText.replace(tagword, `<b>${tagword}</b>`);
// Add post count & color if it's a tag
// Wildcards & Embeds have no tag type
if (!result[1].startsWith("wildcard") && result[1] !== "embedding") {
// Set the color of the tag
let tagType = result[1];
let colorGroup = tagColors[tagFileName];
// Default to danbooru scheme if no matching one is found
if (colorGroup === undefined) colorGroup = tagColors["danbooru"];
if (!colorGroup)
colorGroup = tagColors["danbooru"];
li.style = `color: ${colorGroup[tagType][mode]};`;
// Set tag type to invalid if not found
if (!colorGroup[tagType])
tagType = "-1";
itemText.style = `color: ${colorGroup[tagType][mode]};`;
// Post count
if (result[2] && !isNaN(result[2])) {
let postCount = result[2];
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("acPostCount");
flexDiv.appendChild(countDiv);
}
}
// Add listener
@@ -387,6 +481,7 @@ var wildcardFiles = [];
var wildcardExtFiles = [];
var embeddings = [];
var allTags = [];
var translations = new Map();
var results = [];
var tagword = "";
var resultCount = 0;
@@ -475,17 +570,29 @@ async function autocomplete(textArea, prompt, fixedTag = null) {
genericResults = allTags.filter(x => x[0].toLowerCase().includes(tagword)).slice(0, acConfig.maxResults);
results = genericResults.concat(tempResults.map(x => ["Embeddings: " + x.trim(), "embedding"])); // Mark as embedding
} else {
if (acConfig.translation.searchByTranslation) {
results = allTags.filter(x => x[2] && x[2].toLowerCase().includes(tagword)); // check have translation
// if search by [a~z],first list the translations, and then search English if it is not enough
// if only show translation,it is unnecessary to list English results
if (!acConfig.translation.onlyShowTranslation) {
results = results.concat(allTags.filter(x => x[0].toLowerCase().includes(tagword) && !results.includes(x)));
}
// If onlyShowAlias is enabled, we don't need to include normal results
if (acConfig.alias.onlyShowAlias) {
results = allTags.filter(x => x[3] && x[3].toLowerCase().includes(tagword));
} else {
results = allTags.filter(x => x[0].toLowerCase().includes(tagword));
// Else both normal tags and aliases/translations are included depending on the config
let baseFilter = (x) => x[0].toLowerCase().includes(tagword);
let aliasFilter = (x) => x[3] && x[3].toLowerCase().includes(tagword);
let translationFilter = (x) => (translations.has(x[0]) && translations.get(x[0]).toLowerCase().includes(tagword))
|| x[3] && x[3].split(",").some(y => translations.has(y) && translations.get(y).toLowerCase().includes(tagword));
let fil;
if (acConfig.alias.searchByAlias && acConfig.translation.searchByTranslation)
fil = (x) => baseFilter(x) || aliasFilter(x) || translationFilter(x);
else if (acConfig.alias.searchByAlias && !acConfig.translation.searchByTranslation)
fil = (x) => baseFilter(x) || aliasFilter(x);
else if (acConfig.translation.searchByTranslation && !acConfig.alias.searchByAlias)
fil = (x) => baseFilter(x) || translationFilter(x);
else
fil = (x) => baseFilter(x);
results = allTags.filter(fil);
}
// it's good to show all results
// Slice if the user has set a max result count
if (!acConfig.showAllResults) {
results = results.slice(0, acConfig.maxResults);
}
@@ -595,15 +702,15 @@ document.addEventListener("DOMContentLoaded", async () => {
if (acConfig === null) {
try {
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
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 main tags and translations
// Load main tags and aliases
if (allTags.length === 0) {
try {
allTags = await loadCSV(`file/${tagBasePath}/${acConfig.tagFile}?${new Date().getTime()}`);
@@ -613,32 +720,57 @@ document.addEventListener("DOMContentLoaded", async () => {
}
if (acConfig.extra.extraFile) {
try {
extras = await loadCSV(`file/${tagBasePath}/${acConfig.extra.extraFile}`);
if (acConfig.extra.onlyTranslationExtraFile) {
extras = await loadCSV(`file/${tagBasePath}/${acConfig.extra.extraFile}?${new Date().getTime()}`);
if (acConfig.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]) {
allTags[i][2] = extras[i][0];
let aliasStr = allTags[i][3] || "";
let optComma = aliasStr.length > 0 ? "," : "";
allTags[i][3] = aliasStr + optComma + extras[i][0];
}
}
} else {
extras.forEach(e => {
// Check if a tag in allTags has the same name as the extra tag
let hasCount = e[2] && e[3] || (!isNaN(e[2]) && !e[3]);
// Check if a tag in allTags has the same name & category as the extra tag
if (tag = allTags.find(t => t[0] === e[0] && t[1] == e[1])) {
if (e[2]) // If the extra tag has a translation, add it to the tag
tag[2] = e[2];
if (hasCount && e[3] || isNaN(e[2])) { // If the extra tag has a translation / alias, add it to the normal tag
let aliasStr = tag[3] || "";
let optComma = aliasStr.length > 0 ? "," : "";
let alias = hasCount && e[3] || isNaN(e[2]) ? e[2] : e[3];
tag[3] = aliasStr + optComma + alias;
}
} else {
let count = hasCount ? e[2] : null;
let aliases = hasCount && e[3] ? e[3] : e[2];
// If the tag doesn't exist, add it to allTags
allTags.push(e);
let newTag = [e[0], e[1], count, aliases];
allTags.push(newTag);
}
});
}
} catch (e) {
console.error("Error loading extra translation file: " + e);
console.error("Error loading extra file: " + e);
return;
}
}
}
// Load translations
if (acConfig.translation.translationFile) {
try {
let tArray = await loadCSV(`file/${tagBasePath}/${acConfig.translation.translationFile}?${new Date().getTime()}`);
tArray.forEach(t => {
if (acConfig.translation.oldFormat)
translations.set(t[0], t[2]);
else
translations.set(t[0], t[1]);
});
} catch (e) {
console.error("Error loading translations file: " + e);
return;
}
}
// Load wildcards
if (acConfig.useWildcards && wildcardFiles.length === 0) {
try {
@@ -729,6 +861,12 @@ document.addEventListener("DOMContentLoaded", async () => {
area.addEventListener('focusout', debounce(() => hideResults(area), 400));
// Add up and down arrow event listener
area.addEventListener('keydown', (e) => navigateInList(area, e));
// CompositionEnd fires after the user has finished IME composing
// We need to block hide here to prevent the enter key from insta-closing the results
area.addEventListener('compositionend', () => {
hideBlocked = true;
setTimeout(() => { hideBlocked = false; }, 100);
});
// Add class so we know we've already added the listeners
area.classList.add('autocomplete');

View File

@@ -16,16 +16,22 @@
"appendComma": true,
"useWildcards": true,
"useEmbeddings": true,
"alias": {
"searchByAlias": true,
"onlyShowAlias": false
},
"translation": {
"searchByTranslation": true,
"onlyShowTranslation": false
"translationFile": "",
"oldFormat": false,
"searchByTranslation": true
},
"extra": {
"extraFile": "",
"onlyTranslationExtraFile": false
"onlyAliasExtraFile": false
},
"colors": {
"danbooru": {
"-1": ["red", "maroon"],
"0": ["lightblue", "dodgerblue"],
"1": ["indianred", "firebrick"],
"3": ["violet", "darkorchid"],

File diff suppressed because it is too large Load Diff

166094
tags/e621.csv

File diff suppressed because one or more lines are too long