众所周知的原因,增强插件已经事实上失效了,脚本作者 @anghunk 大佬正在开发 浏览器扩展来取代原来的脚本,但是目前还有很多问题,我本人长期是增强插件的用户,已经习惯了它的方便,对其在这次论坛整顿中阵亡很是惋惜。
下午花了点时间,复活了 我认为我最喜欢的三个功能,这里分享一下,应该不违反 @neo 这次整顿的宗旨:
代码如下,AI含量50%, @anghunk 大佬 含量 40%,我个人花了点时间 勉强占个10%吧
// ==UserScript==
// @name Linux Do 增强 (极致精简版)
// @version 0.0.1
// @author user705
// @description linux.do 增强插件极致精简版
// @license MIT
// @icon https://cdn3.linux.do/optimized/3X/9/d/9dd49731091ce8656e94433a26a3ef36062b3994_2_32x32.png
// @match *://linux.do/*
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
(function() {
let settings = JSON.parse(localStorage.getItem('linuxdoSettings') || '{"newTab":true,"showTime":true,"disableAvatar":true}');
GM_addStyle(`
.linuxdo-settings {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--header_background);
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
z-index: 9999;
display: none;
color: var(--primary);
}
.linuxdo-settings h3 {
text-align: center;
}
.linuxdo-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 9998;
display: none;
}
.linuxdo-btn {
margin: 5px;
padding: 5px 10px;
border: none;
border-radius: 4px;
cursor: pointer;
background: var(--tertiary-low);
color: var(--primary);
}
.linuxdo-setting {
margin: 0 0.25em;
}
`);
function initSettings() {
const settingsHtml = `
<div class="linuxdo-settings" id="linuxdo-settings">
<h3 style="text-align: center;">设置</h3>
<div style="margin: 10px 0;">
<label>
<input type="checkbox" id="newTab" /> 新标签页打开帖子
</label>
</div>
<div style="margin: 10px 0;">
<label>
<input type="checkbox" id="showTime" /> 显示发布时间
</label>
</div>
<div style="margin: 10px 0;">
<label>
<input type="checkbox" id="disableAvatar" /> 禁用动态头像
</label>
</div>
<div style="margin-top:15px;">
<button class="linuxdo-btn" id="saveSettings">保存</button>
<button class="linuxdo-btn" id="closeSettings">关闭</button>
</div>
</div>
<div class="linuxdo-overlay" id="linuxdo-overlay"></div>
`;
document.body.insertAdjacentHTML('beforeend', settingsHtml);
const settingsPanel = document.getElementById('linuxdo-settings');
const overlay = document.getElementById('linuxdo-overlay');
const openBtn = document.getElementById('openSettings');
const closeBtn = document.getElementById('closeSettings');
const saveBtn = document.getElementById('saveSettings');
document.getElementById('newTab').checked = settings.newTab;
document.getElementById('showTime').checked = settings.showTime;
document.getElementById('disableAvatar').checked = settings.disableAvatar;
const settingsButton = document.createElement('button');
settingsButton.className = 'btn no-text btn-icon linuxdo-setting';
settingsButton.innerHTML = '<svg class="fa d-icon d-icon-gear svg-icon svg-string" xmlns="http://www.w3.org/2000/svg"><use href="#gear"></use></svg>';
settingsButton.title = '增强插件设置';
const interval = setInterval(() => {
const targetElement = document.querySelector('.sidebar-footer-actions');
if (targetElement) {
targetElement.prepend(settingsButton);
clearInterval(interval);
}
}, 1000);
settingsButton.addEventListener('click', () => {
settingsPanel.style.display = 'block';
overlay.style.display = 'block';
});
closeBtn.addEventListener('click', () => {
settingsPanel.style.display = 'none';
overlay.style.display = 'none';
});
overlay.addEventListener('click', () => {
settingsPanel.style.display = 'none';
overlay.style.display = 'none';
});
saveBtn.addEventListener('click', () => {
settings = {
newTab: document.getElementById('newTab').checked,
showTime: document.getElementById('showTime').checked,
disableAvatar: document.getElementById('disableAvatar').checked
};
localStorage.setItem('linuxdoSettings', JSON.stringify(settings));
location.reload();
});
}
// 1. 新标签页打开帖子
if (settings.newTab) {
document.addEventListener('click', (e) => {
// 判断是否在首页
const isHomePage = window.location.pathname === '/' ||
window.location.pathname === '/latest' ||
window.location.pathname === '/categories' ||
window.location.pathname.startsWith('/c/');
if (isHomePage) {
const link = e.target.closest('a[href^="/t/topic/"]');
if (link) {
e.preventDefault();
window.open(link.href, '_blank');
}
}
}, {
capture: true
});
}
// 2. 显示发布时间
if (settings.showTime) {
function formatDate(dateStr) {
const date = new Date(dateStr.replace(/年|月/g, '/').replace('日', ''));
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
return {
formatted: `${year}/${month}/${day} ${hours}:${minutes}`,
date: date
};
}
function getTimeColor(date) {
const now = new Date();
const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24));
if (diffDays === 0) {
return '#45B5AA';
} else if (diffDays <= 3) {
return '#3e8ed2';
} else if (diffDays <= 7) {
return '#8B5CF6';
} else if (diffDays <= 30) {
return '#CFA94A';
} else {
return '#999999';
}
}
new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
const timeElements = document.querySelectorAll('.num.topic-list-data.age.activity');
timeElements.forEach(element => {
if (!element.getAttribute('data-processed')) {
const title = element.getAttribute('title');
if (title) {
const [created, latest] = title.split('最新:');
if (created) {
const createdDate = created.replace('创建日期:', '').trim();
const { formatted, date } = formatDate(createdDate);
const color = getTimeColor(date);
const originalTime = element.innerHTML.trim();
element.innerHTML = `<span style="color: ${color}">${formatted}</span><br>${originalTime}`;
element.setAttribute('data-processed', 'true');
}
}
}
});
}
});
}).observe(document, { childList: true, subtree: true });
}
// 3. 禁用动态头像
if (settings.disableAvatar) {
const processedUrls = new Set();
function handleGifAvatar(img) {
if (processedUrls.has(img.src)) return;
if (img.src.toLowerCase().endsWith('.gif')) {
console.log('Found GIF avatar:', img.src);
const staticUrl = img.src.replace(/\.gif$/i, '.png');
img.src = staticUrl;
processedUrls.add(staticUrl);
img.onerror = () => {
if (img.src.endsWith('.png')) {
img.src = img.src.replace(/\.png$/i, '.jpg');
processedUrls.add(img.src);
}
};
}
}
function processExistingAvatars() {
document.querySelectorAll('.avatar, .user-avatar, img.avatar').forEach(avatar => {
if (avatar.tagName === 'IMG') {
handleGifAvatar(avatar);
} else {
const img = avatar.querySelector('img');
if (img) handleGifAvatar(img);
}
});
}
const avatarObserver = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) {
if (node.classList && (node.classList.contains('avatar') || node.classList.contains('user-avatar'))) {
if (node.tagName === 'IMG') {
handleGifAvatar(node);
} else {
const img = node.querySelector('img');
if (img) handleGifAvatar(img);
}
}
node.querySelectorAll('.avatar, .user-avatar, img.avatar').forEach(avatar => {
if (avatar.tagName === 'IMG') {
handleGifAvatar(avatar);
} else {
const img = avatar.querySelector('img');
if (img) handleGifAvatar(img);
}
});
}
});
}
if (mutation.type === "attributes" && mutation.attributeName === "src") {
const img = mutation.target;
if (img.tagName === 'IMG') {
handleGifAvatar(img);
}
}
});
});
avatarObserver.observe(document, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['src']
});
let scrollTimeout;
document.addEventListener('scroll', () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(processExistingAvatars, 100);
}, { passive: true });
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', processExistingAvatars);
} else {
processExistingAvatars();
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSettings);
} else {
initSettings();
}
})();
