Greasy Fork is available in English.

JanitorAI Enhanced UI

Adds useful UI controls for JanitorAI

Versión del día 9/4/2025. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         JanitorAI Enhanced UI
// @namespace    http://tampermonkey.net/
// @version      0.31
// @description  Adds useful UI controls for JanitorAI
// @author       Fefnik
// @match        https://janitorai.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Settings
    let MIN_TOKENS = localStorage.getItem('janitorAITokenFilter') ? parseInt(localStorage.getItem('janitorAITokenFilter')) : 500;
    let isSliderVisible = localStorage.getItem('janitorAISliderVisible') === 'true';
    let isSidebarHidden = localStorage.getItem('janitorAISidebarHidden') === 'true';
    let isAutoScrollEnabled = localStorage.getItem('janitorAIAutoScroll') !== 'false';

    // Elements
    let sliderElement = null;
    let sliderContainer = null;
    let controlPanel = null;

    function isAllowedPage() {
        return ['/', '/search', '/my_characters', '/profiles/'].some(path =>
            window.location.pathname === path ||
            window.location.pathname.startsWith('/search') ||
            window.location.pathname.startsWith('/profiles/')
        );
    }

    function parseTokens(tokenText) {
        try {
            let cleanText = tokenText.replace(/<!--[\s\S]*?-->/g, '').replace('tokens', '').trim();
            if (cleanText.includes('k')) {
                return parseFloat(cleanText.replace('k', '')) * 1000;
            }
            return parseInt(cleanText, 10) || 0;
        } catch (error) {
            return 0;
        }
    }

    function filterCards() {
        const cards = document.querySelectorAll('.chakra-stack.css-1s5evre, .css-1s5evre');
        cards.forEach(card => {
            const tokenElement = card.querySelector('.chakra-text.css-jccmq6, .css-jccmq6');
            if (!tokenElement) return;

            const tokenCount = parseTokens(tokenElement.textContent);
            const parentContainer = card.closest('.css-1sxhvxh, .css-1dbw1r8');
            if (parentContainer) {
                parentContainer.style.display = tokenCount < MIN_TOKENS ? 'none' : '';
            }
        });
    }

    function setupPaginationScroll() {
        const paginationButtons = document.querySelectorAll('.css-kzd6o0');
        paginationButtons.forEach(button => {
            button.removeEventListener('click', handlePaginationClick);
            button.addEventListener('click', handlePaginationClick);
        });
    }

    function handlePaginationClick() {
        if (isAutoScrollEnabled) {
            setTimeout(() => {
                window.scrollTo({
                    top: 0,
                    behavior: 'smooth'
                });
            }, 300);
        }
    }

    function toggleSidebar() {
        const sidebar = document.querySelector('.css-h988mi');
        if (sidebar) {
            isSidebarHidden = !isSidebarHidden;
            sidebar.style.display = isSidebarHidden ? 'none' : '';
            localStorage.setItem('janitorAISidebarHidden', isSidebarHidden);
            updateControlIcons();
        }
    }

    function toggleAutoScroll() {
        isAutoScrollEnabled = !isAutoScrollEnabled;
        localStorage.setItem('janitorAIAutoScroll', isAutoScrollEnabled);
        updateControlIcons();
    }

    function updateControlIcons() {
        const eyeButton = document.getElementById('sidebar-toggle-button');
        const scrollButton = document.getElementById('auto-scroll-button');
        const settingsButton = document.getElementById('token-filter-toggle');

        if (eyeButton) {
            eyeButton.textContent = isSidebarHidden ? '👁️' : '👁️‍🗨️';
            eyeButton.title = isSidebarHidden ? 'Show sidebar' : 'Hide sidebar';
            eyeButton.style.backgroundColor = isSidebarHidden ? 'rgba(74, 74, 74, 0.3)' : 'rgba(74, 74, 74, 0.7)';
        }

        if (scrollButton) {
            scrollButton.textContent = '⏫';
            scrollButton.title = isAutoScrollEnabled ? 'Auto-scroll: ON' : 'Auto-scroll: OFF';
            scrollButton.style.backgroundColor = isAutoScrollEnabled ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
        }

        if (settingsButton) {
            settingsButton.style.backgroundColor = isSliderVisible ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
        }
    }

    function createControlPanel() {
        if (controlPanel) return;

        // Main control panel (vertical)
        controlPanel = document.createElement('div');
        controlPanel.id = 'janitor-control-panel';
        controlPanel.style.position = 'fixed';
        controlPanel.style.top = '75px';
        controlPanel.style.left = '10px';
        controlPanel.style.zIndex = '100000';
        controlPanel.style.display = 'flex';
        controlPanel.style.flexDirection = 'column';
        controlPanel.style.gap = '5px';
        controlPanel.style.alignItems = 'flex-start';

        // Settings button (gear icon)
        const settingsButton = document.createElement('button');
        settingsButton.id = 'token-filter-toggle';
        settingsButton.textContent = '⚙️';
        settingsButton.title = 'Token filter settings';
        settingsButton.style.width = '30px';
        settingsButton.style.height = '30px';
        settingsButton.style.padding = '0';
        settingsButton.style.backgroundColor = isSliderVisible ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
        settingsButton.style.color = '#fff';
        settingsButton.style.border = 'none';
        settingsButton.style.borderRadius = '5px';
        settingsButton.style.cursor = 'pointer';
        settingsButton.style.display = 'flex';
        settingsButton.style.alignItems = 'center';
        settingsButton.style.justifyContent = 'center';
        settingsButton.style.fontSize = '16px';
        settingsButton.style.transition = 'background-color 0.2s';

        settingsButton.addEventListener('click', () => {
            isSliderVisible = !isSliderVisible;
            if (sliderContainer) {
                sliderContainer.style.display = isSliderVisible ? 'flex' : 'none';
            }
            localStorage.setItem('janitorAISliderVisible', isSliderVisible);
            updateControlIcons();
        });

        // Eye button (sidebar toggle)
        const eyeButton = document.createElement('button');
        eyeButton.id = 'sidebar-toggle-button';
        eyeButton.style.width = '30px';
        eyeButton.style.height = '30px';
        eyeButton.style.padding = '0';
        eyeButton.style.backgroundColor = isSidebarHidden ? 'rgba(74, 74, 74, 0.3)' : 'rgba(74, 74, 74, 0.7)';
        eyeButton.style.color = '#fff';
        eyeButton.style.border = 'none';
        eyeButton.style.borderRadius = '5px';
        eyeButton.style.cursor = 'pointer';
        eyeButton.style.display = 'flex';
        eyeButton.style.alignItems = 'center';
        eyeButton.style.justifyContent = 'center';
        eyeButton.style.fontSize = '16px';
        eyeButton.style.transition = 'background-color 0.2s';

        eyeButton.addEventListener('click', toggleSidebar);

        // Auto-scroll button
        const scrollButton = document.createElement('button');
        scrollButton.id = 'auto-scroll-button';
        scrollButton.style.width = '30px';
        scrollButton.style.height = '30px';
        scrollButton.style.padding = '0';
        scrollButton.style.backgroundColor = isAutoScrollEnabled ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
        scrollButton.style.color = '#fff';
        scrollButton.style.border = 'none';
        scrollButton.style.borderRadius = '5px';
        scrollButton.style.cursor = 'pointer';
        scrollButton.style.display = 'flex';
        scrollButton.style.alignItems = 'center';
        scrollButton.style.justifyContent = 'center';
        scrollButton.style.fontSize = '16px';
        scrollButton.style.transition = 'background-color 0.2s';

        scrollButton.addEventListener('click', toggleAutoScroll);

        controlPanel.appendChild(settingsButton);
        controlPanel.appendChild(eyeButton);
        controlPanel.appendChild(scrollButton);
        document.body.appendChild(controlPanel);
        updateControlIcons();
    }

    function createOrUpdateSlider() {
        if (!sliderElement) {
            // Slider container (horizontal, appears to the right of control panel)
            sliderContainer = document.createElement('div');
            sliderContainer.id = 'token-filter-container';
            sliderContainer.style.position = 'fixed';
            sliderContainer.style.top = '75px';
            sliderContainer.style.left = '50px';
            sliderContainer.style.zIndex = '99999';
            sliderContainer.style.display = 'none';
            sliderContainer.style.flexDirection = 'row';
            sliderContainer.style.alignItems = 'center';
            sliderContainer.style.gap = '10px';
            sliderContainer.style.padding = '5px';
            sliderContainer.style.backgroundColor = 'rgba(74, 74, 74, 0.7)';
            sliderContainer.style.borderRadius = '5px';

            // Slider element
            sliderElement = document.createElement('input');
            sliderElement.type = 'range';
            sliderElement.id = 'token-filter-slider';
            sliderElement.min = '0';
            sliderElement.max = '6000';
            sliderElement.step = '100';
            sliderElement.value = MIN_TOKENS;
            sliderElement.style.width = '150px';
            sliderElement.style.height = '10px';
            sliderElement.style.backgroundColor = '#4a4a4a';
            sliderElement.style.cursor = 'pointer';
            sliderElement.style.appearance = 'none';
            sliderElement.style.outline = 'none';
            sliderElement.style.borderRadius = '5px';

            // Slider thumb style
            const style = document.createElement('style');
            style.textContent = `
                #token-filter-slider::-webkit-slider-thumb {
                    -webkit-appearance: none;
                    appearance: none;
                    width: 16px;
                    height: 16px;
                    background: #ffffff;
                    cursor: pointer;
                    border-radius: 50%;
                    border: 2px solid #000;
                }
                #token-filter-slider::-moz-range-thumb {
                    width: 16px;
                    height: 16px;
                    background: #ffffff;
                    cursor: pointer;
                    border-radius: 50%;
                    border: 2px solid #000;
                }
            `;
            document.head.appendChild(style);

            // Value label
            const label = document.createElement('span');
            label.id = 'token-filter-label';
            label.style.color = '#fff';
            label.style.fontSize = '12px';
            label.style.minWidth = '60px';
            label.textContent = `${MIN_TOKENS} tokens`;

            sliderElement.addEventListener('input', (e) => {
                MIN_TOKENS = parseInt(e.target.value);
                label.textContent = `${MIN_TOKENS} tokens`;
                localStorage.setItem('janitorAITokenFilter', MIN_TOKENS);
                filterCards();
            });

            sliderContainer.appendChild(sliderElement);
            sliderContainer.appendChild(label);
            document.body.appendChild(sliderContainer);
        }

        sliderContainer.style.display = isSliderVisible ? 'flex' : 'none';
    }

    function updateElementsVisibility() {
        const shouldShow = isAllowedPage();
        if (controlPanel) {
            controlPanel.style.display = shouldShow ? 'flex' : 'none';
        }
        if (sliderContainer) {
            sliderContainer.style.display = shouldShow && isSliderVisible ? 'flex' : 'none';
        }
    }

    function initialize() {
        createControlPanel();
        createOrUpdateSlider();

        if (isAllowedPage()) {
            filterCards();
            setupPaginationScroll();

            const sidebar = document.querySelector('.css-h988mi');
            if (sidebar && isSidebarHidden) {
                sidebar.style.display = 'none';
            }

            const observer = new MutationObserver(() => {
                filterCards();
                setupPaginationScroll();
            });
            observer.observe(document.body, { childList: true, subtree: true });
        }
    }

    const tryInitialize = () => {
        if (document.body) {
            initialize();

            let lastPath = window.location.pathname;
            setInterval(() => {
                if (lastPath !== window.location.pathname) {
                    lastPath = window.location.pathname;
                    updateElementsVisibility();
                    if (isAllowedPage()) {
                        filterCards();
                        setupPaginationScroll();
                    }
                }
            }, 500);
        } else {
            setTimeout(tryInitialize, 1000);
        }
    };

    tryInitialize();
})();