// ==UserScript==
// @name JanitorAI Enhanced UI
// @namespace http://tampermonkey.net/
// @version 0.35
// @description Adds useful UI controls for JanitorAI, hides buttons on all chat pages
// @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() {
const path = window.location.pathname;
console.log('Checking allowed page:', path);
return (path === '/' || path.startsWith('/search') || path === '/my_characters' || path.startsWith('/profiles/'));
}
function isChatsPage() {
const path = window.location.pathname;
console.log('Current path:', path, 'Is chats page:', path.startsWith('/chats'));
return path.startsWith('/chats');
}
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;
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';
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 && !isChatsPage() ? 'flex' : 'none';
}
localStorage.setItem('janitorAISliderVisible', isSliderVisible);
updateControlIcons();
});
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);
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) {
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';
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';
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);
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 && !isChatsPage() ? 'flex' : 'none';
}
function updateElementsVisibility() {
const shouldShow = isAllowedPage() && !isChatsPage();
console.log('Should show controls:', shouldShow);
if (controlPanel) {
controlPanel.style.display = shouldShow ? 'flex' : 'none';
}
if (sliderContainer) {
sliderContainer.style.display = shouldShow && isSliderVisible ? 'flex' : 'none';
}
}
function initialize() {
createControlPanel();
createOrUpdateSlider();
updateElementsVisibility();
if (isAllowedPage() && !isChatsPage()) {
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() && !isChatsPage()) {
filterCards();
setupPaginationScroll();
}
}
}, 500);
} else {
setTimeout(tryInitialize, 1000);
}
};
tryInitialize();
})();