Greasy Fork

Greasy Fork is available in English.

B站打call表情集合

自动筛选B站直播间打call表情,alt+左键可手动添加

// ==UserScript==
// @name         B站打call表情集合
// @namespace    http://tampermonkey.net/
// @version      3.0.0
// @description  自动筛选B站直播间打call表情,alt+左键可手动添加
// @author       DeepSeek, Claude,Qwen
// @match        *://*.bilibili.com/live*
// @match        *://live.bilibili.com/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    // 注入CSS样式
    const css = `
    /* 基础面板样式 */
    .custom-panel {
        transition: all 0.3s ease;
        position: relative;
        background-color: var(--bg1, #fff) !important;
        border-radius: 8px;

        color: var(--text1, #333) !important;
        line-height: 1.15;
        display: block;
    }

    /* 隐藏面板时的样式 */
    .custom-panel.hidden {
        display: none !important;
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
    }

    /* 打call面板特定样式 */
    #bili-emote-panel {
        width: 300px !important;
        padding: 0 !important;
        overflow: auto !important;
        overflow-x: hidden !important;
        background-color: var(--bg1, #fff) !important;
        z-index: 9999;
    }

    /* 加载状态样式 */
    .custom-panel[data-loading] {
        opacity: 0.5;
        pointer-events: none;
    }

    /* 修改表情容器网格布局 */
    .emotion-container {
        display: grid !important;
        grid-template-columns: repeat(4, 1fr) !important;
        gap: 2px !important;
        padding: 8px 8px !important;
        box-sizing: border-box;
        justify-content: center;
        justify-items: center;
        background-color: var(--bg1, #fff) !important;
        width: 100% !important;
    }

    /* 修改表情项样式 */
    .emotion-item {
        width: 100% !important;
        height: 65px !important;
        aspect-ratio: 1;
        margin: 0 !important;
        border: 1px solid var(--line_regular, #e5e5e5);
        transition: transform 0.2s ease, box-shadow 0.2s ease;
        border-radius: 4px;
        overflow: hidden;
    }

    /* 表情项悬停效果 */
    .emotion-item:hover {
        transform: scale(1.05);
        box-shadow: 0 4px 12px var(--brand_pink_thin, rgba(251,114,153,0.2));
        border-color: var(--brand_pink, #fb7299);
    }

    /* 加载指示器 */
    .loading-indicator {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        color: var(--Ga5, #999);
        font-size: 14px;
        display: none;
    }

    .custom-panel[data-loading] .loading-indicator {
        display: block;
    }

    /* 优化滚动条 */
    .ps__scrollbar-y {
        background-color: var(--Ga5, #999) !important;
        border-radius: 3px;
    }

    .ps__scrollbar-y:hover {
        background-color: var(--Ga7, #666) !important;
    }

    /* 底栏图标样式 */
    #bili-emote-icon {
        display: inline-block;
        margin-right: 8px;
        cursor: pointer;
        vertical-align: middle;
        transition: transform 0.2s ease;
    }

    #bili-emote-icon:hover {
        transform: scale(1.1);
    }

    /* 无数据提示 */
    .no-data-tip {
        grid-column: 1 / -1;
        padding: 20px;
        text-align: center;
        color: #999;
        font-size: 14px;
    }

    /* 标签样式 */
    #bili-emote-tab {
        cursor: pointer;
        position: relative;
    }

    /* 强制覆盖B站原生样式 */
    #bili-emote-tab.active {
        border-bottom: 2px solid #23ade5 !important;
        color: #23ade5 !important;
        opacity: 1 !important;
        transform: translateZ(0);
    }

    /* 清除可能存在的B站动画干扰 */
    #bili-emote-tab {
        animation: none !important;
        transition: none !important;
    }

    /* 激活面板显示优先级 */
    .img-pane.custom-panel[style*="display: block"] {
        display: block !important;
        opacity: 1 !important;
        z-index: 9999 !important;
    }
    `;
    document.head.insertAdjacentHTML('beforeend', `<style>${css}</style>`);
    // 虚拟滚动实现
    class VirtualScroller {
        constructor(container, itemHeight, visibleCount) {
            this.container = container;
            this.itemHeight = itemHeight;
            this.visibleCount = visibleCount;
            this.scrollTop = 0;
        }

        render(data) {
            const startIndex = Math.floor(this.scrollTop / this.itemHeight);
            const endIndex = Math.min(startIndex + this.visibleCount, data.length);

            // 只渲染可见区域的元素
            const visibleItems = data.slice(startIndex, endIndex);
            this.renderItems(visibleItems, startIndex);
        }
    }
    // 实现对象池减少GC压力
    class ElementPool {
        constructor() {
            this.pool = [];
            this.maxSize = 50;
        }

        get() {
            return this.pool.pop() || this.createElement();
        }

        release(element) {
            if (this.pool.length < this.maxSize) {
                this.resetElement(element);
                this.pool.push(element);
            }
        }

        createElement() {
            return document.createElement('div');
        }

        resetElement(element) {
            element.innerHTML = '';
            element.className = '';
            element.style.cssText = '';
        }
    }
    // LRU缓存实现
    class LRUCache {
        constructor(maxSize = 100) {
            this.maxSize = maxSize;
            this.cache = new Map();
        }

        get(key) {
            if (!this.cache.has(key)) return null;
            const value = this.cache.get(key);
            this.cache.delete(key);
            this.cache.set(key, value);
            return value;
        }

        set(key, value) {
            this.cache.delete(key);
            if (this.cache.size >= this.maxSize) {
                this.cache.delete(this.cache.keys().next().value);
            }
            this.cache.set(key, value);
        }
    }

    // 控制并发请求数量
    class ConcurrencyController {
        constructor(maxConcurrency = 3) {
            this.maxConcurrency = maxConcurrency;
            this.running = 0;
            this.queue = [];
        }

        async add(promiseFactory) {
            return new Promise((resolve, reject) => {
                this.queue.push({
                    promiseFactory,
                    resolve,
                    reject
                });
                this.process();
            });
        }

        async process() {
            if (this.running >= this.maxConcurrency || this.queue.length === 0) {
                return;
            }

            this.running++;
            const { promiseFactory, resolve, reject } = this.queue.shift();

            try {
                const result = await promiseFactory();
                resolve(result);
            } catch (error) {
                reject(error);
            } finally {
                this.running--;
                this.process();
            }
        }
    }
    // 改进的图片加载策略
    class ImageLoader {
        constructor() {
            this.loadingImages = new Set();
            this.imageCache = new Map();
        }

        async loadImage(url, priority = 'normal') {
            if (this.imageCache.has(url)) {
                return this.imageCache.get(url);
            }

            if (this.loadingImages.has(url)) {
                return this.waitForImage(url);
            }

            this.loadingImages.add(url);

            try {
                const img = new Image();
                if (priority === 'high') {
                    img.loading = 'eager';
                } else {
                    img.loading = 'lazy';
                }

                const promise = new Promise((resolve, reject) => {
                    img.onload = () => resolve(img);
                    img.onerror = reject;
                });

                img.src = url;
                const result = await promise;

                this.imageCache.set(url, result);
                return result;
            } finally {
                this.loadingImages.delete(url);
            }
        }
    }

    class BiliEmotionEnhancer {
        constructor() {
            // 基础属性初始化
            this.observer = null;
            this.collectionPanel = null;
            this.collectionTab = null;
            this.emotionData = [];
            this.isInitialized = false;
            this.debug = true;
            this._isCollecting = false;
            this.urlCache = new Map();
            this.cloneCache = new Map();
            this.storageKey = 'bili-emotion-enhancer-data';
            this.currentRoomId = null;
            this.initializationAttempts = 0;
            this.maxInitAttempts = 10;
            this.initTimeout = null;
            this.isHoveringIcon = false;
            this.isHoveringPanel = false;
            this.closeEmotionPanelTimer = null;
            this.isClosingPanel = false;
            this.mouseEnterDebounceTimer = null;
            this.lastMouseY = 0;
            this.panelOpenLock = false;
            this._globalClickHandler = null;
            this.timers = new Set();
            this.saveTimeout = null;
            this.checkInterval = null;
            this.eventHandlers = new Map();


            // 配置信息
            this.config = {
                keywords: ["打call", "好听", "唱歌"],

                collectionIcon: this.getFirstEmotionIcon(),
                excludeImages: [
                    "https://i0.hdslb.com/bfs/live/fa1eb4dce3ad198bb8650499830560886ce1116c.png",
                    "https://i0.hdslb.com/bfs/live/[email protected]"
                ],
                manualCollections: [], // 新增:手动收藏的表情URL列表
                selectors: {
                    emoticonsPanel: ['.emoticons-panel', '.chat-input-tool-item[title="表情"]'],
                    tabContainer: ['.tab-pane-content[data-v-041466f0]', '.tab-pane-content'],
                    contentContainer: ['.emoticons-pane[data-v-041466f0]', '.emoticons-pane', '.emoticon-areas'],
                    emotionItem: ['.emoticon-item[data-v-041466f0]', '.emoticon-item', '.emoji-item'],
                    originalPanel: ['.img-pane[data-v-041466f0]', '.img-pane', '.content-panel'],
                    tabPaneItem: ['.tab-pane-item[data-v-041466f0]', '.tab-pane-item', '.tab-item'],
                    iconRightPart: ['.icon-right-part', '.control-buttons-row', '.chat-input-ctnr'],
                    chatInput: ['.chat-input textarea', '.chat-input input', 'textarea.input-box']
                },
                dimensions: {
                    icon: { width: 30, height: 30 },
                    panel: { width: 300, height: 192 },
                    item: { size: 65, margin: 0 }
                },
                iconId: 'bili-emote-icon',
                panelId: 'bili-emote-panel',
                tabId: '标签图标',
                checkInterval: 10000

            };

            // [!code ++] 新增:预处理关键词为Set,提高查找效率
            this.keywordSet = new Set(
                this.config.keywords.map(k => k.toLowerCase())
            );

            // [!code ++] 新增:添加分数缓存
            this.scoreCache = new Map();

            // 从localStorage读取已保存的数据
            this.loadSavedData();

            // 延迟初始化,避免阻塞页面加载
            this.deferredInit();

        }

        // 防抖方法
        debounce(func, wait) {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    clearTimeout(timeout);
                    func.apply(this, args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        }

        // 统一的定时器管理
        clearTimer(timerName) {
            if (this[timerName]) {
                if (timerName.includes('Interval')) {
                    clearInterval(this[timerName]);
                } else {
                    clearTimeout(this[timerName]);
                }
                this.timers.delete(this[timerName]);
                this[timerName] = null;
            }
        }

        // 统一的定时器管理
        clearTimer(timerName) {
            if (this[timerName]) {
                if (timerName.includes('Interval')) {
                    clearInterval(this[timerName]);
                } else {
                    clearTimeout(this[timerName]);
                }
                this.timers.delete(this[timerName]);
                this[timerName] = null;
            }
        }

        // 批量清理定时器
        clearAllTimers() {
            const timerNames = ['saveTimeout', 'closeEmotionPanelTimer', 'checkInterval', 'initTimeout', 'mouseEnterDebounceTimer'];
            timerNames.forEach(name => this.clearTimer(name));
        }

        // 统一的状态重置方法
        resetPanelState() {
            this.isClosingPanel = false;
            this.panelOpenLock = false;
            this.isHoveringPanel = false;
            this.isHoveringIcon = false;
        }

        // 统一的事件监听器清理
        removeEventListeners(element, events) {
            if (!element) return;
            Object.entries(events).forEach(([eventName, handler]) => {
                if (handler) {
                    element.removeEventListener(eventName, handler);
                }
            });
        }

        // 创建定时器的统一方法
        createTimer(name, callback, delay, isInterval = false) {
            this.clearTimer(name);

            if (isInterval) {
                this[name] = setInterval(callback, delay);
            } else {
                this[name] = setTimeout(callback, delay);
            }

            this.timers.add(this[name]);
            return this[name];
        }

        // 统一的事件监听器管理方法
        setupEventListeners(element, eventConfig, namespace = 'default') {
            if (!element) {
                this.log(`无法为 ${namespace} 设置事件监听器:元素不存在`);
                return;
            }

            // 清理该命名空间下的旧事件
            this.removeEventListeners(namespace);

            const handlers = {};

            Object.entries(eventConfig).forEach(([eventName, handler]) => {
                const wrappedHandler = (event) => {
                    try {
                        handler.call(this, event);
                    } catch (error) {
                        this.log(`事件处理器 ${namespace}.${eventName} 执行出错:`, error);
                    }
                };

                element.addEventListener(eventName, wrappedHandler);
                handlers[eventName] = {
                    element,
                    handler: wrappedHandler,
                    originalHandler: handler
                };
            });

            // 保存到事件处理器映射中
            this.eventHandlers.set(namespace, handlers);
            this.log(`已为 ${namespace} 设置 ${Object.keys(eventConfig).length} 个事件监听器`);
        }

        // 移除指定命名空间的事件监听器
        removeEventListeners(namespace) {
            const handlers = this.eventHandlers.get(namespace);
            if (!handlers) return;

            Object.entries(handlers).forEach(([eventName, config]) => {
                config.element.removeEventListener(eventName, config.handler);
            });

            this.eventHandlers.delete(namespace);
            this.log(`已移除 ${namespace} 的事件监听器`);
        }

        // 移除所有事件监听器
        removeAllEventListeners() {
            for (const namespace of this.eventHandlers.keys()) {
                this.removeEventListeners(namespace);
            }
        }


        // 新增方法
        getFirstEmotionIcon() {
            return this.emotionData.length > 0 ?
                this.emotionData[0].url :
            "https://i0.hdslb.com/bfs/live/b51824125d09923a4ca064f0c0b49fc97d3fab79.png";
        }

        // 延迟初始化
        deferredInit() {
            // 使用 requestIdleCallback 在浏览器空闲时初始化
            if (window.requestIdleCallback) {
                window.requestIdleCallback(() => this.init(), { timeout: 2000 });
            } else {
                // 降级处理:使用 setTimeout
                setTimeout(() => this.init(), 100);
            }
        }

        // 打印调试信息
        log(message, ...args) {
            if (this.debug) {
                console.log(`[BiliEmote] ${message}`, ...args);
            }
        }

        // 加载已保存的数据
        loadSavedData() {
            try {
                this.detectRoomId();
                const savedData = localStorage.getItem(this.getRoomStorageKey());

                if (savedData) {
                    const parsedData = JSON.parse(savedData);

                    // 兼容旧格式(之前只存了数组)
                    if (Array.isArray(parsedData)) {
                        this.emotionData = parsedData.map(item => ({
                            ...item,
                            element: null,
                            timestamp: item.timestamp || Date.now()
                        }));
                        this.log(`从旧格式加载了 ${this.emotionData.length} 条数据`);
                    }
                    // 新格式(包含多个字段的对象)
                    else {
                        // 加载表情数据
                        this.emotionData = (parsedData.emotions || []).map(item => ({
                            ...item,
                            element: null,
                            timestamp: item.timestamp || Date.now()
                        }));

                        // 加载手动收藏列表
                        this.config.manualCollections = parsedData.manualCollections || [];

                        this.log(`从存储加载了 ${this.emotionData.length} 条表情和 ${this.config.manualCollections.length} 个收藏`);
                    }
                }
            } catch (error) {
                this.log('加载本地数据出错', error);
                this.emotionData = [];
                this.config.manualCollections = [];
            }
        }

        // 获取当前房间的存储键名
        getRoomStorageKey() {
            return `${this.storageKey}-${this.currentRoomId || 'global'}`;
        }

        // 检测当前直播间ID
        detectRoomId() {
            try {
                const url = window.location.href;
                const match = url.match(/live\.bilibili\.com\/(?:.*?\/)?(\d+)/);
                const newRoomId = match ? match[1] : 'unknown';

                if (this.currentRoomId !== newRoomId) {
                    this.log(`直播间ID变化: ${this.currentRoomId} -> ${newRoomId}`);
                    this.currentRoomId = newRoomId;
                    this.emotionData = [];
                }

                return this.currentRoomId;
            } catch (error) {
                this.log('检测房间ID出错', error);
                return 'unknown';
            }
        }

        // 保存数据到localStorage
        saveData() {
            try {
                this.detectRoomId();
                this.clearTimer('saveTimeout');

                this.saveTimeout = setTimeout(() => {
                    // 构建要保存的数据对象
                    const storageData = {
                        // 保存表情数据
                        emotions: this.emotionData.map(item => ({
                            title: item.title,
                            normalizedUrl: item.normalizedUrl,
                            url: item.url,
                            timestamp: item.timestamp,
                            userRank: item.userRank || 0
                        })),
                        // 保存手动收藏列表
                        manualCollections: this.config.manualCollections
                    };

                    localStorage.setItem(this.getRoomStorageKey(), JSON.stringify(storageData));
                    this.log(`已保存 ${storageData.emotions.length} 个表情和 ${storageData.manualCollections.length} 个收藏到本地存储`);
                }, 500);
            } catch (error) {
                this.log('保存数据出错', error);
            }
        }


        // 修改setupGlobalTabListener方法
        setupGlobalTabListener() {
            const globalEvents = {
                'click': (e) => {
                    if (!this.collectionPanel) return;

                    const clickedTab = e.target.closest([
                        ...this.config.selectors.tabPaneItem,
                        `#${this.config.tabId}`
                    ].join(','));

                    if (clickedTab?.id !== this.config.tabId) {
                        this.collectionPanel.style.display = 'none';
                    }
                }
            };

            // 使用防抖处理
            const debouncedEvents = {
                'click': this.debounce(globalEvents.click, 150)
            };

            this.setupEventListeners(document, debouncedEvents, 'globalTab');
        }



        // 初始化插件
        init() {
            this.log("插件初始化开始");

            // 检查是否已初始化
            if (this.isInitialized) {
                this.log("插件已初始化,跳过");
                return;
            }

            // 检查页面是否准备好
            if (document.readyState !== 'complete' && document.readyState !== 'interactive') {
                this.log("页面未加载完成,延迟初始化");
                if (this.initializationAttempts < this.maxInitAttempts) {
                    this.initializationAttempts++;
                    setTimeout(() => this.init(), 500);
                }
                return;
            }

            // 设置DOM观察器
            this.setupObserver();

            // 立即尝试初始设置
            this.setupPlugin();

            this.checkInterval = setInterval(() => {
                const previousRoomId = this.currentRoomId;
                this.detectRoomId();

                if (previousRoomId !== this.currentRoomId) {
                    this.loadSavedData();
                    this.updatePanelContent();
                }
                this.setupPlugin();
            }, this.config.checkInterval);

            // 预缓存表情数据 - 延迟执行避免影响页面加载
            setTimeout(() => {
                this.preloadEmotionData();
            }, 5000);

            this.setupGlobalTabListener();

            this.isInitialized = true;

            // 启用收藏功能
            this.addCollectionFeatureToOriginalPanel();
            this.log("插件初始化完成");

        }

        // 预加载表情数据
        preloadEmotionData() {
            this.log("尝试预加载表情数据");

            // 检查表情面板是否已打开
            const panelOpen = this.checkEmoticonPanelOpen();
            if (!panelOpen) {
                // 找到表情按钮
                const emoticonsButton = this.getFirstMatchingElement(this.config.selectors.emoticonsPanel);
                if (emoticonsButton) {
                    // 暂存现有激活元素,以便恢复焦点
                    const activeElement = document.activeElement;

                    // 临时打开面板
                    emoticonsButton.click();

                    // 收集数据
                    setTimeout(() => {
                        this.collectEmotionData();

                        // 关闭面板
                        emoticonsButton.click();

                        // 恢复焦点
                        if (activeElement && document.contains(activeElement)) {
                            activeElement.focus();
                        }

                        this.log("预加载表情数据完成");
                    }, 300);
                }
            } else {
                // 面板已打开,直接收集
                this.collectEmotionData();
                this.log("面板已打开,直接预加载数据");
            }
        }

        // 设置DOM观察器
        setupObserver() {
            this.log("设置DOM观察器");
            // 如果已存在观察器,先断开连接
            if (this.observer) {
                this.observer.disconnect();
                this.observer = null;
            }

            // 节流状态变量
            let pendingMutations = [];
            let isScheduled = false; // 替代 isThrottled
            const throttleDelay = 200; // 节流时间

            this.observer = new MutationObserver((mutations) => {
                // 累积所有 mutations
                pendingMutations.push(...mutations);

                // 如果尚未安排处理,则调度处理
                if (!isScheduled) {
                    isScheduled = true;

                    setTimeout(() => {
                        // 检查累积的 mutations 是否包含相关变化
                        const relevantChange = pendingMutations.some(mutation => {
                            if (mutation.addedNodes.length > 0) {
                                return Array.from(mutation.addedNodes).some(node => {
                                    if (node.nodeType !== Node.ELEMENT_NODE) return false;
                                    return (
                                        this.getFirstMatchingElement(this.config.selectors.emoticonsPanel, node) !== null ||
                                        this.getFirstMatchingElement(this.config.selectors.contentContainer, node) !== null
                                    );
                                });
                            }
                            return false;
                        });

                        if (relevantChange) {
                            this.log("检测到相关DOM变化,重新设置插件");
                            this.setupPlugin();
                        }

                        // 检查URL变化
                        this.detectRoomId();

                        // 重置状态
                        pendingMutations = [];
                        isScheduled = false;
                    }, throttleDelay);
                }
            });

            // 优化观察范围
            const targetNode = document.querySelector('.live-room-app') || document.body;
            this.observer.observe(targetNode, { childList: true, subtree: true });

            // 🔥 替换原来的URL监听器代码为统一的事件管理
            this.setupUrlChangeListener();
        }

        // 🔥 新增这个方法
        setupUrlChangeListener() {
            // 移除旧的URL变化监听器
            this.removeEventListeners('urlChange');

            const urlEvents = {
                'popstate': () => {
                    this.log("检测到URL变化");
                    this.detectRoomId();
                    this.loadSavedData();
                }
            };

            this.setupEventListeners(window, urlEvents, 'urlChange');
        }

        queryElements(selectorList, rootElement = document, single = false) {
            const results = single ? null : [];
            for (const selector of selectorList) {
                try {
                    if (!selector || typeof selector !== 'string') continue;
                    const elements = rootElement[single ? 'querySelector' : 'querySelectorAll'](selector);
                    if (single && elements) return elements;
                    if (!single && elements?.length) results.push(...elements);
                } catch (error) {
                    this.log(`选择器错误: ${selector}`, error);
                }
            }
            return single ? null : results;
        }

        // 使用方式
        getFirstMatchingElement(selectorList, root) { return this.queryElements(selectorList, root, true); }
        getAllMatchingElements(selectorList, root) { return this.queryElements(selectorList, root, false); }



        // 主要插件设置函数
        setupPlugin() {
            // 确保房间ID是最新的
            this.detectRoomId();

            // 插入底栏图标
            this.insertBottomBarIcon();

            // 检查表情面板是否打开
            const isEmoticonPanelOpen = this.checkEmoticonPanelOpen();

            // 即使面板未打开也尝试初始化,提前准备好
            this.setupEmotionPanel();

            // 确保为其他标签也绑定事件
            if (this.isInitialized) {
                this.bindOtherTabsEvents();
            }
        }


        // 在面板状态检测中添加更精确的判断
        checkEmoticonPanelOpen() {
            // 同时检查原始面板和内容容器
            const panel = this.getFirstMatchingElement(this.config.selectors.originalPanel);
            const contentContainer = this.getFirstMatchingElement(this.config.selectors.contentContainer);

            // 确保两个元素都存在
            if (!panel || !contentContainer) return false;

            // 获取样式计算结果
            const panelStyle = window.getComputedStyle(panel);
            const containerStyle = window.getComputedStyle(contentContainer);

            // 双重验证显示状态
            return panelStyle.display !== 'none' &&
                containerStyle.display !== 'none' &&
                containerStyle.visibility !== 'hidden';
        }

        // 修改 insertBottomBarIcon 方法
        insertBottomBarIcon() {
            // 检查图标是否已存在
            const existingIcon = document.getElementById(this.config.iconId);
            if (existingIcon) return;

            // 查找目标容器
            const targetContainer = this.getFirstMatchingElement(this.config.selectors.iconRightPart);
            if (!targetContainer) {
                this.log("未找到底栏容器");
                return;
            }

            this.log("创建底栏图标");

            // 创建图标元素
            const iconElement = document.createElement('div');
            iconElement.id = this.config.iconId;
            iconElement.style.cssText = `
    display: inline-block;
    margin-right: 8px;
    cursor: pointer;
    vertical-align: middle;
  `;

            // 创建图标图片
            const iconImage = document.createElement('img');
            iconImage.src = this.getFirstEmotionIcon();
            iconImage.alt = "打call";
            iconImage.title = "打call";
            iconImage.style.cssText = `
    width: 24px;
    height: 24px;
    vertical-align: middle;
  `;

            // 添加鼠标进入事件
            iconElement.addEventListener('mouseenter', (e) => {

                // 记录当前鼠标位置
                this.lastMouseY = e.clientY;

                // 清除之前的定时器
                if (this.mouseEnterDebounceTimer) {
                    clearTimeout(this.mouseEnterDebounceTimer);
                }

                // 清除离开时设置的定时器
                if (this.leaveTimer) {
                    clearTimeout(this.leaveTimer);
                    this.leaveTimer = null;
                }

                // 从底部进入时使用更长的延迟
                const isFromBottom = e.clientY > (iconElement.getBoundingClientRect().bottom - 5);
                const debounceTime = isFromBottom ? 300 : 100;

                // 设置防抖动定时器
                this.mouseEnterDebounceTimer = setTimeout(() => {
                    this.isHoveringIcon = true;
                    this.log("鼠标进入底栏图标");

                    // 如果面板未打开且不在关闭过程中,则打开面板
                    if (!this.checkEmoticonPanelOpen() && !this.isClosingPanel) {
                        this.openEmotionPanel();
                    } else {
                        // 如果面板已打开,则取消关闭计时器
                        if (this.closeEmotionPanelTimer) {
                            clearTimeout(this.closeEmotionPanelTimer);
                            this.closeEmotionPanelTimer = null;
                        }
                    }
                }, debounceTime);
            });

            // 添加鼠标离开事件
            iconElement.addEventListener('mouseleave', (e) => {
                this.isHoveringIcon = false;

                // 强制检查面板是否被悬停(通过位置判断)
                const panelRect = this.collectionPanel?.getBoundingClientRect();
                let isMouseOverPanel = false;

                if (panelRect) {
                    isMouseOverPanel = (
                        e.clientX >= panelRect.left &&
                        e.clientX <= panelRect.right &&
                        e.clientY >= panelRect.top &&
                        e.clientY <= panelRect.bottom
                    );
                    this.isHoveringPanel = isMouseOverPanel;
                }

                // 清除之前可能存在的离开定时器
                if (this.leaveTimer) {
                    clearTimeout(this.leaveTimer);
                }

                // 只有在离开底栏图标时面板依旧被悬停才设置计时器
                if (!isMouseOverPanel) {
                    // 设置200ms的计时器
                    this.leaveTimer = setTimeout(() => {
                        // 如果鼠标没有重新进入图标或进入面板,则点击表情按钮
                        if (!this.isHoveringIcon && !this.isHoveringPanel) {
                            this.log("离开200ms后点击底栏图标左侧10px处");

                            // 获取底栏图标的位置
                            const iconRect = iconElement.getBoundingClientRect();
                            // 计算左侧10px处的坐标
                            const clickX = iconRect.left - 10;
                            const clickY = iconRect.top + (iconRect.height / 2);

                            // 获取该坐标处的元素
                            const elementAtPoint = document.elementFromPoint(clickX, clickY);

                            if (elementAtPoint) {
                                // 创建并触发点击事件
                                const clickEvent = new MouseEvent('click', {
                                    view: window,
                                    bubbles: true,
                                    cancelable: true,
                                    clientX: clickX,
                                    clientY: clickY
                                });
                                elementAtPoint.dispatchEvent(clickEvent);
                            } else {
                                this.log("无法在指定位置找到元素");
                            }
                        }
                    }, 200);
                }

                this.scheduleCloseEmotionPanel();
            });

            // 修改底栏图标点击事件
            iconElement.addEventListener('click', async (e) => {
                e.preventDefault();
                e.stopImmediatePropagation();
                this.log("底栏图标被点击 - 尝试发送第一个表情包");

                // 确保面板已打开,这是关键修改
                if (!this.checkEmoticonPanelOpen()) {
                    await this.openEmotionPanel();
                }

                // 强制重新收集数据
                await this.collectEmotionData();

                // 查找第一个表情包
                if (this.emotionData && this.emotionData.length > 0) {
                    // 获取排名第一的表情,或者默认第一个
                    const sortedEmotions = [...this.emotionData].sort((a, b) => (b.userRank || 0) - (a.userRank || 0));
                    const firstEmotion = sortedEmotions[0];
                    this.log("发送第一个表情包", firstEmotion.title || "未命名表情");

                    // 尝试在原始面板中查找并点击对应表情
                    const success = this.findAndClickByUrl(firstEmotion.url, firstEmotion.title);
                    if (!success) {
                        this.log("无法找到匹配的表情元素进行点击", firstEmotion.url);
                    }

                    // 增加使用次数统计
                    firstEmotion.userRank = (firstEmotion.userRank || 0) + 1;
                    this.saveData();

                    // 关闭面板前重置状态
                    this.isClosingPanel = false;
                    this.panelOpenLock = false;

                    // 关闭面板
                    if (this.checkEmoticonPanelOpen()) {
                        this.closeEmotionPanel();
                    }
                } else {
                    this.log("没有可用的表情包");
                }
            });

            // 添加图片到图标
            iconElement.appendChild(iconImage);

            // 插入到页面
            targetContainer.insertBefore(iconElement, targetContainer.firstChild);
            this.log("底栏图标已插入");
        }

        // 修改安排关闭面板的方法
        scheduleCloseEmotionPanel() {
            this.clearTimer('closeEmotionPanelTimer');

            this.closeEmotionPanelTimer = setTimeout(() => {
                const panelVisible = this.checkEmoticonPanelOpen();

                if (!panelVisible) {
                    this.closeEmotionPanel();
                    return;
                }

                if (!this.isHoveringIcon && !this.isHoveringPanel) {
                    this.closeEmotionPanel();
                }
            }, 300);

            this.timers.add(this.closeEmotionPanelTimer);
        }


        // 修改打开方法增加状态锁检查
        async openEmotionPanel() {
            if (this.isClosingPanel || this.panelOpenLock) {
                this.log("阻止打开:正在关闭或已锁定");
                return;
            }
            // 设置锁定状态
            this.panelOpenLock = true;
            // 查找并点击表情按钮
            const emoticonsButton = this.getFirstMatchingElement(this.config.selectors.emoticonsPanel);
            if (emoticonsButton) {
                this.log("打开表情面板");
                emoticonsButton.click();

                // 等待表情面板打开和加载
                await this.waitForPanelLoad();

                // 设置和收集数据
                this.setupEmotionPanel();

                // 等待标签加载完成
                await this.waitForTabLoad();

                // 点击标签并等待内容加载
                const tabElement = document.getElementById(this.config.tabId);
                if (tabElement) {
                    this.log("点击打call标签");
                    tabElement.click();

                    // 等待内容加载完成
                    await this.waitForContentLoad();

                    // 面板已加载完成,绑定面板的鼠标事件
                    this.bindPanelHoverEvents();
                } else {
                    this.log("未找到打call标签");
                }
            } else {
                this.log("未找到表情按钮");
            }
        }

        // 修改关闭面板的方法
        closeEmotionPanel() {
            if (this.isClosingPanel) return;
            this.isClosingPanel = true;

            const emoticonsButton = this.getFirstMatchingElement(this.config.selectors.emoticonsPanel);
            if (emoticonsButton && this.checkEmoticonPanelOpen()) {
                this.log("关闭表情面板");
                emoticonsButton.click();
            }

            // 新增:重置悬停状态 // [!code ++]
            this.isHoveringPanel = false; // [!code ++]
            this.isHoveringIcon = false;  // [!code ++]

            setTimeout(() => {
                this.isClosingPanel = false;
                this.panelOpenLock = false;
            }, 200);
        }


        // 在bindPanelHoverEvents中确保正确移除旧事件监听器
        bindPanelHoverEvents() {
            const contentContainer = this.getFirstMatchingElement(this.config.selectors.contentContainer);
            if (!contentContainer) {
                this.log("未找到表情面板容器,无法绑定鼠标事件");
                return;
            }

            // 使用统一的事件管理器
            const panelEvents = {
                'mouseenter': () => {
                    this.isHoveringPanel = true;
                    this.log("鼠标进入面板");
                    // 清除所有关闭计时器
                    this.clearTimer('closeEmotionPanelTimer');
                },
                'mouseleave': () => {
                    this.isHoveringPanel = false;
                    this.log("鼠标离开面板");
                    // 立即触发关闭检查
                    this.scheduleCloseEmotionPanel();
                }
            };

            this.setupEventListeners(contentContainer, panelEvents, 'panelHover');
        }


        // 修改 waitForPanelLoad 方法,在面板加载完成后绑定鼠标事件
        async waitForPanelLoad() {
            return new Promise((resolve) => {
                const checkPanel = () => {
                    const contentContainer = this.getFirstMatchingElement(this.config.selectors.contentContainer);
                    if (contentContainer && window.getComputedStyle(contentContainer).display !== 'none') {
                        // 面板已加载,绑定鼠标事件
                        this.bindPanelHoverEvents();
                        resolve();
                    } else {
                        setTimeout(checkPanel, 100);
                    }
                };
                checkPanel();
            });
        }
        async waitForTabLoad() {
            return new Promise((resolve) => {
                const checkTab = () => {
                    const tabElement = document.getElementById(this.config.tabId);
                    if (tabElement) {
                        resolve();
                    } else {
                        setTimeout(checkTab, 100);
                    }
                };
                checkTab();
            });
        }

        async waitForContentLoad() {
            return new Promise((resolve) => {
                const checkContent = () => {
                    if (this.collectionPanel && this.emotionData.length > 0) {
                        resolve();
                    } else {
                        setTimeout(checkContent, 100);
                    }
                };
                checkContent();
            });
        }

        // 修改 setupEmotionPanel 方法
        setupEmotionPanel() {
            this.log("设置表情面板");

            // 设置加载状态
            if (this.collectionPanel) {
                this.collectionPanel.setAttribute('data-loading', 'true');
            }

            // 查找必要的容器
            const tabContainer = this.getFirstMatchingElement(this.config.selectors.tabContainer);
            const contentContainer = this.getFirstMatchingElement(this.config.selectors.contentContainer);

            if (!tabContainer || !contentContainer) {
                this.log("未找到表情面板容器", {
                    tabContainer: !!tabContainer,
                    contentContainer: !!contentContainer
                });
                return;
            }

            // 检查是否已创建
            const existingTab = document.getElementById(this.config.tabId);
            const existingPanel = document.getElementById(this.config.panelId);

            if (existingTab && existingPanel) {
                this.log("表情面板已存在");

                // 收集表情数据
                this.collectEmotionData().then(() => {
                    // 移除加载状态
                    if (this.collectionPanel) {
                        this.collectionPanel.removeAttribute('data-loading');
                    }
                });
                return;
            }

            // 创建标签和面板
            this.createEmotionTab(tabContainer);
            this.createEmotionPanel(contentContainer);

            // 收集表情数据
            this.collectEmotionData().then(() => {
                // 移除加载状态
                if (this.collectionPanel) {
                    this.collectionPanel.removeAttribute('data-loading');
                }
            });

            // 绑定事件
            this.bindEvents();

            this.log("表情面板设置完成");
        }

        // 绑定事件处理
        bindEvents() {
            this.log("绑定面板事件");

            // 确保tab存在
            if (!this.collectionTab) {
                this.log("无法绑定事件:未找到tab元素");
                return;
            }

            // 绑定tab点击事件 - 显示我们的面板,隐藏其他面板
            this.collectionTab.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();

                this.log("打call标签被点击");

                // 获取所有可能的面板
                const allPanels = this.getAllMatchingElements(this.config.selectors.originalPanel);

                // 隐藏所有原始面板
                allPanels.forEach(panel => {
                    if (panel.id !== this.config.panelId) {
                        panel.style.display = 'none';
                    }
                });

                // 显示我们的自定义面板
                if (this.collectionPanel) {
                    this.collectionPanel.style.display = 'block';
                }

                // 清除所有原生标签的active类(包括可能被B站添加的)
                const allTabs = this.getAllMatchingElements(this.config.selectors.tabPaneItem);
                allTabs.forEach(tab => {
                    tab.classList.remove('active', 'selected'); // 兼容不同类名
                });

                // 强制设置当前标签为active
                this.collectionTab.classList.add('active');

                // 收集并更新表情数据
                this.collectEmotionData();
            });

            // 为其他标签添加点击事件监听
            this.bindOtherTabsEvents();
        }

        // 为其他标签绑定点击事件
        bindOtherTabsEvents() {
            const allTabs = this.getAllMatchingElements(this.config.selectors.tabPaneItem);

            allTabs.forEach((tab, index) => {
                // 跳过我们自己的标签
                if (tab.id === this.config.tabId) return;

                const tabEvents = {
                    'click': (e) => {
                        // 不阻止事件传播,让原生事件处理器也能执行
                        this.log(`点击了其他标签: ${tab.textContent || tab.id}`);

                        // 隐藏我们的面板
                        if (this.collectionPanel) {
                            this.collectionPanel.style.display = 'none';
                        }

                        // 移除我们标签的active状态
                        if (this.collectionTab) {
                            this.collectionTab.classList.remove('active');
                        }
                    }
                };

                // 为每个标签使用唯一的命名空间
                this.setupEventListeners(tab, tabEvents, `tab-${index}`);

                // 标记为已绑定
                tab.setAttribute('data-enhancer-bound', 'true');
            });
        }


        // 创建表情标签
        createEmotionTab(container) {
            this.log("创建打call标签");

            // 检查是否已存在
            if (document.getElementById(this.config.tabId)) return;

            // 创建标签元素
            const tabElement = document.createElement('div');
            tabElement.classList.remove('active');
            tabElement.id = this.config.tabId;
            tabElement.className = 'tab-pane-item';

            // 尝试添加相同的属性
            const existingTab = this.getFirstMatchingElement(this.config.selectors.tabPaneItem);
            if (existingTab) {
                // 复制数据属性
                const attributes = existingTab.attributes;
                for (let i = 0; i < attributes.length; i++) {
                    const attr = attributes[i];
                    if (attr.name.startsWith('data-')) {
                        tabElement.setAttribute(attr.name, attr.value);
                    }
                }

                // 复制类名,确保样式一致
                tabElement.className = existingTab.className;
            }

            // 创建图标
            const imgElement = document.createElement('img');
            imgElement.className = '标签图片';
            imgElement.src = this.config.collectionIcon;
            imgElement.style.width = this.config.dimensions.icon.width + 'px';
            imgElement.style.height = this.config.dimensions.icon.height + 'px';
            imgElement.alt = "打call";
            imgElement.title = "打call";

            // 复制图片的数据属性
            if (existingTab) {
                const existingImg = existingTab.querySelector('img');
                if (existingImg) {
                    const attributes = existingImg.attributes;
                    for (let i = 0; i < attributes.length; i++) {
                        const attr = attributes[i];
                        if (attr.name.startsWith('data-')) {
                            imgElement.setAttribute(attr.name, attr.value);
                        }
                    }
                }
            }

            // 添加图标到标签
            tabElement.appendChild(imgElement);

            // 插入到容器的第一个位置
            if (container.firstChild) {
                container.insertBefore(tabElement, container.firstChild);
            } else {
                container.appendChild(tabElement);
            }

            this.collectionTab = tabElement;
            this.log("打call标签已创建");
        }

        // 创建我们的面板时,确保它能够正确叠加在B站原始面板上
        createEmotionPanel(container) {
            this.log("创建打call面板");

            // 检查面板是否已存在
            const existingPanel = document.getElementById(this.config.panelId);
            if (existingPanel) {
                this.log("打call面板已存在,无需重新创建");
                this.collectionPanel = existingPanel;
                return;
            }

            // 创建面板元素
            const panelElement = document.createElement('div');
            panelElement.id = this.config.panelId;
            panelElement.className = 'img-pane custom-panel';

            // 添加关键样式使其覆盖其他面板
            panelElement.style.width = this.config.dimensions.panel.width + 'px';
            panelElement.style.height = this.config.dimensions.panel.height + 'px';
            panelElement.style.display = 'none'; // 初始不显示
            panelElement.style.backgroundColor = '#fff';
            panelElement.style.position = 'relative';
            panelElement.style.zIndex = '9999';
            panelElement.style.overflow = 'auto';

            // 确保面板显示在容器的最上层
            panelElement.style.top = '0';
            panelElement.style.left = '0';

            // 添加加载指示器
            const loadingIndicator = document.createElement('div');
            loadingIndicator.className = 'loading-indicator';
            loadingIndicator.textContent = '加载中...';
            loadingIndicator.style.position = 'absolute';
            loadingIndicator.style.top = '50%';
            loadingIndicator.style.left = '50%';
            loadingIndicator.style.transform = 'translate(-50%, -50%)';
            loadingIndicator.style.color = '#999';
            loadingIndicator.style.fontSize = '14px';

            panelElement.appendChild(loadingIndicator);

            // 添加到容器
            container.appendChild(this.collectionPanel = panelElement);

            this.collectionPanel = panelElement;
            this.log("打call面板已创建");
        }

        // 修改后的表情项克隆方法
        optimizedCloneEmotionItem(emotionItem) {
            // 使用缓存避免重复克隆
            if (this.cloneCache.has(emotionItem)) {
                return this.cloneCache.get(emotionItem);
            }

            // 完整深度克隆表情元素及其所有子元素和属性
            const clone = emotionItem.cloneNode(true);

            // 查找并优化图片元素
            const img = clone.querySelector('img');
            if (img) {
                img.loading = 'eager';
                img.decoding = 'async';

                // 保持原始宽高比例
                img.style.cssText = `
      width: ${this.config.dimensions.item.size}px;
      height: ${this.config.dimensions.item.size}px;
      object-fit: contain;
    `;
            }

            // 保留所有CSS类,确保未解锁状态能够正确显示
            const lockIndicator = clone.querySelector('.lock-indicator, .disabled, .unavailable');
            if (lockIndicator) {
                // 确保锁图标样式正确显示
                lockIndicator.style.display = 'block';
            }

            // 缓存克隆结果
            this.cloneCache.set(emotionItem, clone);
            return clone;
        }

        // 新增方法:更新所有相关图标
        updateCollectionIcon() {
            // 更新底栏图标
            const icon = document.getElementById(this.config.iconId);
            if (icon) {
                const img = icon.querySelector('img');
                if (img) {
                    img.src = this.getFirstEmotionIcon();
                    img.onerror = () => {
                        img.src = "https://i0.hdslb.com/bfs/live/b51824125d09923a4ca064f0c0b49fc97d3fab79.png";
                    };
                }
            }

            // 更新标签页图标
            const tabImg = document.getElementById(this.config.tabId)?.querySelector('img');
            if (tabImg) {
                tabImg.src = this.getFirstEmotionIcon();
            }
        }

        // 修改 collectEmotionData 方法,添加防抖功能
        async collectEmotionData() {
            // 防止短时间内重复收集
            if (this._isCollecting) {
                this.log("表情收集正在进行中,跳过此次收集");
                return;
            }

            // 设置收集状态
            this._isCollecting = true;
            this.log("开始收集表情数据");

            try {
                // 确保房间ID是最新的
                this.detectRoomId();

                // 获取所有表情元素
                const allEmotions = this.getAllMatchingElements(this.config.selectors.emotionItem);
                this.log(`找到 ${allEmotions.length} 个表情元素`);

                if (allEmotions.length === 0) {
                    this.log("未找到表情元素,可能需要先打开其他表情标签");
                    // 如果已有缓存数据,不清空,直接更新面板
                    if (this.emotionData.length > 0) {
                        this.updatePanelContent();
                    }
                    return;
                }

                // 保留现有的userRank信息
                const existingRanks = new Map();
                this.emotionData.forEach(item => {
                    existingRanks.set(item.normalizedUrl, {
                        userRank: item.userRank || 0,
                        timestamp: item.timestamp
                    });
                });

                // 收集新数据前清空现有的数据
                this.emotionData = [];
                const seenUrls = new Set(); // 用于去重

                // 使用 Promise.all 并行处理表情数据
                await Promise.all(allEmotions.map(async item => {
                    const imgElement = item.querySelector('img');
                    if (!imgElement || !imgElement.src) return;

                    const title = item.getAttribute('title') || '';
                    const url = imgElement.src;
                    const normalizedUrl = this.getNormalizedUrl(url);

                    // 去重检查
                    if (seenUrls.has(normalizedUrl)) return;
                    seenUrls.add(normalizedUrl);

                    // 检查是否匹配关键词(原有逻辑)
                    const matchesKeyword = this.keywordSet.has(title.toLowerCase()) ||
                          Array.from(this.keywordSet).some(keyword =>
                                                           title.toLowerCase().includes(keyword)
                                                          );

                    // 新增:检查是否在手动收藏列表中
                    const isManuallyCollected = this.config.manualCollections.some(collectedUrl => {
                        const normalizedCollectedUrl = this.getNormalizedUrl(collectedUrl);
                        return normalizedUrl === normalizedCollectedUrl;
                    });

                    // 排除检查
                    const isExcluded = this.config.excludeImages.some(excludeUrl => {
                        const normalizedExcludeUrl = this.getNormalizedUrl(excludeUrl);
                        return normalizedUrl === normalizedExcludeUrl ||
                            normalizedUrl.includes(normalizedExcludeUrl);
                    });

                    // 修改条件:关键词匹配 OR 手动收藏,且不在排除列表中
                    if ((matchesKeyword || isManuallyCollected) && !isExcluded) {
                        // 保留现有排名和时间戳
                        const existing = existingRanks.get(normalizedUrl) || {
                            userRank: 0,
                            timestamp: Date.now()
                        };

                        // 预加载图片以提高性能
                        await this.preloadImage(url);

                        this.emotionData.push({
                            element: item,
                            title: title,
                            url: url,
                            normalizedUrl: normalizedUrl,
                            userRank: existing.userRank,
                            timestamp: existing.timestamp,
                            isManuallyAdded: isManuallyCollected && !matchesKeyword // 标记手动添加
                        });
                    }
                }));

                // 应用稳定排序
                this.sortEmotionData();

                // 新增:更新图标 // [!code ++]
                if (this.emotionData.length > 0) {
                    this.config.collectionIcon = this.emotionData[0].url;
                    this.updateCollectionIcon(); // [!code ++]
                }

                this.log(`收集完成,共有 ${this.emotionData.length} 个表情`);

                // 保存到本地存储
                this.saveData();

                // 更新面板内容
                this.updatePanelContent();
            } catch (error) {
                this.log("收集表情数据出错", error);
            } finally {
                // 收集完成后重置标志
                setTimeout(() => {
                    this._isCollecting = false;
                    this.log("表情收集状态重置");
                }, 500); // 500ms防抖
            }
        }

        // 预加载图片
        preloadImage(url) {
            return new Promise((resolve) => {
                const img = new Image();
                img.onload = () => resolve();
                img.onerror = () => resolve(); // 即使加载失败也继续
                img.src = url;
            });
        }
        // 添加表情到手动收藏
        addToManualCollection(url, title) {
            const normalizedUrl = this.getNormalizedUrl(url);

            // 检查是否已经存在
            const alreadyExists = this.config.manualCollections.some(collectedUrl =>
                                                                     this.getNormalizedUrl(collectedUrl) === normalizedUrl
                                                                    );

            if (!alreadyExists) {
                this.config.manualCollections.push(url);
                this.saveData();
                this.log(`手动添加表情: ${title || '未命名表情'}`);

                // 重新收集数据以更新面板
                this.collectEmotionData();
                return true;
            } else {
                this.log(`表情已存在于收藏中: ${title || '未命名表情'}`);
                return false;
            }
        }

        // 从手动收藏中移除表情
        removeFromManualCollection(url) {
            const normalizedUrl = this.getNormalizedUrl(url);
            const originalLength = this.config.manualCollections.length;

            this.config.manualCollections = this.config.manualCollections.filter(collectedUrl =>
                                                                                 this.getNormalizedUrl(collectedUrl) !== normalizedUrl
                                                                                );

            if (this.config.manualCollections.length < originalLength) {
                this.saveData();
                this.log(`移除手动收藏表情: ${url}`);

                // 重新收集数据以更新面板
                this.collectEmotionData();
                return true;
            }
            return false;
        }

        // 修正后的 sortEmotionData 方法
        sortEmotionData() {
            // 计算每个表情的匹配分数和关键词索引
            this.emotionData.forEach(item => {
                item.emotionScore = this.getKeywordMatchScore(item.title);
                item.keywordIndex = this.getKeywordIndex(item.title); // 新增:计算关键词索引
            });

            // 应用排序
            this.emotionData.sort((a, b) => {
                // 第一条件:按 userRank 降序
                if (a.userRank !== b.userRank) {
                    return b.userRank - a.userRank;
                }
                // 第二条件:按关键词匹配度(emotionScore)降序
                if (a.emotionScore !== b.emotionScore) {
                    return b.emotionScore - a.emotionScore;
                }
                // 第三条件:按关键词顺序升序(索引小的排前面)
                if (a.keywordIndex !== b.keywordIndex) {
                    return a.keywordIndex - b.keywordIndex;
                }
                // 第四条件:如果关键词索引也相同,则按URL字母序作为最终稳定排序
                return a.normalizedUrl.localeCompare(b.normalizedUrl);
            });
        }
        // 获取表情标题匹配的关键词索引
        getKeywordIndex(title) {
            const lowerTitle = title.toLowerCase();

            // 遍历关键词数组,返回第一个匹配的关键词索引
            for (let i = 0; i < this.config.keywords.length; i++) {
                const keyword = this.config.keywords[i].toLowerCase();

                // 完全匹配优先
                if (lowerTitle === keyword) {
                    return i;
                }
                // 包含匹配
                if (lowerTitle.includes(keyword)) {
                    return i;
                }
                // 开头匹配
                if (lowerTitle.startsWith(keyword)) {
                    return i;
                }

            }

            // 如果没有匹配到任何关键词,返回一个较大的数值,排到后面
            return this.config.keywords.length;
        }


        // 计算标题与关键词的匹配分数
        getKeywordMatchScore(title) {
            let score = 0;
            const lowerTitle = title.toLowerCase(); // 只计算一次

            // 使用缓存避免重复计算
            if (this.scoreCache.has(lowerTitle)) {
                return this.scoreCache.get(lowerTitle);
            }

            this.config.keywords.forEach(keyword => {
                const lowerKeyword = keyword.toLowerCase();
                if (lowerTitle.includes(lowerKeyword)) {
                    if (lowerTitle === lowerKeyword) {
                        score += 10;
                    } else if (lowerTitle.startsWith(lowerKeyword)) {
                        score += 5;
                    } else {
                        score += 2;
                    }
                }
            });

            // 缓存结果
            this.scoreCache.set(lowerTitle, score);
            return score;
        }

        // 更新面板内容
        updatePanelContent() {
            this.log("更新面板内容", this.emotionData.length);

            // 确保面板存在
            if (!this.collectionPanel) {
                this.log("无法更新面板:未找到面板元素");
                return;
            }

            // 清空现有内容
            this.collectionPanel.innerHTML = '';

            // 如果没有数据,显示提示信息
            if (this.emotionData.length === 0) {
                const noDataMsg = document.createElement('div');
                noDataMsg.textContent = '未找到匹配的表情,请点击其他表情标签收集';
                noDataMsg.style.cssText = `
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            color: #999;
            font-size: 14px;
            text-align: center;
            width: 80%;
        `;
                this.collectionPanel.appendChild(noDataMsg);
                return;
            }

            // 创建容器
            const container = document.createElement('div');
            container.className = 'emotion-container';
            container.style.cssText = `
        display: grid;
        flex-wrap: wrap;
        padding: 8px;
        justify-content: flex-start;
    `;

            // 使用 DocumentFragment 提高性能
            const fragment = document.createDocumentFragment();

            // 添加表情项
            this.emotionData.forEach(item => {
                // 验证URL的有效性 - 如果URL不存在则跳过
                if (!item.url || !item.normalizedUrl) {
                    return;
                }

                // 使用条件判断选择元素创建方式
                if (item.element instanceof Element) {
                    // 如果有原始元素引用,使用专用方法克隆
                    const clonedItem = this.optimizedCloneEmotionItem(item.element);

                    // 修改表情点击事件处理
                    clonedItem.addEventListener('click', () => {
                        this.log("表情被点击", item.title);
                        item.userRank = (item.userRank || 0) + 1;
                        this.saveData();
                        const success = this.findAndClickByUrl(item.url, item.title);
                        if (!success) {
                            this.log("无法找到匹配的表情元素进行点击", item.url);
                        }
                    });

                    // 悬停效果
                    clonedItem.addEventListener('mouseover', () => {
                        clonedItem.style.backgroundColor = '#f5f5f5';
                    });

                    clonedItem.addEventListener('mouseout', () => {
                        clonedItem.style.backgroundColor = 'transparent';
                    });

                    // 添加右键菜单事件(修正位置)
                    clonedItem.addEventListener('click', (e) => {
                        // 检查是否按住了alt键
                        if (e.altKey && item.isManuallyAdded) {
                            e.preventDefault();
                            e.stopPropagation();
                            e.stopImmediatePropagation();

                            if (confirm(`是否要从收藏中移除 "${item.title || '未命名表情'}"?`)) {
                                this.removeFromManualCollection(item.url);
                                this.showRemoveSuccess(item.title);
                            }
                            return;
                        }

                        // 原有的点击逻辑
                        this.log("表情被点击", item.title);
                        item.userRank = (item.userRank || 0) + 1;
                        this.saveData();
                        const success = this.findAndClickByUrl(item.url, item.title);
                        if (!success) {
                            this.log("无法找到匹配的表情元素进行点击", item.url);
                        }
                    });

                    // 使用克隆后的元素
                    fragment.appendChild(clonedItem);
                } else {
                    // 如果没有原始元素引用,使用自定义创建方式
                    const itemElement = document.createElement('div');
                    itemElement.className = 'emotion-item';
                    itemElement.style.cssText = `
                width: ${this.config.dimensions.item.size}px;
                height: ${this.config.dimensions.item.size}px;
                margin: ${this.config.dimensions.item.margin}px;
                cursor: pointer;
                display: grid;
                align-items: center;
                justify-content: center;
                border-radius: 4px;
                transition: background-color 0.2s;
            `;

                    // 悬停效果
                    itemElement.addEventListener('mouseover', () => {
                        itemElement.style.backgroundColor = '#f5f5f5';
                    });

                    itemElement.addEventListener('mouseout', () => {
                        itemElement.style.backgroundColor = 'transparent';
                    });

                    // 创建图片
                    const imgElement = document.createElement('img');
                    imgElement.src = item.url;
                    imgElement.alt = item.title;
                    imgElement.title = item.title;
                    imgElement.style.cssText = `
                max-width: 80%;
                max-height: 80%;
                object-fit: contain;
            `;

                    // 添加图片加载错误处理
                    imgElement.onerror = () => {
                        this.emotionData = this.emotionData.filter(
                            emote => emote.normalizedUrl !== item.normalizedUrl
                        );

                        if (itemElement.parentNode) {
                            itemElement.parentNode.removeChild(itemElement);
                        }

                        if (this.emotionData.length === 0 && this.collectionPanel) {
                            this.updatePanelContent();
                        }

                        this.saveData();
                    };

                    // 添加图片到表情元素
                    itemElement.appendChild(imgElement);

                    // 添加点击事件处理
                    itemElement.addEventListener('click', () => {
                        this.log("表情被点击", item.title);
                        item.userRank = (item.userRank || 0) + 1;
                        this.saveData();
                        const success = this.findAndClickByUrl(item.url, item.title);
                        if (!success) {
                            this.log("无法找到匹配的表情元素进行点击", item.url);
                        }
                    });

                    // 为自定义创建的元素也添加右键菜单
                    itemElement.addEventListener('contextmenu', (e) => {
                        e.preventDefault();
                        if (item.isManuallyAdded) {
                            this.removeFromManualCollection(item.url);
                            this.showRemoveSuccess(item.title);
                        }
                    });


                    // 添加自定义创建的元素
                    fragment.appendChild(itemElement);
                }
            });

            // 将容器添加到面板
            container.appendChild(fragment);
            this.collectionPanel.appendChild(container);
        }

        // 为原始表情面板添加收藏功能 - 修改为alt+左键收藏/移除
        addCollectionFeatureToOriginalPanel() {
            // 使用mousedown事件在捕获阶段拦截
            document.addEventListener('mousedown', (e) => {
                // 只处理左键+alt的情况
                if (e.button !== 0 || !e.altKey) return;

                const emotionElement = e.target.closest('.emotion-item, .emoticon-item, [class*="emotion"], [class*="emoticon"]');
                if (emotionElement) {
                    const imgElement = emotionElement.querySelector('img');
                    if (imgElement && imgElement.src) {
                        // 立即阻止所有后续事件
                        e.preventDefault();
                        e.stopPropagation();
                        e.stopImmediatePropagation();

                        const title = emotionElement.getAttribute('title') ||
                              emotionElement.getAttribute('alt') ||
                              imgElement.getAttribute('alt') ||
                              imgElement.getAttribute('title') || '';
                        const url = imgElement.src;
                        const normalizedUrl = this.getNormalizedUrl(url);

                        // 检查是否已经收藏
                        const isAlreadyCollected = this.config.manualCollections.some(collectedUrl =>
                                                                                      this.getNormalizedUrl(collectedUrl) === normalizedUrl
                                                                                     );

                        if (isAlreadyCollected) {
                            // 如果已收藏,则移除
                            this.removeFromManualCollection(url);
                            this.showRemoveSuccess(title);
                        } else {
                            // 如果未收藏,则添加
                            this.addToManualCollection(url, title);
                            this.showCollectionSuccess(title);
                        }

                        // 阻止后续的click事件
                        this.blockNextClick = true;
                        setTimeout(() => {
                            this.blockNextClick = false;
                        }, 100);
                    }
                }
            }, true); // 使用捕获阶段

            // 额外添加click事件拦截器
            document.addEventListener('click', (e) => {
                if (this.blockNextClick || (e.altKey && this.isEmotionElement(e.target))) {
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                }
            }, true);

            // 同样处理mouseup事件
            document.addEventListener('mouseup', (e) => {
                if (this.blockNextClick || (e.altKey && this.isEmotionElement(e.target))) {
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                }
            }, true);
        }


        // 辅助方法:判断是否为表情元素
        isEmotionElement(target) {
            const emotionElement = target.closest('.emotion-item, .emoticon-item, [class*="emotion"], [class*="emoticon"]');
            return emotionElement && emotionElement.querySelector('img');
        }

        // 显示收藏成功提示(保持不变)
        showCollectionSuccess(title) {
            // 移除已存在的提示
            const existingToast = document.getElementById('emotion-collection-toast');
            if (existingToast) {
                existingToast.remove();
            }

            const toast = document.createElement('div');
            toast.id = 'emotion-collection-toast';
            toast.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: #4CAF50;
        color: white;
        padding: 12px 20px;
        border-radius: 4px;
        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
        z-index: 10001;
        font-size: 14px;
        opacity: 0;
        transition: opacity 0.3s ease;
        pointer-events: none;
    `;
            toast.textContent = `✓ 已收藏: ${title || '表情'}`;

            document.body.appendChild(toast);

            requestAnimationFrame(() => {
                toast.style.opacity = '1';
            });

            setTimeout(() => {
                toast.style.opacity = '0';
                setTimeout(() => {
                    if (toast.parentNode) {
                        toast.parentNode.removeChild(toast);
                    }
                }, 300);
            }, 3000);
        }
        // 新增移除成功提示
        showRemoveSuccess(title) {
            const toast = document.createElement('div');
            toast.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: #f44336;
        color: white;
        padding: 12px 20px;
        border-radius: 4px;
        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
        z-index: 10001;
        font-size: 14px;
        opacity: 0;
        transition: opacity 0.3s ease;
    `;
            toast.textContent = `✗ 已移除: ${title || '表情'}`;

            document.body.appendChild(toast);

            requestAnimationFrame(() => {
                toast.style.opacity = '1';
            });

            setTimeout(() => {
                toast.style.opacity = '0';
                setTimeout(() => {
                    if (toast.parentNode) {
                        toast.parentNode.removeChild(toast);
                    }
                }, 300);
            }, 3000);
        }

        // 优化的查找元素方法
        findFreshElementByUrl(url) {
            const normalizedTargetUrl = this.getNormalizedUrl(url);
            this.log("查找URL匹配的最新元素", normalizedTargetUrl);

            // 获取所有可能的表情元素
            const allEmotions = this.getAllMatchingElements(this.config.selectors.emotionItem);

            // 使用Map存储结果以提高查找效率
            const urlToElementMap = new Map();

            // 遍历所有元素查找匹配的URL
            for (const item of allEmotions) {
                const imgElement = item.querySelector('img');
                if (!imgElement || !imgElement.src) continue;

                const itemUrl = imgElement.src;
                const normalizedItemUrl = this.getNormalizedUrl(itemUrl);

                // 存储到Map
                urlToElementMap.set(normalizedItemUrl, item);

                // 如果找到精确匹配,可以提前返回
                if (normalizedItemUrl === normalizedTargetUrl) {
                    this.log("找到URL精确匹配的最新元素");
                    return item;
                }
            }

            // 如果没有精确匹配,尝试部分匹配
            for (const [mappedUrl, element] of urlToElementMap.entries()) {
                if ((mappedUrl.includes(normalizedTargetUrl) && normalizedTargetUrl.length > 10) ||
                    (normalizedTargetUrl.includes(mappedUrl) && mappedUrl.length > 10)) {
                    this.log("找到URL部分匹配的元素");
                    return element;
                }
            }

            return null;
        }

        // 新增:尝试各种方法通过URL查找并点击表情
        findAndClickByUrl(url, title) {
            const normalizedTargetUrl = this.getNormalizedUrl(url);
            this.log("查找URL匹配的所有可能元素", normalizedTargetUrl);

            // 各种可能的选择器
            const selectors = [
                ...this.config.selectors.emotionItem,
                'img[src*="hdslb"]',
                '.emotion-item',
                '.emoticon-item',
                '.emoji-item',
                '.emoji'
            ];

            // 遍历选择器尝试查找元素
            for (const selector of selectors) {
                try {
                    const elements = document.querySelectorAll(selector);
                    this.log(`选择器 ${selector} 找到 ${elements.length} 个元素`);

                    // 找匹配的URL
                    for (const element of elements) {
                        // 获取元素URL
                        let elementUrl = '';

                        // 如果是图片元素
                        if (element.tagName === 'IMG') {
                            elementUrl = element.src;
                        }
                        // 如果是容器元素,查找子图片
                        else {
                            const img = element.querySelector('img');
                            if (img && img.src) {
                                elementUrl = img.src;
                            }
                        }

                        if (elementUrl) {
                            const normalizedElementUrl = this.getNormalizedUrl(elementUrl);

                            // 比较URL
                            if (normalizedElementUrl === normalizedTargetUrl ||
                                normalizedElementUrl.includes(normalizedTargetUrl) ||
                                normalizedTargetUrl.includes(normalizedElementUrl)) {
                                this.log("找到URL匹配的元素", element);

                                try {
                                    element.click();
                                    return true;
                                } catch (error) {
                                    this.log("点击URL匹配元素失败", error);
                                }
                            }
                        }
                    }
                } catch (error) {
                    this.log(`选择器 ${selector} 查询失败:`, error);
                    return false;
                }
            }

            // 如果URL查找失败,尝试通过标题查找
            if (title) {
                this.log("通过标题查找元素", title);

                for (const selector of selectors) {
                    try {
                        const elements = document.querySelectorAll(selector);

                        for (const element of elements) {
                            const elementTitle = element.getAttribute('title') ||
                                  element.getAttribute('alt') ||
                                  element.textContent.trim();
                            if (elementTitle && elementTitle === title) {
                                this.log("找到标题匹配的元素", element);

                                try {
                                    element.click();
                                    return true;
                                } catch (error) {
                                    this.log("点击标题匹配元素失败", error);
                                }
                            }
                        }
                    } catch (error) {
                        this.log(`选择器 ${selector} 查询失败:`, error);
                        return false;
                    }
                }
            }

            this.log("无法找到匹配的元素");
            return false;
        }

        // 优化的URL标准化函数
        getNormalizedUrl(url) {
            if (!url) return '';

            // 限制缓存大小
            const MAX_CACHE_SIZE = 1000;
            if (this.urlCache.size >= MAX_CACHE_SIZE) {
                // 清理最旧的一半缓存
                const entries = Array.from(this.urlCache.entries());
                const toDelete = entries.slice(0, Math.floor(MAX_CACHE_SIZE / 2));
                toDelete.forEach(([key]) => this.urlCache.delete(key));
            }

            if (this.urlCache.has(url)) {
                return this.urlCache.get(url);
            }

            // URL标准化逻辑
            const normalized = url.split(/[@?#]/)[0].replace(/^https?:\/\/(i[0-9]\.)?/, '');

            // 缓存结果
            this.urlCache.set(url, normalized);
            return normalized;
        }

        cleanup() {
            this.log("执行插件清理");

            try {
                // 1. 移除所有事件监听器(新增的统一管理)
                this.removeAllEventListeners();

                // 2. 清理所有定时器
                this.clearAllTimers();

                // 3. 重置所有状态
                this.resetPanelState();

                // 4. 清理DOM观察器
                if (this.observer) {
                    this.observer.disconnect();
                    this.observer = null;
                    this.log("DOM观察器已清理");
                }

                // 5. 清理遗留的事件监听器(兼容旧代码)
                this.cleanupLegacyEventListeners();

                // 6. 清理缓存
                this.cleanupCaches();

                // 7. 清理DOM元素引用
                this.cleanupDOMReferences();

                // 8. 重置数据状态
                this.resetDataState();

                this.log("插件清理完成");

            } catch (error) {
                this.log("清理过程中出现错误:", error);
            }
        }

    }


    // 初始化插件
    console.log("[BiliEmote] 初始化B站打call增强版插件");
    window.biliEmotionEnhancer = new BiliEmotionEnhancer();

    window.addEventListener('beforeunload', () => {
        if (window.biliEmotionEnhancer) window.biliEmotionEnhancer.cleanup();
    });

    window.__BILI_EMOTION_ENHANCER__ = {
        refresh: () => window.biliEmotionEnhancer?.collectEmotionData(),
        getConfig: () => window.biliEmotionEnhancer ? { ...window.biliEmotionEnhancer.config } : null,
        debug: (enable) => window.biliEmotionEnhancer && (window.biliEmotionEnhancer.debug = !!enable),
        clearRoomData: (roomId) => {
            if (window.biliEmotionEnhancer) {
                const key = roomId ? `bili-emotion-enhancer-data-${roomId}` : window.biliEmotionEnhancer.getRoomStorageKey();
                localStorage.removeItem(key);
                console.log(`[BiliEmote] 已清除房间 ${roomId || '当前房间'} 的数据`);
                if (!roomId || roomId === window.biliEmotionEnhancer.currentRoomId) {
                    window.biliEmotionEnhancer.emotionData = [];
                    window.biliEmotionEnhancer.updatePanelContent();
                }
            }
        }
    };
})();