|
|
|
|
@@ -20,6 +20,9 @@ const autocompleteCSS_dark = `
|
|
|
|
|
#autocompleteResultsList > li:hover {
|
|
|
|
|
background-color: #1f2937;
|
|
|
|
|
}
|
|
|
|
|
#autocompleteResultsList > li.selected {
|
|
|
|
|
background-color: #374151;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
const autocompleteCSS_light = `
|
|
|
|
|
#autocompleteResults {
|
|
|
|
|
@@ -42,6 +45,9 @@ const autocompleteCSS_light = `
|
|
|
|
|
#autocompleteResultsList > li:hover {
|
|
|
|
|
background-color: #f5f6f8;
|
|
|
|
|
}
|
|
|
|
|
#autocompleteResultsList > li.selected {
|
|
|
|
|
background-color: #e5e7eb;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
var acConfig = null;
|
|
|
|
|
@@ -110,6 +116,13 @@ const debounce = (func, wait = 300) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Difference function to fix duplicates not being seen as changes in normal filter
|
|
|
|
|
function difference(a, b) {
|
|
|
|
|
return [...b.reduce( (acc, v) => acc.set(v, (acc.get(v) || 0) - 1),
|
|
|
|
|
a.reduce( (acc, v) => acc.set(v, (acc.get(v) || 0) + 1), new Map() )
|
|
|
|
|
)].reduce( (acc, [v, count]) => acc.concat(Array(Math.abs(count)).fill(v)), [] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the result list div and necessary styling
|
|
|
|
|
function createResultsDiv() {
|
|
|
|
|
let resultsDiv = document.createElement("div");
|
|
|
|
|
@@ -122,14 +135,21 @@ function createResultsDiv() {
|
|
|
|
|
return resultsDiv;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The selected tag index. Needs to be up here so hide can access it.
|
|
|
|
|
var selectedTag = null;
|
|
|
|
|
|
|
|
|
|
// Show or hide the results div
|
|
|
|
|
var isVisible = false;
|
|
|
|
|
function showResults() {
|
|
|
|
|
let resultsDiv = gradioApp().querySelector('#autocompleteResults');
|
|
|
|
|
resultsDiv.style.display = "block";
|
|
|
|
|
isVisible = true;
|
|
|
|
|
}
|
|
|
|
|
function hideResults() {
|
|
|
|
|
let resultsDiv = gradioApp().querySelector('#autocompleteResults');
|
|
|
|
|
resultsDiv.style.display = "none";
|
|
|
|
|
isVisible = false;
|
|
|
|
|
selectedTag = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// On click, insert the tag into the prompt textbox with respect to the cursor position
|
|
|
|
|
@@ -174,9 +194,22 @@ function addResultsToList(results, tagword) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateSelectionStyle(num) {
|
|
|
|
|
let resultsList = gradioApp().querySelector('#autocompleteResultsList');
|
|
|
|
|
let items = resultsList.getElementsByTagName('li');
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
items[i].classList.remove('selected');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
items[num].classList.add('selected');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allTags = [];
|
|
|
|
|
previousTags = [];
|
|
|
|
|
|
|
|
|
|
results = [];
|
|
|
|
|
tagword = "";
|
|
|
|
|
resultCount = 0;
|
|
|
|
|
function autocomplete(prompt) {
|
|
|
|
|
// Guard for empty prompt
|
|
|
|
|
if (prompt.length == 0) {
|
|
|
|
|
@@ -186,16 +219,16 @@ function autocomplete(prompt) {
|
|
|
|
|
|
|
|
|
|
// Match tags with RegEx to get the last edited one
|
|
|
|
|
let tags = prompt.match(/[^, ]+/g);
|
|
|
|
|
let difference = tags.filter(x => !previousTags.includes(x));
|
|
|
|
|
let diff = difference(tags, previousTags)
|
|
|
|
|
previousTags = tags;
|
|
|
|
|
|
|
|
|
|
// Guard for no difference / only whitespace remaining
|
|
|
|
|
if (difference == undefined || difference.length == 0) {
|
|
|
|
|
if (diff == undefined || diff.length == 0) {
|
|
|
|
|
hideResults();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let tagword = difference[0]
|
|
|
|
|
tagword = diff[0]
|
|
|
|
|
|
|
|
|
|
// Guard for empty tagword
|
|
|
|
|
if (tagword == undefined || tagword.length == 0) {
|
|
|
|
|
@@ -203,10 +236,11 @@ function autocomplete(prompt) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let results = allTags.filter(x => x[0].includes(tagword)).slice(0, acConfig.maxResults);
|
|
|
|
|
results = allTags.filter(x => x[0].includes(tagword)).slice(0, acConfig.maxResults);
|
|
|
|
|
resultCount = results.length;
|
|
|
|
|
|
|
|
|
|
// Guard for empty results
|
|
|
|
|
if (results.length == 0) {
|
|
|
|
|
if (resultCount == 0) {
|
|
|
|
|
hideResults();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@@ -215,6 +249,51 @@ function autocomplete(prompt) {
|
|
|
|
|
addResultsToList(results, tagword);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function navigateInList(event) {
|
|
|
|
|
validKeys = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Enter", "Escape"];
|
|
|
|
|
|
|
|
|
|
if (!validKeys.includes(event.key)) return;
|
|
|
|
|
if (!isVisible) return
|
|
|
|
|
|
|
|
|
|
switch (event.key) {
|
|
|
|
|
case "ArrowUp":
|
|
|
|
|
if (selectedTag == null) {
|
|
|
|
|
selectedTag = resultCount - 1;
|
|
|
|
|
} else {
|
|
|
|
|
selectedTag = (selectedTag - 1 + resultCount) % resultCount;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "ArrowDown":
|
|
|
|
|
if (selectedTag == null) {
|
|
|
|
|
selectedTag = 0;
|
|
|
|
|
} else {
|
|
|
|
|
selectedTag = (selectedTag + 1) % resultCount;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "ArrowLeft":
|
|
|
|
|
selectedTag = 0;
|
|
|
|
|
break;
|
|
|
|
|
case "ArrowRight":
|
|
|
|
|
selectedTag = resultCount - 1;
|
|
|
|
|
break;
|
|
|
|
|
case "Enter":
|
|
|
|
|
if (selectedTag != null) {
|
|
|
|
|
insertTextAtCursor(results[selectedTag][0], tagword);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case "Escape":
|
|
|
|
|
hideResults();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// Update highlighting
|
|
|
|
|
if (selectedTag != null)
|
|
|
|
|
updateSelectionStyle(selectedTag);
|
|
|
|
|
|
|
|
|
|
// Prevent default behavior
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onUiUpdate(function(){
|
|
|
|
|
// One-time CSV setup
|
|
|
|
|
if (acConfig == null) acConfig = JSON.parse(readFile("file/tags/config.json"));
|
|
|
|
|
@@ -237,6 +316,10 @@ onUiUpdate(function(){
|
|
|
|
|
promptTextbox.addEventListener('input', debounce(() => autocomplete(promptTextbox.value), 100));
|
|
|
|
|
// Add focusout event listener
|
|
|
|
|
promptTextbox.addEventListener('focusout', debounce(() => hideResults(), 400));
|
|
|
|
|
// Add up and down arrow event listener
|
|
|
|
|
promptTextbox.addEventListener('keydown', function(e) { navigateInList(e); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add class so we know we've already added the listeners
|
|
|
|
|
promptTextbox.classList.add('autocomplete');
|
|
|
|
|
|