Greasy Fork 还支持 简体中文。

JanitorAI Token Filter

Filters character cards on JanitorAI by token count

目前為 2025-04-09 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name JanitorAI Token Filter
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.12
  5. // @description Filters character cards on JanitorAI by token count
  6. // @author You
  7. // @match https://janitorai.com/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. let MIN_TOKENS = 2000;
  15. let selectElement = null;
  16.  
  17. function parseTokens(tokenText) {
  18. try {
  19. let cleanText = tokenText.replace(/<!--[\s\S]*?-->/g, '').replace('tokens', '').trim();
  20. if (cleanText.includes('k')) {
  21. return parseFloat(cleanText.replace('k', '')) * 1000;
  22. }
  23. return parseInt(cleanText, 10) || 0;
  24. } catch (error) {
  25. return 0;
  26. }
  27. }
  28.  
  29. function filterCards() {
  30. const cards = document.querySelectorAll('.chakra-stack.css-1s5evre');
  31. cards.forEach(card => {
  32. const tokenElement = card.querySelector('.chakra-text.css-jccmq6');
  33. if (!tokenElement) return;
  34.  
  35. const tokenCount = parseTokens(tokenElement.textContent);
  36. const parentContainer = card.closest('.css-1sxhvxh');
  37. if (parentContainer) {
  38. parentContainer.style.display = tokenCount < MIN_TOKENS ? 'none' : '';
  39. }
  40. });
  41. }
  42.  
  43. function createOrUpdateSelector() {
  44. if (!selectElement) {
  45. selectElement = document.createElement('select');
  46. selectElement.id = 'token-filter-select';
  47. selectElement.style.position = 'fixed';
  48. selectElement.style.top = '10px';
  49. selectElement.style.left = '10px';
  50. selectElement.style.zIndex = '99999';
  51. selectElement.style.padding = '4px 8px';
  52. selectElement.style.backgroundColor = '#4a4a4a';
  53. selectElement.style.border = '1px solid #666';
  54. selectElement.style.borderRadius = '4px';
  55. selectElement.style.fontSize = '12px';
  56. selectElement.style.color = '#fff';
  57. selectElement.style.cursor = 'pointer';
  58.  
  59. const options = [100, 500, 1000, 1500, 2000, 2500, 3000, 4000];
  60. options.forEach(value => {
  61. const option = document.createElement('option');
  62. option.value = value;
  63. option.text = `${value} tokens`;
  64. if (value === MIN_TOKENS) option.selected = true;
  65. selectElement.appendChild(option);
  66. });
  67.  
  68. selectElement.addEventListener('change', (e) => {
  69. MIN_TOKENS = parseInt(e.target.value);
  70. filterCards();
  71. });
  72.  
  73. const appendSelector = () => {
  74. if (document.body) {
  75. document.body.appendChild(selectElement);
  76. } else {
  77. setTimeout(appendSelector, 500);
  78. }
  79. };
  80. appendSelector();
  81. }
  82.  
  83. selectElement.style.display = window.location.pathname === '/' ? 'block' : 'none';
  84. }
  85.  
  86. function initialize() {
  87. createOrUpdateSelector();
  88. if (window.location.pathname === '/') {
  89. filterCards();
  90. }
  91. }
  92.  
  93. const tryInitialize = () => {
  94. if (document.body) {
  95. initialize();
  96.  
  97. let lastPath = window.location.pathname;
  98. const checkPath = () => {
  99. if (lastPath !== window.location.pathname) {
  100. lastPath = window.location.pathname;
  101. createOrUpdateSelector();
  102. if (lastPath === '/') {
  103. filterCards();
  104. }
  105. }
  106. };
  107.  
  108. setInterval(checkPath, 500);
  109.  
  110. const observer = new MutationObserver(() => {
  111. if (window.location.pathname === '/') {
  112. setTimeout(filterCards, 500);
  113. }
  114. });
  115. observer.observe(document.body, { childList: true, subtree: true });
  116. } else {
  117. setTimeout(tryInitialize, 1000);
  118. }
  119. };
  120.  
  121. tryInitialize();
  122. })();