Hide starred repos in trending and remove slob
// ==UserScript==
// @name GitHub: Undiscovered Trending
// @namespace shiftgeist
// @icon https://github.com/fluidicon.png
// @match https://github.com/trending*
// @version 20260402
// @author shiftgeist
// @description Hide starred repos in trending and remove slob
// @license GNU GPLv3
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
const IGNORE_FILTER_KEY = 'undiscovered-filter-ignored'
GM_registerMenuCommand(
'Ignore keywords (' + (GM_getValue(IGNORE_FILTER_KEY) ? 'ON' : 'OFF') + ')',
() => {
const newState = !GM_getValue(IGNORE_FILTER_KEY)
GM_setValue(IGNORE_FILTER_KEY, newState)
location.reload()
}
)
const ignoreKeywordsInText = [
' ai ',
'ai assistant',
'ai chat',
'ai models',
'ai-powered',
'crypto',
'deepseek',
'defi',
'gemini',
'gpt',
'llm',
'mcp',
'ollama',
'openai',
'qwenlm',
'deep learning',
'claude',
'vibe',
'agentic',
'agent',
'deepfake',
]
function log(...params) {
if (localStorage.getItem('undiscovered-debug') === 'true') {
console.log('[undiscovered]', ...params)
}
}
function createButton(text, className, onclick) {
const button = document.createElement('button')
button.className = `Button--secondary Button--small Button ml-2 ${className}`
button.innerText = text
button.onclick = onclick
return button
}
function createIgnoreButton(ignoredRepos, urlToIgnore, onclick) {
return createButton('Ignore', 'ignore-button', () => {
ignoredRepos.push(urlToIgnore)
onclick()
localStorage.setItem('undiscovered-ignored', JSON.stringify(ignoredRepos))
})
}
function createResetButton(ignoredRepos, onclick) {
const button = createButton('Reset', 'ignore-reset', () => {
ignoredRepos = []
onclick()
localStorage.setItem('undiscovered-ignored', JSON.stringify(ignoredRepos))
})
const p = document.createElement('p')
// TODO: This does. not jet update the text
p.innerText = 'Ignored: ' + JSON.stringify(ignoredRepos)
p.append(button)
return p
}
function hasKeyword(article) {
const ignoredFilterEnabled = GM_getValue(IGNORE_FILTER_KEY)
log('ignoredFilterEnabled', ignoredFilterEnabled)
const hasIgnoredKeyword = ignoreKeywordsInText.findIndex(e => article.innerText.toLowerCase().includes(e)) >= 0
log('hasIgnoredKeyword', hasIgnoredKeyword)
return ignoredFilterEnabled && hasIgnoredKeyword
}
function main() {
log('start of main')
setTimeout(() => {
log('delay done')
const ignoredRepos = JSON.parse(localStorage.getItem('undiscovered-ignored') || '[]')
const articles = document.querySelectorAll('article')
const parent = document.querySelector('[data-hpc=""]')
const box = document.querySelector('.Box')
const reset = document.querySelector('.ignore-reset')
if (!reset) {
box.parentElement.append(createResetButton(ignoredRepos, main))
}
log('articles', articles)
for (const article of articles) {
const url = article.querySelector('h2 a').getAttribute('href')
const hasButton = article.querySelector('.ignore-button')
const buttonsContainer = article.querySelector('.float-right.d-flex')
if (!hasButton) {
const button = createIgnoreButton(ignoredRepos, url, () => {
article.remove()
main()
})
buttonsContainer.append(button)
}
if (
// Already starred
article.querySelector('.starred-button-icon').getClientRects().length > 0 ||
// Ignored
ignoredRepos.includes(url) ||
// Contains AI
hasKeyword(article)
) {
article.remove()
}
}
// no repos dispalyed
if (parent.childElementCount === 0) {
const empty = document.createElement('article')
empty.className = 'Box-row'
empty.innerText = 'Nothing to discover'
parent.append(empty)
}
}, 100)
}
log('init')
let previousUrl = ''
const observer = new MutationObserver(function (mutations) {
if (location.href !== previousUrl) {
previousUrl = location.href
main()
}
})
const config = { subtree: true, childList: true }
observer.observe(document, config)