// ==UserScript==
// @name Chants for NovelAI
// @namespace https://www6.notion.site/dc99953d5f04405c893fba95dace0722
// @version 4
// @description Chants script for NovelAI
// @author SenY
// @match https://novelai.net/image
// @icon https://www.google.com/s2/favicons?sz=64&domain=novelai.net
// @grant none
// @license MIT
// ==/UserScript==
(function () {
const colors = {
"-1": ["red", "maroon"],
"0": ["lightblue", "dodgerblue"],
"1": ["gold", "goldenrod"],
"3": ["violet", "darkorchid"],
"4": ["lightgreen", "darkgreen"],
"5": ["tomato", "darksalmon"],
"6": ["red", "maroon"],
"7": ["whitesmoke", "black"],
"8": ["seagreen", "darkseagreen"]
}
const getChantURL = function (force) {
if (force === true) {
localStorage.removeItem("chantURL");
}
let chantURL = localStorage.getItem("chantURL") || prompt("Input your chants json url.\nThe URL must be a Cors-enabled server (e.g., gist.github.com).", "https://gist.githubusercontent.com/vkff5833/989808aadebf8648831955cdf2a7b3e3/raw/yuuri.json");
if (chantURL) {
localStorage.setItem("chantURL", chantURL);
}
return chantURL;
}
let chantURL = getChantURL(null);
let ui = document.createElement("div");
setInterval(() => {
document.querySelectorAll("textarea[placeholder]").forEach(el => {
if (el.offsetParent) {
el.parentElement.appendChild(ui);
}
});
}, 500);
let allTags = [];
fetch("https://gist.githubusercontent.com/vkff5833/275ccf8fa51c2c4ba767e2fb9c653f9a/raw/danbooru.json", {
method: "GET",
}).then((response) => response.json())
.then((data) => {
allTags = data;
fetch("https://gist.githubusercontent.com/vkff5833/275ccf8fa51c2c4ba767e2fb9c653f9a/raw/danbooru_wiki.slim.json", {
method: "GET",
}).then((response) => response.json())
.then((wikiPages) => {
allTags = allTags.map(x => {
let wikiPage = wikiPages.find(y => y.name == x.name);
if(wikiPage){
x.terms = x.terms.concat(wikiPage.otherNames);
}
return x;
});
});
});
let chants = [];
const Append = function (key, value) {
let text;
let newTags;
let textarea = document.querySelector("textarea[placeholder]");
let oldTags = textarea.value.split(",").map(x => x.trim());
if (key) {
let chant = chants.find(x => x.name == key.trim());
if (chant) {
newTags = chant.content.split(",").map(x => x.trim());
}
}
if (value) {
newTags = [value.trim()];
if (oldTags[oldTags.length - 1]) {
oldTags.pop();
}
}
if (newTags.length) {
let tags = oldTags.concat(newTags).filter(x => x);
textarea.textContent = tags.join(", ").replace(/^, /g, "");
textarea.value = tags.join(", ").replace(/^, /g, "") + ",";
}
}
const Suggest = function () {
let tags = document.querySelector("textarea[placeholder]");
if (tags) {
tags = tags.value.split(",").map(x => x.trim())
if (tags.length) {
let tag = tags[tags.length - 1];
document.getElementById("suggestionField").textContent = "";
let suggestions = allTags.filter(x => x.name.search(tag.replace(/_/g, " ")) >= 0).slice(0, 10);
suggestions = suggestions.concat(allTags.filter(x => {
return x.terms.map(y => y.search(tag.replace(/_/g, " ")) >= 0).includes(true);
}).slice(0, 10));
let done = new Set();
suggestions.forEach(tag => {
if (!done.has(tag.name)) {
let button = document.createElement("button");
let count = tag.coumt;
if (count > 1000) {
count /= 1000;
count = Math.round(count * 10) / 10;
count += "k";
}
button.textContent = tag.name + " (" + count + ")";
button.style.color = colors[tag.category][1];
button.addEventListener("click", function () {
Append(null, tag.name);
});
document.getElementById("suggestionField").appendChild(button);
}
done.add(tag.name);
});
}
}
}
const Build = function () {
ui.textContent = "";
let hr = document.createElement("hr");
hr.style.color = "grey";
hr.style.borderWidth = "2px";
let optionField = document.createElement("div");
let chantsField = document.createElement("div");
let suggestionField = document.createElement("div");
suggestionField.setAttribute("id", "suggestionField");
ui.appendChild(optionField);
ui.appendChild(hr.cloneNode());
ui.appendChild(chantsField);
ui.appendChild(hr.cloneNode());
ui.appendChild(suggestionField);
let resetButton = document.createElement("button");
resetButton.textContent = "Reset Chants URL";
resetButton.style.color = "red";
resetButton.addEventListener("click", function () {
chantURL = getChantURL(true);
Build();
});
optionField.appendChild(resetButton);
let clearButton = document.createElement("button");
clearButton.textContent = "Clear Suggestion";
clearButton.style.color = "red";
clearButton.addEventListener("click", function () {
document.getElementById("suggestionField").textContent = "";
});
optionField.appendChild(clearButton);
fetch(chantURL, {
method: "GET",
}).then((response) => response.json())
.then((data) => {
chants = data;
chants.forEach(chant => {
let button = document.createElement("button");
button.textContent = chant.name;
button.style.color = colors[chant.color][1];
button.addEventListener("click", function () {
Append(chant.name, null);
});
chantsField.appendChild(button);
});
});
}
let init = false;
document.addEventListener("DOMContentLoaded", function () {
if (init === false) {
Build();
init = true;
}
});
document.addEventListener("keyup", function () {
Suggest();
});
})();