LeetCode题单助手

LeetCode中英文站点增强工具,智能转换链接并显示做题状态

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         LeetCode题单助手
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  LeetCode中英文站点增强工具,智能转换链接并显示做题状态
// @author       You
// @match        https://leetcode.cn/*
// @match        https://leetcode.com/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_notification
// @connect      leetcode.com
// @connect      leetcode.cn
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    const CONFIG = {
        STORAGE_KEY: 'leetcode_progress_data',
        LAST_SYNC_KEY: 'leetcode_last_sync',
        SETTINGS_KEY: 'leetcode_settings',
        CACHE_DURATION: 2 * 60 * 60 * 1000,
        VERSION: '1.1'
    };

    const DEFAULT_SETTINGS = {
        autoSync: true,
        showDifficulty: true,
        showProgress: true,
        compactMode: false,
        hideCompleted: false,
        customCSS: ''
    };

    const DIFFICULTY_MAP = {
        1: { text: 'Easy', color: '#00b8a3' },
        2: { text: 'Med.', color: '#ffc01e' },
        3: { text: 'Hard', color: '#ff375f' }
    };

    const STATUS_MAP = {
        'ac': { emoji: '✅', text: 'Solved', color: '#52c41a', bgColor: '#f6ffed' },
        'notac': { emoji: '❌', text: 'Attempted', color: '#ff4d4f', bgColor: '#fff2f0' },
        null: { emoji: '⭕', text: 'Not Attempted', color: '#8c8c8c', bgColor: '#fafafa' }
    };

    console.log('🚀 LeetCode题单助手启动,版本:', CONFIG.VERSION);

    GM_addStyle(`
        .lc-converted {
            padding-left: 8px !important;
            transition: all 0.3s ease;
        }

        .lc-converted:hover {
            background-color: #f0f8ff !important;
            transform: translateX(2px);
        }

        .lc-status-container {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            margin-right: 8px;
        }

        .lc-status-badge {
            display: inline-flex;
            align-items: center;
            padding: 2px 6px;
            border-radius: 12px;
            font-size: 11px;
            font-weight: 500;
            border: 1px solid #d9d9d9;
            transition: transform 0.2s ease;
        }

        .lc-status-badge:hover {
            transform: scale(1.1);
        }

        .lc-difficulty-badge {
            display: inline-flex;
            align-items: center;
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 11px;
            font-weight: bold;
            color: white;
            min-width: 32px;
            justify-content: center;
        }

        .lc-control-panel {
            position: fixed;
            top: 80px;
            right: 20px;
            z-index: 9999;
            background: #ffffff;
            color: #333333;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            overflow: hidden;
            min-width: 200px;
            transition: all 0.3s ease;
        }

        [data-theme="dark"] .lc-control-panel,
        .dark .lc-control-panel,
        body[class*="dark"] .lc-control-panel {
            background: #1f1f1f !important;
            color: #ffffff !important;
            border: 1px solid #333;
        }

        .lc-panel-header {
            background: linear-gradient(135deg, #1890ff, #40a9ff);
            color: white;
            padding: 12px 16px;
            font-weight: bold;
            font-size: 14px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            cursor: pointer;
            user-select: none;
        }

        .lc-panel-toggle {
            font-size: 12px;
            opacity: 0.8;
            transition: transform 0.3s ease;
        }

        .lc-panel-toggle.collapsed {
            transform: rotate(180deg);
        }

        .lc-panel-content {
            padding: 12px;
            transition: all 0.3s ease;
            overflow: hidden;
        }

        .lc-control-panel.collapsed .lc-panel-content {
            max-height: 0;
            padding: 0 12px;
            opacity: 0;
        }

        .lc-control-panel:not(.collapsed) .lc-panel-content {
            max-height: 500px;
            opacity: 1;
        }

        .lc-button {
            width: 100%;
            padding: 8px 12px;
            margin-bottom: 8px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            gap: 6px;
        }

        .lc-button-primary {
            background: #1890ff;
            color: white;
        }

        .lc-button-primary:hover {
            background: #40a9ff;
        }

        .lc-button-secondary {
            background: #f0f0f0;
            color: #333;
        }

        .lc-button-secondary:hover {
            background: #e0e0e0;
        }

        [data-theme="dark"] .lc-button-secondary,
        .dark .lc-button-secondary,
        body[class*="dark"] .lc-button-secondary {
            background: #333 !important;
            color: #fff !important;
        }

        [data-theme="dark"] .lc-button-secondary:hover,
        .dark .lc-button-secondary:hover,
        body[class*="dark"] .lc-button-secondary:hover {
            background: #444 !important;
        }

        .lc-stats {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 6px;
            margin: 8px 0;
            font-size: 10px;
        }

        .lc-stat-item {
            text-align: center;
            padding: 4px;
            border-radius: 4px;
            background: #f9f9f9;
            color: #333;
        }

        [data-theme="dark"] .lc-stat-item,
        .dark .lc-stat-item,
        body[class*="dark"] .lc-stat-item {
            background: #333 !important;
            color: #fff !important;
        }

        .lc-progress-bar {
            width: 100%;
            height: 6px;
            background: #f0f0f0;
            border-radius: 3px;
            overflow: hidden;
            margin: 8px 0;
        }

        .lc-progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #52c41a, #73d13d);
            transition: width 0.3s ease;
        }

        .lc-settings {
            border-top: 1px solid #f0f0f0;
            margin-top: 8px;
            padding-top: 8px;
        }

        [data-theme="dark"] .lc-settings,
        .dark .lc-settings,
        body[class*="dark"] .lc-settings {
            border-top-color: #444 !important;
        }

        .lc-setting-item {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 6px;
            font-size: 11px;
            color: inherit;
        }

        .lc-switch {
            position: relative;
            width: 32px;
            height: 18px;
            background: #ccc;
            border-radius: 9px;
            cursor: pointer;
            transition: background 0.2s;
        }

        .lc-switch.active {
            background: #1890ff;
        }

        .lc-switch::after {
            content: '';
            position: absolute;
            top: 2px;
            left: 2px;
            width: 14px;
            height: 14px;
            background: white;
            border-radius: 50%;
            transition: transform 0.2s;
        }

        .lc-switch.active::after {
            transform: translateX(14px);
        }

        .lc-notification {
            position: fixed;
            top: 20px;
            right: 20px;
            z-index: 10000;
            background: white;
            border-left: 4px solid #52c41a;
            padding: 16px 20px;
            border-radius: 4px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            max-width: 300px;
            animation: slideIn 0.3s ease;
        }

        @keyframes slideIn {
            from { transform: translateX(100%); opacity: 0; }
            to { transform: translateX(0); opacity: 1; }
        }

        .lc-hidden {
            opacity: 0.3;
            filter: grayscale(50%);
        }

        .lc-jump-button {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 4px 8px;
            margin-left: 12px;
            background: linear-gradient(135deg, #1890ff, #40a9ff);
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 12px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s ease;
            text-decoration: none;
            box-shadow: 0 2px 4px rgba(24, 144, 255, 0.3);
        }

        .lc-jump-button:hover {
            background: linear-gradient(135deg, #40a9ff, #69c0ff);
            transform: translateY(-1px);
            box-shadow: 0 4px 8px rgba(24, 144, 255, 0.4);
            color: white;
            text-decoration: none;
        }

        .lc-jump-button:active {
            transform: translateY(0);
        }

        /* 新增的复制按钮样式 */
        .lc-copy-buttons-container {
            display: inline-flex;
            align-items: center;
            gap: 8px;
            margin-left: 12px;
            flex-wrap: wrap;
        }

        .lc-copy-button {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 6px 10px;
            background: linear-gradient(135deg, #52c41a, #73d13d);
            color: white;
            border: none;
            border-radius: 6px;
            font-size: 11px;
            font-weight: 500;
            cursor: pointer;
            transition: all 0.2s ease;
            box-shadow: 0 2px 4px rgba(82, 196, 26, 0.3);
            white-space: nowrap;
        }

        .lc-copy-button:hover {
            background: linear-gradient(135deg, #73d13d, #95de64);
            transform: translateY(-1px);
            box-shadow: 0 4px 8px rgba(82, 196, 26, 0.4);
        }

        .lc-copy-button:active {
            transform: translateY(0);
        }

        .lc-copy-button.copying {
            background: #faad14;
            transform: none;
        }

        .lc-copy-button.success {
            background: #52c41a;
            transform: none;
        }

        .lc-copy-button.error {
            background: #ff4d4f;
            transform: none;
        }

        /* 适配小屏幕 */
        @media (max-width: 768px) {
            .lc-copy-buttons-container {
                margin-left: 0;
                margin-top: 8px;
                width: 100%;
            }

            .lc-copy-button {
                flex: 1;
                justify-content: center;
                min-width: 0;
            }
        }
    `);

    class LeetCodeUltimate {
        constructor() {
            this.settings = { ...DEFAULT_SETTINGS, ...GM_getValue(CONFIG.SETTINGS_KEY, {}) };
            this.progressData = GM_getValue(CONFIG.STORAGE_KEY, {});
            this.isInternational = window.location.hostname === 'leetcode.com';
            this.isChinese = window.location.hostname === 'leetcode.cn';
            this.stats = { total: 0, solved: 0, attempted: 0 };

            this.init();
        }

        init() {
            if (this.isInternational) {
                this.handleInternationalSite();
            } else if (this.isChinese) {
                if (this.isProblemsDetailPage()) {
                    this.addJumpButton();
                    return;
                }
                this.handleChineseSite();
            }

            if (this.settings.customCSS) {
                GM_addStyle(this.settings.customCSS);
            }
        }

        isProblemsDetailPage() {
            const path = window.location.pathname;
            return /^\/problems\/[^\/]+\/(description|solutions|discuss|submissions|editorial)/.test(path) ||
                   /^\/problems\/[^\/]+\/$/.test(path);
        }

        addJumpButton() {
            setTimeout(() => {
                this.insertJumpButton();
            }, 1500);

            const observer = new MutationObserver(() => {
                if (!document.querySelector('.lc-jump-button')) {
                    setTimeout(() => this.insertJumpButton(), 500);
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        insertJumpButton() {
            if (document.querySelector('.lc-jump-button')) {
                return;
            }

            const titleSelectors = [
                'h1[data-cy="question-title"]',
                '.question-title h1',
                '.question-content h1',
                '.css-v3d350',
                '[class*="title"]'
            ];

            let titleElement = null;
            for (const selector of titleSelectors) {
                try {
                    titleElement = document.querySelector(selector);
                    if (titleElement) break;
                } catch (e) {
                    continue;
                }
            }

            if (!titleElement) {
                const h1Elements = document.querySelectorAll('h1');
                for (const h1 of h1Elements) {
                    const text = h1.textContent.trim();
                    if (/^\d+\./.test(text) && text.length > 5) {
                        titleElement = h1;
                        break;
                    }
                }
            }

            if (!titleElement) {
                return;
            }

            const currentUrl = window.location.href;
            const targetUrl = this.getTargetUrl(currentUrl);

            if (!targetUrl) {
                return;
            }

            // 创建按钮容器
            const buttonsContainer = document.createElement('div');
            buttonsContainer.style.display = 'inline-flex';
            buttonsContainer.style.alignItems = 'center';
            buttonsContainer.style.gap = '8px';
            buttonsContainer.style.marginLeft = '12px';
            buttonsContainer.style.flexWrap = 'wrap';

            // 跳转按钮
            const jumpButton = document.createElement('a');
            jumpButton.className = 'lc-jump-button';
            jumpButton.href = targetUrl;
            jumpButton.target = '_blank';
            jumpButton.rel = 'noopener noreferrer';

            if (this.isChinese) {
                jumpButton.innerHTML = `
                    <span>🌍</span>
                    <span>English</span>
                `;
                jumpButton.title = '在LeetCode国际站打开 / Open on LeetCode.com';
            } else {
                jumpButton.innerHTML = `
                    <span>🇨🇳</span>
                    <span>中文</span>
                `;
                jumpButton.title = '在LeetCode中文站打开 / Open on LeetCode.cn';
            }

            buttonsContainer.appendChild(jumpButton);

            // 如果是国际站,添加复制按钮
            if (this.isInternational) {
                const copyButtonsContainer = this.createCopyButtons();
                buttonsContainer.appendChild(copyButtonsContainer);
            }

            titleElement.appendChild(buttonsContainer);
        }

        createCopyButtons() {
            const container = document.createElement('div');
            container.className = 'lc-copy-buttons-container';

            const buttons = [
                {
                    text: '📋 Title',
                    title: 'Copy problem title to clipboard',
                    action: () => this.copyTitle()
                },
                {
                    text: '📄 Problem',
                    title: 'Copy problem description to clipboard',
                    action: () => this.copyProblem()
                },
                {
                    text: '💻 Solution',
                    title: 'Copy my solution to clipboard',
                    action: () => this.copySolution()
                }
            ];

            buttons.forEach(buttonConfig => {
                const button = document.createElement('button');
                button.className = 'lc-copy-button';
                button.innerHTML = buttonConfig.text;
                button.title = buttonConfig.title;
                button.onclick = buttonConfig.action;
                container.appendChild(button);
            });

            return container;
        }

        async copyTitle() {
            const button = event.target.closest('.lc-copy-button');
            const originalText = button.innerHTML;

            try {
                button.classList.add('copying');
                button.innerHTML = '⏳ Copying...';

                let titleText = '';

                // 根据HTML结构,直接查找正确的标题元素
                const titleElement = document.querySelector('.text-title-large a');
                if (titleElement) {
                    titleText = titleElement.textContent.trim();
                }

                // 备用方案
                if (!titleText) {
                    const altSelectors = [
                        '.text-title-large',
                        '[class*="title-large"]',
                        'h1',
                        '.font-semibold a'
                    ];

                    for (const selector of altSelectors) {
                        const element = document.querySelector(selector);
                        if (element) {
                            let text = element.textContent.trim();
                            // 检查是否是题目标题格式
                            if (/^\d+\.\s*.+/.test(text) && text.length > 5) {
                                titleText = text;
                                break;
                            }
                        }
                    }
                }

                // 最后的备用方案:从URL提取
                if (!titleText) {
                    const urlMatch = window.location.pathname.match(/\/problems\/([^\/]+)/);
                    if (urlMatch) {
                        const slug = urlMatch[1];
                        // 从页面标题提取
                        const pageTitle = document.title;
                        const match = pageTitle.match(/^\d+\.\s*(.+?)\s*-\s*LeetCode/);
                        if (match) {
                            titleText = match[1]; // 只要题目名称,不要数字
                        } else {
                            // 从slug转换
                            titleText = slug.split('-').map(word =>
                                word.charAt(0).toUpperCase() + word.slice(1)
                            ).join(' ');
                        }
                    }
                }

                if (!titleText) {
                    throw new Error('Could not find problem title');
                }

                // 清理按钮文本和其他无关内容,同时移除题目编号
                titleText = titleText
                    .replace(/🌍\s*English|🇨🇳\s*中文|📋\s*Title|📄\s*Problem|💻\s*Solution/g, '')
                    .replace(/Solved\s*$/i, '')
                    .replace(/^\d+\.\s*/, '') // 移除开头的数字和点号
                    .trim();

                await navigator.clipboard.writeText(titleText);

                button.classList.remove('copying');
                button.classList.add('success');
                button.innerHTML = '✅ Copied!';

                this.showNotification('Title Copied', `"${titleText}" has been copied to clipboard`, 'success');

            } catch (error) {
                console.error('Copy title failed:', error);
                button.classList.remove('copying');
                button.classList.add('error');
                button.innerHTML = '❌ Failed';
                this.showNotification('Copy Failed', 'Failed to copy title to clipboard', 'error');
            }

            setTimeout(() => {
                button.className = 'lc-copy-button';
                button.innerHTML = originalText;
            }, 2000);
        }

        async copyProblem() {
            const button = event.target.closest('.lc-copy-button');
            const originalText = button.innerHTML;

            try {
                button.classList.add('copying');
                button.innerHTML = '⏳ Copying...';

                // 根据HTML结构查找题目描述容器
                let descriptionElement = document.querySelector('.elfjS[data-track-load="description_content"]');

                if (!descriptionElement) {
                    // 备用选择器
                    const backupSelectors = [
                        '.elfjS',
                        '[data-track-load="description_content"]',
                        '[class*="content"]',
                        '.description'
                    ];

                    for (const selector of backupSelectors) {
                        descriptionElement = document.querySelector(selector);
                        if (descriptionElement && descriptionElement.textContent.trim().length > 50) {
                            break;
                        }
                    }
                }

                if (!descriptionElement) {
                    throw new Error('Could not find problem description');
                }

                // 转换为Markdown格式
                const problemMarkdown = this.convertToMarkdown(descriptionElement);

                if (!problemMarkdown || problemMarkdown.length < 20) {
                    throw new Error('Problem description is too short or empty');
                }

                await navigator.clipboard.writeText(problemMarkdown);

                button.classList.remove('copying');
                button.classList.add('success');
                button.innerHTML = '✅ Copied!';

                this.showNotification('Problem Copied', `Problem description (${problemMarkdown.split('\n').length} lines, ${problemMarkdown.length} chars) has been copied with Markdown formatting`, 'success');

            } catch (error) {
                console.error('Copy problem failed:', error);
                button.classList.remove('copying');
                button.classList.add('error');
                button.innerHTML = '❌ Failed';
                this.showNotification('Copy Failed', error.message || 'Failed to copy problem description to clipboard', 'error');
            }

            setTimeout(() => {
                button.className = 'lc-copy-button';
                button.innerHTML = originalText;
            }, 2000);
        }

        convertToMarkdown(element) {
            // 创建元素副本以避免修改原DOM
            const clonedElement = element.cloneNode(true);

            // 移除不需要的元素
            const unwantedSelectors = [
                '.monaco-editor',
                '.CodeMirror',
                'button',
                '.lc-copy-button',
                '.lc-jump-button',
                '[class*="editor"]',
                '[class*="playground"]',
                '[class*="testcase"]',
                '[class*="submit"]',
                '[class*="run"]',
                'script',
                'style'
            ];

            unwantedSelectors.forEach(sel => {
                const elements = clonedElement.querySelectorAll(sel);
                elements.forEach(el => el.remove());
            });

            // 转换为Markdown
            let markdown = this.elementToMarkdown(clonedElement);

            // 最小化清理,保持原始格式
            markdown = markdown
                .replace(/\n\n\n+/g, '\n\n') // 只移除多余的连续空行
                .replace(/^\s+|\s+$/g, ''); // 只移除首尾空白

            return markdown;
        }

        elementToMarkdown(element) {
            let markdown = '';

            for (const node of element.childNodes) {
                if (node.nodeType === Node.TEXT_NODE) {
                    const text = node.textContent;
                    if (text) {
                        markdown += text;
                    }
                } else if (node.nodeType === Node.ELEMENT_NODE) {
                    const tagName = node.tagName.toLowerCase();

                    switch (tagName) {
                        case 'p':
                            const pContent = this.elementToMarkdown(node);
                            if (pContent.trim()) {
                                markdown += `${pContent}\n\n`;
                            }
                            break;
                        case 'strong':
                        case 'b':
                            const strongText = node.textContent;
                            if (strongText) {
                                markdown += `**${strongText}**`;
                            }
                            break;
                        case 'em':
                        case 'i':
                            const emText = node.textContent;
                            if (emText) {
                                markdown += `*${emText}*`;
                            }
                            break;
                        case 'code':
                            const codeText = node.textContent;
                            if (codeText) {
                                if (node.parentNode && node.parentNode.tagName.toLowerCase() === 'pre') {
                                    markdown += codeText; // 在pre中的code不需要额外处理
                                } else {
                                    markdown += `\`${codeText}\``;
                                }
                            }
                            break;
                        case 'pre':
                            const preContent = node.textContent;
                            if (preContent) {
                                markdown += `\n\`\`\`\n${preContent}\n\`\`\`\n\n`;
                            }
                            break;
                        case 'ul':
                            markdown += '\n';
                            const listItems = node.querySelectorAll('li');
                            listItems.forEach(li => {
                                const liText = li.textContent;
                                if (liText) {
                                    markdown += `* ${liText}\n`;
                                }
                            });
                            markdown += '\n';
                            break;
                        case 'ol':
                            markdown += '\n';
                            const orderedItems = node.querySelectorAll('li');
                            orderedItems.forEach((li, index) => {
                                const liText = li.textContent;
                                if (liText) {
                                    markdown += `${index + 1}. ${liText}\n`;
                                }
                            });
                            markdown += '\n';
                            break;
                        case 'blockquote':
                            const blockText = node.textContent;
                            if (blockText) {
                                const lines = blockText.split('\n');
                                lines.forEach(line => {
                                    if (line.trim()) {
                                        markdown += `> ${line.trim()}\n`;
                                    }
                                });
                                markdown += '\n';
                            }
                            break;
                        case 'img':
                            const alt = node.getAttribute('alt') || '';
                            const src = node.getAttribute('src') || '';
                            if (src) {
                                markdown += `![${alt}](${src})`;
                            }
                            break;
                        case 'a':
                            const linkText = node.textContent;
                            const href = node.getAttribute('href') || '';
                            if (linkText && href) {
                                markdown += `[${linkText}](${href})`;
                            } else if (linkText) {
                                markdown += linkText;
                            }
                            break;
                        case 'br':
                            markdown += '\n';
                            break;
                        case 'hr':
                            markdown += '\n---\n\n';
                            break;
                        case 'h1':
                        case 'h2':
                        case 'h3':
                        case 'h4':
                        case 'h5':
                        case 'h6':
                            const level = parseInt(tagName.charAt(1));
                            const headingText = node.textContent;
                            if (headingText) {
                                markdown += `\n${'#'.repeat(level)} ${headingText}\n\n`;
                            }
                            break;
                        case 'table':
                            markdown += this.tableToMarkdown(node);
                            break;
                        case 'div':
                        case 'span':
                            // 对于div和span,递归处理子元素
                            markdown += this.elementToMarkdown(node);
                            break;
                        default:
                            // 对于其他元素,递归处理子元素
                            markdown += this.elementToMarkdown(node);
                            break;
                    }
                }
            }

            return markdown;
        }

        tableToMarkdown(tableElement) {
            let markdown = '\n';
            const rows = tableElement.querySelectorAll('tr');

            if (rows.length === 0) return '';

            rows.forEach((row, rowIndex) => {
                const cells = row.querySelectorAll('td, th');
                if (cells.length === 0) return;

                const cellContents = Array.from(cells).map(cell => {
                    return cell.textContent.trim().replace(/\|/g, '\\|'); // 转义管道符
                });

                markdown += '| ' + cellContents.join(' | ') + ' |\n';

                // 添加表头分隔符
                if (rowIndex === 0) {
                    markdown += '| ' + cellContents.map(() => '---').join(' | ') + ' |\n';
                }
            });

            markdown += '\n';
            return markdown;
        }

        async copySolution() {
            const button = event.target.closest('.lc-copy-button');
            const originalText = button.innerHTML;

            try {
                button.classList.add('copying');
                button.innerHTML = '⏳ Copying...';

                let solutionText = '';

                // 方法1: 从Monaco Editor的view-lines获取完整代码
                const viewLines = document.querySelector('.view-lines');
                if (viewLines) {
                    const lineElements = viewLines.querySelectorAll('.view-line');

                    if (lineElements.length > 0) {
                        const lines = [];
                        lineElements.forEach((lineElement) => {
                            // 获取每行的文本内容,保持原始格式
                            function extractTextFromNode(node) {
                                if (node.nodeType === Node.TEXT_NODE) {
                                    return node.textContent;
                                } else if (node.nodeType === Node.ELEMENT_NODE) {
                                    let text = '';
                                    for (const child of node.childNodes) {
                                        text += extractTextFromNode(child);
                                    }
                                    return text;
                                }
                                return '';
                            }

                            let lineText = extractTextFromNode(lineElement);

                            // 如果这种方法失败,使用innerText作为备用
                            if (!lineText) {
                                lineText = lineElement.innerText || lineElement.textContent || '';
                            }

                            lines.push(lineText);
                        });

                        solutionText = lines.join('\n');
                    }
                }

                // 方法2: 尝试从Monaco Editor API获取
                if (!solutionText && window.monaco && window.monaco.editor) {
                    const editors = window.monaco.editor.getEditors();

                    if (editors.length > 0) {
                        for (let i = 0; i < editors.length; i++) {
                            try {
                                const editorValue = editors[i].getValue();
                                if (editorValue && editorValue.trim().length > solutionText.length) {
                                    solutionText = editorValue;
                                }
                            } catch (e) {
                                // 跳过这个编辑器,继续下一个
                            }
                        }
                    }
                }

                // 方法3: 尝试从textarea获取
                if (!solutionText || solutionText.trim().length < 10) {
                    const textareas = document.querySelectorAll('textarea');

                    textareas.forEach((textarea) => {
                        if (textarea.value && textarea.value.trim().length > 10) {
                            if (textarea.value.length > solutionText.length) {
                                solutionText = textarea.value;
                            }
                        }
                    });
                }

                // 方法4: 尝试从可编辑的div获取
                if (!solutionText || solutionText.trim().length < 10) {
                    const editableDivs = document.querySelectorAll('[contenteditable="true"]');

                    editableDivs.forEach((div) => {
                        const content = div.textContent || div.innerText;
                        if (content && content.trim().length > 10) {
                            // 检查是否看起来像代码
                            if (content.includes('def ') || content.includes('class ') ||
                                content.includes('function') || content.includes('var ') ||
                                content.includes('let ') || content.includes('const ') ||
                                content.includes('public ') || content.includes('private ') ||
                                content.includes('return ') || content.includes('if ') ||
                                content.includes('for ') || content.includes('while ')) {

                                if (content.length > solutionText.length) {
                                    solutionText = content;
                                }
                            }
                        }
                    });
                }

                // 方法5: 尝试从CodeMirror获取
                if (!solutionText || solutionText.trim().length < 10) {
                    const codeMirrorElements = document.querySelectorAll('.CodeMirror');

                    codeMirrorElements.forEach((cm) => {
                        if (cm.CodeMirror) {
                            try {
                                const cmValue = cm.CodeMirror.getValue();
                                if (cmValue && cmValue.length > solutionText.length) {
                                    solutionText = cmValue;
                                }
                            } catch (e) {
                                // 跳过这个CodeMirror实例
                            }
                        }
                    });
                }

                // 清理和验证代码
                if (solutionText) {
                    solutionText = solutionText.trim();
                }

                if (!solutionText || solutionText.trim().length < 5) {
                    throw new Error('Could not find solution code or editor is empty. Please make sure you have written code in the editor.');
                }

                await navigator.clipboard.writeText(solutionText);

                button.classList.remove('copying');
                button.classList.add('success');
                button.innerHTML = '✅ Copied!';

                const lineCount = solutionText.split('\n').length;
                this.showNotification('Solution Copied', `Your solution (${lineCount} lines, ${solutionText.length} characters) has been copied to clipboard`, 'success');

            } catch (error) {
                console.error('Copy solution failed:', error);
                button.classList.remove('copying');
                button.classList.add('error');
                button.innerHTML = '❌ Failed';
                this.showNotification('Copy Failed', error.message || 'Failed to copy solution. Make sure you have code in the editor.', 'error');
            }

            setTimeout(() => {
                button.className = 'lc-copy-button';
                button.innerHTML = originalText;
            }, 2000);
        }

        getTargetUrl(currentUrl) {
            try {
                const url = new URL(currentUrl);
                const pathname = url.pathname;
                const search = url.search;
                const hash = url.hash;

                if (this.isChinese) {
                    return `https://leetcode.com${pathname}${search}${hash}`;
                } else if (this.isInternational) {
                    return `https://leetcode.cn${pathname}${search}${hash}`;
                }

                return null;
            } catch (e) {
                return null;
            }
        }

        handleInternationalSite() {
            if (this.isProblemsDetailPage()) {
                this.addJumpButton();
                return;
            }

            setTimeout(() => {
                this.createControlPanel();
                if (this.settings.autoSync) {
                    this.checkAndAutoSync();
                }
            }, 2000);
        }

        createControlPanel() {
            const panel = document.createElement('div');
            panel.className = 'lc-control-panel';
            panel.innerHTML = `
                <div class="lc-panel-header">
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <span>📚</span>
                        <span>LeetCode Supporter / 题单助手</span>
                    </div>
                    <span class="lc-panel-toggle">▼</span>
                </div>
                <div class="lc-panel-content">
                    <button class="lc-button lc-button-primary" id="lc-sync-btn">
                        <span>📊</span>
                        <span>同步到中文站 / Sync to CN</span>
                    </button>
                    <button class="lc-button lc-button-secondary" id="lc-clear-btn">
                        <span>🗑️</span>
                        <span>清除缓存 / Clear Cache</span>
                    </button>
                    <div class="lc-stats" id="lc-stats">
                        <div class="lc-stat-item">
                            <div>📈 已同步 / Synced</div>
                            <div id="lc-sync-count">0</div>
                        </div>
                        <div class="lc-stat-item">
                            <div>⏰ 最后同步 / Last Sync</div>
                            <div id="lc-last-sync">未同步 / Not Synced</div>
                        </div>
                    </div>
                    <div class="lc-settings">
                        <div class="lc-setting-item">
                            <span>自动同步 / Auto Sync</span>
                            <div class="lc-switch ${this.settings.autoSync ? 'active' : ''}" data-setting="autoSync"></div>
                        </div>
                    </div>
                </div>
            `;

            document.body.appendChild(panel);
            this.bindInternationalEvents(panel);
            this.updateSyncStats();

            this.addPanelToggle(panel);
        }

        bindInternationalEvents(panel) {
            panel.querySelector('#lc-sync-btn').onclick = () => this.syncProgress();
            panel.querySelector('#lc-clear-btn').onclick = () => this.clearCache();

            panel.querySelectorAll('.lc-switch').forEach(sw => {
                sw.onclick = () => this.toggleSetting(sw.dataset.setting, sw);
            });
        }

        async checkAndAutoSync() {
            const lastSync = GM_getValue(CONFIG.LAST_SYNC_KEY, 0);
            if (Date.now() - lastSync > CONFIG.CACHE_DURATION) {
                setTimeout(() => this.syncProgress(), 3000);
            }
        }

        async syncProgress() {
            const button = document.querySelector('#lc-sync-btn');
            const originalHTML = button.innerHTML;
            button.innerHTML = '<span>⏳</span><span>同步中...</span>';
            button.disabled = true;

            try {
                let data = await this.fetchFromAPI();
                if (!data) {
                    data = await this.parseFromPage();
                }
                if (!data) {
                    data = await this.extractFromLocalStorage();
                }

                if (data && Object.keys(data).length > 0) {
                    this.saveProgressData(data);
                    button.innerHTML = '<span>✅</span><span>同步成功</span>';
                    this.showNotification('同步成功! / Sync Success!', `已同步 ${Object.keys(data).length} 个题目的状态 / Synced ${Object.keys(data).length} problems`, 'success');
                } else {
                    throw new Error('无法获取有效数据 / Cannot get valid data');
                }

            } catch (error) {
                console.error('❌ 同步失败:', error);
                button.innerHTML = '<span>❌</span><span>同步失败</span>';
                this.showNotification('同步失败 / Sync Failed', error.message || '同步过程中出现错误 / Error during sync', 'error');
            }

            setTimeout(() => {
                button.innerHTML = originalHTML;
                button.disabled = false;
                this.updateSyncStats();
            }, 2000);
        }

        async fetchFromAPI() {
            return new Promise((resolve) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: 'https://leetcode.com/api/problems/all/',
                    headers: {
                        'User-Agent': navigator.userAgent,
                        'Referer': 'https://leetcode.com/problemset/all/'
                    },
                    timeout: 10000,
                    onload: (response) => {
                        try {
                            if (response.status === 200) {
                                const apiData = JSON.parse(response.responseText);
                                if (apiData.stat_status_pairs) {
                                    const data = {};
                                    apiData.stat_status_pairs.forEach(item => {
                                        const slug = item.stat.question__title_slug;
                                        data[slug] = {
                                            titleSlug: slug,
                                            status: item.status,
                                            frontendId: item.stat.frontend_question_id,
                                            difficulty: item.difficulty?.level || null,
                                            title: item.stat.question__title,
                                            _raw_difficulty: item.difficulty
                                        };
                                    });

                                    resolve(data);
                                    return;
                                }
                            }
                            resolve(null);
                        } catch (e) {
                            resolve(null);
                        }
                    },
                    onerror: () => {
                        resolve(null);
                    }
                });
            });
        }

        async parseFromPage() {
            const data = {};

            await new Promise(resolve => setTimeout(resolve, 2000));

            const selectors = [
                'div[role="row"]',
                'tr[data-cy="question-row"]',
                '.question-list-table tbody tr'
            ];

            for (const selector of selectors) {
                const rows = document.querySelectorAll(selector);
                if (rows.length > 0) {
                    rows.forEach((row) => {
                        try {
                            const link = row.querySelector('a[href*="/problems/"]');
                            if (!link) return;

                            const slug = link.href.match(/\/problems\/([^\/\?]+)/)?.[1];
                            if (!slug) return;

                            let status = null;
                            const statusElements = row.querySelectorAll('[data-cy="status"], .text-green-s, .text-yellow, .status, svg');
                            statusElements.forEach(el => {
                                const text = el.textContent?.toLowerCase() || '';
                                const classList = el.classList?.toString() || '';

                                if (text.includes('✓') || classList.includes('text-green') || classList.includes('solved')) {
                                    status = 'ac';
                                } else if (text.includes('?') || classList.includes('text-yellow') || classList.includes('attempted')) {
                                    status = 'notac';
                                }
                            });

                            let difficulty = null;
                            const difficultySelectors = [
                                '[data-cy="difficulty"]',
                                '.difficulty',
                                '[class*="difficulty"]',
                                'span[class*="text-"]',
                                '.text-green-s', '.text-yellow', '.text-red'
                            ];

                            difficultySelectors.forEach(selector => {
                                const diffEl = row.querySelector(selector);
                                if (diffEl && !difficulty) {
                                    const diffText = diffEl.textContent?.toLowerCase() || '';
                                    const classList = diffEl.classList?.toString().toLowerCase() || '';

                                    if (diffText.includes('easy') || classList.includes('green')) {
                                        difficulty = 1;
                                    } else if (diffText.includes('medium') || classList.includes('yellow')) {
                                        difficulty = 2;
                                    } else if (diffText.includes('hard') || classList.includes('red')) {
                                        difficulty = 3;
                                    }
                                }
                            });

                            data[slug] = {
                                titleSlug: slug,
                                status: status,
                                difficulty: difficulty,
                                title: link.textContent.trim(),
                                _source: 'page_parse'
                            };

                        } catch (e) {
                        }
                    });

                    if (Object.keys(data).length > 0) {
                        return data;
                    }
                }
            }

            return null;
        }

        async extractFromLocalStorage() {
            try {
                const keys = Object.keys(localStorage);
                for (const key of keys) {
                    if (key.includes('leetcode') || key.includes('question')) {
                        try {
                            const value = JSON.parse(localStorage.getItem(key));
                            if (value && typeof value === 'object') {
                                if (value.questionData || value.questions || value.stat_status_pairs) {
                                }
                            }
                        } catch (e) {
                        }
                    }
                }
            } catch (e) {
            }
            return null;
        }

        saveProgressData(data) {
            GM_setValue(CONFIG.STORAGE_KEY, data);
            GM_setValue(CONFIG.LAST_SYNC_KEY, Date.now());
            console.log(`💾 保存了 ${Object.keys(data).length} 个题目的数据`);
        }

        clearCache() {
            GM_setValue(CONFIG.STORAGE_KEY, {});
            GM_setValue(CONFIG.LAST_SYNC_KEY, 0);
            this.updateSyncStats();
            this.showNotification('缓存已清除 / Cache Cleared', '所有同步数据已删除 / All sync data deleted', 'info');
        }

        updateSyncStats() {
            const data = GM_getValue(CONFIG.STORAGE_KEY, {});
            const lastSync = GM_getValue(CONFIG.LAST_SYNC_KEY, 0);

            const countEl = document.querySelector('#lc-sync-count');
            const timeEl = document.querySelector('#lc-last-sync');

            if (countEl) countEl.textContent = Object.keys(data).length;
            if (timeEl) {
                timeEl.textContent = lastSync ?
                    new Date(lastSync).toLocaleTimeString() : '未同步 / Not Synced';
            }
        }

        handleChineseSite() {
            setTimeout(() => {
                this.createChinesePanel();
                this.processPage();
                this.observeChanges();
            }, 1500);
        }

        createChinesePanel() {
            const data = GM_getValue(CONFIG.STORAGE_KEY, {});
            const panel = document.createElement('div');
            panel.className = 'lc-control-panel';
            panel.innerHTML = `
                <div class="lc-panel-header">
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <span>📚</span>
                        <span>LeetCode Supporter / 题单助手</span>
                    </div>
                    <span class="lc-panel-toggle">▼</span>
                </div>
                <div class="lc-panel-content">
                    <div class="lc-stats">
                        <div class="lc-stat-item">
                            <div>✅ 已解决 / Solved</div>
                            <div id="lc-solved">0</div>
                        </div>
                        <div class="lc-stat-item">
                            <div>❌ 尝试中 / Attempted</div>
                            <div id="lc-attempted">0</div>
                        </div>
                        <div class="lc-stat-item">
                            <div>⭕ 未尝试 / Not Attempted</div>
                            <div id="lc-not-attempted">0</div>
                        </div>
                        <div class="lc-stat-item">
                            <div>📊 总题数 / Total</div>
                            <div id="lc-total">0</div>
                        </div>
                    </div>
                    <div class="lc-progress-bar">
                        <div class="lc-progress-fill" id="lc-progress"></div>
                    </div>
                    <button class="lc-button lc-button-secondary" id="lc-refresh-btn">
                        <span>🔄</span>
                        <span>刷新状态 / Refresh</span>
                    </button>
                    <div class="lc-settings">
                        <div class="lc-setting-item">
                            <span>显示难度 / Show Difficulty</span>
                            <div class="lc-switch ${this.settings.showDifficulty ? 'active' : ''}" data-setting="showDifficulty"></div>
                        </div>
                        <div class="lc-setting-item">
                            <span>隐藏已完成 / Hide Solved</span>
                            <div class="lc-switch ${this.settings.hideCompleted ? 'active' : ''}" data-setting="hideCompleted"></div>
                        </div>
                    </div>
                    ${Object.keys(data).length === 0 ? `
                        <div style="text-align: center; margin: 12px 0; font-size: 11px; color: #999;">
                            <div>⚠️ 未找到同步数据 / No sync data found</div>
                            <a href="https://leetcode.com/problemset/all/" target="_blank" style="color: #1890ff;">
                                前往国际站同步 / Go to LeetCode.com to sync
                            </a>
                        </div>
                    ` : ''}
                </div>
            `;

            document.body.appendChild(panel);
            this.bindChineseEvents(panel);

            this.addPanelToggle(panel);
        }

        bindChineseEvents(panel) {
            panel.querySelector('#lc-refresh-btn')?.addEventListener('click', () => {
                this.processPage();
                this.showNotification('已刷新 / Refreshed', '状态显示已更新 / Status display updated', 'info');
            });

            panel.querySelectorAll('.lc-switch').forEach(sw => {
                sw.addEventListener('click', (e) => {
                    e.stopPropagation();
                    this.toggleSetting(sw.dataset.setting, sw);
                });
            });
        }

        processPage() {
            const data = GM_getValue(CONFIG.STORAGE_KEY, {});

            if (Object.keys(data).length === 0) {
                return;
            }

            document.querySelectorAll('.lc-status-container').forEach(el => el.remove());

            document.querySelectorAll('.lc-hidden').forEach(el => {
                el.classList.remove('lc-hidden');
            });

            this.processLinks(data);
            this.updateStats();
        }

        processLinks(data) {
            const links = Array.from(document.querySelectorAll('a[href*="/problems/"]'));

            if (links.length === 0) {
                return;
            }

            let processed = 0;

            links.forEach(link => {
                if (this.processLink(link, data)) {
                    processed++;
                }
            });

            if (processed > 0) {
                this.updateStats();
            }
        }

        processLink(linkElement, data) {
            const originalHref = linkElement.href;

            if (!originalHref.includes('/problems/')) {
                return false;
            }

            const slug = this.extractProblemSlug(originalHref);
            if (!slug) return false;

            if (originalHref.includes('leetcode.cn')) {
                linkElement.href = originalHref.replace('leetcode.cn', 'leetcode.com');
                linkElement.target = '_blank';
            }
            linkElement.classList.add('lc-converted');

            const problemData = data[slug];
            const status = problemData?.status;

            this.addEnhancedBadges(linkElement, problemData);

            if (this.settings.hideCompleted && status === 'ac') {
                const parentElement = linkElement.closest('tr, div[role="row"], li, .question-item, .problem-item');
                if (parentElement) {
                    parentElement.classList.add('lc-hidden');
                }
            }

            return true;
        }

        addEnhancedBadges(linkElement, problemData) {
            const existingContainer = linkElement.parentNode.querySelector('.lc-status-container');
            if (existingContainer) {
                existingContainer.remove();
            }

            const container = document.createElement('div');
            container.className = 'lc-status-container';

            if (this.settings.showDifficulty) {
                const difficulty = problemData?.difficulty;

                if (difficulty && DIFFICULTY_MAP[difficulty]) {
                    const diffInfo = DIFFICULTY_MAP[difficulty];
                    const diffBadge = document.createElement('span');
                    diffBadge.className = 'lc-difficulty-badge';
                    diffBadge.textContent = diffInfo.text;
                    diffBadge.title = `难度: ${diffInfo.text}`;
                    diffBadge.style.backgroundColor = diffInfo.color;
                    diffBadge.style.color = 'white';

                    container.appendChild(diffBadge);
                }
            }

            const status = problemData?.status;
            const statusInfo = STATUS_MAP[status] || STATUS_MAP[null];

            const statusBadge = document.createElement('span');
            statusBadge.className = 'lc-status-badge';
            statusBadge.textContent = statusInfo.text;
            statusBadge.title = `状态: ${statusInfo.text} - ${problemData?.titleSlug || 'unknown'}`;
            statusBadge.style.backgroundColor = statusInfo.bgColor;
            statusBadge.style.color = statusInfo.color;
            statusBadge.style.borderColor = statusInfo.color;

            container.appendChild(statusBadge);

            linkElement.parentNode.insertBefore(container, linkElement);
        }

        updateStats() {
            const data = GM_getValue(CONFIG.STORAGE_KEY, {});
            const links = document.querySelectorAll('.lc-converted');

            this.stats = { total: 0, solved: 0, attempted: 0, notAttempted: 0 };

            links.forEach(link => {
                const slug = this.extractProblemSlug(link.href);
                const problemData = data[slug];
                if (problemData) {
                    this.stats.total++;
                    if (problemData.status === 'ac') {
                        this.stats.solved++;
                    } else if (problemData.status === 'notac') {
                        this.stats.attempted++;
                    } else {
                        this.stats.notAttempted++;
                    }
                }
            });

            const solvedEl = document.querySelector('#lc-solved');
            const attemptedEl = document.querySelector('#lc-attempted');
            const notAttemptedEl = document.querySelector('#lc-not-attempted');
            const totalEl = document.querySelector('#lc-total');
            const progressEl = document.querySelector('#lc-progress');

            if (solvedEl) solvedEl.textContent = this.stats.solved;
            if (attemptedEl) attemptedEl.textContent = this.stats.attempted;
            if (notAttemptedEl) notAttemptedEl.textContent = this.stats.notAttempted;
            if (totalEl) totalEl.textContent = this.stats.total;
            if (progressEl && this.stats.total > 0) {
                const percentage = (this.stats.solved / this.stats.total) * 100;
                progressEl.style.width = percentage + '%';
            }
        }

        observeChanges() {
            let isProcessing = false;

            const observer = new MutationObserver((mutations) => {
                if (isProcessing) return;

                let shouldProcess = false;

                mutations.forEach(mutation => {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === 1) {
                            if (node.classList?.contains('lc-status-container') ||
                                node.classList?.contains('lc-control-panel') ||
                                node.classList?.contains('lc-notification') ||
                                node.closest?.('.lc-status-container, .lc-control-panel, .lc-notification')) {
                                return;
                            }

                            if (node.querySelector?.('a[href*="/problems/"]:not(.lc-converted)') ||
                                (node.matches?.('a[href*="/problems/"]') && !node.classList.contains('lc-converted'))) {
                                shouldProcess = true;
                            }
                        }
                    });
                });

                if (shouldProcess) {
                    isProcessing = true;
                    setTimeout(() => {
                        this.processPage();
                        isProcessing = false;
                    }, 1000);
                }
            });

            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }

        extractProblemSlug(url) {
            const match = url.match(/\/problems\/([^\/\?]+)/);
            return match ? match[1] : null;
        }

        toggleSetting(key, switchEl) {
            this.settings[key] = !this.settings[key];
            switchEl.classList.toggle('active', this.settings[key]);
            GM_setValue(CONFIG.SETTINGS_KEY, this.settings);

            if (key === 'showDifficulty' || key === 'hideCompleted') {
                this.processPage();
            }

            const settingMessages = {
                'showDifficulty': {
                    on: '显示难度 / Show Difficulty',
                    off: '隐藏难度 / Hide Difficulty'
                },
                'hideCompleted': {
                    on: '隐藏已完成 / Hide Solved',
                    off: '显示已完成 / Show Solved'
                },
                'autoSync': {
                    on: '自动同步 / Auto Sync',
                    off: '手动同步 / Manual Sync'
                }
            };

            const message = settingMessages[key];
            if (message) {
                const statusText = this.settings[key] ? message.on : message.off;
                this.showNotification('设置已更新 / Settings Updated', statusText, 'info');
            }
        }

        showNotification(title, message, type = 'info') {
            const colors = {
                success: '#52c41a',
                error: '#ff4d4f',
                info: '#1890ff',
                warning: '#faad14'
            };

            const notification = document.createElement('div');
            notification.className = 'lc-notification';
            notification.style.borderLeftColor = colors[type];
            notification.innerHTML = `
                <div style="font-weight: bold; margin-bottom: 4px;">${title}</div>
                <div style="font-size: 12px; color: #666;">${message}</div>
            `;

            document.body.appendChild(notification);

            setTimeout(() => {
                notification.style.animation = 'slideIn 0.3s ease reverse';
                setTimeout(() => notification.remove(), 300);
            }, 3000);
        }

        addPanelToggle(panel) {
            const header = panel.querySelector('.lc-panel-header');
            const toggle = panel.querySelector('.lc-panel-toggle');

            const isCollapsed = GM_getValue('panel_collapsed', false);
            if (isCollapsed) {
                panel.classList.add('collapsed');
                toggle.classList.add('collapsed');
            }

            header.addEventListener('click', () => {
                const collapsed = panel.classList.toggle('collapsed');
                toggle.classList.toggle('collapsed', collapsed);

                GM_setValue('panel_collapsed', collapsed);
            });
        }

        initHotkeys() {
            document.addEventListener('keydown', (e) => {
                if (e.ctrlKey && e.shiftKey && e.key === 'L') {
                    e.preventDefault();
                    if (this.isInternational) {
                        this.syncProgress();
                    } else {
                        this.processPage();
                    }
                }

                if (e.ctrlKey && e.shiftKey && e.key === 'H') {
                    e.preventDefault();
                    if (this.isChinese) {
                        const switchEl = document.querySelector('[data-setting="hideCompleted"]');
                        if (switchEl) {
                            this.toggleSetting('hideCompleted', switchEl);
                        }
                    }
                }
            });
        }
    }

    function initialize() {
        const app = new LeetCodeUltimate();

        app.initHotkeys();

        const lastVersion = GM_getValue('last_version', '');
        if (lastVersion !== CONFIG.VERSION) {
            GM_setValue('last_version', CONFIG.VERSION);
            if (lastVersion) {
                app.showNotification(
                    '更新完成!',
                    `LeetCode题单助手已更新到 v${CONFIG.VERSION}`,
                    'success'
                );
            }
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        setTimeout(initialize, 500);
    }

})();