JanitorAI response count UI

JanitorAI regenerate response UI

// ==UserScript==
// @name        JanitorAI response count UI
// @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();