// ==UserScript== // @name Lightnovel Syntax Highlighting // @name:en Lightnovel Syntax Highlighting // @name:zh 轻小说语法高亮 // @namespace http://tampermonkey.net/ // @version 1.2.0 // @description Enhancing reading experience by highlighing 「dialogues」, 〈keywords〉, etc // @description:en Enhancing reading experience by highlighing 「dialogues」, 〈keywords〉, etc // @description:zh 通过语法高亮「对白」、〈关键词〉等内容增强阅读体验 // @author Boring3 // @match *://*.lightnovel.app/read/* // @match *://*.masiro.me/admin/novelReading* // @match *://*.wenku8.net/modules/article/reader.php* // @match *://*.esjzone.cc/forum/*/*.html // @match *://*.lightnovel.fun/detail/* // @match *://*.syosetu.com/*/* // @match *://*.linovelib.com/novel/*/*.html* // @icon https://www.google.com/s2/favicons?domain=masiro.me // @license GPLv3 // @run-at document-end // @grant none // @downloadURL https://update.greasyfork.icu/scripts/566935/Lightnovel%20Syntax%20Highlighting.user.js // @updateURL https://update.greasyfork.icu/scripts/566935/Lightnovel%20Syntax%20Highlighting.meta.js // ==/UserScript== (function () { 'use strict'; const SELECTOR = [ ".read", // 轻书架 ".nvl-content", // 真白萌 "#contentmain>#content", // Wenku8 ".forum-content", // ESJ "#article-main-contents", // 轻国 ".p-novel__body", // syosetu "#TextContent", // 哔哩 ].join(','); const RULES = [ { name: 'hlTalk', color: '#ff4482', pairs: [['「', '」'], ['“', '”']] }, { name: 'hlTalk2', color: '#d36d33', pairs: [['『', '』'], ['‘', '’']] }, { name: 'hlEnclosed', color: '#33aa04', pairs: [['(', ')'], ['(', ')'], ['[', ']'], ['[', ']']] }, { name: 'hlTitle', color: '#dddd82', pairs: [['《', '》'], ['〈', '〉']] }, { name: 'hlBrace', color: '#cccc00', pairs: [['【', '】'], ['〖', '〗'], ['〔', '〕']] } ]; const styleContent = [ ...RULES.map(r => `.${r.name} { color: ${r.color}; }`), `hl-processed { display: none; }`, ].join('\n'); const styleEl = document.createElement('style'); styleEl.textContent = styleContent; document.head.appendChild(styleEl); function escapeRegExp(str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function hasProcessedMarker(el) { if (!el) return false; for (const child of el.children) { if ((child.tagName || '').toLowerCase() === 'hl-processed') return true; } return false; } function addProcessedMarker(el) { if (!el || hasProcessedMarker(el)) return; const marker = document.createElement('hl-processed'); marker.setAttribute('aria-hidden', 'true'); el.appendChild(marker); } function highlightContent(contentEl) { if (!contentEl || hasProcessedMarker(contentEl)) return; let html = contentEl.innerHTML; let hasReplacement = false; RULES.forEach(rule => { if (!rule.pairs || rule.pairs.length === 0) return; const patterns = rule.pairs.map(([s, e]) => { const start = escapeRegExp(s); const end = escapeRegExp(e); return `${start}(.*?)${end}`; }).join('|'); const regex = new RegExp(patterns, "gi"); html = html.replace(regex, (match) => { hasReplacement = true; return `${match}`; }); }); if (hasReplacement) { contentEl.innerHTML = html; addProcessedMarker(contentEl); // 通过子元素避免SPA局部更新问题 } console.log("Lightnovel Highlighting: processed") } const observer = new MutationObserver(() => { const contentEl = document.querySelector(SELECTOR); if (contentEl && !hasProcessedMarker(contentEl)) { highlightContent(contentEl); } }); observer.observe(document.body, { childList: true, subtree: true }); highlightContent(document.querySelector(SELECTOR)); })();