Greasy Fork

Greasy Fork is available in English.

AI Page Summarizer Pro

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

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

您需要先安装一个扩展,例如 篡改猴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
// @namespace    http://tampermonkey.net/
// @version      0.9.8
// @description  网页内容智能总结,支持自定义API和提示词
// @author       Your Name
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @require      https://cdn.jsdelivr.net/npm/[email protected]/marked.min.js
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // 配置项
    let config = {
        apiUrl: GM_getValue('apiUrl', 'https://api.openai.com/v1/chat/completions'),
        apiKey: GM_getValue('apiKey', ''),
        model: GM_getValue('model', 'gpt-3.5-turbo'),
        prompt: GM_getValue('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 Markdown formatting
   - Start with a brief overview
   - Use appropriate headings (h2, h3)
   - Include bullet points for key points
   - Use bold for important terms
   - Use blockquotes for notable quotes
   - Use code blocks for technical content

2. Content Structure:
   - Main Topic/Title
   - Key Points
   - Important Details
   - Conclusions/Summary

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

5. Length Guidelines:
   - Overview: 1-2 sentences
   - Key Points: 3-5 bullet points
   - Important Details: 2-3 paragraphs
   - Summary: 1-2 sentences

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

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

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

    // 快捷键处理
    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');
                    
                    // 检查按键是否匹配 - 同时检查key和code
                    const isMainKeyMatched = 
                        e.key.toLowerCase() === mainKey || 
                        (e.code && e.code.toLowerCase() === 'key' + mainKey);
                        
                    // 检查修饰键是否匹配
                    if (isMainKeyMatched && 
                        e.altKey === needAlt && 
                        e.ctrlKey === needCtrl && 
                        e.shiftKey === needShift && 
                        e.metaKey === needMeta) {
                        
                        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;
            }
        },
        
        // 测试快捷键是否工作
        test() {
            try {
                if (!keyboardListenerActive) {
                    console.warn('键盘监听器未激活,请先调用 keyManager.setup()');
                    return false;
                }
                
                // 解析当前快捷键
                const parts = config.shortcut.toLowerCase().split('+');
                const mainKey = parts.filter(part => 
                    !['alt', 'option', 'ctrl', 'control', 'shift', 'cmd', 'command', 'meta']
                    .includes(part)
                )[0] || 'a';
                
                // 创建事件对象
                const eventOptions = {
                    key: mainKey,
                    code: 'Key' + mainKey.toUpperCase(),
                    altKey: parts.includes('alt') || parts.includes('option'),
                    ctrlKey: parts.includes('ctrl') || parts.includes('control'),
                    shiftKey: parts.includes('shift'),
                    metaKey: parts.includes('cmd') || parts.includes('command') || parts.includes('meta'),
                    bubbles: true,
                    cancelable: true
                };
                
                console.log('模拟快捷键按下:', JSON.stringify(eventOptions));
                
                const event = new KeyboardEvent('keydown', eventOptions);
                
                // 由于 KeyboardEvent 的限制,某些属性可能无法正确设置,所以我们通过这种方式确认
                if (!event.altKey && eventOptions.altKey) {
                    console.warn('注意: 无法在模拟事件中设置 altKey 属性');
                }
                
                // 分发事件
                document.dispatchEvent(event);
                
                // 因为可能无法模拟事件,所以直接提供一个调用方法
                console.log('您也可以通过控制台调用 window.activateSummary() 来直接触发');
                
                return true;
            } catch (error) {
                console.error('测试快捷键失败:', error);
                return false;
            }
        }
    };

    // 等待依赖库加载
    function waitForDependencies(callback) {
        if (window.marked) {
            window.marked.setOptions({ breaks: true, gfm: true });
            callback();
            return;
        }
        setTimeout(() => waitForDependencies(callback), 100);
    }

    // 创建图标
    function createIcon() {
        const icon = document.createElement('div');
        icon.id = 'website-summary-icon';
        
        // Mac风格书本图标
        icon.innerHTML = `
            <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
                <defs>
                    <linearGradient id="ws-grad" x1="0%" y1="0%" x2="100%" y2="100%">
                        <stop offset="0%" style="stop-color:#4299e1;stop-opacity:1" />
                        <stop offset="100%" style="stop-color:#3182ce;stop-opacity:1" />
                    </linearGradient>
                    <filter id="ws-shadow" x="-10%" y="-10%" width="120%" height="120%">
                        <feDropShadow dx="0" dy="1" stdDeviation="1" flood-color="#000000" flood-opacity="0.2"/>
                    </filter>
                    <linearGradient id="ws-page-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                        <stop offset="0%" style="stop-color:#ffffff;stop-opacity:1" />
                        <stop offset="100%" style="stop-color:#f7fafc;stop-opacity:1" />
                    </linearGradient>
                </defs>
                
                <!-- 背景圆 -->
                <circle cx="20" cy="20" r="18" fill="url(#ws-grad)" filter="url(#ws-shadow)" />
                
                <!-- 书本 -->
                <g transform="translate(10, 10) scale(0.8)">
                    <!-- 书本封面 -->
                    <path d="M5 6 L5 27 L23 27 C25 27 25 24 23 24 L8 24 L8 9 L23 9 C25 9 25 6 23 6 Z" 
                          fill="#ffffff" stroke="#ffffff" stroke-width="1" />
                    
                    <!-- 书本纸张 -->
                    <path d="M8 9 L8 24 L23 24 C21 24 21 9 23 9 Z" 
                          fill="url(#ws-page-grad)" stroke="#e2e8f0" stroke-width="0.5" />
                    
                    <!-- 书脊 -->
                    <path d="M5 6 L5 27" stroke="#ffffff" stroke-width="2" stroke-linecap="round" />
                    
                    <!-- 装饰线1 -->
                    <path d="M11 14 L19 14" stroke="#3182ce" stroke-width="0.75" stroke-linecap="round" />
                    
                    <!-- 装饰线2 -->
                    <path d="M11 17 L17 17" stroke="#3182ce" stroke-width="0.75" stroke-linecap="round" />
                    
                    <!-- 装饰线3 -->
                    <path d="M11 20 L19 20" stroke="#3182ce" stroke-width="0.75" stroke-linecap="round" />
                </g>
            </svg>
        `;
        
        icon.style.cssText = `
            position: fixed;
            z-index: 999999;
            width: 40px;
            height: 40px;
            border-radius: 20px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: transform 0.2s ease, box-shadow 0.2s ease;
            backdrop-filter: blur(5px);
            right: 20px;
            top: ${config.iconPosition.y || 20}px;
            touch-action: none;
            will-change: transform;
        `;

        icon.addEventListener('mouseover', () => {
            icon.style.transform = 'scale(1.1)';
            icon.style.boxShadow = '0 4px 12px rgba(49, 130, 206, 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', showSummary);
        icon.addEventListener('contextmenu', (e) => {
            e.preventDefault();
            showSettings();
        });
        
        makeDraggable(icon);
        document.body.appendChild(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: #666;">正在获取总结...</p>';
        
        try {
            const pageContent = getPageContent();
            if (!pageContent) throw new Error('无法获取页面内容');
            
            const summary = await getSummary(pageContent);
            if (!summary) throw new Error('获取总结失败');
            
            renderContent(summary);
        } catch (error) {
            content.innerHTML = `
                <p style="text-align: center; color: #ff4444;">
                    获取总结失败:${error.message}<br>
                    请检查API配置是否正确
                </p>`;
        }
    }

    // 创建/显示背景
    function showBackdrop() {
        if (!elements.backdrop) {
            const backdrop = document.createElement('div');
            backdrop.id = 'website-summary-backdrop';
            backdrop.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: 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;
        }
        
        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';
        container.style.cssText = `
            position: fixed;
            z-index: 999998;
            background: rgba(255, 255, 255, 0.9);
            border-radius: 12px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.08);
            padding: 16px;
            width: 80%;
            max-width: 800px;
            max-height: 80vh;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            display: none;
            backdrop-filter: blur(10px);
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            overflow: hidden;
            opacity: 0;
            transition: opacity 0.3s ease;
        `;

        // 标题栏
        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 #eee;
        `;

        // 标题
        const title = document.createElement('h3');
        title.textContent = '网页总结';
        title.style.cssText = 'margin: 0; font-size: 18px; color: #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: #4CAF50;
            color: 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 = '#45a049');
        copyBtn.addEventListener('mouseout', () => copyBtn.style.backgroundColor = '#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: #666;
            transition: color 0.2s;
        `;

        closeBtn.addEventListener('mouseover', () => closeBtn.style.color = '#ff4444');
        closeBtn.addEventListener('mouseout', () => closeBtn.style.color = '#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;
        `;

        // 组装界面
        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';
        settingsContainer.style.cssText = `
            position: fixed;
            z-index: 1000000;
            background: rgba(255, 255, 255, 0.98);
            border-radius: 12px;
            box-shadow: 0 4px 20px 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.margin = '0';

        const closeBtn = document.createElement('button');
        closeBtn.textContent = '×';
        closeBtn.style.cssText = 'background: none; border: none; font-size: 24px; cursor: pointer; padding: 0 8px; color: #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: #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 #ddd; border-radius: 6px; font-family: inherit;';
            input.style.cssText = type === 'textarea' ? baseStyle + 'height: 100px; resize: vertical;' : baseStyle;
            
            container.appendChild(labelElem);
            container.appendChild(input);
            return { container, input };
        }
        
        // 创建字段
        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(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: #007AFF;
            color: 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 = '#0056b3');
        saveBtn.addEventListener('mouseout', () => saveBtn.style.backgroundColor = '#007AFF');
        
        // 保存逻辑
        saveBtn.addEventListener('click', (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;
            }

            // 保存到存储
            GM_setValue('apiUrl', newApiUrl);
            GM_setValue('apiKey', newApiKey);
            GM_setValue('model', newModel);
            GM_setValue('prompt', newPrompt);
            GM_setValue('shortcut', newShortcut);

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

            // 显示成功提示
            showToast('设置已保存');
            
            // 关闭设置
            settingsContainer.style.display = 'none';
        });

        // 组装界面
        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 showToast(message) {
        const toast = document.createElement('div');
        toast.textContent = message;
        toast.style.cssText = `
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: #4CAF50;
            color: white;
            padding: 10px 20px;
            border-radius: 4px;
            z-index: 1000001;
            font-size: 14px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        `;
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), 2000);
    }

    // 拖拽功能
    function makeDraggable(element) {
        const header = element.querySelector('div') || element;
        let startX = 0, startY = 0;
        let elementX = 0, elementY = 0;
        let dragging = false;
        
        // 鼠标事件
        header.addEventListener('mousedown', startDrag);
        
        // 触摸事件
        header.addEventListener('touchstart', (e) => {
            const touch = e.touches[0];
            startDrag({ clientX: touch.clientX, clientY: touch.clientY, preventDefault: () => e.preventDefault() });
        }, { passive: false });
        
        function startDrag(e) {
            e.preventDefault();
            dragging = true;
            
            // 记录起始位置
            startX = e.clientX;
            startY = e.clientY;
            elementX = element.offsetLeft;
            elementY = element.offsetTop;
            
            // 设置样式
            if (element.id === 'website-summary-icon') {
                element.style.transition = 'none';
                element.style.opacity = '0.9';
            }
            
            // 添加移动和结束事件
            document.addEventListener('mousemove', onDrag);
            document.addEventListener('touchmove', onTouchDrag, { passive: false });
            document.addEventListener('mouseup', stopDrag);
            document.addEventListener('touchend', stopDrag);
        }
        
        function onDrag(e) {
            if (!dragging) return;
            move(e.clientX, e.clientY);
        }
        
        function onTouchDrag(e) {
            if (!dragging) return;
            e.preventDefault();
            const touch = e.touches[0];
            move(touch.clientX, touch.clientY);
        }
        
        function move(clientX, clientY) {
            // 计算新位置
            const deltaX = clientX - startX;
            const deltaY = 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 {
                // 自由移动其他元素
                element.style.left = (elementX + deltaX) + 'px';
                element.style.top = (elementY + deltaY) + 'px';
                
                // 重置transform以避免与translate(-50%, -50%)冲突
                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 };
                GM_setValue('iconPosition', config.iconPosition);
            }
            
            // 移除事件监听器
            document.removeEventListener('mousemove', onDrag);
            document.removeEventListener('touchmove', onTouchDrag);
            document.removeEventListener('mouseup', stopDrag);
            document.removeEventListener('touchend', stopDrag);
        }
    }

    // 获取页面内容
    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);
        }
    }

    // 调用API获取总结
    function getSummary(content) {
        return new Promise((resolve, reject) => {
            const apiKey = config.apiKey.trim();
            
            if (!apiKey) {
                resolve('请先设置API Key');
                return;
            }

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

            GM_xmlhttpRequest({
                method: 'POST',
                url: config.apiUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${apiKey}`
                },
                data: JSON.stringify(requestData),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.error) {
                            resolve('API调用失败: ' + data.error.message);
                            return;
                        }
                        if (data.choices && data.choices[0] && data.choices[0].message) {
                            resolve(data.choices[0].message.content);
                        } else {
                            resolve('API响应格式错误,请检查配置。');
                        }
                    } catch (error) {
                        resolve('解析API响应失败,请检查网络连接。');
                    }
                },
                onerror: function() {
                    resolve('API调用失败,请检查网络连接和API配置。');
                }
            });
        });
    }

    // 渲染Markdown内容
    function renderContent(content) {
        const container = document.getElementById('website-summary-content');
        if (!container) return;
        
        try {
            if (!content) throw new Error('内容为空');

            // 渲染Markdown
            let html = window.marked.parse(content);
            container.innerHTML = html;

            // 添加样式
            addMarkdownStyles();
        } catch (error) {
            container.innerHTML = '<p style="text-align: center; color: #ff4444;">渲染内容失败,请刷新页面重试。</p>';
        }
    }

    // 添加Markdown样式
    function addMarkdownStyles() {
        const styleId = 'website-summary-styles';
        if (document.getElementById(styleId)) return;
        
        const style = document.createElement('style');
        style.id = styleId;
        style.textContent = `
            #website-summary-content {
                font-size: 14px;
                line-height: 1.6;
                color: #333;
            }
            #website-summary-content h1, #website-summary-content h2, #website-summary-content h3 {
                margin-top: 20px;
                margin-bottom: 10px;
                color: #222;
            }
            #website-summary-content p {
                margin: 10px 0;
            }
            #website-summary-content code {
                background: #f5f5f5;
                padding: 2px 4px;
                border-radius: 3px;
                font-family: monospace;
            }
            #website-summary-content pre {
                background: #f5f5f5;
                padding: 15px;
                border-radius: 5px;
                overflow-x: auto;
            }
            #website-summary-content blockquote {
                border-left: 4px solid #ddd;
                margin: 10px 0;
                padding-left: 15px;
                color: #666;
            }
            #website-summary-content ul, #website-summary-content ol {
                margin: 10px 0;
                padding-left: 20px;
            }
            #website-summary-content li {
                margin: 5px 0;
            }
            #website-summary-content table {
                border-collapse: collapse;
                width: 100%;
                margin: 10px 0;
            }
            #website-summary-content th, #website-summary-content td {
                border: 1px solid #ddd;
                padding: 8px;
                text-align: left;
            }
            #website-summary-content th {
                background: #f5f5f5;
            }
        `;
        document.head.appendChild(style);
    }

    // 添加菜单命令
    function registerMenuCommands() {
        try {
            GM_registerMenuCommand('显示网页总结 (快捷键: ' + config.shortcut + ')', showSummary);
            GM_registerMenuCommand('打开设置', showSettings);
        } catch (error) {
            console.log('注册菜单命令失败:', error);
        }
    }

    // 初始化
    function init() {
        // 等待页面完全加载
        if (document.readyState !== 'complete') {
            window.addEventListener('load', init);
            return;
        }

        createIcon();
        
        // 设置快捷键
        const keySetupSuccess = keyManager.setup();
        
        // 在页面获得焦点时重新注册快捷键
        window.addEventListener('focus', () => keyManager.setup());
        
        // 注册菜单命令
        registerMenuCommands();
        
        console.log('AI Page Summarizer Pro 初始化完成');
        console.log('快捷键:', config.shortcut);
        
        // 在页面加载完成后测试快捷键
        setTimeout(() => {
            keyManager.test();
            console.log('快捷键使用提示: 按下 ' + config.shortcut + ' 触发网页总结,或右键点击图标打开设置');
            console.log('也可以通过控制台调用 window.activateSummary() 直接触发');
        }, 2000);
    }

    // 启动应用
    waitForDependencies(init);
})();