TLDR Content Filter & Navigation

Filter sponsored articles, remove margin classes, and add date navigation on tldr.tech

当前为 2025-01-31 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name TLDR Content Filter & Navigation
  3. // @version 0.6
  4. // @description Filter sponsored articles, remove margin classes, and add date navigation on tldr.tech
  5. // @author Jerry with Claude
  6. // @match https://tldr.tech/*
  7. // @grant none
  8. // @homepage https://greasyfork.org/en/scripts/525303
  9.  
  10. // @namespace http://tampermonkey.net/
  11. // ==/UserScript==
  12. // https://tldr.tech/api/latest/ai
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. // Function to get the next business day
  18. function getNextBusinessDay(dateStr) {
  19. const parts = dateStr.split('-').map(Number);
  20. let nextDay = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2]));
  21.  
  22. do {
  23. nextDay = new Date(nextDay.getTime() + 86400000);
  24. } while (nextDay.getUTCDay() === 0 || nextDay.getUTCDay() === 6);
  25.  
  26. return nextDay;
  27. }
  28.  
  29. // Function to get the previous business day
  30. function getPreviousBusinessDay(dateStr) {
  31. const parts = dateStr.split('-').map(Number);
  32. let prevDay = new Date(Date.UTC(parts[0], parts[1] - 1, parts[2]));
  33.  
  34. do {
  35. prevDay = new Date(prevDay.getTime() - 86400000);
  36. } while (prevDay.getUTCDay() === 0 || prevDay.getUTCDay() === 6);
  37.  
  38. return prevDay;
  39. }
  40.  
  41. // Function to format date as YYYY-MM-DD
  42. function formatDate(date) {
  43. return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}-${String(date.getUTCDate()).padStart(2, '0')}`;
  44. }
  45.  
  46. // Function to add navigation buttons
  47. function addNavigationButtons() {
  48. const currentPath = window.location.pathname;
  49. const dateMatch = currentPath.match(/\d{4}-\d{2}-\d{2}/);
  50.  
  51. // Already exists, don't add again
  52. if (document.querySelector('#tldr-nav-container')) {
  53. return;
  54. }
  55.  
  56. if (dateMatch) {
  57. // Create container for buttons
  58. const container = document.createElement('div');
  59. container.id = 'tldr-nav-container';
  60. container.style.cssText = `
  61. position: fixed;
  62. bottom: 20px;
  63. right: 20px;
  64. display: flex;
  65. gap: 8px;
  66. z-index: 9999;
  67. background: rgba(255, 255, 255, 0.9);
  68. padding: 4px;
  69. border-radius: 8px;
  70. box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  71. `;
  72.  
  73. // Common button styles
  74. const buttonStyle = `
  75. padding: 4px 12px;
  76. background: #6366f1;
  77. color: white;
  78. border: none;
  79. border-radius: 6px;
  80. cursor: pointer;
  81. font-size: 14px;
  82. font-weight: 500;
  83. transition: background 0.2s;
  84. `;
  85.  
  86. // Create previous day button
  87. const prevButton = document.createElement('button');
  88. prevButton.textContent = '← Prev';
  89. prevButton.style.cssText = buttonStyle;
  90.  
  91. // Create next day button
  92. const nextButton = document.createElement('button');
  93. nextButton.textContent = 'Next →';
  94. nextButton.style.cssText = buttonStyle;
  95.  
  96. // Add hover effects
  97. [prevButton, nextButton].forEach(button => {
  98. button.addEventListener('mouseover', () => {
  99. button.style.background = '#4f46e5';
  100. });
  101. button.addEventListener('mouseout', () => {
  102. button.style.background = '#6366f1';
  103. });
  104. });
  105.  
  106. // Add click handlers
  107. prevButton.onclick = () => {
  108. const prevDay = getPreviousBusinessDay(dateMatch[0]);
  109. window.location.href = currentPath.replace(dateMatch[0], formatDate(prevDay));
  110. };
  111.  
  112. nextButton.onclick = () => {
  113. const nextDay = getNextBusinessDay(dateMatch[0]);
  114. window.location.href = currentPath.replace(dateMatch[0], formatDate(nextDay));
  115. };
  116.  
  117. // Assemble and insert
  118. container.appendChild(prevButton);
  119. container.appendChild(nextButton);
  120.  
  121. // Insert at the top of the body
  122. const targetElement = document.body;
  123. if (targetElement) {
  124. targetElement.insertBefore(container, targetElement.firstChild);
  125. }
  126. }
  127. }
  128.  
  129. // Function to remove unwanted content
  130. function removeContent() {
  131. // Remove sponsored articles
  132. const articles = document.querySelectorAll('article');
  133. articles.forEach(article => {
  134. const heading = article.querySelector('h3');
  135. if (heading && heading.textContent.includes('(Sponsor)')) {
  136. article.style.display = 'none';
  137. }
  138. });
  139.  
  140. // Remove elements with specific margin classes
  141. const marginElements = document.querySelectorAll('.mb-2');
  142. marginElements.forEach(element => {
  143. element.style.display = 'none';
  144. });
  145. }
  146.  
  147. // Run immediately and also after a short delay to ensure DOM is ready
  148. function initialize() {
  149. removeContent();
  150. addNavigationButtons();
  151.  
  152. // Try again after a short delay
  153. setTimeout(() => {
  154. removeContent();
  155. addNavigationButtons();
  156. }, 1000);
  157. }
  158.  
  159. // Run when page loads
  160. if (document.readyState === 'loading') {
  161. document.addEventListener('DOMContentLoaded', initialize);
  162. } else {
  163. initialize();
  164. }
  165.  
  166. // Watch for dynamic content changes
  167. const observer = new MutationObserver(() => {
  168. removeContent();
  169. addNavigationButtons();
  170. });
  171.  
  172. observer.observe(document.body, {
  173. childList: true,
  174. subtree: true
  175. });
  176. })();