Greasy Fork

Greasy Fork is available in English.

X/Twitter 优化推文按钮

可以自由显示/隐藏,推文上的按钮,包括,回覆、转推、喜欢、观看次数、书签、分享等按钮,并且有中英两种功能语言可以切换

当前为 2025-08-11 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         X/Twitter Optimized Tweet Buttons
// @name:zh-TW   X/Twitter 優化推文按鈕
// @name:zh-CN   X/Twitter 优化推文按钮
// @namespace    http://tampermonkey.net/
// @version      5.5
// @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"]:not(:has(svg g.download)),
                button[aria-label="分享貼文"]:not(:has(svg g.download)),
                button[aria-label="分享"]:not(:has(svg g.download)),
                button[aria-label="Compartir publicación"]:not(:has(svg g.download))
                { 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);
    })();
})();