GitHub: Undiscovered Trending

Hide starred repos in trending and remove slob

  1. // ==UserScript==
  2. // @name GitHub: Undiscovered Trending
  3. // @namespace shiftgeist
  4. // @icon https://github.com/fluidicon.png
  5. // @match https://github.com/trending*
  6. // @grant none
  7. // @version 20250422
  8. // @author shiftgeist
  9. // @description Hide starred repos in trending and remove slob
  10. // @license GNU GPLv3
  11. // ==/UserScript==
  12.  
  13. function log(...params) {
  14. if (localStorage.getItem('undiscovered-debug') === 'true') {
  15. console.log('[undiscovered]', ...params)
  16. }
  17. }
  18.  
  19. function createIgnoreButton(ignoredRepos, urlToIgnore, onclick) {
  20. const button = document.createElement('button')
  21. button.className = 'Button--secondary Button--small Button ml-2 ignore-button'
  22. button.innerText = 'Ignore'
  23. button.onclick = () => {
  24. ignoredRepos.push(urlToIgnore)
  25. onclick()
  26. localStorage.setItem('undiscovered-ignored', JSON.stringify(ignoredRepos))
  27. }
  28. return button
  29. }
  30.  
  31. function main() {
  32. log('start of main')
  33.  
  34. setTimeout(() => {
  35. log('delay done')
  36.  
  37. const ignoredRepos = JSON.parse(localStorage.getItem('undiscovered-ignored') || '[]')
  38.  
  39. const articles = document.querySelectorAll('article')
  40. const parent = document.querySelector('[data-hpc=""]')
  41.  
  42. for (const article of articles) {
  43. const url = article.querySelector('h2 a').getAttribute('href')
  44. const hasButton = article.querySelector('.ignore-button')
  45. const buttonsContainer = article.querySelector('.float-right.d-flex')
  46.  
  47. if (!hasButton) {
  48. function onclick() {
  49. article.remove()
  50. main()
  51. }
  52.  
  53. const button = createIgnoreButton(ignoredRepos, url, onclick)
  54. buttonsContainer.append(button)
  55. }
  56.  
  57. if (
  58. // Already starred
  59. article.querySelector('.starred-button-icon').getClientRects().length > 0 ||
  60. // Ignored
  61. ignoredRepos.includes(url) ||
  62. // Contains AI
  63. [
  64. ' ai ',
  65. 'ai assistant',
  66. 'ai chat',
  67. 'ai models',
  68. 'ai-powered',
  69. 'crypto',
  70. 'deepseek',
  71. 'defi',
  72. 'gemini',
  73. 'gpt',
  74. 'llm',
  75. 'mcp',
  76. 'ollama',
  77. 'openai',
  78. 'qwenlm',
  79. ].findIndex(e => article.innerText.toLowerCase().includes(e)) >= 0
  80. ) {
  81. article.remove()
  82. }
  83. }
  84.  
  85. // no repos dispalyed
  86. if (parent.childElementCount === 0) {
  87. const empty = document.createElement('article')
  88. empty.className = 'Box-row'
  89. empty.innerText = 'Nothing to discover'
  90. parent.append(empty)
  91. }
  92. }, 100)
  93. }
  94.  
  95. log('init')
  96.  
  97. let previousUrl = ''
  98. const observer = new MutationObserver(function (mutations) {
  99. if (location.href !== previousUrl) {
  100. previousUrl = location.href
  101. main()
  102. }
  103. })
  104. const config = { subtree: true, childList: true }
  105. observer.observe(document, config)