CSDN-PRO,CSDN优化工具

自动清理CSDN网站上的广告、垃圾内容和登录(不可用)弹窗,支持自定义配置。优化UI设计,使用现代化美观样式,避免影响CSDN原有样式。

// ==UserScript==
// @name         CSDN-PRO,CSDN优化工具
// @namespace    http://tampermonkey.net/
// @version      1.0.8
// @description  自动清理CSDN网站上的广告、垃圾内容和登录(不可用)弹窗,支持自定义配置。优化UI设计,使用现代化美观样式,避免影响CSDN原有样式。
// @author       tanzz
// @match        https://*.csdn.net/*
// @grant        GM_addStyle
// @run-at       document-idle
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 脚本版本,用于检测更新
    const SCRIPT_VERSION = '1.0.8';
    
    // 调试开关配置 - 控制调试日志输出
    // 生产环境保持关闭,确保用户体验
    const DEBUG_CONFIG = {
        enabled: false,      // 生产环境静默模式 (关闭所有调试输出)
        logLevel: 'warn'     // 仅显示警告和错误
    };
    
    // 初始配置结构(树形)
    const defaultConfig = {
        version: SCRIPT_VERSION, // 添加版本信息
        login: {
            enabled: true,
            name: '登录(不可用)清理',
            children: {
                loginPopups: {
                    enabled: true,
                    name: '登录(不可用)弹窗',
                    selectors: ['.login-mark',
                        '.passport-login-container',
                        '.modal-backdrop',
                        '.passport-login-tip-container',
                        'body > div.passport-login-tip-container.false'
                    ]
                },
            }
        },
        leftSide: {
            enabled: true,
            name: '左侧栏清理',
            children: {
                author: {
                    enabled: true,
                    name: '作者信息',
                    selectors: ['#asideProfile']
                },
                carouselAds: {
                    enabled: true,
                    name: '轮播图广告',
                    selectors: ['.swiper-remuneration-container']
                },
                cKnow: {
                    enabled: true,
                    name: '知道',
                    selectors: ['#mainBox > aside > div.c-blog-side-box']
                },
                hisSelection: {
                    enabled: true,
                    name: 'TA的精选',
                    selectors: ['#his-selection']
                },
                hotArticle: {
                    enabled: true,
                    name: '热门文章',
                    selectors: ['#asideHotArticle']
                },
            }
        },
        ads: {
            enabled: true,
            name: '广告清理',
            children: {
                bannerAds: {
                    enabled: true,
                    name: '横幅广告',
                    selectors: ['.banner-ad', '.advertisement']
                },
                feedAds: {
                    enabled: true,
                    name: '信息流广告',
                    selectors: ['.feed-advert', '.flow-ad']
                },
                tracking: {
                    enabled: true,
                    name: '统计跟踪元素',
                    selectors: ['.csdn-tracking-statistics']
                },
            }
        },
        garbageContent: {
            enabled: true,
            name: '垃圾内容清理',
            children: {
                toolbar: {
                    enabled: true,
                    name: '标题栏',
                    children: {
                        vipIcon: {
                            enabled: true,
                            name: '会员图标',
                            selectors: ['.toolbar-btn-vip']
                        }
                    }
                },
                recommendedArticles: {
                    enabled: true,
                    name: '推荐文章',
                    selectors: ['.recommend-article', '.related-read']
                },
                footerContent: {
                    enabled: true,
                    name: '底部垃圾内容',
                    selectors: ['.blog-footer-bottom']
                },
                toolbox: {
                    enabled: true,
                    name: '工具栏',
                    children: {
                        aiAssistant: {
                            enabled: true,
                            name: 'AI助手',
                            selectors: ['.slide-details-cknows-box']
                        },
                        appBox: {
                            enabled: true,
                            name: '应用下载',
                            selectors: ['a.option-box.styleab[data-type="app"]']
                        },
                        chatBox: {
                            enabled: true,
                            name: '客服',
                            selectors: [
                                'a.option-box.styleab[data-type="cs"]',
                            ]
                        },
                        toolbox: {
                            enabled: true,
                            name: '收藏弹窗',
                            selectors: ['.tool-active-list'],
                        }
                    }
                }
            }
        },
        // 定期检查的时间间隔(毫秒)
        checkInterval: 1000
    };

    // 加载用户配置
    let userConfig;
    function loadConfig() {
        try {
            const savedConfig = localStorage.getItem('csdnCleanerConfig');
            debugLog('debug', '从localStorage读取的原始配置数据:', savedConfig);
            
            if (savedConfig) {
                const parsedConfig = JSON.parse(savedConfig);
                debugLog('debug', '从localStorage加载的配置:', parsedConfig);
                
                // 检查版本更新
                if (parsedConfig.version !== SCRIPT_VERSION) {
                    debugLog('info', `检测到脚本版本更新: ${parsedConfig.version} -> ${SCRIPT_VERSION}`);
                    debugLog('info', '将使用新的默认配置但保留用户设置');
                    
                    // 版本更新时,清理旧缓存并保留用户设置
                    userConfig = JSON.parse(JSON.stringify(defaultConfig));
                    
                    // 清理可能的旧版本缓存数据
                    const oldKeys = [
                        'csdnCleanerConfig_old',
                        'csdnCleanerCache',
                        'csdnCleanerSelectors',
                        'csdnCleanerVersion'
                    ];
                    oldKeys.forEach(key => {
                        if (localStorage.getItem(key)) {
                            localStorage.removeItem(key);
                            debugLog('debug', `已清理旧缓存: ${key}`);
                        }
                    });
                    
                    // 只保留用户的启用/禁用设置,使用新的选择器
                    function preserveUserSettings(target, source) {
                        for (let key in source) {
                            if (key === 'selectors' || key === 'version') {
                                // 选择器和版本号始终使用新的
                                continue;
                            }
                            if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
                                if (target[key] && target[key].enabled !== undefined) {
                                    source[key].enabled = target[key].enabled;
                                }
                                if (source[key].children) {
                                    preserveUserSettings(target[key]?.children || {}, source[key].children);
                                }
                            }
                        }
                    }
                    
                    preserveUserSettings(parsedConfig, userConfig);
                    saveConfig(); // 保存更新后的配置
                    
                    debugLog('info', '版本更新完成,旧缓存已清理');
                } else {
                    // 版本相同,正常合并
                    userConfig = JSON.parse(JSON.stringify(defaultConfig));
                    
                    // 深合并配置,确保结构完整
                    function deepMerge(target, source) {
                        for (let key in source) {
                            if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
                                if (!target[key]) target[key] = {};
                                if (typeof target[key] === 'object' && !Array.isArray(target[key])) {
                                    deepMerge(target[key], source[key]);
                                }
                            } else {
                                target[key] = source[key];
                            }
                        }
                        return target;
                    }

                    deepMerge(userConfig, parsedConfig);
                    debugLog('debug', '合并后的配置:', userConfig);
                }
            } else {
                debugLog('info', '未找到已保存的配置,使用默认配置');
                userConfig = JSON.parse(JSON.stringify(defaultConfig));
            }
        } catch (error) {
            debugLog('error', '加载配置失败:', error);
            userConfig = JSON.parse(JSON.stringify(defaultConfig));
        }
    }
    loadConfig();

    // 保存配置到localStorage
    function saveConfig() {
        try {
            const configToSave = JSON.stringify(userConfig, null, 2);
            debugLog('debug', '准备保存的配置:', configToSave);
            localStorage.setItem('csdnCleanerConfig', configToSave);
            debugLog('info', '配置已成功保存到localStorage');
            
            // 验证保存
            const savedConfig = localStorage.getItem('csdnCleanerConfig');
            debugLog('debug', '保存后验证:', savedConfig);
        } catch (error) {
            debugLog('error', '保存配置失败:', error);
            alert('保存配置失败: ' + error.message);
        }
    }

    // 强制清理缓存
    function forceClearCache() {
        try {
            debugLog('info', '开始强制清理缓存...');
            
            // 清理所有相关缓存
            const keysToRemove = [
                'csdnCleanerConfig',
                'csdnCleanerConfig_old',
                'csdnCleanerCache',
                'csdnCleanerSelectors',
                'csdnCleanerVersion'
            ];
            
            keysToRemove.forEach(key => {
                if (localStorage.getItem(key)) {
                    localStorage.removeItem(key);
                    debugLog('debug', `已清理缓存: ${key}`);
                }
            });
            
            debugLog('info', '缓存清理完成,将重新加载默认配置');
            
            // 重新加载配置
            userConfig = JSON.parse(JSON.stringify(defaultConfig));
            saveConfig();
            
            alert('缓存已清理!页面将刷新以应用最新配置。');
            location.reload();
            
        } catch (error) {
            debugLog('error', '清理缓存失败:', error);
            alert('清理缓存失败: ' + error.message);
        }
    }

    // 递归获取所有启用的选择器
    function getAllEnabledSelectors(configNode) {
        let selectors = [];

        // 如果节点本身不启用,直接返回空数组
        if (!configNode.enabled) {
            debugLog('debug', '节点已禁用,跳过:', configNode.name || 'unnamed');
            return selectors;
        }

        if (configNode.selectors) {
            selectors = selectors.concat(configNode.selectors);
            debugLog('debug', `节点 ${configNode.name || '未命名'} 的选择器:`, configNode.selectors);
        }

        if (configNode.children) {
            Object.values(configNode.children).forEach(child => {
                selectors = selectors.concat(getAllEnabledSelectors(child));
            });
        }

        return selectors;
    }

    // 移除元素函数
    function removeElements(selectors) {
        selectors.forEach(selector => {
            const elements = document.querySelectorAll(selector);
            elements.forEach(el => {
                // 先尝试隐藏元素
                el.style.display = 'none';
                // 然后从DOM中移除
                if (el.parentNode) {
                    el.parentNode.removeChild(el);
                }
            });
        });
    }

    // 统一调试日志函数
    function debugLog(level, ...args) {
        if (!DEBUG_CONFIG.enabled) return;
        
        const levels = ['debug', 'info', 'warn', 'error'];
        const currentLevelIndex = levels.indexOf(DEBUG_CONFIG.logLevel);
        const messageLevelIndex = levels.indexOf(level);
        
        if (messageLevelIndex >= currentLevelIndex) {
            console[level](`[CSDN清理工具 ${level.toUpperCase()}]:`, ...args);
        }
    }

    // 主清理函数
    function cleanPage() {
        debugLog('info', '开始清理页面...');
        
        // 动态记录所有配置状态
        function logConfigStatus(config, prefix = '') {
            Object.entries(config).forEach(([key, value]) => {
                if (typeof value === 'object' && value !== null && key !== 'selectors') {
                    if (value.enabled !== undefined) {
                        debugLog('debug', `${prefix}${key}: ${value.enabled ? '启用' : '禁用'}`);
                    }
                    if (value.children) {
                        logConfigStatus(value.children, `${prefix}${key}.`);
                    }
                }
            });
        }
        
        // 记录所有配置状态
        logConfigStatus(userConfig);
        
        // 动态记录所有选择器匹配情况
        function logSelectors(config, prefix = '') {
            Object.entries(config).forEach(([key, value]) => {
                if (typeof value === 'object' && value !== null) {
                    if (value.enabled && value.selectors) {
                        value.selectors.forEach(selector => {
                            const elements = document.querySelectorAll(selector);
                            debugLog('debug', `${prefix}${key} - 选择器 "${selector}" 匹配到 ${elements.length} 个元素`);
                        });
                    }
                    if (value.children) {
                        logSelectors(value.children, `${prefix}${key}.`);
                    }
                }
            });
        }
        
        // 记录所有选择器匹配情况(仅在调试模式下显示)
        logSelectors(userConfig);

        // 获取所有启用的选择器
        const allSelectors = [];
        
        function collectAllSelectors(config) {
            let selectors = [];
            Object.values(config).forEach(node => {
                if (typeof node === 'object' && node !== null) {
                    selectors = selectors.concat(getAllEnabledSelectors(node));
                }
            });
            return selectors;
        }
        
        const allEnabledSelectors = collectAllSelectors(userConfig);
        debugLog('debug', '启用的选择器统计:', {
            总选择器数量: allEnabledSelectors.length
        });

        // 移除所有匹配的元素
        removeElements(allEnabledSelectors);

        // 移除可能的弹窗事件监听
        document.body.style.overflow = 'auto'; // 恢复页面滚动
        debugLog('info', '页面清理完成');
    }

    // 显示版本信息
    debugLog('info', `CSDN Cleaner v${SCRIPT_VERSION} 正式版已加载`);

    // 初始清理
    cleanPage();

    // 设置定期检查,处理动态加载的内容
    setInterval(cleanPage, defaultConfig.checkInterval);

    // 创建配置面板UI
    function createConfigPanel() {
        // 添加样式 - 现代化的美观设计
        GM_addStyle(
            '#csdn-cleaner-panel {' +
            '    position: fixed;' +
            '    top: 50%;' +
            '    transform: translateY(-50%);' +
            '    right: 20px;' +
            '    width: 320px;' +
            '    max-height: 80vh;' +
            '    background: #ffffff;' +
            '    border: 1px solid #e2e8f0;' +
            '    border-radius: 12px;' +
            '    box-shadow: 0 10px 25px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05);' +
            '    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;' +
            '    font-size: 14px;' +
            '    line-height: 1.5;' +
            '    z-index: 9999;' +
            '    overflow: hidden;' +
            '    backdrop-filter: blur(10px);' +
            '    display: flex;' +
            '    flex-direction: column;' +
            '}' +
            '#csdn-cleaner-toggle {' +
            '    position: fixed;' +
            '    top: 50%;' +
            '    right: 0px;' +
            '    transform: translateY(-50%);' +
            '    width: 24px;' +
            '    height: 60px;' +
            '    background: linear-gradient(135deg, rgba(102, 126, 234, 0.7) 0%, rgba(118, 75, 162, 0.7) 100%);' +
            '    border: none;' +
            '    border-radius: 12px 0 0 12px;' +
            '    color: white;' +
            '    cursor: pointer;' +
            '    font-size: 10px;' +
            '    line-height: 1.1;' +
            '    z-index: 1000;' +
            '    box-shadow: -1px 0 6px rgba(102, 126, 234, 0.15);' +
            '    transition: all 0.3s ease;' +
            '    display: flex;' +
            '    align-items: center;' +
            '    justify-content: center;' +
            '    opacity: 0.5;' +
            '    writing-mode: vertical-lr;' +
            '    text-orientation: mixed;' +
            '    padding: 6px 2px;' +
            '}' +
            '#csdn-cleaner-toggle:hover {' +
            '    opacity: 0.8;' +
            '    transform: translateY(-50%) translateX(-1px);' +
            '    box-shadow: -2px 0 8px rgba(102, 126, 234, 0.25);' +
            '}' +
            '#csdn-cleaner-panel h3 {' +
            '    margin: 0 0 20px 0;' +
            '    padding: 20px 20px 0 20px;' +
            '    font-size: 18px;' +
            '    font-weight: 700;' +
            '    color: #1a202c;' +
            '    text-align: center;' +
            '    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);' +
            '    -webkit-background-clip: text;' +
            '    -webkit-text-fill-color: transparent;' +
            '    background-clip: text;' +
            '}' +
            '.config-group {' +
            '    margin-bottom: 12px;' +
            '}' +
            '.config-header {' +
            '    display: flex;' +
            '    align-items: center;' +
            '    margin-bottom: 6px;' +
            '    cursor: pointer;' +
            '    padding: 8px 0;' +
            '    border-radius: 6px;' +
            '    transition: all 0.2s ease;' +
            '}' +
            '.config-header:hover {' +
            '    background: #f7fafc;' +
            '    transform: translateX(2px);' +
            '}' +
            '.config-header input[type="checkbox"] {' +
            '    margin: 0 10px 0 0;' +
            '    width: 16px;' +
            '    height: 16px;' +
            '    cursor: pointer;' +
            '    accent-color: #667eea;' +
            '}' +
            '.config-header label {' +
            '    cursor: pointer;' +
            '    user-select: none;' +
            '    color: #2d3748;' +
            '    font-size: 14px;' +
            '    font-weight: 500;' +
            '}' +
            '.config-children {' +
            '    margin-left: 24px;' +
            '    margin-top: 6px;' +
            '    border-left: 2px solid #e2e8f0;' +
            '    padding-left: 12px;' +
            '}' +
            '.config-children .config-header label {' +
            '    font-size: 13px;' +
            '    color: #4a5568;' +
            '    font-weight: 400;' +
            '}' +
            '.button-container {' +
            '    padding: 0 20px 15px 20px;' +
            '    margin-top: 15px;' +
            '    border-top: 1px solid #e2e8f0;' +
            '    padding-top: 12px;' +
            '    flex-shrink: 0;' +
            '    display: flex;' +
            '    gap: 10px;' +
            '}' +
            '.save-btn, .reset-btn {' +
            '    border: none;' +
            '    padding: 10px 16px;' +
            '    border-radius: 8px;' +
            '    cursor: pointer;' +
            '    font-size: 14px;' +
            '    font-weight: 600;' +
            '    width: 50%;' +
            '    margin-top: 8px;' +
            '    transition: all 0.3s ease;' +
            '    font-family: inherit;' +
            '}' +
            '.save-btn {' +
            '    background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);' +
            '    color: white;' +
            '    box-shadow: 0 2px 8px rgba(72, 187, 120, 0.3);' +
            '}' +
            '.save-btn:hover {' +
            '    transform: translateY(-1px);' +
            '    box-shadow: 0 4px 12px rgba(72, 187, 120, 0.4);' +
            '}' +
            '.reset-btn {' +
            '    background: linear-gradient(135deg, #f56565 0%, #e53e3e 100%);' +
            '    color: white;' +
            '    box-shadow: 0 2px 8px rgba(245, 101, 101, 0.3);' +
            '}' +
            '.reset-btn:hover {' +
            '    transform: translateY(-1px);' +
            '    box-shadow: 0 4px 12px rgba(245, 101, 101, 0.4);' +
            '}' +
            '.scroll-container {' +
            '    padding: 0 20px;' +
            '    max-height: 450px;' +
            '    overflow-y: auto;' +
            '    scrollbar-width: thin;' +
            '    scrollbar-color: #cbd5e0 #f7fafc;' +
            '    overscroll-behavior: contain;' +
            '}' +
            '.scroll-container::-webkit-scrollbar {' +
            '    width: 6px;' +
            '}' +
            '.scroll-container::-webkit-scrollbar-track {' +
            '    background: #f7fafc;' +
            '    border-radius: 3px;' +
            '}' +
            '.scroll-container::-webkit-scrollbar-thumb {' +
            '    background: #cbd5e0;' +
            '    border-radius: 3px;' +
            '}' +
            '.scroll-container::-webkit-scrollbar-thumb:hover {' +
            '    background: #a0aec0;' +
            '}'
        );

        // 切换按钮 - 可拖拽
        const toggleBtn = document.createElement('button');
        toggleBtn.id = 'csdn-cleaner-toggle';
        toggleBtn.textContent = '设置';
        toggleBtn.draggable = true;
        document.body.appendChild(toggleBtn);

        // 拖拽功能
        let isDragging = false;
        let currentY;
        let initialY;
        let yOffset = 0;

        // 从localStorage读取保存的位置
        const savedPosition = localStorage.getItem('csdnCleanerTogglePosition');
        if (savedPosition) {
            try {
                const pos = JSON.parse(savedPosition);
                toggleBtn.style.top = pos.top + 'px';
                toggleBtn.style.transform = 'translateY(0)';
                yOffset = pos.top;
            } catch (e) {
                // 如果位置数据无效,使用默认位置
            }
        }

        toggleBtn.addEventListener('dragstart', function(e) {
            isDragging = true;
            initialY = e.clientY - yOffset;
            toggleBtn.style.transition = 'none';
            e.dataTransfer.effectAllowed = 'move';
            e.dataTransfer.setData('text/html', '');
        });

        document.addEventListener('dragover', function(e) {
            if (isDragging) {
                e.preventDefault();
                e.dataTransfer.dropEffect = 'move';
            }
        });

        document.addEventListener('drop', function(e) {
            if (isDragging) {
                e.preventDefault();
                isDragging = false;
                
                currentY = e.clientY;
                yOffset = currentY - initialY;
                
                // 限制在可视窗口内
                const maxY = window.innerHeight - toggleBtn.offsetHeight;
                const newTop = Math.max(0, Math.min(currentY - initialY, maxY));
                
                toggleBtn.style.top = newTop + 'px';
                toggleBtn.style.transform = 'translateY(0)';
                
                // 保存位置到localStorage
                localStorage.setItem('csdnCleanerTogglePosition', JSON.stringify({ top: newTop }));
                
                toggleBtn.style.transition = 'all 0.3s ease';
            }
        });

        toggleBtn.addEventListener('dragend', function(e) {
            if (isDragging) {
                isDragging = false;
                toggleBtn.style.transition = 'all 0.3s ease';
            }
        });

        // 触摸设备支持
        let touchStartY = 0;
        let touchCurrentY = 0;

        toggleBtn.addEventListener('touchstart', function(e) {
            touchStartY = e.touches[0].clientY;
            toggleBtn.style.transition = 'none';
        });

        toggleBtn.addEventListener('touchmove', function(e) {
            e.preventDefault();
            touchCurrentY = e.touches[0].clientY;
            
            const deltaY = touchCurrentY - touchStartY;
            const currentTop = parseInt(toggleBtn.style.top) || (window.innerHeight / 2 - toggleBtn.offsetHeight / 2);
            
            // 限制在可视窗口内
            const maxY = window.innerHeight - toggleBtn.offsetHeight;
            const newTop = Math.max(0, Math.min(currentTop + deltaY, maxY));
            
            toggleBtn.style.top = newTop + 'px';
            toggleBtn.style.transform = 'translateY(0)';
            
            touchStartY = touchCurrentY;
            
            // 保存位置
            localStorage.setItem('csdnCleanerTogglePosition', JSON.stringify({ top: newTop }));
        });

        toggleBtn.addEventListener('touchend', function(e) {
            toggleBtn.style.transition = 'all 0.3s ease';
        });

        // 创建配置面板
        const panel = document.createElement('div');
        panel.id = 'csdn-cleaner-panel';
        panel.style.display = 'none';
        document.body.appendChild(panel);

        // 创建标题容器
        const titleContainer = document.createElement('div');
        titleContainer.style.cssText = 'display: flex; align-items: center; justify-content: space-between; padding: 20px 20px 0 20px; margin-bottom: 20px;';
        
        // 创建面板标题
        const title = document.createElement('h3');
        title.textContent = 'CSDN清理工具';
        title.style.cssText = 'margin: 0; font-size: 18px; font-weight: 700; color: #1a202c; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;';
        
        // 创建清理缓存小按钮
        const clearCacheBtn = document.createElement('button');
        clearCacheBtn.textContent = '⚡';
        clearCacheBtn.title = '清理缓存';
        clearCacheBtn.style.cssText = 'width: 28px; height: 28px; background: #f7fafc; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 14px; cursor: pointer; transition: all 0.2s ease; display: flex; align-items: center; justify-content: center;';
        clearCacheBtn.addEventListener('mouseenter', function() {
            this.style.background = '#edf2f7';
            this.style.transform = 'scale(1.1)';
        });
        clearCacheBtn.addEventListener('mouseleave', function() {
            this.style.background = '#f7fafc';
            this.style.transform = 'scale(1)';
        });
        clearCacheBtn.addEventListener('click', function(e) {
            e.stopPropagation();
            debugLog('info', '清理缓存按钮被点击');
            if (confirm('确定要清理缓存并重新加载配置吗?')) {
                forceClearCache();
            }
        });
        
        titleContainer.appendChild(title);
        titleContainer.appendChild(clearCacheBtn);
        panel.appendChild(titleContainer);

        // 创建可滚动的内容容器
        const scrollContainer = document.createElement('div');
        scrollContainer.className = 'scroll-container';
        scrollContainer.style.cssText = 'flex: 1; overflow-y: auto; padding: 0 20px;';
        panel.appendChild(scrollContainer);

        // 根据路径获取配置值
        function getConfigValueByPath(path) {
            const parts = path.split('.');
            let current = userConfig;

            // 处理顶层节点
            if (parts.length === 1) {
                return current[parts[0]] ? current[parts[0]].enabled : undefined;
            }

            // 处理嵌套节点
            for (let i = 0; i < parts.length - 1; i++) {
                if (current[parts[i]] && current[parts[i]].children) {
                    current = current[parts[i]].children;
                } else {
                    return undefined; // 路径无效
                }
            }

            // 获取最终节点
            if (current[parts[parts.length - 1]]) {
                return current[parts[parts.length - 1]].enabled;
            }
            return undefined;
        }

        // 根据路径更新配置
        function updateConfigByPath(path, key, value) {
            const parts = path.split('.');
            let current = userConfig;

            // 处理顶层节点
            if (parts.length === 1) {
                if (current[parts[0]]) {
                    current[parts[0]][key] = value;
                }
                return;
            }

            // 处理嵌套节点
            for (let i = 0; i < parts.length - 1; i++) {
                if (current[parts[i]] && current[parts[i]].children) {
                    current = current[parts[i]].children;
                } else {
                    return; // 路径无效
                }
            }

            // 更新最终节点
            if (current[parts[parts.length - 1]]) {
                current[parts[parts.length - 1]][key] = value;
            }
        }

        // 创建配置项
        function createConfigItems(configNode, parentElement, path) {
            const group = document.createElement('div');
            group.className = 'config-group';

            const header = document.createElement('div');
            header.className = 'config-header';

            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            // 根据当前保存的配置值设置复选框状态
            const savedValue = getConfigValueByPath(path);
            checkbox.checked = savedValue !== undefined ? savedValue : configNode.enabled;
            checkbox.dataset.path = path;

            checkbox.addEventListener('change', function () {
                updateConfigByPath(path, 'enabled', this.checked);
                debugLog('debug', `已更新 ${path}.enabled 为 ${this.checked}`);

                // 更新子节点
                const childCheckboxes = document.querySelectorAll(`input[data-path^="${path}."]`);
                childCheckboxes.forEach(cb => {
                    cb.checked = this.checked;
                    updateConfigByPath(cb.dataset.path, 'enabled', this.checked);
                    debugLog('debug', `已更新 ${cb.dataset.path}.enabled 为 ${this.checked}`);
                });

                // 更新父节点
                updateParentCheckboxState(this);

                // 自动保存配置
                saveConfig();
                debugLog('info', '配置已自动保存');
            });

            const label = document.createElement('label');
            label.textContent = configNode.name || path.split('.').pop();

            header.appendChild(checkbox);
            header.appendChild(label);
            group.appendChild(header);

            // 如果有子项,创建子项
            if (configNode.children) {
                const childrenContainer = document.createElement('div');
                childrenContainer.className = 'config-children';

                Object.entries(configNode.children).forEach(([key, childNode]) => {
                    const childPath = path ? `${path}.${key}` : key;
                    createConfigItems(childNode, childrenContainer, childPath);
                });

                group.appendChild(childrenContainer);
            }

            parentElement.appendChild(group);
        }

        // 更新父节点复选框状态
        function updateParentCheckboxState(childCheckbox) {
            const childPath = childCheckbox.dataset.path;
            const parentPath = childPath.substring(0, childPath.lastIndexOf('.'));

            if (parentPath) {
                const parentCheckbox = document.querySelector(`input[data-path="${parentPath}"]`);
                if (parentCheckbox) {
                    const childCheckboxes = document.querySelectorAll(`input[data-path^="${parentPath}."]`);
                    const allChecked = Array.from(childCheckboxes).every(cb => cb.checked);
                    const anyChecked = Array.from(childCheckboxes).some(cb => cb.checked);

                    if (allChecked) {
                        parentCheckbox.checked = true;
                        parentCheckbox.indeterminate = false;
                    } else if (anyChecked) {
                        parentCheckbox.checked = false;
                        parentCheckbox.indeterminate = true;
                    } else {
                        parentCheckbox.checked = false;
                        parentCheckbox.indeterminate = false;
                    }

                    // 更新父节点配置
                    updateConfigByPath(parentPath, 'enabled', allChecked);

                    // 递归更新更上层的父节点 - 限制递归深度以避免堆栈溢出
                    if (parentPath.split('.').length < 5) {
                        updateParentCheckboxState(parentCheckbox);
                    }
                }
            }
        }

        // 动态添加所有配置项
        function addAllConfigItems(config, container) {
            Object.entries(config).forEach(([key, value]) => {
                if (typeof value === 'object' && value !== null && key !== 'version' && key !== 'checkInterval') {
                    createConfigItems(value, container, key);
                }
            });
        }

        // 添加所有配置项
        addAllConfigItems(userConfig, scrollContainer);

        // 初始化所有父节点状态
        function initAllParentStates() {
            // 获取所有顶级配置项
            const topLevelKeys = Object.keys(userConfig).filter(key => 
                typeof userConfig[key] === 'object' && 
                userConfig[key] !== null && 
                key !== 'version' && 
                key !== 'checkInterval'
            );

            // 为每个顶级配置项初始化父节点状态
            topLevelKeys.forEach(key => {
                const childCheckboxes = document.querySelectorAll(`input[data-path^="${key}."]`);
                if (childCheckboxes.length > 0) {
                    updateParentCheckboxState(childCheckboxes[0]);
                }
            });
        }

        // 调用初始化函数
        initAllParentStates();

        // 创建按钮容器 - 精简布局
        const buttonContainer = document.createElement('div');
        buttonContainer.style.cssText = 'display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid #e2e8f0; background: #f8fafc; flex-shrink: 0;';
        buttonContainer.style.flexShrink = '0';
        buttonContainer.className = 'button-container';

        // 保存按钮
        const saveBtn = document.createElement('button');
        saveBtn.className = 'save-btn';
        saveBtn.textContent = '保存';
        saveBtn.style.cssText = 'flex: 1; padding: 8px 12px; font-size: 13px; font-weight: 600; color: white; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s ease;';
        debugLog('debug', '保存按钮已创建');
        saveBtn.addEventListener('click', function () {
            debugLog('info', '保存按钮被点击');
            saveConfig();
            alert('配置已保存!页面将自动刷新以应用新配置。');
            location.reload();
        });

        // 重置按钮
        const resetBtn = document.createElement('button');
        resetBtn.className = 'reset-btn';
        resetBtn.textContent = '重置';
        resetBtn.style.cssText = 'flex: 1; padding: 8px 12px; font-size: 13px; font-weight: 600; color: white; background: linear-gradient(135deg, #fc8181 0%, #f56565 100%); border: none; border-radius: 6px; cursor: pointer; transition: all 0.2s ease;';
        debugLog('debug', '重置按钮已创建');
        resetBtn.addEventListener('click', function () {
            debugLog('info', '重置按钮被点击');
            if (confirm('确定要重置为默认配置吗?此操作不可撤销!')) {
                userConfig = JSON.parse(JSON.stringify(defaultConfig));
                debugLog('debug', '重置后的配置:', userConfig);
                
                // 更新UI状态
                function updateCheckboxes(config, path = '') {
                    Object.entries(config).forEach(([key, node]) => {
                        if (typeof node === 'object' && node !== null) {
                            const currentPath = path ? `${path}.${key}` : key;
                            const checkbox = document.querySelector(`input[data-path="${currentPath}"]`);
                            if (checkbox) {
                                checkbox.checked = node.enabled;
                            }
                            if (node.children) {
                                updateCheckboxes(node.children, currentPath);
                            }
                        }
                    });
                }
                updateCheckboxes(defaultConfig);
                
                // 保存重置后的配置
                saveConfig();
                
                // 重新执行清理
                cleanPage();
                
                debugLog('info', '配置已重置为默认值');
                
                alert('配置已重置为默认值!页面将自动刷新以应用默认配置。');
                location.reload(); // 重新加载页面以应用默认配置
            }
        });

        // 将按钮添加到按钮容器
        buttonContainer.appendChild(saveBtn);
        buttonContainer.appendChild(resetBtn);

        // 将按钮容器添加到面板
        panel.appendChild(buttonContainer);
        debugLog('debug', '按钮容器已添加到面板');
        debugLog('debug', '面板中的子元素数量:', panel.children.length);

        // 切换面板显示/隐藏
        toggleBtn.addEventListener('click', function (e) {
            e.stopPropagation();
            debugLog('debug', '切换按钮被点击');
            const isVisible = panel.style.display !== 'none';
            panel.style.display = isVisible ? 'none' : 'block';

            // 控制下层页面滚动
            if (!isVisible) {
                // 不再修改body样式,让页面保持正常滚动
            }

            debugLog('debug', '面板显示状态:', !isVisible);
        });

        // 点击面板外部关闭面板
        document.addEventListener('click', function (e) {
            if (panel.style.display !== 'none') {
                const isClickInsidePanel = panel.contains(e.target);
                const isClickOnToggle = toggleBtn.contains(e.target);

                if (!isClickInsidePanel && !isClickOnToggle) {
                    panel.style.display = 'none';
                }
            }
        });

        // 阻止面板内部点击事件冒泡
        panel.addEventListener('click', function (e) {
            e.stopPropagation();
        });

        // 检查面板初始状态
        console.log('面板初始显示状态:', panel.style.display);
    }

    // 创建配置面板
    createConfigPanel();

    // 监控DOM变化,处理动态添加的元素
    const observer = new MutationObserver(cleanPage);
    observer.observe(document.body, {
        childList: true,
        subtree: true,
        attributes: false,
        characterData: false
    });
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。