多家大模型网页同时回答 & 目录导航

输入一次问题,就能自动同步在各家大模型官网提问,免去到处粘贴的麻烦;提供多种便捷的页内目录导航。支持范围:DS,Kimi,千问,豆包,元宝,ChatGPT,Gemini,Claude,Grok……更多介绍见本页面下方。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         多家大模型网页同时回答 & 目录导航
// @namespace    http://tampermonkey.net/
// @version      4.1.1
// @description  输入一次问题,就能自动同步在各家大模型官网提问,免去到处粘贴的麻烦;提供多种便捷的页内目录导航。支持范围:DS,Kimi,千问,豆包,元宝,ChatGPT,Gemini,Claude,Grok……更多介绍见本页面下方。
// @author       interest2
// @match        https://www.kimi.com/*
// @match        https://chat.deepseek.com/*
// @match        https://www.tongyi.com/*
// @match        https://www.qianwen.com/*
// @match        https://chat.qwen.ai/*
// @match        https://www.doubao.com/*
// @match        https://yuanbao.tencent.com/*
// @match        https://chat.zchat.tech/*
// @match        https://chatgpt.com/*
// @match        https://gemini.google.com/*
// @match        https://aistudio.google.com/*
// @match        https://claude.ai/*
// @match        https://grok.com/*
// @noframes
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_addValueChangeListener
// @connect      www.ratetend.com
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';
    console.log("ai script, start");

    const STUDIO_CONTENT_MAX_WIDTH = "800px"; // ai studio 内容最大宽度
    const GEMINI_MAX_WIDTH = "850px"; // gemini 内容最大宽度
    const DEFAULT_WAIT_ELEMENT_TIME = 20000; // 等待元素出现的超时时间
    const version = "4.1.1";

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  🔧 1、适配各站点相关代码  🔧                                      ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 定义站点常量
    const DEEPSEEK = 0;
    const KIMI = 1;
    const TONGYI = 2;
    const QWEN = 3;
    const DOUBAO = 4;
    const YUANBAO = 5;

    const ZCHAT = 10;
    const CHATGPT = 11;
    const GEMINI = 12;
    const STUDIO = 13;
    const CLAUDE = 14;
    const GROK = 15;

    // 默认不启用的站点列表,移除元素可启用对应站点
    const DISABLE_SITES = [];

    // 输入框类型分类
    const inputAreaTypes = {
        textarea: [DEEPSEEK, TONGYI, DOUBAO, QWEN, STUDIO],
        lexical: [KIMI, CHATGPT, ZCHAT, GEMINI, CLAUDE, GROK, YUANBAO]
    };

    // 通用输入框选择器,两类:textarea标签、lexical
    const getTextareaInput = () => document.getElementsByTagName('textarea')[0];
    const getContenteditableInput = () => document.querySelector('[contenteditable="true"]:has(p)');

    // 选择器配置
    const selectors = {
        // 输入框分两类处理
        inputArea: {
            ...Object.fromEntries(inputAreaTypes.textarea.map(site => [site, getTextareaInput])),
            ...Object.fromEntries(inputAreaTypes.lexical.map(site => [site, getContenteditableInput]))
        },
        // 输入框里的发送按钮
        sendBtn: {
            [DEEPSEEK]: () => ((btns) => btns[btns.length - 1])(document.querySelectorAll('[role="button"]')),
            [KIMI]: () => document.getElementsByClassName('send-button')[0],
            [TONGYI]: () => document.querySelector('[class^="operateBtn-"], [class*=" operateBtn-"]'),
            [QWEN]: () => document.getElementById('send-message-button'),
            [DOUBAO]: () => document.getElementById('flow-end-msg-send'),
            [YUANBAO]: () => document.getElementById('yuanbao-send-btn'),

            [ZCHAT]: () => document.getElementById('composer-submit-button'),
            [CHATGPT]: () => document.getElementById('composer-submit-button'),
            [GEMINI]: () => document.querySelector('button.send-button'),
            [STUDIO]: () => document.querySelector('.run-button-content'),
            [CLAUDE]: () => document.querySelector('[aria-label^="Send"]'),
            [GROK]: () => document.querySelector('button[type="submit"]')
        },
        // 已提问的列表(官网样式变更不会影响同步提问功能,只影响目录功能)
        questionList: {
            [DEEPSEEK]: () => filterQuestions(document.getElementsByClassName("ds-message")),
            [KIMI]: () => document.getElementsByClassName("user-content"),
            [TONGYI]: () => document.querySelectorAll('[class^="bubble-"]'),
            [QWEN]: () => document.getElementsByClassName("user-message-content"),
            [DOUBAO]: () => Array.from(document.querySelectorAll('[data-testid="message_text_content"]')).filter(el => !el.children || el.children.length === 0),
            [YUANBAO]: () => document.querySelectorAll(".hyc-content-text"),

            [ZCHAT]: () => document.querySelectorAll('[data-message-author-role="user"]'),
            [CHATGPT]: () => document.querySelectorAll('[data-message-author-role="user"]'),
            [GEMINI]: () => document.getElementsByTagName('user-query'),
            [STUDIO]: () => document.querySelectorAll('[data-turn-role="User"]'),
            [CLAUDE]: () => document.querySelectorAll('[data-testid="user-message"]'),
            [GROK]: () => document.querySelectorAll('div.items-end .message-bubble')
        }
    };

    // url里关键词与各站点的对应关系
    const keywords = {
        "deepseek": DEEPSEEK,
        "kimi": KIMI,
        "qianwen": TONGYI,
        "qwen": QWEN,
        "doubao": DOUBAO,
        "yuanbao": YUANBAO,

        "zchat": ZCHAT,
        "chatgpt": CHATGPT,
        "gemini": GEMINI,
        "aistudio": STUDIO,
        "claude": CLAUDE,
        "grok": GROK
    };

    // 各家大模型的网址(新对话,历史对话的前缀)
    const webSites = {
        [KIMI]: ["https://www.kimi.com/"],
        [DEEPSEEK]: ["https://chat.deepseek.com/"],
        [TONGYI]: ["https://www.qianwen.com/"],
        [CHATGPT]: ["https://chatgpt.com/"],
        [DOUBAO]: ["https://www.doubao.com/chat"],
        [YUANBAO]: ["https://yuanbao.tencent.com/"],

        [ZCHAT]: ["https://chat.zchat.tech/"],
        [GEMINI]: ["https://gemini.google.com/app"],
        [STUDIO]: ["https://aistudio.google.com/"],
        [QWEN]: ["https://chat.qwen.ai/"],
        [CLAUDE]: ["https://claude.ai/chat"],
        [GROK]: ["https://grok.com/"]
    };

    // 多选面板里,各站点的全称、简称
    let wordConfig = [
        { site: DEEPSEEK, word: 'DeepSeek', alias: 'D'},
        { site: KIMI, word: 'Kimi', alias: 'K' },
        { site: TONGYI, word: '千问', alias: '千' },
        { site: QWEN, word: 'Qwen', alias: 'Q' },
        { site: DOUBAO, word: '豆包', alias: '豆' },
        { site: YUANBAO, word: '元宝', alias: '元' },

        { site: ZCHAT, word: 'ZCHAT-GPT', alias: 'Z' },
        { site: CHATGPT, word: 'ChatGPT', alias: 'C' },
        { site: GEMINI, word: 'Gemini', alias: 'G' },
        { site: STUDIO, word: 'AI Studio', alias: 'A' },
        { site: CLAUDE, word: 'Claude', alias: 'Cl' },
        { site: GROK, word: 'Grok', alias: 'Gr' }
    ];
    // 过滤掉被禁用的站点
    wordConfig = wordConfig.filter(item => !DISABLE_SITES.includes(item.site));

    // (可选)隐藏输入框及周边区域,所需隐藏的元素,是输入框本体的第几层父元素?以下数字即层数(后续应改为半自动配置)
    const inputAreaHideParentLevel = {
        [DEEPSEEK]: 5,
        [KIMI]: 4,
        [TONGYI]: 6,
        [QWEN]: 9,
        [DOUBAO]: 11,
        [YUANBAO]: 10,

        [ZCHAT]: 10,
        [CHATGPT]: 10,
        [GEMINI]: 9,
        [STUDIO]: 11,
        [CLAUDE]: 6,
        [GROK]: 10
    };

    const newSites = Object.fromEntries(
        Object.entries(webSites).map(([key, [baseUrl]]) => [key, baseUrl])
    );

    // 表示当前站点的变量
    let site = 0;
    let currentUrl = getUrl();

    // 根据当前网址关键词,设置site值
    for (const keyword in keywords) {
        if (currentUrl.indexOf(keyword) > -1) {
            site = keywords[keyword];
            break;
        }
    }

    // 检查当前站点是否被禁用
    if (DISABLE_SITES.includes(site)) {
        console.log(`站点 ${site} 已被禁用,脚本完全退出`);
        return;
    }

    // 面板数据常量
    const CHOSEN_SITE = "chosenSite";
    
    // 按钮显示状态存储键名(GM存储,所有站点共享)
    const SHOW_TOGGLE_BUTTON_KEY = "showToggleButton";
    const SHOW_BOOKMARK_BUTTON_KEY = "showBookmarkButton"; // 同时控制"书签"和"历史"两个按钮
    const DEFAULT_HIDE_INPUT_AREA_KEY = "defaultHideInputArea"; // 默认隐藏输入框
    
    // 书签功能总开关存储键名(GM存储,所有站点共享)
    const ENABLE_BOOKMARK_FEATURE_KEY = "enableBookmarkFeature";
    
    // 多选面板可见模型列表存储键名(GM存储,所有站点共享)
    const VISIBLE_MODELS_KEY = "visibleModels";
    
    // 输入框隐藏层级自定义配置存储键名(GM存储,所有站点共享)
    const INPUT_AREA_HIDE_PARENT_LEVEL_KEY = "inputAreaHideParentLevel";

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  🔧 2、一些函数和变量  🔧                                            ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 过滤出问题列表(偶数索引元素)
    const filterQuestions = (elements) => {
        if (!isEmpty(elements)) {
            let elementsArray = Array.from(elements);
            return elementsArray.filter((item, index) => index % 2 === 0);
        }
        return [];
    };

    const INVISIBLE_EDGE_CHARS_REGEX = /^[\u200B-\u200D\uFEFF]+|[\u200B-\u200D\uFEFF]+$/g;

    // 标准化问题文本:移除头尾不可见字符 + 特定站点前缀
    const normalizeQuestionText = (text) => {
        if (!text) return '';
        const cleanedText = text.replace(INVISIBLE_EDGE_CHARS_REGEX, '');
        const trimmedText = cleanedText.trim();
        const removeWord = 'User';
        if (site === STUDIO && trimmedText.startsWith(removeWord)) {
            return trimmedText.substring(removeWord.length).trim();
        }
        return trimmedText;
    };


    // 以下几个,是获取元素的入口方法
    function getQuestionList() {
        const selector = selectors.questionList[site];
        return selector ? selector() : [];
    }

    function getInputArea() {
        const selector = selectors.inputArea[site];
        return selector ? selector() : null;
    }

    function getSendButton() {
        const selector = selectors.sendBtn[site];
        return selector ? selector() : null;
    }

    // STUDIO站点的特殊处理已移到getSubNavTop函数中

    // 系统功能配置
    const checkGap = 100;
    const maxRetries = 200;
    const OPEN_GAP = 300; // 打开网页的间隔
    const HIBERNATE_GAP = 600; // 单位:秒
    let testLocalFlag = 0;

    // 存储时的特征词
    const T = "tool-";
    const HEART_KEY_PREFIX ="lastHeartbeat-";

    // 同步书签相关常量
    const BOOKMARK_PREFIX = "bookmark-";           // 书签存储key前缀
    const BOOKMARK_ID_COUNTER = "bookmarkIdCounter"; // 书签ID计数器
    const CURRENT_BOOKMARK_KEY = "currentBookmarkKey"; // 当前书签key
    const BOOKMARK_KEY_LIST = "bookmarkKeyList";   // 书签key列表
    const BOOKMARK_DELETE_CONFIRMED = "bookmarkDeleteConfirmed"; // 是否已首次确认删除
    const BOOKMARK_GROUP_LIST = "bookmarkGroupList"; // 分组列表
    const BOOKMARK_LAST_SELECTED_GROUP = "bookmarkLastSelectedGroup"; // 上次选中的分组ID
    const DEFAULT_GROUP_NAME = "默认"; // 默认分组名称
    const DEFAULT_GROUP_ID = 0; // 默认分组代号
    // 书签按钮公共样式(不包含 bottom 和 background)
    const BOOKMARK_BTN_BASE_STYLE = "position:fixed;right:0;color:white;font-size:14px;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:10000;border-radius:6px 0 0 6px;box-shadow:-2px 2px 8px rgba(0,0,0,0.2);user-select:none;padding:3px 5px";

    let DOMAIN = "https://www.ratetend.com:5001";
    const DEVELOPER_USERID = "7bca846c-be51-4c49-ba2b6"

    let userid = getGV("userid");
    if(isEmpty(userid)){
        userid = guid();
        setGV("userid", userid);
    }

    setTimeout(developTest, 2000);
    function developTest(){
        // kimi 表格太窄,自测调大用
        if(DEVELOPER_USERID === userid && site === KIMI){
            // let kimiPage = document.getElementsByClassName("chat-content-list")[0];
            // kimiPage.style.maxWidth = TEST_KIMI_WIDTH;
        }
    }


    let startUrl = DOMAIN + "/start";
    let startData = {
        "userid": userid,
        "site": site,
        "version": version
    };
    remoteHttp(startUrl, startData);

    // 生成映射
    const wordToSite = {};
    const siteToWord = {};
    const siteToAlias = {};
    const wordToAlias = {};
    const words = [];

    wordConfig.forEach(({ site, word, alias }) => {
        words.push(word);

        wordToSite[word] = site;
        siteToWord[site] = word;
        siteToAlias[site] = alias;
        wordToAlias[word] = alias;
    });


    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  📡 3、主从节点逻辑  📡                                              ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 给发送环节加锁。因为send环节是异步轮询,新问题来时send未必轮询结束
    let sendLock = false;

    // 页面加载时,向本地存储发送一次心跳
    setGV(HEART_KEY_PREFIX + site, Date.now());

    function masterCheck(lastestQ){
        if(sendLock){
            return;
        }
        if(isEmpty(lastestQ)){
            return;
        }

        let msg = {
            question: lastestQ,
            date: Date.now()
        };
        console.log(msg);
        setGV("msg", msg);

        addCurrentToStorage();

        let isDisable = getGV("disable");
        if(isDisable){
            return;
        }
        let remoteUrl = DOMAIN + "/masterQ";
        let sites = getSitesExcludeCurrent();
        let data = {
            "userid": userid,
            "sites": sites
        };
        remoteHttp(remoteUrl, data);

        sites.forEach(site => {
            let lastHeartbeat = getGV(HEART_KEY_PREFIX + site);
            // 如果从节点 xx 秒没有更新心跳时刻,则认为已经关闭,需打开
            if(isEmpty(lastHeartbeat) || Date.now() - lastHeartbeat > 1000 * HIBERNATE_GAP){
                setTimeout(function(){
                    window.open(newSites[site], '_blank');
                }, OPEN_GAP);
            }
        });
    }

    let lastQuestion = "";

    // 监听是否有新的提问
    GM_addValueChangeListener('msg', function(name, oldValue, msg, remote) {
        if(!remote){
            return;
        }
        if(getGV("disable") === true){
            return;
        }

        let sites = getSitesOfStorage();
        if(sites.includes(site)){
            // 避免短时重复发送:假定新的提问出现时,上次的提问已经发送出去,故正常情况sendLock已解锁
            if(sendLock){
                return;
            }
            
            let msg = getGV("msg");
            let question = msg.question;
            // 避免重复发送
            if(question === lastQuestion){
                return;
            }
            sendQuestion(question);
        }
    });

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  ⚙️ 4、从节点异步轮询检查  ⚙️                                        ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    /**
     * 发送提问内容
     * 整体涉及这些轮询检查:① 输入框的存在 ② 发送按钮的存在 ③ 输入框的清空
     */

    /**
     * 发送问题的主流程
     */
    async function sendQuestion(content) {
        updateBoxFromStorage();
        sendLock = true;
        lastQuestion = content;

        try {
            // 步骤1: 等待输入框出现(使用 MutationObserver)
            const inputArea = await waitForElement(
                () => getInputArea(),
                {timeout: 10000, timeoutMsg: "监测输入框存在超时"}
            );
            // 步骤2、3: 粘贴内容到输入框、等待发送按钮出现并点击
            await pasteContent(inputArea, content);
            await waitAndClickSendButton();

        } catch (error) {
            console.error("发送问题失败:", error);
            sendLock = false;
        }
    }

    /**
     * 等待发送按钮出现并执行发送流程
     */
    async function waitAndClickSendButton() {
        console.log(curDate() + "h1 等待发送按钮");

        try {
            // 等待发送按钮出现(使用 MutationObserver)
            const sendBtn = await waitForElement(
                () => getSendButton(),
                {timeout: maxRetries * checkGap, timeoutMsg: "发送按钮未找到"}
            );

            // 点击页面空白处,然后点击发送按钮
            await new Promise((resolve) => {
                setTimeout(() => {
                    document.body.click();
                    setTimeout(() => {
                        console.log(curDate() + "h2 点击发送按钮");
                        sendBtn.click();
                        resolve();
                    }, 200);
                }, 200);
            });

            // 验证发送成功
            await verifySendSuccess(sendBtn);

        } catch (error) {
            console.error("发送失败:", error);
            sendLock = false;
            throw error;
        }
    }

    /**
     * 验证发送成功(输入框内容清空)
     */
    async function verifySendSuccess(sendBtn) {
        const pollInterval = 500;
        const maxPollTime = 20000;
        const startTime = Date.now();
        if(site === YUANBAO){
            pollInterval = 2000;
        }

        return new Promise((resolve) => {
            function checkInputArea() {
                const elapsed = Date.now() - startTime;
                const inputArea = getInputArea();
                const areaContent = getInputContent(inputArea);

                // 输入框为空,表明发送成功
                if (!areaContent || areaContent.trim() === '') {
                    sendLock = false;
                    resolve();
                    return;
                }

                // 超时,解锁并返回
                if (elapsed >= maxPollTime) {
                    console.warn("发送验证超时,但可能已经成功发送");
                    sendLock = false;
                    resolve();
                    return;
                }


                // 输入框仍有内容,继续点击发送按钮
                console.log(curDate() + "h3 重试发送");
                sendBtn.click();

                setTimeout(checkInputArea, pollInterval);
            }

            setTimeout(checkInputArea, pollInterval);
        });
    }

    /**
     * 输入框粘贴提问内容
     */
    async function pasteContent(editor, content) {
        return new Promise((resolve) => {
            setTimeout(() => {
                // 输入框粘贴文字,大致分两类处理。其中第一类里 kimi 特殊处理
                //  第一类(lexical)
                if (inputAreaTypes.lexical.includes(site)) {
                    if ([KIMI].includes(site)) {
                        editor.dispatchEvent(new InputEvent('input', { bubbles: true, data: content }));
                    } else {
                        editor.textContent = content;
                    }
                    //  第二类(textarea 标签)
                } else if (inputAreaTypes.textarea.includes(site)) {
                    const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
                        window.HTMLTextAreaElement.prototype,
                        'value'
                    ).set;
                    nativeInputValueSetter.call(editor, content);
                    // 触发 input 事件
                    editor.dispatchEvent(new Event('input', { bubbles: true }));
                }
                resolve();
            }, 100);
        });
    }

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  🖼️ 5、图片同步功能  🖼️                                              ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 监听是否有新的图片
    GM_addValueChangeListener('image', function(name, oldValue, msg, remote) {
        if(!remote){
            return;
        }
        if(getGV("disable") === true){
            return;
        }

        let sites = getSitesOfStorage();
        if(sites.includes(site)){
            pasteImage();
        }
    });

    // 主节点监听粘贴事件
    const imageKey = "image";
    const currentAskHasImage = "currentAskHasImage";

    document.addEventListener('paste', async (e) => {
        const items = e.clipboardData?.items;
        if (!items) return;

        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            if (item.type.startsWith('image/')) {
                const blob = item.getAsFile();
                if (!blob) continue;

                // 转为 Base64
                const base64 = await blobToBase64(blob);

                GM_setValue(imageKey, base64);
                setS(T + currentAskHasImage, "1");

                break; // 手动粘贴图片后,脚本读取最后一张图,存入共享存储
            }
        }
    });

    // 模拟将 base64 图片粘贴到输入框(返回在实际触发粘贴后才 resolve)
    async function pasteImage() {
        const base64 = GM_getValue(imageKey);
        if (!base64) {
            console.error('未找到指定的图片');
            return false;
        }
        return new Promise((resolve) => {
            try {
                const blob = base64ToBlob(base64);
                const file = new File([blob], 'pasted-image.png', {
                    type: blob.type || 'image/png',
                    lastModified: Date.now()
                });

                const dataTransfer = new DataTransfer();
                dataTransfer.items.add(file);

                const pasteEvent = new ClipboardEvent('paste', {
                    clipboardData: dataTransfer,
                    bubbles: true,
                    cancelable: true
                });

                let targetElement = getInputArea();
                let retryCount = 0;
                const maxRetries = 100; // 最多重试(20秒)
                const interval = setInterval(() => {
                    retryCount++;
                    targetElement = getInputArea(); // 每次重试时重新获取元素
                    if (targetElement && typeof targetElement.focus === 'function') {
                        clearInterval(interval);
                        targetElement.focus();

                        // 粘贴
                        const dispatched = targetElement.dispatchEvent(pasteEvent);
                        console.log('模拟粘贴图片成功');
                        resolve(!!dispatched);
                    } else if (retryCount >= maxRetries) {
                        clearInterval(interval);
                        console.warn('粘贴图片超时:输入框未找到或无法聚焦');
                        resolve(false);
                    }
                }, 200);
            } catch (error) {
                console.error('模拟粘贴失败:', error);
                resolve(false);
            }
        });
    }

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  👂 6、监听新的提问:监听输入框回车事件、发送按钮点击事件  👂        ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 检查事件是否带有修饰键
    const hasModifierKey = (event) => event.shiftKey || event.ctrlKey || event.altKey || event.metaKey;

    // 根据输入框类型获取内容
    function getInputContent(inputArea) {
        if (isEmpty(inputArea)) return '';

        // textarea 类型使用 .value
        if (inputAreaTypes.textarea.includes(site)) {
            return inputArea.value ? inputArea.value.trim() : '';
        }
        // lexical 类型使用 .textContent
        else if (inputAreaTypes.lexical.includes(site)) {
            return inputArea.textContent ? inputArea.textContent.trim() : '';
        }

        return '';
    }

    // 监听发送按钮点击事件和回车键
    let sendBtnListenerAdded = false;
    let inputAreaListenerAdded = false;
    let pendingQuestion = null; // 临时存储按下时的问题
    let lastUrl = getUrl(); // 记录上次的URL
    let cachedInputContent = ""; // 缓存的输入框内容

    function addSendButtonListener() {
        const sendBtn = getSendButton();
        const inputArea = getInputArea();

        if (!isEmpty(sendBtn) && !sendBtnListenerAdded) {
            // 给元素添加标记,用于检测元素是否被替换
            sendBtn.setAttribute('data-listener-added', 'true');

            // 鼠标按下(记录输入框内容)
            sendBtn.addEventListener('mousedown', function() {
                const inputArea = getInputArea();
                if (!isEmpty(inputArea)) {
                    const lastestQ = getInputContent(inputArea);
                    // 如果lastestQ为空,则使用缓存的内容
                    const questionToUse = isEmpty(lastestQ) ? cachedInputContent : lastestQ;
                    if (!isEmpty(questionToUse)) {
                        pendingQuestion = questionToUse;
                    }
                }
            });

            // 鼠标移出(取消)
            sendBtn.addEventListener('mouseleave', function() {
                if (!isEmpty(pendingQuestion)) {
                    console.log("鼠标移出按钮,取消发送");
                    pendingQuestion = null;
                }
            });

            // 鼠标释放(发送提问)
            sendBtn.addEventListener('mouseup', function() {

                if (!isEmpty(pendingQuestion)) {
                    const questionToSend = pendingQuestion;
                    pendingQuestion = null; // 清空临时变量

                    setTimeout(function() {
                        masterCheck(questionToSend);
                    }, 100);
                }
            });

            sendBtnListenerAdded = true;
            console.log("✓ 发送按钮监听器已添加");
        }

        // 监听输入框的回车键和输入内容
        if (!isEmpty(inputArea) && !inputAreaListenerAdded) {
            // 给元素添加标记,用于检测元素是否被替换
            inputArea.setAttribute('data-listener-added', 'true');

            // 监听输入框内容变化
            inputArea.addEventListener('input', function() {
                cachedInputContent = getInputContent(inputArea);
            });

            inputArea.addEventListener('keydown', function(event) {
                let isTrigger = false;
                if (site === STUDIO) {
                    // STUDIO: Ctrl + Enter (Windows) or Command + Enter (macOS)
                    if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
                        isTrigger = true;
                    }
                } else {
                    // 单纯的 Enter 键,不带任何修饰键
                    if (event.key === 'Enter' && !hasModifierKey(event)) {
                        isTrigger = true;
                    }
                }

                if (isTrigger) {
                    const lastestQ = getInputContent(inputArea);
                    console.log("lastestQ: "+lastestQ);
                    const questionToUse = isEmpty(lastestQ) ? cachedInputContent : lastestQ;
                    if (!isEmpty(questionToUse)) {
                        setTimeout(function() {
                            masterCheck(questionToUse);
                        }, 100);
                    }
                }
            });
            inputAreaListenerAdded = true;
            console.log("✓ 输入框回车监听器已添加");
        }

        // 如果按钮或输入框还没加载,稍后重试
        if (!sendBtnListenerAdded || !inputAreaListenerAdded) {
            setTimeout(addSendButtonListener, 500);
        } else {
            // 输入框和按钮都加载完成后,应用默认隐藏设置
            applyDefaultHideInputArea();
        }
    }

    // 检查监听器是否丢失(元素被替换)
    function checkListenerIntegrity() {
        const sendBtn = getSendButton();
        const inputArea = getInputArea();

        // 检查发送按钮
        if (!isEmpty(sendBtn) && sendBtnListenerAdded) {
            const hasMarker = sendBtn.getAttribute('data-listener-added') === 'true';
            if (!hasMarker) {
                console.warn("⚠ 发送按钮元素已被替换,监听器丢失!重新添加...");
                sendBtnListenerAdded = false;
            }
        }

        // 检查输入框
        if (!isEmpty(inputArea) && inputAreaListenerAdded) {
            const hasMarker = inputArea.getAttribute('data-listener-added') === 'true';
            if (!hasMarker) {
                console.warn("⚠ 输入框元素已被替换,监听器丢失!重新添加...");
                inputAreaListenerAdded = false;
            }
        }

        // 如果发现监听器丢失,重新添加
        if (!sendBtnListenerAdded || !inputAreaListenerAdded) {
            setTimeout(addSendButtonListener, 1000);
        }
    }
    // 标记输入框是否处于隐藏状态
    let isInputAreaHidden = false;

    // 监听URL变化,重新添加监听器
    function checkUrlChange() {
        const currentUrl = getUrl();

        if (currentUrl !== lastUrl) {
            console.log("URL已变化,重新添加监听器");
            lastUrl = currentUrl;

            let nthInputArea = getNthInputArea();
            if(site === GEMINI){
                // gemini 打开新对话的情况
                if(isInputAreaHidden && nthInputArea.style.display === 'none' && getQuestionList().length === 0){
                    nthInputArea.style.display = 'flex';
                    isInputAreaHidden = false;
                }
            }
            // 如果打开新对话,可能导致 display 值清空,此时输入框并未隐藏
            if(nthInputArea.style.display === ''){
                toggleBtnStatus(true);
                isInputAreaHidden = false;
            }

            sendBtnListenerAdded = false;
            inputAreaListenerAdded = false;
            pendingQuestion = null;

            // URL 变化时隐藏副目录
            if (typeof hideSubNavBar === 'function') {
                hideSubNavBar();
            }

            setTimeout(addSendButtonListener, 500);
        }
    }

    // 定期检查URL变化和监听器完整性
    setInterval(function() {
        reloadCompactMode();
        checkUrlChange();
        checkListenerIntegrity();
        setGV(HEART_KEY_PREFIX + site, Date.now());

        let questions = getQuestionList();
        updateNavQuestions(questions);

        // 单独适配:gemini的表格宽度、studio的内容宽度
        if(site === GEMINI){
            const ADAPTIVE_WIDTH = window.outerWidth * 0.8 + "px";

            let askContent = document.querySelectorAll('.conversation-container');
            if(askContent.length > 0){
                askContent.forEach((element, index) => {
                    element.style.maxWidth = GEMINI_MAX_WIDTH;
                    element.style.width = ADAPTIVE_WIDTH;
                });
            }

            let tables = document.querySelectorAll('.horizontal-scroll-wrapper');
            if(tables.length > 0){
                tables.forEach((element, index) => {
                    element.style.maxWidth = GEMINI_MAX_WIDTH;
                    element.style.width = ADAPTIVE_WIDTH;
                });
            }
            let graphs = document.querySelectorAll('.code-block');
            if(graphs.length > 0){
                graphs.forEach((element, index) => {
                    element.style.maxWidth = GEMINI_MAX_WIDTH;
                    element.style.width = ADAPTIVE_WIDTH;
                });
            }

        }
        if(site === STUDIO){
            let studioContent = document.querySelector('.chat-session-content');
            if(!isEmpty(studioContent)){
                studioContent.style.maxWidth = STUDIO_CONTENT_MAX_WIDTH;
            }
        }


    }, 1800);


    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  🎨 7、trusted HTML & 首次使用指引 🎨                        ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 安全处理HTML内容(Trusted Types支持)
    let policy = null;
    try {
        if (window.trustedTypes) {
            policy = trustedTypes.createPolicy("forceInner", {
                createHTML: (to_escape) => to_escape
            });
        }
    } catch(e) {
        policy = null;
    }

    function makeHTML(content){
        if(isEmpty(policy)){
            return content;
        }else{
            try {
                return policy.createHTML(content);
            } catch(e) {
                return content;
            }
        }
    }

    // 安全设置 innerHTML,如果失败则使用 DOM 方法
    function setInnerHTML(element, html) {
        if (isEmpty(html)) {
            // 清空内容使用 replaceChildren 更安全
            element.replaceChildren();
            return;
        }

        try {
            const trustedHTML = makeHTML(html);
            element.innerHTML = trustedHTML;
        } catch(e) {
            // 如果 Trusted Types 失败,使用 DOMParser
            try {
                const parser = new DOMParser();
                const doc = parser.parseFromString(html, 'text/html');
                element.replaceChildren(...Array.from(doc.body.childNodes));
            } catch(parseError) {
                // 如果 DOMParser 也失败,使用 textContent 作为最后手段
                element.textContent = html.replace(/<[^>]*>/g, '');
            }
        }
    }

    // 面板延迟时间
    let panelDelay = site === ZCHAT ? 500 : 50;
    const panel = createTag('div', "", "");

    /**
     * 脚本首次使用的指引
     */
    let FIRST_RUN_KEY = "firstRun";
        setTimeout(function(){
        appendSeveral(document.body, panel, toggleButton, subNavBar);
        reloadDisableStatus();
        updateButtonVisibility(); // 根据设置更新按钮显示状态

        // 添加发送按钮监听
        setTimeout(addSendButtonListener, 1000);

        setTimeout(function(){
            // 首次运行
            if(isEmpty(getGV(FIRST_RUN_KEY))){
                setGV(FIRST_RUN_KEY, 1);
                let updateHint = "网页右下方的多选面板可勾选提问范围,\n\n" +
                    "点击\"设置\"按钮可进行多种设置;\n\n";

                showMessagePopup(updateHint, "脚本使用提示");
            } else {
                // 非首次运行,检查版本更新
                let VERSION_MARK = FIRST_RUN_KEY + "_5";
                if(isEmpty(getGV(VERSION_MARK))){
                    setGV(VERSION_MARK, 1);
                    let updateHint = "多选面板上新增了【设置】按钮,可进行多种自定义设置;\n\n";
                    showMessagePopup(updateHint, "近期更新");
                }
            }

        }, 800);
    }, panelDelay);


    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  🎨 8、输入框的显示/隐藏切换 🎨                        ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/


    /**
     * 输入框的显示/隐藏切换功能
     */
    // 切换按钮相关常量
    const TOGGLE_BUTTON_BG_SHOW = '#ec7258';
    const TOGGLE_BUTTON_BG_HIDE = '#999';
    const TOGGLE_BUTTON_STYLE = `font-size:14px;padding:5px;position:fixed;cursor:pointer;background:${TOGGLE_BUTTON_BG_SHOW};color:white;border:1px solid #ddd;border-radius:30%;box-shadow:0 4px 12px rgba(0,0,0,0.2);z-index:99999999;display:flex;align-items:center;justify-content:center;`;

    // 切换状态配置
    const TOGGLE_STATES = {
        show: {
            text: '隐藏',
            bg: TOGGLE_BUTTON_BG_SHOW,
            display: 'flex'
        },
        hide: {
            text: '显示',
            bg: TOGGLE_BUTTON_BG_HIDE,
            display: 'none'
        }
    };

    const toggleButton = createTag('div', TOGGLE_STATES.show.text, TOGGLE_BUTTON_STYLE);
    toggleButton.title = '临时隐藏输入框获得更大的视野高度';

    const getNthParent = (el, n) => n > 0 ? getNthParent(el?.parentElement, n - 1) : el;

    function getNthInputArea(){
        const inputArea = getInputArea();
        // 优先使用用户自定义的层级值
        const customLevels = getGV(INPUT_AREA_HIDE_PARENT_LEVEL_KEY) || {};
        let level = customLevels[site] !== undefined ? customLevels[site] : inputAreaHideParentLevel[site];
        if(site === CHATGPT && getUrl().indexOf("/g/") > -1){
            level = level - 2;
        }
        return getNthParent(inputArea, level);
    }

    // 按钮点击事件 - 切换面板显示/隐藏
    toggleButton.addEventListener('click', (e) => {
        e.stopPropagation();
        toggleInput();
    });

    function toggleInput(){
        const aroundInputArea = getNthInputArea();
        // 如果输入框是被用户隐藏的,则切换输入框、按钮状态
        const isHidden = aroundInputArea.style.display === 'none';
        // 更新隐藏状态标记
        isInputAreaHidden = !isHidden;

        const state = isHidden ? TOGGLE_STATES.show : TOGGLE_STATES.hide;
        toggleBtnStatus(isHidden);
        aroundInputArea.style.display = state.display;
    }

    function toggleBtnStatus(isHidden){
        const state = isHidden ? TOGGLE_STATES.show : TOGGLE_STATES.hide;
        toggleButton.textContent = state.text;
        toggleButton.style.background = state.bg;
    }

    // 应用默认隐藏输入框设置
    function applyDefaultHideInputArea() {
        const shouldHide = getGV(DEFAULT_HIDE_INPUT_AREA_KEY) === true;
        if (!shouldHide) {
            return;
        }
        
        // 如果是新对话(问题列表为空),则不隐藏输入框
        const questions = getQuestionList();
        if (questions.length === 0) {
            return;
        }

        
        const aroundInputArea = getNthInputArea();
        if (aroundInputArea && aroundInputArea.style.display !== 'none') {
            aroundInputArea.style.display = TOGGLE_STATES.hide.display;
            isInputAreaHidden = true;
            toggleBtnStatus(false);
        }
    }

    // 存储的key
    const TOGGLE_BOTTOM_KEY = T + 'toggleBottom';
    const TOGGLE_LEFT_KEY = T + 'toggleLeft';
    const TOGGLE_MAX_LEFT_KEY = T + 'toggleMaxLeft';
    const TOGGLE_DELTA1_KEY = T + 'toggleDelta1';
    const TOGGLE_DELTA2_KEY = T + 'toggleDelta2';

    const BUTTON_RIGHT_OFFSET = 20; // 按钮右边缘的偏移量
    const DEFAULT_LEFT_OFFSET = 40; // 默认left值的偏移量
    const MIN_RIGHT_THRESHOLD = 10; // right值的最小阈值
    const TOOL_PANEL_ID = 'tool-panel'; // 多选面板的ID

    /**
     * 计算bottom值
     */
    function calculateBottom() {
        const savedBottom = getS(TOGGLE_BOTTOM_KEY);
        if (savedBottom !== null) {
            return parseFloat(savedBottom);
        }

        const UPDATE_BOTTOM_THRESHOLD = 45;
        const sendButton = getSendButton();
        // 发送按钮存在,若新 bottom < 阈值,才更新
        if (sendButton) {
            const calculatedBottom = window.innerHeight - sendButton.getBoundingClientRect().bottom;
            if (calculatedBottom < UPDATE_BOTTOM_THRESHOLD) {
                setS(TOGGLE_BOTTOM_KEY, calculatedBottom.toString());
                return calculatedBottom;
            }
        }

        // 默认值
        return UPDATE_BOTTOM_THRESHOLD;
    }

    /**
     * 计算left值
     * @param {HTMLElement} inputArea - 输入框元素
     * @param {HTMLElement} sendButton - 发送按钮元素
     */
    function calculateLeft(inputArea, sendButton) {
        let hasInputArea = !!inputArea;
        let hasSendButton = !!sendButton;

        const defaultLeft = window.innerWidth - DEFAULT_LEFT_OFFSET;

        // 情况1: 输入框√,按钮√
        if (hasInputArea && hasSendButton) {
            const right1 = sendButton.getBoundingClientRect().right;
            const right2 = inputArea.getBoundingClientRect().right;

            // 检查right值是否有效,无效则重置对应标志
            hasSendButton = hasSendButton && right1 >= MIN_RIGHT_THRESHOLD;
            hasInputArea = hasInputArea && right2 >= MIN_RIGHT_THRESHOLD;

            // 两者都有效才存储
            if (hasInputArea && hasSendButton) {
                const left = right1 + BUTTON_RIGHT_OFFSET;
                const delta1 = BUTTON_RIGHT_OFFSET;
                const delta2 = left - right2;

                setS(TOGGLE_LEFT_KEY, left.toString());
                setS(TOGGLE_DELTA1_KEY, delta1.toString());
                setS(TOGGLE_DELTA2_KEY, delta2.toString());

                // 如果当前是最大宽度,额外记录maxLeft
                if (isMaxWidth()) {
                    setS(TOGGLE_MAX_LEFT_KEY, left.toString());
                }

                return left;
            }
        }

        // 情况2: 输入框√,按钮×。等于 输入框右边缘 + delta
        if (hasInputArea && !hasSendButton) {
            const savedDelta2 = getS(TOGGLE_DELTA2_KEY);
            if (savedDelta2 !== null) {
                const right2 = inputArea.getBoundingClientRect().right;
                return right2 + parseFloat(savedDelta2);
            }
            return defaultLeft;
        }

        // 情况3: 输入框×,按钮√。等于 按钮右边缘 + delta
        if (!hasInputArea && hasSendButton) {
            const savedDelta1 = getS(TOGGLE_DELTA1_KEY);
            if (savedDelta1 !== null) {
                const right1 = sendButton.getBoundingClientRect().right;
                return right1 + parseFloat(savedDelta1);
            }
            return defaultLeft;
        }

        // 情况4: 输入框×,按钮×。用存储的 left
        const savedLeft = getS(TOGGLE_LEFT_KEY);
        if (savedLeft !== null) {
            return parseFloat(savedLeft);
        }
        return defaultLeft;
    }

    /**
     * 更新 toggle 按钮的位置和显示状态
     * @param {boolean} isResizeEvent - 是否是resize事件触发
     */
    function updateToggleButtonPosition(isResizeEvent = false) {
        // 如果处于隐藏状态且非resize场景,直接返回,不更新位置
        if (isInputAreaHidden && !isResizeEvent) {
            return;
        }

        const bottom = calculateBottom();
        let left;

        // 如果处于隐藏状态且是 resize 场景
        if (isInputAreaHidden && isResizeEvent) {
            // 特殊情况:如果resize到最大宽度且有保存的maxLeft,优先使用maxLeft
            if (isMaxWidth()) {
                const savedMaxLeft = getS(TOGGLE_MAX_LEFT_KEY);
                if (savedMaxLeft !== null) {
                    left = parseFloat(savedMaxLeft);
                } else {
                    // 没有保存的maxLeft,跟随多选面板的位置
                    const toolPanel = document.getElementById(TOOL_PANEL_ID);
                    if (toolPanel) {
                        const panelRect = toolPanel.getBoundingClientRect();
                        left = panelRect.left;
                    } else {
                        left = window.innerWidth - DEFAULT_LEFT_OFFSET;
                    }
                }
            } else {
                // 非最大宽度,跟随缩略状态的多选面板的left位置
                const toolPanel = document.getElementById(TOOL_PANEL_ID);
                if (toolPanel) {
                    const panelRect = toolPanel.getBoundingClientRect();
                    left = panelRect.left;
                } else {
                    left = window.innerWidth - DEFAULT_LEFT_OFFSET;
                }
            }
        } else {
            const inputArea = getInputArea();
            const sendButton = getSendButton();
            left = calculateLeft(inputArea, sendButton);
        }

        // 更新toggle按钮位置
        toggleButton.style.left = `${left}px`;
        toggleButton.style.bottom = `${bottom}px`;
    }

    /**
     * 轮询更新 toggle 按钮的位置和显示状态
     */
    function pollToggleButtonPosition() {
        const POLL_INTERVAL = 1000; // 轮询间隔1000ms

        const checkAndUpdate = () => {
            updateToggleButtonPosition();
            setTimeout(checkAndUpdate, POLL_INTERVAL);
        };

        // 开始轮询
        checkAndUpdate();
    }

    // 页面加载后开始持续轮询
    pollToggleButtonPosition();

    // 监听窗口宽度变化,更新toggle按钮的位置和显示状态
    let resizeTimer = null;
    window.addEventListener('resize', () => {
        // 防抖处理,避免频繁触发
        clearTimeout(resizeTimer);
        resizeTimer = setTimeout(() => updateToggleButtonPosition(true), 50);
    });

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  📑 9、目录导航功能  📑                                              ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 导航变量默认值
    const DEFAULT_NAV_MAX_WIDTH = "230px";
    const DEFAULT_NAV_TOP = "20%";
    const DEFAULT_NAV_TOP_OVERFLOW = "7%";
    const DEFAULT_SUB_NAV_MAX_WIDTH = "260px";
    const DEFAULT_SUB_NAV_TOP = "20%";
    
    // 存储键名
    const NAV_MAX_WIDTH_KEY = "navMaxWidth";
    const SUB_NAV_MAX_WIDTH_KEY = "subNavMaxWidth";
    const NAV_TOP_KEY = "navTop";
    const NAV_TOP_OVERFLOW_KEY = "navTopOverflow";
    const SUB_NAV_TOP_KEY = "subNavTop";
    
    // 从GM存储读取导航变量,如果没有则使用默认值
    const getNavMaxWidth = () => {
        return getGV(NAV_MAX_WIDTH_KEY) || DEFAULT_NAV_MAX_WIDTH;
    };
    
    const getNavTop = () => {
        return getGV(NAV_TOP_KEY) || DEFAULT_NAV_TOP;
    };
    
    const getNavTopOverflow = () => {
        return getGV(NAV_TOP_OVERFLOW_KEY) || DEFAULT_NAV_TOP_OVERFLOW;
    };
    
    const getSubNavMaxWidth = () => {
        return getGV(SUB_NAV_MAX_WIDTH_KEY) || DEFAULT_SUB_NAV_MAX_WIDTH;
    };
    
    const getSubNavTop = () => {
        const saved = getGV(SUB_NAV_TOP_KEY);
        if (saved) {
            return saved;
        }
        return site === STUDIO ? "35%" : DEFAULT_SUB_NAV_TOP;
    };
    
    const NAV_TOP_THRESHOLD = 7;    // 主目录条目超过此阈值时,top位置抬高
    const NAV_COUNT_THRESHOLD = 12; // 主目录条数超过此阈值时,会显示"共xx条"

    const SUB_NAV_LEFT = "270px";     // 副目录的水平位置(距离屏幕左侧)
    const SUB_NAV_MIN_ITEMS = 2;      // 副目录标题总条数超过此阈值才显示
    const SUB_NAV_TOP_THRESHOLD = 18; // 副目录标题条数超过此阈值时,top位置抬高到5%
    const SUB_NAV_PREV_LEVEL_THRESHOLD = 25; // 总条数超过此阈值时,默认显示到上一层级(如h4显示到h3,h3显示到h2)

    // 查找回答内容区域的查找限制(用于性能优化)
    const FIND_ANSWER_MIDDLE_SIBLING_LIMIT = 30; // 中间问题查找时的兄弟元素上限(原50,已优化)
    const FIND_ANSWER_LAST_SIBLING_LIMIT = 15; // 最后一个问题查找时的兄弟元素上限(原20,已优化)
    const FIND_ANSWER_PARENT_DEPTH_LIMIT = 10// 向上查找父元素的最大深度(原10,已优化)


    const NAV_ITEM_COLOR = "#333";
    // 副目录项悬停样式常量
    const SUB_NAV_ITEM_HOVER_BG = '#f0f0f0';
    const SUB_NAV_ITEM_HOVER_COLOR = '#0066cc';
    const SUB_NAV_ITEM_NORMAL_BG = 'transparent';
    const SUB_NAV_ITEM_NORMAL_COLOR = '#333';
    // 目录导航相关常量
    const NAV_HIGHLIGHT_THRESHOLD = 0.3; // 目录高亮阈值(0~30%高亮当前项,30%~100%高亮前一项)
    const NAV_VIEWPORT_THRESHOLD = 0.9; // 可视区域阈值(90%)
    const NAV_NEAR_TOP_THRESHOLD = 24; // 接近顶部阈值(像素)
    const NAV_CLICK_LOCK_DURATION = 1200; // 点击锁定持续时间(毫秒)
    const NAV_UPDATE_TEXT_DELAY = 500; // 导航链接文本更新延迟(毫秒)
    const NAV_RETRY_MAX_COUNT = 10; // 导航链接跳转最大重试次数
    const NAV_RETRY_INTERVAL = 100; // 导航链接跳转重试间隔(毫秒)
    // 副目录标题级别配置(可配置为 h1~h4、h2~h4 或 h2~h3)
    const SUB_NAV_HEADING_LEVELS = [4, 3, 2, 1]; // 支持 h4, h3, h2, h1(顺序从低到高)
    const SUB_NAV_HEADING_SELECTOR = SUB_NAV_HEADING_LEVELS.map(level => `h${level}`).join(', '); // 生成选择器字符串,如 "h1, h2, h3, h4"
    const SUB_NAV_HEADING_TAGS = SUB_NAV_HEADING_LEVELS.map(level => `H${level}`); // 生成标签数组,如 ["H1", "H2", "H3", "H4"]
    const SUB_POS_RIGHT = "25px";

    // 获取导航样式(动态生成,支持运行时修改变量)
    const getNavStyles = () => {
        const navTop = getNavTop();
        const navMaxWidth = getNavMaxWidth();
        const subNavTop = getSubNavTop();
        const subNavMaxWidth = getSubNavMaxWidth();
        
        return {
            // 主目录样式
            navBar: `position:fixed;visibility:hidden;top:${navTop};right:15px;max-width:${navMaxWidth};min-width:150px;background:rgba(255,255,255,0.95);border:1px solid #ccc;border-radius:6px;padding:0 5px;z-index:99999;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;box-shadow:0 2px 8px rgba(0,0,0,0.15);max-height:90vh;overflow-y:auto;box-sizing:border-box;`,
            miniButton: `position:fixed;top:${navTop};right:15px;color:${NAV_ITEM_COLOR};border:1px solid #ddd;border-radius:8px;padding:2px 8px;font-size:14px;font-weight: bold;cursor:pointer;z-index:99999;visibility:hidden;box-shadow:0 2px 6px rgba(0,0,0,0.15);user-select:none;`,
            title: `display:flex;align-items:center;justify-content:flex-start;gap:6px;font-weight:bold;color:#333;padding:4px 5px;border-bottom:1px solid #eaeaea;margin-bottom:4px;position:sticky;top:0;background:rgba(255,255,255,0.95);z-index:10;`,
            hideBtn: `font-weight:normal;color:#333;font-size:12px;padding:2px 6px;border:1px solid #aaa;border-radius:10px;cursor:pointer;user-select:none;`,
            countText: `font-weight:normal;color:#333;font-size:14px;margin-left:6px;user-select:none;`,
            linkContainer: `display:flex;align-items:center;gap:4px;width:100%;`,
            link: `width:100%;padding:4px 2px;cursor:pointer;color:#333;font-size:14px;line-height:1.5;white-space:normal;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-word;max-height:calc(1.9em * 2);box-sizing:border-box;`,
            waveIcon: `font-size:12px;cursor:pointer;color:#333;padding:0;border-radius:3px;user-select:none;flex-shrink:0;transition:background-color 0.2s;`,
            waveIconHover: `background-color:#f0f0f0;color:#0066cc;`,
            waveIconNormal: `background-color:transparent;color:#333;`,

            // 副目录样式
            subNavBar: `position:fixed;left:${SUB_NAV_LEFT};top:${subNavTop};max-width:${subNavMaxWidth};min-width:220px;max-height:94vh;background:rgba(255,255,255,1);border:1px solid #ccc;border-radius:6px;padding:8px;z-index:2147483646;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;box-shadow:0 2px 8px rgba(0,0,0,0.15);overflow-y:auto;box-sizing:border-box;display:none;`,
            subNavTitle: `font-weight:bold;color:#111;padding:4px 0;border-bottom:1px solid #eaeaea;margin-bottom:6px;font-size:14px;`,
            subNavCloseBtn: `position:absolute;top:0;right:5px;font-size:16px;cursor:pointer;color:#333;width:20px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:background-color 0.2s;`,

            subNavItem: `padding:4px 2px;cursor:pointer;color:#333;font-size:13px;line-height:1.6;border-radius:3px;margin:2px 0;transition:background-color 0.2s;word-break:break-word;`,
            subNavItemH1: `padding-left:0px;font-weight:700;`,
            subNavItemH2: `padding-left:2px;font-weight:600;`,
            subNavItemH3: `padding-left:8px;font-weight:500;`,
            subNavItemH4: `padding-left:14px;font-weight:400;`,

            levelBtnGroup: `display:flex;gap:4px;align-items:center;`,
            levelBtn: `padding:2px 4px;font-size:11px;cursor:pointer;border:1px solid #ddd;border-radius:4px;background:#fff;color:#333;transition:all 0.2s;user-select:none;`,
            levelBtnActive: `background:#0066cc;color:#fff;border-color:#0066cc;`,
            levelBtnHover: `background-color:#f0f0f0;border-color:#ccc;`,
            levelBtnLeave: `background-color:#fff;border-color:#ddd;color:#333;`,

            subNavPositionBtn: `position:absolute;top:0;right:${SUB_POS_RIGHT};font-size:12px;cursor:pointer;color:#111;width:36px;height:20px;display:flex;align-items:center;justify-content:center;border-radius:3px;transition:background-color 0.2s;`,
            subNavPositionBtnHover: `background-color:#f0f0f0;`,
            subNavPositionBtnNormal: `background-color:transparent;`,
            subNavPositionInput: `position:absolute;top:0;right:${SUB_POS_RIGHT};width:45px;height:20px;padding:0 4px;font-size:12px;border:1px solid #ccc;border-radius:3px;outline:none;`
        };
    };
    
    // 样式常量(向后兼容,使用函数生成)
    const NAV_STYLES = getNavStyles();

    // 创建导航元素
    const navBar = createTag('div', "", NAV_STYLES.navBar);
    navBar.id = "tool-nav-bar";

    const navMiniButton = createTag('div', '目录', NAV_STYLES.miniButton);
    
    // 更新导航栏样式的函数(当变量改变时调用)
    const updateNavStyles = () => {
        const styles = getNavStyles();
        if (navBar) {
            navBar.style.top = getNavTop();
            navBar.style.maxWidth = getNavMaxWidth();
        }
        if (navMiniButton) {
            navMiniButton.style.top = getNavTop();
        }
        if (subNavBar) {
            subNavBar.style.top = getSubNavTop();
            subNavBar.style.maxWidth = getSubNavMaxWidth();
        }
        // 刷新导航栏显示状态以应用新的top值
        if (typeof refreshNavBarVisibility === 'function') {
            refreshNavBarVisibility();
        }
        if (typeof updateSubNavTop === 'function') {
            updateSubNavTop();
        }
    };

    // 获取副目录left位置的key
    const getSubNavLeftKey = () => {
        return `${T}subNavLeft`;
    };

    // 获取副目录的left值(优先从localStorage读取)
    const getSubNavLeft = () => {
        const key = getSubNavLeftKey();
        const savedLeft = getS(key);
        return savedLeft || SUB_NAV_LEFT;
    };

    // 设置副目录的left值到localStorage
    const setSubNavLeft = (left) => {
        const key = getSubNavLeftKey();
        setS(key, left);
    };

    // 创建副目录栏元素
    const subNavLeft = getSubNavLeft();
    const subNavBar = createTag('div', "", NAV_STYLES.subNavBar.replace(`left:${SUB_NAV_LEFT}`, `left:${subNavLeft}`));
    subNavBar.id = "tool-sub-nav-bar";

    // 状态变量
    let navQuestions, navLinks = [], navIO, elToLink = new Map();
    let clickedTarget = null, clickLockUntil = 0, scrollDebounceTimer;
    let currentSubNavQuestionIndex = -1; // 当前显示的副目录对应的主目录索引
    let preservedNavTextsUrl = null; // 保存保留文本时的 URL
    let currentNavBarUrl = null; // 当前导航栏对应的 URL,用于检测 URL 变化
    let currentSubNavLevel = 4; // 当前副目录显示的层级(默认 h4)
    let currentSubNavHeadings = []; // 当前副目录的所有标题数据(未过滤)
    let subNavPollInterval = null; // 副目录轮询定时器
    let isSubNavLevelManuallySet = false; // 用户是否手动选择了层级
    let h1Count = 0; // h1标题的数量
    let navCountText = null; // 主目录条数显示元素

    // 从localStorage读取最小化状态,默认为false
    let navMinimized = getS(T + 'navMinimized') === 'true';

    // 设置导航链接的样式(高亮或普通状态)
    const setLinkStyle = (linkContainer, isActive) => {
        if(!linkContainer) return;
        // 如果是 linkContainer,从中查找 link 元素
        const link = linkContainer.classList?.contains('tool-nav-link-container')
            ? linkContainer.querySelector('.tool-nav-link')
            : linkContainer;
        if(!link) return;
        const color = isActive ? SUB_NAV_ITEM_HOVER_COLOR : NAV_ITEM_COLOR;
        link.style.cssText = NAV_STYLES.link + `background-color:;color:${color};`;
    };

    // 清除所有导航链接的高亮状态
    const clearAllHighlights = () => navLinks.forEach(link => setLinkStyle(link, false));

    // 统一的元素可见性判断函数
    const isElementVisible = (rect, viewportThreshold = NAV_VIEWPORT_THRESHOLD) => {
        if (!rect) return false;
        const viewportHeight = window.innerHeight || document.documentElement.clientHeight;
        return rect.bottom > 0 && rect.top < viewportHeight * viewportThreshold;
    };

    // 判断元素是否接近顶部
    const isElementNearTop = (rect, threshold = NAV_NEAR_TOP_THRESHOLD) => {
        return rect ? Math.abs(rect.top) < threshold : false;
    };

    // 获取视口高度
    const getViewportHeight = () => window.innerHeight || document.documentElement.clientHeight;

    // 计算元素在视口中的位置百分比
    const getElementPositionPercent = (rect) => {
        const viewportHeight = getViewportHeight();
        return rect.top / viewportHeight;
    };

    // 获取所有可见的元素
    const getVisibleElements = (elements, viewportThreshold = NAV_VIEWPORT_THRESHOLD) => {
        return elements.filter(el => {
            const rect = el?.getBoundingClientRect();
            return isElementVisible(rect, viewportThreshold);
        });
    };

    // 更新主目录条数显示
    const updateNavCount = () => {
        if (!navCountText) return;

        const linkCount = navBar.querySelectorAll('.tool-nav-link').length;

        // 如果条数超过阈值,显示"共xx条"
        if (linkCount > NAV_COUNT_THRESHOLD) {
            navCountText.textContent = `共${linkCount}条`;
            navCountText.style.display = '';
        } else {
            navCountText.style.display = 'none';
        }
    };

    // 刷新导航栏的显示状态(显示/隐藏/最小化)
    const refreshNavBarVisibility = () => {
        const root = document.body || document.documentElement;
        if(!root.contains(navMiniButton)) root.appendChild(navMiniButton);

        const linkCount = navBar.querySelectorAll('.tool-nav-link').length;
        if(linkCount === 0) {
            navBar.style.visibility = navMiniButton.style.visibility = "hidden";
            updateNavCount(); // 更新条数显示
            return;
        }

        // 如果条目数量超过指定阈值,则将navBar的top抬高
        let navTop = linkCount > NAV_TOP_THRESHOLD ? getNavTopOverflow() : getNavTop();
        navBar.style.top = navTop;
        navMiniButton.style.top = navTop;

        // 更新条数显示
        updateNavCount();

        if(navMinimized) {
            navBar.style.visibility = "hidden";
            navMiniButton.style.visibility = "visible";
        } else {
            navBar.style.visibility = "visible";
            navMiniButton.style.visibility = "hidden";
            if(!root.contains(navBar)) root.appendChild(navBar);
        }
    };

    // 设置导航栏的最小化状态
    const setNavMinimized = (min) => {
        navMinimized = min === true;
        setS(T + 'navMinimized', navMinimized.toString());
        refreshNavBarVisibility();
    };

    // 计算当前应该高亮的导航项索引
    const computeActiveIndex = () => {
        if(!navQuestions?.length) return -1;
        let candidateIndex = -1, smallestPositiveTop = Infinity, lastNegativeIndex = -1;

        navQuestions.forEach((el, i) => {
            if(!el?.getBoundingClientRect) return;
            const rect = el.getBoundingClientRect();
            if(rect.top >= 0) {
                if(rect.top < smallestPositiveTop) {
                    smallestPositiveTop = rect.top;
                    candidateIndex = i;
                }
            } else {
                lastNegativeIndex = i;
            }
        });
        return candidateIndex !== -1 ? candidateIndex : lastNegativeIndex;
    };

    // 高亮当前活跃的导航项
    const highlightActiveNav = () => {
        const idx = computeActiveIndex();
        navLinks.forEach((link, i) => setLinkStyle(link, i === idx));
        // 自动显示当前高亮项对应的副目录
        if (idx >= 0 && typeof autoShowSubNav === 'function') {
            autoShowSubNav(idx);
        }
    };

    // 检查并切换高亮状态(根据滚动位置智能高亮)
    const checkAndSwitchHighlight = () => {
        if(!navQuestions?.length) return;

        // 找到所有可见的目录项
        const visibleElements = getVisibleElements(navQuestions, 1.0); // 使用100%视口高度进行初步筛选
        if(visibleElements.length === 0) {
            return;
        }

        const firstVisibleEl = visibleElements[0];
        const rect = firstVisibleEl.getBoundingClientRect();
        const positionPercent = getElementPositionPercent(rect);

        let targetIndex = -1;
        if(positionPercent >= 0 && positionPercent <= NAV_HIGHLIGHT_THRESHOLD) {
            // 0~30%:高亮当前项
            targetIndex = navQuestions.indexOf(firstVisibleEl);
        } else if(positionPercent > NAV_HIGHLIGHT_THRESHOLD && positionPercent <= 1.0) {
            // 30%~100%:高亮前一项
            const currentIndex = navQuestions.indexOf(firstVisibleEl);
            targetIndex = currentIndex > 0 ? currentIndex - 1 : currentIndex;
        }

        if(targetIndex >= 0) {
            clearAllHighlights();
            setLinkStyle(navLinks[targetIndex], true);
            // 自动显示当前高亮项对应的副目录
            if (typeof autoShowSubNav === 'function') {
                autoShowSubNav(targetIndex);
            }
        }
    };

    // 滚动事件处理函数(优化的节流处理)
    let lastScrollTime = 0;
    const onScrollRefreshActive = () => {
        const now = Date.now();
        if(now - lastScrollTime < 32) return; // 约30fps的节流,减少性能消耗
        lastScrollTime = now;

        // 清除之前的防抖计时器
        if(scrollDebounceTimer) {
            clearTimeout(scrollDebounceTimer);
            scrollDebounceTimer = null;
        }

        // 设置防抖,避免重复执行
        scrollDebounceTimer = setTimeout(() => {
            scrollDebounceTimer = null;
            highlightActiveNav();
            checkAndSwitchHighlight();
        }, 30); // 减少延迟到30ms,提高响应性
    };

    window.addEventListener('scroll', onScrollRefreshActive, { passive: true });

    // 查找问题对应的回答内容区域
    const findAnswerContent = (questionEl) => {
        if (!questionEl) return null;

        // 获取所有问题元素,用于确定回答区域的边界
        const allQuestions = getQuestionList();
        if (!allQuestions || allQuestions.length === 0) return null;

        const questionIndex = Array.from(allQuestions).indexOf(questionEl);

        // 兼容:问题节点已不在最新列表中时,仍然从其后续兄弟中兜底查找回答区域
        if (questionIndex < 0) {
            let nextSibling = questionEl.nextElementSibling;
            let checkedCount = 0;
            while (nextSibling && checkedCount < FIND_ANSWER_MIDDLE_SIBLING_LIMIT) {
                const headings = nextSibling.querySelectorAll(SUB_NAV_HEADING_SELECTOR);
                if (headings.length > 0) {
                    return nextSibling;
                }
                if (nextSibling.tagName && SUB_NAV_HEADING_TAGS.includes(nextSibling.tagName)) {
                    return nextSibling.parentElement;
                }
                nextSibling = nextSibling.nextElementSibling;
                checkedCount++;
            }
            return null;
        }

        // 先按父级兄弟链查找回答区域
        if (questionIndex >= allQuestions.length - 1) {
            // 如果是最后一个问题,查找它之后的所有内容
            const answerFromParents = searchInParentSiblings(questionEl, FIND_ANSWER_LAST_SIBLING_LIMIT, null);
            if (answerFromParents) return answerFromParents;
        } else {
            // 如果不是最后一个问题,查找当前问题和下一个问题之间的内容
            const nextQuestion = allQuestions[questionIndex + 1];
            if (!nextQuestion) return null;

            const stopCondition = (sibling) => {
                return sibling.contains(nextQuestion) || sibling === nextQuestion;
            };
            const answerFromParents = searchInParentSiblings(questionEl, FIND_ANSWER_MIDDLE_SIBLING_LIMIT, stopCondition);
            if (answerFromParents) return answerFromParents;
        }

        // 父级兄弟未找到时,最后再从当前问题节点的后续兄弟元素中兜底查找
        let nextSibling = questionEl.nextElementSibling;
        let checkedCount = 0;
        while (nextSibling && checkedCount < FIND_ANSWER_MIDDLE_SIBLING_LIMIT) {
            const headings = nextSibling.querySelectorAll(SUB_NAV_HEADING_SELECTOR);
            if (headings.length > 0) {
                return nextSibling;
            }
            if (nextSibling.tagName && SUB_NAV_HEADING_TAGS.includes(nextSibling.tagName)) {
                return nextSibling.parentElement;
            }
            nextSibling = nextSibling.nextElementSibling;
            checkedCount++;
        }

        return null;
    };
    
    // 向上查找父元素的兄弟元素,查找回答区域
    const searchInParentSiblings = (startEl, siblingLimit, stopCondition) => {
        let current = startEl;
        let depth = 0;
        while (current && depth < FIND_ANSWER_PARENT_DEPTH_LIMIT) {
            const parent = current.parentElement;
            if (!parent) break;

            let sibling = parent.nextElementSibling;
            let checkedCount = 0;
            while (sibling && checkedCount < siblingLimit) {
                // 检查停止条件(如遇到下一个问题)
                if (stopCondition && stopCondition(sibling)) {
                    break;
                }
                // 查找包含标题的兄弟元素
                const headings = sibling.querySelectorAll(SUB_NAV_HEADING_SELECTOR);
                if (headings.length > 0) {
                    return sibling;
                }
                if (sibling.tagName && SUB_NAV_HEADING_TAGS.includes(sibling.tagName)) {
                    return sibling.parentElement;
                }
                sibling = sibling.nextElementSibling;
                checkedCount++;
            }
            current = parent;
            depth++;
        }
        return null;
    };

    // 规范化标题文本(移除 emoji、空格、冒号等,但保留数字编号)
    const normalizeHeadingText = (text) => {
        if (!text) return '';
        let normalized = text.trim();
        // 先移除开头的连续空格
        normalized = normalized.replace(/^\s+/, '');

        // 关键优化:先检查第一个字符是否是数字,避免某些环境将数字误识别为 emoji
        const firstChar = normalized.charAt(0);
        if (/[0-9]/.test(firstChar)) {
            // 第一个字符是数字,不做任何处理,保留完整的数字编号
        } else {
            // 检查是否是 emoji 开头,且后面紧跟数字(可能含空格)
            if (/^\p{Emoji}\s*[0-9]/u.test(normalized)) {
                // emoji 后面是数字,只移除 emoji 和空格,保留数字
                normalized = normalized.substring(2);
            } else if (/^\p{Emoji}/u.test(normalized)) {
                // emoji 后面不是数字,安全移除 emoji
                // 再次确认第一个字符不是数字(双重检查,防止误识别)
                if (!/[0-9]/.test(normalized.charAt(0))) {
                    normalized = normalized.replace(/^\p{Emoji}+\s*/u, '');
                }
                // 如果第一个字符是数字,说明被误识别为 emoji,不做处理
            }
        }
        // 移除末尾的冒号(中英文)
        return normalized.replace(/[::]+$/, '');
    };


    // 在回答内容区域中查找所有配置的标题级别
    const findHeadingsInContent = (contentEl) => {
        if (!contentEl) return [];

        const headingList = [];

        // 1. 查找现有的 h2~h4 标签标题
        let domOrder = 0; // 初始化DOM顺序计数器(HTML标签标题用)
        const headings = contentEl.querySelectorAll(SUB_NAV_HEADING_SELECTOR);
        Array.from(headings).forEach(h => {
            // 确保标题是可见的
            const rect = h.getBoundingClientRect();
            if (rect.width === 0 || rect.height === 0) return;
            // 确保标题级别在配置的范围内
            const level = parseInt(h.tagName.substring(1));
            if (!SUB_NAV_HEADING_LEVELS.includes(level)) return;

            // 规范化标题文本
            const text = normalizeHeadingText(h.textContent);

            headingList.push({
                element: h,
                tagName: h.tagName,
                text: text,
                level: level,
                domOrder: domOrder++ // 为HTML标签标题也添加domOrder,确保排序正确
            });
        });

        // 2. 去重并排序(按DOM顺序,保持文档中的原始顺序)
        const uniqueHeadings = [];
        const seenKeys = new Set();

        // 按DOM顺序排序(TreeWalker遍历的顺序)
        headingList.sort((a, b) => a.domOrder - b.domOrder);

        headingList.forEach(heading => {
            // 使用文本、级别和domOrder作为唯一标识,避免重复
            // domOrder是稳定的,不会随页面滚动而变化
            const key = `${heading.text}_${heading.level}_${heading.domOrder}`;

            if (!seenKeys.has(key)) {
                seenKeys.add(key);
                uniqueHeadings.push({
                    element: heading.element,
                    tagName: heading.tagName,
                    text: heading.text,
                    level: heading.level,
                    domOrder: heading.domOrder
                });
            }
        });

        return uniqueHeadings;
    };

    // 渲染副目录项(根据当前选择的层级过滤)
    const renderSubNavItems = () => {
        // 获取标题容器后的所有元素
        const titleContainer = subNavBar.querySelector('.sub-nav-title-container');
        if (!titleContainer) return;

        // 移除所有标题项(保留标题容器)
        const items = subNavBar.querySelectorAll('.sub-nav-item');
        items.forEach(item => item.remove());

        // 根据当前选择的层级过滤标题
        let filteredHeadings = currentSubNavHeadings.filter(h => h.level <= currentSubNavLevel);
        
        // 如果h1只有1个,则过滤掉h1标题项
        if (h1Count === 1) {
            filteredHeadings = filteredHeadings.filter(h => h.level !== 1);
        }

        // 创建标题级别样式映射
        const headingStyleMap = {
            1: NAV_STYLES.subNavItemH1,
            2: NAV_STYLES.subNavItemH2,
            3: NAV_STYLES.subNavItemH3,
            4: NAV_STYLES.subNavItemH4
        };

        // 添加过滤后的标题
        filteredHeadings.forEach((heading, index) => {
            const item = document.createElement('div');
            item.className = 'sub-nav-item';
            let itemStyle = NAV_STYLES.subNavItem;

            // 根据标题级别设置不同的缩进(如果配置中包含该级别)
            if (SUB_NAV_HEADING_LEVELS.includes(heading.level) && headingStyleMap[heading.level]) {
                itemStyle += headingStyleMap[heading.level];
            } else {
                // 如果级别不在预定义样式中,根据级别动态计算缩进(每级8px)
                const paddingLeft = heading.level * 8;
                itemStyle += `padding-left:${paddingLeft}px;`;
            }

            item.style.cssText = itemStyle;
            item.textContent = heading.text;
            item.title = heading.text;

            // 鼠标悬停效果
            item.addEventListener('mouseenter', () => {
                item.style.backgroundColor = SUB_NAV_ITEM_HOVER_BG;
                item.style.color = SUB_NAV_ITEM_HOVER_COLOR;
            });
            item.addEventListener('mouseleave', () => {
                item.style.backgroundColor = SUB_NAV_ITEM_NORMAL_BG;
                item.style.color = SUB_NAV_ITEM_NORMAL_COLOR;
            });

            // 点击跳转
            item.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();

                // 先尝试使用保存的元素引用
                let targetElement = heading.element;

                // 如果元素引用失效,重新查找对应的标题元素
                if (!targetElement || !document.body.contains(targetElement)) {
                    // 获取当前问题索引
                    const questionIndex = currentSubNavQuestionIndex;
                    if (questionIndex >= 0 && navQuestions && questionIndex < navQuestions.length) {
                        const targetEl = navQuestions[questionIndex];
                        if (targetEl && document.body.contains(targetEl)) {
                            // 查找回答内容区域
                            const answerContent = findAnswerContent(targetEl);
                            if (answerContent) {
                                // 重新查找所有标题
                                const headings = findHeadingsInContent(answerContent);
                                // 查找匹配的标题(通过文本和级别)
                                const matchedHeading = headings.find(h =>
                                    h.text === heading.text && h.level === heading.level
                                );
                                if (matchedHeading && matchedHeading.element) {
                                    targetElement = matchedHeading.element;
                                }
                            }
                        }
                    }
                }

                if (!targetElement || !document.body.contains(targetElement)) {
                    console.warn('标题元素不存在,无法跳转');
                    return;
                }
                targetElement.scrollIntoView({ block: 'start' });
            });

            subNavBar.appendChild(item);
        });
    };

    // 根据副目录条目数量动态设置top位置
    const updateSubNavTop = () => {
        const subNavItemCount = subNavBar.querySelectorAll('.sub-nav-item').length;
        subNavBar.style.top = subNavItemCount > SUB_NAV_TOP_THRESHOLD ? "7%" : getSubNavTop();
    };

    // 更新副目录状态
    const updateSubNavState = (questionIndex, headings) => {
        // 保存标题数据和状态
        currentSubNavHeadings = headings;

        // 统计h1标题的数量
        h1Count = headings.filter(h => h.level === 1).length;

        // 获取实际存在的标题层级(从高到低:h4, h3, h2, h1)
        let existingLevels = [...new Set(headings.map(h => h.level))].sort((a, b) => b - a);

        // 如果h1只有1个,则从层级列表中过滤掉h1
        if (h1Count === 1) {
            existingLevels = existingLevels.filter(level => level !== 1);
        }

        // 检查是否是同一个问题且用户已手动选择层级
        const isSameQuestion = questionIndex === currentSubNavQuestionIndex;
        if (isSameQuestion && isSubNavLevelManuallySet) {
            // 如果是同一个问题且用户已手动选择层级,保留用户的选择,不重新计算
            currentSubNavQuestionIndex = questionIndex;
        } else {
            // 如果是新问题或用户未手动选择,重新计算层级
            currentSubNavQuestionIndex = questionIndex;
            isSubNavLevelManuallySet = false; // 重置手动选择标志

            // 设置默认层级
            if (existingLevels.length > 0) {
                const highestLevel = existingLevels[0]; // 最高层级(数字最大,如h4=4)
                // 如果总条数超过阈值,则默认显示到上一层级
                if (headings.length > SUB_NAV_PREV_LEVEL_THRESHOLD) {
                    // 查找上一层级(比最高层级小1的层级)
                    const prevLevel = highestLevel - 1;
                    // 如果存在上一层级,则显示到上一层级;否则显示到最高层级
                    currentSubNavLevel = existingLevels.includes(prevLevel) ? prevLevel : highestLevel;
                } else {
                    // 否则显示到实际存在的最高层级(h4 > h3 > h2)
                    currentSubNavLevel = highestLevel;
                }
            }
            // 如果h1只有1个且当前层级是h1,则降级到h2
            if (h1Count === 1 && currentSubNavLevel === 1) {
                currentSubNavLevel = existingLevels.length > 0 ? existingLevels[0] : 2;
            }
        }

        return existingLevels;
    };

    // 创建副目录位置按钮
    const createSubNavPositionBtn = (titleContainer) => {
        const positionBtn = createTag('div', "", NAV_STYLES.subNavPositionBtn);
        positionBtn.textContent = '位置';
        positionBtn.title = '设置副目录位置';
        positionBtn.addEventListener('mouseenter', () => {
            positionBtn.style.backgroundColor = '#f0f0f0';
        });
        positionBtn.addEventListener('mouseleave', () => {
            positionBtn.style.backgroundColor = 'transparent';
        });
        positionBtn.addEventListener('click', (e) => {
            e.stopPropagation();

            // 创建输入框
            const input = document.createElement('input');
            input.type = 'text';
            input.value = getSubNavLeft();
            input.style.cssText = NAV_STYLES.subNavPositionInput;

            // 替换按钮为输入框
            positionBtn.style.display = 'none';
            titleContainer.appendChild(input);
            input.focus();
            input.select();

            // blur事件:保存值并更新位置
            input.addEventListener('blur', () => {
                const newLeft = input.value.trim();
                const formatRegex = /^\d+(\.\d+)?px$/;
                if (newLeft && formatRegex.test(newLeft)) {
                    // 格式正确,保存到localStorage,更新副目录的left位置
                    setSubNavLeft(newLeft);
                    subNavBar.style.left = newLeft;
                } else if (newLeft) {
                    input.value = getSubNavLeft();
                    alert('位置格式错误,请输入"数字+px"格式,例如:270px');
                }
                // 恢复按钮
                input.remove();
                positionBtn.style.display = 'flex';
            });

            // Enter键也触发blur
            input.addEventListener('keydown', (e) => {
                if (e.key === 'Enter') {
                    input.blur();
                }
            });
        });
        return positionBtn;
    };

    // 创建副目录关闭按钮
    const createSubNavCloseBtn = () => {
        const closeBtn = createTag('div', "", NAV_STYLES.subNavCloseBtn);
        closeBtn.textContent = '×';
        closeBtn.title = '关闭副目录';
        closeBtn.addEventListener('mouseenter', () => {
            closeBtn.style.backgroundColor = '#f0f0f0';
        });
        closeBtn.addEventListener('mouseleave', () => {
            closeBtn.style.backgroundColor = 'transparent';
        });
        closeBtn.addEventListener('click', (e) => {
            e.stopPropagation();

            // 检查是否是首次点击(用GM存储标记状态)
            const firstCloseKey = `${T}subNavFirstCloseShown`;
            const hasShownFirstClose = GM_getValue(firstCloseKey, false);
            if (!hasShownFirstClose) {
                alert("这家大模型将不再显示副目录;\n若需恢复,点击主目录每条提问前的小图标即可");
                GM_setValue(firstCloseKey, true);
            }

            // 记录关闭状态
            setSubNavClosed(true);
            hideSubNavBar();
        });
        return closeBtn;
    };

    // 创建副目录层级按钮组
    const createSubNavLevelBtnGroup = (existingLevels) => {
        const levelBtnGroup = createTag('div', "", NAV_STYLES.levelBtnGroup);

        // 创建层级按钮(只显示实际存在的层级,按钮显示顺序为 h2, h3, h4,从高到低)
        existingLevels.slice().reverse().forEach(level => {
            const btn = document.createElement('div');
            btn.textContent = `h${level}`;
            btn.dataset.level = level;

            // 设置按钮样式
            let btnStyle = NAV_STYLES.levelBtn;
            if (level === currentSubNavLevel) {
                btnStyle += NAV_STYLES.levelBtnActive;
            }
            btn.style.cssText = btnStyle;

            // 鼠标悬停效果
            btn.addEventListener('mouseenter', () => {
                if (level !== currentSubNavLevel) {
                    btn.style.cssText = btnStyle + NAV_STYLES.levelBtnHover;
                }
            });
            btn.addEventListener('mouseleave', () => {
                if (level !== currentSubNavLevel) {
                    btn.style.cssText = btnStyle + NAV_STYLES.levelBtnLeave;
                }
            });

            // 点击切换层级
            btn.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();

                // 更新当前层级
                currentSubNavLevel = level;
                // 标记用户已手动选择层级
                isSubNavLevelManuallySet = true;

                // 更新所有按钮的样式
                levelBtnGroup.querySelectorAll('[data-level]').forEach(b => {
                    if (parseInt(b.dataset.level) === level) {
                        b.style.cssText = NAV_STYLES.levelBtn + NAV_STYLES.levelBtnActive;
                    } else {
                        b.style.cssText = NAV_STYLES.levelBtn;
                    }
                });

                // 重新渲染标题项
                renderSubNavItems();

                // 根据副目录条目数量动态设置top位置
                updateSubNavTop();
            });

            levelBtnGroup.appendChild(btn);
        });

        return levelBtnGroup;
    };

    // 渲染副目录UI
    const renderSubNavUI = (questionIndex, existingLevels) => {
        // 清空副目录栏
        subNavBar.replaceChildren();

        // 创建标题容器(相对定位,用于放置关闭按钮)
        const titleContainer = createTag('div', "", 'position:relative;padding-right:24px;padding-bottom:6px;border-bottom:1px solid #eaeaea;margin-bottom:6px;');
        titleContainer.className = 'sub-nav-title-container';
        // 创建标题行容器、标题
        const titleRow = createTag('div', "", 'display:flex;align-items:center;justify-content:space-between;gap:8px;');
        const titleLeft = createTag('div', "", 'display:flex;align-items:center;gap:8px;flex:1;');

        // 创建标题文本
        const titleText = createTag('span', "", 'font-weight:bold;color:#333;font-size:14px;');
        titleText.textContent = `副目录 ${questionIndex + 1}`;

        // 创建层级按钮组
        const levelBtnGroup = createSubNavLevelBtnGroup(existingLevels);

        // 组装左侧(标题和按钮组)
        appendSeveral(titleLeft, titleText, levelBtnGroup);
        titleRow.appendChild(titleLeft);
        titleContainer.appendChild(titleRow);

        // 创建位置按钮和关闭按钮
        const positionBtn = createSubNavPositionBtn(titleContainer);
        const closeBtn = createSubNavCloseBtn();
        appendSeveral(titleContainer, positionBtn, closeBtn);

        // 添加到副目录栏
        subNavBar.appendChild(titleContainer);

        // 渲染标题项
        renderSubNavItems();

        // 根据副目录条目数量动态设置top位置
        updateSubNavTop();

        // 确保使用最新的left值(从localStorage读取)
        subNavBar.style.left = getSubNavLeft();

        // 显示副目录栏
        subNavBar.style.display = 'block';

        // 启动轮询更新,每10秒检查一次是否需要更新副目录
        startSubNavObserver(questionIndex);
    };

    // 显示副目录栏
    const showSubNavBar = (questionIndex, headings, isPolling = false) => {
        // 如果已关闭,则不加载
        if (isSubNavClosed()) {
            return;
        }
        if (!headings || headings.length === 0) {
            console.log('未找到标题');
            return;
        }
        // 检测标题总条数,超过指定数量才显示副目录
        if (headings.length <= SUB_NAV_MIN_ITEMS) {
            return;
        }

        // 轮询时的优化:如果当前已有标题且新标题数量少于或等于现有标题数量,可能是DOM还没完全加载
        // 只有在标题数量增加时才更新(保留更完整的数据)
        if (isPolling && currentSubNavHeadings.length > 0) {
            // 如果新标题数量少于现有标题,说明可能丢失了某些标题,不更新
            if (headings.length < currentSubNavHeadings.length) {
                console.log(`轮询时标题数量减少(${headings.length} < ${currentSubNavHeadings.length}),保留现有标题`);
                return;
            }
            // 如果标题数量相同,检查是否有实际变化(避免不必要的重建)
            if (headings.length === currentSubNavHeadings.length) {
                // 检查标题列表是否完全相同(通过比较标题文本、级别和domOrder)
                const existingKeys = new Set(currentSubNavHeadings.map(h =>
                    `${h.text}_${h.level}_${h.domOrder || 0}`
                ));
                const newKeys = new Set(headings.map(h =>
                    `${h.text}_${h.level}_${h.domOrder || 0}`
                ));
                // 如果标题完全相同,不更新
                if (existingKeys.size === newKeys.size &&
                    [...existingKeys].every(k => newKeys.has(k))) {
                    return;
                }
            }
        }

        // 更新状态
        const existingLevels = updateSubNavState(questionIndex, headings);

        // 渲染UI
        renderSubNavUI(questionIndex, existingLevels);
    };

    const SUB_NAV_CLOSED_KEY = `${T}subNavClosed`;
    // 检查副目录是否已关闭
    const isSubNavClosed = () => {
        return getS(SUB_NAV_CLOSED_KEY) === 'true';
    };

    // 设置副目录关闭状态
    const setSubNavClosed = (closed) => {
        if (closed) {
            setS(SUB_NAV_CLOSED_KEY, 'true');
        } else {
            localStorage.removeItem(SUB_NAV_CLOSED_KEY);
        }
    };

    // 启动副目录轮询更新(复用 autoShowSubNav 实现)
    const startSubNavObserver = (questionIndex) => {
        // 先停止之前的轮询
        stopSubNavObserver();

        if (questionIndex < 0 || !navQuestions || questionIndex >= navQuestions.length) {
            return;
        }

        // 保存问题索引,供轮询函数使用
        const pollQuestionIndex = questionIndex;

        // 轮询间隔
        const POLL_INTERVAL = 8000;

        // 启动轮询定时器,复用 autoShowSubNav 实现更新
        subNavPollInterval = setInterval(() => {
            // 检查副目录是否还在显示或已关闭
            if (subNavBar.style.display !== 'block' || currentSubNavQuestionIndex !== pollQuestionIndex || isSubNavClosed()) {
                stopSubNavObserver();
                return;
            }

            // 复用 autoShowSubNav 实现更新
            autoShowSubNav(pollQuestionIndex);
        }, POLL_INTERVAL);
    };

    // 停止副目录轮询更新
    const stopSubNavObserver = () => {
        if (subNavPollInterval) {
            clearInterval(subNavPollInterval);
            subNavPollInterval = null;
        }
    };

    // 隐藏副目录栏
    const hideSubNavBar = () => {
        subNavBar.style.display = 'none';
        currentSubNavQuestionIndex = -1;
        // 停止内容变化监听
        stopSubNavObserver();
    };

    // 根据问题索引自动显示对应的副目录
    const autoShowSubNav = (questionIndex) => {
        if (questionIndex < 0 || !navQuestions || questionIndex >= navQuestions.length) {
            return;
        }

        // 如果已关闭,则不加载
        if (isSubNavClosed()) {
            return;
        }

        const targetEl = navQuestions[questionIndex];
        if (!targetEl || !document.body.contains(targetEl)) {
            return;
        }

        // 查找回答内容区域
        const answerContent = findAnswerContent(targetEl);
        if (!answerContent) {
            return;
        }

        // 查找标题
        const headings = findHeadingsInContent(answerContent);
        if (headings.length === 0) {
            return;
        }

        // 显示副目录栏
        // 检查是否是轮询调用(通过检查副目录栏是否已显示来判断)
        const isPolling = subNavBar.style.display === 'block' &&
                         currentSubNavQuestionIndex === questionIndex;
        showSubNavBar(questionIndex, headings, isPolling);
    };

    // 处理导航链接点击事件
    const handleNavLinkClick = (el, i, linkContainer) => {
        return (e) => {
            e.preventDefault();
            // 验证元素是否存在,如果不存在则尝试重新获取
            let targetEl = el;
            const questions = getQuestionList();

            if (!targetEl || !document.body.contains(targetEl)) {
                // 元素可能已被移除或重新渲染,尝试重新获取
                if (questions && questions.length > i) {
                    targetEl = questions[i];
                }
            }

            setTimeout(function(){
                // 遍历更新所有条目文字:如果条目内容为空而questionList里的textContent非空
                if (questions && navLinks) {
                    questions.forEach((question, index) => {
                        if (index >= navLinks.length) return;

                        const currentLinkContainer = navLinks[index];
                        const linkElement = currentLinkContainer.querySelector('.tool-nav-link');
                        if (!linkElement) return;

                        const spans = linkElement.querySelectorAll('span');
                        if (spans.length < 2) return;

                        const textSpanElement = spans[1]; // 第二个span是文本span
                        const currentText = textSpanElement.textContent.trim();
                        const newText = normalizeQuestionText(question.textContent);

                        if (isEmpty(currentText) && !isEmpty(newText)) {
                            textSpanElement.textContent = newText;
                            linkElement.title = (index + 1) + '. ' + newText;
                        }
                    });
                }
            }, NAV_UPDATE_TEXT_DELAY);

            // 如果元素存在,执行滚动
            if (targetEl && document.body.contains(targetEl)) {
                targetEl.scrollIntoView({block: 'start'});
                clickedTarget = targetEl;
                clickLockUntil = Date.now() + NAV_CLICK_LOCK_DURATION;
                clearAllHighlights();
                setLinkStyle(linkContainer, true);
                // 自动显示当前点击项对应的副目录
                if (typeof autoShowSubNav === 'function') {
                    autoShowSubNav(i);
                }
            } else {
                // 元素不存在,等待一段时间后重试
            }
        };
    };

    // 创建导航链接元素
    const createNavLink = (el, i, preservedText) => {
        // 创建链接容器
        const linkContainer = createTag('div', "", NAV_STYLES.linkContainer);
        linkContainer.className = 'tool-nav-link-container';

        // 创建副目录小图标
        const subNavIcon = createTag('span', '📖', NAV_STYLES.waveIcon);
        subNavIcon.title = '显示副目录';
        subNavIcon.addEventListener('mouseenter', () => {
            subNavIcon.style.cssText = NAV_STYLES.waveIcon + NAV_STYLES.waveIconHover;
        });
        subNavIcon.addEventListener('mouseleave', () => {
            subNavIcon.style.cssText = NAV_STYLES.waveIcon + NAV_STYLES.waveIconNormal;
        });
        subNavIcon.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();

            // 如果当前已经显示该问题的副目录,则隐藏
            if (currentSubNavQuestionIndex === i && subNavBar.style.display === 'block') {
                hideSubNavBar();
                return;
            }

            // 查找问题对应的回答内容区域
            let targetEl = el;
            if (!targetEl || !document.body.contains(targetEl)) {
                const questions = getQuestionList();
                if (questions && questions.length > i) {
                    targetEl = questions[i];
                }
            }

            if (!targetEl) {
                console.warn('问题元素不存在');
                return;
            }

            // 查找回答内容区域
            const answerContent = findAnswerContent(targetEl);
            if (!answerContent) {
                console.log('未找到回答内容区域');
                return;
            }

            // 查找标题
            const headings = findHeadingsInContent(answerContent);
            if (headings.length === 0) {
                console.log('未找到h2~h4标题');
                return;
            }

            // 清除关闭状态(恢复副目录)
            setSubNavClosed(false);

            // 显示副目录栏
            showSubNavBar(i, headings);
        });

        // 创建链接内容
        const link = createTag('div', "", NAV_STYLES.link);
        link.className = 'tool-nav-link';

        const indexText = (i + 1) + '. ';
        const indexSpan = createTag('span', indexText, "");
        indexSpan.style.color = NAV_ITEM_COLOR;

        // 先获取新文本,如果新文本非空则使用新文本,否则使用保留文本
        const newText = normalizeQuestionText(el.textContent);
        const normalizedText = (newText && newText.length > 0) ? newText : (preservedText || newText);
        const textSpan = createTag('span', normalizedText, "");

        link.title = (i + 1) + '. ' + normalizedText;
        appendSeveral(link, indexSpan, textSpan);

        // 事件监听
        link.addEventListener('mouseenter', () => link.style.backgroundColor = '#f0f0f0');
        link.addEventListener('mouseleave', () => link.style.backgroundColor = '');
        link.addEventListener('click', handleNavLinkClick(el, i, linkContainer));

        // 组装链接容器
        appendSeveral(linkContainer, subNavIcon, link);

        return linkContainer;
    };

    // 创建导航栏标题元素(包含隐藏按钮)
    const createTitle = () => {
        const title = createTag('div', "", NAV_STYLES.title);

        const titleText = createTag('span', '主目录', "");

        const hideBtn = createTag('span', '隐藏', NAV_STYLES.hideBtn);
        hideBtn.addEventListener('mouseenter', () => hideBtn.style.backgroundColor = '#f5f5f5');
        hideBtn.addEventListener('mouseleave', () => hideBtn.style.backgroundColor = '');
        hideBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            setNavMinimized(true);
        });

        // 创建条数显示元素
        navCountText = createTag('span', "", NAV_STYLES.countText);
        navCountText.style.display = 'none'; // 默认隐藏

        appendSeveral(title, titleText, hideBtn, navCountText);
        return title;
    };

    // 初始化 IntersectionObserver
    const initIntersectionObserver = () => {
        try {
            navIO = new IntersectionObserver((entries) => {
                const now = Date.now();
                let nextEl = null;

                // 点击锁定期内,优先使用点击的目标
                if(now < clickLockUntil && clickedTarget) {
                    const rect = clickedTarget.getBoundingClientRect?.();
                    const nearTop = isElementNearTop(rect);
                    const inView = isElementVisible(rect, NAV_VIEWPORT_THRESHOLD);
                    if(inView || nearTop) nextEl = clickedTarget;
                }

                // 新的高亮逻辑
                if(!nextEl) {
                    // 找到所有可见的目录项,按位置排序
                    const visibleElements = getVisibleElements(navQuestions, 1.0); // 使用100%视口高度

                    if(visibleElements.length > 0) {
                        // 检查第一个可见元素的位置
                        const firstVisibleEl = visibleElements[0];
                        const rect = firstVisibleEl.getBoundingClientRect();
                        const positionPercent = getElementPositionPercent(rect);

                        // 根据位置决定高亮项
                        if(positionPercent >= 0 && positionPercent <= NAV_HIGHLIGHT_THRESHOLD) {
                            // 0~30%:高亮当前项
                            nextEl = firstVisibleEl;
                        } else if(positionPercent > NAV_HIGHLIGHT_THRESHOLD && positionPercent <= 1.0) {
                            // 30%~100%:高亮前一项
                            const currentIndex = navQuestions.indexOf(firstVisibleEl);
                            if(currentIndex > 0) {
                                nextEl = navQuestions[currentIndex - 1];
                            } else {
                                nextEl = firstVisibleEl; // 如果是第一项,仍然高亮自己
                            }
                        }
                    } else {
                        // 视野无任何目录,保持上次高亮项(不改变nextEl)
                        // 这样navLinks会保持之前的状态
                        return;
                    }
                }

                // 应用高亮
                navLinks.forEach((link, i) => setLinkStyle(link, navQuestions[i] === nextEl));
                // 自动显示当前高亮项对应的副目录
                if (nextEl && typeof autoShowSubNav === 'function') {
                    const activeIndex = navQuestions.indexOf(nextEl);
                    if (activeIndex >= 0) {
                        autoShowSubNav(activeIndex);
                    }
                }
            }, { root: null, rootMargin: '0px 0px -70% 0px', threshold: [0, 0.1, 0.5, 1] });

            navQuestions.forEach(el => {
                if(el?.tagName) try { navIO.observe(el); } catch(e) {}
            });
        } catch(e) {}
    };

    // 从主目录栏链接容器中获取实质内容文本
    const getStudioNavLinkText = (linkContainer) => {
        if(!linkContainer) return null;
        const link = linkContainer.querySelector('.tool-nav-link');
        if(!link) return null;
        const spans = link.querySelectorAll('span');
        if(spans.length < 2) return null;
        const textSpan = spans[1]; // 第二个 span 是文本内容
        const normalizedText = normalizeQuestionText(textSpan.textContent);
        return (normalizedText && normalizedText.length > 0) ? normalizedText : null;
    };

    // 检查 STUDIO 站点主目录栏中所有链接的 span 是否都有实质内容
    const checkStudioNavContent = () => {
        return navLinks.length > 0 && navLinks.every(linkContainer => {
            return getStudioNavLinkText(linkContainer) !== null;
        });
    };

    // 获取主目录栏中第一个有实质内容的链接文本
    const getFirstStudioNavLinkText = () => {
        if(!navLinks || navLinks.length === 0) return null;
        for (let i = 0; i < navLinks.length; i++) {
            const text = getStudioNavLinkText(navLinks[i]);
            if(text) return text;
        }
        return null;
    };

    // 保存 STUDIO 站点旧链接中有实质内容的文本
    const preserveStudioNavTexts = () => {
        const preservedTexts = [];
        const currentUrl = getUrl();
        // 如果 URL 变化了,清空保留的文本,不保存旧文本
        if(navLinks.length > 0) {
            // 检查 URL 是否变化
            if(preservedNavTextsUrl !== null && preservedNavTextsUrl !== currentUrl) {
                preservedNavTextsUrl = null;
                // URL 变化了,不保存旧文本,preservedTexts 保持为空数组
            } else {
                // URL 未变化,保存旧文本
                navLinks.forEach((linkContainer, i) => {
                    preservedTexts[i] = getStudioNavLinkText(linkContainer);
                });
                // 保存当前的 URL
                preservedNavTextsUrl = currentUrl;
            }
        }
        return preservedTexts;
    };

    // 获取 STUDIO 站点保留的文本(用于创建链接时)
    const getPreservedTextForStudio = (preservedTexts, index) => {
        const urlWhenUsing = getUrl();
        return (preservedNavTextsUrl === urlWhenUsing && preservedTexts[index]) ? preservedTexts[index] : null;
    };

    // 更新导航问题列表(重新构建导航栏)
    const updateNavQuestions = (quesList) => {
        if(isEmpty(quesList)) {
            navBar.replaceChildren();
            navBar.style.visibility = navMiniButton.style.visibility = "hidden";
            currentNavBarUrl = null; // 清空时也重置 URL 跟踪
            updateNavCount(); // 更新条数显示
            return;
        }

        const thisQuestions = Array.from(quesList);
        const currentUrl = getUrl();
        // 检查 URL 是否变化(使用 currentNavBarUrl 来检测,即使 preservedNavTextsUrl 为 null 也能检测到)
        const urlChanged = currentNavBarUrl !== null && currentNavBarUrl !== currentUrl;
        
        if(navQuestions
            && thisQuestions.length === navQuestions.length
            && normalizeQuestionText(thisQuestions[0].textContent) === normalizeQuestionText(navQuestions[0].textContent)) {

            // 非 STUDIO 站点保持原有逻辑,直接返回(除非 URL 变化)
            if(site !== STUDIO) {
                if(!urlChanged) {
                    refreshNavBarVisibility();
                    return;
                }
                // URL 变化了,继续执行后续流程
            } else {
                // STUDIO 站点:检查主目录栏中所有链接的 span 是否都有实质内容
                const hasSubstantialContent = checkStudioNavContent();

                // 如果有实质内容且 URL 未变化,则直接返回;否则继续执行后续流程
                if(hasSubstantialContent && !urlChanged) {
                    refreshNavBarVisibility();
                    return;
                }
            }
        }

        // 对于 STUDIO 站点,保存旧链接中有实质内容的文本
        const preservedTexts = site === STUDIO ? preserveStudioNavTexts() : [];

        navBar.replaceChildren();
        navLinks = [];
        elToLink.clear();
        if(navIO) try { navIO.disconnect(); } catch(e) {}

        // 更新当前导航栏对应的 URL
        currentNavBarUrl = currentUrl;

        navBar.appendChild(createTitle());
        navQuestions = thisQuestions;

        navQuestions.forEach((el, i) => {
            if(!el?.tagName) return;
            // 如果有保留的文本且 URL 未变化,则使用保留的文本;否则不使用
            const preservedText = site === STUDIO ? getPreservedTextForStudio(preservedTexts, i) : null;
            const link = createNavLink(el, i, preservedText);
            navBar.appendChild(link);
            navLinks.push(link);
            elToLink.set(el, link);
        });

        refreshNavBarVisibility();
        initIntersectionObserver();

        // 页面刚加载时,如果视野里没有任何目录项,则自动高亮最后一项
        setTimeout(() => {
            const visibleElements = getVisibleElements(navQuestions, 1.0);

            if(visibleElements.length === 0 && navLinks.length > 0) {
                // 视野无任何目录项,高亮最后一项
                clearAllHighlights();
                setLinkStyle(navLinks[navLinks.length - 1], true);
                // 自动显示最后一项对应的副目录
                if (typeof autoShowSubNav === 'function') {
                    autoShowSubNav(navLinks.length - 1);
                }
            }
        }, 100);
    };

    // 迷你按钮事件
    navMiniButton.addEventListener('click', (e) => {
        e.stopPropagation();
        setNavMinimized(false);
    });

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  🎛️ 10、多选面板  🎛️                                                  ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    // 面板样式集中定义
    const PANEL_STYLES = {
        panel: `z-index:9999;cursor:pointer;position:fixed;right:10px;bottom:80px;max-height:430px;background:white;border:1px solid #ddd;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.1);overflow-y:auto;padding:2px;display:flex;flex-direction:column;`,
        panelCompact: `min-width:120px;`,
        disable: `background:#ec7258;color:white;border-radius:6px;padding:2px 1px;`,
        item: `display:flex;align-items:center;padding:3px 0 3px 3px;border-bottom:1px solid #eee;`,
        wordSpan: `flex:1;margin-right:10px;font-size:14px;`,
        checkbox: `margin-right:1px;font-size:20px;`,
        emptyMessage: `padding:1px;text-align:center;color:#888;font-size:14px;`,
        headline: `font-weight:bold;`,
        hint: `color:#275fe6;width:0;height:0;padding-left:3px;margin-top:5px;margin-bottom:5px;border-top:8px solid transparent;border-right:8px solid #3498db;border-bottom:8px solid transparent;`,
        settingsBtn: `background:#667eea;color:white;border:none;border-radius:4px;padding:4px 8px;font-size:16px;cursor:pointer;margin-bottom:4px;width:100%;`
    };

    // 面板数据
    const contentContainer = createTag('div', "", "");
    let isCompactMode = false;
    let originalHTML = contentContainer.innerHTML;

    // 创建面板容器
    panel.style.cssText = PANEL_STYLES.panel;
    panel.id = TOOL_PANEL_ID;
    let hint = createTag('div', "", "");

    const DISABLE = "禁用";
    const ENABLE = "开启";
    
    // 创建禁用按钮
    let disable = createTag('div', DISABLE, PANEL_STYLES.disable);
    disable.id = "tool-disable";
    disable.addEventListener('click', (e) => disableEvent(e));

    const settingsBtn = createSettingsButton();

    // 根据word在words数组中的索引获取背景色
    const getItemBgColor = (word) => {
        const index = typeof word === 'number' ? word : words.indexOf(word);
        return index < 6 ? '#f0f8ff' : '#fffcf0';
    };

    /**
     * 创建单个面板项
     */
    function createPanelItem(word, selectedSites) {
        const originalIndex = words.indexOf(word);
        const item = createTag('div', "", PANEL_STYLES.item + `background:${getItemBgColor(originalIndex)};`);
        item.className = 'panel-item';
        item.dataset.word = word;

        const wordSpan = createTag('span', word, PANEL_STYLES.wordSpan);

        const checkbox = createTag('input', "", PANEL_STYLES.checkbox);
        checkbox.type = 'checkbox';
        checkbox.id = `word-${word}`;
        checkbox.checked = selectedSites.includes(wordToSite[word]);

        checkbox.addEventListener('change', () => updateStorageSites(word));

        item.addEventListener('click', (e) => {
            if (e.target.tagName === 'INPUT') {
                return;
            }
            e.stopPropagation();
            checkbox.checked = !checkbox.checked;
            updateStorageSites(word);
        });

        appendSeveral(item, wordSpan, checkbox);
        return item;
    }

    /**
     * 创建设置按钮
     */
    function createSettingsButton() {
        const btn = createTag('button', '设置', PANEL_STYLES.settingsBtn);
        btn.id = 'tool-settings';
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            showSettingsPopup();
        });
        btn.addEventListener('mouseenter', () => btn.style.opacity = '0.85');
        btn.addEventListener('mouseleave', () => btn.style.opacity = '1');
        return btn;
    }

    /**
     * 渲染面板内容(公共函数,用于初始化和刷新)
     */
    function renderPanelContent() {
        const selectedSites = getSitesAndCurrent();
        const visibleSites = getVisibleModels();
        const visibleWords = visibleSites.map(site => siteToWord[site]).filter(word => word);
        const items = visibleWords.map(word => createPanelItem(word, selectedSites));

        const headline = createTag('div', "全部模型", PANEL_STYLES.headline);

        appendSeveral(contentContainer, disable, headline, ...items);
        originalHTML = contentContainer.innerHTML;
    }

    // 初始化面板内容
    renderPanelContent();
    appendSeveral(panel, settingsBtn, contentContainer);

    const settingsBtnText = '设置 ⚙️';
    // 首次加载多选面板 是展开状态,后续刷新网页默认缩略状态
    if(getGV(FIRST_RUN_KEY)){
        switchToCompactMode();
    } else {
        // 如果不是第一次运行,面板保持展开状态,更新设置按钮文字
        settingsBtn.textContent = settingsBtnText;
    }

    // 面板相关函数
    function disableEvent(event){
        event.stopPropagation();
        if(disable.textContent === DISABLE){
            changeDisable(true);
        }else{
            changeDisable(false);
        }
    }

    function changeDisable(status){
        if(status === true){
            setGV("disable", true);
            disable.textContent = ENABLE;
            disable.style.background = "#f5a088";
            contentContainer.style.color = "lightgray";
            // 禁用状态下,缩略模式的背景色改为白色
            if(isCompactMode){
                const items = contentContainer.querySelectorAll('[data-word]');
                items.forEach(item => {
                    item.style.background = "white";
                });
            }
        }else{
            setGV("disable", false);
            disable.textContent = DISABLE;
            disable.style.background = "#ec7258";
            contentContainer.style.color = "black";
            // 恢复启用状态,缩略模式的背景色恢复为彩色
            if(isCompactMode){
                const items = contentContainer.querySelectorAll('[data-word]');
                items.forEach(item => {
                    const word = item.dataset.word;
                    item.style.background = getItemBgColor(word);
                });
            }
        }
    }

    // 从前端DOM获取面板被选中的元素,并存储
    function getSitesFromDomAndSave(){
        const checkboxes = document.querySelectorAll('input[type="checkbox"][id^="word-"]');
        const selectedSites = [];

        checkboxes.forEach(checkbox => {
            if (checkbox.checked) {
                const word = checkbox.id.split('-')[1]; // 提取选中的文本
                selectedSites.push(wordToSite[word]);
            }
        });
        setGV(CHOSEN_SITE, selectedSites);
        return selectedSites;
    };

    // 从存储获取已选站点
    function getSitesOfStorage(){
        try {
            return getGV(CHOSEN_SITE) || [];
        } catch (e) {
            console.error('Failed to parse selectedSites from GV', e);
            return [];
        }
    };

    function getSitesAndCurrent() {
        let sitesOfStorage = getSitesOfStorage();
        if(!sitesOfStorage.includes(site)){
            sitesOfStorage.unshift(site);
        }
        return sitesOfStorage;
    };
    function addCurrentToStorage() {
        let sitesOfStorage = getSitesOfStorage();
        if(!sitesOfStorage.includes(site)){
            sitesOfStorage.unshift(site);
            setGV(CHOSEN_SITE, sitesOfStorage);
        }
    };

    function getSitesExcludeCurrent() {
        let sitesOfStorage = getSitesOfStorage();
        if(sitesOfStorage.includes(site)){
            sitesOfStorage = sitesOfStorage.filter(element => element !== site);
        }
        return sitesOfStorage;
    };

    // 更新存储中的已选单词数字
    function updateStorageSites(word) {
        // 只要有勾选动作,就关闭禁用模式
        changeDisable(false);

        const visibleSites = getVisibleModels();
        const visibleWords = visibleSites.map(site => siteToWord[site]).filter(word => word);
        const selectedSites = visibleWords
            .filter(word => document.getElementById(`word-${word}`)?.checked)
            .map(word => wordToSite[word]);

        setGV(CHOSEN_SITE, selectedSites);
        console.log('Current selected sites:', selectedSites);

        let isDisable = getGV("disable");
        if(isDisable){
            return;
        }
        let siteOfWord = wordToSite[word];
        if (siteOfWord!== site && selectedSites.includes(siteOfWord)) {
            let lastHeartbeat = getGV(HEART_KEY_PREFIX + siteOfWord);
            if(isEmpty(lastHeartbeat) || Date.now() - lastHeartbeat > 1000 * HIBERNATE_GAP){
                setTimeout(function(){
                    window.open(newSites[siteOfWord], '_blank');
                }, OPEN_GAP);
            }
        }
    };

    // 存储-->复选框
    function updateBoxFromStorage() {
        const selectedSites = getSitesAndCurrent();
        // console.log('Syncing checkboxes from stoage:', selectedSites);

        const visibleSites = getVisibleModels();
        const visibleWords = visibleSites.map(site => siteToWord[site]).filter(word => word);
        visibleWords.forEach(word => {
            const checkbox = document.getElementById(`word-${word}`);
            if (checkbox) {
                checkbox.checked = selectedSites.includes(wordToSite[word]);
            }
        });
    };

    // zchat特殊处理
    if(site === ZCHAT){
        let lastVisibleState = false; // 记录上一次的可见状态
        const observer = new IntersectionObserver((entries, instance) => {
            entries.forEach(entry => {
                const isCurrentlyVisible = entry.isIntersecting;
                // 状态发生变化时触发逻辑
                if (lastVisibleState === true && isCurrentlyVisible === false) {
                    document.body.appendChild(panel);
                    instance.unobserve(entry.target); // 停止观察当前元素
                }
                lastVisibleState = isCurrentlyVisible; // 更新状态记录
            });
        }, {
            threshold: 0.1 // 阈值可根据需求调整
        });
        observer.observe(panel);
    }

    // 刷新简略模式
    function reloadCompactMode(){
        if (!isCompactMode) return;

        // 确保按钮状态正确
        settingsBtn.style.display = 'none';
        if (contentContainer.contains(disable)) {
            disable.remove();
        }
        if (!panel.contains(disable)) {
            panel.insertBefore(disable, contentContainer);
        }
        disable.style.display = 'block';

        let selectedSites = getSitesAndCurrent();
        let selectedWords = selectedSites.map(site => siteToWord[site]).filter(word => word);
        // 按照可见模型列表的顺序排序
        const visibleSites = getVisibleModels();
        const visibleWords = visibleSites.map(site => siteToWord[site]).filter(word => word);
        selectedWords = visibleWords.filter(word => selectedWords.includes(word));
        drawCompactPanel(selectedWords);

        reloadDisableStatus();
    }

    function reloadDisableStatus(){
        let isDisable = getGV("disable");
        let status = isDisable ? true : false;
        changeDisable(status);
    }

    // 切换到简略模式
    function switchToCompactMode(){
        if (isCompactMode) return;
        reloadDisableStatus();

        // 保存原始内容
        originalHTML = contentContainer.innerHTML;

        // 记录选中的项:优先从DOM读取,如果读取不到则从存储读取
        const visibleSites = getVisibleModels();
        const visibleWords = visibleSites.map(site => siteToWord[site]).filter(word => word);
        let selectedWords = visibleWords.filter(word =>
            document.getElementById(`word-${word}`)?.checked
        );

        // 如果从DOM读取不到,则从存储读取(fallback机制)
        if (selectedWords.length === 0) {
            const selectedSites = getSitesAndCurrent();
            let wordsFromStorage = selectedSites.map(site => siteToWord[site]).filter(word => word);
            // 按照可见模型列表的顺序排序
            selectedWords = visibleWords.filter(word => wordsFromStorage.includes(word));
        }

        // 隐藏设置按钮,显示禁用按钮
        settingsBtn.style.display = 'none';
        // 如果disable在contentContainer中,先移除
        if (contentContainer.contains(disable)) {
            disable.remove();
        }
        // 将disable添加到panel顶部
        if (!panel.contains(disable)) {
            panel.insertBefore(disable, contentContainer);
        }
        disable.style.display = 'block';

        if (selectedWords.length === 0) {
            const emptyMsg = createTag('div', '未选模型', PANEL_STYLES.emptyMessage);
            contentContainer.replaceChildren();
            contentContainer.appendChild(emptyMsg);
        } else {
            drawCompactPanel(selectedWords);
        }

        isCompactMode = true;
        panel.style.cssText = PANEL_STYLES.panel;
    };

    // 绘制缩略模式面板
    function drawCompactPanel(selectedWords){
        contentContainer.replaceChildren();
        hint.style.cssText = PANEL_STYLES.hint;
        contentContainer.appendChild(hint);

        let isDisable = getGV("disable");
        selectedWords.forEach(word => {
            // 禁用状态下使用白色背景,否则使用彩色背景
            const bgColor = isDisable ? 'white' : getItemBgColor(word);
            const item = createTag('div', "", PANEL_STYLES.item + `background:${bgColor};`);
            item.dataset.word = word;

            let alias = wordToAlias[word];
            const wordSpan = createTag('span', alias, PANEL_STYLES.wordSpan);

            item.appendChild(wordSpan);
            contentContainer.appendChild(item);
        });
    }

    // 刷新多选面板(重新生成面板内容)
    function refreshPanel() {
        contentContainer.replaceChildren();
        renderPanelContent();
        
        // 如果是展开模式,确保设置按钮状态正确
        if (!isCompactMode) {
            settingsBtn.style.display = 'block';
            settingsBtn.textContent = settingsBtnText;
            // 确保设置按钮在panel层面,在contentContainer之前
            if (!panel.contains(settingsBtn) || settingsBtn.nextSibling !== contentContainer) {
                if (panel.contains(settingsBtn)) {
                    settingsBtn.remove();
                }
                panel.insertBefore(settingsBtn, contentContainer);
            }
            // 如果disable在panel层面,需要移除(它应该在contentContainer内)
            if (panel.contains(disable) && !contentContainer.contains(disable)) {
                disable.remove();
            }
        }
    }

    // 切换到原始模式
    function switchToOriginalMode() {
        if (!isCompactMode) return;

        // 显示设置按钮,隐藏禁用按钮(禁用按钮会在renderPanelContent中添加到contentContainer)
        settingsBtn.style.display = 'block';
        settingsBtn.textContent = settingsBtnText;
        // 确保设置按钮在panel层面,在contentContainer之前
        if (!panel.contains(settingsBtn) || settingsBtn.nextSibling !== contentContainer) {
            if (panel.contains(settingsBtn)) {
                settingsBtn.remove();
            }
            panel.insertBefore(settingsBtn, contentContainer);
        }
        if (panel.contains(disable)) {
            disable.remove();
        }

        contentContainer.replaceChildren();
        renderPanelContent();
        updateBoxFromStorage();

        isCompactMode = false;
        panel.style.cssText = PANEL_STYLES.panel;
    };

    // 点击面板切换模式
    panel.addEventListener('click', (e) => {
        // 阻止事件冒泡到document
        e.stopPropagation();

        // 如果点击的是复选框、按钮或者panel-item,不切换模式
        if (e.target.tagName === 'INPUT' || 
            e.target.tagName === 'BUTTON' || 
            e.target.id === 'tool-disable' || 
            e.target.id === 'tool-settings' ||
            e.target.closest('.panel-item')) {
            return;
        }

        // 切换模式:缩略-->展开;展开-->缩略
        if (isCompactMode) {
            switchToOriginalMode();
        } else {
            switchToCompactMode();
        }
    });

    // 点击页面其他地方切换到简略模式
    document.addEventListener('click', (e) => {
        // 如果点击的是面板内部,不处理
        if (panel.contains(e.target)) {
            return;
        }

        // 切换到简略模式
        if(panel.style.visibility !== "hidden"){
            switchToCompactMode();
        }
    });


    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  ⚠️ 11、一些工具函数  ⚠️                       ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    /**
     * 创建标签
     */
    function createTag(tag, textContent, css){
        const ele = document.createElement(tag);
        ele.style.cssText = css;
        if(textContent){
            ele.textContent = textContent;
        }
        return ele;
    }

    function createHtml(tag, html, css){
        const ele = document.createElement(tag);
        ele.style.cssText = css;
        if(html){
            setInnerHTML(ele, html);
        }
        return ele;
    }

    /**
     * 一次性添加多个子元素
     */
    function appendSeveral(parent, ...children) {
        children.forEach(child => {
            if (child) {
                parent.appendChild(child);
            }
        });
        return parent;
    }

    /**
     * 使用 MutationObserver 监测元素出现(更优雅的方式)
     * @param {Function} selectorFn - 获取元素的函数
     */
    function waitForElement(selectorFn, options = {}) {
        const {
            timeout = DEFAULT_WAIT_ELEMENT_TIME,
            root = document.body,
            timeoutMsg = "等待元素出现超时"
        } = options;

        return new Promise((resolve, reject) => {
            // 先检查元素是否已经存在
            const element = selectorFn();
            if (element) {
                resolve(element);
                return;
            }

            let timeoutId;
            let observer;

            // 设置超时
            timeoutId = setTimeout(() => {
                if (observer) observer.disconnect();
                console.warn(timeoutMsg);
                reject(new Error(timeoutMsg));
            }, timeout);

            // 创建 MutationObserver 监听 DOM 变化
            observer = new MutationObserver((mutations) => {
                const element = selectorFn();
                if (element) {
                    clearTimeout(timeoutId);
                    observer.disconnect();
                    resolve(element);
                }
            });

            // 开始观察
            observer.observe(root, {
                childList: true,      // 监听子节点的添加/删除
                subtree: true,        // 监听所有后代节点
                attributes: false,    // 不监听属性变化(性能优化)
                characterData: false  // 不监听文本内容变化(性能优化)
            });
        });
    }

    // 获取当前URL
    function getUrl(){
        return window.location.href;
    }

    /**
     * 判断当前是否为最大宽度
     */
    function isMaxWidth() {
        return window.outerWidth >= screen.availWidth - 50;
    }

    // 远程HTTP请求
    function remoteHttp(remoteUrl, data){
        GM_xmlhttpRequest({
            method: "POST",
            url: remoteUrl,
            data: JSON.stringify(data),
            headers: {
                "Content-Type": "application/json"
            },
            onload: function(response) {
                console.log(response.responseText);
            },
            onerror: function(error) {
                console.error('请求失败:', error);
            }
        });
    }

    /**
     * 存储管理
     */

    // Blob --> Base64
    function blobToBase64(blob) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = () => resolve(reader.result);
            reader.onerror = reject;
            reader.readAsDataURL(blob);
        });
    }

    // Base64 --> Blob
    function base64ToBlob(base64) {
        // 移除 data URL 前缀
        const base64Data = base64.split(',')[1];
        if (!base64Data) {
            throw new Error('无效的 Base64 字符串');
        }
        const byteString = atob(base64Data);
        const mimeType = base64.split(',')[0].split(':')[1].split(';')[0] || 'image/png';

        const ab = new ArrayBuffer(byteString.length);
        const ia = new Uint8Array(ab);
        // 填充字节数组
        for (let i = 0; i < byteString.length; i++) {
            ia[i] = byteString.charCodeAt(i);
        }
        return new Blob([ab], { type: mimeType });
    }

    function getS(key){
        return localStorage.getItem(key);
    }
    function setS(key, val){
        localStorage.setItem(key, val);
    }

    // 油猴设置、读取共享存储
    function setGV(key, value){
        GM_setValue(key, value);
    }
    function getGV(key){
        return GM_getValue(key);
    }

    // 获取可见模型列表(返回site值列表)
    function getVisibleModels() {
        const stored = getGV(VISIBLE_MODELS_KEY);
        if (stored && Array.isArray(stored) && stored.length > 0) {
            // 验证存储的site是否仍然有效(未被禁用)
            const validSites = wordConfig.map(item => item.site);
            return stored.filter(site => validSites.includes(site));
        }
        // 默认返回所有模型的 site 列表
        return wordConfig.map(item => item.site);
    }

    // 设置可见模型列表(接受site值列表)
    function setVisibleModels(visibleSites) {
        // 验证:至少保留一个
        if (!visibleSites || visibleSites.length === 0) {
            return false;
        }
        setGV(VISIBLE_MODELS_KEY, visibleSites);
        return true;
    }

    // 获取书签功能总开关状态(默认 false,即关闭)
    function isBookmarkFeatureEnabled() {
        return getGV(ENABLE_BOOKMARK_FEATURE_KEY) === true;
    }

    // 通用判空函数
    function isEmpty(item){
        if(item===null || item===undefined || item.length===0 || item === "null"){
            return true;
        }else{
            return false;
        }
    }


    function guid() {
        return 'xxxxxxxx-xxxx-4xxx-yxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }

    // 格式化的时间
    function curDate() {
        let timer = new Date();
        let year = timer.getFullYear();
        let month = timer.getMonth() + 1; // 由于月份从0开始,因此需加1
        if (month < 10) month = "0" + month;
        let day = timer.getDate();
        if (day < 10) day = "0" + day;
        let hour = timer.getHours();
        if (hour < 10) hour = "0" + hour;
        let minute = timer.getMinutes();
        if (minute < 10) minute = "0" + minute;
        let second = timer.getSeconds();
        if (second < 10) second = "0" + second;
        return `【${hour}:${minute}:${second}】`;
    }

    // 弹窗样式常量
    const POPUP_CONTAINER_STYLE = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:2147483647;display:flex;align-items:center;justify-content:center';
    const POPUP_CONTENT_BASE_STYLE = 'min-width:400px;background:white;border-radius:12px;padding:20px;box-shadow:0 10px 40px rgba(0,0,0,0.3)';
    const POPUP_TITLE_STYLE = 'font-size:16px;font-weight:bold;color:#222;margin-bottom:10px';

    // 设置面板公共样式常量(提取公共基础,通过组合减少重复)
    const _tabBase = 'min-width:120px;padding:12px 20px;text-align:center;cursor:pointer;font-size:14px;';
    const _labelBase = 'font-size:14px;color:#333;';
    const _inputBase = 'border:1px solid #ddd;border-radius:4px;font-size:14px;';
    const _containerBase = 'display:flex;align-items:center;';
    
    const SETTINGS_STYLES = {
        // Tab样式
        tabBase: _tabBase,
        tabActive: _tabBase + 'border-bottom:3px solid #667eea;color:#667eea;font-weight:bold;background:#e8f0fe;',
        tabInactive: _tabBase + 'border-bottom:3px solid transparent;color:#666;background:#f5f5f5;',
        // Label样式
        labelBase: _labelBase,
        labelWithCursor: _labelBase + 'cursor:pointer;flex:1;',
        labelWithMinWidth: _labelBase + 'min-width:220px;flex-shrink:0;user-select:none;',
        labelWithMinWidthSmall: _labelBase + 'min-width:82px;flex-shrink:0;',
        // Input样式
        inputBase: 'padding:6px;' + _inputBase,
        inputSmall: 'width:55px;padding:6px 2px;' + _inputBase + 'text-align:center;',
        inputMedium: 'width:80px;padding:6px 10px;' + _inputBase,
        inputTextarea: 'width:100%;min-height:60px;padding:6px;border:1px solid #667eea;border-radius:4px;font-size:13px;font-family:inherit;resize:vertical;box-sizing:border-box',
        // Container样式
        itemContainer: _containerBase + 'padding:10px 0;border-bottom:1px solid #f0f0f0',
        itemContainerWithGap: _containerBase + 'padding:8px 0;border-bottom:1px solid #f0f0f0',
        toggleContainer: _containerBase + 'justify-content:flex-start;padding:6px 0;border-bottom:1px solid #f0f0f0;gap:12px;',
        columnsContainer: 'display:flex;gap:12px;margin-bottom:15px',
        // Tip文本样式
        tipText: 'color:#333;font-size:14px;margin-bottom:15px;line-height:1.5',
        // 其他样式
        checkboxSmall: 'margin-right:8px;width:16px;height:16px;cursor:pointer;',
        checkboxHidden: 'opacity:0;width:0;height:0;position:absolute;',
        closeBtn: 'cursor:pointer;font-size:20px;font-weight:bold;color:#999;padding:5px;position:absolute;top:15px;right:15px',
        saveBtn: 'padding:4px 8px;background:#4caf50;color:white;border:none;border-radius:4px;cursor:pointer;font-size:12px;flex-shrink:0'
    };

    /**
     * 创建弹窗基础结构
     */
    function createPopupBase(popupId, contentExtraStyle = '') {
        // 移除已有弹窗
        const existingPopup = document.getElementById(popupId);
        if (existingPopup) existingPopup.remove();

        // 创建弹窗容器
        const popup = createTag('div', "", POPUP_CONTAINER_STYLE);
        popup.id = popupId;

        // 创建弹窗内容
        const content = createTag('div', "", POPUP_CONTENT_BASE_STYLE + contentExtraStyle);

        popup.appendChild(content);
        popup.onclick = (e) => { if (e.target === popup) popup.remove(); };
        document.body.appendChild(popup);

        return { popup, content };
    }

    /**
     * 创建主按钮(渐变紫色)
     */
    function createPrimaryButton(text, onClick) {
        const btn = createTag('button', text, 'padding:10px;background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);color:white;border:none;border-radius:4px;cursor:pointer;font-size:14px');
        btn.onclick = onClick;
        btn.addEventListener('mouseenter', () => btn.style.opacity = '0.85');
        btn.addEventListener('mouseleave', () => btn.style.opacity = '1');
        return btn;
    }

    /**
     * 显示提示弹窗
     * @param {string} message 提示内容
     * @param {string} [title] 可选标题
     * @param {Object} [extraButton] 可选额外按钮配置 {text: string, onClick: function}
     */
    function showMessagePopup(message, title, extraButton) {
        const { popup, content } = createPopupBase('message-popup', ';max-width:400px');

        // 标题(可选)
        if (title) {
            const titleDiv = createTag('div', title, POPUP_TITLE_STYLE);
            content.appendChild(titleDiv);
        }

        // 消息内容
        const messageDiv = createTag('div', message, 'color:#333;font-size:14px;line-height:1.6;white-space:pre-line;margin-bottom:15px');

        // 按钮容器
        const buttonContainer = createTag('div', '', 'display:flex;gap:10px;margin-top:15px');
        
        // 确定按钮
        const confirmBtn = createPrimaryButton('确定', () => popup.remove());
        confirmBtn.style.flex = '1';
        
        // 如果有额外按钮,添加到容器中
        if (extraButton && extraButton.text && extraButton.onClick) {
            const extraBtn = createPrimaryButton(extraButton.text, () => {
                extraButton.onClick();
                popup.remove();
            });
            extraBtn.style.flex = '1';
            buttonContainer.appendChild(extraBtn);
        }
        
        buttonContainer.appendChild(confirmBtn);
        appendSeveral(content, messageDiv, buttonContainer);
    }

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  ⚙️ 12、设置弹窗功能  ⚙️                                                   ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    /**
     * 更新按钮显示状态
     */
    function updateButtonVisibility() {
        // 先读取当前开关配置
        const showToggle = getGV(SHOW_TOGGLE_BUTTON_KEY) !== false; // 默认true(显示)
        // 书签/列表按钮:默认 false(隐藏),只有显式设置为 true 时才显示
        const showBookmark = getGV(SHOW_BOOKMARK_BUTTON_KEY) === true;

        // 更新隐藏(输入框)按钮 —— 与书签功能是否启用无关
        if (toggleButton) {
            toggleButton.style.display = showToggle ? 'flex' : 'none';
        }

        // 书签功能整体关闭时,清理书签按钮并退出
        if (!isBookmarkFeatureEnabled()) {
            const bookmarkBtnForce = document.getElementById('bookmark-btn');
            if (bookmarkBtnForce) {
                bookmarkBtnForce.remove();
            }
            const bookmarkViewBtnForce = document.getElementById('bookmark-view-btn');
            if (bookmarkViewBtnForce) {
                bookmarkViewBtnForce.remove();
            }
            return;
        }

        // 当书签功能开启时,按用户配置显示/隐藏书签按钮
        const bookmarkBtn = document.getElementById('bookmark-btn');
        if (bookmarkBtn) {
            bookmarkBtn.style.display = showBookmark ? 'flex' : 'none';
        }

        // 更新历史(书签)按钮(与书签按钮使用同一个设置)
        const bookmarkViewBtn = document.getElementById('bookmark-view-btn');
        if (bookmarkViewBtn) {
            bookmarkViewBtn.style.display = showBookmark ? 'flex' : 'none';
        }
    }

    /**
     * 创建苹果风格开关
     */
    function createToggleSwitch(label, checked, onChange) {
        const container = createTag('div', '', SETTINGS_STYLES.toggleContainer);
        
        const labelDiv = createTag('div', label, SETTINGS_STYLES.labelBase);
        
        const switchContainer = createTag('label', '', 'position:relative;display:inline-block;width:44px;height:26px;cursor:pointer;flex-shrink:0');
        
        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = checked;
        checkbox.style.cssText = SETTINGS_STYLES.checkboxHidden;
        
        const slider = createTag('span', '', 'position:absolute;top:0;left:0;right:0;bottom:0;background-color:' + (checked ? '#34c759' : '#ccc') + ';transition:0.3s;border-radius:26px;');
        slider.style.cssText += 'cursor:pointer;';
        
        const sliderCircle = createTag('span', '', 'position:absolute;content:"";height:20px;width:20px;left:' + (checked ? '21px' : '3px') + ';bottom:3px;background-color:white;transition:0.3s;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,0.2);');
        sliderCircle.style.cssText += 'cursor:pointer;';
        
        checkbox.addEventListener('change', function() {
            const isChecked = checkbox.checked;
            slider.style.backgroundColor = isChecked ? '#34c759' : '#ccc';
            sliderCircle.style.left = isChecked ? '21px' : '3px';
            onChange(isChecked);
        });
        
        appendSeveral(switchContainer, checkbox, slider, sliderCircle);
        appendSeveral(container, switchContainer, labelDiv);
        
        return container;
    }

    /**
     * 创建 Tab 1: 多选面板自定义
     */
    function createModelSelectionTab(checkboxes) {
        const tab = createTag('div', '多选面板自定义', SETTINGS_STYLES.tabActive);
        const tabContent = createTag('div', '', '');
        
        // 创建说明文字
        const tipText = createTag('div', '仅勾选的大模型将出现在多选面板上', SETTINGS_STYLES.tipText);
        appendSeveral(tabContent, tipText);
        
        // 读取当前可见模型列表
        const visibleSites = getVisibleModels();
        
        // 创建两列容器
        const columnsContainer = createTag('div', '', SETTINGS_STYLES.columnsContainer);
        const leftColumn = createTag('div', '', 'flex:1');
        const rightColumn = createTag('div', '', 'flex:1');
        
        // 将 wordConfig 分为前6个和后6个
        const firstHalf = wordConfig.slice(0, 6);
        const secondHalf = wordConfig.slice(6);
        
        // 创建复选框函数
        function createModelCheckbox(config) {
            const { word, site } = config;
            const isVisible = visibleSites.includes(site);
            
            const checkboxContainer = createTag('div', '', SETTINGS_STYLES.itemContainerWithGap);
            
            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.checked = isVisible;
            checkbox.style.cssText = SETTINGS_STYLES.checkboxSmall;
            
            // 立即保存功能:复选框改变时立即生效
            checkbox.addEventListener('change', () => {
                const newVisibleSites = wordConfig
                    .filter(config => checkboxes[config.word]?.checked)
                    .map(config => config.site);
                
                if (newVisibleSites.length === 0) {
                    checkbox.checked = true; // 恢复选中,至少保留一个
                    showMessagePopup('至少需要保留一个模型可见');
                    return;
                }
                
                // 保存配置,退出弹窗后再刷新面板
                setVisibleModels(newVisibleSites);
            });
            
            const label = createTag('label', word, SETTINGS_STYLES.labelWithCursor);
            label.style.cssText += 'user-select:none;';
            label.onclick = () => checkbox.click();
            
            checkboxes[word] = checkbox;
            
            appendSeveral(checkboxContainer, checkbox, label);
            return checkboxContainer;
        }
        
        // 添加前6个到左列
        firstHalf.forEach(config => {
            leftColumn.appendChild(createModelCheckbox(config));
        });
        
        // 添加后6个到右列
        secondHalf.forEach(config => {
            rightColumn.appendChild(createModelCheckbox(config));
        });
        
        appendSeveral(columnsContainer, leftColumn, rightColumn);
        appendSeveral(tabContent, columnsContainer);
        
        return { tab, tabContent };
    }

    /**
     * 创建 Tab 2: 按钮显示设置
     */
    function createButtonDisplayTab() {
        const tab = createTag('div', '按钮显示', SETTINGS_STYLES.tabInactive);
        const tabContent = createTag('div', '', 'display:none;');
        
        // 读取当前设置
        // 隐藏输入框按钮:默认 true(显示)
        const showToggle = getGV(SHOW_TOGGLE_BUTTON_KEY) !== false;
        // 书签/列表按钮:默认 false(隐藏),只有显式设置为 true 时才显示
        const showBookmark = getGV(SHOW_BOOKMARK_BUTTON_KEY) === true;
        // 默认隐藏输入框:默认 false(不隐藏)
        const isInputDefaultHided = getGV(DEFAULT_HIDE_INPUT_AREA_KEY) === true;

        // 创建三个开关
        const toggleSwitch1 = createToggleSwitch('隐藏输入框的按钮,是否展示', showToggle, (checked) => {
            setGV(SHOW_TOGGLE_BUTTON_KEY, checked);
            updateButtonVisibility();
        });

        const toggleSwitch2 = createToggleSwitch('每次打开历史对话页面,输入框是否默认隐藏', isInputDefaultHided, (checked) => {
            setGV(DEFAULT_HIDE_INPUT_AREA_KEY, checked);
            // 如果开启,立即应用;如果关闭,恢复显示
            if (checked) {
                applyDefaultHideInputArea();
            } else {
                const aroundInputArea = getNthInputArea();
                if (aroundInputArea && aroundInputArea.style.display === 'none') {
                    aroundInputArea.style.display = TOGGLE_STATES.show.display;
                    isInputAreaHidden = false;
                    toggleBtnStatus(true);
                }
            }
        });

        const toggleSwitch3 = createToggleSwitch('此按钮留着,暂无实用', showBookmark, (checked) => {
            setGV(SHOW_BOOKMARK_BUTTON_KEY, checked);
            updateButtonVisibility();
        });

        appendSeveral(tabContent, toggleSwitch1, toggleSwitch2, toggleSwitch3);
        
        return { tab, tabContent };
    }

    /**
     * 创建 Tab 3: 导航变量设置
     */
    function createNavVarsTab() {
        const tab = createTag('div', '目录设置', SETTINGS_STYLES.tabInactive);
        const tabContent = createTag('div', '', 'display:none;');
        
        // 读取当前导航变量设置
        const navMaxWidthValue = getGV(NAV_MAX_WIDTH_KEY) || DEFAULT_NAV_MAX_WIDTH;
        const subNavMaxWidthValue = getGV(SUB_NAV_MAX_WIDTH_KEY) || DEFAULT_SUB_NAV_MAX_WIDTH;
        const navTopValue = getGV(NAV_TOP_KEY) || DEFAULT_NAV_TOP;
        const navTopOverflowValue = getGV(NAV_TOP_OVERFLOW_KEY) || DEFAULT_NAV_TOP_OVERFLOW;
        
        // 创建说明文字
        const tipText = createHtml('div', '修改后立即生效。', SETTINGS_STYLES.tipText);
        appendSeveral(tabContent, tipText);
        
        // 创建输入框容器
        const configContainer = createTag('div', '', 'display:flex;flex-direction:column;gap:12px');
        const inputCss = SETTINGS_STYLES.inputMedium;
        const itemContainerCss = SETTINGS_STYLES.itemContainer;
        const labelCss = SETTINGS_STYLES.labelWithMinWidth;
        const defaultLabelCss = 'font-size:13px;color:#666;margin-left:10px;';

        // 导航变量配置
        const navConfigs = [
            { label: '主目录最大宽度', value: navMaxWidthValue, placeholder: DEFAULT_NAV_MAX_WIDTH, key: NAV_MAX_WIDTH_KEY, defaultVal: DEFAULT_NAV_MAX_WIDTH },
            { label: '副目录最大宽度', value: subNavMaxWidthValue, placeholder: DEFAULT_SUB_NAV_MAX_WIDTH, key: SUB_NAV_MAX_WIDTH_KEY, defaultVal: DEFAULT_SUB_NAV_MAX_WIDTH },
            { label: '主目录(默认)垂直位置', value: navTopValue, placeholder: DEFAULT_NAV_TOP, key: NAV_TOP_KEY, defaultVal: DEFAULT_NAV_TOP },
            { label: '主目录(条数较多时)垂直位置', value: navTopOverflowValue, placeholder: DEFAULT_NAV_TOP_OVERFLOW, key: NAV_TOP_OVERFLOW_KEY, defaultVal: DEFAULT_NAV_TOP_OVERFLOW }
        ];

        // 创建输入框项的函数
        function createNavInputItem(config) {
            const item = createTag('div', '', itemContainerCss);
            const label = createTag('label', config.label, labelCss);
            const input = createTag('input', "", inputCss);
            input.type = 'text';
            input.value = config.value;
            input.placeholder = config.placeholder;
            const defaultLabel = createTag('span', `(默认: ${config.defaultVal})`, defaultLabelCss);
            appendSeveral(item, label, input, defaultLabel);
            return { item, input };
        }

        // 批量创建输入框
        const navInputs = navConfigs.map(createNavInputItem);
        const navInputItems = navInputs.map(nav => nav.item);
        const inputElements = navInputs.map(nav => nav.input);

        // 立即保存导航变量配置的函数
        function saveNavVarsImmediately() {
            navConfigs.forEach((config, index) => {
                const inputVal = inputElements[index].value.trim();
                if (inputVal && inputVal !== config.defaultVal) {
                    setGV(config.key, inputVal);
                } else {
                    GM_deleteValue(config.key);
                }
            });
            updateNavStyles();
        }
        
        // 批量添加输入框事件监听
        inputElements.forEach(input => {
            input.addEventListener('change', saveNavVarsImmediately);
            input.addEventListener('blur', saveNavVarsImmediately);
        });
        
        appendSeveral(configContainer, ...navInputItems);
        appendSeveral(tabContent, configContainer);
        
        return { tab, tabContent };
    }

    /**
     * 创建 Tab 4: 输入框隐藏范围设置
     */
    function createInputAreaHideLevelTab() {
        const tab = createTag('div', '输入框隐藏范围设置', SETTINGS_STYLES.tabInactive);
        const tabContent = createTag('div', '', 'display:none;');
        
        // 读取用户自定义的层级配置
        const customLevels = getGV(INPUT_AREA_HIDE_PARENT_LEVEL_KEY) || {};
        const levelInputs = {};
        
        // 创建说明文字
        const tipText = createHtml('div', '如果官网做了某些改动,则隐藏输入框的范围效果可能不合适;<br>此时可尝试修改下面数值:数值越大,则页面隐藏的内容范围越大,反之越小。', SETTINGS_STYLES.tipText);
        appendSeveral(tabContent, tipText);
        
        // 创建两列容器
        const columnsContainer = createTag('div', '', SETTINGS_STYLES.columnsContainer);
        const leftColumn = createTag('div', '', 'flex:1');
        const rightColumn = createTag('div', '', 'flex:1');
        
        // 将 wordConfig 分为前6个和后6个
        const firstHalf = wordConfig.slice(0, 6);
        const secondHalf = wordConfig.slice(6);
        
        // 立即保存层级配置的函数
        function saveLevelsImmediately() {
            const newLevels = {};
            let hasInvalid = false;
            
            // 收集所有输入框的值
            wordConfig.forEach(config => {
                const { site: siteId } = config;
                const input = levelInputs[siteId];
                const value = parseInt(input.value, 10);
                
                if (isNaN(value) || value < 0) {
                    hasInvalid = true;
                    input.style.borderColor = '#ff4444';
                    setTimeout(() => {
                        input.style.borderColor = '#ddd';
                    }, 2000);
                } else {
                    input.style.borderColor = '#ddd';
                    const defaultLevel = inputAreaHideParentLevel[siteId];
                    // 如果与默认值相同,则不保存(使用默认值)
                    if (value !== defaultLevel) {
                        newLevels[siteId] = value;
                    }
                }
            });
            
            if (hasInvalid) {
                return;
            }
            
            // 保存配置
            if (Object.keys(newLevels).length === 0) {
                // 如果所有值都是默认值,删除存储的配置
                GM_deleteValue(INPUT_AREA_HIDE_PARENT_LEVEL_KEY);
            } else {
                setGV(INPUT_AREA_HIDE_PARENT_LEVEL_KEY, newLevels);
            }
        }
        
        // 创建配置项的函数
        function createLevelConfigItem(config) {
            const { site: siteId, word } = config;
            const defaultLevel = inputAreaHideParentLevel[siteId];
            const currentLevel = customLevels[siteId] !== undefined ? customLevels[siteId] : defaultLevel;
            
            const itemContainer = createTag('div', '', SETTINGS_STYLES.itemContainer);
            
            const label = createTag('label', word, SETTINGS_STYLES.labelWithMinWidthSmall);
            label.style.cssText += 'user-select:none;';
            
            const input = document.createElement('input');
            input.type = 'number';
            input.value = currentLevel;
            input.min = '0';
            input.style.cssText = SETTINGS_STYLES.inputSmall;
            
            // 立即保存功能:输入框值改变时立即生效
            input.addEventListener('change', () => {
                saveLevelsImmediately();
            });
            input.addEventListener('blur', () => {
                saveLevelsImmediately();
            });
            
            const defaultLabel = createTag('span', `(默认: ${defaultLevel})`, 'font-size:13px;color:#666;margin:auto 10px;');
            
            levelInputs[siteId] = input;
            
            appendSeveral(itemContainer, label, input, defaultLabel);
            return itemContainer;
        }
        
        // 添加前6个到左列
        firstHalf.forEach(config => {
            leftColumn.appendChild(createLevelConfigItem(config));
        });
        
        // 添加后6个到右列
        secondHalf.forEach(config => {
            rightColumn.appendChild(createLevelConfigItem(config));
        });
        
        appendSeveral(columnsContainer, leftColumn, rightColumn);
        appendSeveral(tabContent, columnsContainer);
        
        return { tab, tabContent };
    }

    /**
     * 显示设置弹窗
     */
    function showSettingsPopup() {
        const { popup, content } = createPopupBase('settings-popup', '');

        // 标题
        const title = createTag('div', '设置', 'font-size:18px;font-weight:bold;margin-bottom:20px;color:#333');

        // Tab 切换容器
        const tabContainer = createTag('div', '', 'display:flex;border-bottom:2px solid #e0e0e0;margin-bottom:20px;width:fit-content;');
        
        // Tab 内容容器
        const tabContentContainer = createTag('div', '', 'min-height:200px;min-width:300px;');
        
        // 存储所有复选框的引用(用于多选面板设置)
        const checkboxes = {};
        
        // 创建各个Tab
        const { tab: tab1, tabContent: tab1Content } = createModelSelectionTab(checkboxes);
        const { tab: tab2, tabContent: tab2Content } = createButtonDisplayTab();
        const { tab: tab3, tabContent: tab3Content } = createNavVarsTab();
        const { tab: tab4, tabContent: tab4Content } = createInputAreaHideLevelTab();
        
        // Tab 切换函数(支持多个tab)
        const tabs = [tab1, tab2, tab3, tab4];
        const tabContents = [tab1Content, tab2Content, tab3Content, tab4Content];
        
        function switchTab(activeIndex) {
            tabs.forEach((tab, index) => {
                if (index === activeIndex) {
                    tab.style.cssText = SETTINGS_STYLES.tabActive;
                    tabContents[index].style.display = '';
                } else {
                    tab.style.cssText = SETTINGS_STYLES.tabInactive;
                    tabContents[index].style.display = 'none';
                }
            });
        }
        
        // Tab 点击事件
        tab1.onclick = () => switchTab(0);
        tab2.onclick = () => switchTab(1);
        tab3.onclick = () => switchTab(2);
        tab4.onclick = () => switchTab(3);
        
        appendSeveral(tabContainer, tab1, tab2, tab3, tab4);
        appendSeveral(tabContentContainer, tab1Content, tab2Content, tab3Content, tab4Content);
        
        // 关闭弹窗的函数,关闭时刷新多选面板
        const closePopup = () => {
            popup.remove();
            refreshPanel();
        };
        
        // 关闭按钮
        const closeBtn = createTag('span', '✕', SETTINGS_STYLES.closeBtn);
        closeBtn.onclick = closePopup;
        
        // 点击背景关闭时也刷新面板
        popup.onclick = (e) => {
            if (e.target === popup) {
                closePopup();
            }
        };
        
        appendSeveral(content, closeBtn, title, tabContainer, tabContentContainer);
    }

    /******************************************************************************
     * ═══════════════════════════════════════════════════════════════════════
     * ║                                                                      ║
     * ║  📚 13、书签功能  📚                                                      ║
     * ║                                                                      ║
     * ═══════════════════════════════════════════════════════════════════════
     ******************************************************************************/

    const BOOKMARK_SIGNAL_KEY = "bookmarkSignal"; // 书签创建信号key
    const BOOKMARK_JUMP_SIGNAL_KEY = "bookmarkJumpSignal"; // 书签跳转信号key
    const SITE_JUMP_REQUEST_PREFIX = "site-jump-request-"; // 单站点跳转请求信号前缀
    const SITE_JUMP_ACK_PREFIX = "site-jump-ack-"; // 单站点跳转确认信号前缀
    const SITE_JUMP_TIMEOUT = 500; // 跳转确认超时时间(毫秒)
    const BOOKMARK_LEN = 100;

    // ──────────────────────────────────────────────────────────────────────
    // 13.1 工具函数
    // ──────────────────────────────────────────────────────────────────────

    // 生成书签key(自增ID)
    function generateBookmarkKey() {
        let counter = getGV(BOOKMARK_ID_COUNTER) || 0;
        counter++;
        setGV(BOOKMARK_ID_COUNTER, counter);
        return `${BOOKMARK_PREFIX}${counter}`;
    }

    /**
     * 获取页面首个提问的内容
     */
    function getFirstQuestionContent() {
        const questions = getQuestionList();
        if (questions && questions.length > 0) {
            const firstQuestion = questions[0];
            const content = normalizeQuestionText(firstQuestion.textContent || firstQuestion.innerText || '');
            
            // 如果是 STUDIO 站点且内容为空,从主目录栏获取实质内容
            if (site === STUDIO && (!content || content.length === 0)) {
                const navLinkText = getFirstStudioNavLinkText();
                if (navLinkText) {
                    return navLinkText;
                }
            }
            
            return content;
        }
        return '';
    }

    // 从完整URL中提取路径部分(去掉域名前缀,截取第一个斜线为止,排除双斜线)
    function extractUrlPath(fullUrl) {
        const match = fullUrl.match(/^https?:\/\/[^\/]+\/(.*)$/);
        return match ? match[1] : '';
    }

    // 从路径部分拼接完整URL(加上域名前缀)
    function buildFullUrl(path, siteId) {
        const baseUrl = webSites[siteId]?.[0];
        if (!baseUrl) return path;
        const match = baseUrl.match(/^(https?:\/\/[^\/]+\/)/);
        return match ? match[1] + path : baseUrl + path;
    }

    /**
     * 更新书签数据(添加或更新当前站点的URL)
     * 解决并发写入覆盖问题:写入前重新读取最新数据并合并
     */
    function updateBookmarkData(bookmarkKey, siteId, url, question) {
        const siteWord = siteToWord[siteId] || siteId;
        // 只存储路径部分,去掉域名前缀
        const urlPath = extractUrlPath(url);

        // 重新读取最新数据,避免并发覆盖
        const bookmarkData = getBookmarkData(bookmarkKey) || { sites: [], group: DEFAULT_GROUP_ID, question: '' };
        const sites = bookmarkData.sites || [];
        const existingIndex = sites.findIndex(item => item.site === siteId);

        if (existingIndex >= 0) {
            sites[existingIndex].url = urlPath;
        } else {
            sites.push({ site: siteId, url: urlPath });
        }

        // 如果提供了question,则更新question字段
        const questionToSave = question !== undefined ? question : (bookmarkData.question || '');
        setBookmarkData(bookmarkKey, sites, bookmarkData.group, questionToSave);

        // 写入后验证,若数据丢失则重试
        setTimeout(() => {
            const verifyData = getBookmarkData(bookmarkKey);
            const verified = verifyData && verifyData.sites && verifyData.sites.some(item => item.site === siteId && item.url === urlPath);
            if (!verified) {
                console.log(curDate() + `书签: 检测到数据丢失,重试写入 ${siteWord}`);
                updateBookmarkData(bookmarkKey, siteId, url);
            }
        }, 100 + Math.random() * 200); // 随机延迟避免再次冲突

        console.log(curDate() + `书签: 站点[${siteWord}]的URL已保存`, bookmarkKey);
    }

    /**
     * 切换站点星标状态
     * @param {string} bookmarkKey - 书签key
     * @param {number} siteId - 站点ID
     * @returns {boolean} - 是否成功
     */
    function toggleSiteStarred(bookmarkKey, siteId) {
        const bookmarkData = getBookmarkData(bookmarkKey);
        if (!bookmarkData || !bookmarkData.sites) return false;

        const sites = bookmarkData.sites;
        const siteIndex = sites.findIndex(item => item.site === siteId);
        if (siteIndex === -1) return false;

        // 切换星标状态
        sites[siteIndex].starred = !sites[siteIndex].starred;

        // 保存数据
        setBookmarkData(bookmarkKey, sites, bookmarkData.group, bookmarkData.question);
        return true;
    }

    /**
     * 从书签中移除站点
     * @param {string} bookmarkKey - 书签key
     * @param {number} siteId - 站点ID
     * @returns {boolean} - 是否成功移除
     */
    function removeSiteFromBookmark(bookmarkKey, siteId) {
        const bookmarkData = getBookmarkData(bookmarkKey);
        if (!bookmarkData || !bookmarkData.sites) return false;

        const sites = bookmarkData.sites;
        const siteIndex = sites.findIndex(item => item.site === siteId);
        if (siteIndex === -1) return false;

        // 移除站点
        sites.splice(siteIndex, 1);

        // 保存数据
        setBookmarkData(bookmarkKey, sites, bookmarkData.group, bookmarkData.question);
        return true;
    }

    // ──────────────────────────────────────────────────────────────────────
    // 13.2 跨站点同步机制
    // ──────────────────────────────────────────────────────────────────────

    // 监听书签创建信号:将当前站点URL添加到书签
    GM_addValueChangeListener(BOOKMARK_SIGNAL_KEY, function(name, oldValue, newValue, remote) {
        if (!remote) return;

        const bookmarkKey = getGV(CURRENT_BOOKMARK_KEY);
        if (!bookmarkKey) return;

        // 检查:如果勾选站点的第一个问题不等于当前站点的,则不加到同步关系里
        const bookmarkData = getBookmarkData(bookmarkKey);
        if (!bookmarkData) return;
        const bookmarkQuestion = normalizeQuestionText(bookmarkData.question || '');
        const currentSiteFirstQuestion = normalizeQuestionText(getFirstQuestionContent() || '');
        if (bookmarkQuestion !== currentSiteFirstQuestion) {
            console.log(curDate() + "书签: 当前站点第一个问题与书签问题不一致,不添加到同步关系");
            return;
        }

        const currentUrl = getUrl();
        updateBookmarkData(bookmarkKey, site, currentUrl);
        console.log(curDate() + "书签: 收到创建信号,已添加URL");
    });

    // 监听书签跳转信号:如果包含当前站点,执行跳转
    GM_addValueChangeListener(BOOKMARK_JUMP_SIGNAL_KEY, function(name, oldValue, newValue, remote) {
        if (!remote || !newValue) return;

        const { jumpData, timestamp } = newValue;
        if (!jumpData || !jumpData[site]) return;

        const targetUrl = jumpData[site];
        const currentUrl = getUrl();
        if (currentUrl !== targetUrl) {
            console.log(curDate() + `书签跳转: 从 ${currentUrl} 跳转到 ${targetUrl}`);
            window.location.href = targetUrl;
        }
    });

    // 监听单站点跳转请求:当前站点收到跳转请求时,返回确认并执行跳转
    const siteJumpRequestKey = SITE_JUMP_REQUEST_PREFIX + site;
    GM_addValueChangeListener(siteJumpRequestKey, function(name, oldValue, newValue, remote) {
        if (!remote || !newValue) return;

        const { url, timestamp } = newValue;
        if (!url) return;

        // 立即返回确认信号
        const ackKey = SITE_JUMP_ACK_PREFIX + site;
        setGV(ackKey, { timestamp: Date.now() });

        // 判断是否需要跳转
        const currentUrl = getUrl();
        if (currentUrl !== url) {
            console.log(curDate() + `单站点跳转: 从 ${currentUrl} 跳转到 ${url}`);
            window.location.href = url;
        }
    });

    /**
     * 发送跳转信号,通知所有站点跳转到书签记录的URL
     */
    function sendJumpSignalToAll(sites) {
        const jumpData = {};
        sites.forEach(s => {
            // 从路径部分拼接完整URL
            jumpData[s.site] = buildFullUrl(s.url, s.site);
        });

        setGV(BOOKMARK_JUMP_SIGNAL_KEY, {
            jumpData: jumpData,
            timestamp: Date.now()
        });

        // 当前站点直接跳转(不依赖监听器的remote检查)
        if (jumpData[site]) {
            const currentUrl = getUrl();
            if (currentUrl !== jumpData[site]) {
                console.log(curDate() + `书签跳转: 当前站点从 ${currentUrl} 跳转到 ${jumpData[site]}`);
                window.location.href = jumpData[site];
            }
        }
        console.log(curDate() + `书签: 已发送一键跳转信号`);
    }

    /**
     * 跳转到指定站点
     * @param {Object} siteInfo - 站点信息 {site: 站点ID, url: 路径部分}
     */
    function jumpToSite(siteInfo) {
        // 从路径部分拼接完整URL
        const fullUrl = buildFullUrl(siteInfo.url, siteInfo.site);
        
        // 当前站点:直接跳转
        if (siteInfo.site === site) {
            const currentUrl = getUrl();
            if (currentUrl !== fullUrl) {
                window.location.href = fullUrl;
            }
            return;
        }

        // 其他站点:发送跳转请求并等待确认
        const requestKey = SITE_JUMP_REQUEST_PREFIX + siteInfo.site;
        const ackKey = SITE_JUMP_ACK_PREFIX + siteInfo.site;
        const siteName = siteToWord[siteInfo.site] || siteInfo.site;

        // 发送跳转请求
        setGV(requestKey, {
            url: fullUrl,
            timestamp: Date.now()
        });

        // 监听确认信号
        let ackReceived = false;
        const listener = GM_addValueChangeListener(ackKey, function(name, oldValue, newValue, remote) {
            if (newValue && newValue.timestamp) {
                ackReceived = true;
                console.log(curDate() + `站点 ${siteName} 已打开,等待其自行跳转`);
            }
        });

        // 超时检查
        setTimeout(() => {
            if (!ackReceived) {
                // 未收到确认,说明站点未打开,新开页面
                console.log(curDate() + `站点 ${siteName} 未打开,新开页面`);
                window.open(fullUrl, '_blank');
            }
            // 移除监听器(如果支持)
            try {
                if (listener && typeof listener.removeListener === 'function') {
                    listener.removeListener();
                }
            } catch (e) {
            }
        }, SITE_JUMP_TIMEOUT);
    }

    // ──────────────────────────────────────────────────────────────────────
    // 13.3 书签CRUD操作
    // ──────────────────────────────────────────────────────────────────────

    /**
     * 创建书签(内部函数)
     */
    function createBookmark(questionText) {
        const bookmarkKey = generateBookmarkKey();
        const currentUrl = getUrl();

        // 存储书签key
        setGV(CURRENT_BOOKMARK_KEY, bookmarkKey);
        addBookmarkKeyToList(bookmarkKey);
        console.log(curDate() + "书签: 创建新书签", bookmarkKey);

        // 添加当前站点的URL(同时保存question)
        updateBookmarkData(bookmarkKey, site, currentUrl, questionText);

        // 发送信号通知其他站点添加URL
        setGV(BOOKMARK_SIGNAL_KEY, Date.now());

        // 延迟显示提示,等待其他站点响应后获取实际添加的站点列表
        setTimeout(() => {
            const bookmarkData = getBookmarkData(bookmarkKey);
            const actualSites = bookmarkData ? (bookmarkData.sites || []).map(item => item.site) : [];
            const siteNames = actualSites.map(s => siteToWord[s] || s).join(', ');
            showMessagePopup(`书签已创建!\n【关联站点】${siteNames}\n【书签名】${questionText}`, null, {
                text: '立即查看书签列表',
                onClick: () => showBookmarkWindow(DEFAULT_GROUP_ID, true)
            });
        }, 1500);
    }

    /**
     * 点击书签按钮时触发
     */
    function onBookmarkButtonClick() {
        const firstQuestion = getFirstQuestionContent();
        if (!firstQuestion) {
            showMessagePopup("当前页面没有提问内容,无法创建书签");
            return;
        }

        const currentUrl = getUrl();

        // 检查是否已存在相同question的书签(通过遍历所有书签)
        const allBookmarks = getAllBookmarks();
        const existingBookmark = allBookmarks.find(b => normalizeQuestionText(b.question) === normalizeQuestionText(firstQuestion));

        if (existingBookmark) {
            // 检查是否真的重复:相同site且相同url(比较路径部分)
            const currentUrlPath = extractUrlPath(currentUrl);
            const isReallyDuplicate = existingBookmark.sites.some(item => item.site === site && item.url === currentUrlPath);

            if (isReallyDuplicate) {
                // 真的重复,提示用户
                showMessagePopup("该书签已存在,无需重复创建");
                return;
            }
        }

        // 其他情况(不重复或只是bookmarkKey重复但site/url不同),直接创建
        createBookmark(firstQuestion);
    }

    /**
     * 添加书签key到列表
     */
    function addBookmarkKeyToList(bookmarkKey) {
        let keyList = getGV(BOOKMARK_KEY_LIST) || [];
        if (!keyList.includes(bookmarkKey)) {
            keyList.push(bookmarkKey);
            setGV(BOOKMARK_KEY_LIST, keyList);
        }
    }

    /**
     * 删除书签
     */
    function removeBookmark(bookmarkKey) {
        // 列表中移除、移除 json
        let keyList = getGV(BOOKMARK_KEY_LIST) || [];
        keyList = keyList.filter(k => k !== bookmarkKey);
        setGV(BOOKMARK_KEY_LIST, keyList);

        GM_deleteValue(bookmarkKey);
        console.log(curDate() + `书签: 已删除 ${bookmarkKey}`);
    }

    /**
     * 移动书签在列表中的位置(上移或下移)
     */
    function moveBookmarkInList(bookmarkKey, direction) {
        let keyList = getGV(BOOKMARK_KEY_LIST) || [];
        const currentIndex = keyList.indexOf(bookmarkKey);
        
        if (currentIndex === -1) {
            console.log(curDate() + `书签: 未找到书签 ${bookmarkKey}`);
            return false;
        }

        let newIndex;
        if (direction === 'top') {
            if (currentIndex === 0) return false; // 已经在最顶部
            // 移除当前元素,插入到最前面
            keyList.splice(currentIndex, 1);
            keyList.unshift(bookmarkKey);
            setGV(BOOKMARK_KEY_LIST, keyList);
            console.log(curDate() + `书签: 置顶 ${bookmarkKey}`);
            return true;
        } else if (direction === 'bottom') {
            if (currentIndex === keyList.length - 1) return false; // 已经在最底部
            // 移除当前元素,插入到最后面(显示时在最上面)
            keyList.splice(currentIndex, 1);
            keyList.push(bookmarkKey);
            setGV(BOOKMARK_KEY_LIST, keyList);
            console.log(curDate() + `书签: 移到底部(显示置顶) ${bookmarkKey}`);
            return true;
        } else if (direction === 'up') {
            if (currentIndex === 0) return false; // 已经在最顶部
            newIndex = currentIndex - 1;
        } else if (direction === 'down') {
            if (currentIndex === keyList.length - 1) return false; // 已经在最底部
            newIndex = currentIndex + 1;
        } else {
            return false;
        }

        // 交换位置
        [keyList[currentIndex], keyList[newIndex]] = [keyList[newIndex], keyList[currentIndex]];
        setGV(BOOKMARK_KEY_LIST, keyList);
        
        console.log(curDate() + `书签: ${direction === 'up' ? '上移' : '下移'} ${bookmarkKey}`);
        return true;
    }

    /**
     * 更新书签的提问内容
     */
    function updateBookmarkQuestion(bookmarkKey, newQuestion) {
        // 获取书签数据
        const data = getBookmarkData(bookmarkKey);
        if (!data || !data.sites || data.sites.length === 0) {
            console.log(curDate() + `书签: 未找到书签数据 ${bookmarkKey}`);
            return false;
        }

        // 直接更新question字段,key不变
        setBookmarkData(bookmarkKey, data.sites, data.group, newQuestion);
        console.log(curDate() + `书签: 已更新提问内容 ${bookmarkKey}`);
        return true;
    }

    // ──────────────────────────────────────────────────────────────────────
    // 13.4 数据管理(分组和书签数据存取)
    // ──────────────────────────────────────────────────────────────────────

    /**
     * 分组管理函数
     */
    // 获取分组列表(对象数组:{id, name})
    function getBookmarkGroups() {
        let groups = getGV(BOOKMARK_GROUP_LIST) || [];
        // 确保默认分组存在
        const hasDefault = groups.some(g => g.id === DEFAULT_GROUP_ID);
        if (!hasDefault) {
            groups.unshift({ id: DEFAULT_GROUP_ID, name: DEFAULT_GROUP_NAME });
            setGV(BOOKMARK_GROUP_LIST, groups);
        }
        return groups;
    }

    // 根据代号获取分组名称
    function getGroupNameById(groupId) {
        const groups = getBookmarkGroups();
        const group = groups.find(g => g.id === groupId);
        return group ? group.name : DEFAULT_GROUP_NAME;
    }

    // 根据名称获取分组代号
    function getGroupIdByName(groupName) {
        if (groupName === '全部') return null;
        const groups = getBookmarkGroups();
        const group = groups.find(g => g.name === groupName);
        return group ? group.id : DEFAULT_GROUP_ID;
    }

    // 分组背景色数组(用于区分不同分组)
    const GROUP_BACKGROUND_COLORS = [
        '#f5f5f5', // 默认灰色
        '#e3f2fd', // 浅蓝色
        '#f3e5f5', // 浅紫色
        '#e8f5e9', // 浅绿色
        '#fff3e0', // 浅橙色
        '#fce4ec', // 浅粉色
        '#e0f2f1', // 浅青色
        '#fff9c4', // 浅黄色
        '#f1f8e9', // 浅黄绿色
        '#ede7f6', // 浅蓝紫色
        '#e8eaf6'  // 浅靛蓝色
    ];

    // 根据分组ID获取对应的背景色
    function getGroupBackgroundColor(groupId) {
        if (groupId === null || groupId === undefined) {
            return '#f5f5f5'; // 默认灰色
        }
        const index = Math.abs(groupId) % GROUP_BACKGROUND_COLORS.length;
        return GROUP_BACKGROUND_COLORS[index];
    }

    // 添加分组
    function addBookmarkGroup(groupName) {
        if (!groupName || !groupName.trim()) {
            return false;
        }
        const trimmedName = groupName.trim();
        const groups = getBookmarkGroups();
        // 检查名称是否已存在
        if (groups.some(g => g.name === trimmedName)) {
            return false; // 已存在
        }
        // 计算新的代号(自增)
        const maxId = groups.length > 0 ? Math.max(...groups.map(g => g.id)) : DEFAULT_GROUP_ID;
        const newId = maxId + 1;
        groups.push({ id: newId, name: trimmedName });
        setGV(BOOKMARK_GROUP_LIST, groups);
        return true;
    }

    // 删除分组
    function deleteBookmarkGroup(groupName) {
        if (groupName === DEFAULT_GROUP_NAME) {
            return false; // 不能删除默认分组
        }
        const groups = getBookmarkGroups();
        const groupToDelete = groups.find(g => g.name === groupName);
        if (!groupToDelete) {
            return false;
        }
        const groupId = groupToDelete.id;
        // 从列表中删除
        const index = groups.findIndex(g => g.id === groupId);
        if (index === -1) {
            return false;
        }
        groups.splice(index, 1);
        setGV(BOOKMARK_GROUP_LIST, groups);
        
        // 将该分组下的所有书签移到默认分组
        const keyList = getGV(BOOKMARK_KEY_LIST) || [];
        keyList.forEach(key => {
            const data = getBookmarkData(key);
            if (data && data.group === groupId) {
                setBookmarkData(key, data.sites, DEFAULT_GROUP_ID);
            }
        });
        return true;
    }

    /**
     * 获取书签数据
     */
    function getBookmarkData(bookmarkKey) {
        const data = getGV(bookmarkKey);
        if (!data) return null;
        return {
            sites: data.sites || [],
            group: (typeof data.group === 'number') ? data.group : DEFAULT_GROUP_ID,
            question: data.question || ''
        };
    }

    /**
     * 设置书签数据(group使用代号)
     */
    function setBookmarkData(bookmarkKey, sites, group, question) {
        setGV(bookmarkKey, {
            sites: sites || [],
            group: (typeof group === 'number') ? group : DEFAULT_GROUP_ID,
            question: question || ''
        });
    }

    /**
     * 设置书签分组(group使用代号)
     */
    function setBookmarkGroup(bookmarkKey, groupId) {
        const data = getBookmarkData(bookmarkKey);
        if (!data) return false;
        setBookmarkData(bookmarkKey, data.sites, groupId || DEFAULT_GROUP_ID, data.question);
        return true;
    }

    /**
     * 获取所有书签数据
     */
    function getAllBookmarks(filterGroupId = null) {
        const keyList = getGV(BOOKMARK_KEY_LIST) || [];
        const bookmarks = [];
        // 倒序遍历,让最新的在上面
        for (let i = keyList.length - 1; i >= 0; i--) {
            const key = keyList[i];
            const data = getBookmarkData(key);
            if (data && data.sites && data.sites.length > 0) {
                // 如果指定了分组过滤,则只返回该分组的书签(使用代号比较)
                if (filterGroupId !== null && data.group !== filterGroupId) {
                    continue;
                }
                // 从value中读取question
                const question = data.question || '';
                // 返回时转换为名称显示
                bookmarks.push({ 
                    question, 
                    sites: data.sites,
                    group: getGroupNameById(data.group || DEFAULT_GROUP_ID),
                    groupId: data.group || DEFAULT_GROUP_ID, // 同时保存代号用于操作
                    bookmarkKey: key
                });
            }
        }
        return bookmarks;
    }

    // ──────────────────────────────────────────────────────────────────────
    // 13.5 UI界面
    // ──────────────────────────────────────────────────────────────────────

    /**
     * 处理编辑书签问题的点击事件
     */
    function editBookmarkQuestion(question, bookmarkKey, questionText, editBtn, questionContainer) {
        // 创建编辑输入框
        const input = document.createElement('textarea');
        input.value = question;
        input.style.cssText = SETTINGS_STYLES.inputTextarea;

        // 替换显示
        questionText.style.display = 'none';
        editBtn.style.display = 'none';

        const inputContainer = createTag('div', "", 'flex:1;display:flex;gap:4px;align-items:flex-start');
        inputContainer.appendChild(input);

        // 保存按钮
        const saveBtn = createTag('button', '✓', SETTINGS_STYLES.saveBtn);
        saveBtn.title = '保存';
        saveBtn.addEventListener('mouseenter', () => saveBtn.style.opacity = '0.85');
        saveBtn.addEventListener('mouseleave', () => saveBtn.style.opacity = '1');
        inputContainer.appendChild(saveBtn);

        questionContainer.appendChild(inputContainer);

        // 保存逻辑
        saveBtn.addEventListener('click', () => {
            const newQuestion = input.value.trim();
            if (!newQuestion) {
                showMessagePopup('提问内容不能为空');
                return;
            }

            if (updateBookmarkQuestion(bookmarkKey, newQuestion)) {
                // 刷新弹窗
                showBookmarkWindow();
            }
        });

        // 自动聚焦
        input.focus();
        input.select();
    }

    /**
     * 创建排序列
     * @param {number} index - 当前索引
     */
    function createSortColumn(bookmarkKey, index, bookmarks, currentGroupId, tr) {
        const TD_SORT_STYLE = 'padding:10px;vertical-align:top;white-space:nowrap;text-align:center';
        const TOP_BTN_STYLE = 'padding:4px 8px;background:transparent;border:none;cursor:pointer;font-size:16px;margin-right:4px;color:#3498db';
        const UP_BTN_STYLE = 'padding:4px 8px;background:transparent;border:none;cursor:pointer;font-size:16px;margin-right:4px;color:#95a5a6';
        const DOWN_BTN_STYLE = 'padding:4px 8px;background:transparent;border:none;cursor:pointer;font-size:16px;color:#95a5a6';

        const tdSort = createTag('td', "", TD_SORT_STYLE);

        // 置顶按钮(显示中最上面,存储中移到最后)
        const topBtn = createTag('button', '⬆', TOP_BTN_STYLE);
        topBtn.title = '置顶';
        // 显示中 index=0 对应存储列表的最后一个,如果已经在最后则不能置顶
        const keyList = getGV(BOOKMARK_KEY_LIST) || [];
        const storageIndex = keyList.indexOf(bookmarkKey);
        const canMoveTop = storageIndex !== -1 && storageIndex < keyList.length - 1;
        if (!canMoveTop) {
            topBtn.style.opacity = '0.5';
            topBtn.style.cursor = 'not-allowed';
        } else {
            topBtn.addEventListener('click', () => {
                if (moveBookmarkInList(bookmarkKey, 'bottom')) {
                    showBookmarkWindow(currentGroupId);
                }
            });
        }

        // 上移按钮(显示中向上,存储中向下)
        const upBtn = createTag('button', '↑', UP_BTN_STYLE);
        upBtn.title = '上移';
        const canMoveUp = index > 0;
        if (!canMoveUp) {
            upBtn.style.opacity = '0.5';
            upBtn.style.cursor = 'not-allowed';
        } else {
            upBtn.addEventListener('click', () => {
                // 显示中向上 = 存储中向下
                if (moveBookmarkInList(bookmarkKey, 'down')) {
                    showBookmarkWindow(currentGroupId);
                }
            });
        }

        // 下移按钮(显示中向下,存储中向上)
        const downBtn = createTag('button', '↓', DOWN_BTN_STYLE);
        downBtn.title = '下移';
        const canMoveDown = index < bookmarks.length - 1;
        if (!canMoveDown) {
            downBtn.style.opacity = '0.5';
            downBtn.style.cursor = 'not-allowed';
        } else {
            downBtn.addEventListener('click', () => {
                // 显示中向下 = 存储中向上
                if (moveBookmarkInList(bookmarkKey, 'up')) {
                    showBookmarkWindow(currentGroupId);
                }
            });
        }

        appendSeveral(tdSort, topBtn, upBtn, downBtn);
        tr.appendChild(tdSort);
    }

    /**
     * 创建站点列单元格
     */
    function createSitesColumn(sites, bookmarkKey, currentGroupId, linkStyle) {
        const tdSites = createTag('td', "", 'max-width:300px;padding:5px;vertical-align:middle');
        
        // 按星标状态排序:星标的在前
        const sortedSites = [...sites].sort((a, b) => {
            const aStarred = a.starred ? 1 : 0;
            const bStarred = b.starred ? 1 : 0;
            return bStarred - aStarred;
        });
        
        sortedSites.forEach(s => {
            // 站点链接容器
            const siteContainer = createTag('div', "", 'display:inline-flex;align-items:center;margin-right:5px;margin-bottom:2px;position:relative');
            
            // 星标emoji(如果已星标)
            if (s.starred) {
                const starEmoji = createTag('span', '⭐', 'margin-right:2px;font-size:14px');
                siteContainer.appendChild(starEmoji);
            }
            
            // 站点链接
            const siteName = siteToWord[s.site] || s.site;
            const link = createTag('a', siteName, linkStyle);
            // 从路径部分拼接完整URL
            link.href = buildFullUrl(s.url, s.site);
            link.style.marginRight = '4px';
            link.addEventListener('click', (e) => {
                e.preventDefault();
                jumpToSite(s);
            });
            siteContainer.appendChild(link);
            
            // 三点按钮(悬停时显示)
            const moreBtn = createTag('button', '⋮', 'padding:2px 3px;background:transparent;border:none;cursor:pointer;font-size:20px;color:#666;opacity:0;transition:opacity 0.2s;vertical-align:middle;line-height:1');
            moreBtn.title = '更多操作';
            
            // 菜单容器
            const menuContainer = createTag('div', "", 'position:absolute;top:100%;right:0;background:white;border:1px solid #ddd;border-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,0.15);z-index:1000;display:none;min-width:120px;margin-top:4px');
            
            // 点击外部关闭菜单的处理器
            let closeMenuHandler = null;
            
            // 悬停显示三点按钮
            siteContainer.addEventListener('mouseenter', () => {
                moreBtn.style.opacity = '1';
            });
            siteContainer.addEventListener('mouseleave', () => {
                setTimeout(() => {
                    moreBtn.style.opacity = '0';
                    menuContainer.style.display = 'none';
                }, 500);
            });
            
            // 星标按钮
            const starBtn = createTag('button', s.starred ? '⭐ 取消星标' : '⭐ 设为星标', 'width:100%;padding:8px 12px;background:transparent;border:none;cursor:pointer;text-align:left;font-size:13px;color:#333');
            starBtn.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                // 关闭菜单
                menuContainer.style.display = 'none';
                if (closeMenuHandler) {
                    document.removeEventListener('click', closeMenuHandler);
                    closeMenuHandler = null;
                }
                if (toggleSiteStarred(bookmarkKey, s.site)) {
                    showBookmarkWindow(currentGroupId);
                }
            });
            starBtn.addEventListener('mouseenter', () => starBtn.style.backgroundColor = '#f5f5f5');
            starBtn.addEventListener('mouseleave', () => starBtn.style.backgroundColor = 'transparent');
            menuContainer.appendChild(starBtn);
            
            // 移除按钮(只有当站点数量大于1时才显示)
            if (sites.length > 1) {
                const removeBtn = createTag('button', '🗑️ 移除', 'width:100%;padding:8px 12px;background:transparent;border:none;cursor:pointer;text-align:left;font-size:13px;color:#333;border-top:1px solid #eee');
                removeBtn.addEventListener('click', (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    // 关闭菜单
                    menuContainer.style.display = 'none';
                    if (closeMenuHandler) {
                        document.removeEventListener('click', closeMenuHandler);
                        closeMenuHandler = null;
                    }
                    if (removeSiteFromBookmark(bookmarkKey, s.site)) {
                        showBookmarkWindow(currentGroupId);
                    }
                });
                removeBtn.addEventListener('mouseenter', () => removeBtn.style.backgroundColor = '#f5f5f5');
                removeBtn.addEventListener('mouseleave', () => removeBtn.style.backgroundColor = 'transparent');
                menuContainer.appendChild(removeBtn);
            }
            
            // 点击三点按钮显示/隐藏菜单
            moreBtn.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                const isVisible = menuContainer.style.display === 'block';
                if (isVisible) {
                    menuContainer.style.display = 'none';
                    if (closeMenuHandler) {
                        document.removeEventListener('click', closeMenuHandler);
                        closeMenuHandler = null;
                    }
                } else {
                    menuContainer.style.display = 'block';
                    // 添加点击外部关闭菜单的监听器
                    if (!closeMenuHandler) {
                        closeMenuHandler = (e) => {
                            if (!siteContainer.contains(e.target)) {
                                menuContainer.style.display = 'none';
                                document.removeEventListener('click', closeMenuHandler);
                                closeMenuHandler = null;
                            }
                        };
                        // 使用setTimeout确保当前点击事件处理完成后再添加监听器
                        setTimeout(() => {
                            document.addEventListener('click', closeMenuHandler);
                        }, 0);
                    }
                }
            });
            
            // 右键三点按钮显示菜单
            moreBtn.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                e.stopPropagation();
                menuContainer.style.display = 'block';
                // 设置菜单位置
                const rect = moreBtn.getBoundingClientRect();
                menuContainer.style.top = rect.bottom + 'px';
                menuContainer.style.right = '0';
                // 添加点击外部关闭菜单的监听器
                if (!closeMenuHandler) {
                    closeMenuHandler = (e) => {
                        if (!siteContainer.contains(e.target)) {
                            menuContainer.style.display = 'none';
                            document.removeEventListener('click', closeMenuHandler);
                            closeMenuHandler = null;
                        }
                    };
                    setTimeout(() => {
                        document.addEventListener('click', closeMenuHandler);
                    }, 0);
                }
            });
            
            siteContainer.appendChild(moreBtn);
            siteContainer.appendChild(menuContainer);
            tdSites.appendChild(siteContainer);
        });
        
        return tdSites;
    }

    /**
     * 渲染书签列表内容(表格部分)
     * @param {HTMLElement} content - 内容容器
     * @param {number|null} currentGroupId - 当前选中的分组代号
     * @param {Array} groups - 分组列表
     */
    function renderTableOfBookmark(content, currentGroupId, groups) {
        // CSS样式变量(属性超过2个的样式)
        const EMPTY_TABLE_TIP_STYLE = 'color:#666;text-align:center;padding:20px';
        const TABLE_STYLE = 'width:100%;border-collapse:collapse;font-size:14px';
        const TH_STYLE = 'padding:10px;text-align:left;border-bottom:2px solid #ddd';
        const TD_STYLE = 'padding:10px;vertical-align:top;white-space:nowrap';
        const TD_QUESTION_STYLE = 'padding:10px;max-width:300px;word-break:break-all;vertical-align:top;position:relative';
        const QUESTION_CONTAINER_STYLE = 'display:flex;align-items:center;gap:0px';
        const EDIT_BTN_STYLE = 'padding:4px;background:transparent;border:none;cursor:pointer;font-size:16px;flex-shrink:0;color:#666';
        const LINK_STYLE = 'color:#1e3a8a;text-decoration:none;margin-right:auto 10px;cursor:pointer';
        const JUMP_BTN_STYLE = 'padding:6px 12px;background:#f5f5f5;color:#000;border:1px solid #ddd;border-radius:4px;cursor:pointer;font-size:12px;margin-right:8px';
        const DEL_BTN_STYLE = 'padding:6px 10px;background:transparent;border:none;cursor:pointer;font-size:18px;color:#ff6b6b';
        const GROUP_SELECT_BASE_STYLE = 'padding:4px 8px;border:1px solid #ddd;border-radius:4px;font-size:13px;cursor:pointer;min-width:50px';

        // 获取书签(根据选中的分组过滤,使用代号)
        const bookmarks = getAllBookmarks(currentGroupId);

        if (bookmarks.length === 0) {
            const emptyTableTip = createHtml('p', '暂无多选同步提问的书签,点击屏幕边缘的书签按钮可添加书签', EMPTY_TABLE_TIP_STYLE);
            content.appendChild(emptyTableTip);
            return;
        }

        // 创建表格
        const table = createTag('table', "", TABLE_STYLE);

        // 表头
        let theadHtml = `<tr style="background:#f5f5f5"><th style="${TH_STYLE}">分组</th><th style="${TH_STYLE}">提问</th><th style="${TH_STYLE}">站点链接</th><th style="${TH_STYLE}">操作</th><th style="${TH_STYLE}">排序</th></tr>`;
        const thead = createHtml('thead', theadHtml, '');
        table.appendChild(thead);

        // 表体
        const tbody = createTag('tbody', "", "");
        bookmarks.forEach((bookmark, index) => {
            const tr = createTag('tr', "", 'border-bottom:1px solid #eee');

            const bookmarkKey = bookmark.bookmarkKey;
            const bookmarkGroupName = bookmark.group || DEFAULT_GROUP_NAME;
            const bookmarkGroupId = bookmark.groupId !== undefined ? bookmark.groupId : getGroupIdByName(bookmarkGroupName);

            // 1、分组列
            const groupBgColor = getGroupBackgroundColor(bookmarkGroupId);
            const tdGroup = createTag('td', "", TD_STYLE);
            const groupSelect = document.createElement('select');
            groupSelect.style.cssText = `${GROUP_SELECT_BASE_STYLE};background:${groupBgColor}`;

            // 添加所有分组选项(显示名称,但value存储代号)
            groups.forEach(group => {
                const option = document.createElement('option');
                option.value = group.id;
                option.textContent = group.name;
                groupSelect.appendChild(option);
            });

            // 选中当前分组(必须在 options 都加完之后再设,使用代号)
            groupSelect.value = bookmarkGroupId;

            groupSelect.addEventListener('change', (e) => {
                const newGroupId = parseInt(e.target.value);
                if (setBookmarkGroup(bookmarkKey, newGroupId)) {
                    // 保持当前Tab选中状态
                    showBookmarkWindow(currentGroupId);
                }
            });

            tdGroup.appendChild(groupSelect);
            tr.appendChild(tdGroup);

            // 2、提问列
            const tdQuestion = createTag('td', "", TD_QUESTION_STYLE);

            // 提问内容容器
            const questionContainer = createTag('div', "", QUESTION_CONTAINER_STYLE);

            // 提问文本
            const SUB_LEN = 80;
            const questionTextContent = bookmark.question.length > SUB_LEN ? bookmark.question.substring(0, SUB_LEN) + '...' : bookmark.question;
            const questionText = createTag('span', questionTextContent, 'flex:1;word-break:break-all');
            questionText.title = bookmark.question;
            questionContainer.appendChild(questionText);

            // 编辑按钮
            const editBtn = createTag('button', '✏️', EDIT_BTN_STYLE);
            editBtn.title = '编辑提问内容';
            editBtn.addEventListener('click', () => {
                editBookmarkQuestion(bookmark.question, bookmarkKey, questionText, editBtn, questionContainer);
            });

            questionContainer.appendChild(editBtn);
            tdQuestion.appendChild(questionContainer);
            tr.appendChild(tdQuestion);

            // 3、站点列
            const tdSites = createSitesColumn(bookmark.sites, bookmarkKey, currentGroupId, LINK_STYLE);
            tr.appendChild(tdSites);

            // 4、操作列
            const tdAction = createTag('td', "", TD_STYLE);

            // 一键跳转按钮
            const jumpBtn = createTag('button', '一键跳转', JUMP_BTN_STYLE);
            jumpBtn.title = '前提是已打开各家网页(任意页面皆可)';
            jumpBtn.addEventListener('click', () => sendJumpSignalToAll(bookmark.sites));
            jumpBtn.addEventListener('mouseenter', () => jumpBtn.style.opacity = '0.85');
            jumpBtn.addEventListener('mouseleave', () => jumpBtn.style.opacity = '1');

            // 删除按钮
            const delBtn = createTag('button', '✕', DEL_BTN_STYLE);
            delBtn.title = '删除此书签,无法恢复';
            delBtn.addEventListener('click', () => {
                removeBookmark(bookmarkKey);
                showBookmarkWindow();
            });
            appendSeveral(tdAction, jumpBtn, delBtn);

            tr.appendChild(tdAction);

            // 5、排序列
            createSortColumn(bookmarkKey, index, bookmarks, currentGroupId, tr);

            tbody.appendChild(tr);
        });
        table.appendChild(tbody);
        content.appendChild(table);
    }

    /**
     * 显示书签列表弹窗
     * @param {number|null} selectedGroupId - 选中的分组代号,null表示"全部"
     * @param {boolean} skipSaveGroup - 是否跳过保存分组选择,true时不更新GV
     */
    function showBookmarkWindow(selectedGroupId = null, skipSaveGroup = false) {
        // CSS样式变量(属性超过2个的样式)
        const POPUP_SIZE_STYLE = 'width:60%;height:80%;overflow:auto';
        const HEADER_STYLE = 'display:flex;justify-content:space-between;align-items:center;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid #eee';
        const CLOSE_BTN_STYLE = 'cursor:pointer;font-size:20px;color:#999;padding:5px';
        const TAB_BASE_STYLE = 'padding:6px 12px;border-radius:4px;cursor:pointer;font-size:13px;color:#333';
        const TAB_CONTAINER_STYLE = 'display:flex;align-items:center;gap:8px;margin-bottom:15px;padding-bottom:10px;border-bottom:1px solid #eee;flex-wrap:wrap';
        const ADD_GROUP_BTN_STYLE = 'padding:6px 12px;background:#4caf50;color:white;border:none;border-radius:4px;cursor:pointer;font-size:13px';

        // 获取分组列表(提前获取,避免重复调用)
        const groups = getBookmarkGroups();
        
        // 如果selectedGroupId为null(首次打开),从存储中读取上次选中的分组ID
        // 但如果skipSaveGroup为true,则跳过读取,直接使用null(用于"立即查看书签列表"按钮)
        if (selectedGroupId === null && !skipSaveGroup) {
            const lastSelectedGroupId = getGV(BOOKMARK_LAST_SELECTED_GROUP);
            if (lastSelectedGroupId !== null && lastSelectedGroupId !== undefined) {
                // 检查该分组是否还存在
                const groupExists = groups.some(g => g.id === lastSelectedGroupId);
                if (groupExists) {
                    selectedGroupId = lastSelectedGroupId;
                } else {
                    // 分组不存在,清除保存的值,使用null(显示全部)
                    setGV(BOOKMARK_LAST_SELECTED_GROUP, null);
                }
            }
        }
        
        // 检查弹窗是否已存在
        const existingPopup = document.getElementById('bookmark-popup');
        let popup, content;
        
        if (existingPopup) {
            // 弹窗已存在,复用它(避免闪烁)
            popup = existingPopup;
            content = popup.firstElementChild; // content是popup的第一个子元素
            // 清空内容,准备重新填充
            if (content) {
                content.replaceChildren(); // 使用replaceChildren代替innerHTML
            }
        } else {
            // 首次创建弹窗,固定宽度60%,高度80%
            const result = createPopupBase('bookmark-popup', ';' + POPUP_SIZE_STYLE);
            popup = result.popup;
            content = result.content;
        }

        // 标题栏
        const header = createHtml('div', '<h3 style="margin:0;color:#333">📚 书签列表</h3>', HEADER_STYLE);

        const closeBtn = createTag('span', '✕', CLOSE_BTN_STYLE);
        closeBtn.onclick = () => {
            if (popup && popup.parentNode) {
                popup.remove();
            }
        };
        header.appendChild(closeBtn);
        content.appendChild(header);

        // 分组管理区域
        const currentGroupId = selectedGroupId;
        
        // Tab切换函数(统一处理保存和切换)
        // 用户主动点击tab时,应该恢复正常保存行为(skipSaveGroup设为false)
        const switchToGroup = (groupId, skipSave = false) => {
            if (!skipSave && !skipSaveGroup) {
                setGV(BOOKMARK_LAST_SELECTED_GROUP, groupId);
            }
            // 用户主动点击tab时,重置skipSaveGroup为false,恢复正常保存行为
            showBookmarkWindow(groupId, skipSave);
        };
        
        // 创建Tab函数(统一处理全部和分组tab)
        const createGroupTab = (text, groupId, isSelected, bgColor, isBold = false) => {
            const fontWeight = isBold ? 'font-weight:bold;' : '';
            const border = isSelected ? '2px solid #667eea' : '1px solid transparent';
            const tab = createTag('div', text, `${TAB_BASE_STYLE};${fontWeight}background:${bgColor};border:${border}`);
            tab.addEventListener('click', () => switchToGroup(groupId));
            return tab;
        };
        
        // Tab切换区域
        const tabContainer = createTag('div', "", TAB_CONTAINER_STYLE);
        
        // 全部Tab
        const allTab = createGroupTab('全部', null, currentGroupId === null, '#f0f0f0', true);
        tabContainer.appendChild(allTab);
        
        // 各分组Tab
        groups.forEach(group => {
            const groupBgColor = getGroupBackgroundColor(group.id);
            const groupTab = createGroupTab(group.name, group.id, currentGroupId === group.id, groupBgColor);
            tabContainer.appendChild(groupTab);
        });
        
        // 添加分组按钮
        const addGroupBtn = createTag('button', '+ 添加分组', ADD_GROUP_BTN_STYLE);
        addGroupBtn.title = '添加新分组';
        addGroupBtn.addEventListener('click', () => {
            const groupName = prompt('请输入分组名称:');
            if (groupName && groupName.trim()) {
                if (addBookmarkGroup(groupName.trim())) {
                    showBookmarkWindow(currentGroupId);
                } else {
                    showMessagePopup('分组名称已存在或无效');
                }
            }
        });
        addGroupBtn.addEventListener('mouseenter', () => addGroupBtn.style.opacity = '0.85');
        addGroupBtn.addEventListener('mouseleave', () => addGroupBtn.style.opacity = '1');
        tabContainer.appendChild(addGroupBtn);
        
        content.appendChild(tabContainer);

        // 渲染书签列表
        renderTableOfBookmark(content, currentGroupId, groups);
    }

    // 初始化书签按钮
    setTimeout(() => {
        if (isBookmarkFeatureEnabled()) {
            createAddButtonOfBookmark();
            createViewButtonOfBookmark();
        }
        updateButtonVisibility(); // 根据设置更新按钮显示状态
    }, 1000);

    // 创建加书签按钮
    function createAddButtonOfBookmark() {
        createButtonOfBookmark({
            id: 'bookmark-btn',
            text: '书签',
            title: '多家同步提问后的各页面,可一键加书签,方便回看',
            bottom: '0px',
            background: 'linear-gradient(135deg,#11998e 0%,#38ef7d 100%)',
            onClick: onBookmarkButtonClick
        });
    }

    // 创建查看书签按钮
    function createViewButtonOfBookmark() {
        createButtonOfBookmark({
            id: 'bookmark-view-btn',
            text: '列表',
            title: '书签列表',
            bottom: '40px',
            background: 'linear-gradient(135deg,#667eea 0%,#764ba2 100%)',
            onClick: showBookmarkWindow
        });
    }

    // 创建书签按钮(通用函数)
    function createButtonOfBookmark(config) {
        const { id, text, title, bottom, background, onClick } = config;
        const btn = document.createElement('div');
        btn.id = id;
        setInnerHTML(btn, text);
        btn.title = title;
        // 组合公共样式和动态样式
        btn.style.cssText = BOOKMARK_BTN_BASE_STYLE + `;bottom:${bottom};background:${background}`;
        btn.addEventListener('click', (e) => {
            e.preventDefault();
            e.stopPropagation();
            onClick();
        });
        document.body.appendChild(btn);
    }
})();