班固米代码高亮

使用 highlight.js 高亮和检测代码块的语言,添加一键复制按钮

  1. // ==UserScript==
  2. // @name 班固米代码高亮
  3. // @namespace https://bgm.tv/group/topic/409276
  4. // @version 1.7
  5. // @description 使用 highlight.js 高亮和检测代码块的语言,添加一键复制按钮
  6. // @author mvm
  7. // @include /^https?:\/\/(((fast\.)?bgm\.tv)|chii\.in|bangumi\.tv)\/(group|subject)\/topic\/*/
  8. // @include /^https?:\/\/(((fast\.)?bgm\.tv)|chii\.in|bangumi\.tv)\/(ep|person|character|blog|pm)\/*/
  9. // @grant none
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (async function() {
  14. 'use strict';
  15.  
  16. // 加载 highlight.js 样式
  17. function loadHighlightJSCSS() {
  18. const dayLink = document.createElement('link');
  19. dayLink.rel = 'stylesheet';
  20. dayLink.id = 'highlight-css-day';
  21. dayLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/github.min.css';
  22. document.head.appendChild(dayLink);
  23.  
  24. const nightLink = document.createElement('link');
  25. nightLink.rel = 'stylesheet';
  26. nightLink.id = 'highlight-css-night';
  27. nightLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/github-dark.min.css';
  28. nightLink.disabled = true;
  29. document.head.appendChild(nightLink);
  30.  
  31. // 添加自定义样式
  32. const customStyles = document.createElement('style');
  33. customStyles.textContent = /* css */`
  34. .codeHighlight {
  35. position: relative;
  36. border: 1px solid #ddd;
  37. border-radius: 5px;
  38. overflow: hidden;
  39. }
  40. html[data-theme="dark"] .codeHighlight {
  41. border: 1px solid #444;
  42. }
  43. .codeHighlight pre {
  44. line-height: 1.5 !important;
  45. border-radius: 0 0 5px 5px;
  46. font-family: monospace;
  47. scrollbar-width: thin;
  48. }
  49. .codeHighlight .top-bar {
  50. display: flex;
  51. justify-content: space-between;
  52. align-items: center;
  53. padding: 5px 10px;
  54. font-size: 12px;
  55. }
  56. .codeHighlight pre {
  57. counter-reset: line;
  58. position: relative;
  59. display: block;
  60. max-height: 400px;
  61. padding-right: 100px !important;
  62. }
  63. .codeHighlight .copy-button {
  64. position: absolute;
  65. top: 10px;
  66. right: 15px;
  67. padding: 5px 10px;
  68. cursor: pointer;
  69. border: none;
  70. background-color: #eee;
  71. color: #444;
  72. font-size: 12px;
  73. border-radius: 2px;
  74. z-index: 1;
  75. transition: all .3s ease-in-out;
  76. }
  77. @media (hover: hover) and (pointer: fine) {
  78. .codeHighlight pre {
  79. padding-right: unset;
  80. }
  81. .codeHighlight .copy-button {
  82. opacity: 0;
  83. }
  84. .codeHighlight:hover .copy-button,
  85. .copy-button:focus {
  86. opacity: 1;
  87. }
  88. }
  89. .copy-button:hover {
  90. background-color: #dedede;
  91. }
  92. html[data-theme="dark"] .copy-button {
  93. background-color: #333;
  94. color: #eee;
  95. }
  96. html[data-theme="dark"] .copy-button:hover {
  97. background-color: #444;
  98. }
  99. `;
  100. document.head.appendChild(customStyles);
  101. }
  102.  
  103. // 切换 highlight.js 样式
  104. function switchHighlightJSCSS(theme) {
  105. const dayLink = document.getElementById('highlight-css-day');
  106. const nightLink = document.getElementById('highlight-css-night');
  107.  
  108. if (theme === 'dark') {
  109. dayLink.disabled = true;
  110. nightLink.disabled = false;
  111. } else {
  112. dayLink.disabled = false;
  113. nightLink.disabled = true;
  114. }
  115. }
  116.  
  117. // 加载 highlight.js
  118. function loadHighlightJS() {
  119. const script = document.createElement('script');
  120. script.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/highlight.min.js';
  121. script.onload = async () => {
  122. await initializeHighlightJS();
  123. switchHighlightJSCSS(document.documentElement.getAttribute('data-theme') || 'light');
  124. };
  125. document.head.appendChild(script);
  126. }
  127.  
  128. // 复制代码到剪贴板
  129. function copyToClipboard(text, button) {
  130. if (navigator.clipboard && window.isSecureContext) {
  131. // 使用 Clipboard API
  132. navigator.clipboard.writeText(text).then(() => {
  133. button.textContent = '已复制';
  134. }).catch(err => {
  135. console.error('无法复制文本:', err);
  136. button.textContent = '复制失败';
  137. });
  138. } else {
  139. // 使用回退方案
  140. const textArea = document.createElement('textarea');
  141. textArea.value = text;
  142. document.body.appendChild(textArea);
  143. textArea.select();
  144. try {
  145. document.execCommand('copy');
  146. button.textContent = '已复制';
  147. } catch (err) {
  148. console.error('无法复制文本:', err);
  149. button.textContent = '复制失败';
  150. }
  151. document.body.removeChild(textArea);
  152. }
  153.  
  154. setTimeout(() => { button.textContent = '复制代码'; }, 3000);
  155. }
  156.  
  157. // 初始化 highlight.js
  158. async function initializeHighlightJS() {
  159. return new Promise((resolve, reject) => {
  160. document.querySelectorAll('.codeHighlight').forEach(blockWrapper => {
  161. blockWrapper.innerHTML = blockWrapper.innerHTML.replace('<pre><br>', '<pre>');
  162. const block = blockWrapper.querySelector('pre');
  163. const result = hljs.highlightAuto(block.textContent);
  164.  
  165. // 创建复制按钮
  166. const copyButton = document.createElement('button');
  167. copyButton.className = 'copy-button';
  168. copyButton.tabIndex = 0;
  169. copyButton.textContent = '复制代码';
  170. copyButton.addEventListener('click', () => {
  171. copyToClipboard(block.textContent, copyButton);
  172. });
  173.  
  174. block.parentNode.insertBefore(copyButton, block);
  175.  
  176. block.classList.add(`language-${result.language ?? 'plaintext'}`);
  177. hljs.highlightElement(block);
  178. });
  179. resolve(0);
  180. })
  181. }
  182.  
  183. // 监听 #toggleTheme 按钮的点击事件
  184. document.getElementById('toggleTheme').addEventListener('click', () => {
  185. const theme = document.documentElement.getAttribute('data-theme');
  186. switchHighlightJSCSS(theme);
  187. });
  188.  
  189. // 初始加载 highlight.js 样式和脚本,并应用当前主题
  190. loadHighlightJSCSS();
  191. loadHighlightJS();
  192.  
  193. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址