linuxdo 增强插件 极精简版

众所周知的原因,增强插件已经事实上失效了,脚本作者 @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();
    }
})(); 
19 个赞

太强了!

1 个赞

太强了百花齐放,借楼宣传一下 tieba_087

2 个赞

大佬tql :tada:

1 个赞

发到扩展商店是不是要刀乐哇 :tieba_087:

2 个赞

大佬,建议一下哈,扩展没弄好之前把 原来的脚本 砍一砍,只要不跟始皇的初衷相悖,先给大家应应急 我想是可行的,你来做比我在这里跟AI纠缠半天出了3个功能要效率高多了

方便大家也方便自己

2 个赞

粗略测试,90%都可用了,剩下一些场景怕没有覆盖到的,可能会有些瑕疵还需要反馈

太强啦,辛苦各位佬们

都是大佬哇,都太强了tieba_013

感谢分享,我还想要个热门贴功能

太强了!

此话题已在最后回复的 30 天后被自动关闭。不再允许新回复。