// ==UserScript==
// @name JanitorAI Enhanced UI
// @namespace http://tampermonkey.net/
// @version 3.3
// @description Adds UI controls for JanitorAI, hides buttons on chat pages
// @author Fefnik
// @match https://janitorai.com/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
let MIN_TOKENS = parseInt(localStorage.getItem('janitorAITokenFilter')) || 500;
let isSidebarHidden = localStorage.getItem('janitorAISidebarHidden') === 'true';
let isAutoScrollEnabled = localStorage.getItem('janitorAIAutoScroll') !== 'false';
let isMenuVisible = localStorage.getItem('janitorAIMenuVisible') === 'true';
let sliderElement = null;
let sliderContainer = null;
let controlPanel = null;
let controlsContainer = null;
let emblaSlide = null;
const isAllowedPage = () => {
const path = window.location.pathname;
return path === '/' || path.startsWith('/search') || path === '/my_characters' || path.startsWith('/profiles/');
};
const isChatsPage = () => window.location.pathname.startsWith('/chats');
const parseTokens = (text) => {
try {
text = text.replace(/<!--[\s\S]*?-->/g, '').replace('tokens', '').trim();
return text.includes('k') ? parseFloat(text.replace('k', '')) * 1000 : parseInt(text, 10) || 0;
} catch {
return 0;
}
};
const filterCards = () => {
document.querySelectorAll('.chakra-stack.css-1s5evre, .css-1s5evre').forEach(card => {
const tokenElement = card.querySelector('.chakra-text.css-jccmq6, .css-jccmq6');
if (!tokenElement) return;
const tokenCount = parseTokens(tokenElement.textContent);
const parent = card.closest('.css-1sxhvxh, .css-1dbw1r8');
if (parent) parent.style.display = tokenCount < MIN_TOKENS ? 'none' : '';
});
};
const setupPaginationScroll = () => {
document.querySelectorAll('.css-kzd6o0').forEach(button => {
button.removeEventListener('click', handlePaginationClick);
button.addEventListener('click', handlePaginationClick);
});
};
const handlePaginationClick = () => {
if (isAutoScrollEnabled) {
setTimeout(() => window.scrollTo({ top: 0, behavior: 'smooth' }), 300);
}
};
const toggleSidebar = () => {
const sidebar = document.querySelector('.css-h988mi');
const css70qvj9 = document.querySelector('.css-70qvj9');
if (sidebar) {
isSidebarHidden = !isSidebarHidden;
sidebar.style.display = isSidebarHidden ? 'none' : '';
if (!emblaSlide) {
emblaSlide = document.querySelector('.is-in-view.is-snapped.embla__slide');
}
if (emblaSlide) {
emblaSlide.style.display = isSidebarHidden ? 'none' : '';
}
if (css70qvj9) {
css70qvj9.style.display = isSidebarHidden ? 'none' : '';
}
localStorage.setItem('janitorAISidebarHidden', isSidebarHidden);
updateControlText();
}
};
const toggleAutoScroll = () => {
isAutoScrollEnabled = !isAutoScrollEnabled;
localStorage.setItem('janitorAIAutoScroll', isAutoScrollEnabled);
updateControlText();
};
const toggleMenu = () => {
isMenuVisible = !isMenuVisible;
localStorage.setItem('janitorAIMenuVisible', isMenuVisible);
updateElementsVisibility();
updateControlText();
};
const updateControlText = () => {
const sidebarText = document.getElementById('sidebar-toggle-text');
const scrollText = document.getElementById('auto-scroll-text');
if (sidebarText) {
sidebarText.textContent = isSidebarHidden ? 'Show Topbar' : 'Hide Topbar';
sidebarText.style.color = isSidebarHidden ? '#fff' : '#ccc';
}
if (scrollText) {
scrollText.textContent = `Auto-Scroll: ${isAutoScrollEnabled ? 'ON' : 'OFF'}`;
scrollText.style.color = isAutoScrollEnabled ? '#fff' : '#ccc';
}
};
const createControlPanel = () => {
if (controlPanel) return;
controlPanel = document.createElement('div');
controlPanel.id = 'janitor-control-panel';
Object.assign(controlPanel.style, {
position: 'fixed',
top: '75px',
left: '10px',
zIndex: '100000',
display: 'flex',
flexDirection: 'column',
gap: '5px',
alignItems: 'flex-start'
});
const settingsButton = document.createElement('button');
settingsButton.id = 'token-filter-toggle';
settingsButton.textContent = '⚙️';
Object.assign(settingsButton.style, {
width: '30px',
height: '30px',
padding: '0',
backgroundColor: 'rgba(74, 74, 74, 0.7)',
color: '#fff',
border: 'none',
borderRadius: '5px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '16px',
transition: 'background-color 0.2s'
});
settingsButton.addEventListener('click', toggleMenu);
controlsContainer = document.createElement('div');
controlsContainer.id = 'controls-container';
Object.assign(controlsContainer.style, {
display: 'none',
flexDirection: 'column',
gap: '5px',
backgroundColor: 'rgba(74, 74, 74, 0.7)',
padding: '5px',
borderRadius: '5px',
zIndex: '100001'
});
const sidebarText = document.createElement('span');
sidebarText.id = 'sidebar-toggle-text';
sidebarText.style.cursor = 'pointer';
sidebarText.style.fontSize = '12px';
sidebarText.addEventListener('click', toggleSidebar);
const scrollText = document.createElement('span');
scrollText.id = 'auto-scroll-text';
scrollText.style.cursor = 'pointer';
scrollText.style.fontSize = '12px';
scrollText.addEventListener('click', toggleAutoScroll);
controlsContainer.appendChild(sidebarText);
controlsContainer.appendChild(scrollText);
controlPanel.appendChild(settingsButton);
controlPanel.appendChild(controlsContainer);
document.body.appendChild(controlPanel);
updateControlText();
};
const createOrUpdateSlider = () => {
if (sliderElement) return;
sliderContainer = document.createElement('div');
sliderContainer.id = 'token-filter-container';
Object.assign(sliderContainer.style, {
position: 'fixed',
top: '75px',
left: '50px',
zIndex: '100002',
display: 'none',
flexDirection: 'row',
alignItems: 'center',
gap: '10px',
padding: '5px',
backgroundColor: 'rgba(74, 74, 74, 0.7)',
borderRadius: '5px'
});
sliderElement = document.createElement('input');
sliderElement.type = 'range';
sliderElement.id = 'token-filter-slider';
Object.assign(sliderElement, {
min: '0',
max: '6000',
step: '100',
value: MIN_TOKENS
});
Object.assign(sliderElement.style, {
width: '150px',
height: '20px',
backgroundColor: '#4a4a4a',
cursor: 'pointer',
appearance: 'none',
outline: 'none',
borderRadius: '5px',
padding: '0',
zIndex: '100003'
});
const style = document.createElement('style');
style.textContent = `
#token-filter-slider {
-webkit-appearance: none;
appearance: none;
background: #4a4a4a;
border-radius: 5px;
z-index: 100003;
}
#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;
box-shadow: 0 0 2px rgba(0,0,0,0.5);
transform: translateY(-5px);
z-index: 100004;
}
#token-filter-slider::-moz-range-thumb {
width: 20px;
height: 20px;
background: #ffffff;
cursor: pointer;
border-radius: 50%;
border: 2px solid #000;
box-shadow: 0 0 2px rgba(0,0,0,0.5);
transform: translateY(-5px);
z-index: 100004;
}
#token-filter-slider::-webkit-slider-runnable-track {
height: 10px;
background: #4a4a4a;
border-radius: 5px;
}
#token-filter-slider::-moz-range-track {
height: 10px;
background: #4a4a4a;
border-radius: 5px;
}
`;
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);
};
const updateElementsVisibility = () => {
const shouldShow = isAllowedPage() && !isChatsPage();
if (controlPanel) controlPanel.style.display = shouldShow ? 'flex' : 'none';
if (sliderContainer) sliderContainer.style.display = shouldShow && isMenuVisible ? 'flex' : 'none';
if (controlsContainer) controlsContainer.style.display = shouldShow && isMenuVisible ? 'flex' : 'none';
};
const initialize = () => {
createControlPanel();
createOrUpdateSlider();
updateElementsVisibility();
if (isAllowedPage() && !isChatsPage()) {
filterCards();
setupPaginationScroll();
const sidebar = document.querySelector('.css-h988mi');
if (sidebar && isSidebarHidden) {
sidebar.style.display = 'none';
emblaSlide = document.querySelector('.is-in-view.is-snapped.embla__slide');
if (emblaSlide) emblaSlide.style.display = 'none';
const css70qvj9 = document.querySelector('.css-70qvj9');
if (css70qvj9) css70qvj9.style.display = 'none';
}
new MutationObserver(() => {
filterCards();
setupPaginationScroll();
}).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();
})();