NodeSeek & DeepFlood 增强插件

标记已读帖子、显示用户详细信息(等级、主题量、鸡腿数、评论量)、自动签到 - 支持 NodeSeek 和 DeepFlood

当前为 2025-11-28 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NodeSeek & DeepFlood 增强插件
// @namespace    http://tampermonkey.net/
// @version      1.6.0
// @description  标记已读帖子、显示用户详细信息(等级、主题量、鸡腿数、评论量)、自动签到 - 支持 NodeSeek 和 DeepFlood
// @author       da niao
// @match        https://www.nodeseek.com/*
// @match        https://nodeseek.com/*
// @match        https://www.deepflood.com/*
// @match        https://deepflood.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect      api.nodeimage.com
// @connect      *
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // ==================== 站点识别 ====================
    const CURRENT_SITE = (() => {
        const host = location.hostname;
        if (host.includes('nodeseek.com')) {
            return {
                code: 'ns',
                name: 'NodeSeek',
                host: host
            };
        } else if (host.includes('deepflood.com')) {
            return {
                code: 'df',
                name: 'DeepFlood',
                host: host
            };
        }
        return null;
    })();

    // ==================== 配置管理 ====================
    class ConfigManager {
        static defaults = {
            visitedColor: '#ff6b6b',
            enableVisitedMark: true,
            enableUserInfo: true,
            cacheExpireTime: 24, // 小时
            badgeBackground: 'transparent', // 透明背景
            badgeTextColor: '#000000', // 默认黑色字体
            requestDelay: 300,
            autoSignIn: true,
            signInMode_ns: 'random', // NodeSeek 签到模式
            signInMode_df: 'random'  // DeepFlood 签到模式
        };

        static get(key) {
            const config = GM_getValue('nse_config', this.defaults);
            return config[key] !== undefined ? config[key] : this.defaults[key];
        }

        static set(key, value) {
            const config = GM_getValue('nse_config', this.defaults);
            config[key] = value;
            GM_setValue('nse_config', config);
        }

        static getAll() {
            return GM_getValue('nse_config', this.defaults);
        }
    }

    // 使用配置
    const CONFIG = {
        get visitedColor() { return ConfigManager.get('visitedColor'); },
        get enableVisitedMark() { return ConfigManager.get('enableVisitedMark'); },
        get enableUserInfo() { return ConfigManager.get('enableUserInfo'); },
        get cacheExpireTime() { return ConfigManager.get('cacheExpireTime') * 3600000; }, // 转换为毫秒
        get badgeBackground() { return ConfigManager.get('badgeBackground'); },
        get badgeTextColor() { return ConfigManager.get('badgeTextColor'); },
        get requestDelay() { return ConfigManager.get('requestDelay'); },
        get autoSignIn() { return ConfigManager.get('autoSignIn'); },
        get signInMode() {
            const siteCode = CURRENT_SITE ? CURRENT_SITE.code : 'ns';
            return ConfigManager.get(`signInMode_${siteCode}`);
        }
    };

    // ==================== 请求队列管理 ====================
    class RequestQueue {
        constructor(delay = 300) {
            this.queue = [];
            this.processing = false;
            this.delay = delay;
            this.pendingRequests = new Map(); // 防止重复请求
        }

        async add(userId, fetchFn) {
            // 如果已经有相同的请求在处理,返回该 Promise
            if (this.pendingRequests.has(userId)) {
                return this.pendingRequests.get(userId);
            }

            const promise = new Promise((resolve) => {
                this.queue.push({ userId, fetchFn, resolve });
            });

            this.pendingRequests.set(userId, promise);

            if (!this.processing) {
                this.process();
            }

            return promise;
        }

        async process() {
            if (this.queue.length === 0) {
                this.processing = false;
                return;
            }

            this.processing = true;
            const { userId, fetchFn, resolve } = this.queue.shift();

            try {
                const result = await fetchFn();
                resolve(result);
            } catch (error) {
                console.error(`请求用户 ${userId} 信息失败:`, error);
                resolve(null);
            } finally {
                this.pendingRequests.delete(userId);
                // 延迟后处理下一个请求
                await new Promise(r => setTimeout(r, this.delay));
                this.process();
            }
        }
    }

    const requestQueue = new RequestQueue(CONFIG.requestDelay);

    // ==================== 菜单管理 ====================
    class MenuManager {
        static registerMenus() {
            const siteCode = CURRENT_SITE ? CURRENT_SITE.code : 'ns';
            const siteName = CURRENT_SITE ? CURRENT_SITE.name : 'NodeSeek';

            // 签到设置(区分站点)
            const signInMode = ConfigManager.get(`signInMode_${siteCode}`);
            const signInText = {
                'random': '🎲 随机鸡腿',
                'fixed': '📌 固定5个',
                'disabled': '❌ 已关闭'
            }[signInMode];

            GM_registerMenuCommand(`[${siteName}] 签到: ${signInText}`, () => {
                const modes = ['random', 'fixed', 'disabled'];
                const current = ConfigManager.get(`signInMode_${siteCode}`);
                const currentIndex = modes.indexOf(current);
                const nextMode = modes[(currentIndex + 1) % modes.length];
                ConfigManager.set(`signInMode_${siteCode}`, nextMode);
                alert(`${siteName} 签到模式已切换为: ${{'random':'随机鸡腿','fixed':'固定5个','disabled':'关闭'}[nextMode]}`);
                location.reload();
            });

            // 已读颜色设置
            GM_registerMenuCommand('🎨 设置已读颜色', () => {
                const current = ConfigManager.get('visitedColor');
                const color = prompt('请输入已读帖子颜色(CSS颜色值):', current);
                if (color && color !== current) {
                    ConfigManager.set('visitedColor', color);
                    alert('已读颜色已更新,刷新页面生效');
                    location.reload();
                }
            });

            // 徽章样式切换
            const badgeStyles = {
                'transparent': { name: '透明背景', bg: 'transparent', color: '#000000' },
                'purple': { name: '紫色渐变', bg: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', color: '#ffffff' },
                'blue': { name: '蓝色渐变', bg: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)', color: '#ffffff' },
                'green': { name: '绿色渐变', bg: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)', color: '#ffffff' },
                'orange': { name: '橙色渐变', bg: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)', color: '#ffffff' },
                'pink': { name: '粉色渐变', bg: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)', color: '#ffffff' },
                'dark': { name: '深色背景', bg: '#2c3e50', color: '#ecf0f1' },
                'light': { name: '浅色背景', bg: '#ecf0f1', color: '#2c3e50' }
            };

            // 获取当前样式
            const currentBg = ConfigManager.get('badgeBackground');
            let currentStyleName = '自定义';
            for (const [key, style] of Object.entries(badgeStyles)) {
                if (style.bg === currentBg) {
                    currentStyleName = style.name;
                    break;
                }
            }

            GM_registerMenuCommand(`🎨 徽章样式: ${currentStyleName}`, () => {
                const styleKeys = Object.keys(badgeStyles);
                const options = styleKeys.map((key, index) =>
                    `${index + 1}. ${badgeStyles[key].name}`
                ).join('\n');

                const choice = prompt(
                    `请选择徽章样式(输入数字):\n\n${options}\n\n当前: ${currentStyleName}`,
                    '1'
                );

                if (choice) {
                    const index = parseInt(choice) - 1;
                    if (index >= 0 && index < styleKeys.length) {
                        const selectedKey = styleKeys[index];
                        const selectedStyle = badgeStyles[selectedKey];
                        ConfigManager.set('badgeBackground', selectedStyle.bg);
                        ConfigManager.set('badgeTextColor', selectedStyle.color);
                        alert(`徽章样式已切换为: ${selectedStyle.name}`);
                        location.reload();
                    } else {
                        alert('无效的选择');
                    }
                }
            });

            // 字体颜色切换
            const textColors = {
                'black': { name: '黑色', color: '#000000' },
                'gray': { name: '深灰', color: '#333333' },
                'blue': { name: '蓝色', color: '#1e90ff' },
                'purple': { name: '紫色', color: '#9b59b6' },
                'green': { name: '绿色', color: '#27ae60' },
                'orange': { name: '橙色', color: '#e67e22' },
                'red': { name: '红色', color: '#e74c3c' }
            };

            // 获取当前字体颜色名称
            const currentTextColor = ConfigManager.get('badgeTextColor');
            let currentColorName = '自定义';
            for (const [key, colorObj] of Object.entries(textColors)) {
                if (colorObj.color === currentTextColor) {
                    currentColorName = colorObj.name;
                    break;
                }
            }

            GM_registerMenuCommand(`🖍️ 字体颜色: ${currentColorName}`, () => {
                const colorKeys = Object.keys(textColors);
                const options = colorKeys.map((key, index) =>
                    `${index + 1}. ${textColors[key].name}`
                ).join('\n');

                const choice = prompt(
                    `请选择字体颜色(输入数字):\n\n${options}\n\n当前: ${currentColorName}`,
                    '1'
                );

                if (choice) {
                    const index = parseInt(choice) - 1;
                    if (index >= 0 && index < colorKeys.length) {
                        const selectedKey = colorKeys[index];
                        const selectedColor = textColors[selectedKey];
                        ConfigManager.set('badgeTextColor', selectedColor.color);
                        alert(`字体颜色已切换为: ${selectedColor.name}`);
                        location.reload();
                    } else {
                        alert('无效的选择');
                    }
                }
            });

            // 缓存时间设置
            const cacheHours = ConfigManager.get('cacheExpireTime');
            GM_registerMenuCommand(`⏰ 缓存时间: ${cacheHours}小时`, () => {
                const hours = prompt('请输入用户信息缓存时间(小时):', cacheHours);
                if (hours && !isNaN(hours) && hours > 0) {
                    ConfigManager.set('cacheExpireTime', parseInt(hours));
                    alert(`缓存时间已设置为 ${hours} 小时`);
                }
            });

            // 切换已读标记
            const visitedEnabled = ConfigManager.get('enableVisitedMark');
            GM_registerMenuCommand(`${visitedEnabled ? '✅' : '❌'} 已读标记`, () => {
                ConfigManager.set('enableVisitedMark', !visitedEnabled);
                alert(`已读标记已${!visitedEnabled ? '开启' : '关闭'}`);
                location.reload();
            });

            // 切换用户信息显示
            const userInfoEnabled = ConfigManager.get('enableUserInfo');
            GM_registerMenuCommand(`${userInfoEnabled ? '✅' : '❌'} 用户信息显示`, () => {
                ConfigManager.set('enableUserInfo', !userInfoEnabled);
                alert(`用户信息显示已${!userInfoEnabled ? '开启' : '关闭'}`);
                location.reload();
            });

            // 清除缓存
            GM_registerMenuCommand('🗑️ 清除用户信息缓存', () => {
                if (confirm('确定要清除所有用户信息缓存吗?')) {
                    GM_setValue('userInfoCache', {});
                    alert('缓存已清除');
                }
            });

            // NodeImage API Key 设置
            const currentApiKey = GM_getValue('nodeimage_apiKey', '');
            const apiKeyStatus = currentApiKey ? '已设置' : '未设置';
            GM_registerMenuCommand(`🔑 NodeImage API Key: ${apiKeyStatus}`, () => {
                const hint = currentApiKey
                    ? `当前API Key: ${currentApiKey.substring(0, 8)}...\n\n输入新的API Key可以更新,留空则清除`
                    : '请输入NodeImage API Key\n\n你可以在 https://www.nodeimage.com 登录后,在个人设置中找到API Key';

                const newKey = prompt(hint, '');
                if (newKey === null) return; // 用户取消

                if (newKey.trim() === '') {
                    GM_setValue('nodeimage_apiKey', '');
                    alert('API Key已清除');
                } else {
                    GM_setValue('nodeimage_apiKey', newKey.trim());
                    alert('API Key已保存');
                }
            });

            // 重置所有设置
            GM_registerMenuCommand('🔄 重置所有设置', () => {
                if (confirm('确定要重置所有设置为默认值吗?')) {
                    GM_setValue('nse_config', ConfigManager.defaults);
                    GM_setValue('userInfoCache', {});
                    GM_setValue('visitedPosts', {});
                    alert('所有设置已重置,页面即将刷新');
                    location.reload();
                }
            });
        }
    }

    // ==================== 签到管理 ====================
    class SignInManager {
        // 获取当前日期(北京时间)
        static getCurrentDate() {
            const localTimezoneOffset = (new Date()).getTimezoneOffset();
            const beijingOffset = 8 * 60;
            const beijingTime = new Date(Date.now() + (localTimezoneOffset + beijingOffset) * 60 * 1000);
            return `${beijingTime.getFullYear()}/${(beijingTime.getMonth() + 1)}/${beijingTime.getDate()}`;
        }

        // 获取上次签到日期(区分站点)
        static getLastSignInDate() {
            const siteCode = CURRENT_SITE ? CURRENT_SITE.code : 'ns';
            return GM_getValue(`lastSignInDate_${siteCode}`, '');
        }

        // 设置签到日期(区分站点)
        static setLastSignInDate(date) {
            const siteCode = CURRENT_SITE ? CURRENT_SITE.code : 'ns';
            GM_setValue(`lastSignInDate_${siteCode}`, date);
        }

        // 检查今天是否已签到
        static hasSignedInToday() {
            const today = this.getCurrentDate();
            const lastDate = this.getLastSignInDate();
            return today === lastDate;
        }

        // 执行签到
        static async signIn() {
            if (!CURRENT_SITE) {
                console.log('[增强插件] 未识别的站点');
                return;
            }

            const signInMode = CONFIG.signInMode;
            const siteName = CURRENT_SITE.name;

            if (signInMode === 'disabled') {
                console.log(`[${siteName}增强] 自动签到已禁用`);
                return;
            }

            if (this.hasSignedInToday()) {
                console.log(`[${siteName}增强] 今天已经签到过了`);
                return;
            }

            const isRandom = signInMode === 'random';

            try {
                const response = await fetch(`/api/attendance?random=${isRandom}`, {
                    method: 'POST',
                    credentials: 'include',
                    headers: {
                        'Content-Type': 'application/json'
                    }
                });

                const data = await response.json();

                if (data.success) {
                    const today = this.getCurrentDate();
                    this.setLastSignInDate(today);
                    const siteName = CURRENT_SITE ? CURRENT_SITE.name : 'NodeSeek';
                    console.log(`[${siteName}增强] 签到成功!获得 ${data.gain} 个鸡腿,当前共有 ${data.current} 个鸡腿`);

                    // 可选:显示通知
                    this.showNotification(`${siteName} 签到成功!获得 ${data.gain} 个🍗`);
                } else {
                    const siteName = CURRENT_SITE ? CURRENT_SITE.name : 'NodeSeek';
                    console.warn(`[${siteName}增强] 签到失败:`, data.message);
                }
            } catch (error) {
                const siteName = CURRENT_SITE ? CURRENT_SITE.name : 'NodeSeek';
                console.error(`[${siteName}增强] 签到请求失败:`, error);
            }
        }

        // 显示通知(简单的页面提示)
        static showNotification(message) {
            const notification = document.createElement('div');
            notification.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white;
                padding: 15px 20px;
                border-radius: 8px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
                z-index: 10000;
                font-size: 14px;
                animation: slideIn 0.3s ease-out;
            `;
            notification.textContent = message;

            // 添加动画样式
            const style = document.createElement('style');
            style.textContent = `
                @keyframes slideIn {
                    from {
                        transform: translateX(400px);
                        opacity: 0;
                    }
                    to {
                        transform: translateX(0);
                        opacity: 1;
                    }
                }
            `;
            document.head.appendChild(style);

            document.body.appendChild(notification);

            // 3秒后自动消失
            setTimeout(() => {
                notification.style.animation = 'slideIn 0.3s ease-out reverse';
                setTimeout(() => notification.remove(), 300);
            }, 3000);
        }
    }

    // ==================== 存储管理 ====================
    class Storage {
        static getVisitedPosts() {
            return GM_getValue('visitedPosts', {});
        }

        static markPostAsVisited(postId) {
            const visited = this.getVisitedPosts();
            visited[postId] = Date.now();
            GM_setValue('visitedPosts', visited);
        }

        static isPostVisited(postId) {
            const visited = this.getVisitedPosts();
            return !!visited[postId];
        }

        static getUserInfoCache(userId) {
            const cache = GM_getValue('userInfoCache', {});
            const userCache = cache[userId];
            if (userCache && (Date.now() - userCache.timestamp < CONFIG.cacheExpireTime)) {
                return userCache.data;
            }
            return null;
        }

        static setUserInfoCache(userId, data) {
            const cache = GM_getValue('userInfoCache', {});
            cache[userId] = {
                data: data,
                timestamp: Date.now()
            };
            GM_setValue('userInfoCache', cache);
        }
    }

    // ==================== 用户信息获取 ====================
    class UserInfoFetcher {
        // 从用户主页获取信息(使用请求队列)
        static async fetchUserInfo(userId) {
            // 先检查缓存
            const cached = Storage.getUserInfoCache(userId);
            if (cached) {
                return cached;
            }

            // 使用请求队列,避免并发请求过多
            return requestQueue.add(userId, async () => {
                try {
                    // 再次检查缓存(可能在队列等待期间已被其他请求缓存)
                    const cached = Storage.getUserInfoCache(userId);
                    if (cached) {
                        return cached;
                    }

                    // 方法1: 从 API 获取
                    const apiData = await this.fetchFromAPI(userId);
                    if (apiData) {
                        Storage.setUserInfoCache(userId, apiData);
                        return apiData;
                    }

                    // 如果 API 返回 429,不再尝试备用方案,直接返回 null
                    return null;
                } catch (error) {
                    console.error('获取用户信息失败:', error);
                    return null;
                }
            });
        }

        // 从 API 获取(使用浏览器 fetch,自动带 cookies)
        static async fetchFromAPI(userId) {
            try {
                // 根据当前站点构建 API URL
                const apiUrl = CURRENT_SITE
                    ? `https://${CURRENT_SITE.host}/api/account/getInfo/${userId}`
                    : `https://www.nodeseek.com/api/account/getInfo/${userId}`;

                const response = await fetch(apiUrl, {
                    method: 'GET',
                    credentials: 'include', // 自动包含 cookies
                    headers: {
                        'Accept': 'application/json',
                        'X-Requested-With': 'XMLHttpRequest'
                    }
                });

                if (!response.ok) {
                    if (response.status === 429) {
                        console.warn(`API 请求过于频繁 (429),跳过用户 ${userId}`);
                    }
                    return null;
                }

                const data = await response.json();
                // API 返回的是 detail 而不是 data
                if (data.success && data.detail) {
                    const user = data.detail;

                    // 计算加入天数
                    let joinDays = 0;
                    if (user.created_at) {
                        const joinDate = new Date(user.created_at);
                        const now = new Date();
                        joinDays = Math.floor((now - joinDate) / (1000 * 60 * 60 * 24));
                    }

                    return {
                        level: user.rank || 0,
                        topicCount: user.nPost || 0,
                        drumstickCount: user.coin || 0,
                        commentCount: user.nComment || 0,
                        joinDays: joinDays
                    };
                } else {
                    return null;
                }
            } catch (error) {
                console.error('API 请求失败:', error);
                return null;
            }
        }


    }

    // ==================== 快照管理 ====================
    class SnapshotManager {
        // 保存快照
        static saveSnapshot(postData) {
            const snapshots = this.getAllSnapshots();
            const snapshot = {
                id: postData.id,
                title: postData.title,
                content: postData.content,
                author: postData.author,
                authorId: postData.authorId,
                authorAvatar: postData.authorAvatar,
                createdAt: postData.createdAt,
                savedAt: Date.now(),
                url: window.location.href,
                type: postData.type || 'post' // 'post' 或 'comment'
            };

            snapshots[postData.id] = snapshot;
            GM_setValue('postSnapshots', snapshots);
            return true;
        }

        // 获取所有快照
        static getAllSnapshots() {
            return GM_getValue('postSnapshots', {});
        }

        // 获取单个快照
        static getSnapshot(postId) {
            const snapshots = this.getAllSnapshots();
            return snapshots[postId] || null;
        }

        // 删除快照
        static async deleteSnapshot(postId) {
            const snapshots = this.getAllSnapshots();
            const snapshot = snapshots[postId];
            
            // 如果快照有上传的图片,尝试删除它们
            if (snapshot && snapshot.uploadedImageIds && snapshot.uploadedImageIds.length > 0) {
                const apiKey = GM_getValue('nodeimage_apiKey', '');
                if (apiKey) {
                    console.log(`[快照删除] 正在删除 ${snapshot.uploadedImageIds.length} 张图床图片...`);
                    let deletedCount = 0;
                    
                    for (const imageId of snapshot.uploadedImageIds) {
                        try {
                            const success = await this.deleteImageFromNodeImage(imageId, apiKey);
                            if (success) {
                                deletedCount++;
                            }
                        } catch (error) {
                            console.error(`[快照删除] 删除图片 ${imageId} 失败:`, error);
                        }
                    }
                    
                    console.log(`[快照删除] 成功删除 ${deletedCount}/${snapshot.uploadedImageIds.length} 张图床图片`);
                }
            }
            
            // 删除快照
            delete snapshots[postId];
            GM_setValue('postSnapshots', snapshots);
        }

        // 导出快照
        static exportSnapshots() {
            const snapshots = this.getAllSnapshots();
            const dataStr = JSON.stringify(snapshots, null, 2);
            const blob = new Blob([dataStr], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `nodeseek-snapshots-${Date.now()}.json`;
            a.click();
            URL.revokeObjectURL(url);
        }

        // 导入快照
        static importSnapshots(file) {
            return new Promise((resolve, reject) => {
                const reader = new FileReader();
                reader.onload = (e) => {
                    try {
                        const imported = JSON.parse(e.target.result);
                        const existing = this.getAllSnapshots();
                        const merged = { ...existing, ...imported };
                        GM_setValue('postSnapshots', merged);
                        resolve(Object.keys(imported).length);
                    } catch (error) {
                        reject(error);
                    }
                };
                reader.onerror = reject;
                reader.readAsText(file);
            });
        }

        // 从当前页面提取帖子数据
        static extractPostData() {
            // 提取帖子ID
            const match = window.location.pathname.match(/\/post-(\d+)-/);
            if (!match) return null;

            const postId = match[1];

            // 查找第一个 content-item(主帖,floor #0)
            const mainPost = document.querySelector('#\\30, .content-item[id="0"]') || document.querySelector('.content-item');
            if (!mainPost) return null;

            // 提取标题
            const titleElement = document.querySelector('.post-title h1 a, .post-title-link');
            const title = titleElement ? titleElement.textContent.trim() : '无标题';

            // 提取内容 - 使用 article.post-content
            const contentElement = mainPost.querySelector('article.post-content');
            const content = contentElement ? contentElement.innerHTML : '';

            // 提取作者信息
            const authorElement = mainPost.querySelector('.author-info .author-name, .nsk-content-meta-info .author-name');
            const author = authorElement ? authorElement.textContent.trim() : '未知作者';
            const authorLink = mainPost.querySelector('.author-info a[href^="/space/"], .nsk-content-meta-info a[href^="/space/"]');
            const authorMatch = authorLink ? authorLink.href.match(/\/space\/(\d+)/) : null;
            const authorId = authorMatch ? authorMatch[1] : '';

            // 提取作者头像
            const avatarElement = mainPost.querySelector('.avatar-wrapper img, img.avatar-normal');
            const authorAvatar = avatarElement ? avatarElement.src : `/avatar/${authorId}.png`;

            // 提取发布时间
            const timeElement = mainPost.querySelector('time');
            const createdAt = timeElement ? timeElement.getAttribute('title') || timeElement.textContent.trim() : '';

            console.log('提取的帖子数据:', {
                id: postId,
                title,
                author,
                authorId,
                authorAvatar,
                createdAt,
                contentLength: content.length,
                contentPreview: content.substring(0, 100)
            });

            if (!content) {
                console.warn('警告:内容为空!');
                console.log('mainPost:', mainPost);
                console.log('contentElement:', contentElement);
            }

            return {
                id: postId,
                title,
                content,
                author,
                authorId,
                authorAvatar,
                createdAt,
                type: 'post'
            };
        }

        // 创建快照列表页面
        static createSnapshotListPage() {
            const snapshots = this.getAllSnapshots();
            const snapshotArray = Object.values(snapshots).sort((a, b) => b.savedAt - a.savedAt);

            const html = `
                <div style="max-width: 1200px; margin: 20px auto; padding: 20px;">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                        <h1 style="margin: 0;">我的快照 (<span id="snapshot-count">${snapshotArray.length}</span>)</h1>
                        <div>
                            <button id="import-snapshots-btn" style="
                                padding: 10px 20px;
                                background: #3498db;
                                color: white;
                                border: none;
                                border-radius: 4px;
                                cursor: pointer;
                                margin-right: 10px;
                            ">导入快照</button>
                            <button id="export-snapshots-btn" style="
                                padding: 10px 20px;
                                background: #2ecc71;
                                color: white;
                                border: none;
                                border-radius: 4px;
                                cursor: pointer;
                            ">导出快照</button>
                            <input type="file" id="import-file-input" accept=".json" style="display: none;">
                        </div>
                    </div>
                    <div style="margin-bottom: 20px;">
                        <input type="text" id="snapshot-search-input" placeholder="搜索快照标题或内容..." style="
                            width: 100%;
                            padding: 12px;
                            border: 1px solid #ddd;
                            border-radius: 4px;
                            font-size: 14px;
                            box-sizing: border-box;
                        ">
                    </div>
                    <div id="snapshots-list">
                        ${snapshotArray.length === 0 ?
                            '<p style="text-align: center; color: #999; padding: 40px;">暂无快照</p>' :
                            snapshotArray.map(snapshot => {
                                // 提取纯文本内容(去除HTML标签)
                                const tempDiv = document.createElement('div');
                                tempDiv.innerHTML = snapshot.content || '';
                                const textContent = tempDiv.textContent || tempDiv.innerText || '';
                                const preview = textContent.trim().substring(0, 30) + (textContent.length > 30 ? '...' : '');
                                
                                // 构建原帖链接
                                const postUrl = snapshot.url || '';
                                const typeLabel = snapshot.type === 'comment' ? '💬 评论' : '📝 帖子';
                                
                                return `
                                <div class="snapshot-item" data-id="${snapshot.id}" style="
                                    border: 1px solid #ddd;
                                    border-radius: 8px;
                                    padding: 15px;
                                    margin-bottom: 15px;
                                    background: white;
                                ">
                                    <div style="display: flex; justify-content: space-between; align-items: start;">
                                        <div style="flex: 1;">
                                            <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
                                                <h3 style="margin: 0; flex: 1;">
                                                    <a href="#" class="view-snapshot" data-id="${snapshot.id}" style="color: #333; text-decoration: none; hover: color: #3498db;">
                                                        ${snapshot.title}
                                                    </a>
                                                </h3>
                                                ${postUrl ? `<a href="${postUrl}" target="_blank" style="
                                                    padding: 4px 10px;
                                                    background: #3498db;
                                                    color: white;
                                                    text-decoration: none;
                                                    border-radius: 4px;
                                                    font-size: 12px;
                                                    white-space: nowrap;
                                                " title="在新窗口打开原帖">🔗 原帖</a>` : ''}
                                            </div>
                                            <div style="font-size: 13px; color: #666; margin-bottom: 8px; line-height: 1.5;">
                                                ${preview}
                                            </div>
                                            <div style="font-size: 12px; color: #999;">
                                                <span style="color: ${snapshot.type === 'comment' ? '#e67e22' : '#3498db'};">${typeLabel}</span>
                                                <span style="margin: 0 5px;">•</span>
                                                <span>作者: ${snapshot.author}</span>
                                                <span style="margin: 0 5px;">•</span>
                                                <span>发布: ${snapshot.createdAt}</span>
                                                <span style="margin: 0 5px;">•</span>
                                                <span>保存: ${new Date(snapshot.savedAt).toLocaleString()}</span>
                                            </div>
                                        </div>
                                        <button class="delete-snapshot" data-id="${snapshot.id}" style="
                                            padding: 5px 15px;
                                            background: #e74c3c;
                                            color: white;
                                            border: none;
                                            border-radius: 4px;
                                            cursor: pointer;
                                            margin-left: 15px;
                                        ">删除</button>
                                    </div>
                                </div>
                            `;
                            }).join('')
                        }
                    </div>
                </div>
            `;

            return html;
        }

        // 创建快照详情页面
        static createSnapshotDetailPage(postId) {
            const snapshot = this.getSnapshot(postId);
            if (!snapshot) {
                return '<div style="text-align: center; padding: 40px;">快照不存在</div>';
            }

            const typeLabel = snapshot.type === 'comment' ? '评论快照' : '帖子快照';
            const avatarUrl = snapshot.authorAvatar || `/avatar/${snapshot.authorId}.png`;

            const html = `
                <div style="max-width: 900px; margin: 20px auto; padding: 20px;">
                    <div style="margin-bottom: 20px; display: flex; justify-content: space-between; align-items: center;">
                        <a href="#snapshots" style="color: #3498db; text-decoration: none;">← 返回快照列表</a>
                        <button id="save-images-to-nodeimage" data-snapshot-id="${snapshot.id}" style="
                            padding: 8px 16px;
                            background: #2ecc71;
                            color: white;
                            border: none;
                            border-radius: 4px;
                            cursor: pointer;
                            font-size: 14px;
                        ">💾 保存图片到图床</button>
                    </div>
                    <div style="background: white; border: 1px solid #ddd; border-radius: 8px; padding: 20px;">
                        <div style="display: flex; align-items: center; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 2px solid #eee;">
                            <a href="/space/${snapshot.authorId}" target="_blank" style="text-decoration: none;">
                                <img src="${avatarUrl}" alt="${snapshot.author}" style="
                                    width: 48px;
                                    height: 48px;
                                    border-radius: 50%;
                                    margin-right: 15px;
                                ">
                            </a>
                            <div style="flex: 1;">
                                <h1 style="margin: 0 0 8px 0; font-size: 24px;">${snapshot.title}</h1>
                                <div style="font-size: 14px; color: #999;">
                                    <a href="/space/${snapshot.authorId}" target="_blank" style="color: #3498db; text-decoration: none; font-weight: 500;">
                                        ${snapshot.author}
                                    </a>
                                    <span style="margin: 0 8px;">•</span>
                                    <span>${typeLabel}</span>
                                    <span style="margin: 0 8px;">•</span>
                                    <span>发布: ${snapshot.createdAt}</span>
                                    <span style="margin: 0 8px;">•</span>
                                    <span>保存: ${new Date(snapshot.savedAt).toLocaleString()}</span>
                                </div>
                            </div>
                        </div>
                        <div class="post-content" id="snapshot-content-${snapshot.id}" style="line-height: 1.8; font-size: 15px;">
                            ${snapshot.content}
                        </div>
                    </div>
                </div>
            `;

            return html;
        }

        // 添加"我的快照"按钮
        static addSnapshotButton() {
            // 查找发帖按钮
            const newDiscussionBtn = document.querySelector('.btn.new-discussion, a[href="/new-discussion"]');
            if (!newDiscussionBtn) return;

            // 检查是否已添加
            if (document.querySelector('.snapshot-button')) return;

            // 创建按钮容器
            const container = document.createElement('div');
            container.style.cssText = 'margin-top: 10px;';

            // 创建按钮
            const button = document.createElement('a');
            button.className = 'snapshot-button btn new-discussion';
            button.href = 'javascript:void(0)';
            button.style.cssText = `
                display: block;
                background: #9b59b6;
                text-align: center;
            `;
            button.innerHTML = `
                <svg class="iconpark-icon"><use href="#folder-focus"></use></svg>
                <span style="vertical-align: middle;">我的快照</span>
            `;

            button.onclick = () => this.showSnapshotList();

            container.appendChild(button);
            newDiscussionBtn.parentElement.insertAdjacentElement('afterend', container);
        }

        // 添加"保存快照"按钮(帖子详情页和评论)
        static addSaveSnapshotButton() {
            // 检查是否在帖子详情页
            if (!/\/post-\d+-/.test(window.location.pathname)) return;

            // 为主帖添加按钮(立即执行)
            setTimeout(() => this.addSnapshotButtonToPost(), 500);

            // 为评论添加按钮(使用懒加载)
            this.addSnapshotButtonToCommentsLazy();
        }

        // 为主帖添加快照按钮
        static addSnapshotButtonToPost() {
            // 查找主帖的楼层号链接
            const firstContentItem = document.querySelector('.content-item');
            if (!firstContentItem) return;
            
            const floorLink = firstContentItem.querySelector('.floor-link');
            if (!floorLink) return;

            // 检查是否已添加
            if (floorLink.previousElementSibling?.classList.contains('save-snapshot-btn')) return;

            // 提取帖子ID
            const match = window.location.pathname.match(/\/post-(\d+)-/);
            if (!match) return;
            const postId = match[1];

            // 检查是否已保存
            const isSaved = this.getSnapshot(postId) !== null;

            const button = document.createElement('button');
            button.className = 'save-snapshot-btn';
            button.textContent = isSaved ? '✅' : '💾';
            button.title = isSaved ? '已保存快照' : '保存快照';
            button.style.cssText = `
                padding: 2px 8px;
                margin-right: 8px;
                background: ${isSaved ? '#2ecc71' : '#3498db'};
                color: white;
                border: none;
                border-radius: 10px;
                font-size: 11px;
                cursor: pointer;
                transition: background 0.3s;
                vertical-align: middle;
                white-space: nowrap;
            `;

            button.onmouseover = () => button.style.background = isSaved ? '#27ae60' : '#2980b9';
            button.onmouseout = () => button.style.background = isSaved ? '#2ecc71' : '#3498db';
            button.onclick = () => {
                const postData = this.extractPostData();
                if (postData) {
                    this.saveSnapshot(postData);
                    button.textContent = '✅';
                    button.title = '已保存快照';
                    button.style.background = '#2ecc71';
                }
            };

            floorLink.insertAdjacentElement('beforebegin', button);
        }

        // 为评论添加快照按钮(懒加载)
        static addSnapshotButtonToCommentsLazy() {
            // 创建 IntersectionObserver 用于懒加载
            const intersectionObserver = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const comment = entry.target;
                        intersectionObserver.unobserve(comment);
                        this.addSnapshotButtonToComment(comment);
                    }
                });
            }, {
                rootMargin: '100px'
            });
            
            // 观察现有评论
            const observeComments = () => {
                const comments = document.querySelectorAll('.content-item');
                comments.forEach((comment, index) => {
                    // 跳过第一个(主帖)
                    if (index === 0) return;
                    // 跳过已经添加按钮的评论
                    if (comment.querySelector('.save-comment-snapshot-btn')) return;
                    intersectionObserver.observe(comment);
                });
            };
            
            // 初始观察
            observeComments();
            
            // 创建 MutationObserver 监听新加载的评论
            const mutationObserver = new MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.addedNodes.length) {
                        // 检查是否有新的评论节点
                        mutation.addedNodes.forEach(node => {
                            if (node.nodeType === 1) { // 元素节点
                                // 检查节点本身是否是评论
                                if (node.classList && node.classList.contains('content-item')) {
                                    intersectionObserver.observe(node);
                                }
                                // 检查节点内是否包含评论
                                const newComments = node.querySelectorAll ? node.querySelectorAll('.content-item') : [];
                                newComments.forEach(comment => {
                                    if (!comment.querySelector('.save-comment-snapshot-btn')) {
                                        intersectionObserver.observe(comment);
                                    }
                                });
                            }
                        });
                    }
                }
            });
            
            // 观察评论容器的变化
            const commentContainer = document.querySelector('.post-content-wrapper, .post-detail, main');
            if (commentContainer) {
                mutationObserver.observe(commentContainer, {
                    childList: true,
                    subtree: true
                });
            }
        }

        // 为单个评论添加快照按钮
        static addSnapshotButtonToComment(comment) {
            // 检查是否已添加
            if (comment.querySelector('.save-comment-snapshot-btn')) return;

            // 查找楼层号链接
            const floorLink = comment.querySelector('.floor-link');
            if (!floorLink) return;

            // 提取评论ID
            const commentId = comment.id || comment.getAttribute('id');
            if (!commentId) return;

            const button = document.createElement('button');
            button.className = 'save-comment-snapshot-btn';
            button.textContent = '💾';
            button.title = '保存评论快照';
            button.style.cssText = `
                padding: 2px 8px;
                margin-right: 8px;
                background: #3498db;
                color: white;
                border: none;
                border-radius: 10px;
                font-size: 11px;
                cursor: pointer;
                transition: background 0.3s;
                vertical-align: middle;
                white-space: nowrap;
            `;

            button.onmouseover = () => button.style.background = '#2980b9';
            button.onmouseout = () => button.style.background = '#3498db';
            button.onclick = () => {
                const commentData = this.extractCommentData(comment, commentId);
                if (commentData) {
                    this.saveSnapshot(commentData);
                    button.textContent = '✅';
                    button.title = '已保存快照';
                    button.style.background = '#2ecc71';
                }
            };

            floorLink.insertAdjacentElement('beforebegin', button);
        }

        // 提取评论数据
        static extractCommentData(commentElement, commentId) {
            const match = window.location.pathname.match(/\/post-(\d+)-/);
            if (!match) return null;

            const postId = match[1];

            // 提取评论内容 - 使用 article.post-content
            const contentElement = commentElement.querySelector('article.post-content');
            const content = contentElement ? contentElement.innerHTML : '';

            // 提取作者信息
            const authorElement = commentElement.querySelector('.author-info .author-name, .nsk-content-meta-info .author-name');
            const author = authorElement ? authorElement.textContent.trim() : '未知作者';
            const authorLink = commentElement.querySelector('.author-info a[href^="/space/"], .nsk-content-meta-info a[href^="/space/"]');
            const authorMatch = authorLink ? authorLink.href.match(/\/space\/(\d+)/) : null;
            const authorId = authorMatch ? authorMatch[1] : '';

            // 提取作者头像
            const avatarElement = commentElement.querySelector('.avatar-wrapper img, img.avatar-normal');
            const authorAvatar = avatarElement ? avatarElement.src : `/avatar/${authorId}.png`;

            // 提取时间
            const timeElement = commentElement.querySelector('time');
            const createdAt = timeElement ? timeElement.getAttribute('title') || timeElement.textContent.trim() : '';

            // 获取主帖标题
            const mainTitle = document.querySelector('.post-title h1 a, .post-title-link');
            const mainTitleText = mainTitle ? mainTitle.textContent.trim() : '未知帖子';

            // 获取楼层号(从 id 属性或 floor-link 获取)
            const floorId = commentElement.id || commentElement.getAttribute('id');
            const floorLink = commentElement.querySelector('.floor-link');
            const floorNumber = floorLink ? floorLink.textContent.trim() : (floorId || '');
            
            // 构建评论URL(包含楼层锚点)
            const commentUrl = `${window.location.origin}${window.location.pathname}${floorNumber}`;

            console.log('提取的评论数据:', { 
                id: `${postId}-${commentId}`, 
                title: `${mainTitleText} - ${author}的评论`, 
                author, 
                authorId, 
                authorAvatar, 
                createdAt, 
                floorNumber,
                url: commentUrl,
                contentLength: content.length 
            });

            return {
                id: `${postId}-${commentId}`,
                title: `${mainTitleText} - ${author}的评论`,
                content,
                author,
                authorId,
                authorAvatar,
                createdAt,
                type: 'comment',
                url: commentUrl
            };
        }

        // 显示快照列表
        static showSnapshotList() {
            const overlay = document.createElement('div');
            overlay.id = 'snapshot-overlay';
            overlay.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background: rgba(0,0,0,0.5);
                z-index: 10000;
                overflow-y: auto;
            `;

            const container = document.createElement('div');
            container.className = 'snapshot-detail-container';
            container.style.cssText = `
                background: #f5f5f5;
                min-height: 100vh;
            `;

            container.innerHTML = this.createSnapshotListPage();
            overlay.appendChild(container);
            document.body.appendChild(overlay);

            // 关闭按钮
            const closeBtn = document.createElement('button');
            closeBtn.textContent = '✕ 关闭';
            closeBtn.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                padding: 10px 20px;
                background: #e74c3c;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                z-index: 10001;
            `;
            closeBtn.onclick = () => overlay.remove();
            overlay.appendChild(closeBtn);

            // 绑定事件
            this.bindSnapshotListEvents(overlay);
        }

        // 过滤快照
        static filterSnapshots(keyword, overlay) {
            const snapshots = this.getAllSnapshots();
            const snapshotArray = Object.values(snapshots).sort((a, b) => b.savedAt - a.savedAt);
            const lowerKeyword = keyword.toLowerCase().trim();

            let filteredSnapshots = snapshotArray;
            if (lowerKeyword) {
                filteredSnapshots = snapshotArray.filter(snapshot => {
                    const titleMatch = (snapshot.title || '').toLowerCase().includes(lowerKeyword);
                    const contentMatch = (snapshot.content || '').toLowerCase().includes(lowerKeyword);
                    const authorMatch = (snapshot.author || '').toLowerCase().includes(lowerKeyword);
                    return titleMatch || contentMatch || authorMatch;
                });
            }

            // 更新列表
            const listContainer = overlay.querySelector('#snapshots-list');
            const countElement = overlay.querySelector('#snapshot-count');

            if (countElement) {
                countElement.textContent = filteredSnapshots.length;
            }

            if (filteredSnapshots.length === 0) {
                listContainer.innerHTML = '<p style="text-align: center; color: #999; padding: 40px;">未找到匹配的快照</p>';
            } else {
                listContainer.innerHTML = filteredSnapshots.map(snapshot => {
                    // 提取纯文本内容(去除HTML标签)
                    const tempDiv = document.createElement('div');
                    tempDiv.innerHTML = snapshot.content || '';
                    const textContent = tempDiv.textContent || tempDiv.innerText || '';
                    const preview = textContent.trim().substring(0, 30) + (textContent.length > 30 ? '...' : '');
                    
                    // 构建原帖链接
                    const postUrl = snapshot.url || '';
                    const typeLabel = snapshot.type === 'comment' ? '💬 评论' : '📝 帖子';
                    
                    return `
                    <div class="snapshot-item" data-id="${snapshot.id}" style="
                        border: 1px solid #ddd;
                        border-radius: 8px;
                        padding: 15px;
                        margin-bottom: 15px;
                        background: white;
                    ">
                        <div style="display: flex; justify-content: space-between; align-items: start;">
                            <div style="flex: 1;">
                                <div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
                                    <h3 style="margin: 0; flex: 1;">
                                        <a href="#" class="view-snapshot" data-id="${snapshot.id}" style="color: #333; text-decoration: none;">
                                            ${snapshot.title}
                                        </a>
                                    </h3>
                                    ${postUrl ? `<a href="${postUrl}" target="_blank" style="
                                        padding: 4px 10px;
                                        background: #3498db;
                                        color: white;
                                        text-decoration: none;
                                        border-radius: 4px;
                                        font-size: 12px;
                                        white-space: nowrap;
                                    " title="在新窗口打开原帖">🔗 原帖</a>` : ''}
                                </div>
                                <div style="font-size: 13px; color: #666; margin-bottom: 8px; line-height: 1.5;">
                                    ${preview}
                                </div>
                                <div style="font-size: 12px; color: #999;">
                                    <span style="color: ${snapshot.type === 'comment' ? '#e67e22' : '#3498db'};">${typeLabel}</span>
                                    <span style="margin: 0 5px;">•</span>
                                    <span>作者: ${snapshot.author}</span>
                                    <span style="margin: 0 5px;">•</span>
                                    <span>发布: ${snapshot.createdAt}</span>
                                    <span style="margin: 0 5px;">•</span>
                                    <span>保存: ${new Date(snapshot.savedAt).toLocaleString()}</span>
                                </div>
                            </div>
                            <button class="delete-snapshot" data-id="${snapshot.id}" style="
                                padding: 5px 15px;
                                background: #e74c3c;
                                color: white;
                                border: none;
                                border-radius: 4px;
                                cursor: pointer;
                                margin-left: 15px;
                            ">删除</button>
                        </div>
                    </div>
                `;
                }).join('');

                // 重新绑定查看和删除事件
                listContainer.querySelectorAll('.view-snapshot').forEach(link => {
                    link.addEventListener('click', (e) => {
                        e.preventDefault();
                        const postId = e.target.dataset.id;
                        this.showSnapshotDetail(postId, overlay);
                    });
                });

                listContainer.querySelectorAll('.delete-snapshot').forEach(btn => {
                    btn.addEventListener('click', async (e) => {
                        const postId = e.target.dataset.id;
                        const snapshot = this.getSnapshot(postId);
                        const hasImages = snapshot && snapshot.uploadedImageIds && snapshot.uploadedImageIds.length > 0;
                        
                        const confirmMsg = hasImages 
                            ? `确定要删除这个快照吗?\n\n此快照包含 ${snapshot.uploadedImageIds.length} 张已上传到图床的图片,\n删除快照时会同时删除这些图片。`
                            : '确定要删除这个快照吗?';
                        
                        if (confirm(confirmMsg)) {
                            const deleteBtn = e.target;
                            deleteBtn.textContent = '删除中...';
                            deleteBtn.disabled = true;
                            
                            await this.deleteSnapshot(postId);
                            overlay.remove();
                            this.showSnapshotList();
                        }
                    });
                });
            }
        }

        // 绑定快照列表事件
        static bindSnapshotListEvents(overlay) {
            // 搜索功能
            const searchInput = overlay.querySelector('#snapshot-search-input');
            if (searchInput) {
                searchInput.addEventListener('input', (e) => {
                    this.filterSnapshots(e.target.value, overlay);
                });
            }

            // 导出按钮
            overlay.querySelector('#export-snapshots-btn')?.addEventListener('click', () => {
                this.exportSnapshots();
            });

            // 导入按钮
            overlay.querySelector('#import-snapshots-btn')?.addEventListener('click', () => {
                overlay.querySelector('#import-file-input').click();
            });

            // 文件选择
            overlay.querySelector('#import-file-input')?.addEventListener('change', async (e) => {
                const file = e.target.files[0];
                if (file) {
                    try {
                        const count = await this.importSnapshots(file);
                        alert(`成功导入 ${count} 个快照`);
                        overlay.remove();
                        this.showSnapshotList();
                    } catch (error) {
                        alert('导入失败: ' + error.message);
                    }
                }
            });

            // 查看快照
            overlay.querySelectorAll('.view-snapshot').forEach(link => {
                link.addEventListener('click', (e) => {
                    e.preventDefault();
                    const postId = e.target.dataset.id;
                    this.showSnapshotDetail(postId, overlay);
                });
            });

            // 删除快照
            overlay.querySelectorAll('.delete-snapshot').forEach(btn => {
                btn.addEventListener('click', async (e) => {
                    const postId = e.target.dataset.id;
                    const snapshot = this.getSnapshot(postId);
                    const hasImages = snapshot && snapshot.uploadedImageIds && snapshot.uploadedImageIds.length > 0;
                    
                    const confirmMsg = hasImages 
                        ? `确定要删除这个快照吗?\n\n此快照包含 ${snapshot.uploadedImageIds.length} 张已上传到图床的图片,\n删除快照时会同时删除这些图片。`
                        : '确定要删除这个快照吗?';
                    
                    if (confirm(confirmMsg)) {
                        const deleteBtn = e.target;
                        deleteBtn.textContent = '删除中...';
                        deleteBtn.disabled = true;
                        
                        await this.deleteSnapshot(postId);
                        overlay.remove();
                        this.showSnapshotList();
                    }
                });
            });
        }

        // 保存快照中的图片到NodeImage
        static async saveImagesToNodeImage(snapshotId) {
            const snapshot = this.getSnapshot(snapshotId);
            if (!snapshot) {
                alert('快照不存在');
                return;
            }

            // 检查API Key
            let apiKey = GM_getValue('nodeimage_apiKey', '');

            if (!apiKey) {
                const userInput = prompt(
                    '请输入NodeImage API Key\n\n' +
                    '你可以在 https://www.nodeimage.com 登录后,\n' +
                    '在个人设置中找到API Key\n\n' +
                    '提示:也可以通过油猴菜单设置API Key'
                );

                if (!userInput || userInput.trim() === '') {
                    alert('未提供API Key,操作已取消');
                    return;
                }

                apiKey = userInput.trim();
                GM_setValue('nodeimage_apiKey', apiKey);
            }

            const button = document.querySelector('#save-images-to-nodeimage');
            if (!button) return;

            // 创建临时div来解析HTML
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = snapshot.content;

            // 提取所有图片
            const images = tempDiv.querySelectorAll('img');
            if (images.length === 0) {
                alert('快照中没有图片');
                return;
            }

            // 创建进度显示元素
            const progressDiv = document.createElement('div');
            progressDiv.style.cssText = `
                margin-top: 10px;
                padding: 10px;
                background: #f5f5f5;
                border-radius: 4px;
                font-size: 14px;
            `;
            button.parentElement.insertBefore(progressDiv, button.nextSibling);

            button.textContent = `⏳ 准备处理 ${images.length} 张图片...`;
            button.disabled = true;

            let successCount = 0;
            let failCount = 0;
            let failedImages = [];
            let uploadedImageIds = snapshot.uploadedImageIds || []; // 获取已上传的图片ID列表

            for (let i = 0; i < images.length; i++) {
                const img = images[i];
                const originalSrc = img.src;

                // 更新进度
                button.textContent = `⏳ 正在处理 ${i + 1}/${images.length} 张图片...`;
                progressDiv.innerHTML = `
                    <div style="margin-bottom: 5px;">
                        <strong>进度:</strong>${i + 1}/${images.length}
                    </div>
                    <div style="margin-bottom: 5px;">
                        <span style="color: #2ecc71;">✅ 成功:${successCount}</span>
                        <span style="margin-left: 15px; color: #e74c3c;">❌ 失败:${failCount}</span>
                    </div>
                    <div style="background: #ddd; height: 8px; border-radius: 4px; overflow: hidden;">
                        <div style="background: #2ecc71; height: 100%; width: ${((i + 1) / images.length * 100).toFixed(1)}%; transition: width 0.3s;"></div>
                    </div>
                `;

                try {
                    // 使用 GM_xmlhttpRequest 下载图片(绕过CORS)
                    const blob = await this.downloadImage(originalSrc);
                    
                    // 确定文件扩展名和MIME类型
                    let extension = 'png';
                    let mimeType = blob.type || 'image/png';
                    
                    // 从blob类型获取扩展名
                    if (blob.type) {
                        const typeMatch = blob.type.match(/image\/(.*)/);
                        if (typeMatch) {
                            extension = typeMatch[1];
                        }
                    }
                    
                    // 从URL获取扩展名(如果blob类型不可用)
                    if (!blob.type || blob.type === 'application/octet-stream') {
                        const urlMatch = originalSrc.match(/\.([a-z0-9]+)(?:\?|$)/i);
                        if (urlMatch) {
                            extension = urlMatch[1].toLowerCase();
                            // 映射常见扩展名到MIME类型
                            const mimeMap = {
                                'jpg': 'image/jpeg',
                                'jpeg': 'image/jpeg',
                                'png': 'image/png',
                                'gif': 'image/gif',
                                'webp': 'image/webp',
                                'avif': 'image/avif',
                                'svg': 'image/svg+xml'
                            };
                            mimeType = mimeMap[extension] || 'image/png';
                        }
                    }
                    
                    // 验证是否是支持的图片格式
                    const supportedFormats = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'svg'];
                    if (!supportedFormats.includes(extension.toLowerCase())) {
                        console.warn(`[图片 ${i + 1}] 不支持的格式: ${extension}, 跳过`);
                        failCount++;
                        failedImages.push({ index: i + 1, src: originalSrc, error: `不支持的图片格式: ${extension}` });
                        continue;
                    }
                    
                    // 创建File对象,确保有正确的MIME类型
                    const file = new File([blob], `image-${i}.${extension}`, { type: mimeType });
                    
                    console.log(`[图片 ${i + 1}] 准备上传 - 文件名: ${file.name}, MIME类型: ${file.type}, 大小: ${(file.size / 1024).toFixed(2)}KB`);

                    // 上传到NodeImage
                    const result = await this.uploadToNodeImage(file, apiKey);

                    if (result.success) {
                        // 替换图片链接
                        img.src = result.url;
                        // 记录上传的图片ID
                        if (result.imageId) {
                            uploadedImageIds.push(result.imageId);
                        }
                        successCount++;
                    } else {
                        failCount++;
                        failedImages.push({ index: i + 1, src: originalSrc, error: result.error });
                        console.error(`图片 ${i + 1} 上传失败:`, result.error);

                        // 检查是否是API Key错误
                        if (result.error && (
                            result.error.includes('API') ||
                            result.error.includes('unauthorized') ||
                            result.error.includes('invalid')
                        )) {
                            alert(`API Key无效或已过期:${result.error}\n\n操作已取消,请在油猴菜单中重新设置API Key`);
                            button.textContent = '❌ API Key无效';
                            button.disabled = false;
                            progressDiv.remove();
                            return;
                        }
                    }
                } catch (error) {
                    failCount++;
                    failedImages.push({ index: i + 1, src: originalSrc, error: error.message });
                    console.error(`图片 ${i + 1} 处理失败:`, error);
                }
            }

            // 更新快照内容和图片ID列表
            snapshot.content = tempDiv.innerHTML;
            snapshot.uploadedImageIds = uploadedImageIds;
            const snapshots = this.getAllSnapshots();
            snapshots[snapshotId] = snapshot;
            GM_setValue('postSnapshots', snapshots);

            // 更新显示
            const contentElement = document.querySelector(`#snapshot-content-${snapshotId}`);
            if (contentElement) {
                contentElement.innerHTML = snapshot.content;
            }

            // 显示完成结果
            const totalImages = images.length;
            button.textContent = `✅ 完成!成功 ${successCount}/${totalImages}`;
            button.disabled = false;

            // 更新进度显示为最终结果
            progressDiv.innerHTML = `
                <div style="margin-bottom: 10px; font-weight: bold; color: ${failCount > 0 ? '#e67e22' : '#2ecc71'};">
                    ${failCount > 0 ? '⚠️ 处理完成(部分失败)' : '✅ 全部处理成功!'}
                </div>
                <div style="margin-bottom: 5px;">
                    <span style="color: #2ecc71;">✅ 成功:${successCount} 张</span>
                    <span style="margin-left: 15px; color: #e74c3c;">❌ 失败:${failCount} 张</span>
                </div>
                ${failCount > 0 ? `
                    <div style="margin-top: 10px; padding: 10px; background: #fff3cd; border-radius: 4px; border-left: 4px solid #ffc107;">
                        <div style="font-weight: bold; margin-bottom: 5px;">失败的图片:</div>
                        ${failedImages.map(img => `<div style="font-size: 12px; color: #666;">• 图片 ${img.index}: ${img.error}</div>`).join('')}
                        <div style="margin-top: 10px; color: #e67e22;">
                            💡 提示:可以再次点击"保存图片到图床"按钮重试失败的图片
                        </div>
                    </div>
                ` : ''}
            `;

            // 5秒后恢复按钮文本
            setTimeout(() => {
                button.textContent = '💾 保存图片到图床';
                if (failCount === 0) {
                    progressDiv.remove();
                }
            }, 5000);
        }

        // 下载图片(使用GM_xmlhttpRequest绕过CORS)
        static async downloadImage(url) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    responseType: 'blob',
                    onload: (response) => {
                        if (response.status === 200) {
                            resolve(response.response);
                        } else {
                            reject(new Error(`下载失败: ${response.status}`));
                        }
                    },
                    onerror: (error) => {
                        reject(new Error(`下载失败: ${error}`));
                    }
                });
            });
        }

        // 从NodeImage删除图片
        static async deleteImageFromNodeImage(imageId, apiKey) {
            return new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'DELETE',
                    url: `https://api.nodeimage.com/api/v1/delete/${imageId}`,
                    headers: {
                        'X-API-Key': apiKey
                    },
                    onload: (response) => {
                        try {
                            const result = JSON.parse(response.responseText);
                            resolve(result.success || response.status === 200);
                        } catch (error) {
                            // 如果返回204或其他成功状态码
                            resolve(response.status >= 200 && response.status < 300);
                        }
                    },
                    onerror: () => resolve(false) // 删除失败不影响快照删除
                });
            });
        }

        // 上传图片到NodeImage
        static async uploadToNodeImage(file, apiKey) {
            return new Promise((resolve, reject) => {
                const formData = new FormData();
                // 确保文件名和类型都正确
                formData.append('image', file, file.name);
                
                console.log('[上传请求] 文件名:', file.name, '类型:', file.type, '大小:', file.size);

                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'https://api.nodeimage.com/api/upload',
                    headers: {
                        'X-API-Key': apiKey
                        // 不要手动设置 Content-Type,让浏览器自动设置 multipart/form-data
                    },
                    data: formData,
                    onload: (response) => {
                        // 检查HTTP状态码
                        if (response.status !== 200) {
                            console.error('[上传失败] HTTP状态:', response.status);
                            console.error('[上传失败] 响应内容:', response.responseText.substring(0, 500));
                            
                            // 尝试从HTML响应中提取错误信息
                            let errorMsg = response.statusText || '上传失败';
                            const errorMatch = response.responseText.match(/Error:\s*([^<\n]+)/);
                            if (errorMatch) {
                                errorMsg = errorMatch[1].trim();
                            }
                            
                            resolve({ 
                                success: false, 
                                error: errorMsg
                            });
                            return;
                        }

                        try {
                            const result = JSON.parse(response.responseText);
                            if (result.success) {
                                resolve({
                                    success: true,
                                    url: result.links.direct,
                                    imageId: result.image_id || result.id // 记录图片ID
                                });
                            } else {
                                resolve({ success: false, error: result.error || '上传失败' });
                            }
                        } catch (error) {
                            console.error('[上传失败] JSON解析错误:', error);
                            console.error('[上传失败] 响应内容:', response.responseText.substring(0, 200));
                            resolve({ 
                                success: false, 
                                error: `服务器返回了无效的响应(可能是API Key错误或服务器问题)` 
                            });
                        }
                    },
                    onerror: (error) => {
                        reject(new Error(`网络错误: ${error}`));
                    }
                });
            });
        }

        // 显示快照详情
        static showSnapshotDetail(postId, overlay) {
            // 查找容器,使用多种方式
            let container = overlay.querySelector('.snapshot-detail-container');
            if (!container) {
                container = overlay.querySelector('div[style*="background"]');
            }
            if (!container) {
                // 如果还是找不到,使用 overlay 的第一个子元素
                container = overlay.firstElementChild;
            }

            if (!container) {
                console.error('无法找到快照详情容器');
                return;
            }

            container.innerHTML = this.createSnapshotDetailPage(postId);

            // 返回按钮
            const backLink = container.querySelector('a[href="#snapshots"]');
            if (backLink) {
                backLink.addEventListener('click', (e) => {
                    e.preventDefault();
                    overlay.remove();
                    this.showSnapshotList();
                });
            }

            // 保存图片按钮
            const saveImagesBtn = container.querySelector('#save-images-to-nodeimage');
            if (saveImagesBtn) {
                saveImagesBtn.addEventListener('click', () => {
                    const snapshotId = saveImagesBtn.dataset.snapshotId;
                    this.saveImagesToNodeImage(snapshotId);
                });
            }
        }
    }

    // ==================== 收藏夹搜索 ====================
    class CollectionSearch {
        // 从HTML中提取当前用户ID
        static getCurrentUserId() {
            const userStyleLink = document.querySelector('link[href*="/userstyle/"]');
            if (userStyleLink) {
                const match = userStyleLink.href.match(/\/userstyle\/(\d+)\.css/);
                if (match) return match[1];
            }
            return null;
        }

        // 获取所有收藏的帖子(分页获取)
        static async fetchAllCollections() {
            const collections = [];
            let page = 1;

            while (true) {
                try {
                    const apiUrl = CURRENT_SITE
                        ? `https://${CURRENT_SITE.host}/api/statistics/list-collection?page=${page}`
                        : `https://www.nodeseek.com/api/statistics/list-collection?page=${page}`;

                    const response = await fetch(apiUrl, {
                        method: 'GET',
                        credentials: 'include',
                        headers: {
                            'Accept': 'application/json',
                            'X-Requested-With': 'XMLHttpRequest'
                        }
                    });

                    if (!response.ok) break;

                    const data = await response.json();

                    // API 返回的是 collections 字段
                    if (data.success && data.collections && data.collections.length > 0) {
                        collections.push(...data.collections);
                        page++;
                    } else {
                        // 没有数据了,停止搜索
                        break;
                    }
                } catch (error) {
                    console.error(`获取收藏夹第${page}页失败:`, error);
                    break;
                }
            }

            return collections;
        }

        // 搜索收藏夹
        static async searchCollections(keyword) {
            if (!keyword || keyword.trim() === '') {
                return [];
            }

            const collections = await this.fetchAllCollections();
            const lowerKeyword = keyword.toLowerCase();

            return collections.filter(item => {
                const title = (item.title || '').toLowerCase();
                return title.includes(lowerKeyword);
            });
        }

        // 创建搜索框UI
        static createSearchBox() {
            // 查找发帖按钮
            const newDiscussionBtn = document.querySelector('.btn.new-discussion, a[href="/new-discussion"]');
            if (!newDiscussionBtn) {
                console.log('[收藏夹搜索] 未找到发帖按钮');
                return;
            }

            // 检查是否已经添加过
            if (document.querySelector('.collection-search-box')) {
                return;
            }

            // 创建容器
            const container = document.createElement('div');
            container.className = 'collection-search-box';
            container.style.cssText = `
                display: block;
                margin-bottom: 10px;
                padding: 0;
                position: relative;
                width: 100%;
            `;

            // 创建搜索输入框
            const input = document.createElement('input');
            input.type = 'text';
            input.placeholder = '搜索收藏...';
            input.style.cssText = `
                height: 36px;
                padding: 0 12px;
                border: 1px solid #ddd;
                border-radius: 4px;
                font-size: 14px;
                width: 100%;
                outline: none;
                box-sizing: border-box;
            `;

            // 创建结果容器
            const resultsBox = document.createElement('div');
            resultsBox.className = 'collection-search-results';
            resultsBox.style.cssText = `
                position: absolute;
                top: 100%;
                left: 0;
                right: 0;
                background: white;
                border: 1px solid #ddd;
                border-radius: 4px;
                margin-top: 5px;
                max-height: 400px;
                overflow-y: auto;
                display: none;
                z-index: 1000;
                box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            `;

            // 搜索处理
            let searchTimeout;
            input.addEventListener('input', (e) => {
                clearTimeout(searchTimeout);
                const keyword = e.target.value.trim();

                if (keyword === '') {
                    resultsBox.style.display = 'none';
                    return;
                }

                searchTimeout = setTimeout(async () => {
                    resultsBox.innerHTML = '<div style="padding: 10px; text-align: center; color: #999;">搜索中...</div>';
                    resultsBox.style.display = 'block';

                    const results = await this.searchCollections(keyword);

                    if (results.length === 0) {
                        resultsBox.innerHTML = '<div style="padding: 10px; text-align: center; color: #999;">未找到相关收藏</div>';
                    } else {
                        resultsBox.innerHTML = results.map(item => `
                            <a href="/post-${item.post_id}-1" style="
                                display: block;
                                padding: 10px;
                                border-bottom: 1px solid #f0f0f0;
                                color: #333;
                                text-decoration: none;
                                transition: background 0.2s;
                            " onmouseover="this.style.background='#f5f5f5'" onmouseout="this.style.background='white'">
                                <div style="
                                    font-size: 14px;
                                    font-weight: 500;
                                    white-space: nowrap;
                                    overflow: hidden;
                                    text-overflow: ellipsis;
                                ">${item.title || '无标题'}</div>
                            </a>
                        `).join('');
                    }
                }, 300);
            });

            // 点击外部关闭结果框
            document.addEventListener('click', (e) => {
                if (!container.contains(e.target)) {
                    resultsBox.style.display = 'none';
                }
            });

            container.appendChild(input);
            container.appendChild(resultsBox);

            // 插入到发帖按钮前面
            newDiscussionBtn.parentElement.insertBefore(container, newDiscussionBtn.parentElement.firstChild);
        }
    }

    // ==================== UI 增强 ====================
    class UIEnhancer {
        // 创建用户信息标签
        static createUserInfoBadge(userInfo) {
            if (!userInfo) return null;

            const badge = document.createElement('span');
            badge.className = 'nodeseek-user-info';
            badge.style.cssText = `
                display: inline-block;
                margin-left: 8px;
                padding: 2px 8px;
                background: ${CONFIG.badgeBackground};
                color: ${CONFIG.badgeTextColor};
                border-radius: 10px;
                font-size: 11px;
                white-space: nowrap;
                vertical-align: middle;
                line-height: 1.5;
            `;

            // 格式化加入天数显示
            let joinText = '';
            if (userInfo.joinDays !== undefined) {
                joinText = `<span title="加入 ${userInfo.joinDays} 天" style="margin-left: 4px;">📅${userInfo.joinDays}天</span>`;
            }

            badge.innerHTML = `
                <span title="等级">⭐${userInfo.level}</span>
                <span title="主题数" style="margin-left: 4px;">📝${userInfo.topicCount}</span>
                <span title="鸡腿数" style="margin-left: 4px;">🍗${userInfo.drumstickCount}</span>
                <span title="评论数" style="margin-left: 4px;">💬${userInfo.commentCount}</span>
                ${joinText}
            `;
            return badge;
        }

        // 标记已访问的帖子
        static markVisitedPost(postElement, postId) {
            if (Storage.isPostVisited(postId)) {
                const titleElement = postElement.querySelector('.post-title a');
                if (titleElement) {
                    titleElement.style.color = CONFIG.visitedColor;
                }
            }
        }

        // 为帖子列表添加用户信息(使用 IntersectionObserver 懒加载)
        static enhancePostList() {
            // 帖子列表项选择器
            const postItems = document.querySelectorAll('.post-list-item');

            // 创建 IntersectionObserver 用于懒加载
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const postItem = entry.target;
                        // 只处理一次
                        observer.unobserve(postItem);
                        this.enhancePostItem(postItem);
                    }
                });
            }, {
                rootMargin: '100px' // 提前 100px 开始加载
            });

            // 观察所有帖子项
            postItems.forEach(postItem => {
                // 先标记已访问的帖子(不需要等待)
                const postId = this.extractPostId(postItem);
                if (postId && CONFIG.enableVisitedMark) {
                    this.markVisitedPost(postItem, postId);
                }

                // 使用 IntersectionObserver 懒加载用户信息
                if (CONFIG.enableUserInfo) {
                    observer.observe(postItem);
                }
            });
        }

        // 增强单个帖子项
        static async enhancePostItem(postItem) {
            // 检查是否已经添加过用户信息(防止重复)
            if (postItem.dataset.enhanced === 'true') {
                return;
            }

            // 标记为已处理
            postItem.dataset.enhanced = 'true';

            const userLink = postItem.querySelector('a[href^="/space/"]');
            if (userLink) {
                const userId = this.extractUserId(userLink);

                if (userId) {
                    const userInfo = await UserInfoFetcher.fetchUserInfo(userId);
                    const badge = this.createUserInfoBadge(userInfo);
                    if (badge) {
                        // 找到帖子标题的链接
                        const titleLink = postItem.querySelector('.post-title > a');
                        if (titleLink && !titleLink.nextElementSibling?.classList.contains('nodeseek-user-info')) {
                            titleLink.insertAdjacentElement('afterend', badge);
                        }
                    }
                }
            }
        }

        // 为帖子详情页添加用户信息(使用懒加载)
        static enhancePostDetail() {
            // 标记当前帖子为已访问
            const postId = this.extractPostIdFromURL();
            if (postId && CONFIG.enableVisitedMark) {
                Storage.markPostAsVisited(postId);
            }

            if (!CONFIG.enableUserInfo) return;

            // 获取所有评论项(包括主帖)
            const contentItems = document.querySelectorAll('.content-item');

            // 创建 IntersectionObserver 用于懒加载
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (entry.isIntersecting) {
                        const contentItem = entry.target;
                        // 只处理一次
                        observer.unobserve(contentItem);
                        this.enhanceContentItem(contentItem);
                    }
                });
            }, {
                rootMargin: '100px' // 提前 100px 开始加载
            });

            // 观察所有内容项
            contentItems.forEach(contentItem => {
                if (CONFIG.enableUserInfo) {
                    observer.observe(contentItem);
                }
            });
        }

        // 增强单个内容项(帖子详情页)
        static async enhanceContentItem(contentItem) {
            // 检查是否已经添加过用户信息
            if (contentItem.dataset.enhanced === 'true') {
                return;
            }

            // 标记为已处理
            contentItem.dataset.enhanced = 'true';

            // 查找用户链接(在 .author-info 或 .nsk-content-meta-info 中)
            const userLink = contentItem.querySelector('.author-info a[href^="/space/"], .nsk-content-meta-info a[href^="/space/"]');

            if (userLink) {
                const userId = this.extractUserId(userLink);

                if (userId) {
                    const userInfo = await UserInfoFetcher.fetchUserInfo(userId);
                    const badge = this.createUserInfoBadge(userInfo);
                    if (badge) {
                        // 在用户名后面插入徽章
                        const authorInfo = contentItem.querySelector('.author-info');
                        if (authorInfo && !authorInfo.querySelector('.nodeseek-user-info')) {
                            // 找到用户名链接后插入
                            const authorNameLink = authorInfo.querySelector('a[href^="/space/"]');
                            if (authorNameLink) {
                                authorNameLink.insertAdjacentElement('afterend', badge);
                            }
                        }
                    }
                }
            }
        }

        // 辅助方法:从元素提取帖子ID
        static extractPostId(element) {
            // 从帖子标题链接提取
            const link = element.querySelector('a[href^="/post-"]');
            if (link) {
                const match = link.href.match(/\/post-(\d+)-/);
                if (match) return match[1];
            }

            return null;
        }

        // 从 URL 提取帖子ID
        static extractPostIdFromURL() {
            const match = window.location.pathname.match(/\/post-(\d+)-/);
            return match ? match[1] : null;
        }

        // 提取用户ID
        static extractUserId(userLink) {
            const match = userLink.href.match(/\/space\/(\d+)/);
            return match ? match[1] : null;
        }
    }

    // ==================== 主程序 ====================
    class NodeSeekEnhancer {
        static init() {
            const siteName = CURRENT_SITE ? CURRENT_SITE.name : 'Unknown';
            console.log(`${siteName} 增强插件已启动`);

            // 根据页面类型执行不同的增强
            if (this.isPostDetailPage()) {
                UIEnhancer.enhancePostDetail();
            } else if (this.isPostListPage()) {
                UIEnhancer.enhancePostList();
            }

            // 监听页面变化(适用于 SPA)
            this.observePageChanges();
        }

        static isPostDetailPage() {
            return /\/post-\d+-/.test(window.location.pathname);
        }

        static isPostListPage() {
            return window.location.pathname === '/' ||
                   /\/(categories|page-)/.test(window.location.pathname);
        }

        static observePageChanges() {
            const observer = new MutationObserver((mutations) => {
                for (const mutation of mutations) {
                    if (mutation.addedNodes.length) {
                        // 延迟执行,等待内容加载完成
                        setTimeout(() => {
                            if (this.isPostDetailPage()) {
                                UIEnhancer.enhancePostDetail();
                            } else if (this.isPostListPage()) {
                                UIEnhancer.enhancePostList();
                            }
                        }, 500);
                        break;
                    }
                }
            });

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

    // ==================== 样式注入 ====================
    function injectStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .post-list .post-title a:visited {
                color: ${CONFIG.visitedColor} !important;
            }
        `;
        document.head.appendChild(style);
    }

    // ==================== 初始化 ====================
    function initialize() {
        // 注册菜单
        MenuManager.registerMenus();

        // 注入样式
        injectStyles();

        // 启动主功能
        NodeSeekEnhancer.init();

        // 添加收藏夹搜索框
        setTimeout(() => CollectionSearch.createSearchBox(), 1000);

        // 添加快照功能
        setTimeout(() => {
            SnapshotManager.addSnapshotButton();
            SnapshotManager.addSaveSnapshotButton();
        }, 1000);

        // 延迟执行签到
        setTimeout(() => SignInManager.signIn(), 2000);
    }

    // 启动插件
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize);
    } else {
        initialize();
    }

})();