JanitorAI Token Filter

Filters character cards on JanitorAI by token count

Verze ze dne 09. 04. 2025. Zobrazit nejnovější verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         JanitorAI Token Filter
// @namespace    http://tampermonkey.net/
// @version      0.20
// @description  Filters character cards on JanitorAI by token count
// @author       Fefnik
// @match        https://janitorai.com/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let MIN_TOKENS = 500;
    let sliderElement = null;

    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');
        cards.forEach(card => {
            const tokenElement = card.querySelector('.chakra-text.css-jccmq6');
            if (!tokenElement) return;

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

    function createOrUpdateSlider() {
        if (!sliderElement) {
            // Создаем контейнер для слайдера и метки
            const sliderContainer = document.createElement('div');
            sliderContainer.id = 'token-filter-container';
            sliderContainer.style.position = 'fixed';
            sliderContainer.style.top = '50px';
            sliderContainer.style.left = '0px';
            sliderContainer.style.zIndex = '99999';
            sliderContainer.style.display = 'flex';
            sliderContainer.style.flexDirection = 'column';
            sliderContainer.style.alignItems = 'center';
            sliderContainer.style.height = '150px'; // Высота контейнера
            sliderContainer.style.width = '50px'; // Ограничиваем ширину

            // Создаем дополнительный контейнер для слайдера с правильной ориентацией
            const sliderWrapper = document.createElement('div');
            sliderWrapper.style.width = '100px'; // Реальная длина слайдера
            sliderWrapper.style.height = '10px'; // Толщина слайдера
            sliderWrapper.style.transform = 'rotate(-90deg)';
            sliderWrapper.style.transformOrigin = 'center center';
            sliderWrapper.style.marginTop = '10px'; // Отступ сверху для слайдера

            // Создаем слайдер
            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 = '100%'; // Занимает всю длину контейнера
            sliderElement.style.height = '20px';
            sliderElement.style.backgroundColor = '#4a4a4a';
            sliderElement.style.cursor = 'pointer';
            sliderElement.style.appearance = 'none'; // Убираем стандартные стили
            sliderElement.style.outline = 'none';
            sliderElement.style.borderRadius = '5px';

            // Добавляем кастомные стили для ползунка
            const style = document.createElement('style');
            style.textContent = `
                #token-filter-slider::-webkit-slider-thumb {
                    -webkit-appearance: none;
                    appearance: none;
                    width: 20px;
                    height: 20px;
                    background: #ffffff;
                    cursor: pointer;
                    border-radius: 50%;
                    border: 2px solid #000;
                }
                #token-filter-slider::-moz-range-thumb {
                    width: 20px;
                    height: 20px;
                    background: #ffffff;
                    cursor: pointer;
                    border-radius: 50%;
                    border: 2px solid #000;
                }
            `;
            document.head.appendChild(style);

            // Создаем метку
            const label = document.createElement('span');
            label.id = 'token-filter-label';
            label.style.color = '#fff';
            label.style.fontSize = '12px';
            label.style.textAlign = 'center';
            label.style.marginTop = '50px'; // Увеличиваем отступ, чтобы опустить текст ниже на ~10px
            label.textContent = `${MIN_TOKENS} tokens`;

            // Добавляем обработчик событий
            sliderElement.addEventListener('input', (e) => {
                MIN_TOKENS = parseInt(e.target.value);
                label.textContent = `${MIN_TOKENS} tokens`;
                filterCards();
            });

            // Собираем все вместе: сначала слайдер, потом метка
            sliderWrapper.appendChild(sliderElement);
            sliderContainer.appendChild(sliderWrapper);
            sliderContainer.appendChild(label);

            const appendSlider = () => {
                if (document.body) {
                    document.body.appendChild(sliderContainer);
                } else {
                    setTimeout(appendSlider, 500);
                }
            };
            appendSlider();
        }

        const container = document.getElementById('token-filter-container');
        if (container) {
            container.style.display = window.location.pathname === '/' ? 'flex' : 'none';
        }
    }

    function initialize() {
        createOrUpdateSlider();
        if (window.location.pathname === '/') {
            filterCards();
        }
    }

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

            let lastPath = window.location.pathname;
            const checkPath = () => {
                if (lastPath !== window.location.pathname) {
                    lastPath = window.location.pathname;
                    createOrUpdateSlider();
                    if (lastPath === '/') {
                        filterCards();
                    }
                }
            };

            setInterval(checkPath, 500);

            const observer = new MutationObserver(() => {
                if (window.location.pathname === '/') {
                    setTimeout(filterCards, 500);
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });
        } else {
            setTimeout(tryInitialize, 1000);
        }
    };

    tryInitialize();
})();