X - 优化推文按钮,可开关功能,中英文选单

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

当前为 2025-04-09 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name X - Optimized Tweet Buttons with Toggleable Features and Bilingual (Chinese/English) Menu
  3. // @name:zh-TW X - 優化推文按鈕,可開關功能,中英文選單
  4. // @name:zh-CN X - 优化推文按钮,可开关功能,中英文选单
  5. // @namespace http://tampermonkey.net/
  6. // @version 4.9
  7. // @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.
  8. // @description:zh-TW 可以自由顯示/隱藏,推文上的按鈕,包括,回覆、轉推、喜歡、觀看次數、書籤、分享等按鈕,並且有中英兩種功能語言可以切換
  9. // @description:zh-CN 可以自由显示/隐藏,推文上的按钮,包括,回覆、转推、喜欢、观看次数、书签、分享等按钮,并且有中英两种功能语言可以切换
  10. // @author deepseek
  11. // @match https://twitter.com/*
  12. // @match https://x.com/*
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @license MIT
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. // === 性能優化核心 ===
  23. const OPT = {
  24. debounceTime: 500, // 防抖間隔
  25. observerConfig: { // 精準監控範圍
  26. childList: true,
  27. subtree: false,
  28. attributes: false,
  29. characterData: false
  30. }
  31. };
  32.  
  33. // === 配置系統 ===
  34. const CONFIG_KEY = 'XButtonSettings';
  35. const defaults = {
  36. hideReply: true,
  37. hideRetweet: true,
  38. hideBookmark: true,
  39. hideViews: true,
  40. hideShare: true,
  41. hideLike: false,
  42. language: 'EN' // 預設英文
  43. };
  44.  
  45. const config = {
  46. get() {
  47. return {...defaults, ...GM_getValue(CONFIG_KEY, {})};
  48. },
  49. update(key, value) {
  50. const current = this.get();
  51. GM_setValue(CONFIG_KEY, {...current, [key]: value});
  52. }
  53. };
  54.  
  55. // === 高效多語言系統 ===
  56. const i18n = {
  57. EN: {
  58. reply: 'Reply',
  59. retweet: 'Retweet',
  60. bookmark: 'Bookmark',
  61. views: 'View count',
  62. share: 'Share',
  63. like: 'Like',
  64. language: 'Language'
  65. },
  66. ZH: {
  67. reply: '回覆',
  68. retweet: '轉推',
  69. bookmark: '書籤',
  70. views: '觀看次數',
  71. share: '分享',
  72. like: '喜歡',
  73. language: '語言'
  74. }
  75. };
  76.  
  77. const t = () => i18n[config.get().language];
  78.  
  79. // === 樣式管理 ===
  80. const style = {
  81. element: null,
  82. rules: new Map([
  83. ['hideReply', '[data-testid="reply"]{display:none!important}'],
  84. ['hideRetweet','[data-testid="retweet"]{display:none!important}'],
  85. ['hideBookmark','[data-testid="bookmark"]{display:none!important}'],
  86. ['hideViews','a[href*="/analytics"]{display:none!important}'],
  87. ['hideShare','button[aria-label="分享貼文"]:not(:has(svg g.download)){display:none!important}'],
  88. ['hideLike','[data-testid="like"],[data-testid="unlike"]{display:none!important}']
  89. ]),
  90. init() {
  91. this.element = document.createElement('style');
  92. this.element.id = 'x-btn-hider-styles';
  93. document.head.append(this.element);
  94. this.update();
  95. },
  96. update() {
  97. const activeRules = Object.entries(config.get())
  98. .filter(([k,v]) => this.rules.has(k) && v)
  99. .map(([k]) => this.rules.get(k));
  100.  
  101. this.element.textContent = activeRules.join('');
  102. }
  103. };
  104.  
  105. // === 選單系統 (防抖優化) ===
  106. const menu = {
  107. cmds: [],
  108. build: () => {
  109. // 清除舊選單
  110. menu.cmds.forEach(id => GM_unregisterMenuCommand(id));
  111. menu.cmds = [];
  112.  
  113. // 主功能選單
  114. const items = [
  115. {key: 'hideReply', label: t().reply},
  116. {key: 'hideRetweet', label: t().retweet},
  117. {key: 'hideBookmark', label: t().bookmark},
  118. {key: 'hideViews', label: t().views},
  119. {key: 'hideShare', label: t().share},
  120. {key: 'hideLike', label: t().like}
  121. ];
  122.  
  123. items.forEach(({key, label}) => {
  124. const status = config.get()[key] ? '✅' : '❌';
  125. menu.cmds.push(GM_registerMenuCommand(
  126. `${label} ${status}`,
  127. () => {
  128. config.update(key, !config.get()[key]);
  129. debouncedReload();
  130. }
  131. ));
  132. });
  133.  
  134. // 語言切換
  135. const langStatus = config.get().language === 'EN' ? 'EN' : 'ZH';
  136. menu.cmds.push(GM_registerMenuCommand(
  137. `${t().language}: ${langStatus}`,
  138. () => {
  139. config.update('language', config.get().language === 'EN' ? 'ZH' : 'EN');
  140. debouncedReload();
  141. }
  142. ));
  143. }
  144. };
  145.  
  146. // === 性能優化工具 ===
  147. const debounce = (func, delay) => {
  148. let timer;
  149. return (...args) => {
  150. clearTimeout(timer);
  151. timer = setTimeout(() => func(...args), delay);
  152. };
  153. };
  154.  
  155. const debouncedReload = debounce(() => location.reload(), 300);
  156. const debouncedStyleUpdate = debounce(() => style.update(), OPT.debounceTime);
  157.  
  158. // === 初始化流程 ===
  159. (function init() {
  160. // 樣式初始化
  161. style.init();
  162.  
  163. // 選單初始化
  164. menu.build();
  165.  
  166. // 精準DOM監控
  167. const observer = new MutationObserver(mutations => {
  168. if (mutations.some(m => m.addedNodes.length > 0)) {
  169. debouncedStyleUpdate();
  170. }
  171. });
  172.  
  173. observer.observe(document.body, OPT.observerConfig);
  174. })();
  175. })();