Greasy Fork

Greasy Fork is available in English.

AI网页内容智能总结助手

网页内容智能总结,支持自定义API和提示词

当前为 2025-03-20 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         AI Page Summarizer Pro
// @name:zh-CN   AI网页内容智能总结助手
// @namespace    http://tampermonkey.net/
// @version      0.9.9.1
// @description  网页内容智能总结,支持自定义API和提示词
// @description:zh-CN  网页内容智能总结,支持自定义API和提示词
// @author       Your Name
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @grant        GM.xmlHttpRequest
// @grant        GM.setValue
// @grant        GM.getValue
// @grant        GM.registerMenuCommand
// @grant        GM.addStyle
// @grant        window.fetch
// @grant        window.localStorage
// @connect      api.openai.com
// @connect      *
// @require      https://cdn.jsdelivr.net/npm/[email protected]/marked.min.js
// @run-at       document-start
// @noframes
// @license      MIT
// @compatible   chrome
// @compatible   firefox
// @compatible   edge
// @compatible   opera
// @compatible   safari
// @compatible   android
// ==/UserScript==

(function() {
    'use strict';

    // 添加全局错误处理
    window.addEventListener('error', function(event) {
        console.error('脚本错误:', event.error);
        if (event.error && event.error.stack) {
            console.error('错误堆栈:', event.error.stack);
        }
    });

    window.addEventListener('unhandledrejection', function(event) {
        console.error('未处理的Promise错误:', event.reason);
    });

    // 兼容性检查
    const browserSupport = {
        hasGM: typeof GM !== 'undefined',
        hasGMFunctions: typeof GM_getValue !== 'undefined',
        hasLocalStorage: (function() {
            try {
                localStorage.setItem('test', 'test');
                localStorage.removeItem('test');
                return true;
            } catch (e) {
                return false;
            }
        })(),
        hasBackdropFilter: (function() {
            const el = document.createElement('div');
            return typeof el.style.backdropFilter !== 'undefined' || 
                   typeof el.style.webkitBackdropFilter !== 'undefined';
        })(),
        isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),
        isSafari: /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
    };

    // 兼容性处理层
    const scriptHandler = {
        // 存储值
        setValue: async function(key, value) {
            try {
                if (browserSupport.hasGMFunctions) {
                    GM_setValue(key, value);
                    return true;
                } else if (browserSupport.hasGM && GM.setValue) {
                    await GM.setValue(key, value);
                    return true;
                } else if (browserSupport.hasLocalStorage) {
                    localStorage.setItem('ws_' + key, JSON.stringify(value));
                    return true;
                }
                return false;
            } catch (error) {
                console.error('存储值失败:', error);
                return false;
            }
        },
        
        // 获取值
        getValue: async function(key, defaultValue) {
            try {
                if (browserSupport.hasGMFunctions) {
                    return GM_getValue(key, defaultValue);
                } else if (browserSupport.hasGM && GM.getValue) {
                    return await GM.getValue(key, defaultValue);
                } else if (browserSupport.hasLocalStorage) {
                    const value = localStorage.getItem('ws_' + key);
                    return value ? JSON.parse(value) : defaultValue;
                }
                return defaultValue;
            } catch (error) {
                console.error('获取值失败:', error);
                return defaultValue;
            }
        },
        
        // HTTP请求
        xmlHttpRequest: function(details) {
            return new Promise((resolve, reject) => {
                const handleResponse = (response) => {
                    resolve(response);
                };

                const handleError = (error) => {
                    reject(new Error('请求错误: ' + error.message));
                };

                if (browserSupport.hasGMFunctions && typeof GM_xmlhttpRequest !== 'undefined') {
                    GM_xmlhttpRequest({
                        ...details,
                        onload: handleResponse,
                        onerror: handleError,
                        ontimeout: details.ontimeout
                    });
                } else if (browserSupport.hasGM && typeof GM !== 'undefined' && GM.xmlHttpRequest) {
                    GM.xmlHttpRequest({
                        ...details,
                        onload: handleResponse,
                        onerror: handleError,
                        ontimeout: details.ontimeout
                    });
                } else {
                    fetch(details.url, {
                        method: details.method,
                        headers: details.headers,
                        body: details.data,
                        mode: 'cors',
                        credentials: 'omit'
                    })
                    .then(async response => {
                        const text = await response.text();
                        handleResponse({
                            status: response.status,
                            responseText: text,
                            responseHeaders: [...response.headers].join('\n')
                        });
                    })
                    .catch(handleError);
                }
            }).then(response => {
                if (details.onload) {
                    details.onload(response);
                }
                return response;
            }).catch(error => {
                if (details.onerror) {
                    details.onerror(error);
                }
                throw error;
            });
        },
        
        // 注册菜单命令
        registerMenuCommand: function(name, fn) {
            try {
                if (browserSupport.hasGMFunctions) {
                    GM_registerMenuCommand(name, fn);
                    return true;
                } else if (browserSupport.hasGM && GM.registerMenuCommand) {
                    GM.registerMenuCommand(name, fn);
                    return true;
                }
                return false;
            } catch (error) {
                console.log('注册菜单命令失败:', error);
                return false;
            }
        },
        
        // 添加样式
        addStyle: function(css) {
            try {
                if (browserSupport.hasGMFunctions) {
                    GM_addStyle(css);
                    return true;
                } else if (browserSupport.hasGM && GM.addStyle) {
                    GM.addStyle(css);
                    return true;
                } else {
                    const style = document.createElement('style');
                    style.textContent = css;
                    document.head.appendChild(style);
                    return true;
                }
            } catch (error) {
                console.error('添加样式失败:', error);
                return false;
            }
        }
    };

    // 配置项
    let config = {
        apiUrl: 'https://api.openai.com/v1/chat/completions',
        apiKey: '',
        model: 'gpt-3.5-turbo',
        theme: 'light',
        prompt: `You are a professional content summarizer in chinese. Your task is to create a clear, concise, and well-structured summary of the webpage content. Follow these guidelines:

1. Output Format:
   - Use ## for main sections
   - Use bullet points (•) for key points and details
   - Use bold for important terms
   - Use blockquotes for notable quotes

2. Content Structure:
   ## 核心观点
   • Key points here...

   ## 关键信息
   • Important details here...

   ## 市场情绪
   • Market sentiment here...

   ## 专家观点
   • Expert opinions here...

   ## 总结
   • Final summary here...

3. Writing Style:
   - Clear and concise language
   - Professional tone
   - Logical flow
   - Easy to understand
   - Focus on essential information

4. Important Rules:
   - DO NOT show your reasoning process
   - DO NOT include meta-commentary
   - DO NOT explain your methodology
   - DO NOT use phrases like "this summary shows" or "the content indicates"
   - Start directly with the content summary
   - Make sure bullet points (•) are in the same line with text
   - Use ## for main section headers

Remember: Focus on delivering the information directly without any meta-analysis or explanation of your process.`,
        iconPosition: { y: 20 },
        shortcut: 'option+a'
    };

    // 初始化配置
    async function initConfig() {
        config.apiUrl = await scriptHandler.getValue('apiUrl', config.apiUrl);
        config.apiKey = await scriptHandler.getValue('apiKey', config.apiKey);
        config.model = await scriptHandler.getValue('model', config.model);
        config.prompt = await scriptHandler.getValue('prompt', config.prompt);
        config.iconPosition = await scriptHandler.getValue('iconPosition', config.iconPosition);
        config.shortcut = await scriptHandler.getValue('shortcut', config.shortcut);
        config.theme = await scriptHandler.getValue('theme', config.theme);
    }

    // DOM 元素引用
    const elements = {
        icon: null,
        container: null,
        settings: null,
        backdrop: null
    };

    // 全局变量用于判断是否已经监听了键盘事件
    let keyboardListenerActive = false;

    // 拖拽功能
    function makeDraggable(element) {
        const header = element.querySelector('div') || element;
        let startX = 0, startY = 0;
        let elementX = 0, elementY = 0;
        let dragging = false;
        let lastTouchTime = 0;
        
        // 处理触摸事件
        function handleTouchStart(e) {
            const touch = e.touches[0];
            const currentTime = new Date().getTime();
            const tapLength = currentTime - lastTouchTime;
            
            // 检测双击
            if (tapLength < 500 && tapLength > 0) {
                e.preventDefault();
                return;
            }
            lastTouchTime = currentTime;
            
            startDrag(touch);
        }
        
        function handleTouchMove(e) {
            if (!dragging) return;
            e.preventDefault();
            const touch = e.touches[0];
            move(touch);
        }
        
        function handleTouchEnd() {
            stopDrag();
        }
        
        // 处理鼠标事件
        function handleMouseDown(e) {
            if (e.button !== 0) return; // 只响应左键
            e.preventDefault();
            startDrag(e);
        }
        
        function handleMouseMove(e) {
            if (!dragging) return;
            e.preventDefault();
            move(e);
        }
        
        function handleMouseUp() {
            stopDrag();
        }
        
        function startDrag(e) {
            dragging = true;
            
            // 记录起始位置
            startX = e.clientX;
            startY = e.clientY;
            
            const rect = element.getBoundingClientRect();
            elementX = rect.left;
            elementY = rect.top;
            
            // 设置样式
            if (element.id === 'website-summary-icon') {
                element.style.transition = 'none';
                element.style.opacity = '0.9';
            }
            
            // 添加事件监听
            if (browserSupport.isMobile) {
                document.addEventListener('touchmove', handleTouchMove, { passive: false });
                document.addEventListener('touchend', handleTouchEnd);
                document.addEventListener('touchcancel', handleTouchEnd);
            } else {
                document.addEventListener('mousemove', handleMouseMove);
                document.addEventListener('mouseup', handleMouseUp);
            }
        }
        
        function move(e) {
            // 计算新位置
            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;
            
            if (element.id === 'website-summary-icon') {
                // 仅垂直移动图标
                const newY = elementY + deltaY;
                const maxY = window.innerHeight - element.offsetHeight - 10;
                element.style.top = Math.max(10, Math.min(newY, maxY)) + 'px';
            } else {
                // 自由移动其他元素
                const maxX = window.innerWidth - element.offsetWidth;
                const maxY = window.innerHeight - element.offsetHeight;
                
                element.style.left = Math.max(0, Math.min(elementX + deltaX, maxX)) + 'px';
                element.style.top = Math.max(0, Math.min(elementY + deltaY, maxY)) + 'px';
                element.style.transform = 'none';
            }
        }
        
        function stopDrag() {
            if (!dragging) return;
            dragging = false;
            
            // 恢复样式
            if (element.id === 'website-summary-icon') {
                element.style.transition = 'transform 0.2s ease, box-shadow 0.2s ease';
                element.style.opacity = '1';
                
                // 保存图标位置
                config.iconPosition = { y: element.offsetTop };
                scriptHandler.setValue('iconPosition', config.iconPosition);
            }
            
            // 移除事件监听
            if (browserSupport.isMobile) {
                document.removeEventListener('touchmove', handleTouchMove);
                document.removeEventListener('touchend', handleTouchEnd);
                document.removeEventListener('touchcancel', handleTouchEnd);
            } else {
                document.removeEventListener('mousemove', handleMouseMove);
                document.removeEventListener('mouseup', handleMouseUp);
            }
        }
        
        // 添加事件监听
        if (browserSupport.isMobile) {
            header.addEventListener('touchstart', handleTouchStart, { passive: false });
        } else {
            header.addEventListener('mousedown', handleMouseDown);
        }
        
        // 防止iOS Safari的滚动橡皮筋效果
        if (browserSupport.isMobile) {
            document.body.addEventListener('touchmove', function(e) {
                if (dragging) {
                    e.preventDefault();
                }
            }, { passive: false });
        }
    }

    // 显示提示消息
    function showToast(message) {
        const toast = document.createElement('div');
        toast.textContent = message;
        
        const baseStyle = `
            position: fixed;
            left: 50%;
            transform: translateX(-50%);
            background: #4CAF50;
            color: white;
            padding: ${browserSupport.isMobile ? '12px 24px' : '10px 20px'};
            border-radius: 4px;
            z-index: 1000001;
            font-size: ${browserSupport.isMobile ? '16px' : '14px'};
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            text-align: center;
            max-width: ${browserSupport.isMobile ? '90%' : '300px'};
            word-break: break-word;
        `;
        
        // 在移动设备上显示在底部,否则显示在顶部
        const position = browserSupport.isMobile ? 
            'bottom: 80px;' : 
            'top: 20px;';
        
        toast.style.cssText = baseStyle + position;
        
        document.body.appendChild(toast);
        setTimeout(() => {
            toast.style.opacity = '0';
            toast.style.transition = 'opacity 0.3s ease';
            setTimeout(() => toast.remove(), 300);
        }, 2000);
    }

    // 快捷键处理
    const keyManager = {
        setup() {
            try {
                // 移除旧的监听器
                if (keyboardListenerActive) {
                    document.removeEventListener('keydown', this._handleKeyDown);
                }

                // 添加新的监听器
                this._handleKeyDown = (e) => {
                    // 忽略输入框中的按键
                    if (e.target.tagName === 'INPUT' || 
                        e.target.tagName === 'TEXTAREA' || 
                        e.target.isContentEditable ||
                        e.target.getAttribute('role') === 'textbox') {
                        return;
                    }

                    // 解析配置的快捷键
                    const shortcutParts = config.shortcut.toLowerCase().split('+');
                    
                    // 获取主键(非修饰键)
                    const mainKey = shortcutParts.filter(part => 
                        !['alt', 'option', 'ctrl', 'control', 'shift', 'cmd', 'command', 'meta']
                        .includes(part)
                    )[0] || 'a';
                    
                    // 检查所需的修饰键
                    const needAlt = shortcutParts.some(p => p === 'alt' || p === 'option');
                    const needCtrl = shortcutParts.some(p => p === 'ctrl' || p === 'control');
                    const needShift = shortcutParts.some(p => p === 'shift');
                    const needMeta = shortcutParts.some(p => p === 'cmd' || p === 'command' || p === 'meta');
                    
                    // 检查按键是否匹配
                    const isMainKeyMatched = 
                        e.key.toLowerCase() === mainKey || 
                        e.code.toLowerCase() === 'key' + mainKey ||
                        e.keyCode === mainKey.toUpperCase().charCodeAt(0);
                        
                    // 检查修饰键是否匹配
                    const modifiersMatch = 
                        e.altKey === needAlt && 
                        e.ctrlKey === needCtrl && 
                        e.shiftKey === needShift && 
                        e.metaKey === needMeta;
                    
                    if (isMainKeyMatched && modifiersMatch) {
                        console.log('快捷键触发成功:', config.shortcut);
                        e.preventDefault();
                        e.stopPropagation();
                        showSummary();
                        return false;
                    }
                };
                
                // 使用捕获阶段来确保我们能先捕获到事件
                document.addEventListener('keydown', this._handleKeyDown, true);
                keyboardListenerActive = true;
                
                // 设置全局访问方法
                window.activateSummary = showSummary;
                
                console.log('快捷键已设置:', config.shortcut);
                return true;
            } catch (error) {
                console.error('设置快捷键失败:', error);
                return false;
            }
        }
    };

    // 等待页面加载完成
    function waitForPageLoad() {
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initializeScript);
        } else {
            initializeScript();
        }
    }

    // 初始化脚本
    async function initializeScript() {
        try {
            // 等待marked库加载
            await waitForMarked();
            // 初始化配置
            await initConfig();
            // 添加全局样式
            addGlobalStyles();
            // 创建图标
            createIcon();
            // 设置快捷键
            keyManager.setup();
            // 注册菜单命令
            registerMenuCommands();
            
            console.log('AI Page Summarizer Pro 初始化完成');
        } catch (error) {
            console.error('初始化失败:', error);
        }
    }

    // 等待marked库加载
    function waitForMarked() {
        return new Promise((resolve) => {
            if (window.marked) {
                window.marked.setOptions({ breaks: true, gfm: true });
                resolve();
            } else {
                const checkMarked = setInterval(() => {
                    if (window.marked) {
                        clearInterval(checkMarked);
                        window.marked.setOptions({ breaks: true, gfm: true });
                        resolve();
                    }
                }, 100);
                // 10秒后超时
                setTimeout(() => {
                    clearInterval(checkMarked);
                    console.warn('marked库加载超时,继续初始化');
                    resolve();
                }, 10000);
            }
        });
    }

    // 添加全局样式
    function addGlobalStyles() {
        const css = `
            #website-summary-icon * {
                box-sizing: border-box !important;
                margin: 0 !important;
                padding: 0 !important;
            }
            #website-summary-icon span {
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;
                line-height: 1 !important;
            }
        `;
        scriptHandler.addStyle(css);
    }

    // 创建图标
    function createIcon() {
        // 检查是否已存在图标
        let icon = document.getElementById('website-summary-icon');
        if (icon) {
            icon.remove();
        }

        icon = document.createElement('div');
        icon.id = 'website-summary-icon';
        
        // 使用星星emoji
        icon.innerHTML = `<span style="font-size: ${browserSupport.isMobile ? '28px' : '24px'} !important;">🌟</span>`;
        
        // 基础样式
        let styles = `
            position: fixed !important;
            z-index: 2147483647 !important;
            width: ${browserSupport.isMobile ? '48px' : '40px'} !important;
            height: ${browserSupport.isMobile ? '48px' : '40px'} !important;
            border-radius: ${browserSupport.isMobile ? '24px' : '20px'} !important;
            background: rgba(255, 255, 255, ${browserSupport.isSafari ? '0.95' : '0.9'}) !important;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            cursor: pointer !important;
            transition: transform 0.2s ease, box-shadow 0.2s ease !important;
            right: ${browserSupport.isMobile ? '16px' : '20px'} !important;
            top: ${config.iconPosition.y || (browserSupport.isMobile ? '80px' : '20px')}px !important;
            touch-action: none !important;
            will-change: transform !important;
            user-select: none !important;
            -webkit-user-select: none !important;
            -webkit-tap-highlight-color: transparent !important;
            pointer-events: auto !important;
            visibility: visible !important;
            opacity: 1 !important;
        `;

        // 添加backdrop-filter(如果支持)
        if (browserSupport.hasBackdropFilter) {
            styles += `
                backdrop-filter: blur(5px) !important;
                -webkit-backdrop-filter: blur(5px) !important;
            `;
        }

        icon.style.cssText = styles;

        // 添加事件监听器
        icon.addEventListener('mouseover', () => {
            icon.style.transform = 'scale(1.1)';
            icon.style.boxShadow = '0 4px 12px rgba(255, 215, 0, 0.3)';
        });

        icon.addEventListener('mouseout', () => {
            icon.style.transform = 'scale(1)';
            icon.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)';
        });

        icon.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            showSummary();
        });

        icon.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            e.stopPropagation();
            showSettings();
        });

        // 确保body存在
        if (!document.body) {
            document.addEventListener('DOMContentLoaded', () => {
                document.body.appendChild(icon);
                makeDraggable(icon);
            });
        } else {
            document.body.appendChild(icon);
            makeDraggable(icon);
        }

        elements.icon = icon;
        return icon;
    }

    // 显示设置界面
    function showSettings() {
        const settings = elements.settings || createSettingsUI();
        settings.style.display = 'block';
    }

    // 显示摘要
    async function showSummary() {
        const container = elements.container || createSummaryUI();
        const content = container.querySelector('#website-summary-content');
        
        // 显示容器和背景
        showBackdrop();
        container.style.display = 'block';
        setTimeout(() => container.style.opacity = '1', 10);
        
        // 显示加载中
        content.innerHTML = `<p style="text-align: center; color: ${config.theme === 'dark' ? '#bdc1c6' : '#666'};">正在获取总结...</p>`;
        
        try {
            // 获取页面内容
            const pageContent = getPageContent();
            if (!pageContent || pageContent.trim().length === 0) {
                throw new Error('无法获取页面内容');
            }
            
            console.log('页面内容长度:', pageContent.length);
            console.log('API配置:', {
                url: config.apiUrl,
                model: config.model,
                contentLength: pageContent.length
            });
            
            // 获取总结
            const summary = await getSummary(pageContent);
            if (!summary || summary.trim().length === 0) {
                throw new Error('API返回内容为空');
            }
            
            // 添加样式并渲染内容
            addMarkdownStyles();
            await renderContent(summary);
        } catch (error) {
            console.error('总结失败:', error);
            content.innerHTML = `
                <p style="text-align: center; color: #ff4444;">
                    获取总结失败:${error.message}<br>
                    <small style="color: ${config.theme === 'dark' ? '#bdc1c6' : '#666'};">
                        请检查控制台以获取详细错误信息
                    </small>
                </p>`;
        }
    }

    // 创建/显示背景
    function showBackdrop() {
        if (!elements.backdrop) {
            const backdrop = document.createElement('div');
            backdrop.id = 'website-summary-backdrop';
            const isDark = config.theme === 'dark';
            backdrop.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: ${isDark ? 'rgba(32, 33, 36, 0.75)' : 'rgba(250, 250, 252, 0.75)'};
                backdrop-filter: blur(5px);
                z-index: 999997;
                display: none;
                opacity: 0;
                transition: opacity 0.3s ease;
            `;
            
            backdrop.addEventListener('click', (e) => {
                if (e.target === backdrop) {
                    hideUI();
                }
            });
            
            document.body.appendChild(backdrop);
            elements.backdrop = backdrop;
        } else {
            // 更新背景颜色以匹配当前主题
            const isDark = config.theme === 'dark';
            elements.backdrop.style.backgroundColor = isDark ? 'rgba(32, 33, 36, 0.75)' : 'rgba(250, 250, 252, 0.75)';
        }
        
        elements.backdrop.style.display = 'block';
        setTimeout(() => elements.backdrop.style.opacity = '1', 10);
    }

    // 隐藏UI
    function hideUI() {
        // 隐藏背景
        if (elements.backdrop) {
            elements.backdrop.style.opacity = '0';
            setTimeout(() => elements.backdrop.style.display = 'none', 300);
        }
        
        // 隐藏摘要容器
        if (elements.container) {
            elements.container.style.opacity = '0';
            setTimeout(() => elements.container.style.display = 'none', 300);
        }
    }

    // 创建摘要UI
    function createSummaryUI() {
        const container = document.createElement('div');
        container.id = 'website-summary-container';
        
        const isDark = config.theme === 'dark';
        let styles = `
            position: fixed;
            z-index: 999998;
            background: ${isDark ? darkColors.containerBg : 'rgba(255, 255, 255, 0.98)'};
            color: ${isDark ? darkColors.text : '#333'};
            border-radius: ${browserSupport.isMobile ? '8px' : '12px'};
            box-shadow: 0 8px 32px ${isDark ? 'rgba(0, 0, 0, 0.4)' : 'rgba(0, 0, 0, 0.08)'};
            padding: ${browserSupport.isMobile ? '12px' : '16px'};
            width: ${browserSupport.isMobile ? '92%' : '80%'};
            max-width: ${browserSupport.isMobile ? '100%' : '800px'};
            max-height: ${browserSupport.isMobile ? '85vh' : '80vh'};
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            display: none;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            overflow: hidden;
            opacity: 0;
            transition: opacity 0.3s ease;
        `;

        // 添加backdrop-filter(如果支持)
        if (browserSupport.hasBackdropFilter) {
            styles += 'backdrop-filter: blur(10px);';
            styles += '-webkit-backdrop-filter: blur(10px);';
        }

        container.style.cssText = styles;

        // 标题栏
        const header = document.createElement('div');
        header.style.cssText = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
            cursor: move;
            padding-bottom: 8px;
            border-bottom: 1px solid ${isDark ? '#555' : '#eee'};
        `;

        // 标题
        const title = document.createElement('h3');
        title.textContent = '网页总结';
        title.style.cssText = `margin: 0; font-size: 18px; color: ${isDark ? '#e8eaed' : '#333'};`;

        // 按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.cssText = 'display: flex; gap: 8px; align-items: center;';

        // 复制按钮
        const copyBtn = document.createElement('button');
        copyBtn.textContent = '复制';
        copyBtn.style.cssText = `
            background: ${isDark ? '#8ab4f8' : '#4CAF50'};
            color: ${isDark ? '#202124' : 'white'};
            border: none;
            padding: 6px 12px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        `;

        copyBtn.addEventListener('mouseover', () => {
            copyBtn.style.backgroundColor = isDark ? '#aecbfa' : '#45a049';
        });
        copyBtn.addEventListener('mouseout', () => {
            copyBtn.style.backgroundColor = isDark ? '#8ab4f8' : '#4CAF50';
        });

        copyBtn.addEventListener('click', () => {
            const content = document.getElementById('website-summary-content').innerText;
            navigator.clipboard.writeText(content).then(() => {
                copyBtn.textContent = '已复制';
                setTimeout(() => copyBtn.textContent = '复制', 2000);
            });
        });

        // 关闭按钮
        const closeBtn = document.createElement('button');
        closeBtn.textContent = '×';
        closeBtn.style.cssText = `
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            padding: 0 8px;
            color: ${isDark ? '#e8eaed' : '#666'};
            transition: color 0.2s;
        `;

        closeBtn.addEventListener('mouseover', () => closeBtn.style.color = '#ff4444');
        closeBtn.addEventListener('mouseout', () => closeBtn.style.color = isDark ? '#e8eaed' : '#666');
        closeBtn.addEventListener('click', hideUI);

        // 内容区域
        const content = document.createElement('div');
        content.id = 'website-summary-content';
        content.style.cssText = `
            max-height: calc(80vh - 60px);
            overflow-y: auto;
            font-size: 14px;
            line-height: 1.6;
            padding: 8px 0;
            color: ${isDark ? '#e8eaed' : '#333'};
        `;

        // 组装界面
        buttonContainer.appendChild(copyBtn);
        buttonContainer.appendChild(closeBtn);
        header.appendChild(title);
        header.appendChild(buttonContainer);
        container.appendChild(header);
        container.appendChild(content);
        document.body.appendChild(container);
        
        makeDraggable(container);
        elements.container = container;
        return container;
    }

    // 创建设置界面
    function createSettingsUI() {
        const settingsContainer = document.createElement('div');
        settingsContainer.id = 'website-summary-settings';
        
        // 基础样式
        const isDark = config.theme === 'dark';
        settingsContainer.style.cssText = `
            position: fixed;
            z-index: 1000000;
            background: ${isDark ? 'rgba(32, 33, 36, 0.98)' : 'rgba(255, 255, 255, 0.98)'};
            color: ${isDark ? '#e8eaed' : '#333'};
            border-radius: 12px;
            box-shadow: 0 4px 20px ${isDark ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.15)'};
            padding: 20px;
            width: 400px;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            display: none;
            backdrop-filter: blur(10px);
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
        `;

        // 标题栏
        const header = document.createElement('div');
        header.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; cursor: move;';

        const title = document.createElement('h3');
        title.textContent = '设置';
        title.style.cssText = `margin: 0; color: ${isDark ? '#e8eaed' : '#333'};`;

        const closeBtn = document.createElement('button');
        closeBtn.textContent = '×';
        closeBtn.style.cssText = `
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            padding: 0 8px;
            color: ${isDark ? '#e8eaed' : '#666'};
        `;
        closeBtn.addEventListener('click', () => settingsContainer.style.display = 'none');

        // 表单
        const form = document.createElement('form');
        form.style.cssText = 'display: flex; flex-direction: column; gap: 16px;';
        
        // 创建输入字段函数
        function createField(id, label, value, type = 'text', placeholder = '') {
            const container = document.createElement('div');
            container.style.cssText = 'display: flex; flex-direction: column; gap: 4px;';
            
            const labelElem = document.createElement('label');
            labelElem.textContent = label;
            labelElem.style.cssText = `font-size: 14px; color: ${isDark ? '#e8eaed' : '#333'}; font-weight: 500;`;
            
            const input = document.createElement(type === 'textarea' ? 'textarea' : 'input');
            if (type !== 'textarea') input.type = type;
            input.id = id;
            input.value = value;
            input.placeholder = placeholder;
            input.autocomplete = 'off';
            input.setAttribute('data-form-type', 'other');
            
            const baseStyle = `
                width: 100%;
                padding: 8px;
                border: 1px solid ${isDark ? '#555' : '#ddd'};
                border-radius: 6px;
                font-family: inherit;
                background: ${isDark ? '#202124' : '#fff'};
                color: ${isDark ? '#e8eaed' : '#333'};
            `;
            input.style.cssText = type === 'textarea' ? baseStyle + 'height: 100px; resize: vertical;' : baseStyle;
            
            container.appendChild(labelElem);
            container.appendChild(input);
            return { container, input };
        }

        // 创建主题切换
        function createThemeSwitch() {
            const container = document.createElement('div');
            container.style.cssText = 'display: flex; align-items: center; gap: 12px; margin-bottom: 16px;';
            
            const label = document.createElement('label');
            label.textContent = '主题模式:';
            label.style.cssText = `font-size: 14px; color: ${isDark ? '#e8eaed' : '#333'}; font-weight: 500;`;
            
            const themeSwitch = document.createElement('div');
            themeSwitch.style.cssText = 'display: flex; gap: 8px;';
            
            const createThemeButton = (themeName, text) => {
                const btn = document.createElement('button');
                btn.textContent = text;
                btn.type = 'button';
                const isActive = config.theme === themeName;
                btn.style.cssText = `
                    padding: 6px 12px;
                    border-radius: 4px;
                    border: 1px solid ${isDark ? '#555' : '#ddd'};
                    background: ${isActive ? (isDark ? '#555' : '#007AFF') : 'transparent'};
                    color: ${isActive ? '#fff' : (isDark ? '#e8eaed' : '#333')};
                    cursor: pointer;
                    transition: all 0.2s;
                `;
                btn.addEventListener('click', async () => {
                    config.theme = themeName;
                    await scriptHandler.setValue('theme', themeName);
                    // 重新创建设置界面而不是移除
                    const oldSettings = elements.settings;
                    elements.settings = null;
                    showSettings();
                    if (oldSettings) {
                        oldSettings.remove();
                    }
                });
                return btn;
            };
            
            const lightBtn = createThemeButton('light', '浅色');
            const darkBtn = createThemeButton('dark', '深色');
            
            themeSwitch.appendChild(lightBtn);
            themeSwitch.appendChild(darkBtn);
            container.appendChild(label);
            container.appendChild(themeSwitch);
            
            return container;
        }
        
        // 创建字段
        const apiUrlField = createField('apiUrl', 'API URL', config.apiUrl, 'text', '输入API URL');
        const apiKeyField = createField('apiKey', 'API Key', config.apiKey, 'text', '输入API Key');
        const modelField = createField('model', 'AI 模型', config.model, 'text', '输入AI模型名称');
        const shortcutField = createField('shortcut', '快捷键', config.shortcut, 'text', '例如: option+a, ctrl+shift+s');
        const promptField = createField('prompt', '提示词', config.prompt, 'textarea', '输入提示词');
        
        // 添加主题切换
        form.appendChild(createThemeSwitch());
        
        // 添加字段到表单
        form.appendChild(apiUrlField.container);
        form.appendChild(apiKeyField.container);
        form.appendChild(modelField.container);
        form.appendChild(shortcutField.container);
        form.appendChild(promptField.container);
        
        // 保存按钮
        const saveBtn = document.createElement('button');
        saveBtn.textContent = '保存设置';
        saveBtn.type = 'button';
        saveBtn.style.cssText = `
            background: ${isDark ? '#8ab4f8' : '#007AFF'};
            color: ${isDark ? '#202124' : 'white'};
            border: none;
            padding: 10px;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            transition: background-color 0.2s;
        `;

        saveBtn.addEventListener('mouseover', () => {
            saveBtn.style.backgroundColor = isDark ? '#aecbfa' : '#0056b3';
        });
        saveBtn.addEventListener('mouseout', () => {
            saveBtn.style.backgroundColor = isDark ? '#8ab4f8' : '#007AFF';
        });
        
        // 保存逻辑
        saveBtn.addEventListener('click', async (e) => {
            e.preventDefault();
            
            // 获取并验证表单值
            const newApiUrl = apiUrlField.input.value.trim();
            const newApiKey = apiKeyField.input.value.trim();
            const newModel = modelField.input.value.trim();
            const newPrompt = promptField.input.value.trim();
            const newShortcut = shortcutField.input.value.trim();
            
            if (!newApiUrl || !newApiKey) {
                alert('请至少填写API URL和API Key');
                return;
            }

            try {
                // 使用scriptHandler保存设置
                await scriptHandler.setValue('apiUrl', newApiUrl);
                await scriptHandler.setValue('apiKey', newApiKey);
                await scriptHandler.setValue('model', newModel);
                await scriptHandler.setValue('prompt', newPrompt);
                await scriptHandler.setValue('shortcut', newShortcut);
                await scriptHandler.setValue('theme', config.theme);

                // 更新内存配置
                config.apiUrl = newApiUrl;
                config.apiKey = newApiKey;
                config.model = newModel;
                config.prompt = newPrompt;
                config.shortcut = newShortcut;
                
                // 更新快捷键
                keyManager.setup();

                // 显示成功提示
                showToast('设置已保存');
                
                // 关闭设置
                settingsContainer.style.display = 'none';
            } catch (error) {
                console.error('保存设置失败:', error);
                showToast('保存设置失败,请重试');
            }
        });

        // 组装界面
        header.appendChild(title);
        header.appendChild(closeBtn);
        form.appendChild(saveBtn);
        settingsContainer.appendChild(header);
        settingsContainer.appendChild(form);
        document.body.appendChild(settingsContainer);

        makeDraggable(settingsContainer);
        elements.settings = settingsContainer;
        return settingsContainer;
    }

    // 获取页面内容
    function getPageContent() {
        try {
            const clone = document.body.cloneNode(true);
            const elementsToRemove = clone.querySelectorAll('script, style, iframe, nav, header, footer, .ad, .advertisement, .social-share, .comment, .related-content');
            elementsToRemove.forEach(el => el.remove());
            return clone.innerText.replace(/\s+/g, ' ').trim().slice(0, 5000);
        } catch (error) {
            return document.body.innerText.slice(0, 5000);
        }
    }

    // 修改深色模式颜色方案
    const darkColors = {
        background: '#242526',           // 更柔和的深色背景
        containerBg: '#2d2d30',          // 容器背景色
        text: '#e4e6eb',                 // 更柔和的文字颜色
        secondaryText: '#b0b3b8',        // 次要文字颜色
        border: '#3e4042',               // 边框颜色
        codeBackground: '#3a3b3c',       // 代码块背景
        blockquoteBorder: '#4a4b4d',     // 引用块边框
        blockquoteText: '#cacbcc',       // 引用块文字
        linkColor: '#4e89e8'             // 链接颜色
    };

    // 修改 API 调用函数
    function getSummary(content) {
        return new Promise((resolve, reject) => {
            const apiKey = config.apiKey.trim();
            
            if (!apiKey) {
                reject(new Error('请先设置API Key'));
                return;
            }

            const requestData = {
                model: config.model,
                messages: [
                    {
                        role: 'system',
                        content: '你是一个专业的网页内容总结助手,善于使用markdown格式来组织信息。'
                    },
                    {
                        role: 'user',
                        content: config.prompt + '\n\n' + content
                    }
                ],
                temperature: 0.7,
                stream: false
            };

            // 处理 URL
            let apiUrl = config.apiUrl.trim();
            if (!apiUrl.startsWith('http://') && !apiUrl.startsWith('https://')) {
                apiUrl = 'https://' + apiUrl;
            }

            // 打印请求信息用于调试
            console.log('发送请求到:', apiUrl);
            console.log('请求数据:', JSON.stringify(requestData, null, 2));

            // 发送请求
            const xhr = typeof GM_xmlhttpRequest !== 'undefined' ? GM_xmlhttpRequest : (typeof GM !== 'undefined' && GM.xmlHttpRequest);
            
            if (!xhr) {
                reject(new Error('不支持的环境:无法发送跨域请求'));
                return;
            }

            xhr({
                method: 'POST',
                url: apiUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${apiKey}`,
                    'Accept': 'application/json'
                },
                data: JSON.stringify(requestData),
                timeout: 30000,
                onload: function(response) {
                    try {
                        console.log('收到响应:', response.status);
                        console.log('响应头:', response.responseHeaders);
                        console.log('响应内容:', response.responseText);

                        if (response.status === 429) {
                            reject(new Error('API请求过于频繁,请稍后再试'));
                            return;
                        }

                        if (response.status !== 200) {
                            reject(new Error(`API请求失败: HTTP ${response.status}`));
                            return;
                        }

                        let data;
                        try {
                            data = JSON.parse(response.responseText);
                        } catch (e) {
                            console.error('JSON解析失败:', e);
                            reject(new Error('API响应格式错误'));
                            return;
                        }

                        if (data.error) {
                            reject(new Error('API错误: ' + (data.error.message || JSON.stringify(data.error))));
                            return;
                        }

                        // 提取内容
                        let content = null;
                        
                        if (data.choices && Array.isArray(data.choices) && data.choices.length > 0) {
                            const choice = data.choices[0];
                            if (choice.message && choice.message.content) {
                                content = choice.message.content;
                            } else if (choice.text) {
                                content = choice.text;
                            }
                        }

                        if (!content && data.response) {
                            content = typeof data.response === 'string' ? data.response : JSON.stringify(data.response);
                        }

                        if (!content && data.content) {
                            content = data.content;
                        }

                        if (content) {
                            resolve(content.trim());
                        } else {
                            reject(new Error('无法从API响应中提取内容'));
                        }
                    } catch (error) {
                        console.error('处理响应时出错:', error);
                        reject(new Error('处理响应失败: ' + error.message));
                    }
                },
                onerror: function(error) {
                    console.error('请求错误:', error);
                    reject(new Error('请求失败: ' + (error.message || '网络错误')));
                },
                ontimeout: function() {
                    reject(new Error('请求超时'));
                }
            });
        });
    }

    // 配置 Marked 渲染器
    function configureMarked() {
        if (typeof marked === 'undefined') return;

        // 配置 marked 选项
        marked.setOptions({
            gfm: true, // 启用 GitHub 风格 Markdown
            breaks: true, // 启用换行符转换
            headerIds: true, // 为标题生成 ID
            mangle: false, // 不转义标题中的内容
            smartLists: true, // 优化列表输出
            smartypants: true, // 优化标点符号
            highlight: function(code, lang) {
                return code;
            }
        });

        // 自定义渲染器
        const renderer = new marked.Renderer();

        // 自定义标题渲染
        renderer.heading = function(text, level) {
            if (level === 2) {
                return `<h2><span class="prefix">##</span>${text}</h2>`;
            }
            return `<h${level}>${text}</h${level}>`;
        };

        // 自定义列表项渲染
        renderer.listitem = function(text) {
            return `<li><span class="bullet">•</span><span class="text">${text}</span></li>`;
        };

        // 自定义段落渲染
        renderer.paragraph = function(text) {
            return `<p>${text}</p>`;
        };

        // 自定义代码块渲染
        renderer.code = function(code, language) {
            return `<pre><code class="language-${language}">${code}</code></pre>`;
        };

        // 自定义引用块渲染
        renderer.blockquote = function(quote) {
            return `<blockquote>${quote}</blockquote>`;
        };

        // 设置渲染器
        marked.setOptions({ renderer });
    }

    // 修改 Markdown 样式
    function addMarkdownStyles() {
        const styleId = 'website-summary-markdown-styles';
        if (document.getElementById(styleId)) return;

        const isDark = config.theme === 'dark';
        const style = document.createElement('style');
        style.id = styleId;
        
        // 定义颜色变量
        const colors = {
            light: {
                text: '#2c3e50',
                background: '#ffffff',
                border: '#e2e8f0',
                link: '#2563eb',
                linkHover: '#1d4ed8',
                code: '#f8fafc',
                codeBorder: '#e2e8f0',
                blockquote: '#f8fafc',
                blockquoteBorder: '#3b82f6',
                heading: '#1e293b',
                hr: '#e2e8f0',
                marker: '#64748b'
            },
            dark: {
                text: '#e2e8f0',
                background: '#1e293b',
                border: '#334155',
                link: '#60a5fa',
                linkHover: '#93c5fd',
                code: '#1e293b',
                codeBorder: '#334155',
                blockquote: '#1e293b',
                blockquoteBorder: '#60a5fa',
                heading: '#f1f5f9',
                hr: '#334155',
                marker: '#94a3b8'
            }
        };

        const c = isDark ? colors.dark : colors.light;

        style.textContent = `
            #website-summary-content {
                font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
                line-height: 1.7;
                color: ${c.text};
                font-size: 15px;
                padding: 16px;
            }

            #website-summary-content h2 {
                font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Helvetica Neue", Arial, sans-serif;
                font-weight: 600;
                line-height: 1.25;
                margin: 1.5em 0 0.8em 0;
                color: ${c.heading};
                font-size: 1.5em;
                display: flex;
                align-items: baseline;
            }

            #website-summary-content h2 .prefix {
                color: ${c.marker};
                font-weight: 300;
                margin-right: 0.5em;
                font-size: 0.9em;
            }

            #website-summary-content p {
                margin: 0.6em 0;
                line-height: 1.75;
            }

            #website-summary-content ul {
                margin: 0.4em 0;
                padding-left: 0;
                list-style: none;
            }

            #website-summary-content ul li {
                display: flex;
                align-items: baseline;
                margin: 0.3em 0;
                line-height: 1.6;
            }

            #website-summary-content ul li .bullet {
                color: ${c.marker};
                margin-right: 0.5em;
                font-weight: bold;
                flex-shrink: 0;
            }

            #website-summary-content ul li .text {
                flex: 1;
            }

            #website-summary-content blockquote {
                margin: 1em 0;
                padding: 0.6em 1em;
                background: ${c.blockquote};
                border-left: 3px solid ${c.blockquoteBorder};
                border-radius: 4px;
                color: ${isDark ? '#cbd5e1' : '#475569'};
                font-style: italic;
            }

            #website-summary-content blockquote p {
                margin: 0.3em 0;
            }

            #website-summary-content code {
                font-family: "SF Mono", Menlo, Monaco, Consolas, monospace;
                font-size: 0.9em;
                background: ${c.code};
                border: 1px solid ${c.codeBorder};
                border-radius: 4px;
                padding: 0.2em 0.4em;
            }

            #website-summary-content pre {
                background: ${c.code};
                border: 1px solid ${c.codeBorder};
                border-radius: 6px;
                padding: 1em;
                overflow-x: auto;
                margin: 1em 0;
            }

            #website-summary-content pre code {
                background: none;
                border: none;
                padding: 0;
                font-size: 0.9em;
                line-height: 1.6;
            }

            #website-summary-content strong {
                font-weight: 600;
                color: ${isDark ? '#f1f5f9' : '#1e293b'};
            }

            #website-summary-content em {
                font-style: italic;
                color: ${isDark ? '#cbd5e1' : '#475569'};
            }

            #website-summary-content hr {
                margin: 1.5em 0;
                border: none;
                border-top: 1px solid ${c.hr};
            }

            #website-summary-content table {
                width: 100%;
                border-collapse: collapse;
                margin: 1em 0;
                font-size: 0.95em;
            }

            #website-summary-content th,
            #website-summary-content td {
                padding: 0.75em;
                border: 1px solid ${c.border};
                text-align: left;
            }

            #website-summary-content th {
                background: ${c.code};
                font-weight: 600;
            }

            #website-summary-content img {
                max-width: 100%;
                height: auto;
                border-radius: 6px;
                margin: 0.8em 0;
            }

            @media (max-width: 768px) {
                #website-summary-content {
                    font-size: 14px;
                    padding: 12px;
                }

                #website-summary-content h2 {
                    font-size: 1.3em;
                }
            }
        `;

        document.head.appendChild(style);
    }

    // 修改渲染内容函数
    async function renderContent(content) {
        const container = document.getElementById('website-summary-content');
        if (!container) return;
        
        try {
            if (!content || content.trim().length === 0) {
                throw new Error('内容为空');
            }

            // 确保 marked 已加载并配置
            if (typeof marked === 'undefined') {
                throw new Error('Markdown 渲染器未加载');
            }
            
            // 配置 marked
            configureMarked();

            // 渲染 Markdown
            const html = marked.parse(content);
            
            // 清空容器
            container.innerHTML = '';
            
            // 创建临时容器
            const temp = document.createElement('div');
            temp.innerHTML = html;
            
            // 逐段落输出,提升性能
            const fragments = Array.from(temp.children);
            const typeDelay = 20; // 每段延迟(毫秒)
            
            for (let fragment of fragments) {
                container.appendChild(fragment);
                await new Promise(resolve => setTimeout(resolve, typeDelay));
            }
        } catch (error) {
            console.error('渲染内容失败:', error);
            container.innerHTML = `
                <p style="text-align: center; color: #ff4444;">
                    渲染内容失败:${error.message}<br>
                    <small style="color: ${config.theme === 'dark' ? '#bdc1c6' : '#666'};">
                        请刷新页面重试
                    </small>
                </p>`;
        }
    }

    // 添加菜单命令
    function registerMenuCommands() {
        scriptHandler.registerMenuCommand('显示网页总结 (快捷键: ' + config.shortcut + ')', showSummary);
        scriptHandler.registerMenuCommand('打开设置', showSettings);
    }

    // 启动脚本
    waitForPageLoad();
})();