GitHub 文件高亮 - 优化版

实现GitHub文件列表按时间倒序排列,高亮30天内更新的文件(背景色),支持鼠标悬停加深背景,支持多主题切换

// ==UserScript==
// @name         GitHub 文件高亮 - 优化版
// @namespace    http://tampermonkey.net/
// @version      0.24
// @description  实现GitHub文件列表按时间倒序排列,高亮30天内更新的文件(背景色),支持鼠标悬停加深背景,支持多主题切换
// @author       Grok
// @icon         https://i.miji.bid/2025/03/15/560664f99070e139e28703cf92975c73.jpeg
// @match        https://github.com/*/*
// @match        https://github.com/*/*/tree/*
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // === 主题配置 ===
    const THEMES = {
        'default': { 
            name: '默认淡蓝色', 
            BGC: { 
                highlight: 'rgba(40, 135, 255, 0.15)', 
                grey: 'rgba(200, 200, 200, 0.1)', 
                highlightHover: 'rgba(40, 135, 255, 0.3)', 
                greyHover: 'rgba(200, 200, 200, 0.25)' 
            }
        },
        'green': { 
            name: '代码绿色', 
            BGC: { 
                highlight: 'rgba(60, 135, 80, 0.15)', 
                grey: 'rgba(200, 200, 200, 0.1)', 
                highlightHover: 'rgba(60, 135, 80, 0.3)', 
                greyHover: 'rgba(200, 200, 200, 0.25)' 
            }
        },
        'purple': { 
            name: '优雅紫色', 
            BGC: { 
                highlight: 'rgba(147, 112, 219, 0.15)', 
                grey: 'rgba(200, 200, 200, 0.1)', 
                highlightHover: 'rgba(147, 112, 219, 0.3)', 
                greyHover: 'rgba(200, 200, 200, 0.25)' 
            }
        },
        'orange': { 
            name: '活力橙色', 
            BGC: { 
                highlight: 'rgba(255, 147, 0, 0.15)', 
                grey: 'rgba(200, 200, 200, 0.1)', 
                highlightHover: 'rgba(255, 147, 0, 0.3)', 
                greyHover: 'rgba(200, 200, 200, 0.25)' 
            }
        },
        'teal': { 
            name: '青色调', 
            BGC: { 
                highlight: 'rgba(32, 201, 151, 0.15)', 
                grey: 'rgba(200, 200, 200, 0.1)', 
                highlightHover: 'rgba(32, 201, 151, 0.3)', 
                greyHover: 'rgba(200, 200, 200, 0.25)' 
            }
        }
    };

    // 时间阈值(30天)
    const TIME_BOUNDARY = { number: 30, unit: 'day' };

    // 获取/设置当前主题
    let currentTheme = GM_getValue('currentTheme', 'default');
    let COLORS = THEMES[currentTheme];

    // 处理时间判断
    function handelTime(time) {
        const now = new Date();
        const targetDate = new Date(now);
        targetDate.setDate(now.getDate() - TIME_BOUNDARY.number);
        return new Date(time) >= targetDate;
    }

    // 设置样式函数
    function setElementStyles(el, timeResult) {
        if (el.length) {
            // 设置默认背景色(为 td 元素设置)
            const defaultBgColor = timeResult ? COLORS.BGC.highlight : COLORS.BGC.grey;
            const tdElements = el.find('td');
            if (tdElements.length) {
                tdElements.each(function() {
                    this.style.setProperty('background-color', defaultBgColor, 'important');
                });
            }

            // 添加类名以标记高亮状态(用于悬停时判断)
            el.removeClass('highlight-row non-highlight-row');
            el.addClass(timeResult ? 'highlight-row' : 'non-highlight-row');

            // 绑定鼠标悬停事件
            el.off('mouseenter mouseleave'); // 移除旧的事件监听,防止重复绑定
            el.on('mouseenter', function() {
                const hoverBgColor = $(this).hasClass('highlight-row') ? COLORS.BGC.highlightHover : COLORS.BGC.greyHover;
                const tdEls = $(this).find('td');
                if (tdEls.length) {
                    tdEls.each(function() {
                        this.style.setProperty('background-color', hoverBgColor, 'important');
                    });
                }
            });
            el.on('mouseleave', function() {
                const tdEls = $(this).find('td');
                if (tdEls.length) {
                    tdEls.each(function() {
                        this.style.setProperty('background-color', defaultBgColor, 'important');
                    });
                }
            });
        }
    }

    // 主函数:实现文件倒序排列、高亮和鼠标悬停效果
    function GitHub_Freshness() {
        if (!isMatchedUrl()) return;

        const elements = $('relative-time'); // 直接使用 <relative-time> 元素
        if (elements.length === 0) return;

        let trRows = [];
        elements.each(function() {
            const datetime = $(this).attr('datetime');
            if (datetime) {
                const timeResult = handelTime(datetime);
                const trElement = $(this).closest('tr');

                trRows.push(trElement[0]);
                setElementStyles(trElement, timeResult);
            }
        });

        if (trRows.length > 0) {
            // 按时间从新到旧排序
            trRows.sort((a, b) => {
                const dateA = new Date(a.querySelector('relative-time').getAttribute('datetime'));
                const dateB = new Date(b.querySelector('relative-time').getAttribute('datetime'));
                return dateB - dateA;
            });

            const tbody = document.querySelector('tbody');
            if (tbody) {
                $(tbody).empty().append(trRows);
            } else {
                console.warn('未找到目标 tbody,排序可能失效');
            }
        }
    }

    // URL匹配检查
    function isMatchedUrl() {
        const currentUrl = window.location.href;
        return /^https:\/\/github\.com\/[^/]+\/[^/]+(?:\?.*)?$|^https:\/\/github\.com\/[^/]+\/[^/]+\/tree\/.+$/.test(currentUrl);
    }

    // 防抖函数
    function debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    const runScript = debounce(GitHub_Freshness, 200);

    // 主题切换函数
    function switchTheme(themeKey) {
        currentTheme = themeKey;
        COLORS = THEMES[themeKey];
        GM_setValue('currentTheme', themeKey);
        GitHub_Freshness();
    }

    // 添加油猴菜单
    GM_registerMenuCommand(`当前主题:${THEMES[currentTheme].name}`, () => {}, '0');
    for (const [key, theme] of Object.entries(THEMES)) {
        GM_registerMenuCommand(
            `${currentTheme === key ? '✓ ' : '  '}${theme.name}`,
            () => switchTheme(key),
            key.substring(0, 1)
        );
    }

    // 立即运行一次
    runScript();

    // 使用 MutationObserver 监听 DOM 变化
    const observer = new MutationObserver(debounce(() => {
        if ($('relative-time').length > 0) {
            runScript();
        }
    }, 200));
    observer.observe(document.body, { childList: true, subtree: true });

    // 事件监听
    window.addEventListener('load', runScript);
    document.addEventListener('pjax:end', runScript);

    // 处理 URL 变化
    (function(history) {
        const pushState = history.pushState;
        const replaceState = history.replaceState;

        history.pushState = function(state, title, url) {
            pushState.apply(history, arguments);
            setTimeout(runScript, 200);
        };

        history.replaceState = function(state, title, url) {
            replaceState.apply(history, arguments);
            setTimeout(runScript, 200);
        };

        window.addEventListener('popstate', () => setTimeout(runScript, 200));
    })(window.history);
})();

QingJ © 2025

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