JanitorAI reroll counter

JanitorAI regenerate response UI

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        JanitorAI reroll counter
// @match       https://janitorai.com/chats/*
// @version     0.1
// @description JanitorAI regenerate response UI
// @grant       none
// @license     MIT
// @namespace   https://greasyfork.org/users/1256538
// ==/UserScript==

const dialogueSelector = 'div:has(> div[data-index])';
const lastResponseSelector = 'div[data-index]:has(button[aria-label="Right"])';
const responseListSelector = 'div[data-index]:has(button[aria-label="Right"]) div:has(> li)';

let Logger = {
    log: function(message) {
        console.log("JanitorAI User Script:", message);
    }
}

async function waitForEl(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        new MutationObserver((records, observer) => {
            if (document.querySelector(selector)) {
                observer.disconnect();
                resolve(document.querySelector(selector));
            }
        })
        .observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}

function onRemove(element, callback) {
    new MutationObserver((records, observer) => {
        for (const record of records) {
            for (const removedEl of record.removedNodes) {
                if (removedEl === element) {
                    observer.disconnect();
                    callback();
                }
            }
        }
    })
    .observe(element.parentNode, { childList: true });
}

function createResponseCountsEl(responseListEl) {
    responseListEl.childNodes.forEach((responseEl, index, responseEls) => {
        let responseCountEl = document.createElement('div');
        responseCountEl.innerText = `(${index + 1}/${responseEls.length})`;
        responseCountEl.className = "response-count";
        responseCountEl.style.position  = "absolute";
        responseCountEl.style.bottom    = "0";
        responseCountEl.style.left      = "50%";
        responseCountEl.style.transform = "translate(-50%, 0)";
        responseCountEl.style.color     = "rgba(255, 255, 255, 0.565)";
        responseEl.appendChild(responseCountEl);
    });
}

function onLastResponseExist() {
    // Wait for last response to exist
    waitForEl(responseListSelector).then((responseListEl) => {
        // Initialize response counts
        createResponseCountsEl(responseListEl);

        // Regenerate response counts when new response is created
        let responseListObserver = new MutationObserver((records, observer) => {
            responseListEl
                .querySelectorAll(".response-count")
                .forEach(el => el.remove());
            createResponseCountsEl(responseListEl);
        })
        .observe(responseListEl, { childList: true });

        // Re-apply onLastResponseExist() when:
        // - Dialogue is pushed forward
        // - Scrolled far top removing latest response
        new MutationObserver((records, observer) => {
            responseListEl.querySelectorAll(".response-count")
                .forEach(el => el.remove());
            observer.disconnect();
            responseListObserver?.disconnect();
            onLastResponseExist();
        })
        .observe(document.querySelector(dialogueSelector), { childList: true });
    })
}

onLastResponseExist();