Greasy Fork

专注阅读 - 移动端沉浸式阅读

消除页面噪音,提供沉浸式阅读体验

// ==UserScript==
// @name         专注阅读 - 移动端沉浸式阅读
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  消除页面噪音,提供沉浸式阅读体验
// @author       FocusReader
// @match        http://*/*
// @match        https://*/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';
    
    let isReaderMode = false;
    let originalContent = null;
    let readerContainer = null;
    
    // 护眼配色方案
    const colorSchemes = {
        default: {
            name: '默认白',
            bg: '#ffffff',
            text: '#333333',
            border: '#e0e0e0'
        },
        warm: {
            name: '暖白护眼',
            bg: '#faf8f3',
            text: '#4a4a4a',
            border: '#e8e6e1'
        },
        sepia: {
            name: '复古褐',
            bg: '#f4f0e6',
            text: '#5c4b37',
            border: '#d4c8b8'
        },
        green: {
            name: '护眼绿',
            bg: '#f0f8f0',
            text: '#2d4a2d',
            border: '#c8e6c8'
        },
        dark: {
            name: '暗夜模式',
            bg: '#1a1a1a',
            text: '#e0e0e0',
            border: '#404040'
        },
        blue: {
            name: '海洋蓝',
            bg: '#f0f8ff',
            text: '#1e3a5f',
            border: '#c8d8e8'
        }
    };
    
    // 字体选择
    const fontFamilies = {
        system: '系统默认',
        songti: '"Songti SC", "SimSun", serif',
        heiti: '"Heiti SC", "SimHei", sans-serif',
        kaiti: '"Kaiti SC", "KaiTi", cursive',
        fangsong: '"FangSong SC", "FangSong", serif',
        pingfang: '"PingFang SC", "Hiragino Sans GB", sans-serif'
    };
    
    // 获取当前设置
    function getCurrentSettings() {
        return {
            colorScheme: GM_getValue('colorScheme', 'warm'),
            fontSize: GM_getValue('fontSize', '18'),
            fontFamily: GM_getValue('fontFamily', 'system'),
            lineHeight: GM_getValue('lineHeight', '1.8'),
            maxWidth: GM_getValue('maxWidth', '90%')
        };
    }
    
    // 保存设置
    function saveSettings(settings) {
        GM_setValue('colorScheme', settings.colorScheme);
        GM_setValue('fontSize', settings.fontSize);
        GM_setValue('fontFamily', settings.fontFamily);
        GM_setValue('lineHeight', settings.lineHeight);
        GM_setValue('maxWidth', settings.maxWidth);
    }
    
    // 智能提取文章内容
    function extractContent() {
        // 常见的文章容器选择器
        const contentSelectors = [
            'article',
            '[role="main"]',
            '.content',
            '.article-content',
            '.post-content',
            '.entry-content',
            '.main-content',
            '#content',
            '.article-body',
            '.post-body'
        ];
        
        let content = null;
        
        // 首先尝试通过选择器查找
        for (let selector of contentSelectors) {
            content = document.querySelector(selector);
            if (content && content.innerText.length > 200) {
                break;
            }
        }
        
        // 如果没找到合适的内容,使用启发式方法
        if (!content || content.innerText.length < 200) {
            const allElements = document.querySelectorAll('div, section, article, main');
            let maxScore = 0;
            
            for (let element of allElements) {
                let score = 0;
                const text = element.innerText || '';
                const textLength = text.length;
                
                if (textLength < 100) continue;
                
                // 文本长度得分
                score += Math.min(textLength / 100, 50);
                
                // 段落数量得分
                const paragraphs = element.querySelectorAll('p').length;
                score += paragraphs * 3;
                
                // 链接密度惩罚
                const links = element.querySelectorAll('a').length;
                const linkDensity = links / Math.max(textLength / 100, 1);
                score -= linkDensity * 10;
                
                // 类名和ID得分
                const className = element.className.toLowerCase();
                const id = element.id.toLowerCase();
                if (className.includes('content') || className.includes('article') || 
                    className.includes('post') || id.includes('content') || 
                    id.includes('article') || id.includes('post')) {
                    score += 15;
                }
                
                if (score > maxScore) {
                    maxScore = score;
                    content = element;
                }
            }
        }
        
        return content;
    }
    
    // 清理内容
    function cleanContent(element) {
        if (!element) return null;
        
        const clonedElement = element.cloneNode(true);
        
        // 移除不需要的元素 - 加强版噪音过滤
        const removeSelectors = [
            // 脚本和样式
            'script', 'style', 'noscript', 'link[rel="stylesheet"]',
            
            // 媒体元素(移除所有图片和视频)
            'img', 'picture', 'figure', 'video', 'audio', 'canvas', 'svg',
            'iframe', 'embed', 'object',
            
            // 广告和营销
            '.advertisement', '.ads', '.ad', '[class*="ad-"]', '[id*="ad-"]',
            '[class*="ads-"]', '[id*="ads-"]', '.promo', '.banner',
            '.sponsor', '.promotion', '[class*="affiliate"]',
            
            // 社交和分享
            '.social', '.share', '.social-share', '.social-links',
            '.share-buttons', '.social-media', '[class*="social"]',
            '[class*="share"]', '.follow', '.subscribe',
            
            // 导航和菜单
            'nav', '.navigation', '.nav', '.menu', '.navbar',
            '.breadcrumb', '.breadcrumbs', '.pagination',
            '.next-prev', '.prev-next',
            
            // 页眉页脚
            'header', '.header', 'footer', '.footer',
            '.site-header', '.site-footer', '.page-header',
            '.page-footer',
            
            // 侧边栏和小工具
            'aside', '.sidebar', '.side-bar', '.widget',
            '.widgets', '.secondary', '.complementary',
            
            // 评论系统
            '.comments', '.comment', '.comment-section',
            '.comment-form', '.comment-list', '.discussion',
            '.disqus', '.facebook-comments',
            
            // 相关内容和推荐
            '.related', '.related-posts', '.related-articles',
            '.recommended', '.suggestions', '.more-posts',
            '.similar', '.also-read', '.you-may-like',
            
            // 表单和输入
            'form', 'input', 'textarea', 'select', 'button[type="submit"]',
            '.form', '.search-form', '.contact-form',
            '.newsletter', '.signup', '.login',
            
            // 弹窗和模态框
            '.popup', '.modal', '.overlay', '.lightbox',
            '.dialog', '.tooltip', '.dropdown',
            
            // 标签和分类
            '.tags', '.tag', '.categories', '.category',
            '.meta', '.post-meta', '.entry-meta',
            '.author', '.author-bio', '.author-info',
            '.date', '.timestamp', '.published',
            
            // 通用垃圾内容
            '.noise', '.clutter', '.extra', '.misc',
            '[role="complementary"]', '[role="banner"]', '[role="navigation"]',
            '[role="contentinfo"]', '.skip-link', '.screen-reader-text',
            
            // 特定网站常见噪音
            '.wp-caption', '.wp-caption-text', '.caption',
            '.gallery', '.slideshow', '.slider',
            '.code-toolbar', '.toolbar', '.copy-code',
            '.highlight-toolbar'
        ];
        
        // 先移除明确的噪音元素
        removeSelectors.forEach(selector => {
            const elements = clonedElement.querySelectorAll(selector);
            elements.forEach(el => el.remove());
        });
        
        // 移除所有包含特定关键词的元素
        const noiseKeywords = [
            'advertisement', 'ads', 'promo', 'banner', 'sponsor',
            'social', 'share', 'follow', 'subscribe', 'newsletter',
            'comment', 'related', 'recommended', 'similar', 'tags',
            'category', 'author', 'meta', 'breadcrumb', 'navigation'
        ];
        
        const allElements = Array.from(clonedElement.querySelectorAll('*'));
        allElements.forEach(el => {
            const className = (el.className || '').toLowerCase();
            const id = (el.id || '').toLowerCase();
            const tagName = el.tagName.toLowerCase();
            
            // 检查类名和ID是否包含噪音关键词
            const hasNoiseKeyword = noiseKeywords.some(keyword => 
                className.includes(keyword) || id.includes(keyword)
            );
            
            if (hasNoiseKeyword) {
                el.remove();
                return;
            }
            
            // 移除空元素或只包含空白的元素
            const text = el.textContent?.trim() || '';
            if (text.length === 0 && 
                !['p', 'div', 'section', 'article', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tagName)) {
                el.remove();
                return;
            }
            
            // 移除链接密度过高的元素(可能是导航或广告)
            if (tagName === 'div' || tagName === 'section') {
                const links = el.querySelectorAll('a').length;
                const textLength = text.length;
                const linkDensity = textLength > 0 ? links / (textLength / 100) : 0;
                
                if (linkDensity > 0.5 && links > 3) {
                    el.remove();
                    return;
                }
            }
            
            // 清理所有属性,只保留基本结构
            Array.from(el.attributes).forEach(attr => {
                el.removeAttribute(attr.name);
            });
        });
        
        // 最后清理:只保留有意义的文本内容
        const finalElements = Array.from(clonedElement.querySelectorAll('*'));
        finalElements.forEach(el => {
            const text = el.textContent?.trim() || '';
            const tagName = el.tagName.toLowerCase();
            
            // 保留标题和段落,但要求有足够的文本内容
            if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tagName)) {
                if (text.length === 0 || text.length > 200) {
                    el.remove();
                }
            } else if (tagName === 'p') {
                if (text.length < 10) {
                    el.remove();
                }
            } else if (tagName === 'div' || tagName === 'section') {
                // 如果div或section没有有效的子元素,移除它
                const hasValidChildren = el.querySelector('p, h1, h2, h3, h4, h5, h6');
                if (!hasValidChildren && text.length < 50) {
                    el.remove();
                }
            }
        });
        
        return clonedElement;
    }
    
    // 创建设置面板
    function createSettingsPanel() {
        const settings = getCurrentSettings();
        const currentScheme = colorSchemes[settings.colorScheme];
        
        const panel = document.createElement('div');
        panel.innerHTML = `
            <div style="
                position: fixed;
                top: 20px;
                right: 20px;
                background: white;
                border: 2px solid #ddd;
                border-radius: 12px;
                padding: 20px;
                box-shadow: 0 8px 32px rgba(0,0,0,0.3);
                z-index: 10001;
                max-width: 280px;
                font-family: -apple-system, BlinkMacSystemFont, sans-serif;
                font-size: 14px;
                max-height: 80vh;
                overflow-y: auto;
            ">
                <div style="
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    margin-bottom: 15px;
                    font-weight: bold;
                    color: #333;
                ">
                    阅读设置
                    <button id="closeSettings" style="
                        background: none;
                        border: none;
                        font-size: 18px;
                        cursor: pointer;
                        color: #666;
                    ">×</button>
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 8px; font-weight: 500;">护眼配色</label>
                    <div id="colorSchemes" style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
                        ${Object.entries(colorSchemes).map(([key, scheme]) => `
                            <div style="
                                padding: 8px;
                                border: 2px solid ${key === settings.colorScheme ? '#007AFF' : '#ddd'};
                                border-radius: 6px;
                                cursor: pointer;
                                text-align: center;
                                font-size: 12px;
                                background: ${scheme.bg};
                                color: ${scheme.text};
                            " data-scheme="${key}">
                                ${scheme.name}
                            </div>
                        `).join('')}
                    </div>
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 8px; font-weight: 500;">字体大小</label>
                    <input type="range" id="fontSize" min="14" max="24" value="${settings.fontSize}" style="width: 100%;">
                    <div style="text-align: center; margin-top: 5px; color: #666;">${settings.fontSize}px</div>
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 8px; font-weight: 500;">字体类型</label>
                    <select id="fontFamily" style="
                        width: 100%;
                        padding: 8px;
                        border: 1px solid #ddd;
                        border-radius: 6px;
                        font-size: 14px;
                    ">
                        ${Object.entries(fontFamilies).map(([key, name]) => `
                            <option value="${key}" ${key === settings.fontFamily ? 'selected' : ''}>${name}</option>
                        `).join('')}
                    </select>
                </div>
                
                <div style="margin-bottom: 15px;">
                    <label style="display: block; margin-bottom: 8px; font-weight: 500;">行间距</label>
                    <input type="range" id="lineHeight" min="1.2" max="2.5" step="0.1" value="${settings.lineHeight}" style="width: 100%;">
                    <div style="text-align: center; margin-top: 5px; color: #666;">${settings.lineHeight}</div>
                </div>
                
                <div style="text-align: center; padding-top: 15px; border-top: 1px solid #eee;">
                    <button id="applySettings" style="
                        background: #007AFF;
                        color: white;
                        border: none;
                        padding: 10px 20px;
                        border-radius: 6px;
                        cursor: pointer;
                        font-size: 14px;
                        margin-right: 10px;
                    ">应用</button>
                    <button id="resetSettings" style="
                        background: #FF3B30;
                        color: white;
                        border: none;
                        padding: 10px 20px;
                        border-radius: 6px;
                        cursor: pointer;
                        font-size: 14px;
                    ">重置</button>
                </div>
            </div>
        `;
        
        document.body.appendChild(panel);
        
        // 事件监听
        panel.querySelector('#closeSettings').onclick = () => panel.remove();
        
        // 配色方案选择
        panel.querySelectorAll('[data-scheme]').forEach(item => {
            item.onclick = () => {
                panel.querySelectorAll('[data-scheme]').forEach(el => 
                    el.style.border = '2px solid #ddd'
                );
                item.style.border = '2px solid #007AFF';
            };
        });
        
        // 字体大小实时预览
        const fontSizeSlider = panel.querySelector('#fontSize');
        const fontSizeDisplay = fontSizeSlider.nextElementSibling;
        fontSizeSlider.oninput = () => {
            fontSizeDisplay.textContent = fontSizeSlider.value + 'px';
        };
        
        // 行间距实时预览
        const lineHeightSlider = panel.querySelector('#lineHeight');
        const lineHeightDisplay = lineHeightSlider.nextElementSibling;
        lineHeightSlider.oninput = () => {
            lineHeightDisplay.textContent = lineHeightSlider.value;
        };
        
        // 应用设置
        panel.querySelector('#applySettings').onclick = () => {
            const newSettings = {
                colorScheme: panel.querySelector('[data-scheme][style*="rgb(0, 122, 255)"]')?.dataset.scheme || settings.colorScheme,
                fontSize: fontSizeSlider.value,
                fontFamily: panel.querySelector('#fontFamily').value,
                lineHeight: lineHeightSlider.value,
                maxWidth: settings.maxWidth
            };
            
            saveSettings(newSettings);
            applyReaderStyles();
            panel.remove();
        };
        
        // 重置设置
        panel.querySelector('#resetSettings').onclick = () => {
            const defaultSettings = {
                colorScheme: 'warm',
                fontSize: '18',
                fontFamily: 'system',
                lineHeight: '1.8',
                maxWidth: '90%'
            };
            saveSettings(defaultSettings);
            applyReaderStyles();
            panel.remove();
        };
    }
    
    // 应用阅读器样式
    function applyReaderStyles() {
        if (!readerContainer) return;
        
        const settings = getCurrentSettings();
        const scheme = colorSchemes[settings.colorScheme];
        const fontFamily = settings.fontFamily === 'system' ? 
            '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' : 
            fontFamilies[settings.fontFamily];
        
        readerContainer.style.cssText = `
            position: fixed !important;
            top: 0 !important;
            left: 0 !important;
            width: 100vw !important;
            height: 100vh !important;
            background: ${scheme.bg} !important;
            color: ${scheme.text} !important;
            z-index: 10000 !important;
            overflow-x: hidden !important;
            overflow-y: auto !important;
            padding: 0 !important;
            margin: 0 !important;
            font-family: ${fontFamily} !important;
            font-size: ${settings.fontSize}px !important;
            line-height: ${settings.lineHeight} !important;
            -webkit-font-smoothing: antialiased !important;
            -moz-osx-font-smoothing: grayscale !important;
        `;
        
        const contentArea = readerContainer.querySelector('.reader-content');
        if (contentArea) {
            contentArea.style.cssText = `
                max-width: ${settings.maxWidth} !important;
                margin: 0 auto !important;
                padding: 40px 20px 60px 20px !important;
                word-wrap: break-word !important;
                hyphens: auto !important;
            `;
        }
        
        // 应用内容样式
        const allElements = readerContainer.querySelectorAll('*');
        allElements.forEach(el => {
            if (el.tagName === 'H1' || el.tagName === 'H2' || el.tagName === 'H3' || 
                el.tagName === 'H4' || el.tagName === 'H5' || el.tagName === 'H6') {
                el.style.cssText = `
                    color: ${scheme.text} !important;
                    margin: 1.5em 0 1em 0 !important;
                    font-weight: bold !important;
                    line-height: 1.4 !important;
                `;
            } else if (el.tagName === 'P') {
                el.style.cssText = `
                    color: ${scheme.text} !important;
                    margin: 1em 0 !important;
                    text-align: justify !important;
                    text-indent: 2em !important;
                `;
            } else if (el.tagName === 'BLOCKQUOTE') {
                el.style.cssText = `
                    border-left: 4px solid ${scheme.border} !important;
                    padding-left: 1em !important;
                    margin: 1em 0 !important;
                    font-style: italic !important;
                    color: ${scheme.text} !important;
                    background: transparent !important;
                `;
            }
            // 移除了图片相关的样式设置,因为图片已经被过滤掉了
        });
    }
    
    // 创建工具栏
    function createToolbar() {
        const toolbar = document.createElement('div');
        toolbar.className = 'reader-toolbar';
        toolbar.innerHTML = `
            <div class="toolbar-content" style="
                position: fixed;
                bottom: 20px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(0,0,0,0.8);
                backdrop-filter: blur(10px);
                border-radius: 25px;
                padding: 10px 20px;
                z-index: 10001;
                display: flex;
                gap: 15px;
                align-items: center;
                transition: all 0.3s ease;
            ">
                <button id="readerSettings" style="
                    background: none;
                    border: none;
                    color: white;
                    font-size: 18px;
                    cursor: pointer;
                    padding: 8px;
                    border-radius: 50%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                " title="设置">⚙️</button>
                
                <button id="readerScrollTop" style="
                    background: none;
                    border: none;
                    color: white;
                    font-size: 18px;
                    cursor: pointer;
                    padding: 8px;
                    border-radius: 50%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                " title="回到顶部">⬆️</button>
                
                <button id="exitReader" style="
                    background: #FF3B30;
                    border: none;
                    color: white;
                    font-size: 14px;
                    cursor: pointer;
                    padding: 8px 15px;
                    border-radius: 15px;
                " title="退出阅读">退出</button>
            </div>
        `;
        
        readerContainer.appendChild(toolbar);
        
        // 获取工具栏内容区域
        const toolbarContent = toolbar.querySelector('.toolbar-content');
        
        // 工具栏事件
        toolbar.querySelector('#readerSettings').onclick = createSettingsPanel;
        toolbar.querySelector('#readerScrollTop').onclick = () => {
            readerContainer.scrollTo({ top: 0, behavior: 'smooth' });
        };
        toolbar.querySelector('#exitReader').onclick = exitReaderMode;
        
        // --- Corrected Toolbar Scroll Logic ---
        let scrollTimeout;
        let lastScrollTop = readerContainer.scrollTop;
        
        function showToolbar() {
            toolbarContent.style.opacity = '1';
            toolbarContent.style.transform = 'translateX(-50%) translateY(0)';
        }
        
        function hideToolbar() {
            toolbarContent.style.opacity = '0';
            toolbarContent.style.transform = 'translateX(-50%) translateY(20px)';
        }

        readerContainer.addEventListener('scroll', () => {
            const currentScrollTop = readerContainer.scrollTop;

            // Clear any existing timeout
            clearTimeout(scrollTimeout);

            if (currentScrollTop > lastScrollTop) { // Scrolling down
                // Temporarily hide immediately when scrolling down
                hideToolbar();
                // If scrolling stops while going down, keep it hidden
                scrollTimeout = setTimeout(() => {
                    // Do nothing, keep hidden if still scrolling down or stopped after scrolling down
                }, 300); // Small delay to check if still scrolling
            } else if (currentScrollTop < lastScrollTop) { // Scrolling up
                // Show immediately when scrolling up
                showToolbar();
                // If scrolling stops after scrolling up, keep it visible
                scrollTimeout = setTimeout(() => {
                    showToolbar();
                }, 300);
            } else { // Scroll stopped (no change in scrollTop)
                if (currentScrollTop === 0) { // If at the very top, always show
                    showToolbar();
                } else if (toolbarContent.style.opacity === '0') {
                    // If previously hidden (scrolled down), keep hidden when stopped
                    // This is the core fix for "down-scroll-then-stop-and-hide"
                } else {
                    // If previously visible (scrolled up or at top), keep visible when stopped
                    showToolbar();
                }
            }
            
            lastScrollTop = currentScrollTop;
        });

        // Ensure toolbar is visible when entering reader mode initially
        showToolbar();
    }
    
    // 进入阅读模式
    function enterReaderMode() {
        if (isReaderMode) return;
        
        // 保存原始内容
        originalContent = {
            html: document.documentElement.innerHTML,
            title: document.title
        };
        
        // 提取文章内容
        const content = extractContent();
        if (!content) {
            alert('未能识别到文章内容,请手动选择文本后重试');
            return;
        }
        
        const cleanedContent = cleanContent(content);
        if (!cleanedContent) {
            alert('内容处理失败,请重试');
            return;
        }
        
        // 创建阅读器容器
        readerContainer = document.createElement('div');
        readerContainer.className = 'focus-reader-container';
        
        // 获取标题
        let title = document.title;
        const h1 = document.querySelector('h1');
        if (h1 && h1.innerText.length < 100) {
            title = h1.innerText;
        }
        
        // 检查cleanedContent中是否已经有标题
        const existingH1 = cleanedContent.querySelector('h1');
        let contentHTML = cleanedContent.innerHTML;
        
        // 如果内容中已有h1标题,就不额外添加标题
        if (existingH1) {
            readerContainer.innerHTML = `
                <div class="reader-content">
                    <div class="article-body">
                        ${contentHTML}
                    </div>
                </div>
            `;
        } else {
            // 如果内容中没有h1标题,才添加页面标题
            readerContainer.innerHTML = `
                <div class="reader-content">
                    <h1 style="
                        font-size: 1.5em !important;
                        margin-bottom: 1em !important;
                        text-align: center !important;
                        font-weight: bold !important;
                        text-indent: 0 !important;
                    ">${title}</h1>
                    <div class="article-body">
                        ${contentHTML}
                    </div>
                </div>
            `;
        }
        
        document.body.appendChild(readerContainer);
        
        // 隐藏原始内容
        document.body.style.overflow = 'hidden';
        Array.from(document.body.children).forEach(child => {
            if (child !== readerContainer) {
                child.style.display = 'none';
            }
        });
        
        // 应用样式和创建工具栏
        applyReaderStyles();
        createToolbar();
        
        isReaderMode = true;
        
        // 阻止页面滚动
        document.addEventListener('touchmove', preventDefaultTouch, { passive: false });
    }
    
    // 阻止默认触摸事件(除了阅读器内部)
    function preventDefaultTouch(e) {
        if (!readerContainer.contains(e.target)) {
            e.preventDefault();
        }
    }
    
    // 退出阅读模式
    function exitReaderMode() {
        if (!isReaderMode) return;
        
        // 移除阅读器容器
        if (readerContainer) {
            readerContainer.remove();
            readerContainer = null;
        }
        
        // 恢复原始内容显示
        document.body.style.overflow = '';
        Array.from(document.body.children).forEach(child => {
            child.style.display = '';
        });
        
        // 移除事件监听
        document.removeEventListener('touchmove', preventDefaultTouch);
        
        isReaderMode = false;
    }
    
    // 注册油猴菜单
    GM_registerMenuCommand('🔍 进入专注阅读', enterReaderMode);
    GM_registerMenuCommand('⚙️ 阅读设置', createSettingsPanel);
    
    // 快捷键支持(可选)
    document.addEventListener('keydown', function(e) {
        // Ctrl/Cmd + Shift + R 进入/退出阅读模式
        if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'R') {
            e.preventDefault();
            if (isReaderMode) {
                exitReaderMode();
            } else {
                enterReaderMode();
            }
        }
    });
    
    // 移动端优化:添加触摸手势支持
    let touchStartY = 0;
    let touchStartTime = 0;
    
    document.addEventListener('touchstart', function(e) {
        if (e.touches.length === 2) {
            touchStartY = e.touches[0].clientY + e.touches[1].clientY;
            touchStartTime = Date.now();
        }
    });
    
    document.addEventListener('touchend', function(e) {
        if (e.changedTouches.length === 2 && touchStartTime > 0) {
            const touchEndTime = Date.now();
            if (touchEndTime - touchStartTime < 500) { // 500ms内的双指触摸
                if (isReaderMode) {
                    exitReaderMode();
                } else {
                    enterReaderMode();
                }
            }
        }
        touchStartTime = 0;
    });
    
    console.log('专注阅读脚本已加载 - 使用油猴菜单或双指轻触进入阅读模式');
    
})();