Chants for NovelAI

Chants script for NovelAI

2023/12/15のページです。最新版はこちら。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         Chants for NovelAI
// @namespace    https://www6.notion.site/dc99953d5f04405c893fba95dace0722
// @version      6
// @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 getTargetTag = function(){
        let textarea = document.querySelector("textarea[placeholder]");
        if(textarea){
            let oldTags = textarea.value.split(",").map(x => x.trim());
            let beforeTags = textarea.value.slice(0, textarea.selectionEnd).split(",").map(x => x.trim());
            let targetTag = beforeTags[beforeTags.length-2];
            targetTag = oldTags[oldTags.indexOf(targetTag)+1];
            console.log(targetTag);
            return targetTag.trim();
        }else{
            return null;
        }
    };

    const Append = function (key, value) {
        let targetTag = getTargetTag();
        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 (newTags.length) {
            //let tags = oldTags.concat(newTags).filter(x => x);
            let tags = [];
            oldTags.forEach(tag => {
                if(tag == targetTag){
                    if ( key ) {
                        tags.push(tag);
                    }
                    newTags.forEach(newTag => {
                        tags.push(newTag);
                    });
                }else{
                    tags.push(tag);
                }
            });
            if(!targetTag){
                tags = tags.concat(newTags);
            }
            tags = Array.from(new Set(tags.filter(x => x)));
            textarea.textContent = tags.join(", ").replace(/^, /g, "");
            textarea.value = tags.join(", ").replace(/^, /g, "") + ",";
            textarea.focus();
            textarea.dispatchEvent(new KeyboardEvent('keydown',{'keyCode':32,'which':32}));
        }
    }
    const Suggest = function () {
        let textarea = document.querySelector("textarea[placeholder]");
        if (textarea) {
            let tags = textarea.value.split(",").map(x => x.trim());
            let beforeTags = textarea.value.slice(0, textarea.selectionEnd).split(",").map(x => x.trim());
            if (tags.length) {
                let tag = beforeTags[beforeTags.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 (e) {
        if ( e.target.tagName == "TEXTAREA" ){
            Suggest();
        }
    });
    document.addEventListener("click", function (e) {
        if ( e.target.tagName == "TEXTAREA" ){
            Suggest();
        }
    });
})();