- // ==UserScript==
- // @name X - Optimized Tweet Buttons
- // @name:zh-TW X - 優化推文按鈕
- // @name:zh-CN X - 优化推文按钮
- // @namespace http://tampermonkey.net/
- // @version 5.3
- // @description You can freely show or hide the buttons on a tweet, including Reply, Retweet, Like, View Count, Bookmark, and Share. The interface supports switching between Chinese and English.
- // @description:zh-TW 可以自由顯示/隱藏,推文上的按鈕,包括,回覆、轉推、喜歡、觀看次數、書籤、分享等按鈕,並且有中英兩種功能語言可以切換
- // @description:zh-CN 可以自由显示/隐藏,推文上的按钮,包括,回覆、转推、喜欢、观看次数、书签、分享等按钮,并且有中英两种功能语言可以切换
- // @author chatgpt
- // @match https://twitter.com/*
- // @match https://x.com/*
- // @grant GM_registerMenuCommand
- // @grant GM_getValue
- // @grant GM_setValue
- // @license MIT
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- // === 性能優化核心 ===
- const OPT = {
- debounceTime: 500,
- observerConfig: {
- childList: true,
- subtree: true,
- attributes: false,
- characterData: false
- }
- };
-
- // === 配置系統 ===
- const CONFIG_KEY = 'XButtonSettings';
- const defaults = {
- hideReply: true,
- hideRetweet: true,
- hideBookmark: true,
- hideViews: true,
- hideShare: true,
- hideLike: false,
- language: 'EN'
- };
-
- const config = {
- get() {
- return { ...defaults, ...GM_getValue(CONFIG_KEY, {}) };
- },
- update(key, value) {
- const current = this.get();
- GM_setValue(CONFIG_KEY, { ...current, [key]: value });
- }
- };
-
- // === 多語言系統 ===
- const i18n = {
- EN: {
- reply: 'Reply',
- retweet: 'Retweet',
- bookmark: 'Bookmark',
- views: 'View count',
- share: 'Share',
- like: 'Like',
- language: 'Language'
- },
- 'ZH-TW': {
- reply: '回覆',
- retweet: '轉推',
- bookmark: '書籤',
- views: '觀看次數',
- share: '分享',
- like: '喜歡',
- language: '語言'
- }
- };
-
- function t() {
- const { language } = config.get();
- return i18n[language] || i18n.EN;
- }
-
- // === 樣式管理 ===
- const style = {
- element: null,
- rules: new Map([
- ['hideReply', '[data-testid="reply"] { display: none !important; }'],
- ['hideRetweet', '[data-testid="retweet"] { display: none !important; }'],
- ['hideBookmark', '[data-testid="bookmark"] { display: none !important; }'],
- ['hideViews', 'a[href*="/analytics"] { display: none !important; }'],
- ['hideShare', 'button[aria-label="Share Post"],button[aria-label="分享貼文"],button[aria-label="分享"],button[aria-label="Compartir publicación"] { display: none !important; }'],
- ['hideLike', '[data-testid="like"], [data-testid="unlike"] { display: none !important; }']
- ]),
- init() {
- if (!document.getElementById('x-btn-hider-styles')) {
- this.element = document.createElement('style');
- this.element.id = 'x-btn-hider-styles';
- document.head.appendChild(this.element);
- } else {
- this.element = document.getElementById('x-btn-hider-styles');
- }
- this.update();
- },
- update() {
- const currentConfig = config.get();
- const activeRules = Array.from(this.rules.entries())
- .filter(([key]) => currentConfig[key])
- .map(([, rule]) => rule);
- this.element.textContent = activeRules.join('\n');
- }
- };
-
- // === 選單系統 ===
- const menu = {
- cmds: [],
- build() {
- this.cmds = [];
- const currentConfig = config.get();
- const items = [
- { key: 'hideReply', label: t().reply },
- { key: 'hideRetweet', label: t().retweet },
- { key: 'hideBookmark', label: t().bookmark },
- { key: 'hideViews', label: t().views },
- { key: 'hideShare', label: t().share },
- { key: 'hideLike', label: t().like }
- ];
- items.forEach(({ key, label }) => {
- const status = currentConfig[key] ? '✅' : '❌';
- this.cmds.push(GM_registerMenuCommand(
- `${label} ${status}`,
- () => {
- config.update(key, !config.get()[key]);
- location.reload(); // 直接刷新頁面
- }
- ));
- });
- // 語言切換
- let langStatus = '';
- if (currentConfig.language === 'EN') {
- langStatus = 'EN';
- } else {
- langStatus = '中文';
- }
- this.cmds.push(GM_registerMenuCommand(
- `${t().language}: ${langStatus}`,
- () => {
- config.update('language', currentConfig.language === 'EN' ? 'ZH-TW' : 'EN');
- location.reload(); // 直接刷新頁面
- }
- ));
- }
- };
-
- // === 防抖工具函數 ===
- function debounce(func, delay) {
- let timer;
- return (...args) => {
- clearTimeout(timer);
- timer = setTimeout(() => func(...args), delay);
- };
- }
-
- const debouncedStyleUpdate = debounce(() => style.update(), OPT.debounceTime);
-
- // === 初始化流程 ===
- (function init() {
- style.init();
- menu.build();
- const observer = new MutationObserver(mutations => {
- if (mutations.some(m => m.addedNodes.length > 0)) {
- debouncedStyleUpdate();
- }
- });
- observer.observe(document.body, OPT.observerConfig);
- })();
- })();