Greasy Fork

来自缓存

Greasy Fork is available in English.

网页划词高亮工具

提供网页划词高亮功能

// ==UserScript==
// @name         网页划词高亮工具
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  提供网页划词高亮功能
// @author       sunny43
// @license      MIT
// @match        *://*/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function () {
    'use strict';

    const STYLE_PREFIX = 'sunny43-';

    // 全局变量
    let highlights = [];
    const rawPageUrl = window.location.href;
    const rawDomain = window.location.hostname;
    const activationContext = resolveActivationContext(rawDomain, rawPageUrl);
    const activationDomain = activationContext.domain;
    const activationPageUrl = activationContext.url;
    let currentPageUrl = rawPageUrl;
    let settings = GM_getValue('highlight_settings', {
        colors: ['#ff909c', '#b89fff', '#74b4ff', '#70d382', '#ffcb7e'],
        activeColor: '#ff909c',
        minTextLength: 1,
        sidebarDescription: '高亮工具',
        sidebarWidth: 320,
        showFloatingButton: true
    });
    let savedRange = null; // 保存选区范围
    let ignoreNextClick = false; // 忽略下一次点击的标志
    let menuDisplayTimer = null; // 菜单显示定时器
    let menuOperationInProgress = false; // 添加菜单操作锁定
    function resolveActivationContext(defaultDomain, defaultUrl) {
        let domain = defaultDomain;
        let url = defaultUrl;

        let isTopWindow = true;
        try {
            isTopWindow = window.top === window.self;
        } catch (e) {
            isTopWindow = false;
        }

        if (!isTopWindow) {
            let resolvedFromTop = false;
            try {
                const topLocation = window.top.location;
                if (topLocation && topLocation.hostname) {
                    domain = topLocation.hostname;
                    resolvedFromTop = true;
                }
                if (topLocation && topLocation.href) {
                    url = topLocation.href;
                }
            } catch (e) {
                // 跨域访问顶层窗口会抛出异常,忽略
            }

            if (!resolvedFromTop && document.referrer) {
                try {
                    const refUrl = new URL(document.referrer);
                    if (refUrl.hostname) {
                        domain = refUrl.hostname;
                    }
                    if (refUrl.href) {
                        url = refUrl.href;
                    }
                } catch (e) {
                    // 忽略解析错误,继续使用默认值
                }
            }
        }

        return { domain, url };
    }

    // 启用列表
    let enabledList = GM_getValue('enabled_list', {
        domains: [],
        urls: []
    });
    // 判断当前页面是否启用:当启用列表为空时,默认启用所有页面
    const isEnabledForCurrentPage = (list) => {
        const emptyList = (!list || (Array.isArray(list.domains) && list.domains.length === 0) && (Array.isArray(list.urls) && list.urls.length === 0));
        if (emptyList) return true; // 默认开启
        return (list.domains || []).includes(activationDomain) || (list.urls || []).includes(activationPageUrl);
    };
    // 检查当前页面是否启用高亮功能
    let isHighlightEnabled = isEnabledForCurrentPage(enabledList);
    let updateSidebarHighlights = null;

    GM_addStyle(`
        /* 高亮菜单样式 */
        .${STYLE_PREFIX}highlight-menu,
        .${STYLE_PREFIX}highlight-menu * {
            all: initial;
            box-sizing: border-box;
        }
        .${STYLE_PREFIX}highlight-menu {
            position: absolute;
            background: #333336;
            border: none;
            border-radius: 24px;
            box-shadow: 0 4px 16px rgba(0,0,0,0.3);
            padding: 8px;
            z-index: 9999;
            display: flex;
            flex-direction: row;
            align-items: center;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            font-size: 13px;
            line-height: 1;
            color: #fff;
            opacity: 0;
            transition: opacity 0.2s ease-in;
            pointer-events: none;
            box-sizing: border-box;
        }
        .${STYLE_PREFIX}highlight-menu.${STYLE_PREFIX}show {
            opacity: 1;
            pointer-events: auto; /* 显示时响应事件 */
        }

        /* 菜单箭头样式 */
        .${STYLE_PREFIX}highlight-menu::after {
            content: '';
            position: absolute;
            bottom: -5px;
            left: var(--arrow-left, 50%);
            width: 0;
            height: 0;
            border-left: 6px solid transparent;
            border-right: 6px solid transparent;
            border-top: 6px solid #333336;
            margin-left: -6px;
            box-sizing: border-box;
        }
        .${STYLE_PREFIX}highlight-menu.${STYLE_PREFIX}arrow-top::after {
            top: -5px;
            bottom: auto;
            border-top: none;
            border-bottom: 6px solid #333336;
        }

        /* 颜色选择区域 */
        .${STYLE_PREFIX}highlight-menu-colors {
            display: flex;
            flex-direction: row;
            align-items: center;
            margin: 0 2px;
            flex-wrap: nowrap;
            flex: 0 0 auto;
            gap: 6px;
        }

        /* 颜色选择按钮 */
        .${STYLE_PREFIX}highlight-menu-color {
            width: 22px !important;
            height: 22px !important;
            min-width: 22px !important;
            min-height: 22px !important;
            max-width: 22px !important;
            max-height: 22px !important;
            border-radius: 50% !important;
            margin: 0 !important;
            padding: 0 !important;
            cursor: pointer;
            position: relative;
            display: flex !important;
            align-items: center;
            justify-content: center;
            transition: transform 0.15s ease;
            box-shadow: inset 0 0 0 1px rgba(255,255,255,0.12) !important;
            flex-shrink: 0 !important;
            box-sizing: border-box !important;
            border: none !important;
            outline: none !important;
        }
        .${STYLE_PREFIX}highlight-menu-color:hover {
            transform: scale(1.12);
        }
        .${STYLE_PREFIX}highlight-menu-color.${STYLE_PREFIX}active::after {
            content: "";
            width: 12px;
            height: 12px;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23333336' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: center;
            background-size: contain;
        }

        /* 菜单按钮样式已移除(当前未使用) */

        /* 闪烁效果用于高亮跳转 */
        @keyframes ${STYLE_PREFIX}highlightFlash {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.3; }
        }
        .${STYLE_PREFIX}highlight-flash {
            animation: ${STYLE_PREFIX}highlightFlash 0.5s ease 4;
            box-shadow: 0 0 0 3px rgba(255, 255, 0, 0.7) !important;
            position: relative;
            z-index: 10;
        }
    `);

    // 保存启用列表
    function saveEnabledList() {
        GM_setValue('enabled_list', enabledList);
        // 刷新当前状态(空列表表示默认启用)
        isHighlightEnabled = isEnabledForCurrentPage(enabledList);

        // 更新浮动按钮显示状态
        const floatingButton = document.getElementById(`${STYLE_PREFIX}floating-button`);
        if (floatingButton) {
            floatingButton.style.display = (settings.showFloatingButton && isHighlightEnabled) ? 'flex' : 'none';
        }
    }

    // 启用域名
    function enableDomain(domain) {
        if (!enabledList.domains.includes(domain)) {
            enabledList.domains.push(domain);
            saveEnabledList();
        }
    }

    // 启用域名
    function disableDomain(domain) {
        enabledList.domains = enabledList.domains.filter(d => d !== domain);
        saveEnabledList();
    }

    // 启用URL
    function enableUrl(url) {
        if (!enabledList.urls.includes(url)) {
            enabledList.urls.push(url);
            saveEnabledList();
        }
    }

    // 启用URL
    function disableUrl(url) {
        enabledList.urls = enabledList.urls.filter(u => u !== url);
        saveEnabledList();
    }

    function generateUrlCandidates(url) {
        const candidates = [];
        if (!url) {
            return candidates;
        }
        candidates.push(url);

        try {
            const parsed = new URL(url);
            const noHash = new URL(parsed.href);
            if (noHash.hash) {
                noHash.hash = '';
                const candidate = noHash.href.endsWith('#') ? noHash.href.slice(0, -1) : noHash.href;
                if (!candidates.includes(candidate)) {
                    candidates.push(candidate);
                }
            }

            const noSearch = new URL(noHash.href);
            if (noSearch.search) {
                noSearch.search = '';
                const candidate = noSearch.href;
                if (!candidates.includes(candidate)) {
                    candidates.push(candidate);
                }
            }

            const trimmed = noSearch.href.endsWith('/') ? noSearch.href.slice(0, -1) : noSearch.href;
            if (trimmed && !candidates.includes(trimmed)) {
                candidates.push(trimmed);
            }
        } catch (e) {
            // 忽略无法解析的 URL
        }

        return candidates;
    }

    // 加载当前页面的高亮
    function loadHighlights() {
        const allHighlights = GM_getValue('highlights', {});
        const candidates = [
            currentPageUrl,
            ...generateUrlCandidates(currentPageUrl),
            activationPageUrl,
            ...generateUrlCandidates(activationPageUrl)
        ];
        const visited = new Set();

        for (const candidate of candidates) {
            if (!candidate || visited.has(candidate)) {
                continue;
            }
            visited.add(candidate);
            const stored = allHighlights[candidate];
            if (Array.isArray(stored)) {
                currentPageUrl = candidate;
                highlights = stored;
                return highlights;
            }
        }

        highlights = [];
        return highlights;
    }

    // 保存高亮到存储
    function saveHighlights() {
        const allHighlights = GM_getValue('highlights', {});
        allHighlights[currentPageUrl] = highlights;
        GM_setValue('highlights', allHighlights);
    }

    // 保存设置
    function saveSettings() {
        GM_setValue('highlight_settings', settings);
    }

    // 移除高亮菜单
    function removeHighlightMenu() {
        const existingMenus = document.querySelectorAll(`.${STYLE_PREFIX}highlight-menu`);
        if (existingMenus.length) {
            existingMenus.forEach(menu => {
                menu.classList.remove(`${STYLE_PREFIX}show`);
                setTimeout(() => {
                    if (menu && menu.parentNode) {
                        menu.parentNode.removeChild(menu);
                    }
                }, 200);
            });
        }
        clearTimeout(menuDisplayTimer);
        ignoreNextClick = false;
        menuOperationInProgress = false;
    }

    // 统一菜单外部点击关闭逻辑
    function attachOutsideClose(menu) {
        document.addEventListener('click', function closeMenu(e) {
            if (ignoreNextClick) {
                ignoreNextClick = false;
                return;
            }
            if (!menu.contains(e.target)) {
                removeHighlightMenu();
            }
        }, { once: true });
    }

    // 高亮选中文本
    function highlightSelection(color) {
        if (!isHighlightEnabled) {
            return null;
        }
        const selection = window.getSelection();
        if (!selection.rangeCount) return null;
        const range = selection.getRangeAt(0);
        const rawSelectedText = selection.toString();
        const trimmedText = rawSelectedText.trim();
        if (!trimmedText || trimmedText.length < settings.minTextLength) {
            return null;
        }
        const selectedText = rawSelectedText;
        const highlightId = 'highlight-' + Date.now() + '-' + Math.floor(Math.random() * 10000);

        // ★ 先从未修改前的文本中提取上下文
        let prefix = '', suffix = '';
        let xpath = '', textOffset = 0, parentElement = null;
        const globalContext = collectRangeContext(range, 20);
        if (globalContext) {
            prefix = globalContext.prefix || '';
            suffix = globalContext.suffix || '';
        }

        if (range.startContainer.nodeType === Node.TEXT_NODE) {
            const originalText = range.startContainer.textContent;
            const startOffset = range.startOffset;
            const endOffset = Math.min(originalText.length, startOffset + selectedText.length);
            if (!prefix) {
                prefix = extractValidContext(originalText, startOffset, 20, "backward");
            }
            if (!suffix) {
                suffix = extractValidContext(originalText, endOffset, 20, "forward");
            }

            // 获取父元素用于生成XPath
            parentElement = range.startContainer.parentElement;
            textOffset = startOffset;

            // 生成XPath
            try {
                xpath = generateXPath(parentElement);
            } catch (e) {
                console.warn('XPath生成失败:', e);
            }
        }

        try {
            // 检查是否需要分片段处理
            const fragments = collectHighlightFragments(range, highlightId, color);

            // 包装高亮(这会处理DOM)
            wrapRangeWithHighlight(range, highlightId, color);

            if (fragments && fragments.length > 1) {
                // 多片段情况:创建主记录和片段记录
                const mainHighlight = {
                    id: highlightId,
                    text: selectedText,  // 完整文本
                    color: color,
                    timestamp: Date.now(),
                    url: currentPageUrl,
                    isMultiFragment: true,  // 标记为多片段
                    fragmentCount: fragments.length,
                    // 保留第一个片段的信息用于兼容
                    xpath: fragments[0].xpath,
                    textOffset: fragments[0].textOffset,
                    textLength: selectedText.length,
                    contextHash: generateContextHash(fragments[0].prefix, fragments[fragments.length - 1].suffix, selectedText),
                    prefix: fragments[0].prefix,
                    suffix: fragments[fragments.length - 1].suffix
                };
                highlights.push(mainHighlight);

                // 添加所有片段记录
                fragments.forEach(fragment => {
                    highlights.push(fragment);
                });

            } else {
                // 单片段或传统情况
                const highlight = {
                    id: highlightId,
                    text: selectedText,
                    color: color,
                    timestamp: Date.now(),
                    url: currentPageUrl,

                    // 优化存储结构
                    xpath: xpath,
                    textOffset: textOffset,
                    textLength: selectedText.length,
                    contextHash: generateContextHash(prefix, suffix, selectedText),

                    // 兼容性:保留原有字段
                    prefix: prefix,    // 前置上下文
                    suffix: suffix     // 后置上下文
                };

                highlights.push(highlight);
            }

            saveHighlights();

            // 点击事件在包装函数中已处理

            // 检查侧边栏是否打开,如果打开则刷新高亮列表
            const sidebar = document.getElementById(`${STYLE_PREFIX}sidebar`);
            if (sidebar && sidebar.style.right === '0px' && updateSidebarHighlights) {
                updateSidebarHighlights();
            }

            selection.removeAllRanges();
            return highlightId;
        } catch (e) {
            console.warn('高亮失败:', e);
            try {
                findAndHighlight(selectedText, color, highlightId);

                // 检查侧边栏是否打开,如果打开则刷新高亮列表
                const sidebar = document.getElementById(`${STYLE_PREFIX}sidebar`);
                if (sidebar && sidebar.style.right === '0px' && updateSidebarHighlights) {
                    updateSidebarHighlights();
                }

                return highlightId;
            } catch (error) {
                console.error('替代高亮方法也失败:', error);
                return null;
            }
        }
    }

    // 收集高亮片段信息(新增辅助函数)
    function collectHighlightFragments(range, highlightId, color) {
        const fragments = [];
        const commonAncestor = range.commonAncestorContainer;
        const blockTags = ['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'BLOCKQUOTE'];

        // 判断是否跨块级元素
        let containsBlockElement = false;
        if (commonAncestor.nodeType === Node.ELEMENT_NODE) {
            containsBlockElement = blockTags.includes(commonAncestor.tagName) ||
                                    !!commonAncestor.querySelector(blockTags.map(tag => tag.toLowerCase()).join(','));
        }

        if (!containsBlockElement) {
            // 单片段情况
            return null; // 返回null表示使用传统方式
        }

        // 多片段情况:遍历所有文本节点
        const walker = document.createTreeWalker(
            commonAncestor,
            NodeFilter.SHOW_TEXT,
            null  // 不使用过滤器,遍历所有文本节点
        );

        let node;
        let fragmentIndex = 0;
        let isInRange = false;
        let foundStart = false;
        let foundEnd = false;

        while (node = walker.nextNode()) {
            // 跳过已高亮的节点
            if (node.parentElement && node.parentElement.classList.contains(`${STYLE_PREFIX}highlight-marked`)) {
                continue;
            }

            let nodeStartOffset = 0;
            let nodeEndOffset = node.textContent.length;
            let shouldInclude = false;

            // 检查是否是起始节点
            if (node === range.startContainer) {
                nodeStartOffset = range.startOffset;
                shouldInclude = true;
                isInRange = true;
                foundStart = true;
            }

            // 检查是否是结束节点
            if (node === range.endContainer) {
                nodeEndOffset = range.endOffset;
                shouldInclude = true;
                foundEnd = true;
            }

            // 如果已经开始但还未结束,包含整个节点
            if (isInRange && !foundEnd && node !== range.startContainer) {
                shouldInclude = true;
            }

            // 收集片段信息
            if (shouldInclude && nodeEndOffset > nodeStartOffset) {
                const fragmentText = node.textContent.substring(nodeStartOffset, nodeEndOffset);
                const parentElement = node.parentElement;

                // 生成该片段的上下文
                const fragmentPrefix = extractValidContext(node.textContent, nodeStartOffset, 20, "backward");
                const fragmentSuffix = extractValidContext(node.textContent, nodeEndOffset, 20, "forward");

                let xpath = '';
                try {
                    // 如果父元素只包含一个文本节点,使用父元素的XPath
                    // 否则,为文本节点生成特定的XPath
                    const textNodes = Array.from(parentElement.childNodes).filter(n => n.nodeType === Node.TEXT_NODE);
                    if (textNodes.length === 1) {
                        xpath = generateXPath(parentElement);
                    } else {
                        // 找出当前文本节点在父元素中的索引
                        const textIndex = textNodes.indexOf(node);
                        xpath = generateXPath(parentElement) + `/text()[${textIndex + 1}]`;
                    }
                } catch (e) {
                    console.warn('片段XPath生成失败:', e);
                }

                fragments.push({
                    id: `${highlightId}-fragment-${fragmentIndex}`,
                    parentId: highlightId,
                    text: fragmentText,
                    color: color,
                    isFragment: true,
                    fragmentIndex: fragmentIndex,
                    xpath: xpath,
                    textOffset: nodeStartOffset,
                    textLength: fragmentText.length,
                    contextHash: generateContextHash(fragmentPrefix, fragmentSuffix, fragmentText),
                    prefix: fragmentPrefix,
                    suffix: fragmentSuffix,
                    timestamp: Date.now(),
                    url: currentPageUrl
                });

                fragmentIndex++;
            }

            // 如果找到了结束节点,停止遍历
            if (foundEnd) {
                break;
            }
        }

        return fragments.length > 1 ? fragments : null;
    }

    // 根据ID删除高亮
    function removeHighlightById(highlightId) {
        // 查找主高亮记录
        const mainHighlight = highlights.find(h => h.id === highlightId);

        if (mainHighlight && mainHighlight.isMultiFragment) {
            // 多片段高亮:需要删除所有片段的DOM元素
            // 先删除主ID的元素(如果有)
            const mainElements = document.querySelectorAll(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${highlightId}"]`);
            mainElements.forEach(elem => {
                const textNode = document.createTextNode(elem.textContent);
                const parent = elem.parentNode;
                if (parent) {
                    parent.replaceChild(textNode, elem);
                    // 合并相邻的文本节点
                    parent.normalize();
                }
            });

            // 再删除所有片段ID的元素
            const fragments = highlights.filter(h => h.parentId === highlightId);
            fragments.forEach(fragment => {
                const fragmentElements = document.querySelectorAll(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${fragment.id}"]`);
                fragmentElements.forEach(elem => {
                    const textNode = document.createTextNode(elem.textContent);
                    const parent = elem.parentNode;
                    if (parent) {
                        parent.replaceChild(textNode, elem);
                        // 合并相邻的文本节点
                        parent.normalize();
                    }
                });
            });

            // 删除主记录和所有片段记录
            highlights = highlights.filter(h => h.id !== highlightId && h.parentId !== highlightId);
        } else {
            // 单片段高亮:使用原逻辑
            const highlightElement = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${highlightId}"]`);
            if (highlightElement) {
                const textNode = document.createTextNode(highlightElement.textContent);
                const parent = highlightElement.parentNode;
                if (parent) {
                    parent.replaceChild(textNode, highlightElement);
                    // 合并相邻的文本节点
                    parent.normalize();
                }
            }
            highlights = highlights.filter(h => h.id !== highlightId);
        }

        saveHighlights();

        // 检查侧边栏是否打开,如果打开则刷新高亮列表
        const sidebar = document.getElementById(`${STYLE_PREFIX}sidebar`);
        if (sidebar && sidebar.style.right === '0px' && updateSidebarHighlights) {
            updateSidebarHighlights();
        }
    }

    // 使用 MutationObserver 监听 DOM 变化,动态恢复高亮
    function observeDomChanges() {
        // 检查 document.body 是否存在
        if (!document.body) {
            console.warn('document.body 不存在,无法启动 DOM 监听');
            return;
        }

        let debounceTimer;  // 新增变量用于防抖
        let isApplyingHighlights = false;  // 防止循环触发
        const observer = new MutationObserver((mutations) => {
            // 如果正在应用高亮,忽略此次变化
            if (isApplyingHighlights) {
                return;
            }

            // 过滤掉由脚本自身创建的高亮元素导致的变化
            const hasRelevantMutation = mutations.some(mutation => {
                // 忽略脚本自己创建的高亮标签变化
                if (mutation.target && mutation.target.classList &&
                    mutation.target.classList.contains(`${STYLE_PREFIX}highlight-marked`)) {
                    return false;
                }

                // 忽略父元素是高亮标签的变化
                if (mutation.target && mutation.target.parentElement &&
                    mutation.target.parentElement.classList &&
                    mutation.target.parentElement.classList.contains(`${STYLE_PREFIX}highlight-marked`)) {
                    return false;
                }

                // 忽略祖先元素是高亮标签的变化(解决嵌套高亮导致的问题)
                let parent = mutation.target;
                while (parent && parent !== document.body) {
                    if (parent.classList && parent.classList.contains(`${STYLE_PREFIX}highlight-marked`)) {
                        return false;
                    }
                    parent = parent.parentElement;
                }

                // 忽略添加的节点是脚本UI元素或高亮元素
                if (mutation.addedNodes.length > 0) {
                    for (let node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const el = node;
                            if (el.classList && (
                                el.classList.contains(`${STYLE_PREFIX}highlight-marked`) ||
                                el.classList.contains(`${STYLE_PREFIX}highlight-menu`) ||
                                el.classList.contains(`${STYLE_PREFIX}floating-button`) ||
                                el.classList.contains(`${STYLE_PREFIX}sidebar`)
                            )) {
                                return false;
                            }
                            // 检查是否包含高亮元素(避免批量DOM操作触发重新应用)
                            if (el.querySelector && el.querySelector(`.${STYLE_PREFIX}highlight-marked`)) {
                                return false;
                            }
                        }
                    }
                }

                // 忽略删除的节点是高亮元素
                if (mutation.removedNodes.length > 0) {
                    for (let node of mutation.removedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const el = node;
                            if (el.classList && el.classList.contains(`${STYLE_PREFIX}highlight-marked`)) {
                                return false;
                            }
                        }
                    }
                }

                return true;
            });

            if (!hasRelevantMutation) {
                return;
            }

            clearTimeout(debounceTimer);
            debounceTimer = setTimeout(() => {
                // 只对没有高亮的元素重新应用高亮,避免影响已有高亮
                isApplyingHighlights = true;
                applyHighlights();
                // 延迟重置标志,确保所有由 applyHighlights 引起的 DOM 变化都被忽略
                setTimeout(() => {
                    isApplyingHighlights = false;
                }, 100);
            }, 300);
        });

        try {
            observer.observe(document.body, {
                childList: true,
                subtree: true,
                characterData: true
            });
        } catch (e) {
            console.warn('启动 DOM 监听失败:', e);
        }
    }

    // 更改高亮颜色
    function changeHighlightColor(highlightId, newColor) {
        // 查找主高亮记录
        const mainHighlight = highlights.find(h => h.id === highlightId);

        if (mainHighlight && mainHighlight.isMultiFragment) {
            // 多片段高亮:更新所有片段的颜色
            const fragments = highlights.filter(h => h.parentId === highlightId);

            // 更新主记录颜色
            mainHighlight.color = newColor;

            // 更新所有片段记录的颜色
            fragments.forEach(fragment => {
                fragment.color = newColor;

                // 更新DOM元素
                const fragmentElements = document.querySelectorAll(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${fragment.id}"]`);
                fragmentElements.forEach(elem => {
                    elem.style.backgroundColor = newColor;
                });
            });

        } else {
            // 单片段高亮:原逻辑
            const highlightElement = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${highlightId}"]`);
            if (highlightElement) {
                highlightElement.style.backgroundColor = newColor;
            }
            const index = highlights.findIndex(h => h.id === highlightId);
            if (index !== -1) {
                highlights[index].color = newColor;
            }
        }

        saveHighlights();

        // 检查侧边栏是否打开,如果打开则刷新高亮列表
        const sidebar = document.getElementById(`${STYLE_PREFIX}sidebar`);
        if (sidebar && sidebar.style.right === '0px' && updateSidebarHighlights) {
            updateSidebarHighlights();
        }
    }

    // 显示/隐藏侧边栏
    function toggleSidebar(forceShow = true) {
        const sidebar = document.getElementById(`${STYLE_PREFIX}sidebar`);
        const floatingButton = document.getElementById(`${STYLE_PREFIX}floating-button`);
        if (!sidebar) return;

        if (forceShow) {
            sidebar.style.right = '0px';
            // 显示侧边栏时隐藏浮动按钮
            if (floatingButton) {
                floatingButton.style.display = 'none';
            }
            if (updateSidebarHighlights) {
                updateSidebarHighlights();
            }
        } else {
            const width = sidebar.style.width || '300px';
            const wasVisible = sidebar.style.right === '0px';
            sidebar.style.right = wasVisible ? `-${width}` : '0px';

            // 更新浮动按钮显示状态
            if (floatingButton) {
                if (wasVisible) {
                    // 关闭侧边栏时,根据设置和启用状态决定是否显示浮动按钮
                    floatingButton.style.display = (settings.showFloatingButton && isHighlightEnabled) ? 'flex' : 'none';
                } else {
                    // 打开侧边栏时,隐藏浮动按钮
                    floatingButton.style.display = 'none';
                }
            }

            if (sidebar.style.right === '0px' && updateSidebarHighlights) {
                updateSidebarHighlights();
            }
        }
    }

    // 切换浮动按钮显示/隐藏
    function toggleFloatingButton() {
        const floatingButton = document.getElementById(`${STYLE_PREFIX}floating-button`);
        if (!floatingButton) return;

        settings.showFloatingButton = !settings.showFloatingButton;
        // 根据设置与启用状态显示或隐藏浮动按钮
        floatingButton.style.display = (settings.showFloatingButton && isHighlightEnabled) ? 'flex' : 'none';
        saveSettings();
    }

    // 显示高亮编辑菜单
    function showHighlightEditMenu(event, highlightId) {
        if (!isHighlightEnabled) {
            return;
        }
        removeHighlightMenu();
        if (menuOperationInProgress) return;
        menuOperationInProgress = true;
        event.preventDefault();
        event.stopPropagation();
        ignoreNextClick = true;

        // 查找高亮记录,如果是片段ID,找到其主记录
        let highlight = highlights.find(h => h.id === highlightId);
        if (highlight && highlight.isFragment && highlight.parentId) {
            // 如果点击的是片段,使用主记录的ID
            highlightId = highlight.parentId;
            highlight = highlights.find(h => h.id === highlightId);
        }

        if (!highlight) {
            menuOperationInProgress = false;
            return;
        }
        const menu = createHighlightMenu(false);
        menu.dataset.currentHighlightId = highlightId;
        menu.querySelectorAll(`.${STYLE_PREFIX}highlight-menu-color`).forEach(colorBtn => {
            colorBtn.classList.remove(`${STYLE_PREFIX}active`);
        });
        const activeColorButton = menu.querySelector(`.${STYLE_PREFIX}highlight-menu-color[data-color="${highlight.color}"]`);
        if (activeColorButton) {
            activeColorButton.classList.add(`${STYLE_PREFIX}active`);
        }
        const menuHeight = 50;
        let menuTop = event.clientY + window.scrollY - menuHeight - 10;
        let showAbove = true;
        if (event.clientY < menuHeight + 10) {
            menuTop = event.clientY + window.scrollY + 10;
            showAbove = false;
        }
        menu.style.top = `${menuTop}px`;
        const menuWidth = menu.offsetWidth || 200;
        let menuLeft;
        if (event.clientX - (menuWidth / 2) < 5) {
            menuLeft = 5;
        } else if (event.clientX + (menuWidth / 2) > window.innerWidth - 5) {
            menuLeft = window.innerWidth - menuWidth - 5;
        } else {
            menuLeft = event.clientX - (menuWidth / 2);
        }
        menu.style.left = `${menuLeft}px`;
        const arrowLeft = event.clientX - menuLeft;
        const minArrowLeft = 12;
        const maxArrowLeft = menuWidth - 12;
        const safeArrowLeft = Math.max(minArrowLeft, Math.min(arrowLeft, maxArrowLeft));
        menu.style.setProperty('--arrow-left', `${safeArrowLeft}px`);
        if (!showAbove) {
            menu.classList.add(`${STYLE_PREFIX}arrow-top`);
        } else {
            menu.classList.remove(`${STYLE_PREFIX}arrow-top`);
        }
        requestAnimationFrame(() => {
            menu.classList.add(`${STYLE_PREFIX}show`);
            // 使用 once:true 来自动清理事件监听
            attachOutsideClose(menu);
            setTimeout(() => {
                ignoreNextClick = false;
                menuOperationInProgress = false;
            }, 50);
        });
    }

    // 查找并高亮文本
    function findAndHighlight(searchText, color, highlightId) {
        // 遍历所有文本节点查找匹配内容
        const treeWalker = document.createTreeWalker(
            document.body,
            NodeFilter.SHOW_TEXT,
            null
        );
        while (treeWalker.nextNode()) {
            const node = treeWalker.currentNode;
            const textContent = node.textContent;
            if (!textContent || textContent.trim().length === 0) continue;
            const idx = textContent.indexOf(searchText);
            if (idx !== -1) {
                const range = document.createRange();
                range.setStart(node, idx);
                range.setEnd(node, idx + searchText.length);
                try {
                    wrapRangeWithHighlight(range, highlightId, color);
                    // 新高亮直接返回 true
                    return true;
                } catch (e) {
                    console.warn('应用高亮失败:', e);
                }
            }
        }
        return false;
    }

    // 应用页面上的所有高亮
    function applyHighlights() {
        // 过滤出主高亮记录(排除片段记录)
        const mainHighlights = highlights.filter(h => !h.isFragment);

        // 分离单片段和多片段高亮
        const singleFragmentHighlights = mainHighlights.filter(h => !h.isMultiFragment);
        const multiFragmentHighlights = mainHighlights.filter(h => h.isMultiFragment);

        // 先按时间戳升序恢复单片段高亮(从早到晚)
        singleFragmentHighlights.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
        singleFragmentHighlights.forEach(highlight => {
            const restored = applyHighlightOptimized(highlight);
            if (!restored) {
                console.warn('优化恢复失败:', highlight.text);
            }
        });

        // 然后按时间戳升序恢复多片段高亮(从早到晚)
        multiFragmentHighlights.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
        multiFragmentHighlights.forEach(highlight => {
            const fragments = highlights.filter(h => h.parentId === highlight.id);

            // 按片段索引倒序排序,从后往前恢复,避免DOM变化影响XPath
            fragments.sort((a, b) => (b.fragmentIndex || 0) - (a.fragmentIndex || 0));

            let allFragmentsRestored = true;
            let anyFragmentRestored = false;

            fragments.forEach(fragment => {
                const restored = applyHighlightOptimized(fragment);
                if (!restored) {
                    allFragmentsRestored = false;
                    console.warn('片段恢复失败:', fragment.text);
                } else {
                    anyFragmentRestored = true;
                }
            });

            // 如果没有任何片段成功恢复,标记主记录为失败
            if (!anyFragmentRestored) {
                markHighlightAsFailed(highlight);
                console.warn('多片段高亮完全失败:', highlight.text);
            } else if (!allFragmentsRestored) {
                // 部分恢复也算失败
                markHighlightAsFailed(highlight);
                console.warn('多片段高亮部分恢复失败:', highlight.text);
            } else {
                // 全部成功,重置失败计数
                highlight.failedCount = 0;
            }
        });
    }

    // 创建高亮菜单
    function createHighlightMenu(isNewHighlight = true) {
        removeHighlightMenu();
        ignoreNextClick = true;
        const menu = document.createElement('div');
        menu.className = `${STYLE_PREFIX}highlight-menu`;
        menu.innerHTML = `
        <div class="${STYLE_PREFIX}highlight-menu-colors">
            ${settings.colors.map(color => `
                <div class="${STYLE_PREFIX}highlight-menu-color"
                    style="background-color: ${color};"
                    data-color="${color}">
                </div>
            `).join('')}
        </div>
    `;
        // 无论如何先置空操作ID
        menu.dataset.currentHighlightId = '';
        document.body.appendChild(menu);

        // 如果是新建高亮,确保所有颜色块没有激活状态
        if (isNewHighlight) {
            menu.querySelectorAll(`.${STYLE_PREFIX}highlight-menu-color`).forEach(el => {
                el.classList.remove(`${STYLE_PREFIX}active`);
            });
        }

        menu.querySelectorAll(`.${STYLE_PREFIX}highlight-menu-color`).forEach(el => {
            el.addEventListener('click', (e) => {
                const color = el.dataset.color;
                const isActive = el.classList.contains(`${STYLE_PREFIX}active`);
                const currentHighlightId = menu.dataset.currentHighlightId;
                if (isActive) {
                    if (currentHighlightId) {
                        removeHighlightById(currentHighlightId);
                        menu.dataset.currentHighlightId = '';
                        menu.querySelectorAll(`.${STYLE_PREFIX}highlight-menu-color`)
                            .forEach(colorEl => colorEl.classList.remove(`${STYLE_PREFIX}active`));
                        const sel = window.getSelection();
                        sel.removeAllRanges();
                        if (savedRange) {
                            sel.addRange(savedRange.cloneRange());
                        }
                    } else {
                        window.getSelection().removeAllRanges();
                        menu.querySelectorAll(`.${STYLE_PREFIX}highlight-menu-color`)
                            .forEach(colorEl => colorEl.classList.remove(`${STYLE_PREFIX}active`));
                    }
                    removeHighlightMenu();
                } else {
                    settings.activeColor = color;
                    saveSettings();
                    if (currentHighlightId) {
                        changeHighlightColor(currentHighlightId, color);
                    } else {
                        const selection = window.getSelection();
                        if (selection.toString().trim() === '' && savedRange) {
                            selection.removeAllRanges();
                            selection.addRange(savedRange.cloneRange());
                        }
                        const newHighlightId = highlightSelection(color);
                        if (newHighlightId) {
                            menu.dataset.currentHighlightId = newHighlightId;
                        }
                    }
                    menu.querySelectorAll(`.${STYLE_PREFIX}highlight-menu-color`)
                        .forEach(colorEl => colorEl.classList.toggle(`${STYLE_PREFIX}active`, colorEl.dataset.color === color));
                }
                e.stopPropagation();
            });
        });
        return menu;
    }

    // 显示高亮菜单
    function showHighlightMenu() {
        if (!isHighlightEnabled) {
            return;
        }
        if (menuOperationInProgress) return;
        menuOperationInProgress = true;
        const selection = window.getSelection();
        const selectedText = selection.toString().trim();
        if (selectedText === '') {
            menuOperationInProgress = false;
            return;
        }

        // 检查选中的文本是否在侧边栏内
        const range = selection.getRangeAt(0);
        let container = range.commonAncestorContainer;
        if (container.nodeType === Node.TEXT_NODE) {
            container = container.parentElement;
        }

        // 向上遍历DOM树,检查是否在侧边栏内
        let element = container;
        while (element && element !== document.body) {
            if (element.id === `${STYLE_PREFIX}sidebar`) {
                // 选中的文本在侧边栏内,不显示颜色选择器
                menuOperationInProgress = false;
                return;
            }
            element = element.parentElement;
        }
        const menu = createHighlightMenu(true);
        const rects = range.getClientRects();
        if (rects.length === 0) {
            menuOperationInProgress = false;
            return;
        }
        const targetRect = rects[0];
        const menuHeight = 50;
        let initialTop = window.scrollY + targetRect.top - menuHeight - 8;
        let showAbove = true;
        if (targetRect.top < menuHeight + 10) {
            initialTop = window.scrollY + targetRect.bottom + 8;
            showAbove = false;
        }
        menu.style.top = `${initialTop}px`;
        setTimeout(() => {
            const menuWidth = menu.offsetWidth;
            const textCenterX = targetRect.left + (targetRect.width / 2);
            let menuLeft;
            if (textCenterX - (menuWidth / 2) < 5) {
                menuLeft = 5;
            } else if (textCenterX + (menuWidth / 2) > window.innerWidth - 5) {
                menuLeft = window.innerWidth - menuWidth - 5;
            } else {
                menuLeft = textCenterX - (menuWidth / 2);
            }
            menu.style.left = `${menuLeft}px`;
            menu.style.transform = 'none';
            const arrowLeft = textCenterX - menuLeft;
            const minArrowLeft = 12;
            const maxArrowLeft = menuWidth - 12;
            const safeArrowLeft = Math.max(minArrowLeft, Math.min(arrowLeft, maxArrowLeft));
            menu.style.setProperty('--arrow-left', `${safeArrowLeft}px`);
            if (!showAbove) {
                menu.classList.add(`${STYLE_PREFIX}arrow-top`);
            } else {
                menu.classList.remove(`${STYLE_PREFIX}arrow-top`);
            }
            requestAnimationFrame(() => {
                menu.classList.add(`${STYLE_PREFIX}show`);
            });
        }, 0);
        attachOutsideClose(menu);
        setTimeout(() => {
            ignoreNextClick = false;
            menuOperationInProgress = false;
        }, 100);
    }

    // 注册事件
    function registerEvents() {
        document.addEventListener('mouseup', function (e) {
            if (!isHighlightEnabled) {
                return;
            }
            if (e.target.closest(`.${STYLE_PREFIX}highlight-menu`)) {
                return;
            }
            const selection = window.getSelection();
            const selectedText = selection.toString().trim();
            if (selectedText.length < (settings.minTextLength || 1)) {
                return;
            }
            if (selection.rangeCount > 0) {
                savedRange = selection.getRangeAt(0).cloneRange();
            }
            removeHighlightMenu();
            clearTimeout(menuDisplayTimer);
            ignoreNextClick = true;
            menuDisplayTimer = setTimeout(() => {
                showHighlightMenu();
            }, 10);
        });
    }

    // 已弃用的模糊匹配实现已移除

    // 优化的高亮恢复函数 - 两层匹配策略
    function applyHighlightOptimized(highlight) {
        // 检查是否已经有相同ID的高亮存在
        const existingHighlight = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${highlight.id}"]`);
        if (existingHighlight) {
            return true;
        }

        // 第一层:XPath + 偏移量快速定位(90%情况适用)
        if (tryXPathMatch(highlight)) {
            highlight.failedCount = 0; // 重置失败计数
            return true;
        }

        // 第二层:上下文哈希快速匹配(8%情况适用)
        if (tryContextHashMatch(highlight)) {
            highlight.failedCount = 0;
            return true;
        }

        // 失败处理:标记为失效,不进行复杂匹配
        markHighlightAsFailed(highlight);
        return false;
    }

    // 第一层匹配:XPath + 偏移量
    function tryXPathMatch(highlight) {
        try {
            if (!highlight.xpath || highlight.textOffset === undefined) {
                return false;
            }

            // 使用XPath查找元素
            const result = document.evaluate(
                highlight.xpath,
                document,
                null,
                XPathResult.FIRST_ORDERED_NODE_TYPE,
                null
            );

            const targetNode = result.singleNodeValue;
            if (!targetNode) {
                return false;
            }

            // 如果XPath直接指向文本节点,直接使用它
            if (targetNode.nodeType === Node.TEXT_NODE) {
                const text = targetNode.textContent;
                if (text.substring(highlight.textOffset, highlight.textOffset + highlight.textLength) === highlight.text) {
                    return createHighlightAt(targetNode, highlight.textOffset, highlight);
                }
                return false;
            }

            // 否则,收集元素下的所有文本节点
            const targetElement = targetNode;
            const textNodes = [];
            const walker = document.createTreeWalker(
                targetElement,
                NodeFilter.SHOW_TEXT,
                null
            );

            let node;
            while (node = walker.nextNode()) {
                textNodes.push(node);
            }

            // 构建完整文本和节点位置映射
            let fullText = '';
            const nodeMap = []; // 记录每个字符对应的节点和节点内偏移

            for (let textNode of textNodes) {
                const nodeText = textNode.textContent;
                const startPos = fullText.length;
                for (let i = 0; i < nodeText.length; i++) {
                    nodeMap.push({ node: textNode, offset: i });
                }
                fullText += nodeText;
            }

            // 检查文本是否匹配
            const extractedText = fullText.substring(
                highlight.textOffset,
                highlight.textOffset + highlight.textLength
            );

            if (extractedText === highlight.text) {
                // 确定起始和结束节点
                const startInfo = nodeMap[highlight.textOffset];
                const endInfo = nodeMap[highlight.textOffset + highlight.textLength - 1];

                if (!startInfo || !endInfo) {
                    return false;
                }

                // 创建跨节点的高亮范围
                return createHighlightAtRange(
                    startInfo.node, startInfo.offset,
                    endInfo.node, endInfo.offset + 1,
                    highlight
                );
            }

            return false;
        } catch (e) {
            console.warn('XPath匹配失败:', e);
            return false;
        }
    }

    // 第二层匹配:上下文哈希快速匹配
    function tryContextHashMatch(highlight) {
        try {
            if (!document.body || !highlight || !highlight.text) {
                return false;
            }

            const contextWindow = 50; // 上下文窗口大小
            const walker = document.createTreeWalker(
                document.body,
                NodeFilter.SHOW_TEXT,
                null
            );

            const bufferNodes = [];
            let bufferText = '';

            const resolveOffsetInBuffer = (targetIndex) => {
                let accumulated = 0;
                for (let i = 0; i < bufferNodes.length; i++) {
                    const entry = bufferNodes[i];
                    const next = accumulated + entry.text.length;
                    if (targetIndex < next) {
                        return { node: entry.node, offset: targetIndex - accumulated };
                    }
                    accumulated = next;
                }
                if (bufferNodes.length) {
                    const lastEntry = bufferNodes[bufferNodes.length - 1];
                    if (targetIndex === accumulated) {
                        return { node: lastEntry.node, offset: lastEntry.text.length };
                    }
                }
                return null;
            };

            let node;
            while (node = walker.nextNode()) {
                if (!node || !node.textContent) {
                    continue;
                }

                // 跳过已经高亮的节点
                if (node.parentElement && node.parentElement.closest(`.${STYLE_PREFIX}highlight-marked`)) {
                    continue;
                }

                const textContent = node.textContent;
                if (!textContent.length) {
                    continue;
                }

                bufferNodes.push({ node, text: textContent });
                bufferText += textContent;

                const maxLength = highlight.text.length + contextWindow * 2;
                while (bufferNodes.length && bufferText.length > maxLength) {
                    const removed = bufferNodes.shift();
                    bufferText = bufferText.slice(removed.text.length);
                }

                let searchFrom = 0;
                while (true) {
                    const idx = bufferText.indexOf(highlight.text, searchFrom);
                    if (idx === -1) {
                        break;
                    }

                    const prefixStart = Math.max(0, idx - contextWindow);
                    const suffixEnd = Math.min(bufferText.length, idx + highlight.text.length + contextWindow);
                    const prefix = bufferText.substring(prefixStart, idx);
                    const suffix = bufferText.substring(idx + highlight.text.length, suffixEnd);
                    const currentHash = generateContextHash(prefix, suffix, highlight.text);

                    if (!highlight.contextHash || currentHash === highlight.contextHash) {
                        const startInfo = resolveOffsetInBuffer(idx);
                        const endInfo = resolveOffsetInBuffer(idx + highlight.text.length);

                        if (startInfo && endInfo && startInfo.node && endInfo.node) {
                            try {
                                const range = document.createRange();
                                range.setStart(startInfo.node, startInfo.offset);
                                range.setEnd(endInfo.node, endInfo.offset);
                                wrapRangeWithHighlight(range, highlight.id, highlight.color);
                                return true;
                            } catch (rangeError) {
                                console.warn('范围创建失败:', rangeError);
                            }
                        }
                    }

                    searchFrom = idx + 1;
                }
            }
            return false;
        } catch (e) {
            console.warn('上下文哈希匹配失败:', e);
            return false;
        }
    }

    // 将选区包装为高亮元素的通用方法
    function wrapRangeWithHighlight(range, highlightId, color) {
        // 检查是否跨越块级元素
        const commonAncestor = range.commonAncestorContainer;
        const blockTags = ['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'BLOCKQUOTE'];

        // 判断是否包含块级元素
        let containsBlockElement = false;
        if (commonAncestor.nodeType === Node.ELEMENT_NODE) {
            containsBlockElement = blockTags.includes(commonAncestor.tagName) ||
                                    !!commonAncestor.querySelector(blockTags.map(tag => tag.toLowerCase()).join(','));
        }

        if (!containsBlockElement) {
            // 简单情况:不跨越块级元素,使用原有逻辑
            const highlightElement = document.createElement('span');
            highlightElement.className = `${STYLE_PREFIX}highlight-marked`;
            highlightElement.dataset.highlightId = highlightId;
            highlightElement.style.backgroundColor = color;

            const fragment = range.extractContents();
            highlightElement.appendChild(fragment);
            range.insertNode(highlightElement);

            highlightElement.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                removeHighlightMenu();
                setTimeout(() => {
                    showHighlightEditMenu(e, highlightId);
                }, 10);
            });

            return highlightElement;
        } else {
            // 复杂情况:跨越块级元素,需要分段处理
            const highlightElements = [];

            try {
                // 获取选区内的所有节点
                const startContainer = range.startContainer;
                const endContainer = range.endContainer;
                const startOffset = range.startOffset;
                const endOffset = range.endOffset;

                // 创建树遍历器
                const walker = document.createTreeWalker(
                    commonAncestor,
                    NodeFilter.SHOW_TEXT,
                    null  // 简化处理,遍历所有文本节点
                );

                let node;
                let isInRange = false;
                let foundEnd = false;

                while (node = walker.nextNode()) {
                    // 跳过已高亮的节点
                    if (node.parentElement && node.parentElement.classList.contains(`${STYLE_PREFIX}highlight-marked`)) {
                        continue;
                    }

                    let nodeStartOffset = 0;
                    let nodeEndOffset = node.textContent.length;
                    let shouldHighlight = false;

                    // 如果是起始节点,调整偏移
                    if (node === startContainer) {
                        nodeStartOffset = startOffset;
                        isInRange = true;
                        shouldHighlight = true;
                    } else if (node === endContainer) {
                        // 如果是结束节点,调整偏移
                        nodeEndOffset = endOffset;
                        shouldHighlight = true;
                        foundEnd = true;
                    } else if (isInRange && !foundEnd) {
                        // 在范围内的中间节点
                        shouldHighlight = true;
                    }

                    // 创建单个文本节点的高亮
                    if (shouldHighlight && nodeEndOffset > nodeStartOffset) {
                        const nodeRange = document.createRange();
                        nodeRange.setStart(node, nodeStartOffset);
                        nodeRange.setEnd(node, nodeEndOffset);

                        const span = document.createElement('span');
                        span.className = `${STYLE_PREFIX}highlight-marked`;
                        span.dataset.highlightId = highlightId;
                        span.style.backgroundColor = color;

                        try {
                            nodeRange.surroundContents(span);

                            span.addEventListener('click', (e) => {
                                e.preventDefault();
                                e.stopPropagation();
                                removeHighlightMenu();
                                setTimeout(() => {
                                    showHighlightEditMenu(e, highlightId);
                                }, 10);
                            });

                            highlightElements.push(span);
                        } catch (e) {
                            // 如果 surroundContents 失败,尝试其他方法
                            const extracted = nodeRange.extractContents();
                            span.appendChild(extracted);
                            nodeRange.insertNode(span);

                            span.addEventListener('click', (e) => {
                                e.preventDefault();
                                e.stopPropagation();
                                removeHighlightMenu();
                                setTimeout(() => {
                                    showHighlightEditMenu(e, highlightId);
                                }, 10);
                            });

                            highlightElements.push(span);
                        }
                    }

                    // 如果已经处理了结束节点,停止遍历
                    if (foundEnd) {
                        break;
                    }
                }

                return highlightElements[0]; // 返回第一个高亮元素

            } catch (e) {
                console.error('跨块级元素高亮失败:', e);
                // 降级处理:使用原始方法
                const highlightElement = document.createElement('span');
                highlightElement.className = `${STYLE_PREFIX}highlight-marked`;
                highlightElement.dataset.highlightId = highlightId;
                highlightElement.style.backgroundColor = color;

                try {
                    const fragment = range.extractContents();
                    highlightElement.appendChild(fragment);
                    range.insertNode(highlightElement);
                } catch (extractError) {
                    console.error('降级处理也失败:', extractError);
                    return null;
                }

                highlightElement.addEventListener('click', (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    removeHighlightMenu();
                    setTimeout(() => {
                        showHighlightEditMenu(e, highlightId);
                    }, 10);
                });

                return highlightElement;
            }
        }
    }

    // 在指定位置创建高亮
    function createHighlightAt(textNode, offset, highlight) {
        try {
            const range = document.createRange();
            range.setStart(textNode, offset);
            range.setEnd(textNode, offset + highlight.text.length);
            wrapRangeWithHighlight(range, highlight.id, highlight.color);
            return true;
        } catch (e) {
            console.warn('创建高亮失败:', e);
            return false;
        }
    }

    // 在跨节点范围创建高亮(支持包含<a>等内嵌元素的文本)
    function createHighlightAtRange(startNode, startOffset, endNode, endOffset, highlight) {
        try {
            const range = document.createRange();
            range.setStart(startNode, startOffset);
            range.setEnd(endNode, endOffset);
            wrapRangeWithHighlight(range, highlight.id, highlight.color);
            return true;
        } catch (e) {
            console.warn('创建跨节点高亮失败:', e);
            return false;
        }
    }

    // 标记高亮为失效
    function markHighlightAsFailed(highlight) {
        highlight.failedCount = (highlight.failedCount || 0) + 1;
        highlight.lastFailedTime = Date.now();
        console.warn('高亮失效:', highlight.text, '失败次数:', highlight.failedCount);
        saveHighlights();
    }

    // 创建失效高亮管理控件
    function createFailedHighlightControls() {
        const container = document.createElement('div');
        container.className = `${STYLE_PREFIX}failed-controls`;
        Object.assign(container.style, {
            padding: '8px',
            borderBottom: '1px solid rgba(255,255,255,0.1)',
            marginBottom: '8px',
            flex: '0 0 auto'
        });

        // 统计失效高亮数量(只统计主记录,不统计片段)
        const mainHighlights = highlights.filter(h => !h.isFragment);
        const failedCount = mainHighlights.filter(h => h.failedCount && h.failedCount >= 3).length;

        if (failedCount === 0) {
            container.style.display = 'none';
            return container;
        }

        container.innerHTML = `
            <div style="display: flex; justify-content: space-between; align-items: center; padding: 4px 8px;
                        background: linear-gradient(135deg, rgba(255, 165, 0, 0.08) 0%, rgba(255, 140, 0, 0.04) 100%);
                        border-radius: 6px; backdrop-filter: blur(8px);">
                <div style="display: flex; align-items: center; gap: 6px;">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#FFA500" stroke-width="2">
                        <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
                        <line x1="12" y1="9" x2="12" y2="13"></line>
                        <line x1="12" y1="17" x2="12.01" y2="17"></line>
                    </svg>
                    <span style="color: #FFA500; font-size: 12px; font-weight: 600;">
                        可能失效 (${failedCount})
                    </span>
                </div>
                <div style="display: flex; gap: 6px;">
                    <button id="${STYLE_PREFIX}detect-failed"
                            style="background: linear-gradient(135deg, rgba(74, 164, 222, 0.15) 0%, rgba(74, 164, 222, 0.08) 100%);
                                   border: 1px solid rgba(74, 164, 222, 0.25);
                                   color: #4EA8DE;
                                   padding: 4px 10px;
                                   border-radius: 4px;
                                   font-size: 11px;
                                   font-weight: 600;
                                   cursor: pointer;
                                   transition: all 0.2s;
                                   display: inline-flex;
                                   align-items: center;
                                   gap: 4px;
                                   backdrop-filter: blur(4px);
                                   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
                        检测
                    </button>
                    <button id="${STYLE_PREFIX}clean-failed"
                            style="background: linear-gradient(135deg, rgba(255, 107, 107, 0.15) 0%, rgba(255, 107, 107, 0.08) 100%);
                                   border: 1px solid rgba(255, 107, 107, 0.25);
                                   color: #FF6B6B;
                                   padding: 4px 10px;
                                   border-radius: 4px;
                                   font-size: 11px;
                                   font-weight: 600;
                                   cursor: pointer;
                                   transition: all 0.2s;
                                   display: inline-flex;
                                   align-items: center;
                                   gap: 4px;
                                   backdrop-filter: blur(4px);
                                   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
                        清理
                    </button>
                </div>
            </div>
        `;

        // 添加事件监听
        const detectBtn = container.querySelector(`#${STYLE_PREFIX}detect-failed`);
        const cleanBtn = container.querySelector(`#${STYLE_PREFIX}clean-failed`);

        // 添加悬浮效果
        detectBtn.addEventListener('mouseenter', () => {
            detectBtn.style.transform = 'translateY(-1px)';
            detectBtn.style.boxShadow = '0 4px 8px rgba(74, 164, 222, 0.2)';
        });
        detectBtn.addEventListener('mouseleave', () => {
            detectBtn.style.transform = 'none';
            detectBtn.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)';
        });

        cleanBtn.addEventListener('mouseenter', () => {
            cleanBtn.style.transform = 'translateY(-1px)';
            cleanBtn.style.boxShadow = '0 4px 8px rgba(255, 107, 107, 0.2)';
        });
        cleanBtn.addEventListener('mouseleave', () => {
            cleanBtn.style.transform = 'none';
            cleanBtn.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)';
        });

        detectBtn.addEventListener('click', detectFailedHighlights);
        cleanBtn.addEventListener('click', cleanSelectedFailedHighlights);

        return container;
    }

    // 检测失效高亮
    function detectFailedHighlights() {
        let detectedCount = 0;

        // 只检测主高亮记录,不检测片段
        const mainHighlights = highlights.filter(h => !h.isFragment);

        mainHighlights.forEach(highlight => {
            const existing = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${highlight.id}"]`);
            if (!existing) {
                highlight.failedCount = (highlight.failedCount || 0) + 1;
                detectedCount++;

                // 如果是多片段高亮,也更新片段的失败计数
                if (highlight.isMultiFragment) {
                    const fragments = highlights.filter(h => h.parentId === highlight.id);
                    fragments.forEach(fragment => {
                        fragment.failedCount = highlight.failedCount;
                    });
                }
            } else {
                highlight.failedCount = 0; // 重置失败计数

                // 如果是多片段高亮,也重置片段的失败计数
                if (highlight.isMultiFragment) {
                    const fragments = highlights.filter(h => h.parentId === highlight.id);
                    fragments.forEach(fragment => {
                        fragment.failedCount = 0;
                    });
                }
            }
        });

        saveHighlights();
        if (updateSidebarHighlights) updateSidebarHighlights();

        alert(`检测完成:发现 ${detectedCount} 个可能失效的高亮`);
    }

    // 清理选中的失效高亮
    function cleanSelectedFailedHighlights() {
        // 只检查主高亮记录
        const mainHighlights = highlights.filter(h => !h.isFragment);
        const failedHighlights = mainHighlights.filter(h => h.failedCount && h.failedCount >= 3);

        if (failedHighlights.length === 0) {
            alert('没有失效的高亮需要清理');
            return;
        }

        if (confirm(`确定要删除 ${failedHighlights.length} 个失效的高亮吗?此操作不可撤销。`)) {
            // 收集需要删除的ID(包括主记录和片段)
            const idsToDelete = new Set();

            failedHighlights.forEach(failedHighlight => {
                idsToDelete.add(failedHighlight.id);

                // 如果是多片段高亮,也添加片段ID
                if (failedHighlight.isMultiFragment) {
                    const fragments = highlights.filter(h => h.parentId === failedHighlight.id);
                    fragments.forEach(fragment => {
                        idsToDelete.add(fragment.id);
                    });
                }
            });

            // 过滤掉所有需要删除的高亮
            highlights = highlights.filter(h => !idsToDelete.has(h.id));

            saveHighlights();
            if (updateSidebarHighlights) updateSidebarHighlights();

            alert(`已成功清理 ${failedHighlights.length} 个失效高亮`);
        }
    }

    // 已弃用的上下文匹配实现已移除

    function extractValidContext(text, start, count, direction) {
        // direction: "backward" 从 start 往前提取, "forward" 从 start 往后提取
        let result = "";
        let processedChars = 0;

        // 对于短文本或单个字符,我们提取更多上下文
        const adjustedCount = count * (text.length <= 3 ? 2 : 1);

        if (direction === "backward") {
            for (let i = start - 1; i >= 0 && processedChars < adjustedCount * 2; i--) {
                const ch = text.charAt(i);
                // 只计算有效字符(中文、英文、数字)
                if (/[\u4e00-\u9fffA-Za-z0-9]/.test(ch)) {
                    result = ch + result;
                    processedChars++;
                    if (processedChars >= adjustedCount) break;
                } else {
                    // 空格和标点也记录,但不计入有效字符数
                    result = ch + result;
                }
            }
        } else { // forward
            for (let i = start; i < text.length && processedChars < adjustedCount * 2; i++) {
                const ch = text.charAt(i);
                // 只计算有效字符(中文、英文、数字)
                if (/[\u4e00-\u9fffA-Za-z0-9]/.test(ch)) {
                    result += ch;
                    processedChars++;
                    if (processedChars >= adjustedCount) break;
                } else {
                    // 空格和标点也记录,但不计入有效字符数
                    result += ch;
                }
            }
        }
        return result;
    }

    function collectRangeContext(range, count) {
        if (!range || typeof count !== 'number' || !document.body) {
            return { prefix: '', suffix: '' };
        }

        let prefix = '';
        let suffix = '';

        try {
            const prefixRange = document.createRange();
            prefixRange.setStart(document.body, 0);
            prefixRange.setEnd(range.startContainer, range.startOffset);
            const prefixText = prefixRange.toString();
            prefix = extractValidContext(prefixText, prefixText.length, count, 'backward');
        } catch (e) {
            console.warn('提取前置上下文失败:', e);
        }

        try {
            const suffixRange = document.createRange();
            suffixRange.setStart(range.endContainer, range.endOffset);
            const body = document.body;
            if (body && body.childNodes.length) {
                suffixRange.setEnd(body, body.childNodes.length);
            } else if (body) {
                suffixRange.setEnd(body, 0);
            }
            const suffixText = suffixRange.toString();
            suffix = extractValidContext(suffixText, 0, count, 'forward');
        } catch (e) {
            console.warn('提取后置上下文失败:', e);
        }

        return { prefix, suffix };
    }

    // 生成元素的XPath
    function generateXPath(element) {
        if (!element || element === document.body) {
            return '/html/body';
        }

        let path = '';
        for (; element && element.nodeType === Node.ELEMENT_NODE; element = element.parentNode) {
            let idx = 1;
            for (let sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
                if (sibling.nodeType === Node.ELEMENT_NODE && sibling.tagName === element.tagName) {
                    idx++;
                }
            }

            const tagName = element.tagName.toLowerCase();
            const xpathPart = tagName === 'body' ? tagName : `${tagName}[${idx}]`;
            path = '/' + xpathPart + path;

            if (element === document.body) break;
        }

        return '/html' + path;
    }

    // 生成上下文哈希(简单快速的字符串哈希)
    function generateContextHash(prefix, suffix, text) {
        const context = (prefix + '|' + text + '|' + suffix).substring(0, 100); // 限制长度
        let hash = 0;
        for (let i = 0; i < context.length; i++) {
            const char = context.charCodeAt(i);
            hash = ((hash << 5) - hash) + char;
            hash = hash & hash; // 转换为32位整数
        }
        return Math.abs(hash).toString(36);
    }

    // 添加浮动按钮和侧边栏功能
    function createFloatingButtonAndSidebar() {
        // 检查 document.body 是否存在
        if (!document.body) {
            console.warn('document.body 不存在,延迟创建浮动按钮和侧边栏');
            // 延迟重试
            setTimeout(createFloatingButtonAndSidebar, 500);
            return;
        }

        const tooltipStyle = document.createElement('style');
        tooltipStyle.textContent = `
            .${STYLE_PREFIX}tooltip {
                position: absolute;
                background: #1A1D24;
                color: #E8E9EB;
                padding: 4px 10px;
                border-radius: 4px;
                font-size: 12px;
                pointer-events: none;
                white-space: nowrap;
                z-index: 10000;
                opacity: 0;
                transition: opacity 0.2s;
                bottom: 130%;
                left: 50%;
                transform: translateX(-50%);
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
                border: 1px solid rgba(59, 165, 216, 0.2);
            }

            .${STYLE_PREFIX}tooltip::after {
                content: "";
                position: absolute;
                top: 100%;
                left: 50%;
                margin-left: -5px;
                border-width: 5px;
                border-style: solid;
                border-color: #1A1D24 transparent transparent transparent;
            }

            .${STYLE_PREFIX}tooltip-container {
                position: relative;
            }
        `;
        document.head.appendChild(tooltipStyle);
        // 创建浮动按钮
        const floatingButton = document.createElement('button');
        floatingButton.id = `${STYLE_PREFIX}floating-button`;
        // 使用 SVG 图标,代表"汉堡菜单"
        floatingButton.innerHTML = `
            <svg viewBox="0 0 100 80" width="16" height="16" fill="#ccc" xmlns="http://www.w3.org/2000/svg">
                <rect width="100" height="10"></rect>
                <rect y="30" width="100" height="10"></rect>
                <rect y="60" width="100" height="10"></rect>
            </svg>
        `;
        // 基础样式(统一由此处控制,避免与 GM_addStyle 重复)
        Object.assign(floatingButton.style, {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            zIndex: '10000',
            width: '38px',
            height: '38px',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            backgroundColor: '#262A33',
            color: '#E8E9EB',
            boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
            transition: 'all 0.2s ease',
            display: (settings.showFloatingButton && isHighlightEnabled) ? 'flex' : 'none',
            alignItems: 'center',
            justifyContent: 'center'
        });

        // 悬浮效果(替代 CSS :hover)
        floatingButton.addEventListener('mouseenter', () => {
            floatingButton.style.backgroundColor = '#3BA5D8';
            floatingButton.style.boxShadow = '0 4px 12px rgba(59, 165, 216, 0.25)';
        });
        floatingButton.addEventListener('mouseleave', () => {
            floatingButton.style.backgroundColor = '#262A33';
            floatingButton.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.15)';
        });

        document.body.appendChild(floatingButton);
        // 创建侧边栏(初始隐藏)
        const sidebar = document.createElement('div');
        sidebar.id = `${STYLE_PREFIX}sidebar`;
        Object.assign(sidebar.style, {
            position: 'fixed',
            top: '0',
            right: '-280px',
            width: '280px',
            height: '100%',
            boxShadow: '-2px 0 24px rgba(0, 0, 0, 0.15)',
            transition: 'none',
            zIndex: '9999',
            overflow: 'hidden',
            display: 'flex',
            flexDirection: 'column',
            color: '#E8E9EB',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Inter", sans-serif',
            background: '#16181D',
            borderLeft: '1px solid rgba(255, 255, 255, 0.06)',
            backdropFilter: 'blur(20px)',
        });

        // 构建侧边栏内部结构
        sidebar.innerHTML = `
            <div class="${STYLE_PREFIX}sidebar-tabs">
                <button class="${STYLE_PREFIX}sidebar-tab ${STYLE_PREFIX}active" data-tab="highlights">
                    <span>高亮</span>
                </button>
                <button class="${STYLE_PREFIX}sidebar-tab" data-tab="disabled">
                    <span>管理</span>
                </button>
                <div style="flex: 1;"></div>
                <button class="${STYLE_PREFIX}sidebar-close" title="关闭">
                    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
                        <path d="M18 6L6 18M6 6l12 12"></path>
                    </svg>
                </button>
            </div>

            <div class="${STYLE_PREFIX}sidebar-content">
                <div class="${STYLE_PREFIX}tab-panel ${STYLE_PREFIX}active" data-panel="highlights">
                    <div class="${STYLE_PREFIX}highlights-list"></div>
                    <div class="${STYLE_PREFIX}highlights-bottom-actions"></div>
                </div>

                <div class="${STYLE_PREFIX}tab-panel" data-panel="disabled">
                    <div class="${STYLE_PREFIX}disabled-container"></div>
                </div>
            </div>
        `;

        document.body.appendChild(sidebar);
        setTimeout(() => {
            sidebar.style.transition = 'right 0.3s ease';
        }, 10);

        // 设置标签栏样式
        const tabs = sidebar.querySelector(`.${STYLE_PREFIX}sidebar-tabs`);
        Object.assign(tabs.style, {
            display: 'flex',
            alignItems: 'center',
            gap: '6px',
            padding: '12px',
            background: 'rgba(22, 24, 29, 0.8)',
            borderBottom: '1px solid rgba(255, 255, 255, 0.06)',
            backdropFilter: 'blur(10px)',
        });

        // 设置标签按钮样式
        sidebar.querySelectorAll(`.${STYLE_PREFIX}sidebar-tab`).forEach(tab => {
            Object.assign(tab.style, {
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                gap: '6px',
                padding: '8px 16px',
                background: 'transparent',
                border: '1px solid transparent',
                borderRadius: '6px',
                color: '#6B7280',
                fontSize: '12.5px',
                fontWeight: '500',
                cursor: 'pointer',
                transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
            });

            // 标签悬停效果
            tab.addEventListener('mouseenter', () => {
                if (!tab.classList.contains(`${STYLE_PREFIX}active`)) {
                    tab.style.background = 'rgba(255, 255, 255, 0.04)';
                    tab.style.color = '#9CA3AF';
                }
            });
            tab.addEventListener('mouseleave', () => {
                if (!tab.classList.contains(`${STYLE_PREFIX}active`)) {
                    tab.style.background = 'transparent';
                    tab.style.color = '#6B7280';
                }
            });
        });

        // 关闭按钮样式
        const closeBtn = sidebar.querySelector(`.${STYLE_PREFIX}sidebar-close`);
        Object.assign(closeBtn.style, {
            background: 'none',
            border: 'none',
            cursor: 'pointer',
            padding: '8px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            color: '#6B7280',
            borderRadius: '6px',
            transition: 'all 0.2s',
        });
        closeBtn.addEventListener('mouseenter', () => {
            closeBtn.style.background = 'rgba(255, 107, 107, 0.12)';
            closeBtn.style.color = '#FF6B6B';
        });
        closeBtn.addEventListener('mouseleave', () => {
            closeBtn.style.background = 'none';
            closeBtn.style.color = '#6B7280';
        });

        // 激活的标签页样式
        const activeTab = sidebar.querySelector(`.${STYLE_PREFIX}sidebar-tab.${STYLE_PREFIX}active`);
        if (activeTab) {
            Object.assign(activeTab.style, {
                background: 'linear-gradient(135deg, rgba(74, 164, 222, 0.15) 0%, rgba(74, 164, 222, 0.08) 100%)',
                border: '1px solid rgba(74, 164, 222, 0.2)',
                color: '#4EA8DE',
            });
        }

        // 内容区域样式
        const contentArea = sidebar.querySelector(`.${STYLE_PREFIX}sidebar-content`);
        Object.assign(contentArea.style, {
            flex: '1',
            overflow: 'hidden',
            position: 'relative'
        });

        // 设置面板样式
        sidebar.querySelectorAll(`.${STYLE_PREFIX}tab-panel`).forEach(panel => {
            Object.assign(panel.style, {
                height: '100%',
                width: '100%',
                position: 'absolute',
                top: '0',
                left: '0',
                padding: '0',
                boxSizing: 'border-box',
                overflow: 'hidden',
                display: 'none',
                flexDirection: 'column'
            });
        });

        // 显示当前活动面板
        const activePanel = sidebar.querySelector(`.${STYLE_PREFIX}tab-panel.${STYLE_PREFIX}active`);
        if (activePanel) {
            activePanel.style.display = 'flex';
        }

        // 添加侧边栏拖拽调整区域(位于侧边栏的最左侧)
        const resizer = document.createElement('div');
        Object.assign(resizer.style, {
            position: 'absolute',
            left: '0',
            top: '0',
            width: '5px',
            height: '100%',
            cursor: 'ew-resize',
            backgroundColor: 'transparent'
        });
        sidebar.appendChild(resizer);

        // 拖拽事件逻辑
        resizer.addEventListener('mousedown', initResize);

        function initResize(e) {
            e.preventDefault();
            window.addEventListener('mousemove', resizeSidebar);
            window.addEventListener('mouseup', stopResize);
        }

        function resizeSidebar(e) {
            // 计算出新的宽度:侧边栏右对齐,宽度 = 窗口宽度 - 鼠标水平位置
            const newWidth = window.innerWidth - e.clientX;
            // 限制最小宽度为 150px,最大宽度为窗口 80%
            if (newWidth >= 150 && newWidth <= window.innerWidth * 0.8) {
                sidebar.style.width = newWidth + 'px';
                // 更新设置中的宽度
                settings.sidebarWidth = newWidth;
                saveSettings();
            }
        }

        function stopResize(e) {
            window.removeEventListener('mousemove', resizeSidebar);
            window.removeEventListener('mouseup', stopResize);
        }

        // 标签页切换事件
        sidebar.querySelectorAll(`.${STYLE_PREFIX}sidebar-tab`).forEach(tab => {
            tab.addEventListener('click', () => {
                // 移除所有标签页和面板的活动状态
                sidebar.querySelectorAll(`.${STYLE_PREFIX}sidebar-tab`).forEach(t => {
                    t.classList.remove(`${STYLE_PREFIX}active`);
                    t.style.background = 'transparent';
                    t.style.border = '1px solid transparent';
                    t.style.color = '#6B7280';
                });

                sidebar.querySelectorAll(`.${STYLE_PREFIX}tab-panel`).forEach(p => {
                    p.classList.remove(`${STYLE_PREFIX}active`);
                    p.style.display = 'none';
                });

                // 激活当前标签和面板
                tab.classList.add(`${STYLE_PREFIX}active`);
                tab.style.background = 'linear-gradient(135deg, rgba(74, 164, 222, 0.15) 0%, rgba(74, 164, 222, 0.08) 100%)';
                tab.style.border = '1px solid rgba(74, 164, 222, 0.2)';
                tab.style.color = '#4EA8DE';

                const panelId = tab.getAttribute('data-tab');
                const panel = sidebar.querySelector(`.${STYLE_PREFIX}tab-panel[data-panel="${panelId}"]`);
                if (panel) {
                    panel.classList.add(`${STYLE_PREFIX}active`);
                    panel.style.display = 'flex';
                }
            });
        });

        // 关闭按钮事件
        const closeButton = sidebar.querySelector(`.${STYLE_PREFIX}sidebar-close`);
        closeButton.addEventListener('click', () => {
            sidebar.style.right = `-${parseInt(sidebar.style.width)}px`;

            // 侧边栏关闭时,如果设置允许显示浮动按钮且当前页面未启用,则恢复显示浮动按钮
            if (settings.showFloatingButton && isHighlightEnabled) {
                floatingButton.style.display = 'flex';
            }
        });

        // 浮动按钮点击后切换侧边栏的显示和隐藏
        floatingButton.addEventListener('click', () => {
            if (sidebar.style.right === '0px') {
                sidebar.style.right = `-${parseInt(sidebar.style.width)}px`;
                // 如果设置允许显示浮动按钮且当前页面已启用,则显示浮动按钮
                if (settings.showFloatingButton && isHighlightEnabled) {  // 正确的变量
                    floatingButton.style.display = 'flex';
                }
            } else {
                sidebar.style.right = '0px';
                // 当侧边栏显示时,隐藏浮动按钮
                floatingButton.style.display = 'none';
                // 刷新高亮列表
                if (updateSidebarHighlights) {
                    updateSidebarHighlights();
                }
            }
        });

        // 初始设置宽度
        if (settings.sidebarWidth) {
            sidebar.style.width = `${settings.sidebarWidth}px`;
            sidebar.style.right = `-${settings.sidebarWidth}px`; // 确保初始位置与实际宽度匹配
        } else {
            sidebar.style.right = '-300px'; // 默认宽度的对应位置
        }

        // 渲染高亮列表面板
        function renderHighlightsList() {
            const highlightsListContainer = sidebar.querySelector(`.${STYLE_PREFIX}highlights-list`);
            if (!highlightsListContainer) return;

            // 清空容器
            highlightsListContainer.innerHTML = '';
            Object.assign(highlightsListContainer.style, {
                flex: '1',
                overflow: 'hidden',
                display: 'flex',
                flexDirection: 'column',
                padding: '0',
                width: '100%',
                position: 'relative',
                boxSizing: 'border-box',
            });

            // 添加失效高亮管理控件
            const failedControlsContainer = createFailedHighlightControls();
            highlightsListContainer.appendChild(failedControlsContainer);

            // 创建高亮列表
            const listContainer = document.createElement('div');
            listContainer.className = `${STYLE_PREFIX}highlights-items`;
            Object.assign(listContainer.style, {
                flex: '1',
                display: 'flex',
                flexDirection: 'column',
                gap: '8px',
                overflowY: 'auto',
                overflowX: 'hidden',
                padding: '12px',
                width: '100%',
                boxSizing: 'border-box',
                alignItems: 'stretch'
            });

            // 自定义滚动条样式(隐藏滚动条但保留滚动能力)
            let highlightScrollStyle = document.getElementById(`${STYLE_PREFIX}highlight-scroll-style`);
            if (!highlightScrollStyle) {
                highlightScrollStyle = document.createElement('style');
                highlightScrollStyle.id = `${STYLE_PREFIX}highlight-scroll-style`;
                document.head.appendChild(highlightScrollStyle);
            }
            highlightScrollStyle.textContent = `
                .${STYLE_PREFIX}highlights-items {
                    scrollbar-width: none;
                    -ms-overflow-style: none;
                }
                .${STYLE_PREFIX}highlights-items::-webkit-scrollbar {
                    width: 0;
                    height: 0;
                }
            `;

            // 过滤出主高亮记录(排除片段记录)
            const mainHighlights = highlights.filter(h => !h.isFragment);

            // 排序高亮,按时间倒序
            const sortedHighlights = [...mainHighlights].sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));

            // 分类高亮:正常和失效
            const normalHighlights = sortedHighlights.filter(h => !h.failedCount || h.failedCount < 3);
            const failedHighlights = sortedHighlights.filter(h => h.failedCount && h.failedCount >= 3);

            if (sortedHighlights.length === 0) {
                // 显示空状态
                const emptyState = document.createElement('div');
                Object.assign(emptyState.style, {
                    display: 'flex',
                    flexDirection: 'column',
                    alignItems: 'center',
                    justifyContent: 'center',
                    padding: '60px 20px',
                    textAlign: 'center',
                    color: '#999',
                    fontSize: '13px'
                });

                // 使用SVG图标作为空状态图标
                emptyState.innerHTML = `
                    <svg width="60" height="60" viewBox="0 0 24 24" fill="none" stroke="rgba(255,255,255,0.15)"
                        stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
                        <path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"></path>
                    </svg>
                    <p style="margin-top:16px; font-size:13px !important;">暂无高亮内容<br>选中文本并点击颜色进行高亮</p>
                `;
                listContainer.appendChild(emptyState);
            } else {
                // 渲染所有高亮项目
                sortedHighlights.forEach((highlight, index) => {
                    const highlightItem = createHighlightItem(highlight, index);
                    listContainer.appendChild(highlightItem);
                });
            }

            highlightsListContainer.appendChild(listContainer);

            // 创建底部固定按钮栏 - 添加到 tab-panel 而不是 highlights-list
            const tabPanel = sidebar.querySelector(`.${STYLE_PREFIX}tab-panel[data-panel="highlights"]`);
            if (!tabPanel) return;

            // 清除旧的底部操作栏(如果存在)
            const existingActionBar = tabPanel.querySelector(`.${STYLE_PREFIX}highlights-bottom-actions`);
            if (existingActionBar) {
                existingActionBar.remove();
            }

            const bottomActionBar = document.createElement('div');
            bottomActionBar.className = `${STYLE_PREFIX}highlights-bottom-actions`;
            Object.assign(bottomActionBar.style, {
                display: 'flex',
                padding: '12px',
                gap: '10px',
                boxSizing: 'border-box',
                background: 'rgba(22, 24, 29, 0.95)',
                borderTop: '1px solid rgba(255, 255, 255, 0.06)',
                flexShrink: '0'
            });
            // 创建刷新按钮
            const refreshBtn = document.createElement('button');
            Object.assign(refreshBtn.style, {
                flex: '1',
                background: 'linear-gradient(135deg, rgba(74, 164, 222, 0.15) 0%, rgba(74, 164, 222, 0.08) 100%)',
                border: '1px solid rgba(74, 164, 222, 0.2)',
                borderRadius: '6px',
                padding: '10px',
                color: '#4EA8DE',
                fontSize: '13px',
                fontWeight: '600',
                cursor: 'pointer',
                transition: 'all 0.2s',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                gap: '6px',
            });
            refreshBtn.innerHTML = `刷新`;

            // 添加悬停效果
            refreshBtn.addEventListener('mouseenter', () => {
                refreshBtn.style.transform = 'translateY(-1px)';
                refreshBtn.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
            });
            refreshBtn.addEventListener('mouseleave', () => {
                refreshBtn.style.transform = 'none';
                refreshBtn.style.boxShadow = 'none';
            });

            refreshBtn.addEventListener('click', () => {
                // 刷新高亮列表
                loadHighlights();
                applyHighlights();
                renderHighlightsList();
            });

            // 创建清除按钮
            const clearBtn = document.createElement('button');
            Object.assign(clearBtn.style, {
                flex: '1',
                background: 'linear-gradient(135deg, rgba(255, 107, 107, 0.15) 0%, rgba(255, 107, 107, 0.08) 100%)',
                border: '1px solid rgba(255, 107, 107, 0.2)',
                borderRadius: '6px',
                padding: '10px',
                color: '#FF6B6B',
                fontSize: '13px',
                fontWeight: '600',
                cursor: 'pointer',
                transition: 'all 0.2s',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                gap: '6px',
            });
            clearBtn.innerHTML = `清空`;

            // 添加悬停效果
            clearBtn.addEventListener('mouseenter', () => {
                clearBtn.style.transform = 'translateY(-1px)';
                clearBtn.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
            });
            clearBtn.addEventListener('mouseleave', () => {
                clearBtn.style.transform = 'none';
                clearBtn.style.boxShadow = 'none';
            });

            clearBtn.addEventListener('click', () => {
                if (highlights.length === 0) return;

                // 确认删除
                if (confirm('确定要删除所有高亮吗?此操作不可撤销。')) {
                    // 移除DOM中的高亮元素
                    document.querySelectorAll(`.${STYLE_PREFIX}highlight-marked`).forEach(el => {
                        const textNode = document.createTextNode(el.textContent);
                        el.parentNode.replaceChild(textNode, el);
                    });

                    // 清空高亮数组
                    highlights = [];
                    saveHighlights();
                    renderHighlightsList();
                }
            });

            bottomActionBar.appendChild(refreshBtn);
            bottomActionBar.appendChild(clearBtn);
            tabPanel.appendChild(bottomActionBar);
        }

        updateSidebarHighlights = renderHighlightsList;

        // 创建单个高亮项目
        function createHighlightItem(highlight, index) {
            const item = document.createElement('div');
            item.className = `${STYLE_PREFIX}highlight-item`;
            item.dataset.highlightId = highlight.id;

            Object.assign(item.style, {
                background: 'linear-gradient(135deg, rgba(78, 89, 108, 0.22) 0%, rgba(46, 48, 58, 0.18) 100%)',
                border: '1px solid rgba(255, 255, 255, 0.08)',
                borderRadius: '10px',
                padding: '14px',
                position: 'relative',
                transition: 'all 0.18s cubic-bezier(0.4, 0, 0.2, 1)',
                cursor: 'pointer',
                overflow: 'hidden',
                flex: '0 0 auto',
                backdropFilter: 'blur(8px)',
                boxShadow: '0 10px 24px rgba(7, 12, 24, 0.28)'
            });

            // 高亮内容
            const content = document.createElement('div');
            Object.assign(content.style, {
                color: '#E8E9EB',
                fontSize: '13.5px',
                lineHeight: '1.6',
                marginBottom: '10px',
                wordBreak: 'break-word',
                display: '-webkit-box',
                WebkitBoxOrient: 'vertical',
                WebkitLineClamp: '3',
                overflow: 'hidden',
                fontWeight: '400',
            });

            // 处理高亮文本,避免XSS
            const textNode = document.createTextNode(highlight.text);
            content.appendChild(textNode);

            // 底部信息栏
            const infoBar = document.createElement('div');
            Object.assign(infoBar.style, {
                display: 'flex',
                justifyContent: 'space-between',
                alignItems: 'center',
            });

            // 时间信息区域(含发光色点)
            const timeContainer = document.createElement('div');
            Object.assign(timeContainer.style, {
                display: 'flex',
                alignItems: 'center',
                gap: '6px',
            });

            // 发光色点
            const colorDot = document.createElement('div');
            Object.assign(colorDot.style, {
                width: '6px',
                height: '6px',
                borderRadius: '50%',
                background: highlight.color,
                boxShadow: `0 0 8px ${highlight.color}80`,
            });

            // 时间信息
            const timeInfo = document.createElement('div');
            Object.assign(timeInfo.style, {
                color: '#6B7280',
                fontSize: '11.5px',
                fontWeight: '500',
            });

            // 格式化时间
            const date = new Date(highlight.timestamp);
            const formattedDate = `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
            timeInfo.textContent = formattedDate;

            timeContainer.appendChild(colorDot);
            timeContainer.appendChild(timeInfo);

            // 失效标识
            if (highlight.failedCount && highlight.failedCount >= 3) {
                const failedBadge = document.createElement('span');
                Object.assign(failedBadge.style, {
                    background: 'linear-gradient(135deg, rgba(255, 165, 0, 0.15) 0%, rgba(255, 140, 0, 0.08) 100%)',
                    border: '1px solid rgba(255, 165, 0, 0.25)',
                    borderRadius: '4px',
                    padding: '2px 6px',
                    color: '#FFA500',
                    fontSize: '10.5px',
                    fontWeight: '600',
                    marginLeft: '8px',
                    display: 'inline-flex',
                    alignItems: 'center',
                    gap: '3px',
                    backdropFilter: 'blur(4px)',
                    boxShadow: '0 0 8px rgba(255, 165, 0, 0.15)',
                    letterSpacing: '0.3px',
                });

                // 使用SVG图标替代emoji
                failedBadge.innerHTML = `
                    <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
                        <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
                        <line x1="12" y1="9" x2="12" y2="13"></line>
                        <line x1="12" y1="17" x2="12.01" y2="17"></line>
                    </svg>
                    <span>失效</span>
                `;
                failedBadge.title = `恢复失败 ${highlight.failedCount} 次`;
                timeContainer.appendChild(failedBadge);
            }

            // 操作按钮容器(默认隐藏,悬停时显示)
            const actionButtons = document.createElement('div');
            actionButtons.className = `${STYLE_PREFIX}card-actions`;
            Object.assign(actionButtons.style, {
                display: 'flex',
                gap: '6px',
                opacity: '0',
                transition: 'opacity 0.2s',
            });

            // 跳转按钮
            const jumpButton = document.createElement('button');
            const jumpDefaultBackground = 'transparent';
            const jumpDefaultBorder = 'transparent';
            const jumpHoverBackground = 'rgba(118, 196, 255, 0.22)';
            const jumpHoverBorder = 'rgba(118, 196, 255, 0.45)';
            Object.assign(jumpButton.style, {
                width: '25px',
                height: '25px',
                background: 'transparent',
                border: '1px solid transparent',
                borderRadius: '50%',
                cursor: 'pointer',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                color: '#8ed0ff',
                transition: 'all 0.18s ease',
                boxShadow: 'none',
                backdropFilter: 'blur(0)'
            });
            jumpButton.setAttribute('aria-label', '定位到原文');
            jumpButton.innerHTML = `
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                    <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
                    <circle cx="12" cy="10" r="3"></circle>
                </svg>
            `;

            const activateJumpVisual = () => {
                jumpButton.style.background = jumpHoverBackground;
                jumpButton.style.borderColor = jumpHoverBorder;
                jumpButton.style.boxShadow = '0 10px 20px rgba(12, 23, 42, 0.35)';
                jumpButton.style.transform = 'translateY(-1px) scale(1.08)';
                jumpButton.style.color = '#e5f5ff';
            };
            const resetJumpVisual = () => {
                jumpButton.style.background = jumpDefaultBackground;
                jumpButton.style.borderColor = jumpDefaultBorder;
                jumpButton.style.boxShadow = 'none';
                jumpButton.style.transform = 'none';
                jumpButton.style.color = '#8ed0ff';
            };
            jumpButton.addEventListener('mouseenter', activateJumpVisual);
            jumpButton.addEventListener('focus', activateJumpVisual);
            jumpButton.addEventListener('mouseleave', resetJumpVisual);
            jumpButton.addEventListener('blur', resetJumpVisual);

            jumpButton.addEventListener('click', (event) => {
                event.preventDefault();
                event.stopPropagation();
                scrollToHighlight(highlight.id);
            });

            // 删除按钮
            const deleteButton = document.createElement('button');
            const deleteDefaultBackground = 'transparent';
            const deleteDefaultBorder = 'transparent';
            const deleteHoverBackground = 'rgba(255, 126, 126, 0.24)';
            const deleteHoverBorder = 'rgba(255, 172, 172, 0.48)';
            Object.assign(deleteButton.style, {
                width: '25px',
                height: '25px',
                background: deleteDefaultBackground,
                border: `1px solid ${deleteDefaultBorder}`,
                borderRadius: '50%',
                cursor: 'pointer',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                color: '#ff9f9f',
                transition: 'all 0.18s ease',
                boxShadow: 'none',
                backdropFilter: 'blur(0)'
            });
            deleteButton.setAttribute('aria-label', '删除高亮');
            deleteButton.innerHTML = `
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
                    <path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2m3 0v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6h14z"></path>
                </svg>
            `;

            const activateDeleteVisual = () => {
                deleteButton.style.background = deleteHoverBackground;
                deleteButton.style.borderColor = deleteHoverBorder;
                deleteButton.style.boxShadow = '0 10px 20px rgba(32, 12, 18, 0.4)';
                deleteButton.style.transform = 'translateY(-1px) scale(1.08)';
                deleteButton.style.color = '#ffe3e3';
            };
            const resetDeleteVisual = () => {
                deleteButton.style.background = deleteDefaultBackground;
                deleteButton.style.borderColor = deleteDefaultBorder;
                deleteButton.style.boxShadow = 'none';
                deleteButton.style.transform = 'none';
                deleteButton.style.color = '#ff9f9f';
            };
            deleteButton.addEventListener('mouseenter', activateDeleteVisual);
            deleteButton.addEventListener('focus', activateDeleteVisual);
            deleteButton.addEventListener('mouseleave', resetDeleteVisual);
            deleteButton.addEventListener('blur', resetDeleteVisual);

            deleteButton.addEventListener('click', (event) => {
                event.preventDefault();
                event.stopPropagation();
                if (confirm('确定要删除这条高亮吗?')) {
                    removeHighlightById(highlight.id);
                    renderHighlightsList();
                }
            });

            actionButtons.appendChild(jumpButton);
            actionButtons.appendChild(deleteButton);

            infoBar.appendChild(timeContainer);
            infoBar.appendChild(actionButtons);

            // 添加卡片悬停效果
            const defaultBackground = item.style.background;
            const defaultBorder = item.style.borderColor;
            const defaultShadow = item.style.boxShadow;

            item.addEventListener('mouseenter', () => {
                item.style.background = 'linear-gradient(135deg, rgba(94, 139, 194, 0.28) 0%, rgba(63, 83, 120, 0.24) 100%)';
                item.style.borderColor = 'rgba(124, 189, 255, 0.45)';
                item.style.transform = 'translateY(-2px)';
                item.style.boxShadow = '0 16px 32px rgba(7, 12, 24, 0.38)';
                actionButtons.style.opacity = '1';
            });
            item.addEventListener('mouseleave', () => {
                item.style.background = defaultBackground;
                item.style.borderColor = defaultBorder;
                item.style.transform = 'none';
                item.style.boxShadow = defaultShadow;
                actionButtons.style.opacity = '0';
            });


            item.appendChild(content);
            item.appendChild(infoBar);

            return item;
        }

        // 滚动到指定高亮
        function scrollToHighlight(highlightId) {
            // 先尝试查找主ID的元素
            let highlightElement = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${highlightId}"]`);

            // 如果没找到主ID元素,检查是否为多片段高亮
            if (!highlightElement) {
                const mainHighlight = highlights.find(h => h.id === highlightId);
                if (mainHighlight && mainHighlight.isMultiFragment) {
                    // 查找第一个片段的元素
                    const firstFragment = highlights.find(h => h.parentId === highlightId);
                    if (firstFragment) {
                        highlightElement = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${firstFragment.id}"]`);
                    }
                }
            }

            if (highlightElement) {
                // 平滑滚动到元素
                highlightElement.scrollIntoView({ behavior: 'smooth', block: 'center' });

                // 获取所有相关的高亮元素(包括多片段)
                const mainHighlight = highlights.find(h => h.id === highlightId);
                let allElements = [highlightElement];

                // 如果是多片段高亮,获取所有片段元素
                if (mainHighlight && mainHighlight.isMultiFragment) {
                    const fragments = highlights.filter(h => h.parentId === highlightId);
                    fragments.forEach(fragment => {
                        const fragmentElement = document.querySelector(`.${STYLE_PREFIX}highlight-marked[data-highlight-id="${fragment.id}"]`);
                        if (fragmentElement && !allElements.includes(fragmentElement)) {
                            allElements.push(fragmentElement);
                        }
                    });
                }

                // 为所有相关元素添加闪烁效果
                allElements.forEach(element => {
                    element.classList.add(`${STYLE_PREFIX}highlight-flash`);
                    const originalTransition = element.style.transition;
                    element.style.transition = 'all 0.3s ease';

                    setTimeout(() => {
                        element.classList.remove(`${STYLE_PREFIX}highlight-flash`);
                        element.style.transition = originalTransition;
                    }, 2500);
                });
            }
        }

        // 初始渲染高亮列表
        renderHighlightsList();

        // 渲染启用管理面板内容
        function renderEnabledPanel() {
            const container = sidebar.querySelector(`.${STYLE_PREFIX}disabled-container`);
            if (!container) return;

            // 清空容器
            container.innerHTML = '';

            const theme = {
                cardBackground: 'linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%)',
                cardBorder: '1px solid rgba(255, 255, 255, 0.08)'
            };

            Object.assign(container.style, {
                color: '#E8E9EB',
                display: 'flex',
                flexDirection: 'column',
                gap: '20px',
                padding: '18px',
                flex: '1',
                overflowY: 'auto',
                boxSizing: 'border-box'
            });

            // 添加当前页面管理区域
            const currentPageSection = document.createElement('div');
            currentPageSection.className = `${STYLE_PREFIX}disabled-section`;
            Object.assign(currentPageSection.style, {
                margin: '0',
                padding: '16px',
                background: theme.cardBackground,
                borderRadius: '10px',
                border: theme.cardBorder,
                boxShadow: '0 12px 32px rgba(0, 0, 0, 0.22)',
                display: 'flex',
                flexDirection: 'column',
                gap: '12px'
            });

            const currentPageTitle = document.createElement('div');
            currentPageTitle.className = `${STYLE_PREFIX}disabled-title`;
            currentPageTitle.innerHTML = `<span>当前页面</span>`;
            Object.assign(currentPageTitle.style, {
                fontSize: '13px',
                fontWeight: '600',
                color: '#E8E9EB',
                marginBottom: '0',
                display: 'flex',
                alignItems: 'center',
                gap: '8px'
            });

            // 当前页面状态
            const currentStatus = document.createElement('div');
            currentStatus.className = `${STYLE_PREFIX}current-status`;
            currentStatus.innerHTML = renderCurrentPageStatus();

            currentPageSection.appendChild(currentPageTitle);
            currentPageSection.appendChild(currentStatus);
            container.appendChild(currentPageSection);

            // 启用域名列表区域
            const domainsSection = document.createElement('div');
            domainsSection.className = `${STYLE_PREFIX}disabled-section`;
            Object.assign(domainsSection.style, {
                margin: '0',
                padding: '16px',
                background: theme.cardBackground,
                borderRadius: '10px',
                border: theme.cardBorder,
                boxShadow: '0 10px 28px rgba(0, 0, 0, 0.2)',
                display: 'flex',
                flexDirection: 'column',
                gap: '12px'
            });

            const domainsTitle = document.createElement('div');
            domainsTitle.className = `${STYLE_PREFIX}disabled-title`;
            domainsTitle.innerHTML = `<span>启用域名列表</span>`;
            Object.assign(domainsTitle.style, {
                fontSize: '13px',
                fontWeight: '600',
                color: '#E8E9EB',
                marginBottom: '0',
                display: 'flex',
                alignItems: 'center',
                gap: '8px'
            });

            const domainsList = document.createElement('div');
            domainsList.className = `${STYLE_PREFIX}domains-list`;
            domainsList.innerHTML = renderEnabledDomains();
            Object.assign(domainsList.style, {
                display: 'flex',
                flexDirection: 'column',
                gap: '10px'
            });

            // 添加域名表单
            const addDomainForm = document.createElement('div');
            addDomainForm.className = `${STYLE_PREFIX}add-disabled-form`;
            Object.assign(addDomainForm.style, {
                display: 'flex',
                marginTop: '0',
                gap: '0',
                borderRadius: '10px',
                overflow: 'hidden',
                border: theme.cardBorder,
                background: 'rgba(12, 14, 18, 0.8)',
                boxShadow: '0 10px 28px rgba(0, 0, 0, 0.18)'
            });

            const domainInput = document.createElement('input');
            domainInput.className = `${STYLE_PREFIX}add-disabled-input`;
            domainInput.id = 'add-domain-input';
            domainInput.placeholder = '输入域名...';
            Object.assign(domainInput.style, {
                flex: '1',
                background: 'transparent',
                border: 'none',
                borderRight: '1px solid rgba(255, 255, 255, 0.08)',
                padding: '12px 14px',
                fontSize: '13px',
                color: '#E8E9EB',
                outline: 'none',
                transition: 'background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease'
            });

            const addDomainBtn = document.createElement('button');
            addDomainBtn.className = `${STYLE_PREFIX}add-disabled-button`;
            addDomainBtn.id = 'add-domain-btn';
            addDomainBtn.textContent = '添加';
            Object.assign(addDomainBtn.style, {
                background: 'linear-gradient(135deg, rgba(78, 168, 222, 0.2) 0%, rgba(78, 168, 222, 0.12) 100%)',
                color: '#E4F3FF',
                border: 'none',
                borderRadius: '0',
                padding: '12px 18px',
                fontSize: '13px',
                fontWeight: '600',
                cursor: 'pointer',
                transition: 'transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease'
            });

            addDomainForm.appendChild(domainInput);
            addDomainForm.appendChild(addDomainBtn);

            domainsSection.appendChild(domainsTitle);
            domainsSection.appendChild(domainsList);
            domainsSection.appendChild(addDomainForm);
            container.appendChild(domainsSection);

            // 启用URL列表区域
            const urlsSection = document.createElement('div');
            urlsSection.className = `${STYLE_PREFIX}disabled-section`;
            Object.assign(urlsSection.style, {
                margin: '0',
                padding: '16px',
                background: theme.cardBackground,
                borderRadius: '10px',
                border: theme.cardBorder,
                boxShadow: '0 10px 28px rgba(0, 0, 0, 0.2)',
                display: 'flex',
                flexDirection: 'column',
                gap: '12px'
            });

            const urlsTitle = document.createElement('div');
            urlsTitle.className = `${STYLE_PREFIX}disabled-title`;
            urlsTitle.innerHTML = `<span>启用网址列表</span>`;
            Object.assign(urlsTitle.style, {
                fontSize: '13px',
                fontWeight: '600',
                color: '#E8E9EB',
                marginBottom: '0',
                display: 'flex',
                alignItems: 'center',
                gap: '8px'
            });

            const urlsList = document.createElement('div');
            urlsList.className = `${STYLE_PREFIX}urls-list`;
            urlsList.innerHTML = renderEnabledUrls();
            Object.assign(urlsList.style, {
                display: 'flex',
                flexDirection: 'column',
                gap: '10px'
            });

            // 添加URL表单
            const addUrlForm = document.createElement('div');
            addUrlForm.className = `${STYLE_PREFIX}add-disabled-form`;
            Object.assign(addUrlForm.style, {
                display: 'flex',
                marginTop: '0',
                gap: '0',
                borderRadius: '10px',
                overflow: 'hidden',
                border: theme.cardBorder,
                background: 'rgba(12, 14, 18, 0.8)',
                boxShadow: '0 10px 28px rgba(0, 0, 0, 0.18)'
            });

            const urlInput = document.createElement('input');
            urlInput.className = `${STYLE_PREFIX}add-disabled-input`;
            urlInput.id = 'add-url-input';
            urlInput.placeholder = '输入网址...';
            Object.assign(urlInput.style, {
                flex: '1',
                background: 'transparent',
                border: 'none',
                borderRight: '1px solid rgba(255, 255, 255, 0.08)',
                padding: '12px 14px',
                fontSize: '13px',
                color: '#E8E9EB',
                outline: 'none',
                transition: 'background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease'
            });

            const addUrlBtn = document.createElement('button');
            addUrlBtn.className = `${STYLE_PREFIX}add-disabled-button`;
            addUrlBtn.id = 'add-url-btn';
            addUrlBtn.textContent = '添加';
            Object.assign(addUrlBtn.style, {
                background: 'linear-gradient(135deg, rgba(78, 168, 222, 0.2) 0%, rgba(78, 168, 222, 0.12) 100%)',
                color: '#E4F3FF',
                border: 'none',
                borderRadius: '0',
                padding: '12px 18px',
                fontSize: '13px',
                fontWeight: '600',
                cursor: 'pointer',
                transition: 'transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease'
            });

            addUrlForm.appendChild(urlInput);
            addUrlForm.appendChild(addUrlBtn);

            urlsSection.appendChild(urlsTitle);
            urlsSection.appendChild(urlsList);
            urlsSection.appendChild(addUrlForm);
            container.appendChild(urlsSection);

            // 绑定事件
            bindDisabledPanelEvents();
        }

        // 渲染当前页面状态
        function renderCurrentPageStatus() {
            const isDomainEnabled = enabledList.domains.includes(activationDomain);
            const isUrlEnabled = enabledList.urls.includes(activationPageUrl);

            if (isDomainEnabled || isUrlEnabled) {
                return `
                    <div class="${STYLE_PREFIX}disabled-item">
                        <div class="${STYLE_PREFIX}disabled-info">
                            <span>${isDomainEnabled ? `此域名 (${activationDomain}) 已启用高亮` : '此网址已启用高亮'}</span>
                        </div>
                        <span class="${STYLE_PREFIX}disabled-action" data-type="${isDomainEnabled ? 'domain' : 'url'}" data-value="${isDomainEnabled ? activationDomain : activationPageUrl}">
                            禁用
                        </span>
                    </div>
                `;
            } else {
                return `
                    <div class="${STYLE_PREFIX}current-page-actions">
                        <button class="${STYLE_PREFIX}disable-btn" id="enable-domain-btn">
                            启用此域名
                        </button>
                        <button class="${STYLE_PREFIX}disable-btn" id="enable-url-btn">
                            启用此网址
                        </button>
                    </div>
                `;
            }
        }

        // 渲染启用域名列表
        function renderEnabledDomains() {
            if (enabledList.domains.length === 0) {
                return `<div class="${STYLE_PREFIX}empty-list">没有启用的域名</div>`;
            }

            return enabledList.domains.map(domain => `
                <div class="${STYLE_PREFIX}disabled-item">
                    <div class="${STYLE_PREFIX}disabled-info">
                        <span>${domain}</span>
                    </div>
                    <span class="${STYLE_PREFIX}disabled-action" data-type="domain" data-value="${domain}">
                        删除
                    </span>
                </div>
            `).join('');
        }

        // 渲染启用URL列表
        function renderEnabledUrls() {
            if (enabledList.urls.length === 0) {
                return `<div class="${STYLE_PREFIX}empty-list">没有启用的网址</div>`;
            }

            return enabledList.urls.map(url => {
                // 为了美观,截断过长的URL
                const displayUrl = url.length > 40 ? url.substring(0, 37) + '...' : url;

                return `
                    <div class="${STYLE_PREFIX}disabled-item" title="${url}">
                        <div class="${STYLE_PREFIX}disabled-info">
                            <span>${displayUrl}</span>
                        </div>
                        <span class="${STYLE_PREFIX}disabled-action" data-type="url" data-value="${url}">
                            删除
                        </span>
                    </div>
                `;
            }).join('');
        }

        // 绑定启用管理面板事件
        function bindDisabledPanelEvents() {
            // 启用当前域名按钮
            const enableDomainBtn = document.getElementById('enable-domain-btn');
            if (enableDomainBtn) {
                enableDomainBtn.addEventListener('click', () => {
                    if (confirm('确定要启用域名 "' + activationDomain + '" 的高亮功能吗?')) {
                        enableDomain(activationDomain);
                        renderEnabledPanel();
                    }
                });
            }

            // 启用当前网址按钮
            const enableUrlBtn = document.getElementById('enable-url-btn');
            if (enableUrlBtn) {
                enableUrlBtn.addEventListener('click', () => {
                    if (confirm('确定要启用当前网址的高亮功能吗?')) {
                        enableUrl(activationPageUrl);
                        renderEnabledPanel();
                    }
                });
            }

            // 添加样式
            const existingDisabledStyle = document.getElementById(`${STYLE_PREFIX}disabled-style`);
            if (existingDisabledStyle) {
                existingDisabledStyle.remove();
            }
            const styleSheet = document.createElement('style');
            styleSheet.id = `${STYLE_PREFIX}disabled-style`;
            styleSheet.textContent = `
                #${STYLE_PREFIX}sidebar,
                #${STYLE_PREFIX}sidebar *,
                .${STYLE_PREFIX}disabled-section,
                .${STYLE_PREFIX}disabled-title,
                .${STYLE_PREFIX}disabled-title span,
                .${STYLE_PREFIX}disabled-item,
                .${STYLE_PREFIX}disabled-info,
                .${STYLE_PREFIX}disabled-info span,
                .${STYLE_PREFIX}empty-list,
                .${STYLE_PREFIX}sidebar-tab,
                .${STYLE_PREFIX}highlight-item {
                    color: #E8E9EB !important;
                }
                .${STYLE_PREFIX}disabled-item {
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    padding: 12px 14px;
                    background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
                    border-radius: 10px;
                    margin-bottom: 6px;
                    transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
                    border: 1px solid rgba(255, 255, 255, 0.08);
                    backdrop-filter: blur(6px);
                }

                .${STYLE_PREFIX}disabled-item:hover {
                    background: linear-gradient(135deg, rgba(255, 255, 255, 0.08) 0%, rgba(255, 255, 255, 0.03) 100%);
                    border-color: rgba(255, 255, 255, 0.16);
                    box-shadow: 0 14px 32px rgba(0, 0, 0, 0.24);
                    transform: translateY(-1px);
                }

                .${STYLE_PREFIX}disabled-info {
                    display: flex;
                    align-items: center;
                    gap: 8px;
                    font-size: 13px;
                    color: #E8E9EB;
                    flex: 1;
                    overflow: hidden;
                    text-overflow: ellipsis;
                    white-space: nowrap;
                }

                .${STYLE_PREFIX}disabled-action {
                    color: #4EA8DE !important;
                    font-size: 12px;
                    cursor: pointer;
                    padding: 4px 12px;
                    border-radius: 999px;
                    transition: all 0.2s ease;
                    background: rgba(78, 168, 222, 0.12);
                    border: 1px solid transparent;
                    opacity: 0.92;
                }

                .${STYLE_PREFIX}disabled-action:hover {
                    background: linear-gradient(135deg, rgba(78, 168, 222, 0.24) 0%, rgba(78, 168, 222, 0.16) 100%);
                    border-color: rgba(78, 168, 222, 0.35);
                    color: #BFE6FF !important;
                    opacity: 1;
                }

                .${STYLE_PREFIX}empty-list {
                    padding: 14px;
                    color: #A1A7B3 !important;
                    font-style: italic;
                    font-size: 13px;
                    text-align: center;
                    background: linear-gradient(135deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.015) 100%);
                    border-radius: 10px;
                    border: 1px dashed rgba(255, 255, 255, 0.08);
                }

                .${STYLE_PREFIX}current-page-actions {
                    display: flex;
                    gap: 12px;
                    width: 100%;
                }

                .${STYLE_PREFIX}disable-btn {
                    flex: 1;
                    background: linear-gradient(135deg, rgba(78, 168, 222, 0.2) 0%, rgba(78, 168, 222, 0.12) 100%);
                    border: 1px solid rgba(78, 168, 222, 0.32);
                    border-radius: 10px;
                    padding: 12px 16px;
                    color: #4EA8DE;
                    font-size: 13px;
                    font-weight: 600;
                    cursor: pointer;
                    transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease, border-color 0.2s ease;
                    backdrop-filter: blur(4px);
                }

                .${STYLE_PREFIX}disable-btn:hover {
                    background: linear-gradient(135deg, rgba(78, 168, 222, 0.26) 0%, rgba(78, 168, 222, 0.16) 100%);
                    border-color: rgba(78, 168, 222, 0.42);
                    box-shadow: 0 12px 30px rgba(0, 0, 0, 0.22);
                    color: #BFE6FF;
                    transform: translateY(-1px);
                }

                .${STYLE_PREFIX}add-disabled-input {
                    background: transparent;
                    color: #E8E9EB;
                }

                .${STYLE_PREFIX}add-disabled-input::placeholder {
                    color: rgba(232, 233, 235, 0.4);
                }

                .${STYLE_PREFIX}add-disabled-input:focus {
                    background: rgba(78, 168, 222, 0.08);
                    box-shadow: inset 0 0 0 1px rgba(78, 168, 222, 0.35);
                }

                .${STYLE_PREFIX}add-disabled-button {
                    background: linear-gradient(135deg, rgba(78, 168, 222, 0.2) 0%, rgba(78, 168, 222, 0.12) 100%);
                    color: #E4F3FF;
                    border-left: 1px solid rgba(255, 255, 255, 0.04);
                }

                .${STYLE_PREFIX}add-disabled-button:hover {
                    background: linear-gradient(135deg, rgba(78, 168, 222, 0.28) 0%, rgba(78, 168, 222, 0.18) 100%);
                    color: #ffffff;
                    transform: translateY(-1px);
                    box-shadow: 0 10px 24px rgba(78, 168, 222, 0.24);
                }

                .${STYLE_PREFIX}disabled-container,
                .${STYLE_PREFIX}disabled-section,
                .${STYLE_PREFIX}domains-list,
                .${STYLE_PREFIX}urls-list {
                    scrollbar-width: none;
                    -ms-overflow-style: none;
                }

                .${STYLE_PREFIX}disabled-container::-webkit-scrollbar,
                .${STYLE_PREFIX}disabled-section::-webkit-scrollbar,
                .${STYLE_PREFIX}domains-list::-webkit-scrollbar,
                .${STYLE_PREFIX}urls-list::-webkit-scrollbar {
                    width: 0;
                    height: 0;
                }
            `;
            document.head.appendChild(styleSheet);

            // 删除按钮事件
            document.querySelectorAll(`.${STYLE_PREFIX}disabled-action`).forEach(btn => {
                btn.addEventListener('click', (e) => {
                    const type = e.target.dataset.type;
                    const value = e.target.dataset.value;

                    if (e.target.textContent.trim() === '删除') {
                        if (type === 'domain') {
                            enabledList.domains = enabledList.domains.filter(d => d !== value);
                        } else if (type === 'url') {
                            enabledList.urls = enabledList.urls.filter(u => u !== value);
                        }
                        saveEnabledList();
                        renderEnabledPanel();
                    } else if (e.target.textContent.trim() === '启用') {
                        if (type === 'domain') {
                            enableDomain(value);
                        } else if (type === 'url') {
                            enableUrl(value);
                        }
                        renderEnabledPanel();
                    } else if (e.target.textContent.trim() === '禁用') {
                        // 添加对禁用按钮的处理
                        if (type === 'domain') {
                            disableDomain(value);
                        } else if (type === 'url') {
                            disableUrl(value);
                        }
                        saveEnabledList();
                        renderEnabledPanel();
                    }
                });
            });

            // 添加域名按钮
            const addDomainBtn = document.getElementById('add-domain-btn');
            if (addDomainBtn) {
                Object.assign(addDomainBtn.style, {
                    background: 'linear-gradient(135deg, rgba(78, 168, 222, 0.2) 0%, rgba(78, 168, 222, 0.12) 100%)',
                    color: '#E4F3FF',
                    border: 'none',
                    borderRadius: '0',
                    padding: '12px 18px',
                    fontSize: '13px',
                    fontWeight: '600',
                    cursor: 'pointer',
                    transition: 'transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease'
                });
                addDomainBtn.addEventListener('click', () => {
                    const input = document.getElementById('add-domain-input');
                    const domain = input.value.trim();

                    if (domain) {
                        if (!enabledList.domains.includes(domain)) {
                            enabledList.domains.push(domain);
                            saveEnabledList();
                            input.value = '';
                            renderEnabledPanel();
                        } else {
                            alert('该域名已在启用列表中');
                        }
                    }
                });
            }

            // 添加URL按钮
            const addUrlBtn = document.getElementById('add-url-btn');
            if (addUrlBtn) {
                Object.assign(addUrlBtn.style, {
                    background: 'linear-gradient(135deg, rgba(78, 168, 222, 0.2) 0%, rgba(78, 168, 222, 0.12) 100%)',
                    color: '#E4F3FF',
                    border: 'none',
                    borderRadius: '0',
                    padding: '12px 18px',
                    fontSize: '13px',
                    fontWeight: '600',
                    cursor: 'pointer',
                    transition: 'transform 0.2s ease, background 0.2s ease, box-shadow 0.2s ease'
                });
                addUrlBtn.addEventListener('click', () => {
                    const input = document.getElementById('add-url-input');
                    const url = input.value.trim();

                    if (url) {
                        if (!enabledList.urls.includes(url)) {
                            enabledList.urls.push(url);
                            saveEnabledList();
                            input.value = '';
                            renderEnabledPanel();
                        } else {
                            alert('该网址已在启用列表中');
                        }
                    }
                });
            }

            // 输入框回车事件
            const domainInput = document.getElementById('add-domain-input');
            if (domainInput) {
                domainInput.addEventListener('keydown', (e) => {
                    if (e.key === 'Enter') {
                        document.getElementById('add-domain-btn').click();
                    }
                });
            }

            const urlInput = document.getElementById('add-url-input');
            if (urlInput) {
                urlInput.addEventListener('keydown', (e) => {
                    if (e.key === 'Enter') {
                        document.getElementById('add-url-btn').click();
                    }
                });
            }
        }

        // 初始渲染启用管理面板
        renderEnabledPanel();
    }

    function init() {
        loadHighlights();
        registerEvents();
        if (document.readyState === 'complete') {
            setTimeout(() => {
                applyHighlights();
                observeDomChanges();
            }, 500);
        } else {
            window.addEventListener('load', () => {
                setTimeout(() => {
                    applyHighlights();
                    observeDomChanges();
                }, 500);
            });
        }
        // 注册油猴菜单命令
        GM_registerMenuCommand('打开侧边栏', () => {
            toggleSidebar(true);
        });
        GM_registerMenuCommand('切换浮动按钮显示/隐藏', toggleFloatingButton);
        GM_registerMenuCommand('启用当前域名高亮', () => {
            if (enabledList.domains.includes(activationDomain)) {
                alert(`当前域名(${activationDomain})已启用高亮功能`);
            } else {
                if (confirm(`确定要启用当前域名(${activationDomain})的高亮功能吗?`)) {
                    enableDomain(activationDomain);
                }
            }
        });
        GM_registerMenuCommand('启用当前网址高亮', () => {
            if (enabledList.urls.includes(activationPageUrl)) {
                alert(`当前网址已启用高亮功能`);
            } else {
                if (confirm(`确定要启用当前网址的高亮功能吗?`)) {
                    enableUrl(activationPageUrl);
                }
            }
        });
    }

    init();
    createFloatingButtonAndSidebar();
})();