您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hide retweets, and other UI tweaks for New Twitter
当前为
// ==UserScript== // @name Tweak New Twitter // @description Hide retweets, and other UI tweaks for New Twitter // @namespace https://github.com/insin/tweak-new-twitter/ // @match https://twitter.com/* // @version 2 // ==/UserScript== const HOME = 'Home' const LATEST_TWEETS = 'Latest Tweets' /** Title of the current page, without the ' / Twitter' suffix */ let currentPage = '' /** MutationObservers active on the current page */ let pageObservers = [] /** * Default config enables all features. * * You'll need to edit the config object manually for now if you're using this * as a user script. */ let config = { hideRetweets: true, hideSidebarContent: true, navBaseFontSize: true, hideExploreNav: true, hideBookmarksNav: true, hideListsNav: true, enableDebugLogging: false, } function addStyle(css) { let $style = document.createElement('style') $style.dataset.insertedBy = 'tweak-new-twitter' $style.textContent = css document.head.appendChild($style) return $style } function log(...args) { if (config.enableDebugLogging) { console.log(`TWT${currentPage ? `(${currentPage})` : ''}`, ...args) } } function s(n) { return n == 1 ? '' : 's' } function applyCss() { var cssRules = [] var hideCssSelectors = [] if (config.hideSidebarContent) { hideCssSelectors.push( // Trends 'div[data-testid="sidebarColumn"] section', // Who to follow 'div[data-testid="sidebarColumn"] aside', // Footery stuff in the side bar because infinite scroll 'div[data-testid="sidebarColumn"] nav' ) } if (config.hideExploreNav) { hideCssSelectors.push('nav a[href="/explore"]') } if (config.hideExploreNav) { hideCssSelectors.push('nav a[href="/i/bookmarks"]') } if (config.hideListsNav) { hideCssSelectors.push('nav a[href*="/lists"]') } if (hideCssSelectors.length > 0) { cssRules.push(`${hideCssSelectors.join(', ')} { display: none !important; }`) } if (cssRules.length > 0) { addStyle(cssRules.join('\n')) } } function observeTitle() { let $title = document.querySelector('title') if ($title == null) { log('waiting for title') return setTimeout(observeTitle, 1000 / 60 * 3) } function processPage(title) { // Ignore Flash of Uninitialised Title navigating to a screen for the first time if (title == 'Twitter') { return } // Assumption: all non-FOUT page title changes are navigation / redraws if (pageObservers.length > 0) { log(`disconnecting ${pageObservers.length} page observer${s(pageObservers.length)}`) pageObservers.forEach(observer => observer.disconnect()) pageObservers = [] } currentPage = title.split(' / ')[0] log('navigation occurred') if (currentPage == HOME || currentPage == LATEST_TWEETS) { if (config.hideRetweets) { observeTimeline(currentPage) } } if (config.hideSidebarContent) { hideSidebarContents(currentPage) } } processPage($title.textContent) // Watch for page navigation log('observing <title> text') new MutationObserver((mutations) => { processPage($title.textContent) }).observe($title, { characterData: true, childList: true, }) } function observeTimeline(page) { var $timeline = document.querySelector('div[aria-label^="Timeline"]') var $firstTweet = document.querySelector('div[data-testid="tweet"]') if ($timeline == null || $timeline.firstElementChild == null || $timeline.firstElementChild.firstElementChild == null || $firstTweet == null) { if (currentPage != page) { return log('stopped waiting for ${page} timeline') } return setTimeout(observeTimeline, 1000 / 60 * 5, page) } function processTweets() { let tweets = document.querySelectorAll('div[data-testid="tweet"]') log(`processing ${tweets.length} tweet${s(tweets.length)}`) for (let $tweet of tweets) { let isRetweet = $tweet.previousElementSibling && $tweet.previousElementSibling.textContent.includes('Retweeted') if (isRetweet) { $tweet.parentNode.parentNode.parentNode.style.display = 'none' } } } // Watch for new tweets being rendered log('observing timeline childList') processTweets() let $tweetContainer = $timeline.firstElementChild.firstElementChild let observer = new MutationObserver(processTweets) observer.observe($tweetContainer, { childList: true }) pageObservers.push(observer) } // TODO Re-trigger removal when the horizontal breakpoint is hit while resizing function hideSidebarContents(page, attempts = 1) { let $trending = document.querySelector('div[data-testid="sidebarColumn"] section') if ($trending) { $trending.parentNode.parentNode.parentNode.style.display = 'none' } let $whoToFollow = document.querySelector('div[data-testid="sidebarColumn"] aside') if ($whoToFollow) { $whoToFollow.parentNode.parentNode.style.display = 'none' } if ($trending == null || $whoToFollow == null) { if (currentPage != page) { return log(`stopped waiting for ${page} sidebar`) } if (attempts == 10) { return log(`stopped waiting for sidebar content after ${attempts} attempts`) } return setTimeout(hideSidebarContents, 333, page, attempts + 1) } log('hid all sidebar content') } function observeHtmlFontSize() { let $html = document.querySelector('html') let $style = addStyle('') function setCss(fontSize) { log(`setting nav font size to ${fontSize}`) $style.textContent = [ `nav[aria-label="Primary"] div[dir="auto"] span { font-size: ${fontSize}; font-weight: normal; }`, 'nav[aria-label="Primary"] div[dir="auto"] { margin-top: -4px; }' ].join('\n') } log('observing <html> style attribute') let lastFontSize = '' new MutationObserver(() => { if ($html.style.fontSize != lastFontSize) { lastFontSize = $html.style.fontSize setCss($html.style.fontSize) } }).observe($html, { attributes: true, attributeFilter: ['style'] }) } function main() { log('config', config) applyCss() if (config.navBaseFontSize) { observeHtmlFontSize() } if (config.hideRetweets || config.hideSidebarContents) { observeTitle() } } main()