// ==UserScript== // @name X - Optimized Tweet Buttons // @name:zh-TW X - 優化推文按鈕 // @name:zh-CN X - 优化推文按钮 // @namespace http://tampermonkey.net/ // @version 5.1 // @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 // @downloadURL none // ==/UserScript== (function() { 'use strict'; // === 性能優化核心 === const OPT = { debounceTime: 500, // 防抖間隔 observerConfig: { // 監控配置:設定 subtree 為 true,以確保監控所有新增節點 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: { 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); })(); })();