JanitorAI Enhanced UI

Adds useful UI controls for JanitorAI, hides buttons on all chat pages

当前为 2025-04-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name JanitorAI Enhanced UI
  3. // @namespace http://tampermonkey.net/
  4. // @version 2
  5. // @description Adds useful UI controls for JanitorAI, hides buttons on all chat pages
  6. // @author Fefnik
  7. // @match https://janitorai.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Settings
  15. let MIN_TOKENS = localStorage.getItem('janitorAITokenFilter') ? parseInt(localStorage.getItem('janitorAITokenFilter')) : 500;
  16. let isSliderVisible = localStorage.getItem('janitorAISliderVisible') === 'true';
  17. let isSidebarHidden = localStorage.getItem('janitorAISidebarHidden') === 'true';
  18. let isAutoScrollEnabled = localStorage.getItem('janitorAIAutoScroll') !== 'false';
  19.  
  20. // Elements
  21. let sliderElement = null;
  22. let sliderContainer = null;
  23. let controlPanel = null;
  24.  
  25. function isAllowedPage() {
  26. const path = window.location.pathname;
  27. console.log('Checking allowed page:', path);
  28. return (path === '/' || path.startsWith('/search') || path === '/my_characters' || path.startsWith('/profiles/'));
  29. }
  30.  
  31. function isChatsPage() {
  32. const path = window.location.pathname;
  33. console.log('Current path:', path, 'Is chats page:', path.startsWith('/chats'));
  34. return path.startsWith('/chats');
  35. }
  36.  
  37. function parseTokens(tokenText) {
  38. try {
  39. let cleanText = tokenText.replace(/<!--[\s\S]*?-->/g, '').replace('tokens', '').trim();
  40. if (cleanText.includes('k')) {
  41. return parseFloat(cleanText.replace('k', '')) * 1000;
  42. }
  43. return parseInt(cleanText, 10) || 0;
  44. } catch (error) {
  45. return 0;
  46. }
  47. }
  48.  
  49. function filterCards() {
  50. const cards = document.querySelectorAll('.chakra-stack.css-1s5evre, .css-1s5evre');
  51. cards.forEach(card => {
  52. const tokenElement = card.querySelector('.chakra-text.css-jccmq6, .css-jccmq6');
  53. if (!tokenElement) return;
  54.  
  55. const tokenCount = parseTokens(tokenElement.textContent);
  56. const parentContainer = card.closest('.css-1sxhvxh, .css-1dbw1r8');
  57. if (parentContainer) {
  58. parentContainer.style.display = tokenCount < MIN_TOKENS ? 'none' : '';
  59. }
  60. });
  61. }
  62.  
  63. function setupPaginationScroll() {
  64. const paginationButtons = document.querySelectorAll('.css-kzd6o0');
  65. paginationButtons.forEach(button => {
  66. button.removeEventListener('click', handlePaginationClick);
  67. button.addEventListener('click', handlePaginationClick);
  68. });
  69. }
  70.  
  71. function handlePaginationClick() {
  72. if (isAutoScrollEnabled) {
  73. setTimeout(() => {
  74. window.scrollTo({
  75. top: 0,
  76. behavior: 'smooth'
  77. });
  78. }, 300);
  79. }
  80. }
  81.  
  82. function toggleSidebar() {
  83. const sidebar = document.querySelector('.css-h988mi');
  84. if (sidebar) {
  85. isSidebarHidden = !isSidebarHidden;
  86. sidebar.style.display = isSidebarHidden ? 'none' : '';
  87. localStorage.setItem('janitorAISidebarHidden', isSidebarHidden);
  88. updateControlIcons();
  89. }
  90. }
  91.  
  92. function toggleAutoScroll() {
  93. isAutoScrollEnabled = !isAutoScrollEnabled;
  94. localStorage.setItem('janitorAIAutoScroll', isAutoScrollEnabled);
  95. updateControlIcons();
  96. }
  97.  
  98. function updateControlIcons() {
  99. const eyeButton = document.getElementById('sidebar-toggle-button');
  100. const scrollButton = document.getElementById('auto-scroll-button');
  101. const settingsButton = document.getElementById('token-filter-toggle');
  102.  
  103. if (eyeButton) {
  104. eyeButton.textContent = isSidebarHidden ? '👁️' : '👁️‍🗨️';
  105. eyeButton.title = isSidebarHidden ? 'Show sidebar' : 'Hide sidebar';
  106. eyeButton.style.backgroundColor = isSidebarHidden ? 'rgba(74, 74, 74, 0.3)' : 'rgba(74, 74, 74, 0.7)';
  107. }
  108.  
  109. if (scrollButton) {
  110. scrollButton.textContent = '⏫';
  111. scrollButton.title = isAutoScrollEnabled ? 'Auto-scroll: ON' : 'Auto-scroll: OFF';
  112. scrollButton.style.backgroundColor = isAutoScrollEnabled ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
  113. }
  114.  
  115. if (settingsButton) {
  116. settingsButton.style.backgroundColor = isSliderVisible ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
  117. }
  118. }
  119.  
  120. function createControlPanel() {
  121. if (controlPanel) return;
  122.  
  123. controlPanel = document.createElement('div');
  124. controlPanel.id = 'janitor-control-panel';
  125. controlPanel.style.position = 'fixed';
  126. controlPanel.style.top = '75px';
  127. controlPanel.style.left = '10px';
  128. controlPanel.style.zIndex = '100000';
  129. controlPanel.style.display = 'flex';
  130. controlPanel.style.flexDirection = 'column';
  131. controlPanel.style.gap = '5px';
  132. controlPanel.style.alignItems = 'flex-start';
  133.  
  134. const settingsButton = document.createElement('button');
  135. settingsButton.id = 'token-filter-toggle';
  136. settingsButton.textContent = '⚙️';
  137. settingsButton.title = 'Token filter settings';
  138. settingsButton.style.width = '30px';
  139. settingsButton.style.height = '30px';
  140. settingsButton.style.padding = '0';
  141. settingsButton.style.backgroundColor = isSliderVisible ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
  142. settingsButton.style.color = '#fff';
  143. settingsButton.style.border = 'none';
  144. settingsButton.style.borderRadius = '5px';
  145. settingsButton.style.cursor = 'pointer';
  146. settingsButton.style.display = 'flex';
  147. settingsButton.style.alignItems = 'center';
  148. settingsButton.style.justifyContent = 'center';
  149. settingsButton.style.fontSize = '16px';
  150. settingsButton.style.transition = 'background-color 0.2s';
  151.  
  152. settingsButton.addEventListener('click', () => {
  153. isSliderVisible = !isSliderVisible;
  154. if (sliderContainer) {
  155. sliderContainer.style.display = isSliderVisible && !isChatsPage() ? 'flex' : 'none';
  156. }
  157. localStorage.setItem('janitorAISliderVisible', isSliderVisible);
  158. updateControlIcons();
  159. });
  160.  
  161. const eyeButton = document.createElement('button');
  162. eyeButton.id = 'sidebar-toggle-button';
  163. eyeButton.style.width = '30px';
  164. eyeButton.style.height = '30px';
  165. eyeButton.style.padding = '0';
  166. eyeButton.style.backgroundColor = isSidebarHidden ? 'rgba(74, 74, 74, 0.3)' : 'rgba(74, 74, 74, 0.7)';
  167. eyeButton.style.color = '#fff';
  168. eyeButton.style.border = 'none';
  169. eyeButton.style.borderRadius = '5px';
  170. eyeButton.style.cursor = 'pointer';
  171. eyeButton.style.display = 'flex';
  172. eyeButton.style.alignItems = 'center';
  173. eyeButton.style.justifyContent = 'center';
  174. eyeButton.style.fontSize = '16px';
  175. eyeButton.style.transition = 'background-color 0.2s';
  176.  
  177. eyeButton.addEventListener('click', toggleSidebar);
  178.  
  179. const scrollButton = document.createElement('button');
  180. scrollButton.id = 'auto-scroll-button';
  181. scrollButton.style.width = '30px';
  182. scrollButton.style.height = '30px';
  183. scrollButton.style.padding = '0';
  184. scrollButton.style.backgroundColor = isAutoScrollEnabled ? 'rgba(74, 74, 74, 0.7)' : 'rgba(74, 74, 74, 0.3)';
  185. scrollButton.style.color = '#fff';
  186. scrollButton.style.border = 'none';
  187. scrollButton.style.borderRadius = '5px';
  188. scrollButton.style.cursor = 'pointer';
  189. scrollButton.style.display = 'flex';
  190. scrollButton.style.alignItems = 'center';
  191. scrollButton.style.justifyContent = 'center';
  192. scrollButton.style.fontSize = '16px';
  193. scrollButton.style.transition = 'background-color 0.2s';
  194.  
  195. scrollButton.addEventListener('click', toggleAutoScroll);
  196.  
  197. controlPanel.appendChild(settingsButton);
  198. controlPanel.appendChild(eyeButton);
  199. controlPanel.appendChild(scrollButton);
  200. document.body.appendChild(controlPanel);
  201. updateControlIcons();
  202. }
  203.  
  204. function createOrUpdateSlider() {
  205. if (!sliderElement) {
  206. sliderContainer = document.createElement('div');
  207. sliderContainer.id = 'token-filter-container';
  208. sliderContainer.style.position = 'fixed';
  209. sliderContainer.style.top = '75px';
  210. sliderContainer.style.left = '50px';
  211. sliderContainer.style.zIndex = '99999';
  212. sliderContainer.style.display = 'none';
  213. sliderContainer.style.flexDirection = 'row';
  214. sliderContainer.style.alignItems = 'center';
  215. sliderContainer.style.gap = '10px';
  216. sliderContainer.style.padding = '5px';
  217. sliderContainer.style.backgroundColor = 'rgba(74, 74, 74, 0.7)';
  218. sliderContainer.style.borderRadius = '5px';
  219.  
  220. sliderElement = document.createElement('input');
  221. sliderElement.type = 'range';
  222. sliderElement.id = 'token-filter-slider';
  223. sliderElement.min = '0';
  224. sliderElement.max = '6000';
  225. sliderElement.step = '100';
  226. sliderElement.value = MIN_TOKENS;
  227. sliderElement.style.width = '150px';
  228. sliderElement.style.height = '10px';
  229. sliderElement.style.backgroundColor = '#4a4a4a';
  230. sliderElement.style.cursor = 'pointer';
  231. sliderElement.style.appearance = 'none';
  232. sliderElement.style.outline = 'none';
  233. sliderElement.style.borderRadius = '5px';
  234.  
  235. const style = document.createElement('style');
  236. style.textContent = `
  237. #token-filter-slider::-webkit-slider-thumb {
  238. -webkit-appearance: none;
  239. appearance: none;
  240. width: 16px;
  241. height: 16px;
  242. background: #ffffff;
  243. cursor: pointer;
  244. border-radius: 50%;
  245. border: 2px solid #000;
  246. }
  247. #token-filter-slider::-moz-range-thumb {
  248. width: 16px;
  249. height: 16px;
  250. background: #ffffff;
  251. cursor: pointer;
  252. border-radius: 50%;
  253. border: 2px solid #000;
  254. }
  255. `;
  256. document.head.appendChild(style);
  257.  
  258. const label = document.createElement('span');
  259. label.id = 'token-filter-label';
  260. label.style.color = '#fff';
  261. label.style.fontSize = '12px';
  262. label.style.minWidth = '60px';
  263. label.textContent = `${MIN_TOKENS} tokens`;
  264.  
  265. sliderElement.addEventListener('input', (e) => {
  266. MIN_TOKENS = parseInt(e.target.value);
  267. label.textContent = `${MIN_TOKENS} tokens`;
  268. localStorage.setItem('janitorAITokenFilter', MIN_TOKENS);
  269. filterCards();
  270. });
  271.  
  272. sliderContainer.appendChild(sliderElement);
  273. sliderContainer.appendChild(label);
  274. document.body.appendChild(sliderContainer);
  275. }
  276.  
  277. sliderContainer.style.display = isSliderVisible && !isChatsPage() ? 'flex' : 'none';
  278. }
  279.  
  280. function updateElementsVisibility() {
  281. const shouldShow = isAllowedPage() && !isChatsPage();
  282. console.log('Should show controls:', shouldShow);
  283. if (controlPanel) {
  284. controlPanel.style.display = shouldShow ? 'flex' : 'none';
  285. }
  286. if (sliderContainer) {
  287. sliderContainer.style.display = shouldShow && isSliderVisible ? 'flex' : 'none';
  288. }
  289. }
  290.  
  291. function initialize() {
  292. createControlPanel();
  293. createOrUpdateSlider();
  294. updateElementsVisibility();
  295.  
  296. if (isAllowedPage() && !isChatsPage()) {
  297. filterCards();
  298. setupPaginationScroll();
  299.  
  300. const sidebar = document.querySelector('.css-h988mi');
  301. if (sidebar && isSidebarHidden) {
  302. sidebar.style.display = 'none';
  303. }
  304.  
  305. const observer = new MutationObserver(() => {
  306. filterCards();
  307. setupPaginationScroll();
  308. });
  309. observer.observe(document.body, { childList: true, subtree: true });
  310. }
  311. }
  312.  
  313. const tryInitialize = () => {
  314. if (document.body) {
  315. initialize();
  316.  
  317. let lastPath = window.location.pathname;
  318. setInterval(() => {
  319. if (lastPath !== window.location.pathname) {
  320. lastPath = window.location.pathname;
  321. updateElementsVisibility();
  322. if (isAllowedPage() && !isChatsPage()) {
  323. filterCards();
  324. setupPaginationScroll();
  325. }
  326. }
  327. }, 500);
  328. } else {
  329. setTimeout(tryInitialize, 1000);
  330. }
  331. };
  332.  
  333. tryInitialize();
  334. })();