X - 优化推文按钮

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

目前为 2025-04-12 提交的版本。查看 最新版本

// ==UserScript==
// @name         X - Optimized Tweet Buttons
// @name:zh-TW   X - 優化推文按鈕
// @name:zh-CN   X - 优化推文按钮
// @namespace    http://tampermonkey.net/
// @version      5.2
// @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: {       // 監控配置:設定 subtree 為 false
            childList: true,
            subtree: false,
            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: {
            reply: '回覆',
            retweet: '轉推',
            bookmark: '書籤',
            views: '觀看次數',
            share: '分享',
            like: '喜歡',
            language: '語言'
        }
    };

    // 每次調用時根據最新配置返回對應語言字串
    const 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="分享貼文"]:not(:has(svg g.download)) { display: none !important; }'],
            ['hideLike', '[data-testid="like"], [data-testid="unlike"] { display: none !important; }']
        ]),
        init() {
            this.element = document.createElement('style');
            this.element.id = 'x-btn-hider-styles';
            document.head.appendChild(this.element);
            this.update();
        },
        update() {
            // 取得當前配置,並套用生效的 CSS 規則
            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() {
            // 清除舊選單(假如 GM_unregisterMenuCommand 可用)
            menu.cmds.forEach(id => {
                if (typeof GM_unregisterMenuCommand === 'function') {
                    GM_unregisterMenuCommand(id);
                }
            });
            menu.cmds = [];

            // 使用配置的緩存,避免多次調用 config.get()
            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] ? '✅' : '❌';
                menu.cmds.push(GM_registerMenuCommand(
                    `${label} ${status}`,
                    () => {
                        // 更新對應設定,然後防抖後重載頁面
                        config.update(key, !config.get()[key]);
                        debouncedReload();
                    }
                ));
            });

            // 語言切換
            const langStatus = currentConfig.language === 'EN' ? 'EN' : 'ZH';
            menu.cmds.push(GM_registerMenuCommand(
                `${t().language}: ${langStatus}`,
                () => {
                    config.update('language', config.get().language === 'EN' ? 'ZH' : 'EN');
                    debouncedReload();
                }
            ));
        }
    };

    // === 防抖工具函數 ===
    const debounce = (func, delay) => {
        let timer;
        return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => func(...args), delay);
        };
    };

    const debouncedReload = debounce(() => location.reload(), 300);
    const debouncedStyleUpdate = debounce(() => style.update(), OPT.debounceTime);

    // === 初始化流程 ===
    (function init() {
        // 初始化樣式管理
        style.init();
        // 建立選單
        menu.build();
        // 初始化 MutationObserver,當 DOM 發生新增時更新樣式
        const observer = new MutationObserver(mutations => {
            if (mutations.some(m => m.addedNodes.length > 0)) {
                debouncedStyleUpdate();
            }
        });
        observer.observe(document.body, OPT.observerConfig);
    })();
})();