网页内容摘要器

使用OpenAI或Google Gemini API快速总结网页内容

// ==UserScript==
// @name         网页内容摘要器
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  使用OpenAI或Google Gemini API快速总结网页内容
// @author       Your Name
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @run-at       document-end
// @license MIT
// @require      https://cdn.jsdelivr.net/npm/[email protected]/marked.min.js
// ==/UserScript==

(function() {
    'use strict';

    // 常量定义
    const DEFAULT_OPENAI_URL = "https://api.openai.com/v1/chat/completions";
    const DEFAULT_GEMINI_URL = "https://generativelanguage.googleapis.com";
    const DEFAULT_HOTKEY = "s";
    const HOTKEY_MODIFIER = "alt";
    const DEFAULT_WIDTH = 500; // 默认宽度
    const DEFAULT_HEIGHT = 600; // 默认高度
    const MIN_WIDTH = 300; // 最小宽度
    const MIN_HEIGHT = 200; // 最小高度

    // 默认配置
    const DEFAULT_CONFIGS = {
        apis: [
            {
                id: "openai-default",
                name: "OpenAI默认",
                type: "openai",
                baseUrl: DEFAULT_OPENAI_URL,
                apiKey: "",
                models: ["gpt-3.5-turbo", "gpt-4", "gpt-4-turbo"]
            },
            {
                id: "gemini-default",
                name: "Gemini默认",
                type: "gemini",
                baseUrl: DEFAULT_GEMINI_URL,
                apiKey: "",
                models: ["gemini-pro", "gemini-1.5-pro", "gemini-1.5-flash"]
            }
        ],
        prompts: [
            {
                id: "summary-default",
                name: "一般摘要",
                content: "请用中文总结以下网页内容的要点,用简洁的语言描述主要信息。请使用Markdown格式输出,以提高可读性。",
                apiType: "openai",
                apiId: "openai-default",
                model: "gpt-3.5-turbo"
            },
            {
                id: "detailed-summary",
                name: "详细摘要",
                content: "请详细分析以下网页内容,提供全面的中文摘要,包括主要观点、关键数据和结论。使用Markdown格式输出,合理使用标题、列表、引用等元素增强可读性。",
                apiType: "openai",
                apiId: "openai-default",
                model: "gpt-3.5-turbo"
            },
            {
                id: "structured-summary",
                name: "结构化摘要",
                content: "请以结构化的方式分析并总结以下网页内容。使用Markdown语法,创建包含以下部分的摘要:\n\n1. **主要内容**: 用1-2段话概述主要内容\n2. **关键点**: 使用项目符号列出3-5个最重要的观点\n3. **细节与数据**: 提取文章中的重要数据和具体细节\n4. **结论**: 总结文章的结论或观点\n\n确保使用合适的Markdown标题、列表、强调和引用格式。",
                apiType: "openai",
                apiId: "openai-default",
                model: "gpt-3.5-turbo"
            },
            {
                id: "gemini-summary",
                name: "Gemini摘要",
                content: "请用中文总结以下网页内容的要点,用简洁的语言描述主要信息。请使用Markdown格式输出,以提高可读性。",
                apiType: "gemini",
                apiId: "gemini-default",
                model: "gemini-pro"
            }
        ],
        settings: {
            hotkey: DEFAULT_HOTKEY,
            autoExpand: false,
            lastUsedPromptId: "summary-default" // 记录上次使用的提示词ID
        },
        position: null,
        size: {
            width: DEFAULT_WIDTH
        }
    };

    // 状态变量
    let configs = GM_getValue("summarizer_configs", DEFAULT_CONFIGS);
    let isDragging = false;
    let dragOffsetX = 0;
    let dragOffsetY = 0;
    let summarizer = null;
    let isProcessing = false;
    let currentRequest = null; // 保存当前请求的引用

    // 添加CSS样式
    function addStyles() {
        GM_addStyle(`
            #web-summarizer {
                position: fixed;
                top: 0;
                right: 0;
                width: ${DEFAULT_WIDTH}px;
                height: 100vh;
                background-color: #fff;
                border-left: 1px solid #ccc;
                font-family: Arial, sans-serif;
                z-index: 10000;
                display: none;
                overflow: hidden;
                transition: none;
                display: flex;
                flex-direction: column;
                min-width: ${MIN_WIDTH}px;
                resize: none;
                box-shadow: -2px 0 10px rgba(0, 0, 0, 0.1);
            }

            #summarizer-header {
                padding: 8px 10px;
                background-color: #f5f5f5;
                border-bottom: 1px solid #ddd;
                display: flex;
                justify-content: space-between;
                align-items: center;
                user-select: none;
                flex-shrink: 0;
            }

            #summarizer-resize-handle {
                position: absolute;
                top: 0;
                left: 0;
                width: 5px;
                height: 100%;
                cursor: ew-resize;
                background-color: transparent;
                z-index: 10002;
            }

            #summarizer-resize-handle:hover {
                background-color: rgba(0, 0, 0, 0.05);
            }

            .resizing #summarizer-resize-handle {
                background-color: rgba(66, 133, 244, 0.2);
            }

            #summarizer-title {
                font-weight: bold;
                font-size: 14px;
                margin: 0;
            }

            #summarizer-close {
                background: none;
                border: none;
                font-size: 16px;
                cursor: pointer;
                padding: 0 5px;
            }

            #summarizer-body {
                padding: 15px;
                overflow-y: auto;
                flex-grow: 1;
            }

            #summarizer-controls {
                margin-bottom: 15px;
                display: flex;
                flex-direction: column;
            }

            #prompt-select {
                width: 100%;
                padding: 8px;
                margin-bottom: 10px;
                border: 1px solid #ccc;
                border-radius: 4px;
            }

            .summarizer-btn {
                padding: 8px 12px;
                background-color: #4285f4;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                margin-right: 8px;
                font-size: 13px;
            }

            .button-group {
                display: flex;
                gap: 8px;
            }

            .summarizer-btn:hover {
                background-color: #3367d6;
            }

            .summarizer-btn:disabled {
                background-color: #b3cefb;
                cursor: not-allowed;
            }

            #summarizer-result {
                border: 1px solid #ddd;
                border-radius: 4px;
                padding: 15px;
                background-color: #f9f9f9;
                font-size: 14px;
                line-height: 1.6;
                max-height: none;
                height: auto;
                flex-grow: 1;
                overflow-y: auto;
                word-wrap: break-word;
                display: none;
                margin-top: 15px;
            }

            /* Markdown 样式 */
            #summarizer-result h1,
            #summarizer-result h2,
            #summarizer-result h3,
            #summarizer-result h4,
            #summarizer-result h5,
            #summarizer-result h6 {
                margin-top: 1.5em;
                margin-bottom: 0.5em;
                line-height: 1.2;
                font-weight: 600;
            }

            #summarizer-result h1 {
                font-size: 1.8em;
                border-bottom: 1px solid #eaecef;
                padding-bottom: 0.3em;
            }

            #summarizer-result h2 {
                font-size: 1.5em;
                border-bottom: 1px solid #eaecef;
                padding-bottom: 0.3em;
            }

            #summarizer-result h3 {
                font-size: 1.3em;
            }

            #summarizer-result h4 {
                font-size: 1.1em;
            }

            #summarizer-result p {
                margin-top: 0.5em;
                margin-bottom: 1em;
            }

            #summarizer-result ul,
            #summarizer-result ol {
                margin-top: 0.5em;
                margin-bottom: 1em;
                padding-left: 2em;
            }

            #summarizer-result li {
                margin: 0.3em 0;
            }

            #summarizer-result code {
                background-color: rgba(27, 31, 35, 0.05);
                border-radius: 3px;
                font-family: monospace;
                padding: 0.2em 0.4em;
                font-size: 0.9em;
            }

            #summarizer-result pre {
                background-color: #f6f8fa;
                border-radius: 3px;
                padding: 1em;
                overflow: auto;
                margin: 1em 0;
            }

            #summarizer-result pre code {
                background-color: transparent;
                padding: 0;
                white-space: pre;
            }

            #summarizer-result blockquote {
                margin: 1em 0;
                padding: 0 1em;
                color: #6a737d;
                border-left: 0.25em solid #dfe2e5;
            }

            #summarizer-result table {
                border-collapse: collapse;
                width: 100%;
                margin: 1em 0;
            }

            #summarizer-result table th,
            #summarizer-result table td {
                padding: 6px 13px;
                border: 1px solid #dfe2e5;
            }

            #summarizer-result table tr {
                background-color: #fff;
                border-top: 1px solid #c6cbd1;
            }

            #summarizer-result table tr:nth-child(2n) {
                background-color: #f6f8fa;
            }

            #summarizer-result img {
                max-width: 100%;
            }

            #summarizer-result a {
                color: #0366d6;
                text-decoration: none;
            }

            #summarizer-result a:hover {
                text-decoration: underline;
            }

            #summarizer-result hr {
                height: 0.25em;
                padding: 0;
                margin: 24px 0;
                background-color: #e1e4e8;
                border: 0;
            }

            /* 模态设置窗口样式 */
            #settings-modal {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0, 0, 0, 0.5);
                z-index: 10001;
                display: none;
                justify-content: center;
                align-items: center;
            }

            #settings-container {
                background-color: #fff;
                border-radius: 5px;
                box-shadow: 0 2px 15px rgba(0, 0, 0, 0.3);
                width: 500px;
                max-width: 90%;
                max-height: 90vh;
                display: flex;
                flex-direction: column;
                overflow: hidden;
            }

            #settings-header {
                padding: 10px 15px;
                background-color: #f5f5f5;
                border-bottom: 1px solid #ddd;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }

            #settings-title {
                font-weight: bold;
                font-size: 16px;
                margin: 0;
            }

            #settings-close {
                background: none;
                border: none;
                font-size: 18px;
                cursor: pointer;
                padding: 0 5px;
            }

            #settings-body {
                padding: 15px;
                overflow-y: auto;
                max-height: 70vh;
                flex-grow: 1;
            }

            #settings-footer {
                padding: 10px 15px;
                background-color: #f5f5f5;
                border-top: 1px solid #ddd;
                display: flex;
                justify-content: flex-end;
                gap: 10px;
            }

            .settings-tabs {
                display: flex;
                border-bottom: 1px solid #ddd;
                margin-bottom: 15px;
            }

            .settings-tab {
                padding: 8px 12px;
                cursor: pointer;
                background-color: #f5f5f5;
                border: 1px solid #ddd;
                border-bottom: none;
                margin-right: 5px;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
            }

            .settings-tab.active {
                background-color: #fff;
                border-bottom: 1px solid #fff;
                margin-bottom: -1px;
            }

            .settings-panel {
                display: none;
                max-height: 350px;
                overflow-y: auto;
            }

            .settings-panel.active {
                display: block;
            }

            .config-item {
                margin-bottom: 15px;
                border: 1px solid #ddd;
                border-radius: 4px;
            }

            .config-header {
                background-color: #f5f5f5;
                padding: 8px 10px;
                cursor: pointer;
                border-bottom: 1px solid #ddd;
            }

            .config-body {
                padding: 10px;
                display: none;
            }

            .config-body.expanded {
                display: block;
            }

            .dragging {
                opacity: 0.9;
                box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
            }

            .form-group {
                margin-bottom: 10px;
            }

            .form-group label {
                display: block;
                margin-bottom: 5px;
                font-weight: bold;
            }

            .form-group input, .form-group textarea, .form-group select {
                width: 100%;
                padding: 8px;
                border: 1px solid #ccc;
                border-radius: 4px;
            }

            .form-group textarea {
                min-height: 80px;
                resize: vertical;
            }

            #add-api-btn, #add-prompt-btn {
                margin-bottom: 15px;
            }

            .model-list {
                border: 1px solid #ddd;
                border-radius: 4px;
                padding: 5px;
                max-height: 100px;
                overflow-y: auto;
                margin-top: 5px;
            }

            .model-item {
                display: flex;
                align-items: center;
                margin-bottom: 5px;
            }

            .delete-btn {
                background-color: #f44336;
                color: white;
            }

            .delete-btn:hover {
                background-color: #d32f2f;
            }

            .spinner {
                display: inline-block;
                width: 20px;
                height: 20px;
                border: 3px solid rgba(0, 0, 0, 0.1);
                border-radius: 50%;
                border-top-color: #4285f4;
                animation: spin 1s ease infinite;
                margin-right: 10px;
                vertical-align: middle;
            }

            @keyframes spin {
                to { transform: rotate(360deg); }
            }

            #stop-btn {
                background-color: #f44336;
            }

            #stop-btn:hover {
                background-color: #d32f2f;
            }
        `);
    }

    // 创建UI界面
    function createUI() {
        try {
            // 创建主容器
            summarizer = document.createElement('div');
            summarizer.id = 'web-summarizer';

            // 设置初始状态为隐藏
            summarizer.style.display = 'none';

            // 如果有保存的大小,应用它
            if (configs.size && configs.size.width) {
                summarizer.style.width = configs.size.width + 'px';
            }

            // 创建调整大小的手柄
            const resizeHandle = document.createElement('div');
            resizeHandle.id = 'summarizer-resize-handle';

            // 创建头部
            const header = document.createElement('div');
            header.id = 'summarizer-header';
            header.innerHTML = `
                <div id="summarizer-title">网页内容摘要器</div>
                <button id="summarizer-close">×</button>
            `;

            // 创建主体内容
            const body = document.createElement('div');
            body.id = 'summarizer-body';

            // 创建控制区域
            const controls = document.createElement('div');
            controls.id = 'summarizer-controls';

            // 创建提示词选择下拉框
            const promptSelect = document.createElement('select');
            promptSelect.id = 'prompt-select';
            configs.prompts.forEach(prompt => {
                const option = document.createElement('option');
                option.value = prompt.id;
                option.textContent = prompt.name;
                // 如果是上次使用的提示词,则默认选中
                if (prompt.id === configs.settings.lastUsedPromptId) {
                    option.selected = true;
                }
                promptSelect.appendChild(option);
            });

            // 创建按钮
            const buttonGroup = document.createElement('div');
            buttonGroup.className = 'button-group';
            buttonGroup.innerHTML = `
                <button id="summarize-btn" class="summarizer-btn">摘要</button>
                <button id="settings-btn" class="summarizer-btn">设置</button>
            `;

            controls.appendChild(promptSelect);
            controls.appendChild(buttonGroup);

            // 创建结果区域
            const result = document.createElement('div');
            result.id = 'summarizer-result';

            // 组装主界面
            body.appendChild(controls);
            body.appendChild(result);
            summarizer.appendChild(resizeHandle);
            summarizer.appendChild(header);
            summarizer.appendChild(body);

            // 创建设置模态窗口
            const settingsModal = document.createElement('div');
            settingsModal.id = 'settings-modal';

            const settingsContainer = document.createElement('div');
            settingsContainer.id = 'settings-container';

            const settingsHeader = document.createElement('div');
            settingsHeader.id = 'settings-header';
            settingsHeader.innerHTML = `
                <div id="settings-title">设置</div>
                <button id="settings-close">×</button>
            `;

            const settingsBody = document.createElement('div');
            settingsBody.id = 'settings-body';
            settingsBody.innerHTML = `
                <div class="settings-tabs">
                    <div class="settings-tab active" data-tab="api-settings">API配置</div>
                    <div class="settings-tab" data-tab="prompt-settings">提示词配置</div>
                    <div class="settings-tab" data-tab="general-settings">常规设置</div>
                </div>

                <div id="api-settings" class="settings-panel active">
                    <div id="api-list"></div>
                    <button id="add-api-btn" class="summarizer-btn">添加新API配置</button>
                </div>

                <div id="prompt-settings" class="settings-panel">
                    <div id="prompt-list"></div>
                    <button id="add-prompt-btn" class="summarizer-btn">添加新提示词</button>
                </div>

                <div id="general-settings" class="settings-panel">
                    <div class="form-group">
                        <label for="hotkey-input">快捷键:</label>
                        <input type="text" id="hotkey-input" maxlength="1" value="${configs.settings.hotkey}">
                        <small>单个字母或数字,与Alt键组合使用</small>
                    </div>
                    <div class="form-group">
                        <label>
                            <input type="checkbox" id="auto-expand" ${configs.settings.autoExpand ? 'checked' : ''}>
                            自动展开设置项
                        </label>
                    </div>
                </div>
            `;

            const settingsFooter = document.createElement('div');
            settingsFooter.id = 'settings-footer';
            settingsFooter.innerHTML = `
                <button id="save-settings-btn" class="summarizer-btn">保存设置</button>
                <button id="cancel-settings-btn" class="summarizer-btn">取消</button>
            `;

            // 组装设置模态窗口
            settingsContainer.appendChild(settingsHeader);
            settingsContainer.appendChild(settingsBody);
            settingsContainer.appendChild(settingsFooter);
            settingsModal.appendChild(settingsContainer);

            // 添加到页面
            document.body.appendChild(summarizer);
            document.body.appendChild(settingsModal);

            // 初始化API和提示词列表
            updateApiList();
            updatePromptList();

            // 绑定事件
            bindAllEvents();

            console.log("[网页内容摘要器] UI创建完成");
        } catch (error) {
            console.error("[网页内容摘要器] 创建UI失败:", error);
        }
    }

    // 更新API配置列表
    function updateApiList() {
        const apiList = document.getElementById('api-list');
        if (!apiList) return;

        apiList.innerHTML = '';

        configs.apis.forEach(api => {
            const apiItem = document.createElement('div');
            apiItem.className = 'config-item';
            apiItem.innerHTML = `
                <div class="config-header" data-id="${api.id}">
                    ${api.name} (${api.type === 'openai' ? 'OpenAI兼容' : 'Gemini'})
                </div>
                <div class="config-body" id="api-${api.id}">
                    <div class="form-group">
                        <label>API名称:</label>
                        <input type="text" class="api-name" value="${api.name}">
                    </div>
                    <div class="form-group">
                        <label>API类型:</label>
                        <select class="api-type">
                            <option value="openai" ${api.type === 'openai' ? 'selected' : ''}>OpenAI兼容</option>
                            <option value="gemini" ${api.type === 'gemini' ? 'selected' : ''}>Gemini</option>
                        </select>
                    </div>
                    <div class="form-group base-url-group" ${api.type === 'gemini' ? 'style="display: none;"' : ''}>
                        <label>基础URL:</label>
                        <input type="text" class="api-base-url" value="${api.baseUrl}" ${api.type === 'gemini' ? 'readonly' : ''}>
                        <small>例如: https://api.openai.com/v1/chat/completions</small>
                    </div>
                    <div class="form-group">
                        <label>API密钥:</label>
                        <input type="password" class="api-key" value="${api.apiKey}">
                    </div>
                    <div class="form-group">
                        <label>可用模型:</label>
                        <small>每行输入一个模型名称</small>
                        <textarea class="api-models">${api.models.join('\n')}</textarea>
                    </div>
                    <div class="button-group">
                        <button class="summarizer-btn test-api-btn">测试API连通性</button>
                        <button class="summarizer-btn delete-btn delete-api-btn">删除</button>
                    </div>
                </div>
            `;
            apiList.appendChild(apiItem);
        });

        // 重新绑定配置项展开/折叠事件
        bindConfigHeaderEvents();
    }

    // 更新提示词列表
    function updatePromptList() {
        const promptList = document.getElementById('prompt-list');
        if (!promptList) return;

        promptList.innerHTML = '';

        configs.prompts.forEach(prompt => {
            const promptItem = document.createElement('div');
            promptItem.className = 'config-item';

            // 获取关联的API
            const api = configs.apis.find(a => a.id === prompt.apiId);

            promptItem.innerHTML = `
                <div class="config-header" data-id="${prompt.id}">
                    ${prompt.name} (${prompt.apiType === 'openai' ? 'OpenAI' : 'Gemini'})
                </div>
                <div class="config-body" id="prompt-${prompt.id}">
                    <div class="form-group">
                        <label>提示词名称:</label>
                        <input type="text" class="prompt-name" value="${prompt.name}">
                    </div>
                    <div class="form-group">
                        <label>提示词内容:</label>
                        <textarea class="prompt-content">${prompt.content}</textarea>
                    </div>
                    <div class="form-group">
                        <label>API类型:</label>
                        <select class="prompt-api-type">
                            <option value="openai" ${prompt.apiType === 'openai' ? 'selected' : ''}>OpenAI兼容</option>
                            <option value="gemini" ${prompt.apiType === 'gemini' ? 'selected' : ''}>Gemini</option>
                        </select>
                    </div>
                    <div class="form-group prompt-api-select-group">
                        <label>选择API配置:</label>
                        <select class="prompt-api-id">
                            ${getApiOptionsHtml(prompt.apiType, prompt.apiId)}
                        </select>
                    </div>
                    <div class="form-group prompt-model-select-group">
                        <label>选择模型:</label>
                        <select class="prompt-model">
                            ${getModelOptionsHtml(prompt.apiId, prompt.model)}
                        </select>
                    </div>
                    <div class="button-group">
                        <button class="summarizer-btn delete-btn delete-prompt-btn">删除</button>
                    </div>
                </div>
            `;
            promptList.appendChild(promptItem);
        });

        // 重新绑定配置项展开/折叠事件
        bindConfigHeaderEvents();
    }

    // 生成API选项HTML
    function getApiOptionsHtml(apiType, selectedApiId) {
        return configs.apis
            .filter(api => api.type === apiType)
            .map(api => `<option value="${api.id}" ${api.id === selectedApiId ? 'selected' : ''}>${api.name}</option>`)
            .join('');
    }

    // 生成模型选项HTML
    function getModelOptionsHtml(apiId, selectedModel) {
        const api = configs.apis.find(a => a.id === apiId);
        if (!api) return '';

        return api.models
            .map(model => `<option value="${model}" ${model === selectedModel ? 'selected' : ''}>${model}</option>`)
            .join('');
    }

    // 绑定事件
    function bindAllEvents() {
        try {
            // 移除可能的旧事件监听器
            const elementsToRebind = [
                'summarizer-close', 'summarizer-header', 'summarize-btn',
                'settings-btn', 'add-api-btn', 'add-prompt-btn',
                'save-settings-btn', 'cancel-settings-btn'
            ];

            elementsToRebind.forEach(id => {
                const el = document.getElementById(id);
                if (el) {
                    const newEl = el.cloneNode(true);
                    el.parentNode.replaceChild(newEl, el);
                }
            });

            // 解绑旧的全局事件
            document.removeEventListener('change', handleDynamicChangeEvents);
            document.removeEventListener('click', handleDynamicClickEvents);
            document.removeEventListener('mousemove', drag);
            document.removeEventListener('mouseup', stopDrag);

            // 重新绑定所有事件
            bindEvents();
        } catch (error) {
            console.error("[网页内容摘要器] 重新绑定事件失败:", error);
        }
    }

    // 绑定事件
    function bindEvents() {
        try {
            // 关闭按钮
            const closeBtn = document.getElementById('summarizer-close');
            if (closeBtn) {
                closeBtn.addEventListener('click', () => {
                    summarizer.style.display = 'none';
                });
            }

            // 拖拽功能
            const header = document.getElementById('summarizer-header');
            if (header) {
                header.addEventListener('mousedown', startDrag);
                document.addEventListener('mousemove', drag);
                document.addEventListener('mouseup', stopDrag);
            }

            // 调整大小功能
            const resizeHandle = document.getElementById('summarizer-resize-handle');
            if (resizeHandle) {
                resizeHandle.addEventListener('mousedown', startResize);
                // 监听器在startResize中动态添加
            }

            // 摘要按钮
            const summarizeBtn = document.getElementById('summarize-btn');
            if (summarizeBtn) {
                summarizeBtn.addEventListener('click', generateSummary);
            }

            // 设置按钮
            const settingsBtn = document.getElementById('settings-btn');
            if (settingsBtn) {
                settingsBtn.addEventListener('click', () => {
                    const settingsModal = document.getElementById('settings-modal');
                    if (settingsModal) {
                        settingsModal.style.display = 'flex';
                    }
                });
            }

            // 设置模态窗口关闭按钮
            const settingsCloseBtn = document.getElementById('settings-close');
            if (settingsCloseBtn) {
                settingsCloseBtn.addEventListener('click', () => {
                    const settingsModal = document.getElementById('settings-modal');
                    if (settingsModal) {
                        settingsModal.style.display = 'none';
                    }
                });
            }

            // 点击模态窗口背景关闭
            const settingsModal = document.getElementById('settings-modal');
            if (settingsModal) {
                settingsModal.addEventListener('click', (e) => {
                    if (e.target === settingsModal) {
                        settingsModal.style.display = 'none';
                    }
                });
            }

            // 设置页签切换
            document.querySelectorAll('.settings-tab').forEach(tab => {
                tab.addEventListener('click', function() {
                    // 激活选中的标签
                    document.querySelectorAll('.settings-tab').forEach(t => t.classList.remove('active'));
                    this.classList.add('active');

                    // 显示对应的面板
                    const tabId = this.getAttribute('data-tab');
                    const targetPanel = document.getElementById(tabId);
                    if (targetPanel) {
                        document.querySelectorAll('.settings-panel').forEach(panel => {
                            panel.classList.remove('active');
                        });
                        targetPanel.classList.add('active');
                    }
                });
            });

            // 绑定配置项展开/折叠事件
            bindConfigHeaderEvents();

            // 添加新API按钮
            const addApiBtn = document.getElementById('add-api-btn');
            if (addApiBtn) {
                addApiBtn.addEventListener('click', addNewApiConfig);
            }

            // 添加新提示词按钮
            const addPromptBtn = document.getElementById('add-prompt-btn');
            if (addPromptBtn) {
                addPromptBtn.addEventListener('click', addNewPrompt);
            }

            // 保存设置按钮
            const saveSettingsBtn = document.getElementById('save-settings-btn');
            if (saveSettingsBtn) {
                saveSettingsBtn.addEventListener('click', saveSettings);
            }

            // 取消按钮
            const cancelSettingsBtn = document.getElementById('cancel-settings-btn');
            if (cancelSettingsBtn) {
                cancelSettingsBtn.addEventListener('click', () => {
                    const settingsModal = document.getElementById('settings-modal');
                    if (settingsModal) {
                        settingsModal.style.display = 'none';
                        updateApiList();
                        updatePromptList();
                    }
                });
            }

            // 使用事件委托处理动态元素的事件
            document.addEventListener('change', handleDynamicChangeEvents);
            document.addEventListener('click', handleDynamicClickEvents);

            console.log("[网页内容摘要器] 事件绑定完成");
        } catch (error) {
            console.error("[网页内容摘要器] 绑定事件失败:", error);
        }
    }

    // 处理动态元素的change事件
    function handleDynamicChangeEvents(e) {
        try {
            if (e.target && e.target.classList.contains('api-type')) {
                const type = e.target.value;
                const container = e.target.closest('.config-body');
                const baseUrlGroup = container.querySelector('.base-url-group');
                const baseUrlInput = container.querySelector('.api-base-url');

                if (type === 'gemini') {
                    baseUrlGroup.style.display = 'none';
                    baseUrlInput.value = DEFAULT_GEMINI_URL;
                    baseUrlInput.setAttribute('readonly', 'readonly');
                } else {
                    baseUrlGroup.style.display = 'block';
                    baseUrlInput.removeAttribute('readonly');
                }
            }

            if (e.target && e.target.classList.contains('prompt-api-type')) {
                const type = e.target.value;
                const container = e.target.closest('.config-body');
                const apiSelect = container.querySelector('.prompt-api-id');

                apiSelect.innerHTML = getApiOptionsHtml(type, '');
                updateModelOptions(apiSelect);
            }

            if (e.target && e.target.classList.contains('prompt-api-id')) {
                updateModelOptions(e.target);
            }
        } catch (error) {
            console.error("[网页内容摘要器] 处理动态change事件失败:", error);
        }
    }

    // 处理动态元素的click事件
    function handleDynamicClickEvents(e) {
        try {
            if (e.target && e.target.classList.contains('delete-api-btn')) {
                const container = e.target.closest('.config-item');
                const id = container.querySelector('.config-header').getAttribute('data-id');

                if (confirm('确定要删除这个API配置吗?这可能会影响依赖它的提示词。')) {
                    deleteApiConfig(id);
                }
            }

            if (e.target && e.target.classList.contains('delete-prompt-btn')) {
                const container = e.target.closest('.config-item');
                const id = container.querySelector('.config-header').getAttribute('data-id');

                if (confirm('确定要删除这个提示词吗?')) {
                    deletePrompt(id);
                }
            }

            if (e.target && e.target.classList.contains('test-api-btn')) {
                const container = e.target.closest('.config-body');
                const apiType = container.querySelector('.api-type').value;
                const baseUrl = container.querySelector('.api-base-url').value;
                const apiKey = container.querySelector('.api-key').value;
                const models = container.querySelector('.api-models').value.split('\n').filter(m => m.trim());

                testApiConnection(apiType, baseUrl, apiKey, models[0] || (apiType === 'openai' ? 'gpt-3.5-turbo' : 'gemini-pro'));
            }
        } catch (error) {
            console.error("[网页内容摘要器] 处理动态click事件失败:", error);
        }
    }

    // 开始拖拽
    function startDrag(e) {
        if (e.target.id === 'summarizer-header' || e.target.id === 'summarizer-title') {
            isDragging = true;
            dragOffsetX = e.clientX - summarizer.getBoundingClientRect().left;
            dragOffsetY = e.clientY - summarizer.getBoundingClientRect().top;
            e.preventDefault();

            // 添加拖拽中的样式
            summarizer.classList.add('dragging');
            document.body.style.userSelect = 'none';
        }
    }

    // 拖拽中
    function drag(e) {
        if (isDragging) {
            const newLeft = e.clientX - dragOffsetX;
            const newTop = e.clientY - dragOffsetY;

            // 限制在视窗范围内
            const maxLeft = window.innerWidth - summarizer.offsetWidth;
            const maxTop = window.innerHeight - summarizer.offsetHeight;

            const limitedLeft = Math.max(0, Math.min(newLeft, maxLeft));
            const limitedTop = Math.max(0, Math.min(newTop, maxTop));

            summarizer.style.left = limitedLeft + 'px';
            summarizer.style.top = limitedTop + 'px';
            summarizer.style.right = 'auto';

            e.preventDefault();
        }
    }

    // 停止拖拽
    function stopDrag() {
        if (isDragging) {
            isDragging = false;

            // 移除拖拽中的样式
            summarizer.classList.remove('dragging');
            document.body.style.userSelect = '';

            // 保存位置到配置中
            configs.position = {
                left: summarizer.style.left,
                top: summarizer.style.top
            };

            GM_setValue("summarizer_configs", configs);
        }
    }

    // 更新模型选项
    function updateModelOptions(apiSelect) {
        const apiId = apiSelect.value;
        const container = apiSelect.closest('.config-body');
        const modelSelect = container.querySelector('.prompt-model');

        modelSelect.innerHTML = getModelOptionsHtml(apiId, '');
    }

    // 添加新API配置
    function addNewApiConfig() {
        const newId = 'api-' + Date.now();
        const newApi = {
            id: newId,
            name: "新API配置",
            type: "openai",
            baseUrl: DEFAULT_OPENAI_URL,
            apiKey: "",
            models: ["gpt-3.5-turbo"]
        };

        configs.apis.push(newApi);
        updateApiList();

        // 重新绑定所有事件
        bindAllEvents();

        // 展开新创建的配置项
        setTimeout(() => {
            const newHeader = document.querySelector(`.config-header[data-id="${newId}"]`);
            if (newHeader) {
                newHeader.click();
            }
        }, 100);
    }

    // 添加新提示词
    function addNewPrompt() {
        const newId = 'prompt-' + Date.now();
        const defaultApi = configs.apis.find(a => a.type === 'openai') || configs.apis[0];
        const newPrompt = {
            id: newId,
            name: "新提示词",
            content: "请总结以下网页内容:",
            apiType: defaultApi ? defaultApi.type : "openai",
            apiId: defaultApi ? defaultApi.id : "",
            model: defaultApi && defaultApi.models.length > 0 ? defaultApi.models[0] : ""
        };

        configs.prompts.push(newPrompt);
        updatePromptList();
        updatePromptSelect();

        // 重新绑定所有事件
        bindAllEvents();

        // 展开新创建的配置项
        setTimeout(() => {
            const newHeader = document.querySelector(`.config-header[data-id="${newId}"]`);
            if (newHeader) {
                newHeader.click();
            }
        }, 100);
    }

    // 删除API配置
    function deleteApiConfig(id) {
        configs.apis = configs.apis.filter(api => api.id !== id);

        // 更新依赖此API的提示词
        configs.prompts.forEach(prompt => {
            if (prompt.apiId === id) {
                const newApi = configs.apis.find(a => a.type === prompt.apiType);
                if (newApi) {
                    prompt.apiId = newApi.id;
                    prompt.model = newApi.models.length > 0 ? newApi.models[0] : "";
                }
            }
        });

        updateApiList();
        updatePromptList();
        updatePromptSelect();

        // 重新绑定所有事件
        bindAllEvents();
    }

    // 删除提示词
    function deletePrompt(id) {
        configs.prompts = configs.prompts.filter(prompt => prompt.id !== id);

        updatePromptList();
        updatePromptSelect();

        // 重新绑定所有事件
        bindAllEvents();
    }

    // 保存设置
    function saveSettings() {
        try {
            // 保存API配置
            const newApis = [];
            document.querySelectorAll('#api-list .config-item').forEach(item => {
                const header = item.querySelector('.config-header');
                const body = item.querySelector('.config-body');
                const id = header.getAttribute('data-id');

                newApis.push({
                    id: id,
                    name: body.querySelector('.api-name').value,
                    type: body.querySelector('.api-type').value,
                    baseUrl: body.querySelector('.api-base-url').value,
                    apiKey: body.querySelector('.api-key').value,
                    models: body.querySelector('.api-models').value.split('\n').filter(m => m.trim())
                });
            });

            // 保存提示词配置
            const newPrompts = [];
            document.querySelectorAll('#prompt-list .config-item').forEach(item => {
                const header = item.querySelector('.config-header');
                const body = item.querySelector('.config-body');
                const id = header.getAttribute('data-id');

                newPrompts.push({
                    id: id,
                    name: body.querySelector('.prompt-name').value,
                    content: body.querySelector('.prompt-content').value,
                    apiType: body.querySelector('.prompt-api-type').value,
                    apiId: body.querySelector('.prompt-api-id').value,
                    model: body.querySelector('.prompt-model').value
                });
            });

            // 保存常规设置
            const hotkey = document.getElementById('hotkey-input').value;
            const autoExpand = document.getElementById('auto-expand').checked;

            // 保存位置信息
            const position = configs.position;

            // 更新配置
            configs.apis = newApis;
            configs.prompts = newPrompts;
            configs.settings.hotkey = hotkey || DEFAULT_HOTKEY;
            configs.settings.autoExpand = autoExpand;
            configs.settings.lastUsedPromptId = document.getElementById('prompt-select').value || configs.settings.lastUsedPromptId;
            configs.position = position;

            // 保存到GM存储
            GM_setValue("summarizer_configs", configs);

            // 更新UI
            const settingsModal = document.getElementById('settings-modal');
            if (settingsModal) {
                settingsModal.style.display = 'none';
            }

            updateApiList();
            updatePromptList();

            // 更新选择框
            updatePromptSelect();

            // 更新热键监听
            document.removeEventListener('keydown', hotkeyHandler);
            addHotkeyListener();

            // 重新绑定所有事件
            bindAllEvents();

            alert('设置已保存');
        } catch (error) {
            console.error("[网页内容摘要器] 保存设置失败:", error);
            alert('保存设置失败: ' + error.message);
        }
    }

    // 更新提示词选择下拉框
    function updatePromptSelect() {
        const promptSelect = document.getElementById('prompt-select');
        if (!promptSelect) return;

        // 保存当前选中的提示词ID
        const selectedPromptId = promptSelect.value || configs.settings.lastUsedPromptId;

        promptSelect.innerHTML = '';
        configs.prompts.forEach(prompt => {
            const option = document.createElement('option');
            option.value = prompt.id;
            option.textContent = prompt.name;
            // 恢复选中状态
            if (prompt.id === selectedPromptId) {
                option.selected = true;
            }
            promptSelect.appendChild(option);
        });
    }

    // 测试API连接
    function testApiConnection(apiType, baseUrl, apiKey, model) {
        if (!apiKey) {
            alert('请先输入API密钥');
            return;
        }

        if (apiType === 'openai') {
            // 测试OpenAI兼容API
            GM_xmlhttpRequest({
                method: 'POST',
                url: baseUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${apiKey}`
                },
                data: JSON.stringify({
                    model: model,
                    messages: [
                        {
                            role: 'user',
                            content: 'Hello, this is a test message. Please respond with "API connection successful".'
                        }
                    ],
                    max_tokens: 50
                }),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.choices && data.choices.length > 0) {
                            alert('API连接成功!');
                        } else {
                            alert('API响应格式不正确: ' + response.responseText);
                        }
                    } catch (e) {
                        alert('API响应解析失败: ' + e.message);
                    }
                },
                onerror: function(response) {
                    alert('API连接失败: ' + response.statusText);
                }
            });
        } else if (apiType === 'gemini') {
            // 测试Gemini API
            const url = `${baseUrl}/v1beta/models/${model}:generateContent?key=${apiKey}`;
            GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({
                    contents: [
                        {
                            parts: [
                                {
                                    text: 'Hello, this is a test message. Please respond with "API connection successful".'
                                }
                            ]
                        }
                    ],
                    generationConfig: {
                        maxOutputTokens: 50
                    }
                }),
                onload: function(response) {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.candidates && data.candidates.length > 0) {
                            alert('API连接成功!');
                        } else {
                            alert('API响应格式不正确: ' + response.responseText);
                        }
                    } catch (e) {
                        alert('API响应解析失败: ' + e.message);
                    }
                },
                onerror: function(response) {
                    alert('API连接失败: ' + response.statusText);
                }
            });
        }
    }

    // 生成摘要
    function generateSummary() {
        // 如果当前正在处理,则表示这是停止操作
        if (isProcessing) {
            stopSummary();
            return;
        }

        // 获取选中的提示词
        const promptId = document.getElementById('prompt-select').value;
        const prompt = configs.prompts.find(p => p.id === promptId);

        if (!prompt) {
            alert('请先选择或创建一个提示词');
            return;
        }

        // 保存当前使用的提示词ID
        configs.settings.lastUsedPromptId = promptId;
        GM_setValue("summarizer_configs", configs);

        // 获取关联的API配置
        const api = configs.apis.find(a => a.id === prompt.apiId);

        if (!api) {
            alert('提示词关联的API配置不存在,请检查设置');
            return;
        }

        if (!api.apiKey) {
            alert('请先设置API密钥');
            return;
        }

        // 获取网页内容
        const pageContent = getPageContent();
        if (!pageContent) {
            alert('无法提取页面内容');
            return;
        }

        // 显示处理中状态
        const summarizeBtn = document.getElementById('summarize-btn');
        const resultDiv = document.getElementById('summarizer-result');

        // 将摘要按钮改为停止按钮
        summarizeBtn.disabled = false;
        summarizeBtn.id = 'stop-btn';
        summarizeBtn.innerHTML = '<span class="spinner"></span>停止摘要';
        resultDiv.style.display = 'block';
        resultDiv.innerHTML = '<div style="text-align: center; padding: 20px;"><span class="spinner"></span> 正在生成摘要,请稍候...</div>';

        isProcessing = true;

        // A根据API类型发送请求
        if (prompt.apiType === 'openai') {
            // 调用OpenAI兼容API
            currentRequest = GM_xmlhttpRequest({
                method: 'POST',
                url: api.baseUrl,
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${api.apiKey}`
                },
                data: JSON.stringify({
                    model: prompt.model,
                    messages: [
                        {
                            role: 'user',
                            content: `${prompt.content}\n\n${pageContent}`
                        }
                    ],
                    max_tokens: 2000  // 增加token数量,确保返回完整内容
                }),
                onload: function(response) {
                    try {
                        // 检查请求是否已经被中止
                        if (this.readyState === 4 && this.status === 0) {
                            resultDiv.innerHTML = '<div style="padding: 15px;">摘要已取消</div>';
                            resetSummaryButton();
                            return;
                        }

                        const data = JSON.parse(response.responseText);
                        if (data.choices && data.choices.length > 0) {
                            const summaryText = data.choices[0].message.content;
                            // 渲染Markdown
                            resultDiv.innerHTML = renderMarkdown(summaryText);
                            // 确保显示完整内容
                            setTimeout(() => {
                                resultDiv.scrollTop = 0;
                            }, 100);
                        } else {
                            resultDiv.innerHTML = '<div style="color: red; padding: 15px;">生成摘要失败: API响应格式不正确</div>';
                            console.error('API响应:', data);
                        }
                    } catch (e) {
                        resultDiv.innerHTML = `<div style="color: red; padding: 15px;">生成摘要失败: ${e.message}</div>`;
                        console.error('API响应解析失败:', e, response.responseText);
                    }

                    resetSummaryButton();
                },
                onerror: function(response) {
                    resultDiv.innerHTML = `<div style="color: red; padding: 15px;">生成摘要失败: ${response.statusText || '请求错误'}</div>`;
                    resetSummaryButton();
                }
            });
        } else if (prompt.apiType === 'gemini') {
            // 调用Gemini API
            const url = `${api.baseUrl}/v1beta/models/${prompt.model}:generateContent?key=${api.apiKey}`;
            currentRequest = GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({
                    contents: [
                        {
                            parts: [
                                {
                                    text: `${prompt.content}\n\n${pageContent}`
                                }
                            ]
                        }
                    ],
                    generationConfig: {
                        maxOutputTokens: 2000  // 增加token数量,确保返回完整内容
                    }
                }),
                onload: function(response) {
                    try {
                        // 检查请求是否已经被中止
                        if (this.readyState === 4 && this.status === 0) {
                            resultDiv.innerHTML = '<div style="padding: 15px;">摘要已取消</div>';
                            resetSummaryButton();
                            return;
                        }

                        const data = JSON.parse(response.responseText);
                        if (data.candidates && data.candidates.length > 0) {
                            const summaryText = data.candidates[0].content.parts[0].text;
                            // 渲染Markdown
                            resultDiv.innerHTML = renderMarkdown(summaryText);
                            // 确保显示完整内容
                            setTimeout(() => {
                                resultDiv.scrollTop = 0;
                            }, 100);
                        } else {
                            resultDiv.innerHTML = '<div style="color: red; padding: 15px;">生成摘要失败: API响应格式不正确</div>';
                            console.error('API响应:', data);
                        }
                    } catch (e) {
                        resultDiv.innerHTML = `<div style="color: red; padding: 15px;">生成摘要失败: ${e.message}</div>`;
                        console.error('API响应解析失败:', e, response.responseText);
                    }

                    resetSummaryButton();
                },
                onerror: function(response) {
                    resultDiv.innerHTML = `<div style="color: red; padding: 15px;">生成摘要失败: ${response.statusText || '请求错误'}</div>`;
                    resetSummaryButton();
                }
            });
        }
    }

    // 停止摘要
    function stopSummary() {
        if (currentRequest && typeof currentRequest.abort === 'function') {
            currentRequest.abort();
        }

        // 重置按钮和状态
        resetSummaryButton();

        console.log("[网页内容摘要器] 摘要已停止");
    }

    // 重置摘要按钮
    function resetSummaryButton() {
        const stopBtn = document.getElementById('stop-btn');
        if (stopBtn) {
            stopBtn.id = 'summarize-btn';
            stopBtn.textContent = '摘要';
            stopBtn.disabled = false;
        }
        isProcessing = false;
        currentRequest = null;
    }

    // 获取页面内容
    function getPageContent() {
        try {
            // 获取主要内容
            let content = '';

            // 尝试获取文章内容区
            const articleElements = document.querySelectorAll('article, .article, .post, .content, main');
            if (articleElements.length > 0) {
                // 使用最大的内容区
                let maxTextElement = null;
                let maxTextLength = 0;

                articleElements.forEach(el => {
                    const textLength = el.textContent.trim().length;
                    if (textLength > maxTextLength) {
                        maxTextLength = textLength;
                        maxTextElement = el;
                    }
                });

                if (maxTextElement) {
                    content = maxTextElement.textContent;
                }
            }

            // 如果没有找到明确的内容区,尝试从段落获取
            if (!content || content.length < 100) {
                const paragraphs = document.querySelectorAll('p');
                const paragraphTexts = [];

                paragraphs.forEach(p => {
                    // 过滤掉短段落和导航/页脚/侧边栏等区域的段落
                    const text = p.textContent.trim();
                    if (text.length > 20 && !isElementInNavOrFooter(p)) {
                        paragraphTexts.push(text);
                    }
                });

                content = paragraphTexts.join('\n\n');
            }

            // 确保内容不为空
            if (!content || content.length < 50) {
                // 退化方案:获取所有可见文本
                content = document.body.innerText;
            }

            // 添加页面标题
            const pageTitle = document.title;
            content = `标题: ${pageTitle}\n\n${content}`;

            // 过滤内容
            content = content
                .replace(/\s+/g, ' ')  // 替换多个空白字符为单个空格
                .replace(/\n{3,}/g, '\n\n')  // 替换多个换行为两个换行
                .trim();

            // 限制内容长度
            const maxLength = 6000;  // 设置合理的长度限制
            if (content.length > maxLength) {
                content = content.substring(0, maxLength) + "\n\n... (内容已截断)";
            }

            return content;
        } catch (e) {
            console.error('获取页面内容失败:', e);
            return document.title + '\n\n' + document.body.innerText.substring(0, 6000);
        }
    }

    // 判断元素是否在导航或页脚中
    function isElementInNavOrFooter(element) {
        let current = element;
        while (current && current !== document.body) {
            const tagName = current.tagName.toLowerCase();
            const className = (current.className || '').toLowerCase();
            const id = (current.id || '').toLowerCase();

            if (
                tagName === 'nav' ||
                tagName === 'footer' ||
                tagName === 'header' ||
                className.includes('nav') ||
                className.includes('menu') ||
                className.includes('footer') ||
                className.includes('sidebar') ||
                id.includes('nav') ||
                id.includes('menu') ||
                id.includes('footer') ||
                id.includes('sidebar')
            ) {
                return true;
            }

            current = current.parentElement;
        }

        return false;
    }

    // 渲染Markdown内容
    function renderMarkdown(text) {
        try {
            // 确保marked库已加载
            if (typeof marked === 'undefined') {
                console.error('[网页内容摘要器] Marked库未加载');
                return text;
            }

            // 配置marked选项
            marked.setOptions({
                breaks: true,
                gfm: true,
                headerIds: false,
                mangle: false,
                sanitize: false,
                smartLists: true
            });

            // 渲染Markdown
            return marked.parse(text);
        } catch (error) {
            console.error('[网页内容摘要器] Markdown渲染失败:', error);
            return text;
        }
    }

    // 初始化
    function initialize() {
        try {
            // 添加CSS样式
            addStyles();

            // 创建UI界面
            createUI();

            // 注册油猴菜单命令
            GM_registerMenuCommand("打开/关闭摘要器", toggleSummarizer);

            // 添加热键监听
            addHotkeyListener();

            console.log("[网页内容摘要器] 初始化完成,按Alt+" + configs.settings.hotkey.toUpperCase() + "打开摘要器");
        } catch (error) {
            console.error("[网页内容摘要器] 初始化失败:", error);
            // 尝试重新初始化
            setTimeout(() => {
                try {
                    console.log("[网页内容摘要器] 尝试重新初始化...");
                    // 清理之前的UI(如果有)
                    const oldSummarizer = document.getElementById('web-summarizer');
                    if (oldSummarizer) {
                        oldSummarizer.remove();
                    }

                    // 重新创建UI
                    createUI();
                    console.log("[网页内容摘要器] 重新初始化完成");
                } catch (e) {
                    console.error("[网页内容摘要器] 重新初始化失败:", e);
                }
            }, 2000);
        }
    }

    // 切换摘要器显示状态
    function toggleSummarizer() {
        try {
            if (!summarizer) {
                console.log("[网页内容摘要器] 摘要器未初始化,尝试重新初始化");
                initialize();
                return;
            }

            if (summarizer.style.display === 'none' || summarizer.style.display === '') {
                summarizer.style.display = 'flex';
            } else {
                summarizer.style.display = 'none';
            }
        } catch (error) {
            console.error("[网页内容摘要器] 切换显示状态失败:", error);
        }
    }

    // 添加热键监听
    function addHotkeyListener() {
        try {
            // 先移除旧的事件监听(如果有)以避免重复
            document.removeEventListener('keydown', hotkeyHandler);

            // 添加新的事件监听
            document.addEventListener('keydown', hotkeyHandler);
        } catch (error) {
            console.error("[网页内容摘要器] 添加热键监听失败:", error);
        }
    }

    // 热键处理函数
    function hotkeyHandler(e) {
        if (e.altKey && e.key.toLowerCase() === configs.settings.hotkey.toLowerCase()) {
            e.preventDefault();
            toggleSummarizer();
        }
    }

    // 绑定配置项展开/折叠事件
    function bindConfigHeaderEvents() {
        document.querySelectorAll('.config-header').forEach(header => {
            // 移除旧事件以避免重复绑定
            const newHeader = header.cloneNode(true);
            header.parentNode.replaceChild(newHeader, header);

            newHeader.addEventListener('click', function() {
                const id = this.getAttribute('data-id');
                const body = this.nextElementSibling;

                if (body.classList.contains('expanded')) {
                    body.classList.remove('expanded');
                } else {
                    if (!configs.settings.autoExpand) {
                        document.querySelectorAll('.config-body').forEach(b => b.classList.remove('expanded'));
                    }
                    body.classList.add('expanded');
                }
            });
        });
    }

    // 开始调整大小
    let isResizing = false;
    let startX = 0;
    let startWidth = 0;

    function startResize(e) {
        // 防止事件传播
        e.stopPropagation();
        e.preventDefault();

        isResizing = true;
        startX = e.clientX;
        startWidth = summarizer.offsetWidth;

        // 添加调整大小时的事件监听
        document.addEventListener('mousemove', resize);
        document.addEventListener('mouseup', stopResize);

        // 添加调整大小时的样式
        document.body.style.userSelect = 'none';
        summarizer.classList.add('resizing');
    }

    // 调整大小中
    function resize(e) {
        if (!isResizing) return;

        // 计算新宽度 - 这里与之前不同,向左拖动会增加宽度
        const changeX = startX - e.clientX;
        const newWidth = Math.max(MIN_WIDTH, startWidth + changeX);

        // 限制最大宽度
        const maxWidth = window.innerWidth * 0.8;
        summarizer.style.width = Math.min(newWidth, maxWidth) + 'px';
    }

    // 停止调整大小
    function stopResize() {
        if (!isResizing) return;

        isResizing = false;

        // 移除事件监听
        document.removeEventListener('mousemove', resize);
        document.removeEventListener('mouseup', stopResize);

        // 移除样式
        document.body.style.userSelect = '';
        summarizer.classList.remove('resizing');

        // 保存大小到配置
        configs.size = {
            width: summarizer.offsetWidth
        };

        GM_setValue("summarizer_configs", configs);
    }

    // 主函数
    // 等待DOM完全加载后再初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }
})();