Greasy Fork

Greasy Fork is available in English.

linux.do 小助手(增强版)

自动浏览、点赞、只看楼主、楼层号、保存帖子到本地、清爽模式、用户信息展示(批量展示)、查看用户话题。支持拖动和隐藏控制面板。支持 linux.do 和 idcflare.com

当前为 2025-10-13 提交的版本,查看 最新版本

// ==UserScript==
// @name        linux.do 小助手(增强版)
// @description 自动浏览、点赞、只看楼主、楼层号、保存帖子到本地、清爽模式、用户信息展示(批量展示)、查看用户话题。支持拖动和隐藏控制面板。支持 linux.do 和 idcflare.com
// @namespace    https://example.com/userscripts
// @match       https://linux.do/*
// @match       https://idcflare.com/*
// @grant       none
// @version     1.4.0
// @author      quantumcat & nulluser & enhanced
// @license     MIT
// @icon        https://www.google.com/s2/favicons?domain=linux.do
// ==/UserScript==

// 获取当前站点域名
const CURRENT_DOMAIN = window.location.hostname;
const BASE_URL = `https://${CURRENT_DOMAIN}`;

// 配置项
const CONFIG = {
    scroll: {
        minSpeed: 10,
        maxSpeed: 15,
        minDistance: 2,
        maxDistance: 4,
        checkInterval: 500,
        fastScrollChance: 0.08,
        fastScrollMin: 80,
        fastScrollMax: 200
    },
    time: {
        browseTime: 3600000,
        restTime: 600000,
        minPause: 300,
        maxPause: 500,
        loadWait: 1500,
    },
    article: {
        commentLimit: 1000,
        topicListLimit: 100,
        retryLimit: 3
    },
    levelRequirements: {
        0: { // 0级升1级
            topics_entered: 5,
            posts_read_count: 30,
            time_read: 600 // 10分钟 = 600秒
        },
        1: { // 1级升2级
            days_visited: 15,
            likes_given: 1,
            likes_received: 1,
            post_count: 3,
            topics_entered: 20,
            posts_read_count: 100,
            time_read: 3600 // 60分钟 = 3600秒
        }
    },
    mustRead: {
        posts: [
            {
                id: '1051',
                url: 'https://linux.do/t/topic/1051/'
            },
            {
                id: '5973',
                url: 'https://linux.do/t/topic/5973'
            },
            {
                id: '102770',
                url: 'https://linux.do/t/topic/102770'
            },
            {
                id: '154010',
                url: 'https://linux.do/t/topic/154010'
            },
            {
                id: '149576',
                url: 'https://linux.do/t/topic/149576'
            },
            {
                id: '22118',
                url: 'https://linux.do/t/topic/22118'
            },
        ],
        likesNeeded: 5
    }
};

// 工具函数
const Utils = {
    random: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
    sleep: (ms) => new Promise(resolve => setTimeout(resolve, ms)),
    isPageLoaded: () => {
        const loadingElements = document.querySelectorAll('.loading, .infinite-scroll');
        return loadingElements.length === 0;
    },
    isNearBottom: () => {
        const {scrollHeight, clientHeight, scrollTop} = document.documentElement;
        return (scrollTop + clientHeight) >= (scrollHeight - 200);
    },
    debounce: (func, wait) => {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }
};

// 存储管理
const Storage = {
    get: (key, defaultValue = null) => {
        try {
            const value = localStorage.getItem(key);
            return value ? JSON.parse(value) : defaultValue;
        } catch {
            return defaultValue;
        }
    },
    set: (key, value) => {
        try {
            localStorage.setItem(key, JSON.stringify(value));
            return true;
        } catch (error) {
            console.error('Storage error:', error);
            return false;
        }
    }
};

// 用户信息助手类
class UserInfoHelper {
    constructor() {
        this.userInfoCache = new Map();
        this.pendingRequests = new Map();
        this.TRUST_LEVEL_LABELS = {
            0: 'Lv0',
            1: 'Lv1',
            2: 'Lv2',
            3: 'Lv3',
            4: 'Lv4'
        };
        this.DAY_IN_MS = 24 * 60 * 60 * 1000;
        this.revealInProgress = false;
        this.isEnabled = true; // 用户信息展示是否启用
        this.observer = null;

        this.init();
    }

    enable() {
        this.isEnabled = true;
        this.init();
    }

    disable() {
        this.isEnabled = false;
        if (this.observer) {
            this.observer.disconnect();
            this.observer = null;
        }
    }

    init() {
        if (!this.isEnabled) return;

        // 如果已有观察器,先断开
        if (this.observer) {
            this.observer.disconnect();
        }

        // 使用防抖,避免频繁触发
        const debouncedEnhance = this.debounce(() => {
            if (this.isEnabled) {
                this.enhanceUserInfo();
            }
        }, 300);

        // 监听页面变化,自动为新加载的用户添加信息
        this.observer = new MutationObserver(() => {
            if (this.isEnabled) {
                debouncedEnhance();
            }
        });

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

        // 初始增强
        this.enhanceUserInfo();
    }

    debounce(func, wait) {
        let timeout;
        return function(...args) {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    }

    isTopicPage() {
        return window.location.pathname.includes('/t/topic/');
    }

    async enhanceUserInfo() {
        if (!this.isTopicPage()) return;

        const articles = document.querySelectorAll('.topic-post article');
        for (const article of articles) {
            const anchor = article.querySelector('.names a[data-user-card]');
            if (!anchor) continue;

            const slug = anchor.getAttribute('data-user-card');
            if (!slug) continue;

            const normalizedSlug = slug.trim().toLowerCase();

            // 检查是否已经添加过信息
            if (article.querySelector(`.user-reg-info[data-user="${normalizedSlug}"]`)) {
                continue;
            }

            // 检查是否是第一楼(楼主)
            const postWrapper = article.closest('.topic-post');
            const postNumber = postWrapper?.getAttribute('data-post-number');
            const isFirstPost = postNumber === '1';

            // 第一楼直接显示,其他楼添加按钮
            if (isFirstPost) {
                await this.loadAndDisplayUserInfo(anchor, slug, normalizedSlug);
            } else {
                this.addInfoButton(anchor, slug, normalizedSlug);
            }
        }
    }

    addInfoButton(anchor, rawSlug, normalizedSlug) {
        const namesContainer = anchor.closest('.names');
        if (!namesContainer) return;

        // 检查是否已有按钮或信息
        if (namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`)) {
            return;
        }

        // 如果已经有信息节点,不添加按钮
        if (namesContainer.querySelector(`.user-reg-info[data-user="${normalizedSlug}"]`)) {
            return;
        }

        const button = document.createElement('button');
        button.className = 'user-info-btn';
        button.setAttribute('data-user', normalizedSlug);
        button.setAttribute('data-raw-slug', rawSlug);
        button.textContent = '📊';
        button.title = '点击查看用户注册信息';
        button.style.cssText = `
            margin-left: 6px;
            font-size: 14px;
            cursor: pointer;
            background: none;
            border: none;
            padding: 2px 4px;
            opacity: 0.6;
            transition: opacity 0.2s;
            vertical-align: middle;
        `;

        button.addEventListener('mouseenter', () => {
            button.style.opacity = '1';
        });

        button.addEventListener('mouseleave', () => {
            button.style.opacity = '0.6';
        });

        button.addEventListener('click', async (e) => {
            e.preventDefault();
            e.stopPropagation();
            if (button.disabled) return;

            button.disabled = true;
            button.textContent = '⏳';

            try {
                await this.loadAndDisplayUserInfo(anchor, rawSlug, normalizedSlug);
                // 成功后按钮会被 loadAndDisplayUserInfo 中移除
            } catch (error) {
                console.error('加载用户信息失败:', error);
                button.textContent = '📊';
                button.disabled = false;
            }
        });

        anchor.insertAdjacentElement('afterend', button);

        // 添加"查看话题"按钮
        this.addTopicsButton(anchor, rawSlug, normalizedSlug);
    }

    addTopicsButton(anchor, rawSlug, normalizedSlug) {
        const namesContainer = anchor.closest('.names');
        if (!namesContainer) return;

        // 检查是否已有话题按钮
        if (namesContainer.querySelector(`.user-topics-btn[data-user="${normalizedSlug}"]`)) {
            return;
        }

        const topicsBtn = document.createElement('a');
        topicsBtn.className = 'user-topics-btn';
        topicsBtn.setAttribute('data-user', normalizedSlug);
        topicsBtn.href = `${BASE_URL}/u/${rawSlug}/activity/topics`;
        topicsBtn.target = '_blank';
        topicsBtn.textContent = '查看话题';
        topicsBtn.title = '查看该用户的话题';
        topicsBtn.style.cssText = `
            margin-left: 6px;
            font-size: 12px;
            cursor: pointer;
            text-decoration: none;
            padding: 2px 6px;
            opacity: 0.7;
            transition: all 0.2s;
            vertical-align: middle;
            display: inline-block;
            color: #667eea;
            background: rgba(102, 126, 234, 0.1);
            border-radius: 4px;
        `;

        topicsBtn.addEventListener('mouseenter', () => {
            topicsBtn.style.opacity = '1';
            topicsBtn.style.background = 'rgba(102, 126, 234, 0.2)';
        });

        topicsBtn.addEventListener('mouseleave', () => {
            topicsBtn.style.opacity = '0.7';
            topicsBtn.style.background = 'rgba(102, 126, 234, 0.1)';
        });

        // 插入到信息按钮后面
        const infoBtn = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
        if (infoBtn) {
            infoBtn.insertAdjacentElement('afterend', topicsBtn);
        } else {
            anchor.insertAdjacentElement('afterend', topicsBtn);
        }
    }

    async loadAndDisplayUserInfo(anchor, slug, normalizedSlug) {
        const namesContainer = anchor.closest('.names');
        if (!namesContainer) return;

        // 再次检查是否已经存在,避免重复
        const existingInfo = namesContainer.querySelector(`.user-reg-info[data-user="${normalizedSlug}"]`);
        if (existingInfo) {
            console.log(`用户 ${normalizedSlug} 信息已存在,跳过`);
            // 确保按钮被移除
            const button = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
            if (button) button.remove();
            return;
        }

        const info = await this.fetchUserInfo(slug, normalizedSlug);
        if (!info) {
            // 获取失败,恢复按钮
            const button = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
            if (button) {
                button.textContent = '📊';
                button.disabled = false;
            }
            return;
        }

        const infoNode = this.buildInfoNode(info, normalizedSlug);
        if (!infoNode) {
            // 构建失败,恢复按钮
            const button = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
            if (button) {
                button.textContent = '📊';
                button.disabled = false;
            }
            return;
        }

        // 最后一次检查,确保在异步等待期间没有被其他调用添加
        const finalCheck = namesContainer.querySelector(`.user-reg-info[data-user="${normalizedSlug}"]`);
        if (finalCheck) {
            console.log(`用户 ${normalizedSlug} 信息在等待期间已被添加,跳过`);
            // 移除按钮
            const button = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
            if (button) button.remove();
            return;
        }

        // 先移除信息按钮
        const button = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
        if (button) button.remove();

        // 添加信息节点
        anchor.insertAdjacentElement('afterend', infoNode);

        // 确保话题按钮存在(如果还没有添加)
        if (!namesContainer.querySelector(`.user-topics-btn[data-user="${normalizedSlug}"]`)) {
            this.addTopicsButton(anchor, slug, normalizedSlug);
        }
    }

    async fetchUserInfo(slug, normalizedSlug) {
        // 检查缓存
        if (this.userInfoCache.has(normalizedSlug)) {
            return this.userInfoCache.get(normalizedSlug);
        }

        // 检查是否正在请求
        if (this.pendingRequests.has(normalizedSlug)) {
            return this.pendingRequests.get(normalizedSlug);
        }

        // 创建请求
        const requestPromise = this.doFetchUserInfo(slug, normalizedSlug);
        this.pendingRequests.set(normalizedSlug, requestPromise);

        try {
            const info = await requestPromise;
            if (info) {
                this.userInfoCache.set(normalizedSlug, info);
            }
            return info;
        } finally {
            this.pendingRequests.delete(normalizedSlug);
        }
    }

    async doFetchUserInfo(slug, normalizedSlug) {
        try {
            // 使用两个API并行请求,与原脚本保持一致
            const PROFILE_API_BUILDERS = [
                (s) => `${BASE_URL}/u/${encodeURIComponent(s)}.json`,
                (s) => `${BASE_URL}/users/${encodeURIComponent(s)}.json`,
            ];

            const SUMMARY_API_BUILDERS = [
                (s) => `${BASE_URL}/u/${encodeURIComponent(s)}/summary.json`,
                (s) => `${BASE_URL}/users/${encodeURIComponent(s)}/summary.json`,
            ];

            const [profileData, summaryData] = await Promise.all([
                this.fetchFirstAvailable(PROFILE_API_BUILDERS, slug),
                this.fetchFirstAvailable(SUMMARY_API_BUILDERS, slug),
            ]);

            if (!profileData && !summaryData) {
                return null;
            }

            const user = profileData && (profileData.user || profileData);
            const summary = summaryData && (summaryData.user_summary || summaryData.summary || summaryData);

            const createdAt = this.pickCreatedAt(user) || (summary && this.pickCreatedAt(summary));
            if (!createdAt) {
                return null;
            }

            const topicCount = this.pickFirstNumber(
                user && (user.topic_count ?? user.topicCount),
                summary && (summary.topic_count ?? summary.topics_count),
            );

            const totalPostCount = this.pickFirstNumber(
                user && (user.post_count ?? user.postCount),
                summary && (summary.post_count ?? summary.posts_count),
            );

            let repliesCount = this.pickFirstNumber(
                summary && (summary.replies_count ?? summary.reply_count),
            );
            if (repliesCount === null && totalPostCount !== null && topicCount !== null) {
                repliesCount = Math.max(0, totalPostCount - topicCount);
            }

            const trustLevelRaw = this.pickFirstValue(
                user && (user.trust_level ?? user.trustLevel),
                summary && (summary.trust_level ?? summary.trustLevel),
            );
            const trustLevel = this.normalizeTrustLevel(trustLevelRaw);

            const days = this.calcDays(createdAt);

            return {
                slug: normalizedSlug,
                createdAt,
                days,
                topicCount: typeof topicCount === 'number' && Number.isFinite(topicCount) ? topicCount : undefined,
                repliesCount: typeof repliesCount === 'number' && Number.isFinite(repliesCount) ? repliesCount : undefined,
                trustLevel
            };
        } catch (error) {
            console.error('获取用户信息失败:', slug, error);
            return null;
        }
    }

    async fetchFirstAvailable(builders, slug) {
        for (const builder of builders) {
            const url = builder(slug);
            const data = await this.safeFetchJson(url);
            if (data) {
                return data;
            }
        }
        return null;
    }

    async safeFetchJson(url) {
        try {
            const response = await fetch(url, { credentials: 'include' });
            if (!response.ok) {
                return null;
            }
            return await response.json();
        } catch (error) {
            return null;
        }
    }

    pickFirstNumber(...values) {
        for (const value of values) {
            const numberValue = Number(value);
            if (!Number.isNaN(numberValue)) {
                return numberValue;
            }
        }
        return null;
    }

    pickFirstValue(...values) {
        for (const value of values) {
            if (value !== undefined && value !== null) {
                return value;
            }
        }
        return null;
    }

    normalizeTrustLevel(raw) {
        if (raw === undefined || raw === null) {
            return undefined;
        }

        if (typeof raw === 'number' && Number.isFinite(raw)) {
            return raw;
        }

        if (typeof raw === 'string') {
            const TRUST_LEVEL_ALIAS = {
                newuser: 0,
                basic: 1,
                member: 2,
                regular: 3,
                leader: 4,
            };
            const alias = TRUST_LEVEL_ALIAS[raw.toLowerCase()];
            if (alias !== undefined) {
                return alias;
            }
            const numeric = Number(raw);
            if (!Number.isNaN(numeric)) {
                return numeric;
            }
        }

        return undefined;
    }

    pickCreatedAt(source) {
        if (!source) {
            return null;
        }
        return (
            source.created_at ||
            source.createdAt ||
            source.registration_date ||
            source.registrationDate ||
            source.joined ||
            source.joinedAt ||
            null
        );
    }

    calcDays(createdAt) {
        const createdTime = new Date(createdAt).getTime();
        if (Number.isNaN(createdTime)) {
            return 0;
        }
        const diff = Date.now() - createdTime;
        return Math.max(0, Math.floor(diff / this.DAY_IN_MS));
    }

    buildInfoNode(info, normalizedSlug) {
        const segments = [`注册 ${this.formatNumber(info.days)} 天`];

        if (typeof info.topicCount === 'number' && Number.isFinite(info.topicCount)) {
            segments.push(`发帖 ${this.formatNumber(info.topicCount)}`);
        }

        if (typeof info.repliesCount === 'number' && Number.isFinite(info.repliesCount)) {
            segments.push(`回帖 ${this.formatNumber(info.repliesCount)}`);
        }

        if (typeof info.trustLevel === 'number' && Number.isFinite(info.trustLevel)) {
            const FULL_TRUST_LEVEL_LABELS = {
                0: 'Lv0 新手',
                1: 'Lv1 入门',
                2: 'Lv2 成员',
                3: 'Lv3 常驻',
                4: 'Lv4 领袖',
            };
            const label = FULL_TRUST_LEVEL_LABELS[info.trustLevel] || `信任级别 Lv${info.trustLevel}`;
            segments.push(label);
        }

        if (!segments.length) {
            return null;
        }

        const span = document.createElement('span');
        span.className = 'user-reg-info';
        span.setAttribute('data-user', normalizedSlug);
        span.textContent = ` · ${segments.join(' · ')}`;
        span.style.cssText = `
            margin-left: 6px;
            font-size: 12px;
            color: #1a4c7c;
        `;

        return span;
    }

    formatNumber(value) {
        return Number(value).toLocaleString('zh-CN');
    }

    // 批量展示所有已加载的回复用户信息
    async revealAllVisibleReplies() {
        if (!this.isTopicPage()) return;
        if (this.revealInProgress) return;

        this.revealInProgress = true;

        try {
            const articles = document.querySelectorAll('.topic-post article');

            for (let index = 0; index < articles.length; index++) {
                const article = articles[index];

                // 跳过第一楼(楼主)
                const postWrapper = article.closest('.topic-post');
                const postNumber = postWrapper?.getAttribute('data-post-number');
                if (postNumber === '1') continue;

                const anchor = article.querySelector('.names a[data-user-card]');
                if (!anchor) continue;

                const slug = anchor.getAttribute('data-user-card');
                if (!slug) continue;

                const normalizedSlug = slug.trim().toLowerCase();
                const namesContainer = anchor.closest('.names');
                if (!namesContainer) continue;

                // 检查是否已经展示过
                const hasInfo = namesContainer.querySelector(`.user-reg-info[data-user="${normalizedSlug}"]`);
                if (hasInfo) {
                    // 移除可能残留的按钮
                    const button = namesContainer.querySelector(`.user-info-btn[data-user="${normalizedSlug}"]`);
                    if (button) button.remove();
                    continue;
                }

                // 加载并显示用户信息
                await this.loadAndDisplayUserInfo(anchor, slug, normalizedSlug);
            }
        } catch (error) {
            console.error('批量展示用户信息失败:', error);
        } finally {
            this.revealInProgress = false;
        }
    }
}

class BrowseController {
    constructor() {
        this.isScrolling = false;
        this.scrollInterval = null;
        this.pauseTimeout = null;
        this.trustLevelMonitorInterval = null; // 等级监控定时器

        // 使用 sessionStorage 存储窗口独立的状态
        this.accumulatedTime = this.getSessionStorage('accumulatedTime', 0);
        this.lastActionTime = Date.now();
        this.isTopicPage = window.location.href.includes("/t/topic/");
        this.autoRunning = this.getSessionStorage('autoRunning', false);
        this.topicList = this.getSessionStorage('topicList', []);

        // 使用 localStorage 存储全局共享的状态
        this.firstUseChecked = Storage.get('firstUseChecked', false);
        this.likesCount = Storage.get('likesCount', 0);
        this.selectedPost = Storage.get('selectedPost', null);
        this.autoLikeEnabled = Storage.get('autoLikeEnabled', false);
        this.cleanModeEnabled = Storage.get('cleanModeEnabled', false);
        this.likedTopics = Storage.get('likedTopics', []);
        this.panelMinimized = Storage.get('panelMinimized', false);
        this.panelPosition = Storage.get('panelPosition', { x: null, y: null });
        this.likeResumeTime = Storage.get('likeResumeTime', null);
        this.currentUsername = null; // 当前用户名
        this.lastDetectedUser = null; // 上次检测到的用户名(用于账号切换检测)

        // 检查是否到达恢复点赞的时间
        this.checkLikeResumeTime();
        // 监听点赞限制弹窗
        this.observeLikeLimit();

        this.setupButton();
        this.loadUserTrustLevel(); // 加载用户信任等级
        this.startUserSwitchMonitoring(); // 启动账号切换监控
        this.initFloorNumberDisplay();
        this.setupWindowResizeHandler(); // 设置窗口大小调整处理
        this.applyCleanModeStyles();
        this.initOnlyOwnerView();

        if (!this.firstUseChecked) {
            this.handleFirstUse();
        } else if (this.autoRunning) {
            if (this.isTopicPage) {
                this.startScrolling();
                if (this.autoLikeEnabled) {
                    this.autoLikeTopic();
                }
            } else {
                this.getLatestTopics().then(() => this.navigateNextTopic());
            }
        }

        if (this.autoLikeEnabled && this.isTopicPage) {
            this.autoLikeTopic();
        }

        // 初始化用户信息助手 - 默认启用,让每个窗口独立工作
        this.userInfoHelper = new UserInfoHelper();

        // 启动等级监控(60秒刷新一次)- 默认启用
        this.startTrustLevelMonitor();
    }

    // 启动等级监控(60秒刷新一次)
    startTrustLevelMonitor() {
        // 如果已经有定时器在运行,先清除
        if (this.trustLevelMonitorInterval) {
            clearInterval(this.trustLevelMonitorInterval);
        }

        this.trustLevelMonitorInterval = setInterval(() => {
            console.log('自动刷新等级信息...');
            this.loadUserTrustLevel(false);
        }, 60000); // 60秒

        console.log('等级监控已启动(60秒刷新一次)');
    }

    // 停止等级监控
    stopTrustLevelMonitor() {
        if (this.trustLevelMonitorInterval) {
            clearInterval(this.trustLevelMonitorInterval);
            this.trustLevelMonitorInterval = null;
            console.log('等级监控已停止');
        }
    }

    // sessionStorage 辅助方法(用于窗口独立状态)
    getSessionStorage(key, defaultValue = null) {
        try {
            const value = sessionStorage.getItem(key);
            return value ? JSON.parse(value) : defaultValue;
        } catch {
            return defaultValue;
        }
    }

    setSessionStorage(key, value) {
        try {
            sessionStorage.setItem(key, JSON.stringify(value));
            return true;
        } catch (error) {
            console.error('SessionStorage error:', error);
            return false;
        }
    }

    addGlobalStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .linuxdo-helper-panel {
                position: fixed;
                right: 20px;
                top: 50%;
                transform: translateY(-50%);
                width: 280px;
                min-width: 280px;
                max-height: calc(100vh - 40px);
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                border-radius: 16px;
                box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
                z-index: 99999;
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
                overflow-y: auto;
                overflow-x: hidden;
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                backdrop-filter: blur(10px);
                will-change: transform;
            }

            .linuxdo-helper-panel:hover {
                box-shadow: 0 15px 50px rgba(0, 0, 0, 0.4);
                transform: translateY(-2px);
            }

            .linuxdo-helper-panel.minimized {
                width: 280px;
            }

            .panel-header {
                background: rgba(255, 255, 255, 0.15);
                padding: 12px 16px;
                cursor: move;
                display: flex;
                justify-content: space-between;
                align-items: center;
                user-select: none;
                border-bottom: 1px solid rgba(255, 255, 255, 0.2);
            }

            .panel-header:active {
                cursor: grabbing;
            }

            .panel-title {
                color: white;
                font-weight: 600;
                font-size: 14px;
                text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            }

            .panel-controls {
                display: flex;
                gap: 8px;
            }

            .panel-control-btn {
                width: 24px;
                height: 24px;
                border-radius: 6px;
                border: none;
                background: rgba(255, 255, 255, 0.2);
                color: white;
                cursor: pointer;
                font-size: 14px;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: all 0.2s;
                padding: 0;
                line-height: 1;
            }

            .panel-control-btn:hover {
                background: rgba(255, 255, 255, 0.3);
                transform: scale(1.1);
            }

            .panel-control-btn:active {
                transform: scale(0.95);
            }

            .panel-content {
                padding: 12px;
                display: flex;
                flex-direction: column;
                gap: 6px;
                transition: all 0.3s;
                overflow: hidden;
            }

            .panel-content.hidden {
                max-height: 0;
                padding: 0 16px;
            }

            .main-action-btn {
                width: 100%;
                padding: 8px 12px;
                font-size: 13px;
                font-weight: 600;
                background: white;
                color: #667eea;
                border: none;
                border-radius: 8px;
                cursor: pointer;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 5px;
                white-space: nowrap;
                overflow: hidden;
                min-height: 32px;
                line-height: 1.1;
            }

            .main-action-btn .btn-text {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                flex: 1;
                min-width: 0;
            }

            .main-action-btn .btn-icon {
                flex-shrink: 0;
                font-size: 14px;
            }

            .main-action-btn:hover {
                transform: translateY(-2px);
                box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
            }

            .main-action-btn:active {
                transform: translateY(0);
            }

            .main-action-btn.running {
                background: #ff6b6b;
                color: white;
            }

            .btn-icon {
                font-size: 18px;
            }

            .trust-level-row {
                background: rgba(255, 255, 255, 0.15);
                padding: 8px 12px;
                border-radius: 10px;
                margin-top: 8px;
            }

            .trust-level-header {
                color: white;
                font-size: 13px;
                font-weight: 600;
                margin-bottom: 6px;
                text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
                display: flex;
                justify-content: space-between;
                align-items: center;
            }

            .trust-level-refresh {
                background: rgba(255, 255, 255, 0.2);
                border: none;
                color: white;
                padding: 4px 8px;
                border-radius: 4px;
                cursor: pointer;
                font-size: 11px;
                transition: all 0.2s;
            }

            .trust-level-refresh:hover {
                background: rgba(255, 255, 255, 0.3);
                transform: scale(1.05);
            }

            .trust-level-refresh:disabled {
                opacity: 0.5;
                cursor: not-allowed;
            }

            .trust-level-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                color: rgba(255, 255, 255, 0.9);
                font-size: 11px;
                margin: 4px 0;
                padding: 3px 0;
            }

            .trust-level-name {
                flex: 1;
            }

            .trust-level-progress {
                display: flex;
                align-items: center;
                gap: 6px;
            }

            .trust-level-bar {
                width: 60px;
                height: 6px;
                background: rgba(255, 255, 255, 0.2);
                border-radius: 3px;
                overflow: hidden;
            }

            .trust-level-bar-fill {
                height: 100%;
                background: linear-gradient(90deg, #48bb78 0%, #68d391 100%);
                transition: width 0.3s;
            }

            .trust-level-bar-fill.completed {
                background: linear-gradient(90deg, #4299e1 0%, #63b3ed 100%);
            }

            .trust-level-value {
                font-size: 10px;
                color: rgba(255, 255, 255, 0.8);
                min-width: 50px;
                text-align: right;
            }

            .trust-level-loading {
                color: rgba(255, 255, 255, 0.7);
                font-size: 11px;
                text-align: center;
                padding: 8px 0;
            }

            .random-floor-btn, .reveal-users-btn {
                width: 100%;
                padding: 7px 12px;
                font-size: 12px;
                font-weight: 600;
                background: rgba(255, 255, 255, 0.95);
                color: #667eea;
                border: none;
                border-radius: 8px;
                cursor: pointer;
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
                transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                display: flex;
                align-items: center;
                justify-content: center;
                gap: 6px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                min-height: 28px;
                line-height: 1.2;
                margin-bottom: 6px;
            }

            .reveal-users-btn {
                margin-bottom: 0;
            }

            .random-floor-btn .btn-text,
            .reveal-users-btn .btn-text {
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                flex: 1;
                min-width: 0;
            }

            .random-floor-btn .btn-icon,
            .reveal-users-btn .btn-icon {
                flex-shrink: 0;
                font-size: 13px;
            }

            .random-floor-btn:hover, .reveal-users-btn:hover {
                transform: translateY(-2px);
                box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
                background: rgba(255, 255, 255, 1);
            }

            .random-floor-btn:active, .reveal-users-btn:active {
                transform: translateY(0);
            }

            .reveal-users-btn:disabled {
                opacity: 0.6;
                cursor: not-allowed;
                transform: none !important;
            }

            .toggle-row {
                background: rgba(255, 255, 255, 0.15);
                padding: 5px 10px;
                border-radius: 8px;
                display: flex;
                justify-content: space-between;
                align-items: center;
                transition: all 0.2s;
                min-height: 26px;
            }

            .toggle-row:hover {
                background: rgba(255, 255, 255, 0.22);
            }

            .toggle-label {
                color: white;
                font-size: 12px;
                font-weight: 500;
                text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
            }

            .toggle-switch {
                position: relative;
                width: 36px;
                height: 20px;
                flex-shrink: 0;
            }

            .toggle-switch input {
                opacity: 0;
                width: 0;
                height: 0;
            }

            .toggle-slider {
                position: absolute;
                cursor: pointer;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: rgba(255, 255, 255, 0.3);
                transition: 0.3s;
                border-radius: 26px;
            }

            .toggle-slider:before {
                position: absolute;
                content: "";
                height: 14px;
                width: 14px;
                left: 3px;
                bottom: 3px;
                background-color: white;
                transition: 0.3s;
                border-radius: 50%;
                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            }

            .toggle-switch input:checked + .toggle-slider {
                background-color: rgba(76, 175, 80, 0.8);
            }

            .toggle-switch input:checked + .toggle-slider:before {
                transform: translateX(16px);
            }

            .section-divider {
                height: 1px;
                background: rgba(255, 255, 255, 0.2);
                margin: 6px 0;
            }

            .section-title {
                color: rgba(255, 255, 255, 0.9);
                font-size: 12px;
                font-weight: 600;
                margin: 4px 0 4px 0;
                padding: 0 4px;
                text-transform: uppercase;
                letter-spacing: 0.5px;
            }

            @keyframes fadeIn {
                from {
                    opacity: 0;
                    transform: scale(0.8);
                }
                to {
                    opacity: 1;
                    transform: scale(1);
                }
            }

            .linuxdo-helper-panel {
                animation: fadeIn 0.3s ease-out;
            }
        `;
        document.head.appendChild(style);
    }

    setupButton() {
        this.addGlobalStyles();

        // 创建主容器
        this.container = document.createElement("div");
        this.container.className = "linuxdo-helper-panel";
        if (this.panelMinimized) {
            this.container.classList.add('minimized');
        }

        // 如果有保存的位置,使用保存的位置
        if (this.panelPosition.x !== null && this.panelPosition.y !== null) {
            // 使用绝对定位的 left/top 而不是 transform,避免与居中 transform 冲突
            this.container.style.position = 'fixed';
            this.container.style.left = this.panelPosition.x + 'px';
            this.container.style.top = this.panelPosition.y + 'px';
            this.container.style.right = 'auto';
            this.container.style.bottom = 'auto';
            this.container.style.transform = 'none';
        }

        // 创建面板头部
        const header = document.createElement("div");
        header.className = "panel-header";
        header.innerHTML = `
            <span class="panel-title">📚 Linux.do 助手</span>
            <div class="panel-controls">
                <button class="panel-control-btn minimize-btn" title="最小化">─</button>
            </div>
        `;

        // 创建面板内容区
        const content = document.createElement("div");
        content.className = "panel-content";
        if (this.panelMinimized) {
            content.classList.add('hidden');
        }

        // 主按钮
        this.button = document.createElement("button");
        this.button.className = "main-action-btn" + (this.autoRunning ? " running" : "");
        this.button.innerHTML = this.autoRunning
            ? '<span class="btn-icon">⏸</span><span class="btn-text">停止阅读</span>'
            : '<span class="btn-icon">▶</span><span class="btn-text">开始阅读</span>';
        this.button.addEventListener("click", () => this.handleButtonClick());

        // 随机楼层按钮
        this.randomBtn = document.createElement("button");
        this.randomBtn.className = "random-floor-btn";
        this.randomBtn.innerHTML = '<span class="btn-icon">🎲</span><span class="btn-text">随机楼层</span>';
        this.randomBtn.addEventListener("click", () => this.randomJump());
        this.randomBtn.style.display = this.isTopicPage ? 'flex' : 'none';
        this.randomBtn.title = '随机跳转到某个楼层(抽奖用)';

        // 批量展示用户信息按钮
        this.revealUsersBtn = document.createElement("button");
        this.revealUsersBtn.className = "reveal-users-btn";
        this.revealUsersBtn.innerHTML = '<span class="btn-icon">📊</span><span class="btn-text">批量展示信息</span>';
        this.revealUsersBtn.addEventListener("click", () => this.handleRevealUsersClick());
        this.revealUsersBtn.style.display = this.isTopicPage ? 'flex' : 'none';
        this.revealUsersBtn.title = '批量展示当前页面所有已加载回复的用户信息';

        // 自动点赞开关
        const autoLikeRow = this.createToggleRow(
            "👍 自动点赞主题",
            this.autoLikeEnabled,
            (checked) => {
                // 检查是否在冷却期
                if (checked && this.likeResumeTime && Date.now() < this.likeResumeTime) {
                    const now = Date.now();
                    const remainingHours = Math.ceil((this.likeResumeTime - now) / (1000 * 60 * 60));
                    const resumeDate = new Date(this.likeResumeTime);
                    this.showNotification(`点赞功能冷却中,将在 ${resumeDate.toLocaleTimeString()} 恢复`);
                    console.log(`点赞冷却中,还需约 ${remainingHours} 小时,无法开启`);

                    // 恢复开关状态为关闭
                    setTimeout(() => {
                        const toggleRows = this.container.querySelectorAll('.toggle-row');
                        for (const row of toggleRows) {
                            const label = row.querySelector('.toggle-label');
                            if (label && label.textContent.includes('自动点赞')) {
                                const input = row.querySelector('input[type="checkbox"]');
                                if (input) {
                                    input.checked = false;
                                }
                                break;
                            }
                        }
                    }, 100);
                    return;
                }

                this.autoLikeEnabled = checked;
                Storage.set('autoLikeEnabled', this.autoLikeEnabled);
                console.log(`自动点赞主题: ${this.autoLikeEnabled ? '开启' : '关闭'}`);
                if (this.autoLikeEnabled && this.isTopicPage) {
                    this.autoLikeTopic();
                }
            }
        );

        // 清爽模式开关
        const cleanModeRow = this.createToggleRow(
            "✨ 清爽模式",
            this.cleanModeEnabled,
            (checked) => {
                this.cleanModeEnabled = checked;
                Storage.set('cleanModeEnabled', this.cleanModeEnabled);
                console.log(`清爽模式: ${this.cleanModeEnabled ? '开启' : '关闭'}`);
                this.toggleCleanMode();
            }
        );

        // 清除点赞冷却按钮
        this.clearCooldownBtn = document.createElement("button");
        this.clearCooldownBtn.className = "reveal-users-btn";
        this.clearCooldownBtn.innerHTML = '<span class="btn-icon">🔥</span><span class="btn-text">清除点赞冷却</span>';
        this.clearCooldownBtn.addEventListener("click", () => this.handleClearCooldown());
        this.clearCooldownBtn.title = '清除点赞冷却时间,立即恢复点赞功能';
        this.clearCooldownBtn.style.background = 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)';

        // 信任等级显示容器
        this.trustLevelContainer = document.createElement("div");
        this.trustLevelContainer.className = "trust-level-row";
        this.trustLevelContainer.innerHTML = '<div class="trust-level-loading">加载等级信息...</div>';

        // 组装面板 - 按功能分类
        // 📖 自动阅读区(包含阅读按钮和相关设置)
        const autoSection = document.createElement("div");
        autoSection.innerHTML = '<div class="section-title">📖 自动阅读</div>';
        content.appendChild(autoSection);
        content.appendChild(this.button);
        content.appendChild(autoLikeRow);
        content.appendChild(this.clearCooldownBtn);
        content.appendChild(cleanModeRow);

        // 分隔线1
        this.divider1 = document.createElement("div");
        this.divider1.className = "section-divider";
        content.appendChild(this.divider1);

        // 🛠️ 工具功能区(只在文章页显示)
        this.toolSectionContainer = document.createElement("div");
        this.toolSectionContainer.className = "tool-section-container";
        const toolSection = document.createElement("div");
        toolSection.innerHTML = '<div class="section-title">🛠️ 工具功能</div>';
        this.toolSectionContainer.appendChild(toolSection);
        this.toolSectionContainer.appendChild(this.randomBtn);
        this.toolSectionContainer.appendChild(this.revealUsersBtn);
        content.appendChild(this.toolSectionContainer);

        // 分隔线2
        this.divider2 = document.createElement("div");
        this.divider2.className = "section-divider";
        content.appendChild(this.divider2);

        // 📊 账号信息区
        const accountSection = document.createElement("div");
        accountSection.innerHTML = '<div class="section-title">📊 账号信息</div>';
        content.appendChild(accountSection);
        content.appendChild(this.trustLevelContainer);

        this.container.appendChild(header);
        this.container.appendChild(content);
        document.body.appendChild(this.container);

        // 添加拖动功能
        this.makeDraggable(header);

        // 添加最小化功能
        header.querySelector('.minimize-btn').addEventListener('click', (e) => {
            e.stopPropagation();
            this.toggleMinimize();
        });
    }

    createToggleRow(label, checked, onChange) {
        const row = document.createElement("div");
        row.className = "toggle-row";

        const labelEl = document.createElement("span");
        labelEl.className = "toggle-label";
        labelEl.textContent = label;

        const toggleSwitch = document.createElement("label");
        toggleSwitch.className = "toggle-switch";

        const input = document.createElement("input");
        input.type = "checkbox";
        input.checked = checked;
        input.addEventListener("change", (e) => onChange(e.target.checked));

        const slider = document.createElement("span");
        slider.className = "toggle-slider";

        toggleSwitch.appendChild(input);
        toggleSwitch.appendChild(slider);

        row.appendChild(labelEl);
        row.appendChild(toggleSwitch);

        return row;
    }

    makeDraggable(header) {
        let isDragging = false;
        let currentX;
        let currentY;
        let initialX;
        let initialY;
        let rafId = null;

        // 禁用过渡效果以提高拖动流畅度
        const disableTransition = () => {
            this.container.style.transition = 'none';
        };

        const enableTransition = () => {
            this.container.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
        };

        header.addEventListener('mousedown', (e) => {
            if (e.target.classList.contains('panel-control-btn') ||
                e.target.closest('.panel-control-btn')) {
                return;
            }

            isDragging = true;
            disableTransition();

            const rect = this.container.getBoundingClientRect();
            initialX = e.clientX - rect.left;
            initialY = e.clientY - rect.top;

            // 使用捕获阶段,提高响应速度
            document.addEventListener('mousemove', onMouseMove, true);
            document.addEventListener('mouseup', onMouseUp, true);

            // 防止文本选择
            e.preventDefault();
        });

        const updatePosition = () => {
            // 限制在视窗内
            const maxX = window.innerWidth - this.container.offsetWidth;
            const maxY = window.innerHeight - this.container.offsetHeight;

            currentX = Math.max(0, Math.min(currentX, maxX));
            currentY = Math.max(0, Math.min(currentY, maxY));

            // 使用 left/top 定位(避免与居中 transform 冲突)
            this.container.style.position = 'fixed';
            this.container.style.left = currentX + 'px';
            this.container.style.top = currentY + 'px';
            this.container.style.right = 'auto';
            this.container.style.bottom = 'auto';
            this.container.style.transform = 'none';

            // 保存位置供页面调整大小时使用
            this.currentTranslateX = currentX;
            this.currentTranslateY = currentY;
        };

        const onMouseMove = (e) => {
            if (!isDragging) return;

            e.preventDefault();
            e.stopPropagation();

            currentX = e.clientX - initialX;
            currentY = e.clientY - initialY;

            // 使用 requestAnimationFrame 确保流畅渲染
            if (rafId) {
                cancelAnimationFrame(rafId);
            }
            rafId = requestAnimationFrame(updatePosition);
        };

        const onMouseUp = () => {
            if (isDragging) {
                isDragging = false;
                enableTransition();

                // 取消未完成的动画帧
                if (rafId) {
                    cancelAnimationFrame(rafId);
                    rafId = null;
                }

                // 保存最终位置
                this.panelPosition = { x: currentX, y: currentY };
                Storage.set('panelPosition', this.panelPosition);
            }
            document.removeEventListener('mousemove', onMouseMove, true);
            document.removeEventListener('mouseup', onMouseUp, true);
        };
    }

    toggleMinimize() {
        this.panelMinimized = !this.panelMinimized;
        Storage.set('panelMinimized', this.panelMinimized);

        const content = this.container.querySelector('.panel-content');
        if (this.panelMinimized) {
            content.classList.add('hidden');
            this.container.classList.add('minimized');
        } else {
            content.classList.remove('hidden');
            this.container.classList.remove('minimized');
        }
    }

    setupWindowResizeHandler() {
        // 监听窗口大小变化,确保面板始终在可见区域内
        let resizeTimer;

        const adjustPosition = () => {
            const rect = this.container.getBoundingClientRect();
            const maxX = window.innerWidth - rect.width;
            const maxY = window.innerHeight - rect.height;

            // 如果面板有自定义位置
            if (this.panelPosition.x !== null && this.panelPosition.y !== null) {
                // 当前位置(从 transform 获取)
                let currentX = this.currentTranslateX || this.panelPosition.x;
                let currentY = this.currentTranslateY || this.panelPosition.y;

                // 限制在视窗内(预留20px边距)
                currentX = Math.max(20, Math.min(currentX, maxX - 20));
                currentY = Math.max(20, Math.min(currentY, maxY - 20));

                // 更新位置
                this.container.style.position = 'fixed';
                this.container.style.left = currentX + 'px';
                this.container.style.top = currentY + 'px';
                this.container.style.transform = 'none';
                this.currentTranslateX = currentX;
                this.currentTranslateY = currentY;

                // 保存新位置
                this.panelPosition = { x: currentX, y: currentY };
                Storage.set('panelPosition', this.panelPosition);
            } else {
                // 默认位置:垂直居中,右侧20px
                // 确保面板高度不超过视口
                const currentRect = this.container.getBoundingClientRect();

                // 如果面板太高,调整为顶部对齐并限制最大高度
                if (currentRect.height > window.innerHeight - 40) {
                    this.container.style.top = '20px';
                    this.container.style.transform = 'none';
                    this.container.style.maxHeight = (window.innerHeight - 40) + 'px';
                } else {
                    // 否则保持垂直居中
                    this.container.style.top = '50%';
                    this.container.style.transform = 'translateY(-50%)';
                }

                // 确保不超出右边
                if (currentRect.right > window.innerWidth) {
                    this.container.style.right = '0px';
                }
            }
        };

        window.addEventListener('resize', () => {
            clearTimeout(resizeTimer);
            resizeTimer = setTimeout(adjustPosition, 100);
        });

        // 初始调整一次
        setTimeout(adjustPosition, 500);
    }

    checkLikeResumeTime() {
        if (this.likeResumeTime) {
            const now = Date.now();
            if (now >= this.likeResumeTime) {
                // 时间到了,清除冷却时间
                console.log('点赞冷却时间已过,可以正常使用点赞功能');
                this.likeResumeTime = null;
                Storage.set('likeResumeTime', null);
                this.updateClearCooldownButton();
                // 不自动开启点赞,由用户决定
            } else {
                // 还在冷却期,记录状态但不修改开关
                const remainingHours = Math.ceil((this.likeResumeTime - now) / (1000 * 60 * 60));
                const resumeDate = new Date(this.likeResumeTime);
                console.log(`点赞功能冷却中,将在 ${resumeDate.toLocaleString()} (还需约 ${remainingHours} 小时) 后恢复`);
                console.log(`提示:可以点击"清除点赞冷却"按钮立即恢复点赞功能`);
                this.updateClearCooldownButton();
            }
        } else {
            this.updateClearCooldownButton();
        }
    }

    updateClearCooldownButton() {
        if (!this.clearCooldownBtn) return;

        if (this.likeResumeTime && Date.now() < this.likeResumeTime) {
            const remainingHours = Math.ceil((this.likeResumeTime - Date.now()) / (1000 * 60 * 60));
            this.clearCooldownBtn.innerHTML = `<span class="btn-icon">🔥</span><span class="btn-text">清除冷却 (${remainingHours}h)</span>`;
            this.clearCooldownBtn.style.display = 'flex';
        } else {
            this.clearCooldownBtn.style.display = 'none';
        }
    }

    handleClearCooldown() {
        if (!this.likeResumeTime) {
            this.showNotification('当前没有点赞冷却');
            return;
        }

        // 清除冷却时间
        this.likeResumeTime = null;
        Storage.set('likeResumeTime', null);

        // 更新按钮显示
        this.updateClearCooldownButton();

        // 显示成功提示
        this.showNotification('✅ 点赞冷却已清除,可以正常点赞了!');
        console.log('[清除冷却] 点赞冷却时间已清除');
    }

    observeLikeLimit() {
        // 监听 DOM 变化,检测点赞限制弹窗
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === 1) {
                        // 检测弹窗内容
                        const text = node.textContent || '';

                        // 只处理点赞限制弹窗,排除回复限制弹窗
                        // 点赞限制关键词:点赞上限、点赞、分享很多爱
                        // 回复限制关键词:回复数量、创建更多新回复
                        const isLikeLimit = (
                            (text.includes('点赞上限') ||
                             text.includes('分享很多爱') ||
                             text.includes('点赞') && text.includes('小时后再次点赞')) &&
                            !text.includes('回复') &&
                            !text.includes('创建更多新回复')
                        );

                        if (isLikeLimit) {
                            this.handleLikeLimit(text);
                            // 自动关闭弹窗
                            setTimeout(() => {
                                const confirmBtn = document.querySelector('.modal-footer .btn-primary, .dialog-footer .btn-primary, button.btn-primary');
                                if (confirmBtn && confirmBtn.textContent.includes('确定')) {
                                    confirmBtn.click();
                                }
                            }, 1000);
                            break;
                        }
                    }
                }
            }
        });

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

    handleLikeLimit(text) {
        console.log('检测到点赞限制提示:', text);

        let waitMinutes = 0; // 等待时间(分钟)

        // 优先匹配 "在 X 分钟后" 格式
        const minuteMatch = text.match(/[在|可以在]\s*(\d+)\s*分钟后/);
        if (minuteMatch) {
            waitMinutes = parseInt(minuteMatch[1]);
            console.log(`从 "X分钟后" 提取到等待时间: ${waitMinutes} 分钟`);
        } else {
            // 匹配 "在 X 小时后" 格式
            const hourMatch = text.match(/[在|可以在]\s*(\d+)\s*小时后/);
            if (hourMatch) {
                waitMinutes = parseInt(hourMatch[1]) * 60;
                console.log(`从 "X小时后" 提取到等待时间: ${hourMatch[1]} 小时 = ${waitMinutes} 分钟`);
            } else {
                // 尝试匹配最后一个数字+单位的组合
                const allMinuteMatches = text.match(/(\d+)\s*分钟/g);
                const allHourMatches = text.match(/(\d+)\s*小时/g);

                if (allMinuteMatches && allMinuteMatches.length > 0) {
                    // 取最后一个分钟匹配
                    const lastMatch = allMinuteMatches[allMinuteMatches.length - 1].match(/(\d+)/);
                    if (lastMatch) {
                        waitMinutes = parseInt(lastMatch[1]);
                        console.log(`从最后一个匹配提取到等待时间: ${waitMinutes} 分钟`);
                    }
                } else if (allHourMatches && allHourMatches.length > 0) {
                    // 取最后一个小时匹配
                    const lastMatch = allHourMatches[allHourMatches.length - 1].match(/(\d+)/);
                    if (lastMatch) {
                        waitMinutes = parseInt(lastMatch[1]) * 60;
                        console.log(`从最后一个匹配提取到等待时间: ${lastMatch[1]} 小时 = ${waitMinutes} 分钟`);
                    }
                } else {
                    // 默认10小时
                    waitMinutes = 10 * 60;
                    console.log(`未能提取等待时间,使用默认值: 10 小时 = ${waitMinutes} 分钟`);
                }
            }
        }

        // 计算恢复时间
        const resumeTime = Date.now() + (waitMinutes * 60 * 1000);
        this.likeResumeTime = resumeTime;
        Storage.set('likeResumeTime', resumeTime);

        // 关闭自动点赞
        this.autoLikeEnabled = false;
        Storage.set('autoLikeEnabled', false);

        // 更新UI - 更精确地定位到自动点赞开关
        const toggleRows = this.container.querySelectorAll('.toggle-row');
        for (const row of toggleRows) {
            const label = row.querySelector('.toggle-label');
            if (label && label.textContent.includes('自动点赞')) {
                const input = row.querySelector('input[type="checkbox"]');
                if (input) {
                    input.checked = false;
                }
                break;
            }
        }

        const resumeDate = new Date(resumeTime);
        const displayTime = waitMinutes >= 60
            ? `${Math.floor(waitMinutes / 60)} 小时 ${waitMinutes % 60 > 0 ? (waitMinutes % 60) + ' 分钟' : ''}`.trim()
            : `${waitMinutes} 分钟`;

        console.log(`已达到点赞上限,自动关闭点赞功能,将在 ${resumeDate.toLocaleString()} (${displayTime}后) 恢复`);

        // 显示提示 - 使用提取到的实际时间
        this.showNotification(`点赞已达上限,将在 ${displayTime}后自动恢复`);
    }

    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: 10px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
            z-index: 100000;
            font-size: 14px;
            max-width: 300px;
            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.transition = 'all 0.3s';
            notification.style.opacity = '0';
            notification.style.transform = 'translateX(400px)';
            setTimeout(() => notification.remove(), 300);
        }, 3000);
    }

    // 获取当前用户名
    async getCurrentUsername() {
        if (this.currentUsername) return this.currentUsername;

        try {
            // 方法1:从用户菜单获取
            const userMenuBtn = document.querySelector('.header-dropdown-toggle.current-user');
            if (userMenuBtn) {
                const img = userMenuBtn.querySelector('img[alt]');
                if (img && img.alt) {
                    this.currentUsername = img.alt;
                    return this.currentUsername;
                }
            }

            // 方法2:从 API 获取
            const response = await fetch(`${BASE_URL}/session/current.json`);
            if (response.ok) {
                const data = await response.json();
                if (data.current_user && data.current_user.username) {
                    this.currentUsername = data.current_user.username;
                    return this.currentUsername;
                }
            }
        } catch (error) {
            console.error('获取用户名失败:', error);
        }
        return null;
    }

    // 加载用户信任等级
    async loadUserTrustLevel(isManualRefresh = false) {
        const username = await this.getCurrentUsername();
        if (!username) {
            this.trustLevelContainer.innerHTML = '<div class="trust-level-loading">未登录</div>';
            return;
        }

        // 如果不是手动刷新,显示加载状态
        if (isManualRefresh) {
            const refreshBtn = this.trustLevelContainer.querySelector('.trust-level-refresh');
            if (refreshBtn) {
                refreshBtn.textContent = '刷新中...';
                refreshBtn.disabled = true;
            }
        }

        try {
            // 首先尝试从 summary.json 获取(适用于TL0-TL1)
            const summaryResponse = await fetch(`${BASE_URL}/u/${username}/summary.json`);
            if (summaryResponse.ok) {
                const data = await summaryResponse.json();
                // 检查是否能从 summary 获取足够信息
                if (data.user_summary) {
                    this.renderTrustLevel(data, username);
                    return;
                }
            }

            // 如果 summary 不可用或等级较高,尝试从 connect.linux.do 获取详细信息
            if (CURRENT_DOMAIN === 'linux.do') {
                await this.loadTrustLevelFromConnect(username);
            } else {
                throw new Error('无法获取等级数据');
            }
        } catch (error) {
            console.error('加载信任等级失败:', error);
            this.trustLevelContainer.innerHTML = `
                <div class="trust-level-header">
                    📊 信任等级
                    <button class="trust-level-refresh" onclick="window.browseController.loadUserTrustLevel(true)">🔄 刷新</button>
                </div>
                <div class="trust-level-loading">加载失败,请点击刷新重试</div>
            `;
        } finally {
            // 恢复刷新按钮状态
            if (isManualRefresh) {
                setTimeout(() => {
                    const refreshBtn = this.trustLevelContainer.querySelector('.trust-level-refresh');
                    if (refreshBtn) {
                        refreshBtn.textContent = '🔄 刷新';
                        refreshBtn.disabled = false;
                    }
                }, 1000);
            }
        }
    }

    // 从 connect.linux.do 加载等级信息(适用于TL2+)
    async loadTrustLevelFromConnect(username) {
        try {
            const response = await fetch('https://connect.linux.do/');
            if (!response.ok) {
                throw new Error('无法访问 connect.linux.do');
            }

            const html = await response.text();
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');

            // 查找包含"信任级别"的区块
            const trustLevelSection = Array.from(doc.querySelectorAll('div.bg-white.p-6.rounded-lg')).find(div => {
                const h2 = div.querySelector('h2');
                return h2 && h2.textContent.includes('信任级别');
            });

            if (!trustLevelSection) {
                throw new Error('未找到信任级别数据');
            }

            // 解析标题获取目标等级
            const h2 = trustLevelSection.querySelector('h2');
            const titleMatch = h2.textContent.match(/信任级别\s*(\d+)\s*的要求/);
            const targetLevel = titleMatch ? titleMatch[1] : '未知';

            // 解析表格数据
            const tableRows = trustLevelSection.querySelectorAll('table tbody tr');
            const requirements = [];

            tableRows.forEach((row, index) => {
                if (index === 0) return; // 跳过表头

                const cells = row.querySelectorAll('td');
                if (cells.length >= 3) {
                    const name = cells[0].textContent.trim();
                    const current = cells[1].textContent.trim();
                    const required = cells[2].textContent.trim();
                    const isMet = cells[1].classList.contains('text-green-500');

                    requirements.push({ name, current, required, isMet });
                }
            });

            // 渲染高级等级信息
            this.renderAdvancedTrustLevel(username, targetLevel, requirements);

        } catch (error) {
            console.error('从 connect.linux.do 加载失败:', error);
            throw error;
        }
    }

    // 渲染信任等级信息(支持 TL0->TL1 和 TL1->TL2 - 基于 summary.json)
    renderTrustLevel(data, username) {
        const summary = data.user_summary;
        if (!summary) {
            this.trustLevelContainer.innerHTML = '<div class="trust-level-loading">无数据</div>';
            return;
        }

        // 获取当前信任等级
        // 优先从 user_summary 中获取,如果没有则从外层获取
        const currentLevel = summary.trust_level !== undefined ? summary.trust_level :
                           (data.user && data.user.trust_level !== undefined ? data.user.trust_level : 1);
        const targetLevel = currentLevel + 1;

        // 根据当前等级获取对应的升级要求
        const levelConfig = CONFIG.levelRequirements[currentLevel];

        if (!levelConfig) {
            // 如果没有配置(比如已经是最高等级),使用原来的逻辑
            this.renderDefaultTrustLevel(summary, username);
            return;
        }

        const requirements = [];

        // 根据配置动态构建要求列表
        Object.entries(levelConfig).forEach(([key, requiredValue]) => {
            let currentValue = 0;
            let label = '';

            switch (key) {
                case 'topics_entered':
                    currentValue = summary.topics_entered || 0;
                    label = '浏览的话题';
                    break;
                case 'posts_read_count':
                    currentValue = summary.posts_read_count || 0;
                    label = '已读帖子';
                    break;
                case 'time_read':
                    currentValue = Math.floor((summary.time_read || 0) / 60);
                    label = '阅读时长(分)';
                    requiredValue = Math.floor(requiredValue / 60);
                    break;
                case 'days_visited':
                    currentValue = summary.days_visited || 0;
                    label = '访问天数';
                    break;
                case 'likes_given':
                    currentValue = summary.likes_given || 0;
                    label = '给出的赞';
                    break;
                case 'likes_received':
                    currentValue = summary.likes_received || 0;
                    label = '收到的赞';
                    break;
                case 'post_count':
                    currentValue = summary.post_count || 0;
                    label = '帖子数量';
                    break;
            }

            if (label) {
                requirements.push({
                    name: label,
                    current: currentValue,
                    required: requiredValue
                });
            }
        });

        // 计算达标数量
        const achievedCount = requirements.filter(req => req.current >= req.required).length;
        const totalCount = requirements.length;
        const allMet = achievedCount === totalCount;

        const levelNames = {
            0: 'Lv0 → Lv1',
            1: 'Lv1 → Lv2',
            2: 'Lv2 → Lv3',
            3: 'Lv3 → Lv4'
        };

        let html = `
            <div class="trust-level-header">
                <span>📊 ${levelNames[currentLevel] || `Lv${currentLevel} → Lv${targetLevel}`} (${username})</span>
                <button class="trust-level-refresh" data-action="refresh">🔄 刷新</button>
            </div>
        `;

        requirements.forEach(req => {
            const progress = Math.min((req.current / req.required) * 100, 100);
            const isCompleted = req.current >= req.required;
            const fillClass = isCompleted ? 'completed' : '';

            html += `
                <div class="trust-level-item">
                    <span class="trust-level-name">${req.name}</span>
                    <div class="trust-level-progress">
                        <div class="trust-level-bar">
                            <div class="trust-level-bar-fill ${fillClass}" style="width: ${progress}%"></div>
                        </div>
                        <span class="trust-level-value">${req.current}/${req.required}</span>
                    </div>
                </div>
            `;
        });

        // 在数据下方添加总结信息
        if (allMet) {
            html += `
                <div style="background: rgba(255, 255, 255, 0.25); padding: 6px 8px; border-radius: 6px; margin: 6px 0 0 0;">
                    <div style="color: #fff; font-size: 11px; font-weight: 600; text-align: center;">
                        ✅ 已满足 Lv${targetLevel} 要求
                    </div>
                </div>
            `;
        } else {
            const unmetCount = totalCount - achievedCount;
            html += `
                <div style="background: rgba(255, 255, 255, 0.15); padding: 6px 8px; border-radius: 6px; margin: 6px 0 0 0;">
                    <div style="color: rgba(255, 255, 255, 0.9); font-size: 11px; font-weight: 500; text-align: center;">
                        还需完成 ${unmetCount} 项升级到 Lv${targetLevel}
                    </div>
                </div>
            `;
        }

        this.trustLevelContainer.innerHTML = html;

        // 添加刷新按钮事件监听
        setTimeout(() => {
            const refreshBtn = this.trustLevelContainer.querySelector('.trust-level-refresh');
            if (refreshBtn) {
                refreshBtn.addEventListener('click', () => this.loadUserTrustLevel(true));
            }
        }, 100);
    }

    // 默认渲染方法(用于没有配置的等级)
    renderDefaultTrustLevel(summary, username) {
        const requirements = [
            { name: '访问天数', current: summary.days_visited, required: 15 },
            { name: '给出的赞', current: summary.likes_given, required: 1 },
            { name: '收到的赞', current: summary.likes_received, required: 1 },
            { name: '帖子数量', current: summary.post_count, required: 3 },
            { name: '进入主题', current: summary.topics_entered, required: 20 },
            { name: '阅读帖子', current: summary.posts_read_count, required: 100 },
            { name: '阅读时长(分)', current: Math.floor(summary.time_read / 60), required: 60 }
        ];

        // 计算达标数量
        const achievedCount = requirements.filter(req => req.current >= req.required).length;
        const totalCount = requirements.length;
        const allMet = achievedCount === totalCount;

        let html = `
            <div class="trust-level-header">
                <span>📊 等级 (L2+) (${username || ''})</span>
                <button class="trust-level-refresh" data-action="refresh">🔄 刷新</button>
            </div>
        `;

        // 添加总结信息
        if (allMet) {
            html += `
                <div style="background: rgba(16, 185, 129, 0.2); padding: 6px 8px; border-radius: 6px; margin: 6px 0;">
                    <div style="color: #10b981; font-size: 11px; font-weight: 600; text-align: center;">
                        🎉 所有要求已达标!
                    </div>
                </div>
            `;
        } else {
            const unmetCount = totalCount - achievedCount;
            html += `
                <div style="background: rgba(251, 146, 60, 0.2); padding: 6px 8px; border-radius: 6px; margin: 6px 0;">
                    <div style="color: #ea580c; font-size: 11px; font-weight: 600; text-align: center;">
                        还需完成 ${unmetCount} 项要求
                    </div>
                </div>
            `;
        }

        requirements.forEach(req => {
            const progress = Math.min((req.current / req.required) * 100, 100);
            const isCompleted = req.current >= req.required;
            const fillClass = isCompleted ? 'completed' : '';

            html += `
                <div class="trust-level-item">
                    <span class="trust-level-name">${req.name}</span>
                    <div class="trust-level-progress">
                        <div class="trust-level-bar">
                            <div class="trust-level-bar-fill ${fillClass}" style="width: ${progress}%"></div>
                        </div>
                        <span class="trust-level-value">${req.current}/${req.required}</span>
                    </div>
                </div>
            `;
        });

        this.trustLevelContainer.innerHTML = html;

        setTimeout(() => {
            const refreshBtn = this.trustLevelContainer.querySelector('.trust-level-refresh');
            if (refreshBtn) {
                refreshBtn.addEventListener('click', () => this.loadUserTrustLevel(true));
            }
        }, 100);
    }

    // 渲染高级信任等级信息(从 connect.linux.do 获取的TL2+数据)
    renderAdvancedTrustLevel(username, targetLevel, requirements) {
        const achievedCount = requirements.filter(r => r.isMet).length;
        const totalCount = requirements.length;

        // 计算当前等级
        const currentLevel = parseInt(targetLevel) - 1;

        // 等级名称映射(简化显示)
        const levelNames = {
            2: 'Lv1 → Lv2',
            3: 'Lv2 → Lv3',
            4: 'Lv3 → Lv4'
        };

        let html = `
            <div class="trust-level-header">
                <span>📊 ${levelNames[targetLevel] || `Lv${currentLevel} → Lv${targetLevel}`} (${username})</span>
                <button class="trust-level-refresh" data-action="refresh">🔄 刷新</button>
            </div>
        `;

        requirements.forEach(req => {
            // 尝试从文本中提取数字
            const currentMatch = req.current.match(/(\d+)/);
            const requiredMatch = req.required.match(/(\d+)/);

            const currentNum = currentMatch ? parseInt(currentMatch[1]) : 0;
            const requiredNum = requiredMatch ? parseInt(requiredMatch[1]) : 1;

            const progress = Math.min((currentNum / requiredNum) * 100, 100);
            const isCompleted = req.isMet;
            const fillClass = isCompleted ? 'completed' : '';

            // 简化标签名称
            let simpleName = req.name
                .replace('已读帖子(所有时间)', '已读帖子')
                .replace('浏览的话题(所有时间)', '浏览话题')
                .replace('获赞:点赞用户数量', '点赞用户数')
                .replace('被禁言(过去 6 个月)', '被禁言')
                .replace('被封禁(过去 6 个月)', '被封禁');

            html += `
                <div class="trust-level-item">
                    <span class="trust-level-name">${simpleName}</span>
                    <div class="trust-level-progress">
                        <div class="trust-level-bar">
                            <div class="trust-level-bar-fill ${fillClass}" style="width: ${progress}%"></div>
                        </div>
                        <span class="trust-level-value">${req.current}/${req.required}</span>
                    </div>
                </div>
            `;
        });

        // 在数据下方添加总结信息
        if (achievedCount === totalCount) {
            html += `
                <div style="background: rgba(255, 255, 255, 0.25); padding: 6px 8px; border-radius: 6px; margin: 6px 0 0 0;">
                    <div style="color: #fff; font-size: 11px; font-weight: 600; text-align: center;">
                        ✅ 已满足 Lv${targetLevel} 要求
                    </div>
                </div>
            `;
        } else {
            const unmetCount = totalCount - achievedCount;
            html += `
                <div style="background: rgba(255, 255, 255, 0.15); padding: 6px 8px; border-radius: 6px; margin: 6px 0 0 0;">
                    <div style="color: rgba(255, 255, 255, 0.9); font-size: 11px; font-weight: 500; text-align: center;">
                        还需完成 ${unmetCount} 项升级到 Lv${targetLevel}
                    </div>
                </div>
            `;
        }

        this.trustLevelContainer.innerHTML = html;

        // 添加刷新按钮事件监听
        setTimeout(() => {
            const refreshBtn = this.trustLevelContainer.querySelector('.trust-level-refresh');
            if (refreshBtn) {
                refreshBtn.addEventListener('click', () => this.loadUserTrustLevel(true));
            }
        }, 100);
    }

    // 启动账号切换监控
    startUserSwitchMonitoring() {
        // 初始化当前用户
        this.getCurrentUsername().then(username => {
            this.lastDetectedUser = username;
        });

        // 每5秒检查一次是否切换账号
        setInterval(async () => {
            const currentDetectedUser = await this.getCurrentUsername();

            if (currentDetectedUser && this.lastDetectedUser &&
                currentDetectedUser !== this.lastDetectedUser) {
                console.log(`检测到账号切换: ${this.lastDetectedUser} -> ${currentDetectedUser}`);
                this.lastDetectedUser = currentDetectedUser;
                this.currentUsername = currentDetectedUser;

                // 延迟一点时间再刷新,确保页面稳定
                setTimeout(() => {
                    console.log('账号切换后重新加载等级信息');
                    this.loadUserTrustLevel(true);
                }, 1000);
            } else if (currentDetectedUser) {
                this.lastDetectedUser = currentDetectedUser;
            }
        }, 5000);
    }

    toggleCleanMode() {
        const sidebarToggle = document.querySelector('button.btn-sidebar-toggle');
        if (sidebarToggle && this.cleanModeEnabled) {
            if (sidebarToggle.getAttribute('aria-expanded') === 'true') {
                console.log('清爽模式启用,收起边栏');
                sidebarToggle.click();
            }
        }
        this.applyCleanModeStyles();
    }

    applyCleanModeStyles() {
        let styleElement = document.getElementById('clean-mode-styles');
        if (styleElement) {
            styleElement.remove();
        }

        if (this.cleanModeEnabled) {
            styleElement = document.createElement('style');
            styleElement.id = 'clean-mode-styles';
            styleElement.textContent = `
                p:contains("希望你喜欢这里。有问题,请提问,或搜索现有帖子。") {
                    display: none !important;
                }
                div#global-notice-alert-global-notice.alert.alert-info.alert-global-notice {
                    display: none !important;
                }
                a[href="https://linux.do/t/topic/482293"] {
                    display: none !important;
                }
                div.link-bottom-line a.badge-category__wrapper {
                    display: none !important;
                }
                td.posters.topic-list-data {
                    display: none !important;
                }
                a.discourse-tag.box[href^="/tag/"] {
                    display: none !important;
                }
            `;
            document.head.appendChild(styleElement);
        }
    }

    initOnlyOwnerView() {
        this.createToggleButton();
        this.observePageChanges();
        this.toggleVisibility();
    }

    toggleVisibility() {
        const displayMode = localStorage.getItem("on_off") || "当前查看全部";
        const userId = document.getElementById("post_1")?.getAttribute('data-user-id');
        if (userId) {
            document.querySelectorAll('article').forEach(article => {
                article.style.display = (displayMode === "当前只看楼主" && article.dataset.userId !== userId) ? 'none' : '';
            });
        }
    }

    createToggleButton() {
        if (document.getElementById("toggleVisibilityBtn")) {
            return;
        }

        const btn = document.createElement("button");
        btn.id = "toggleVisibilityBtn";
        btn.textContent = localStorage.getItem("on_off") || "当前查看全部";
        btn.onclick = () => {
            const newText = btn.textContent === '当前查看全部' ? '当前只看楼主' : '当前查看全部';
            document.getElementsByClassName("start-date")[0]?.click();
            btn.textContent = newText;
            localStorage.setItem("on_off", newText);
            this.toggleVisibility();
        };

        btn.style.backgroundColor = "#333";
        btn.style.color = "#FFF";
        btn.style.border = "none";
        btn.style.padding = "8px 16px";
        btn.style.marginLeft = "10px";
        btn.style.borderRadius = "5px";
        btn.style.cursor = "pointer";

        const saveButton = document.querySelector('.save-to-local-btn');
        if (saveButton) {
            saveButton.parentElement.appendChild(btn);
        } else {
            const firstPostContent = document.querySelector('.boxed.onscreen-post[data-post-id] .cooked');
            if (firstPostContent) {
                firstPostContent.appendChild(btn);
            }
        }
    }

    observePageChanges() {
        const observer = new MutationObserver(() => {
            if (document.querySelector(".timeline-footer-controls") && !document.getElementById("toggleVisibilityBtn")) {
                this.createToggleButton();
            }
            this.toggleVisibility();
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    initFloorNumberDisplay() {
        this.addFloorNumbers();
        this.initMutationObserver();
        this.setupRandomJumpButton();
        this.monitorURLChangeAndUpdateButton();
    }

    addFloorNumbers() {
        document.querySelectorAll('.boxed.onscreen-post').forEach((post) => {
            if (!post.querySelector('.floor-number')) {
                const floorNumber = document.createElement('div');
                floorNumber.className = 'floor-number';
                floorNumber.textContent = '楼层: ' + post.id.split("_")[1];
                floorNumber.style.cssText = 'color: grey; margin-left: 10px;';
                post.querySelector('.topic-meta-data').appendChild(floorNumber);
            }
        });
        this.setupSaveButton();
    }

    initMutationObserver() {
        const observer = new MutationObserver(() => {
            this.addFloorNumbers();
            this.setupSaveButton();
            this.toggleCleanMode();
        });
        observer.observe(document.body, { childList: true, subtree: true });
    }

    randomJump() {
        fetch(window.location.href + '.json')
            .then(response => response.json())
            .then(data => {
                if (data && data.posts_count) {
                    const postId = 1 + Math.floor(Math.random() * data.posts_count);
                    const currentUrl = new URL(window.location.href);
                    const list1 = currentUrl.pathname.split("/");
                    if (list1[list1.length - 2] === "topic") {
                        list1.push(postId);
                    } else if (list1[list1.length - 3] === "topic") {
                        list1[list1.length - 1] = postId;
                    }
                    const newUrl = list1.join("/");
                    window.location.href = newUrl;
                    alert('恭喜楼层【' + postId + '】的用户被抽中!');
                }
            })
            .catch(error => console.error('Error:', error));
    }

    setupRandomJumpButton() {
        // 随机按钮已集成到主面板中,不需要单独创建
    }

    setupSaveButton() {
        const firstPost = document.querySelector('.boxed.onscreen-post[data-post-id]');
        if (firstPost && firstPost.id.includes('post_1')) {
            if (!firstPost.querySelector('.save-to-local-btn')) {
                const saveButton = document.createElement('button');
                saveButton.className = 'save-to-local-btn';
                saveButton.textContent = '💾 保存到本地';
                Object.assign(saveButton.style, {
                    padding: '10px 20px',
                    fontSize: '15px',
                    fontWeight: '600',
                    backgroundColor: '#ff9800',
                    color: 'white',
                    border: 'none',
                    borderRadius: '8px',
                    cursor: 'pointer',
                    marginTop: '10px',
                    boxShadow: '0 4px 12px rgba(255, 152, 0, 0.3)',
                    transition: 'all 0.3s'
                });
                saveButton.addEventListener('mouseover', () => {
                    saveButton.style.transform = 'translateY(-2px)';
                    saveButton.style.boxShadow = '0 6px 20px rgba(255, 152, 0, 0.4)';
                });
                saveButton.addEventListener('mouseout', () => {
                    saveButton.style.transform = 'translateY(0)';
                    saveButton.style.boxShadow = '0 4px 12px rgba(255, 152, 0, 0.3)';
                });
                saveButton.addEventListener('click', () => this.savePostToLocal(firstPost));
                const postContent = firstPost.querySelector('.cooked');
                if (postContent) {
                    postContent.appendChild(saveButton);
                }
            }
        }
    }

    async savePostToLocal(postElement) {
        try {
            const topicTitle = document.querySelector('.fancy-title')?.textContent.trim() || 'Untitled_Topic';
            const postContent = postElement.querySelector('.cooked');
            if (!postContent) {
                alert('无法获取帖子内容!');
                return;
            }

            const contentClone = postContent.cloneNode(true);
            contentClone.querySelector('.save-to-local-btn')?.remove();

            const images = contentClone.querySelectorAll('img');
            for (const img of images) {
                try {
                    const response = await fetch(img.src);
                    const blob = await response.blob();
                    const reader = new FileReader();
                    await new Promise((resolve) => {
                        reader.onload = resolve;
                        reader.readAsDataURL(blob);
                    });
                    img.src = reader.result;
                } catch (error) {
                    console.error('图片加载失败:', img.src, error);
                    img.alt = '[图片加载失败]';
                }
            }

            const htmlContent = `
                <!DOCTYPE html>
                <html lang="zh-CN">
                <head>
                    <meta charset="UTF-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1.0">
                    <title>${topicTitle}</title>
                    <style>
                        body { font-family: Arial, sans-serif; margin: 20px; }
                        .post-content { max-width: 800px; margin: 0 auto; }
                        img { max-width: 100%; height: auto; }
                    </style>
                </head>
                <body>
                    <div class="post-content">
                        <h1>${topicTitle}</h1>
                        ${contentClone.innerHTML}
                    </div>
                </body>
                </html>
            `;

            const blob = new Blob([htmlContent], { type: 'text/html' });
            const url = URL.createObjectURL(blob);
            const link = document.createElement('a');
            link.href = url;
            const fileName = topicTitle
                .replace(/[\\/:*?"<>|]/g, '_')
                .replace(/\s+/g, '_')
                + '.html';
            link.download = fileName;
            link.click();
            URL.revokeObjectURL(url);

            alert('帖子内容已保存到本地!');
        } catch (error) {
            console.error('保存帖子失败:', error);
            alert('保存失败,请查看控制台错误信息。');
        }
    }

    monitorURLChangeAndUpdateButton() {
        let lastURL = location.href;

        // 初始检查一次
        this.updateButtonVisibility();

        setInterval(() => {
            const currentURL = location.href;
            if (currentURL !== lastURL) {
                lastURL = currentURL;
                this.isTopicPage = location.pathname.includes('/t/topic/');
                this.updateButtonVisibility();
                this.toggleCleanMode();
                if (this.autoLikeEnabled && currentURL.includes('/t/topic/')) {
                    this.autoLikeTopic();
                }
            }
        }, 1000);
    }

    updateButtonVisibility() {
        const isTopicPage = location.pathname.includes('/t/topic/');

        // 整个工具功能区:只在文章页显示
        if (this.toolSectionContainer) {
            this.toolSectionContainer.style.display = isTopicPage ? 'block' : 'none';
        }

        // 工具区上方的分隔线:只在文章页显示
        if (this.divider1) {
            this.divider1.style.display = isTopicPage ? 'block' : 'none';
        }

        // 工具区下方的分隔线:只在文章页显示
        if (this.divider2) {
            this.divider2.style.display = isTopicPage ? 'block' : 'none';
        }

        console.log(`页面类型: ${isTopicPage ? '文章页' : '非文章页'},工具功能区${isTopicPage ? '显示' : '隐藏'}`);
    }

    async handleRevealUsersClick() {
        if (this.userInfoHelper.revealInProgress) return;

        // 更新按钮状态
        this.revealUsersBtn.disabled = true;
        this.revealUsersBtn.innerHTML = '<span class="btn-icon">⏳</span><span class="btn-text">加载中...</span>';

        try {
            await this.userInfoHelper.revealAllVisibleReplies();
            this.revealUsersBtn.innerHTML = '<span class="btn-icon">✅</span><span class="btn-text">加载完成</span>';

            // 2秒后恢复按钮
            setTimeout(() => {
                this.revealUsersBtn.disabled = false;
                this.revealUsersBtn.innerHTML = '<span class="btn-icon">📊</span><span class="btn-text">批量展示信息</span>';
            }, 2000);
        } catch (error) {
            console.error('展示用户信息失败:', error);
            this.revealUsersBtn.disabled = false;
            this.revealUsersBtn.innerHTML = '<span class="btn-icon">❌</span><span class="btn-text">加载失败</span>';

            setTimeout(() => {
                this.revealUsersBtn.innerHTML = '<span class="btn-icon">📊</span><span class="btn-text">批量展示信息</span>';
            }, 2000);
        }
    }

    handleButtonClick() {
        if (this.isScrolling || this.autoRunning) {
            // 停止自动阅读
            this.stopScrolling();
            this.autoRunning = false;
            this.setSessionStorage('autoRunning', false);
            this.button.innerHTML = '<span class="btn-icon">▶</span><span class="btn-text">开始阅读</span>';
            this.button.classList.remove('running');
        } else {
            // 开启自动阅读
            this.autoRunning = true;
            this.setSessionStorage('autoRunning', true);
            this.button.innerHTML = '<span class="btn-icon">⏸</span><span class="btn-text">停止阅读</span>';
            this.button.classList.add('running');

            if (!this.firstUseChecked) {
                this.handleFirstUse();
            } else if (this.isTopicPage) {
                this.startScrolling();
                if (this.autoLikeEnabled) {
                    this.autoLikeTopic();
                }
            } else {
                this.getLatestTopics().then(() => this.navigateNextTopic());
            }
        }
    }

    async autoLikeTopic() {
        if (!this.autoLikeEnabled) return;

        // 检查是否在冷却期
        if (this.likeResumeTime && Date.now() < this.likeResumeTime) {
            console.log("点赞功能冷却中,跳过点赞");
            return;
        }

        const match = window.location.pathname.match(/\/t\/topic\/(\d+)/);
        if (!match) {
            console.log("无法获取当前主题ID");
            return;
        }
        const topicId = match[1];

        if (this.likedTopics.includes(topicId)) {
            console.log(`主题 ${topicId} 已经点赞过,跳过点赞操作`);
            return;
        }

        console.log("正在检查是否需要自动点赞主题...");
        await Utils.sleep(2000);

        const likeButton = document.querySelector('div.discourse-reactions-reaction-button button.btn-toggle-reaction-like');
        if (likeButton && !likeButton.classList.contains('has-like') && !likeButton.classList.contains('liked')) {
            likeButton.scrollIntoView({ behavior: 'smooth', block: 'center' });
            await Utils.sleep(1000);
            console.log("找到主题点赞按钮,执行点击操作");
            likeButton.click();

            this.likedTopics.push(topicId);
            Storage.set('likedTopics', this.likedTopics);
            console.log(`已记录点赞主题 ${topicId}`);
        } else {
            console.log("未找到可点赞的主题按钮或已点赞");
            if (likeButton && (likeButton.classList.contains('has-like') || likeButton.classList.contains('liked'))) {
                if (!this.likedTopics.includes(topicId)) {
                    this.likedTopics.push(topicId);
                    Storage.set('likedTopics', this.likedTopics);
                    console.log(`主题 ${topicId} 已点赞,记录到列表`);
                }
            }
        }
    }

    async handleFirstUse() {
        if (!this.autoRunning) return;

        // 只在 linux.do 域名下执行新手教程
        if (CURRENT_DOMAIN !== 'linux.do') {
            console.log('非 linux.do 域名,跳过新手教程');
            Storage.set('firstUseChecked', true);
            this.firstUseChecked = true;
            await this.getLatestTopics();
            await this.navigateNextTopic();
            return;
        }

        if (!this.selectedPost) {
            const randomIndex = Math.floor(Math.random() * CONFIG.mustRead.posts.length);
            this.selectedPost = CONFIG.mustRead.posts[randomIndex];
            Storage.set('selectedPost', this.selectedPost);
            console.log(`随机选择文章: ${this.selectedPost.url}`);
            window.location.href = this.selectedPost.url;
            return;
        }

        const currentUrl = window.location.href;
        if (currentUrl.includes(this.selectedPost.url)) {
            console.log(`当前在选中的文章页面,已点赞数: ${this.likesCount}`);
            while (this.likesCount < CONFIG.mustRead.likesNeeded && this.autoRunning) {
                await this.likeRandomComment();
                if (this.likesCount >= CONFIG.mustRead.likesNeeded) {
                    console.log('完成所需点赞数量,开始正常浏览');
                    Storage.set('firstUseChecked', true);
                    this.firstUseChecked = true;
                    await this.getLatestTopics();
                    await this.navigateNextTopic();
                    break;
                }
                await Utils.sleep(1000);
            }
        } else {
            window.location.href = this.selectedPost.url;
        }
    }

    async likeRandomComment() {
        if (!this.autoRunning) return false;

        // 检查是否在冷却期
        if (this.likeResumeTime && Date.now() < this.likeResumeTime) {
            console.log("点赞功能冷却中,跳过点赞");
            return false;
        }

        const likeButtons = Array.from(document.querySelectorAll('.like-button, .like-count, [data-like-button], .discourse-reactions-reaction-button'))
            .filter(button =>
                button &&
                button.offsetParent !== null &&
                !button.classList.contains('has-like') &&
                !button.classList.contains('liked')
            );

        if (likeButtons.length > 0) {
            const randomButton = likeButtons[Math.floor(Math.random() * likeButtons.length)];
            randomButton.scrollIntoView({ behavior: 'smooth', block: 'center' });
            await Utils.sleep(1000);

            if (!this.autoRunning) return false;
            console.log('找到可点赞的评论,准备点赞');
            randomButton.click();
            this.likesCount++;
            Storage.set('likesCount', this.likesCount);
            await Utils.sleep(1000);
            return true;
        }

        window.scrollBy({
            top: 500,
            behavior: 'smooth'
        });
        await Utils.sleep(1000);
        console.log('当前位置没有找到可点赞的评论,继续往下找');
        return false;
    }

    async getLatestTopics() {
        let page = 1;
        let topicList = [];
        let retryCount = 0;

        while (topicList.length < CONFIG.article.topicListLimit && retryCount < CONFIG.article.retryLimit) {
            try {
                const response = await fetch(`${BASE_URL}/latest.json?no_definitions=true&page=${page}`);
                const data = await response.json();

                if (data?.topic_list?.topics) {
                    const filteredTopics = data.topic_list.topics.filter(topic =>
                        topic.posts_count < CONFIG.article.commentLimit
                    );
                    topicList.push(...filteredTopics);
                    page++;
                } else {
                    break;
                }
            } catch (error) {
                console.error('获取文章列表失败:', error);
                retryCount++;
                await Utils.sleep(1000);
            }
        }

        if (topicList.length > CONFIG.article.topicListLimit) {
            topicList = topicList.slice(0, CONFIG.article.topicListLimit);
        }

        this.topicList = topicList;
        this.setSessionStorage('topicList', topicList);
        console.log(`已获取 ${topicList.length} 篇文章`);
    }

    async getNextTopic() {
        if (this.topicList.length === 0) {
            await this.getLatestTopics();
        }

        if (this.topicList.length > 0) {
            const topic = this.topicList.shift();
            this.setSessionStorage('topicList', this.topicList);
            return topic;
        }

        return null;
    }

    async startScrolling() {
        if (this.isScrolling) return;

        this.isScrolling = true;
        this.button.innerHTML = '<span class="btn-icon">⏸</span><span class="btn-text">停止阅读</span>';
        this.button.classList.add('running');
        this.lastActionTime = Date.now();
        
        // 记录页面开始滚动的时间,用于强制跳转
        this.scrollStartTime = Date.now();
        // 设置最大滚动时间(30秒),超过后强制跳转,避免卡在一个页面
        const maxScrollTime = 30000; // 30秒

        while (this.isScrolling) {
            const speed = Utils.random(CONFIG.scroll.minSpeed, CONFIG.scroll.maxSpeed);
            const distance = Utils.random(CONFIG.scroll.minDistance, CONFIG.scroll.maxDistance);
            const scrollStep = distance * 2.5;

            window.scrollBy({
                top: scrollStep,
                behavior: 'smooth'
            });

            // 检查是否到达底部
            if (Utils.isNearBottom()) {
                await Utils.sleep(800);

                if (Utils.isNearBottom() && Utils.isPageLoaded()) {
                    console.log("已到达页面底部,准备导航到下一篇文章...");
                    await Utils.sleep(1000);
                    await this.navigateNextTopic();
                    break;
                }
            }
            
            // 强制跳转检查:如果在当前页面滚动超过最大时间,强制跳转到下一篇
            const scrolledTime = Date.now() - this.scrollStartTime;
            if (scrolledTime > maxScrollTime) {
                console.log(`已在当前页面滚动${Math.floor(scrolledTime/1000)}秒,强制跳转到下一篇文章...`);
                await this.navigateNextTopic();
                break;
            }

            await Utils.sleep(speed);
            this.accumulateTime();

            if (Math.random() < CONFIG.scroll.fastScrollChance) {
                const fastScroll = Utils.random(CONFIG.scroll.fastScrollMin, CONFIG.scroll.fastScrollMax);
                window.scrollBy({
                    top: fastScroll,
                    behavior: 'smooth'
                });
                await Utils.sleep(200);
            }
        }
    }

    async waitForPageLoad() {
        let attempts = 0;
        const maxAttempts = 5;

        while (attempts < maxAttempts) {
            if (Utils.isPageLoaded()) {
                return true;
            }
            await Utils.sleep(300);
            attempts++;
        }

        return false;
    }

    stopScrolling() {
        this.isScrolling = false;
        clearInterval(this.scrollInterval);
        clearTimeout(this.pauseTimeout);
        this.button.innerHTML = '<span class="btn-icon">▶</span><span class="btn-text">开始阅读</span>';
        this.button.classList.remove('running');
    }

    accumulateTime() {
        const now = Date.now();
        this.accumulatedTime += now - this.lastActionTime;
        this.setSessionStorage('accumulatedTime', this.accumulatedTime);
        this.lastActionTime = now;

        if (this.accumulatedTime >= CONFIG.time.browseTime) {
            this.accumulatedTime = 0;
            this.setSessionStorage('accumulatedTime', 0);
            this.pauseForRest();
        }
    }

    async pauseForRest() {
        this.stopScrolling();
        const restMinutes = Math.floor(CONFIG.time.restTime / 60000);
        console.log(`休息${restMinutes}分钟...`);
        
        // 显示休息开始通知
        this.showNotification(`⏸️ 开始休息 ${restMinutes} 分钟`);
        
        await Utils.sleep(CONFIG.time.restTime);
        
        console.log("休息结束,继续浏览...");
        
        // 显示休息结束通知
        this.showNotification(`✅ 休息结束,继续浏览`);
        
        this.startScrolling();
    }

    async navigateNextTopic() {
        const nextTopic = await this.getNextTopic();
        if (nextTopic) {
            console.log("导航到新文章:", nextTopic.title);
            const url = nextTopic.last_read_post_number
                ? `${BASE_URL}/t/topic/${nextTopic.id}/${nextTopic.last_read_post_number}`
                : `${BASE_URL}/t/topic/${nextTopic.id}`;
            
            // 直接使用 window.location.href 跳转
            // 在后台时也能工作,因为有强制跳转定时器兜底
            console.log("正在跳转到:", url);
            window.location.href = url;
        } else {
            console.log("没有更多文章,返回首页");
            window.location.href = `${BASE_URL}/latest`;
        }
    }

    resetFirstUse() {
        Storage.set('firstUseChecked', false);
        Storage.set('likesCount', 0);
        Storage.set('selectedPost', null);
        this.firstUseChecked = false;
        this.likesCount = 0;
        this.selectedPost = null;
        console.log('已重置首次使用状态');
    }
}

// 初始化
(function() {
    window.browseController = new BrowseController();
})();