NodeSeek & DeepFlood 增强插件

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         NodeSeek & DeepFlood 增强插件
// @namespace    http://tampermonkey.net/
// @version      1.5.5
// @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
// @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('缓存已清除');
                }
            });

            // 重置所有设置
            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 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() {
            // 查找"收藏"元素(包含"收藏 X"文本的span)
            const collectionSpan = Array.from(document.querySelectorAll('span[data-v-244123cf]')).find(
                span => span.textContent.includes('收藏')
            );
            
            if (!collectionSpan) {
                console.log('[收藏夹搜索] 未找到收藏元素,可能不在收藏页面');
                return;
            }

            // 找到 .user-stat 容器(黄色统计框)
            const userStat = collectionSpan.closest('.user-stat');
            if (!userStat) {
                console.log('[收藏夹搜索] 未找到 user-stat 容器');
                return;
            }

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

            // 创建容器
            const container = document.createElement('div');
            container.className = 'collection-search-box';
            container.style.cssText = `
                display: block;
                margin: 15px 0;
                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);

            // 插入到 user-stat 容器后面(黄色框外面)
            userStat.insertAdjacentElement('afterend', container);
        }
    }

    // ==================== 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(() => SignInManager.signIn(), 2000);
    }

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

})();