Linux.po

对 linux.do 的增强脚本

当前为 2025-01-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Linux.po
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1.1
  5. // @description 对 linux.do 的增强脚本
  6. // @author PRO-2684
  7. // @match https://linux.do/*
  8. // @run-at document-start
  9. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABOxJREFUWEfNV0tslFUU/s7/nmmHmWE6NRUqKZXER9OIuNFA3Ei6w6CUWiPGjRiaSLtoWZi4MnFBu2gxgYgLjRgqtNHIrsGFBqIbAdPUR4LQYIXGTseZ6WPmfx+9f+dvphRIDWPxzm7+e8/3nXPPOfc7hLssZiYAKhHZ4RZm3gtgD4Am27Z3EpEsvjGzp2naRQCTAM4R0ZcVZzQADhHxnaAEyKrFzALYKRtPADjuOE7n2NgYLly4iPHxcVy6dEVAl88SduzYjtbWVuzatRNtbW1QVXUYQBcR5ct2lm1WAq4iwMxa6DUzD7qu2z04OIRjxz7A1NQNF4BfUxOTa2pqZKKl48yMxcVFb3Fx3gMgNTZuUQ4ffhs9Pd1QFGXoHxI95X3LtkMSKwgws05EFjNvBDAxMjLacOhQF7LZjF1XV6/GYjESoJ7nwff9FZGTJAmyLAdk5ufneXZ2xkml0tqJE8fR3r5vGkALEf0VYqwiEHpeLBY3RyKRqb6+IxgY6DfT6YeMWCwGx3EC42tZgqSqqoIIMpk/zd7ePqO//yhKpVJjNBr9ozLKQQTCOy97nu3oeAVnz56xmpqa9Tt5uxYSYk8YlcnJa9b+/R36mTOfi79T5UgEOUEi28MMZeZbfX1HGgYG+q3m5m26ZVlrxbrnPl3Xce3aVau3t0/v7z86TUQPlx0nQSBIDJFwo6Oj3e3t7WZz8zbDtperryokNE0TJMyRkVFj376Xg8QMsMNSy2Qyufr6ehE4RCKRVUl2vyzEdZRKJVFEmJmZQTqdTooSDQjYtn365s2bnRMTE3Y8Htc8z0e5wu4Xd/m8yF9ZllAoFOyWlhZt06ZNw5qmvRoQyOVybBgGDMMQaX7H5lQ1JgCbpkmmaSKZTBLlcrm9RPQFM7ue5ylVBLqrKVmWXSJSmPklKhQKHzPzG+ImAIi+vR4rwCKiTyifz38D4HkAoo0Gj8s6rBDrW0FA9Pf1Ar7dN08QWFt//Y/C8r8g8MCv4MEm4QMvw7ARAeyC16cRgWQXKDcikdz5fI4hGWBlfVoxuSbBN5FIJJeenPy8e5rMG53y3GWb1aQGXim3qlaBJIGcvO1t2K6xsWU4EVPKjxFzgnKZXHysfukpkqoGudKQ6H8ACm0z4GRaPEV5+olZe5LInmUejPz2WXf0+wMmElEDfnUFCSQNyBfN4rOnjNKjrw3VEfUI7BWSLOvxrdrLbzVov560EI/q8KsjySDpQKFo2Y8d1Bee/nA6JVdIsrIiCgRiocAb3SiyG77bDeX3ry3EI7ooDrCI3b+VCQyIwYkUoFCy3Ede0OeeOw+liFQ8HsjzJVEaXlKoDbNF3gwVU7U/HoT2y0cmamFAiQK+GJTWmpwSIKmAWwQWYNqPv2ksPHUScNCYitJqWR6SuMqsbyOygkjUYsK4fqohOv46YMJGBCpkgwKtJiJye6WQtOSx0F6eySjBgQGt2PopzK0HppUFtAjPQ4wQ856jWZZ5UFrwuo3J92Bcfx9YdFwQfEEl+FUuDx4ceGBIqFEVc+s7MJvehV8rD6XWOppVXMfyIJljTvjAcWnB7VQzX0HNnoc8dwly4YeV+PFn4G3YASe1G076Rfi1yrAEdIlSq8yz2wv8rpklBpafAVWUaHholnkv+dgjWWiC5+6sEDIeZOWir2OSJZyrqxjPRak9cY/x/G9pa4SNlgBs7AAAAABJRU5ErkJggg==
  10. // @license gpl-3.0
  11. // @grant GM_setValue
  12. // @grant GM_getValue
  13. // @grant GM_deleteValue
  14. // @grant GM_registerMenuCommand
  15. // @grant GM_unregisterMenuCommand
  16. // @grant GM_addValueChangeListener
  17. // @require https://github.com/PRO-2684/GM_config/releases/download/v1.2.1/config.min.js#md5=525526b8f0b6b8606cedf08c651163c2
  18. // ==/UserScript==
  19.  
  20. (function () {
  21. 'use strict';
  22. const { name, version } = GM_info.script;
  23. const idPrefix = "linux-po-";
  24. const configDesc = {
  25. $default: {
  26. autoClose: false,
  27. },
  28. appearance: {
  29. name: "🎨 外观",
  30. title: "外观",
  31. type: "folder",
  32. items: {
  33. sidebarManager: {
  34. name: "⬅️ 侧栏管理",
  35. title: "允许你隐藏侧栏中的各个部分",
  36. type: "folder",
  37. items: {
  38. $default: {
  39. value: false,
  40. title: (prop, value, desc) => desc.name,
  41. formatter: (prop, value, desc) => `${desc.name}: ${value ? "🫥" : "👀"}`,
  42. },
  43. customCategories: {
  44. name: "自定义板块",
  45. type: "bool",
  46. },
  47. externalLinks: {
  48. name: "外部链接",
  49. type: "bool",
  50. },
  51. categories: {
  52. name: "类别",
  53. type: "bool",
  54. },
  55. tags: {
  56. name: "标签",
  57. type: "bool",
  58. },
  59. messages: {
  60. name: "消息",
  61. type: "bool",
  62. },
  63. channels: {
  64. name: "频道",
  65. type: "bool",
  66. },
  67. directMessages: {
  68. name: "直接消息",
  69. type: "bool",
  70. },
  71. chat: {
  72. name: "聊天",
  73. type: "bool",
  74. },
  75. bottomMenu: {
  76. name: "底部菜单",
  77. type: "bool",
  78. },
  79. },
  80. },
  81. },
  82. },
  83. accessibility: {
  84. name: "♿ 辅助功能",
  85. title: "辅助功能",
  86. type: "folder",
  87. items: {
  88. largerClickArea: {
  89. name: "👆 增大点击区域",
  90. title: "增大帖子列表中各帖子的可点击区域 (仅支持左键)",
  91. type: "bool",
  92. value: true,
  93. },
  94. showPostsFloor: {
  95. name: "🔢 显示楼层",
  96. title: "在帖子中显示楼层",
  97. type: "bool",
  98. value: false,
  99. },
  100. },
  101. },
  102. };
  103. const config = new GM_config(configDesc);
  104.  
  105. // Helper function for css
  106. function injectCSS(id, css) {
  107. const style = document.head.appendChild(document.createElement("style"));
  108. style.id = idPrefix + id;
  109. style.textContent = css;
  110. return style;
  111. }
  112. function cssHelper(id, enable) {
  113. const current = document.getElementById(idPrefix + id);
  114. if (current) {
  115. current.disabled = !enable;
  116. } else if (enable) {
  117. injectCSS(id, dynamicStyles[id]);
  118. }
  119. }
  120. /**
  121. * Generates CSS for hiding given sidebar section.
  122. */
  123. function hideSidebarSection(section) {
  124. return `#d-sidebar > .sidebar-sections div.sidebar-section[data-section-name="${section}"] { display: none; }`;
  125. }
  126.  
  127. // Dynamic styles
  128. const dynamicStyles = {
  129. "appearance.sidebarManager.customCategories": hideSidebarSection("community"),
  130. "appearance.sidebarManager.externalLinks": hideSidebarSection("外部链接"),
  131. "appearance.sidebarManager.categories": hideSidebarSection("categories"),
  132. "appearance.sidebarManager.tags": hideSidebarSection("tags"),
  133. "appearance.sidebarManager.messages": hideSidebarSection("messages"),
  134. "appearance.sidebarManager.channels": hideSidebarSection("chat-channels"),
  135. "appearance.sidebarManager.directMessages": hideSidebarSection("chat-dms"),
  136. "appearance.sidebarManager.chat": "#d-sidebar > button[data-key='chat'] { display: none; }",
  137. "appearance.sidebarManager.bottomMenu": "#d-sidebar > div.sidebar-footer-wrapper { display: none; }",
  138. "accessibility.largerClickArea": ".topic-list-item > .main-link { cursor: pointer; }",
  139. "accessibility.showPostsFloor": `.post-stream > .topic-post > article[id^='post_']::after {
  140. content: attr(id) '#'; color: var(--primary-med-or-secondary-med);
  141. position: absolute; right: 0; top: calc(0.8em + 1px);
  142. text-indent: -2.4em; overflow: hidden; /* Dirty trick to hide leading "post_" */
  143. }`,
  144. };
  145. for (const prop in dynamicStyles) {
  146. cssHelper(prop, config.get(prop));
  147. }
  148.  
  149. // Accessibility
  150. // Larger click area
  151. let largerClickAreaEnabled = false;
  152. /**
  153. * Handles the click event when larger click area is enabled.
  154. * @param {MouseEvent} e
  155. */
  156. function largerClickAreaHandler(e) {
  157. if (e.defaultPrevented || !e.isTrusted) return;
  158. e.preventDefault();
  159. const mainLink = e.target.closest(".topic-list-item > .main-link");
  160. if (mainLink) {
  161. const title = mainLink.querySelector(".title.raw-link.raw-topic-link");
  162. title?.click();
  163. }
  164. }
  165. /**
  166. * Enables or disables the larger click area feature.
  167. * @param {boolean} enable
  168. */
  169. function largerClickArea(enable) {
  170. if (enable && !largerClickAreaEnabled) {
  171. document.body.addEventListener("click", largerClickAreaHandler);
  172. largerClickAreaEnabled = true;
  173. } else if (!enable && largerClickAreaEnabled) {
  174. document.body.removeEventListener("click", largerClickAreaHandler);
  175. largerClickAreaEnabled = false;
  176. }
  177. }
  178.  
  179. // Callbacks
  180. const callbacks = {
  181. "accessibility.largerClickArea": largerClickArea,
  182. };
  183. for (const [prop, callback] of Object.entries(callbacks)) {
  184. callback(config.get(prop));
  185. }
  186. config.addEventListener("set", e => {
  187. if (e.detail.prop in dynamicStyles) {
  188. cssHelper(e.detail.prop, e.detail.after);
  189. }
  190. if (e.detail.prop in callbacks) {
  191. callbacks[e.detail.prop](e.detail.after);
  192. }
  193. });
  194.  
  195. // https://linux.do/emojis.json
  196. })();
  197.