Greasy Fork

来自缓存

Greasy Fork is available in English.

GitHub镜像站增强工具

汉化GitHub镜像站界面、加速下载、自动跳转到镜像站

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GitHub镜像站增强工具
// @namespace    https://github.com/GamblerIX/
// @description  汉化GitHub镜像站界面、加速下载、自动跳转到镜像站
// @icon         https://github.githubassets.com/pinned-octocat.svg
// @version      3.0.3
// @author       GamblerIX
// @license      MIT
// @homepage     https://github.com/GamblerIX/GithubProxy
// @supportURL   https://github.com/GamblerIX/GithubProxy/issues
// @match        https://github.com/*
// @match        https://hub.mihoyo.online/*
// @match        https://skills.github.com/*
// @match        https://gist.github.com/*
// @match        https://gist.mihoyo.online/*
// @match        https://education.github.com/*
// @match        https://www.githubstatus.com/*
// @require      https://update.greasyfork.icu/scripts/461072/1661491/GitHub%20%E4%B8%AD%E6%96%87.js
// @run-at       document-start
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_notification
// @grant        GM_setClipboard
// @grant        GM_openInTab
// @grant        window.onurlchange
// @connect      fanyi.iflyrec.com
// @noframes
// ==/UserScript==

(function () {
    'use strict';

    // ==================== 配置常量 ====================
    const CONFIG = {
        VERSION: '3.0.3',
        LANG: 'zh-CN',

        // 存储键名
        STORAGE_KEYS: {
            AUTO_REDIRECT: 'auto_redirect',
            ENABLE_TRANSLATION: 'enable_translation',
            RAW_FAST_INDEX: 'xiu2_menu_raw_fast',
            RAW_DOWN_LINK: 'menu_rawDownLink',
            GIT_CLONE: 'menu_gitClone',
        },

        // 性能配置
        PERFORMANCE: {
            MAX_TEXT_LENGTH: 500,
            DEBOUNCE_DELAY: 300,
            CACHE_EXPIRE_TIME: 5 * 60 * 1000,
            REQUEST_TIMEOUT: 10000,
        },

        // CSS选择器
        SELECTORS: {
            RELEASE_FOOTER: '.Box-footer',
            RAW_BUTTON: 'a[data-testid="raw-button"]',
            FILE_ICONS: 'div.Box-row svg.octicon.octicon-file, .react-directory-filename-column>svg.color-fg-muted',
        },

        // CSS类名
        CSS_CLASSES: {
            XIU2_RS: 'XIU2-RS',
            XIU2_RF: 'XIU2-RF',
            FILE_DOWNLOAD_LINK: 'fileDownLink',
            TRANSLATE_BUTTON: 'translate-me',
        }
    };

    // ==================== 下载源配置 ====================
    const DOWNLOAD_SOURCES = {
        release: [
            ['https://releases.mihoyo.online/https://github.com', '自建', 'CF CDN - 自建加速源'],
            ['https://github.com', '官方', 'GitHub 官方源']
        ],
        clone: [
            ['https://gitclone.com', 'GitClone', '大陆推荐,首次慢,缓存后较快'],
            ['https://github.com', '官方', 'GitHub 官方源']
        ],
        ssh: [
            ['ssh://[email protected]:443/', '官方SSH', 'GitHub 官方 SSH 443端口']
        ],
        raw: [
            ['https://raw.mihoyo.online', '自建', 'CF CDN - 自建加速源'],
            ['https://raw.githubusercontent.com', '官方', 'GitHub 官方源']
        ]
    };

    // ==================== 工具函数 ====================
    const Utils = {
        // 防抖函数
        debounce(func, wait) {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    clearTimeout(timeout);
                    func(...args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        },

        // 安全DOM查询
        safeQuerySelector(selector, parent = document) {
            try {
                return parent.querySelector(selector);
            } catch (error) {
                console.warn(`[GitHub增强] 查询选择器失败: ${selector}`, error);
                return null;
            }
        },

        // 安全DOM查询(多个)
        safeQuerySelectorAll(selector, parent = document) {
            try {
                return parent.querySelectorAll(selector);
            } catch (error) {
                console.warn(`[GitHub增强] 查询选择器失败: ${selector}`, error);
                return [];
            }
        },

        // 创建元素
        createElement(tag, attributes = {}, textContent = '') {
            const element = document.createElement(tag);
            Object.entries(attributes).forEach(([key, value]) => {
                if (key === 'style' && typeof value === 'object') {
                    Object.assign(element.style, value);
                } else {
                    element.setAttribute(key, value);
                }
            });
            if (textContent) element.textContent = textContent;
            return element;
        },

        // 获取嵌套属性
        getNestedProperty(obj, path) {
            return path.split('.').reduce((acc, part) => {
                const match = part.match(/(\w+)(?:\[(\d+)\])?/);
                if (!match) return undefined;
                const key = match[1];
                const index = match[2];
                if (acc && acc[key] !== undefined) {
                    return index !== undefined ? acc[key][index] : acc[key];
                }
                return undefined;
            }, obj);
        }
    };

    // ==================== 存储管理 ====================
    const Storage = {
        get(key, defaultValue = null) {
            try {
                return GM_getValue(key, defaultValue);
            } catch (error) {
                console.warn(`[GitHub增强] 获取存储失败: ${key}`, error);
                return defaultValue;
            }
        },

        set(key, value) {
            try {
                GM_setValue(key, value);
                return true;
            } catch (error) {
                console.warn(`[GitHub增强] 设置存储失败: ${key}`, error);
                return false;
            }
        }
    };

    // ==================== 缓存管理 ====================
    class SimpleCache {
        constructor() {
            this.cache = new Map();
            this.expireTime = CONFIG.PERFORMANCE.CACHE_EXPIRE_TIME;
        }

        set(key, value) {
            this.cache.set(key, {
                value,
                expire: Date.now() + this.expireTime
            });
        }

        get(key) {
            const item = this.cache.get(key);
            if (!item) return null;

            if (Date.now() > item.expire) {
                this.cache.delete(key);
                return null;
            }

            return item.value;
        }

        clear() {
            this.cache.clear();
        }
    }

    // ==================== 主应用类 ====================
    class GitHubEnhancer {
        constructor() {
            this.cache = new SimpleCache();
            this.pageConfig = {};
            this.rawFastIndex = Storage.get(CONFIG.STORAGE_KEYS.RAW_FAST_INDEX, 0);

            // 初始化默认设置
            this.initDefaultSettings();
        }

        // 初始化默认设置
        initDefaultSettings() {
            const defaults = {
                [CONFIG.STORAGE_KEYS.AUTO_REDIRECT]: true,
                [CONFIG.STORAGE_KEYS.ENABLE_TRANSLATION]: true,
                [CONFIG.STORAGE_KEYS.RAW_FAST_INDEX]: 0,
                [CONFIG.STORAGE_KEYS.RAW_DOWN_LINK]: true,
                [CONFIG.STORAGE_KEYS.GIT_CLONE]: true,
            };

            Object.entries(defaults).forEach(([key, defaultValue]) => {
                if (Storage.get(key) === null) {
                    Storage.set(key, defaultValue);
                }
            });
        }

        // 初始化应用
        async init() {
            try {
                console.log(`[GitHub增强] v${CONFIG.VERSION} 开始初始化...`);

                // 检查自动跳转
                if (this.shouldRedirect()) {
                    this.performRedirect();
                    return;
                }

                // 检查依赖
                if (!this.checkDependencies()) {
                    return;
                }

                // 初始化功能
                this.setupLanguageEnvironment();
                this.updatePageConfig();
                this.setupEventListeners();
                this.registerMenuCommands();
                this.setupColorMode();

                // 延迟执行的功能
                setTimeout(() => this.addRawFile(), 1000);
                setTimeout(() => this.addRawDownLink(), 2000);

                // 检查Release页面
                if (location.pathname.indexOf('/releases') > -1) {
                    setTimeout(() => this.addRelease(), 1500);
                }

                // 执行初始翻译
                this.performInitialTranslation();

                console.log(`[GitHub增强] 初始化完成`);

            } catch (error) {
                console.error('[GitHub增强] 初始化失败:', error);
                this.showNotification('初始化失败,请刷新页面重试');
            }
        }

        // 检查是否需要重定向
        shouldRedirect() {
            return Storage.get(CONFIG.STORAGE_KEYS.AUTO_REDIRECT) && window.location.host === 'github.com';
        }

        // 执行重定向
        performRedirect() {
            const newUrl = window.location.href.replace('https://github.com', 'https://hub.mihoyo.online');
            console.log(`[GitHub增强] 重定向到: ${newUrl}`);
            window.location.replace(newUrl);
        }

        // 检查依赖
        checkDependencies() {
            if (typeof I18N === 'undefined') {
                this.showNotification('词库文件未加载,脚本无法运行!');
                return false;
            }
            console.log('[GitHub增强] 词库文件已加载');
            return true;
        }

        // 设置语言环境
        setupLanguageEnvironment() {
            document.documentElement.lang = CONFIG.LANG;

            new MutationObserver(() => {
                if (document.documentElement.lang === "en") {
                    document.documentElement.lang = CONFIG.LANG;
                }
            }).observe(document.documentElement, { attributeFilter: ['lang'] });
        }

        // 更新页面配置
        updatePageConfig() {
            const pageType = this.detectPageType();
            if (pageType) {
                this.pageConfig = this.buildPageConfig(pageType);
            }
        }

        // 检测页面类型
        detectPageType() {
            try {
                const url = new URL(window.location.href);
                const { hostname, pathname } = url;

                const pageMap = {
                    'gist.github.com': 'gist1',
                    'www.githubstatus.com': 'status',
                    'skills.github.com': 'skills',
                    'education.github.com': 'education',
                    'gist.mihoyo.online': 'gist2',
                };

                const site = pageMap[hostname] || 'github';
                const specialSites = ['gist1', 'status', 'skills', 'education', 'gist2'];

                if (specialSites.includes(site)) {
                    return site;
                }

                // 简化的页面类型检测
                if (pathname === '/') {
                    return document.body?.classList.contains("logged-in") ? 'dashboard' : 'homepage';
                } else if (pathname.includes('/releases')) {
                    return 'repository';
                } else {
                    return 'repository'; // 默认为仓库页面
                }

            } catch (error) {
                console.warn('[GitHub增强] 页面类型检测失败:', error);
                return 'repository';
            }
        }

        // 构建页面配置
        buildPageConfig(pageType) {
            try {
                return {
                    currentPageType: pageType,
                    staticDict: {
                        ...I18N[CONFIG.LANG].public.static,
                        ...(I18N[CONFIG.LANG][pageType]?.static || {})
                    },
                    regexpRules: [
                        ...(I18N[CONFIG.LANG][pageType]?.regexp || []),
                        ...I18N[CONFIG.LANG].public.regexp
                    ]
                };
            } catch (error) {
                console.warn('[GitHub增强] 构建页面配置失败:', error);
                return { currentPageType: pageType };
            }
        }

        // 设置事件监听
        setupEventListeners() {
            // URL变化监听
            if (window.onurlchange === undefined) {
                this.addUrlChangeEvent();
            }

            window.addEventListener('urlchange', () => {
                this.setupColorMode();
                if (location.pathname.indexOf('/releases') > -1) {
                    this.addRelease();
                }
                setTimeout(() => this.addRawFile(), 1000);
                setTimeout(() => this.addRawDownLink(), 2000);
            });

            // Turbo事件监听
            document.addEventListener('turbo:load', () => {
                this.translateTitle();
            });

            // 设置DOM变化监听器
            this.setupMutationObserver();
        }

        // 设置DOM变化监听器
        setupMutationObserver() {
            const observer = new MutationObserver((mutations) => {
                this.handleMutations(mutations);
            });

            const config = {
                childList: true,
                subtree: true,
                characterData: true
            };

            if (document.body) {
                observer.observe(document.body, config);
            } else {
                document.addEventListener('DOMContentLoaded', () => {
                    if (document.body) {
                        observer.observe(document.body, config);
                    }
                });
            }
        }

        // 处理DOM变化
        handleMutations(mutations) {
            // 处理Release页面的动态加载
            if (location.pathname.indexOf('/releases') > -1) {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            if (node.tagName === 'DIV' &&
                                node.dataset &&
                                node.dataset.viewComponent === 'true' &&
                                node.classList &&
                                node.classList.contains('Box')) {
                                setTimeout(() => this.addRelease(), 100);
                                break;
                            }
                        }
                    }
                }
            }

            // 处理仓库页面的Git Clone等功能
            if (Utils.safeQuerySelector('#repository-container-header:not([hidden])')) {
                for (const mutation of mutations) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'DIV') {
                            if (node.parentElement && node.parentElement.id === '__primerPortalRoot__') {
                                // 这里可以添加Git Clone相关的处理
                                setTimeout(() => {
                                    this.addDownloadZIP(node);
                                    this.addGitClone(node);
                                }, 100);
                            }
                        }
                    }
                }
            }

            // 处理翻译
            if (Storage.get(CONFIG.STORAGE_KEYS.ENABLE_TRANSLATION)) {
                const nodesToTranslate = mutations.flatMap(({ addedNodes, type }) => {
                    if (type === 'childList' && addedNodes.length > 0) {
                        return [...addedNodes].filter(node =>
                            node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE
                        );
                    }
                    return [];
                });

                nodesToTranslate.forEach(node => {
                    this.traverseNode(node);
                });
            }
        }

        // 添加URL变化事件
        addUrlChangeEvent() {
            const originalPushState = history.pushState;
            const originalReplaceState = history.replaceState;

            history.pushState = function (...args) {
                const result = originalPushState.apply(this, args);
                window.dispatchEvent(new Event('urlchange'));
                return result;
            };

            history.replaceState = function (...args) {
                const result = originalReplaceState.apply(this, args);
                window.dispatchEvent(new Event('urlchange'));
                return result;
            };

            window.addEventListener('popstate', () => {
                window.dispatchEvent(new Event('urlchange'));
            });
        }

        // 执行初始翻译
        performInitialTranslation() {
            if (Storage.get(CONFIG.STORAGE_KEYS.ENABLE_TRANSLATION)) {
                this.translateTitle();
                this.traverseNode(document.body);
            }
        }

        // 翻译页面标题
        translateTitle() {
            if (!Storage.get(CONFIG.STORAGE_KEYS.ENABLE_TRANSLATION)) return;

            try {
                const text = document.title;
                const cacheKey = `title_${text}`;

                let translatedText = this.cache.get(cacheKey);
                if (translatedText) {
                    document.title = translatedText;
                    return;
                }

                translatedText = I18N[CONFIG.LANG]['title']['static'][text] || '';

                if (!translatedText) {
                    const rules = I18N[CONFIG.LANG]['title'].regexp || [];
                    for (const [pattern, replacement] of rules) {
                        translatedText = text.replace(pattern, replacement);
                        if (translatedText !== text) break;
                    }
                }

                if (translatedText && translatedText !== text) {
                    document.title = translatedText;
                    this.cache.set(cacheKey, translatedText);
                }
            } catch (error) {
                console.warn('[GitHub增强] 标题翻译失败:', error);
            }
        }

        // 遍历并翻译节点
        traverseNode(rootNode) {
            if (!Storage.get(CONFIG.STORAGE_KEYS.ENABLE_TRANSLATION) || !rootNode) return;

            try {
                if (rootNode.nodeType === Node.TEXT_NODE) {
                    this.translateTextNode(rootNode);
                    return;
                }

                const walker = document.createTreeWalker(
                    rootNode,
                    NodeFilter.SHOW_TEXT,
                    null,
                    false
                );

                let node;
                while (node = walker.nextNode()) {
                    this.translateTextNode(node);
                }
            } catch (error) {
                console.warn('[GitHub增强] 节点遍历失败:', error);
            }
        }

        // 翻译文本节点
        translateTextNode(node) {
            if (!node.textContent || node.textContent.length > CONFIG.PERFORMANCE.MAX_TEXT_LENGTH) return;

            const text = node.textContent.trim();
            if (!text || /^[\s0-9]*$/.test(text) || /^[\u4e00-\u9fa5]+$/.test(text)) return;

            const translatedText = this.translateText(text);
            if (translatedText && translatedText !== text) {
                node.textContent = node.textContent.replace(text, translatedText);
            }
        }

        // 翻译文本
        translateText(text) {
            if (!this.pageConfig.staticDict) return false;

            const cacheKey = `text_${text}`;
            let translatedText = this.cache.get(cacheKey);
            if (translatedText) return translatedText;

            // 静态翻译
            translatedText = this.pageConfig.staticDict[text];
            if (typeof translatedText === 'string') {
                this.cache.set(cacheKey, translatedText);
                return translatedText;
            }

            // 正则翻译
            if (this.pageConfig.regexpRules) {
                for (const [pattern, replacement] of this.pageConfig.regexpRules) {
                    try {
                        translatedText = text.replace(pattern, replacement);
                        if (translatedText !== text) {
                            this.cache.set(cacheKey, translatedText);
                            return translatedText;
                        }
                    } catch (error) {
                        console.warn('[GitHub增强] 正则翻译失败:', error);
                    }
                }
            }

            return false;
        }

        // 设置颜色模式
        setupColorMode() {
            try {
                let styleElement = document.getElementById('XIU2-Github');
                if (!styleElement) {
                    styleElement = Utils.createElement('style', {
                        id: 'XIU2-Github',
                        type: 'text/css'
                    });
                    document.head.appendChild(styleElement);
                }

                let backColor = '#ffffff', fontColor = '#888888';
                const rootElement = document.documentElement;

                if (rootElement.dataset.colorMode === 'dark') {
                    if (rootElement.dataset.darkTheme === 'dark_dimmed') {
                        backColor = '#272e37';
                        fontColor = '#768390';
                    } else {
                        backColor = '#161a21';
                        fontColor = '#97a0aa';
                    }
                }

                styleElement.textContent = `.XIU2-RS a {--XIU2-background-color: ${backColor}; --XIU2-font-color: ${fontColor};}`;
            } catch (error) {
                console.warn('[GitHub增强] 颜色模式设置失败:', error);
            }
        }

        // 添加Release加速下载
        addRelease() {
            try {
                console.log('[GitHub增强] 尝试添加Release加速下载...');

                const footers = Utils.safeQuerySelectorAll(CONFIG.SELECTORS.RELEASE_FOOTER);
                console.log(`[GitHub增强] 找到 ${footers.length} 个 footer 元素`);

                if (footers.length === 0) {
                    console.log('[GitHub增强] 未找到Release页面的footer元素');
                    return;
                }

                if (location.pathname.indexOf('/releases') === -1) {
                    console.log('[GitHub增强] 当前不在Release页面');
                    return;
                }

                const downloadSources = DOWNLOAD_SOURCES.release;
                const divDisplay = document.documentElement.clientWidth > 755
                    ? 'margin-top: -3px;margin-left: 8px;display: inherit;'
                    : 'margin-left: -90px;';

                // 添加样式(如果不存在)
                if (!document.querySelector('#XIU2-release-style')) {
                    const style = Utils.createElement('style', { id: 'XIU2-release-style' });
                    style.textContent = '@media (min-width: 768px) {.Box-footer li.Box-row>div>span.color-fg-muted {min-width: 27px !important;}}';
                    document.head.appendChild(style);
                }

                let addedCount = 0;
                footers.forEach(footer => {
                    if (footer.querySelector(`.${CONFIG.CSS_CLASSES.XIU2_RS}`)) {
                        console.log('[GitHub增强] 该footer已存在加速按钮,跳过');
                        return;
                    }

                    const links = Utils.safeQuerySelectorAll('li.Box-row a', footer);
                    console.log(`[GitHub增强] 在footer中找到 ${links.length} 个下载链接`);

                    links.forEach(link => {
                        const href = link.href.split(location.host);
                        if (href.length < 2) return;

                        let html = `<div class="${CONFIG.CSS_CLASSES.XIU2_RS}" style="${divDisplay}">`;

                        downloadSources.forEach(([sourceUrl, sourceName, sourceDesc]) => {
                            const url = sourceUrl + href[1];
                            const buttonStyle = 'padding:0 6px; margin-right: -1px; border-radius: 2px; background-color: var(--XIU2-background-color); border-color: var(--borderColor-default); font-size: 11px; color: var(--XIU2-font-color);';
                            html += `<a style="${buttonStyle}" class="btn" href="${url}" target="_blank" title="${sourceDesc}" rel="noreferrer noopener nofollow">${sourceName}</a>`;
                        });

                        html += '</div>';

                        const nextElement = link.parentElement.nextElementSibling;
                        if (nextElement) {
                            nextElement.insertAdjacentHTML('beforeend', html);
                            addedCount++;
                        }
                    });
                });

                console.log(`[GitHub增强] 成功添加了 ${addedCount} 个Release加速按钮`);

            } catch (error) {
                console.warn('[GitHub增强] Release加速添加失败:', error);
            }
        }

        // 添加Raw文件加速
        addRawFile() {
            try {
                const rawButton = Utils.safeQuerySelector(CONFIG.SELECTORS.RAW_BUTTON);
                if (!rawButton) return;

                Utils.safeQuerySelectorAll(`.${CONFIG.CSS_CLASSES.XIU2_RF}`).forEach(el => el.remove());

                const href = location.href.replace(`https://${location.host}`, '');
                const href2 = href.replace('/blob/', '/');
                let html = '';

                for (let i = 1; i < DOWNLOAD_SOURCES.raw.length; i++) {
                    const [sourceUrl, sourceName, sourceDesc] = DOWNLOAD_SOURCES.raw[i];
                    const url = sourceUrl + href2;
                    html += `<a href="${url}" title="${sourceDesc}" target="_blank" role="button" rel="noreferrer noopener nofollow" data-size="small" data-variant="default" class="${rawButton.className} ${CONFIG.CSS_CLASSES.XIU2_RF}" style="border-radius: 0;margin-left: -1px;">${sourceName}</a>`;
                }

                rawButton.insertAdjacentHTML('afterend', html);

            } catch (error) {
                console.warn('[GitHub增强] Raw文件加速添加失败:', error);
            }
        }

        // 添加Raw文件下载链接
        addRawDownLink() {
            if (!Storage.get(CONFIG.STORAGE_KEYS.RAW_DOWN_LINK)) return;

            try {
                const files = Utils.safeQuerySelectorAll(CONFIG.SELECTORS.FILE_ICONS);
                if (files.length === 0 || location.pathname.indexOf('/tags') > -1) return;

                if (Utils.safeQuerySelectorAll(`.${CONFIG.CSS_CLASSES.FILE_DOWNLOAD_LINK}`).length > 0) return;

                const mouseOverHandler = (evt) => {
                    const elem = evt.currentTarget;
                    const downloadLinks = Utils.safeQuerySelectorAll(`.${CONFIG.CSS_CLASSES.FILE_DOWNLOAD_LINK}`, elem);
                    const fileIcons = Utils.safeQuerySelectorAll('svg.octicon.octicon-file, svg.color-fg-muted', elem);

                    downloadLinks.forEach(el => el.style.display = 'inline');
                    fileIcons.forEach(el => el.style.display = 'none');
                };

                const mouseOutHandler = (evt) => {
                    const elem = evt.currentTarget;
                    const downloadLinks = Utils.safeQuerySelectorAll(`.${CONFIG.CSS_CLASSES.FILE_DOWNLOAD_LINK}`, elem);
                    const fileIcons = Utils.safeQuerySelectorAll('svg.octicon.octicon-file, svg.color-fg-muted', elem);

                    downloadLinks.forEach(el => el.style.display = 'none');
                    fileIcons.forEach(el => el.style.display = 'inline');
                };

                files.forEach(fileIcon => {
                    const row = fileIcon.parentNode?.parentNode;
                    if (!row) return;

                    const linkElement = Utils.safeQuerySelector('[role="rowheader"] > .css-truncate.css-truncate-target.d-block.width-fit > a, .react-directory-truncate>a', row);
                    if (!linkElement) return;

                    const fileName = linkElement.innerText;
                    const href = linkElement.getAttribute('href');
                    if (!href) return;

                    const href2 = href.replace('/blob/', '/');
                    const currentSource = DOWNLOAD_SOURCES.raw[this.rawFastIndex];
                    const url = currentSource[0] + href2;

                    const svgIcon = '<svg class="octicon octicon-cloud-download" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path d="M9 12h2l-3 3-3-3h2V7h2v5zm3-8c0-.44-.91-3-4.5-3C5.08 1 3 2.92 3 5 1.02 5 0 6.52 0 8c0 1.53 1 3 3 3h3V9.7H3C1.38 9.7 1.3 8.28 1.3 8c0-.17.05-1.7 1.7-1.7h1.3V5c0-1.39 1.56-2.7 3.2-2.7 2.55 0 3.13 1.55 3.2 1.8v1.2H12c.81 0 2.7.22 2.7 2.2 0 2.09-2.25 2.2-2.7 2.2h-2V11h2c2.08 0 4-1.16 4-3.5C16 5.06 14.08 4 12 4z"></path></svg>';

                    fileIcon.insertAdjacentHTML('afterend',
                        `<a href="${url}" download="${fileName}" target="_blank" rel="noreferrer noopener nofollow" class="${CONFIG.CSS_CLASSES.FILE_DOWNLOAD_LINK}" style="display: none;" title="「${currentSource[1]}」加速下载">${svgIcon}</a>`
                    );

                    row.addEventListener('mouseover', mouseOverHandler);
                    row.addEventListener('mouseout', mouseOutHandler);
                });

            } catch (error) {
                console.warn('[GitHub增强] Raw下载链接添加失败:', error);
            }
        }

        // 切换Raw加速源
        toggleRawSource() {
            try {
                if (this.rawFastIndex >= DOWNLOAD_SOURCES.raw.length - 1) {
                    this.rawFastIndex = 0;
                } else {
                    this.rawFastIndex += 1;
                }

                Storage.set(CONFIG.STORAGE_KEYS.RAW_FAST_INDEX, this.rawFastIndex);

                // 移除旧的下载链接
                Utils.safeQuerySelectorAll(`.${CONFIG.CSS_CLASSES.FILE_DOWNLOAD_LINK}`).forEach(el => el.remove());

                setTimeout(() => this.addRawDownLink(), 100);

                const currentSource = DOWNLOAD_SOURCES.raw[this.rawFastIndex];
                this.showNotification(`已切换加速源为:${currentSource[1]}`);

            } catch (error) {
                console.warn('[GitHub增强] 切换加速源失败:', error);
            }
        }

        // 注册菜单命令
        registerMenuCommands() {
            const menuConfigs = [
                {
                    label: "所有翻译",
                    key: CONFIG.STORAGE_KEYS.ENABLE_TRANSLATION,
                    callback: (newState) => {
                        if (newState) {
                            this.traverseNode(document.body);
                        }
                    }
                },
                {
                    label: "自动跳转",
                    key: CONFIG.STORAGE_KEYS.AUTO_REDIRECT,
                    callback: (newState) => {
                        this.showNotification(`自动跳转已${newState ? '启用' : '禁用'},刷新页面生效`);
                    }
                }
            ];

            menuConfigs.forEach(config => this.createMenuCommand(config));

            GM_registerMenuCommand("切换加速源", () => {
                this.toggleRawSource();
            });
        }

        // 创建菜单命令
        createMenuCommand({ label, key, callback }) {
            let menuId;

            const getMenuLabel = (label, isEnabled) => `${isEnabled ? "禁用" : "启用"} ${label}`;

            const toggle = () => {
                const currentState = Storage.get(key);
                const newState = !currentState;

                Storage.set(key, newState);
                this.showNotification(`${label}已${newState ? '启用' : '禁用'}`);

                if (callback) callback(newState);

                GM_unregisterMenuCommand(menuId);
                menuId = GM_registerMenuCommand(getMenuLabel(label, newState), toggle);
            };

            const currentState = Storage.get(key);
            menuId = GM_registerMenuCommand(getMenuLabel(label, currentState), toggle);
        }

        // 添加Download ZIP加速
        addDownloadZIP(target) {
            try {
                const html = Utils.safeQuerySelector('ul[class^=prc-ActionList-ActionList-]>li:last-child', target);
                if (!html) return;

                const scriptElement = Utils.safeQuerySelector('react-partial[partial-name=repos-overview]>script[data-target="react-partial.embeddedData"]');
                if (!scriptElement) return;

                const scriptContent = scriptElement.textContent;
                const zipUrlStart = scriptContent.indexOf('"zipballUrl":"');
                if (zipUrlStart === -1) return;

                const zipUrlSlice = scriptContent.slice(zipUrlStart + 14);
                const zipUrlEnd = zipUrlSlice.indexOf('"');
                if (zipUrlEnd === -1) return;

                const href = zipUrlSlice.slice(0, zipUrlEnd);
                const downloadSources = DOWNLOAD_SOURCES.release;
                let htmlContent = '';

                downloadSources.forEach(([sourceUrl, sourceName, sourceDesc]) => {
                    const clonedElement = html.cloneNode(true);
                    const linkElement = Utils.safeQuerySelector('a[href$=".zip"]', clonedElement);
                    const spanElement = Utils.safeQuerySelector('span[id]', clonedElement);

                    if (linkElement && spanElement) {
                        const url = sourceUrl + href;
                        linkElement.href = url;
                        linkElement.setAttribute('title', sourceDesc);
                        linkElement.setAttribute('target', '_blank');
                        linkElement.setAttribute('rel', 'noreferrer noopener nofollow');
                        spanElement.textContent = `Download ZIP ${sourceName}`;
                        htmlContent += clonedElement.outerHTML;
                    }
                });

                if (htmlContent) {
                    html.insertAdjacentHTML('afterend', htmlContent);
                }

            } catch (error) {
                console.warn('[GitHub增强] Download ZIP加速添加失败:', error);
            }
        }

        // 添加Git Clone加速
        addGitClone(target) {
            try {
                const html = Utils.safeQuerySelector('input[value^="https:"]:not([title])', target);
                if (!html) return;

                // 将镜像站URL替换为官方URL
                const originalUrl = html.value.replace('https://hub.mihoyo.online/', 'https://github.com/');
                const hrefSplit = originalUrl.split('https://github.com')[1];
                if (!hrefSplit) return;

                const htmlParent = `<div style="margin-top: 4px;" class="XIU2-GC ${html.parentElement.className}">`;
                let htmlContent = '';
                let gitClonePrefix = '';

                if (html.nextElementSibling) {
                    html.nextElementSibling.hidden = true;
                }

                if (html.parentElement.nextElementSibling && html.parentElement.nextElementSibling.tagName === 'SPAN') {
                    html.parentElement.nextElementSibling.textContent += ' (↑点击上面文字可复制)';
                }

                if (Storage.get(CONFIG.STORAGE_KEYS.GIT_CLONE)) {
                    gitClonePrefix = 'git clone ';
                    html.value = gitClonePrefix + html.value;
                    html.setAttribute('value', html.value);
                }

                DOWNLOAD_SOURCES.clone.forEach(([sourceUrl, sourceName, sourceDesc]) => {
                    const clonedInput = html.cloneNode(true);
                    let url;

                    if (sourceUrl === 'https://gitclone.com') {
                        url = sourceUrl + '/github.com' + hrefSplit;
                    } else {
                        url = sourceUrl + hrefSplit;
                    }

                    clonedInput.title = `${url}\n\n${sourceDesc}\n\n提示:点击文字可直接复制`;
                    clonedInput.setAttribute('value', gitClonePrefix + url);
                    htmlContent += htmlParent + clonedInput.outerHTML + '</div>';
                });

                if (htmlContent) {
                    html.parentElement.insertAdjacentHTML('afterend', htmlContent);

                    // 添加复制功能
                    if (!html.parentElement.parentElement.classList.contains('XIU2-GCP')) {
                        html.parentElement.parentElement.classList.add('XIU2-GCP');
                        html.parentElement.parentElement.addEventListener('click', (e) => {
                            if (e.target.tagName === 'INPUT') {
                                try {
                                    GM_setClipboard(e.target.value);
                                    this.showNotification('已复制到剪贴板');
                                } catch (error) {
                                    console.warn('[GitHub增强] 复制失败:', error);
                                }
                            }
                        });
                    }
                }

            } catch (error) {
                console.warn('[GitHub增强] Git Clone加速添加失败:', error);
            }
        }

        // 显示通知
        showNotification(message) {
            try {
                GM_notification({
                    text: `[GitHub增强] ${message}`,
                    timeout: 3000
                });
            } catch (error) {
                console.log(`[GitHub增强] ${message}`);
            }
        }
    }

    // ==================== 应用启动 ====================

    const app = new GitHubEnhancer();

    const startApp = async () => {
        try {
            await app.init();
        } catch (error) {
            console.error('[GitHub增强] 应用启动失败:', error);
        }
    };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', startApp);
    } else {
        startApp();
    }

    // 导出到全局作用域(用于调试)
    if (typeof window !== 'undefined') {
        window.GitHubEnhancer = app;
    }

})();