您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Switch to several provided styles to customise the comment lines.
// ==UserScript== // @name [Lemmy] Comment Line Styles // @match *://*/* // @exclude /^https:\/\/(?:(?:alex|old|photon|tess|voyager)\.lemmy\.ca|(?:a|m|old|photon)\.lemmy\.world)/ // @noframes // @run-at document-idle // @inject-into content // @grant GM_addStyle // @grant GM_getValues // @grant GM_registerMenuCommand // @grant GM_setValue // @namespace Violentmonkey Scripts // @author SedapnyaTidur // @version 1.0.0 // @license MIT // @revision 8/18/2025, 8:00:27 PM // @description Switch to several provided styles to customise the comment lines. // ==/UserScript== (function() { 'use strict'; if (!document.head.querySelector(':scope > meta[name="Description"][content="Lemmy"]')) return; GM_addStyle(` .custom-ml { margin-left: 0.5rem; } .custom-mt { margin-top: 0.75rem; } .custom-p { padding: 0.25rem 0.5rem 0.25rem 0.5rem; } .custom-pb { padding-bottom: 0.5rem; } .custom-pbrt { padding: 0.25rem 0.5rem 0.25rem 0px; } .custom-plr { padding: 0px 0.5rem; } .custom-pt { padding-top: 0.5rem; }`); const Styles = [{ name: 'Original', // Must have at least 1 style. That means the styles.length must be at least 1. // Can have more than 7 for any styles. That means styles can be: styles.length > 7. styles: [ 'border-left: 2px solid rgba(172, 83, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba(172, 157, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba(113, 172, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba( 83, 172, 128, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba( 83, 142, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba( 98, 83, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba(172, 83, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;'] }, { name: 'Bright Original', styles: [ 'border-left: 2px solid rgba(172, 83, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba(172, 157, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba(113, 172, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba( 83, 172, 128, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba( 83, 142, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba( 98, 83, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;', 'border-left: 2px solid rgba(172, 83, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.25rem !important;'] }, { name: 'Gapped Original', styles: [ 'border-left: 2px solid rgba(172, 83, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(172, 157, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(113, 172, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba( 83, 172, 128, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba( 83, 142, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba( 98, 83, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(172, 83, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;'] }, { name: 'Bright & Gapped Original', styles: [ 'border-left: 2px solid rgba(172, 83, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(172, 157, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(113, 172, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba( 83, 172, 128, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba( 83, 142, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba( 98, 83, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(172, 83, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;'] }, { name: 'Blue & Yellow', styles: [ 'border-left: 2px solid rgba( 83, 142, 172, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(172, 157, 83, 0.5) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;'] }, { name: 'Bright Blue & Yellow', styles: [ 'border-left: 2px solid rgba( 83, 142, 172, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;', 'border-left: 2px solid rgba(172, 157, 83, 0.8) !important; color: rgb(200, 200, 200) !important; margin-left: 0.5rem !important;'] }, { name: 'Stacked Darks', styles: [ 'background-color: rgb(34, 34, 34) !important; color: rgb(200, 200, 200) !important; border-color: rgb(94, 94, 94) !important; border-width: 2px 0px 0px 2px !important; border-style: solid !important; border-radius: 0.5rem !important; margin-left: 0.35rem !important;', 'background-color: rgb(54, 54, 54) !important; color: rgb(200, 200, 200) !important; border-color: rgb(94, 94, 94) !important; border-width: 2px 0px 0px 2px !important; border-style: solid !important; border-radius: 0.5rem !important; margin-left: 0.35rem !important;'] }]; const StyleTypes = [1, 2, 3, 4]; // Use emojis? //const window = unsafeWindow; const target = ':scope > div#root > div > main > div > div > div > div > :last-child:not([class])'; const waitTimeout = 10000; // 10 seconds. let attrObserver, childObserver, searchInterval = 0, searchTimeout = 0; let { type, style } = GM_getValues({ type: StyleTypes[0], style: Styles[0].name }); const getStyles = function(name) { for (const object of Styles) { if (object.name === name) return object.styles; } return Styles[0].styles; }; const color4 = function(node, recurseCount = 0, styles = getStyles(style)) { if (recurseCount === 0 && attrObserver) { attrObserver.disconnect(); attrObserver = undefined; } node.removeAttribute('style'); node.classList.remove('border-top', 'ms-1'); const children = node.children; const len = children.length; for (let i = 0; i < len; ++i) { const li = children[i]; if (recurseCount === 0) { li.classList.add('custom-mt'); } else { li.classList.add('custom-ml'); } for (const element of li.children) { const tagName = element.tagName.toLowerCase(); if (tagName === 'ul') { color4(element, recurseCount + 1, styles); } else if (tagName === 'article') { element.classList.remove('border-top', 'mark', 'py-2'); element.classList.add('custom-plr'); element.style = styles[recurseCount % styles.length]; if (recurseCount === 0 && i == 0 && element.style.marginLeft) { node.style.setProperty('margin-left', `-${element.style.marginLeft}`, 'important'); attrObserver = new MutationObserver(() => color4(node, 0, styles)); attrObserver.observe(li, { attributes: true }); } const parent = element.firstChild; parent.classList.remove('ms-2'); const secChild = parent.querySelector(':scope > :nth-child(2)'); secChild.querySelector(':scope > :first-child > :last-child')?.style.setProperty('margin-bottom', '0px', 'important'); const lastChild = parent.lastChild; lastChild.classList.remove('mt-1'); if (lastChild.childElementCount === 0) { secChild.classList.add('custom-pb'); lastChild.style.setProperty('display', 'none', 'important'); } } else if (tagName === 'div' && element.classList.contains('details')) { //N more replies. element.classList.remove('ms-1'); //element.classList.add('custom-ml'); element.style = styles[(recurseCount + 1) % styles.length]; } } } }; // node must be a ul element (HTMLUListElement). const color3 = function(node, recurseCount = 0, styles = getStyles(style)) { // Disconnect if called multiple times by childObserver and disconnect for recursion called by attrObserver; if (recurseCount === 0 && attrObserver) { attrObserver.disconnect(); attrObserver = undefined; } node.classList.remove('border-top', 'ms-1'); // margin-left: 0.25rem // Run in order because Lemmy may override our styles. forEach() is not an option. for (const li of node.children) { //const liChildCount = li.childElementCount; for (const element of li.children) { const tagName = element.tagName.toLowerCase(); if (tagName === 'ul') { element.style = styles[recurseCount % styles.length]; color3(element, recurseCount + 1, styles); } else if (tagName === 'article') { element.classList.remove('border-top', 'mark', 'py-2'); // padding-top: 0.5rem; padding-bottom: 0.5rem //element.classList.add('custom-pt'); const parent = element.firstChild; parent.classList.remove('ms-2'); // margin-left: 0.5rem if (recurseCount === 0 && !attrObserver) { // Styles may get overriden by Lemmy. attrObserver = new MutationObserver(() => color3(node, 0, styles)); attrObserver.observe(parent, { attributes: true }); } parent.firstChild.classList.add('custom-pbrt'); const secChild = parent.querySelector(':scope > :nth-child(2)'); secChild.classList.add('custom-p'); secChild.style = styles[recurseCount % styles.length]; secChild.querySelector(':scope > :first-child > :last-child')?.style.setProperty('margin-bottom', '0px', 'important'); const lastChild = parent.lastChild; lastChild.classList.remove('mt-1'); // margin-top: 0.25rem lastChild.style = styles[recurseCount % styles.length]; if (lastChild.childElementCount === 0) lastChild.style.setProperty('display', 'none', 'important'); } else if (tagName === 'div' && element.classList.contains('details')) { //N more replies. element.classList.remove('ms-1'); element.style = styles[recurseCount % styles.length]; } } } }; const color2 = function(node, recurseCount = 0, styles = getStyles(style)) { if (recurseCount === 0 && attrObserver) { attrObserver.disconnect(); attrObserver = undefined; } node.removeAttribute('style'); node.classList.remove('border-top', 'ms-1'); const children = node.children; const len = children.length; for (let i = 0; i < len; ++i) { const li = children[i]; const liChildCount = li.childElementCount; li.classList.add('custom-mt'); li.style = styles[recurseCount % styles.length]; if (recurseCount === 0 && i == 0 && li.style.marginLeft) { node.style.setProperty('margin-left', `-${li.style.marginLeft}`, 'important'); attrObserver = new MutationObserver(() => color2(node, 0, styles)); attrObserver.observe(li, { attributes: true }); } for (const element of li.children) { const tagName = element.tagName.toLowerCase(); if (tagName === 'ul') { color2(element, recurseCount + 1, styles); } else if (tagName === 'article') { element.classList.remove('border-top', 'mark', 'py-2'); element.classList.add('custom-plr'); const parent = element.firstChild; parent.classList.remove('ms-2'); const secChild = parent.querySelector(':scope > :nth-child(2)'); secChild.querySelector(':scope > :first-child > :last-child')?.style.setProperty('margin-bottom', '0px', 'important'); const lastChild = parent.lastChild; lastChild.classList.remove('mt-1'); if (lastChild.childElementCount === 0) { if (liChildCount === 1) secChild.classList.add('custom-pb'); lastChild.style.setProperty('display', 'none', 'important'); } } else if (tagName === 'div' && element.classList.contains('details')) { //N more replies. element.classList.remove('ms-1'); element.classList.add('custom-mt'); element.style = styles[(recurseCount + 1) % styles.length]; } } } }; const color1 = function(node, recurseCount = 0, styles = getStyles(style)) { if (recurseCount === 0 && attrObserver) { attrObserver.disconnect(); attrObserver = undefined; } node.classList.remove('border-top', 'ms-1'); if (recurseCount === 0) node.style.setProperty('margin-left', '-0.5rem', 'important'); for (const li of node.children) { for (const element of li.children) { const tagName = element.tagName.toLowerCase(); if (tagName === 'ul') { element.style = styles[recurseCount % styles.length]; if (recurseCount === 0 && !attrObserver) { attrObserver = new MutationObserver(() => color1(node, 0, styles)); attrObserver.observe(node, { attributes: true }); } color1(element, recurseCount + 1, styles); } else if (tagName === 'article') { element.classList.remove('border-top', 'mark', 'py-2'); element.classList.add('custom-plr'); const parent = element.firstChild; parent.classList.remove('ms-2'); const secChild = parent.querySelector(':scope > :nth-child(2)'); secChild.querySelector(':scope > :first-child > :last-child')?.style.setProperty('margin-bottom', '0px', 'important'); const lastChild = parent.lastChild; lastChild.classList.remove('mt-1'); if (lastChild.childElementCount === 0) { secChild.classList.add('custom-pb'); lastChild.style.setProperty('display', 'none', 'important'); } } else if (tagName === 'div' && element.classList.contains('details')) { //N more replies. element.classList.remove('ms-1'); element.style = styles[recurseCount % styles.length]; } } } }; const color = function(node) { switch(type) { case StyleTypes[0]: color1(node); break; case StyleTypes[1]: color2(node); break; case StyleTypes[2]: color3(node); break; case StyleTypes[3]: color4(node); break; } }; const reset = function() { clearInterval(searchInterval); clearTimeout(searchTimeout); searchInterval = 0; searchTimeout = 0; if (childObserver) { childObserver.disconnect(); childObserver = undefined; } if (attrObserver) { attrObserver.disconnect(); attrObserver = undefined; } }; const run = function() { searchTimeout = setTimeout(() => { clearInterval(searchInterval); }, waitTimeout); searchInterval = setInterval(() => { if (!document.body) return; const targetParent = document.body.querySelector(target); if (!targetParent) return; clearInterval(searchInterval); clearTimeout(searchTimeout); if (targetParent.childElementCount === 0) return; childObserver = new MutationObserver(mutations => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node instanceof HTMLUListElement || node instanceof HTMLLIElement) { color(targetParent.lastChild); } } } }); childObserver.observe(targetParent, { childList: true, subtree: true }); color(targetParent.lastChild); }, 100); }; new MutationObserver(mutations => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node instanceof HTMLLinkElement && node.rel === 'canonical') { reset(); if (/^https:\/\/[^/]+\/(?:post|m\/[^/]+\/t)\/[0-9]/.test(node.href)) run(); return; } } } }).observe(document.head, { childList: true }); // First visit or reload the page. if (/^\/(?:comment|post)\//.test(window.location.pathname)) run(); // Edit the style's name? if (!Styles.some(object => object.name === style)) { style = Styles[0].name; GM_setValue('style', style); } const nextStyleType = function() { type = StyleTypes[(StyleTypes.indexOf(type) + 1) % StyleTypes.length]; GM_setValue('type', type); GM_registerMenuCommand(`Type:《${type}》`, nextStyleType, { id: '0', autoClose: false, title: "Click to change the comments' style." }); }; const nextStyle = function() { for (let i=0; i<Styles.length; ++i) { if (Styles[i].name === style) { style = Styles[(i + 1) % Styles.length].name; break; } } GM_setValue('style', style); GM_registerMenuCommand(`Style:《${style}》`, nextStyle, { id: '1', autoClose: false, title: "Click to change the comments' style." }); }; GM_registerMenuCommand(`Type:《${type}》`, nextStyleType, { id: '0', autoClose: false, title: "Click to change the comments' style." }); GM_registerMenuCommand(`Style:《${style}》`, nextStyle, { id: '1', autoClose: false, title: "Click to change the comments' style." }); })();