幕布MubuPlus

(同步搜索框+搜索历史面板+选中快速筛选+悬停复制标签+保留软换行和颜色中转粘贴-复制剪切) v3.6.0

当前为 2025-04-03 提交的版本,查看 最新版本

// ==UserScript==
// @name         幕布MubuPlus 
// @namespace    http://tampermonkey.net/
// @version      3.6.0
// @author       Yeeel
// @description  (同步搜索框+搜索历史面板+选中快速筛选+悬停复制标签+保留软换行和颜色中转粘贴-复制剪切) v3.6.0
// @match        *://mubu.com/*
// @match        *://*.mubu.com/*
// @grant        GM_addStyle
// @run-at       document-idle
// @icon         https://mubu.com/favicon.ico
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    console.log('[Mubu Combined Helper v3.6.0 重构按钮逻辑] 脚本加载');

    // --- [ ☆ 功能开关 (默认值) ☆ ] ---
    const FEATURES = {
        syncSearchBox: { enabled: true, label: '同步搜索框' },
        historyPanel: { enabled: true, label: '搜索历史面板' },
        selectSearchPopup: { enabled: true, label: '选中快速筛选' }, // "筛选"按钮
        copyTagOnHover: { enabled: false, label: '悬停复制标签' },
        transferPasteCopy: { enabled: false, label: '中转粘贴-复制' }, // "复制"按钮
        transferPasteCut: { enabled: false, label: '中转粘贴-剪切' },  // "剪切"按钮
    };

    // --- [ ☆ 运行时功能状态 ☆ ] ---
    let runtimeFeatureState = {};
    for (const key in FEATURES) {
        runtimeFeatureState[key] = FEATURES[key].enabled;
    }
    const isFeatureEnabled = (key) => !!runtimeFeatureState[key];

    // --- [ ☆ 配置项 ☆ ] ---
    const config = {
        cacheTTL: 3000,
        initDelay: 1000,
        selectors: {
            originalInput: 'input[placeholder="搜索关键词"]:not([disabled])',
            domObserverTarget: 'div.search-wrap',
            copyTagParentContainer: 'div.outliner-page',
        },
        sync: {
            historySize: 30,
            mutationDebounce: 5,
            throttleTime: 50,
            activeItemBgColor: '#e9e8f9',
            topBarId: 'custom-search-sync-container-v35',
            historyPanelId: 'search-history-panel-v35',
            historyListId: 'search-history-list-v35',
        },
        select: { // 用于 "筛选" 按钮
            popupId: 'mubu-select-search-popup-v35',
            popupText: '筛选',
            popupAboveGap: 5, // 按钮组距离光标上方的距离
            fallbackWidth: 35,
            fallbackHeight: 22,
            popupAppearDelay: 50, // 延迟出现时间(现在由统一逻辑控制,但可保留)
        },
        copyTag: {
            popupId: 'mubu-copy-tag-popup-hover-v35',
            feedbackId: 'mubu-copy-tag-feedback-v35',
            copyIcon: '📋',
            copiedText: '✅ 已复制',
            popupMarginBottom: 0,
            hoverDelay: 10,
            hideDelay: 50,
            copiedMessageDuration: 500,
            tagSelector: 'span.tag',
            popupFallbackWidth: 25,
            popupFallbackHeight: 18,
            feedbackFallbackWidth: 60,
            feedbackFallbackHeight: 18,
        },
        transferPaste: { // 用于 "复制" 和 "剪切" 按钮
            editorContainerSelector: '#js-outliner',
            triggerButtonId: 'mu-transfer-copy-button-v35', // 复制按钮 ID
            cutButtonId: 'mu-transfer-cut-button-v35',     // 剪切按钮 ID
            pasteButtonId: 'mu-transfer-paste-button-v35',   // 粘贴按钮 ID (独立逻辑)
            triggerButtonText: '复制',
            cutButtonText: '剪切',
            pasteButtonText: '粘贴',
            // buttonVerticalGap: 5, // 不再单独使用,统一用 select.popupAboveGap
            buttonHorizontalGap: 2, // 按钮水平间距
            cssPrefix: 'mu-transfer-paste-v35-',
            btnBaseClass: 'btn-base',
            btnCopyClass: 'btn-copy',
            btnCutClass: 'btn-cut',
            btnPasteClass: 'btn-paste',
            // 基础样式移到 GM_addStyle 中定义,这里只保留绝对定位所需
             buttonBaseStyleInline: {
                 position: 'absolute', zIndex: '29998', top: '0', left: '0',
                 opacity: '0', display: 'none', visibility: 'hidden',
                 // transition: 'opacity 0.1s ease-in-out, visibility 0.1s ease-in-out', // 移到CSS
             },
            initWaitMaxRetries: 15,
            initWaitRetryInterval: 700,
            buttonFallbackWidth: 35,
            buttonFallbackHeight: 22,
            buttonsAppearDelay: 50, // 延迟出现时间(现在由统一逻辑控制,但可保留)
        },
        togglePanel: {
            panelId: 'mubu-helper-toggle-panel-v35',
            triggerId: 'mubu-helper-toggle-trigger-v35',
            panelWidth: 160,
            triggerWidth: 20,
            triggerHeight: 230,
            hideDelay: 100,
        }
    };
    const BUTTON_GAP = config.transferPaste.buttonHorizontalGap; // 统一间距常量


    // --- [ ☆ rAF 样式批量处理 ☆ ] ---
    let styleUpdateQueue = [];
    let isRafScheduled = false;
    function processStyleUpdates() {
        const tasksToProcess = [...styleUpdateQueue];
        styleUpdateQueue = [];
        tasksToProcess.forEach(task => {
            if (task.element && task.element.isConnected) {
                try { Object.assign(task.element.style, task.styles); } catch (e) { console.warn('[Mubu Helper] Style apply err:', e, task.element); }
            }
        });
        isRafScheduled = false;
    }
    function scheduleStyleUpdate(element, styles) {
        if (!element) return;
        styleUpdateQueue.push({ element, styles });
        if (!isRafScheduled) {
            isRafScheduled = true;
            requestAnimationFrame(processStyleUpdates);
        }
    }

    // --- [ ☆ ResizeObserver 尺寸缓存 ☆ ] ---
    const elementDimensionsCache = new WeakMap();
    const elementObserverMap = new Map();
    const resizeObserverCallback = (entries) => {
        for (let entry of entries) {
            let width = 0, height = 0;
            if (entry.borderBoxSize?.length > 0) { width = entry.borderBoxSize[0].inlineSize; height = entry.borderBoxSize[0].blockSize; }
            else if (entry.contentRect) { width = entry.contentRect.width; height = entry.contentRect.height; }
            if (width > 0 && height > 0) { elementDimensionsCache.set(entry.target, { width, height }); }
            else if (entry.target.offsetWidth > 0 && entry.target.offsetHeight > 0) { width = entry.target.offsetWidth; height = entry.target.offsetHeight; elementDimensionsCache.set(entry.target, { width, height }); }
             // Optional: Trigger repositioning if dimensions change while visible? Might be complex.
             // if (entry.target.style.visibility === 'visible') {
             //    // Maybe re-trigger showSelectionActionButtons if needed, carefully to avoid loops
             // }
        }
    };
    const observerInstance = new ResizeObserver(resizeObserverCallback);
    function observeElementResize(element) {
        if (!element || elementObserverMap.has(element)) return;
        try { observerInstance.observe(element); elementObserverMap.set(element, observerInstance); } catch (e) { console.error('[Mubu Helper] RO observe err:', e, element); }
    }
    function unobserveElementResize(element) {
        if (!element || !elementObserverMap.has(element)) return;
        try { observerInstance.unobserve(element); elementObserverMap.delete(element); } catch (e) { console.error('[Mubu Helper] RO unobserve err:', e, element); }
    }

    // --- [ ☆ 内部状态与工具函数 ☆ ] ---
    let originalInput = null, lastSyncedValue = null, isSyncing = false, domObserver = null, customInput = null;
    let originalInputSyncHandler = null, originalInputHistoryHandler = null;
    let topBarControls = { container: null, input: null, prevBtn: null, nextBtn: null, clearBtn: null };
    let historyPanel = null, historyListElement = null, activeHistoryItemElement = null;
    let popupElement = null; // "筛选" 按钮元素
    let currentSelectedText = ''; // 存储当前选中的文本,主要给筛选按钮用
    let ct_copyPopupElement = null, ct_feedbackElement = null, ct_currentHoveredTag = null, ct_currentTagText = '';
    let ct_showTimeout = null, ct_hideTimeout = null, ct_feedbackTimeout = null, ct_listenerTarget = null;
    let tp_editorContainer = null, tp_storedHTML = '', tp_storedText = '', tp_ctrlApressed = false, tp_listenersAttached = false;
    let togglePanelElement = null, toggleTriggerElement = null, togglePanelHideTimeout = null;
    const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
    const inputEvent = new Event('input', { bubbles: true, cancelable: true });
    const debounce = (fn, delay) => { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn.apply(this, a), delay); }; };
    const throttle = (fn, delay) => { let l=0, t; return (...a) => { const n=performance.now(); clearTimeout(t); if(n-l>=delay){requestAnimationFrame(()=>fn.apply(this,a)); l=n;} else {t=setTimeout(()=>{requestAnimationFrame(()=>fn.apply(this,a)); l=performance.now();},delay-(n-l));}};};
    const optimizedFindSearchBox = (()=>{let c=null,l=0;return()=>{const n=performance.now();if(c&&c.isConnected&&(n-l<config.cacheTTL)){return c;}c=null;try{c=document.querySelector(config.selectors.originalInput);}catch(e){c=null;}l=n;return c;}})();
    const docBody = document.body;
    const historyNavPrevListener = () => handleHistoryNavigation(-1);
    const historyNavNextListener = () => handleHistoryNavigation(1);
    const clearBtnListener = () => handleClear();
    const customInputListener = () => handleCustomInputChange();
    const mouseDownPopupListener = (e) => handleMouseDownPopup(e); // Renamed for clarity
    const mouseUpPopupListener = (e) => handleMouseUpSelectionEnd(e); // Renamed for clarity
    const isHistoryTrackingNeeded = () => isFeatureEnabled('syncSearchBox') || isFeatureEnabled('selectSearchPopup') || isFeatureEnabled('historyPanel');

    // 历史记录管理器模块 (保持不变)
    const historyManager = (() => {
        const history = new Array(config.sync.historySize);
        let writeIndex = 0, count = 0, navIndex = -1;
        const add = (value) => {
            if (!isHistoryTrackingNeeded()) return false;
            const term = String(value).trim(); if (!term) return false;
            const lastIdx = (writeIndex - 1 + config.sync.historySize) % config.sync.historySize;
            if (count > 0 && history[lastIdx] === term) { navIndex = -1; return false; }
            history[writeIndex] = term; writeIndex = (writeIndex + 1) % config.sync.historySize;
            count = Math.min(count + 1, config.sync.historySize); navIndex = -1; return true;
        };
        const get = (logicalIndex) => { if (!isHistoryTrackingNeeded() || logicalIndex < 0 || logicalIndex >= count) return null; const physicalIndex = (writeIndex - count + logicalIndex + config.sync.historySize) % config.sync.historySize; return history[physicalIndex]; };
        const size = () => isHistoryTrackingNeeded() ? count : 0;
        const getCurrentIndex = () => navIndex; const setCurrentIndex = (index) => { if (isHistoryTrackingNeeded()) navIndex = index; }; const resetIndexToCurrent = () => { if (isHistoryTrackingNeeded()) navIndex = -1; };
        const updatePanel = () => {
            if (!isFeatureEnabled('historyPanel') || !historyPanel || !historyListElement) return;
            const scrollTop = historyListElement.scrollTop; historyListElement.innerHTML = '';
            const numItems = historyManager.size(); if (numItems === 0) return;
            const currentNavIndex = historyManager.getCurrentIndex(); let activeElement = null;
            const fragment = document.createDocumentFragment();
            for (let i = 0; i < numItems; i++) {
                const logicalIndex = numItems - 1 - i; const term = historyManager.get(logicalIndex);
                if (term) {
                    const li = document.createElement('li'); li.className = 'search-history-item';
                    li.textContent = term; li.title = term; li.dataset.term = term; li.dataset.historyIndex = logicalIndex;
                    if (logicalIndex === currentNavIndex) { li.classList.add('search-history-item--active'); activeElement = li; }
                    fragment.appendChild(li);
                }
            }
            historyListElement.appendChild(fragment);
            try { historyListElement.scrollTop = scrollTop; } catch (e) {/* ignore */}
            activeHistoryItemElement = activeElement;
        };
        return { add, get, size, getCurrentIndex, setCurrentIndex, resetIndexToCurrent, updatePanel };
    })();

    // --- [ ☆ 同步与历史记录核心逻辑 ☆ ] --- (保持不变)
    const runSynced = (action) => { if (!isFeatureEnabled('syncSearchBox') || isSyncing) return; isSyncing = true; try { action(); } finally { queueMicrotask(() => { isSyncing = false; }); } };
    const updateCustomInputAndAddHistory = (newValue, source = 'unknown') => {
        if (isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== newValue) { customInput.value = newValue; }
        let historyChanged = false; if (isHistoryTrackingNeeded()) { historyChanged = historyManager.add(newValue); }
        if (isFeatureEnabled('historyPanel') && (historyChanged || (!newValue && lastSyncedValue !== ''))) { historyManager.updatePanel(); }
        if (isFeatureEnabled('historyPanel') && (!newValue || (historyChanged && source !== 'navigation'))) { historyManager.resetIndexToCurrent(); removeActiveHighlight(); }
        lastSyncedValue = newValue;
    };
    const findAndSetupDebounced = debounce(() => {
        if (!isFeatureEnabled('syncSearchBox') && !isFeatureEnabled('historyPanel')) return;
        const foundInput = optimizedFindSearchBox();
        if (foundInput) {
            const currentValue = foundInput.value;
            if (foundInput !== originalInput) {
                teardownInputListeners(originalInput); originalInput = foundInput; setupInputListeners(originalInput);
                lastSyncedValue = currentValue;
                if (isHistoryTrackingNeeded()) { const hChanged = historyManager.add(lastSyncedValue); if (hChanged && isFeatureEnabled('historyPanel')) historyManager.updatePanel(); }
                if (isFeatureEnabled('syncSearchBox') && customInput) customInput.value = lastSyncedValue;
            } else if (currentValue !== lastSyncedValue && !isSyncing) {
                if (isFeatureEnabled('syncSearchBox')) { runSynced(() => { updateCustomInputAndAddHistory(currentValue, 'observer_external'); }); }
                else if (isHistoryTrackingNeeded()) { updateCustomInputAndAddHistory(currentValue, 'observer_external'); }
            }
        } else if (!foundInput && originalInput) { teardownInputListeners(originalInput); originalInput = null; lastSyncedValue = null; if (isFeatureEnabled('syncSearchBox')) isSyncing = false; }
    }, config.sync.mutationDebounce);

    // --- [ ☆ 中转粘贴 (Transfer Paste) 相关 ☆ ] ---
    const tp_triggerButtonRef = { element: null }; // "复制" 按钮 Ref
    const tp_cutButtonRef = { element: null };     // "剪切" 按钮 Ref
    const tp_pasteButtonRef = { element: null };    // "粘贴" 按钮 Ref (独立逻辑)

    // 移除旧的 tp_hideTriggerButton, tp_hideCutButton - 由 hideSelectionActionButtons 统一处理
    // function tp_hideAndRemoveButton(buttonRef) { if (buttonRef.element) { unobserveElementResize(buttonRef.element); try { buttonRef.element.remove(); } catch (e) { /* 忽略 */ } buttonRef.element = null; } }
    // const tp_hideTriggerButton = () => tp_hideAndRemoveButton(tp_triggerButtonRef);
    // const tp_hideCutButton = () => tp_hideAndRemoveButton(tp_cutButtonRef);
    const tp_hidePasteButton = () => { // 粘贴按钮的隐藏逻辑保持独立
         if (tp_pasteButtonRef.element) {
             scheduleStyleUpdate(tp_pasteButtonRef.element, { opacity: '0', visibility: 'hidden' });
             setTimeout(() => {
                if (tp_pasteButtonRef.element?.style.opacity === '0') {
                   scheduleStyleUpdate(tp_pasteButtonRef.element, { display: 'none' });
                }
             }, 150); // 稍长延迟确保平滑
             // 不需要移除,下次直接复用或被 tp_createButton 替换
         }
    }

    function getCursorRect(selection) { if (!selection || !selection.focusNode || selection.rangeCount === 0) { return null; } const range = document.createRange(); try { range.setStart(selection.focusNode, selection.focusOffset); range.setEnd(selection.focusNode, selection.focusOffset); const rect = range.getBoundingClientRect(); if (rect.width === 0 && rect.height === 0 && selection.toString().trim().length > 0) { const mainRange = selection.getRangeAt(0); const rects = mainRange.getClientRects(); return rects.length > 0 ? rects[rects.length - 1] : mainRange.getBoundingClientRect(); } return rect; } catch (e) { try { return selection.getRangeAt(0).getBoundingClientRect(); } catch { return null; } } }
    function tp_captureSelectionAndStore() { const selection = window.getSelection(); if (!selection || selection.isCollapsed || selection.rangeCount === 0) return false; try { const range = selection.getRangeAt(0); const tempDiv = document.createElement('div'); tempDiv.appendChild(range.cloneContents()); tp_storedHTML = tempDiv.innerHTML; tp_storedText = selection.toString(); return true; } catch (e) { console.error('[Mubu Helper TP] Capture err:', e); tp_storedHTML = ''; tp_storedText = ''; return false; } }
    function tp_isElementEditable(element) { if (!element) return false; if (element instanceof Element && element.closest) { if (element.closest('[contenteditable="true"], .mm-editor')) { return true; } } if (element instanceof HTMLElement && ['INPUT', 'TEXTAREA'].includes(element.tagName)) { return !element.readOnly && !element.disabled; } return false; }

    // 移除旧的 tp_hideActionButtons - 由 hideSelectionActionButtons 统一处理
    // function tp_hideActionButtons() { tp_hideTriggerButton(); tp_hideCutButton(); }

    // 移除旧的 tp_positionButton, tp_positionSecondButton - 由 showSelectionActionButtons 统一处理
    // function tp_positionButton(...) { ... }
    // function tp_positionSecondButton(...) { ... }

    // tp_createButton 现在主要由 showSelectionActionButtons 调用
    function tp_createButton(id, text, buttonClass, clickHandler) {
        const tpConfig = config.transferPaste;
        let existing = document.getElementById(id);
        if (existing) {
            // 不移除,只更新内容和事件监听器(如果需要),并确保在 DOM 中
            existing.textContent = text;
            // 移除旧监听器(简单起见,可以先移除再添加)
            // 注意:这可能不是最高效的方式,但可以避免重复监听
            existing.removeEventListener('click', existing.__clickHandler__); // 假设之前存储了回调
            existing.removeEventListener('mousedown', existing.__mousedownHandler__);
        } else {
             existing = document.createElement('button');
             existing.id = id;
             existing.textContent = text;
             Object.assign(existing.style, tpConfig.buttonBaseStyleInline); // 应用基础内联样式
             document.body.appendChild(existing);
        }
        // 应用样式类
        existing.className = ''; // 清除旧类
        existing.classList.add(tpConfig.cssPrefix + tpConfig.btnBaseClass, tpConfig.cssPrefix + buttonClass);

        // 存储并添加新的事件监听器
        const mousedownHandler = (e) => e.stopPropagation();
        const clickHandlerWrapper = (e) => { e.stopPropagation(); clickHandler(existing); };
        existing.addEventListener('mousedown', mousedownHandler);
        existing.addEventListener('click', clickHandlerWrapper);
        existing.__clickHandler__ = clickHandlerWrapper; // 存储回调以便移除
        existing.__mousedownHandler__ = mousedownHandler;

        observeElementResize(existing); // 确保被观察
        return existing;
    }

    // 移除旧的 tp_showActionButtons - 由 showSelectionActionButtons 统一处理
    // function tp_showActionButtons(selection) { ... }

    // tp_showPasteButton 保持独立,因为它与选区按钮组的触发条件不同
     function tp_showPasteButton(event) {
         const tpConfig = config.transferPaste;
         hideSelectionActionButtons(); // 隐藏其他按钮
         tp_hidePasteButton(); // 隐藏旧的粘贴按钮(如果存在)

         if (!tp_storedHTML && !tp_storedText) return;
         const targetElement = event.target instanceof Node ? event.target : document.elementFromPoint(event.clientX, event.clientY);
         if (!tp_isElementEditable(targetElement)) { return; }

         // 使用简单的 event clientX/Y 作为定位基准
         const positionRect = { top: event.clientY, left: event.clientX, bottom: event.clientY, right: event.clientX, width: 0, height: 0 };

         // --- 定位逻辑 ---
         const positionButtonStandalone = (buttonElement, targetRect) => {
             try {
                 if (!targetRect || !buttonElement || !buttonElement.isConnected) return false;
                 const dims = elementDimensionsCache.get(buttonElement);
                 const btnW = dims?.width || tpConfig.buttonFallbackWidth;
                 const btnH = dims?.height || tpConfig.buttonFallbackHeight;
                 const vpW = window.innerWidth;
                 const scrollY = window.pageYOffset;
                 const scrollX = window.pageXOffset;

                 // 粘贴按钮出现在鼠标点击位置下方一点
                 let top = scrollY + targetRect.top + 10; // 距离点击点下方10px
                 let left = scrollX + targetRect.left - btnW / 2; // 水平居中

                 top = Math.max(scrollY + 5, top);
                 left = Math.max(scrollX + 5, Math.min(left, scrollX + vpW - btnW - 5));

                 scheduleStyleUpdate(buttonElement, {
                     transform: `translate(${left.toFixed(1)}px, ${top.toFixed(1)}px)`,
                     display: 'inline-block',
                     opacity: '1',
                     visibility: 'visible'
                 });
                 return true;
             } catch (e) {
                 console.error('[Mubu Helper TP] Pos paste btn err:', e, buttonElement);
                 if (buttonElement) scheduleStyleUpdate(buttonElement, { display: 'none', opacity: '0', visibility: 'hidden' });
                 return false;
             }
         };
         // --- 定位逻辑结束 ---

         tp_pasteButtonRef.element = tp_createButton(
             tpConfig.pasteButtonId,
             tpConfig.pasteButtonText,
             tpConfig.btnPasteClass,
             (button) => {
                 const currentSel = window.getSelection();
                 const range = currentSel?.rangeCount > 0 ? currentSel.getRangeAt(0) : null;
                 // 确定粘贴目标,优先使用当前光标位置,如果不在可编辑区,则回退到触发事件的元素
                 let pasteTarget = null;
                 if (range) {
                      pasteTarget = range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement;
                      if (!tp_isElementEditable(pasteTarget)) {
                           pasteTarget = document.elementFromPoint(event.clientX, event.clientY);
                      }
                 } else {
                      pasteTarget = document.elementFromPoint(event.clientX, event.clientY);
                 }

                 if (!tp_isElementEditable(pasteTarget)) {
                     alert('无法粘贴:位置不可编辑。');
                     tp_hidePasteButton();
                     return;
                 }
                 // --- 粘贴执行逻辑 (保持不变) ---
                 try {
                     let success = false;
                     // 优先尝试 HTML 粘贴
                     if (tp_storedHTML && document.queryCommandSupported('insertHTML')) {
                         try {
                             // 确保光标在目标位置
                              if (range) {
                                  currentSel.removeAllRanges();
                                  currentSel.addRange(range);
                              } else {
                                  // 如果没有 range,尝试聚焦目标元素(可能不精确)
                                  if (pasteTarget instanceof HTMLElement) pasteTarget.focus();
                              }
                             if (document.execCommand('insertHTML', false, tp_storedHTML)) {
                                 success = true;
                             } else { console.warn('[Mubu TP] insertHTML failed.'); }
                         } catch (e) { console.error('[Mubu TP] insertHTML err:', e); }
                     }
                     // 如果 HTML 失败或不支持,尝试文本粘贴
                     if (!success && tp_storedText && document.queryCommandSupported('insertText')) {
                         try {
                              // 再次确保光标
                              if (range) {
                                  currentSel.removeAllRanges();
                                  currentSel.addRange(range);
                              } else {
                                   if (pasteTarget instanceof HTMLElement) pasteTarget.focus();
                              }
                             if (document.execCommand('insertText', false, tp_storedText)) {
                                 success = true;
                             } else { console.warn('[Mubu TP] insertText failed.'); }
                         } catch (e) { console.error('[Mubu TP] insertText err:', e); }
                     }
                     // 如果都失败了...
                     if (!success) {
                         console.error('[Mubu TP] Paste failed using execCommand.');
                         alert('粘贴失败。请尝试手动 Ctrl+V。');
                         // 不清除缓存,允许用户手动粘贴
                     } else {
                         // 成功粘贴后清除缓存
                         tp_storedHTML = '';
                         tp_storedText = '';
                     }
                 } catch (e) {
                     console.error('[Mubu TP] Paste process err:', e);
                     alert(`粘贴时出错: ${e.message}`);
                 } finally {
                     tp_hidePasteButton(); // 无论成功失败都隐藏按钮
                 }
                 // --- 粘贴执行逻辑结束 ---
             }
         );

         if (!positionButtonStandalone(tp_pasteButtonRef.element, positionRect)) {
             tp_hidePasteButton(); // 定位失败则隐藏
         }
     }


    // --- [ ☆ 复制标签 (Copy Tag) 相关 ☆ ] --- (保持不变)
    function calculateTransformForPopup(element, targetRect, marginBottom = 0) {
        if (!element || !targetRect || !element.isConnected) return null; const ctConfig = config.copyTag;
        const dims = elementDimensionsCache.get(element); let W, H;
        if (element === ct_copyPopupElement) { W = dims?.width || ctConfig.popupFallbackWidth; H = dims?.height || ctConfig.popupFallbackHeight; }
        else if (element === ct_feedbackElement) { W = dims?.width || ctConfig.feedbackFallbackWidth; H = dims?.height || ctConfig.feedbackFallbackHeight; }
        else { W = dims?.width || 30; H = dims?.height || 20; }
        const x = window.pageXOffset; const y = window.pageYOffset; const targetCenterX = targetRect.left + targetRect.width / 2;
        const top = y + targetRect.top - H - marginBottom; const left = x + targetCenterX - W / 2;
        return `translate(${left.toFixed(1)}px, ${top.toFixed(1)}px)`;
    }
    function ct_showCopyPopup(tagElement){
        if (!isFeatureEnabled('copyTagOnHover')) return;
        ct_createElements(); // 确保按钮存在
        if (!ct_copyPopupElement) return;

        if (!tagElement || !ct_copyPopupElement.isConnected) return;
        const tagText = tagElement.textContent?.trim(); if (!tagText) return; ct_currentTagText = tagText; // 更新当前标签文本

        const targetRect = tagElement.getBoundingClientRect();
        const transform = calculateTransformForPopup(ct_copyPopupElement, targetRect, config.copyTag.popupMarginBottom);

        // 显式设置显示样式
        if (transform) {
             scheduleStyleUpdate(ct_copyPopupElement, { transform: transform, display: 'block', opacity: '1', visibility: 'visible' });
        } else {
             console.warn("[CT] show copy popup: no transform");
             // 提供一个回退位置,避免完全不显示
             scheduleStyleUpdate(ct_copyPopupElement, { transform: 'translate(0px, -20px)', display: 'block', opacity: '1', visibility: 'visible' });
        }
    }
    function ct_hideCopyPopupImmediately(clearState = true){
        clearTimeout(ct_showTimeout); ct_showTimeout = null;
        clearTimeout(ct_hideTimeout); ct_hideTimeout = null; // 清除隐藏计划计时器
        if (ct_copyPopupElement?.isConnected) {
            scheduleStyleUpdate(ct_copyPopupElement, { opacity: '0', visibility: 'hidden' });
            setTimeout(() => {
                // 再次检查是否仍应隐藏
                if (ct_copyPopupElement?.style.opacity === '0') {
                     scheduleStyleUpdate(ct_copyPopupElement, { display: 'none' });
                }
            }, 150); // 动画时间
        }
        if (clearState) {
            ct_currentHoveredTag = null;
            ct_currentTagText = '';
        }
    }
    function ct_scheduleHidePopup() {
        clearTimeout(ct_showTimeout); ct_showTimeout = null;
        clearTimeout(ct_hideTimeout); // 清除之前的隐藏计划
        ct_hideTimeout = setTimeout(() => {
            const tagHover = ct_currentHoveredTag?.matches(':hover');
            const popupHover = ct_copyPopupElement?.matches(':hover');
            if (!tagHover && !popupHover) {
                // console.log("[CT Debug] Hiding copy popup via scheduled timeout.");
                ct_hideCopyPopupImmediately(true); // true 表示重置状态
            }
            ct_hideTimeout = null;
        }, config.copyTag.hideDelay);
    }
    // --- [ ☆☆ v3.5.5: 保持反馈逻辑独立 ☆☆ ] --- (保持不变)
    function ct_showFeedbackIndicator(tagElement){
        if (!isFeatureEnabled('copyTagOnHover')) return;
        ct_createElements(); // 确保元素存在
        if (!ct_feedbackElement) { console.error("[CT] Feedback element not available in showFeedbackIndicator."); return; }

        if (!tagElement || !ct_feedbackElement.isConnected) return;

        const duration = config.copyTag.copiedMessageDuration;
        // console.log(`[CT Debug] Setting feedback timeout for ${duration}ms. Current timeout ID: ${ct_feedbackTimeout}`);

        clearTimeout(ct_feedbackTimeout); // 清除旧计时器

        const targetRect = tagElement.getBoundingClientRect();
        const transform = calculateTransformForPopup(ct_feedbackElement, targetRect, config.copyTag.popupMarginBottom);

        // 显式设置显示样式
        if (transform) {
            scheduleStyleUpdate(ct_feedbackElement, { transform: transform, display: 'block', opacity: '1', visibility: 'visible' });
        } else {
            console.warn("[CT] feedback: no transform");
             scheduleStyleUpdate(ct_feedbackElement, { transform: 'translate(0px, -20px)', display: 'block', opacity: '1', visibility: 'visible' });
        }

        // 设置隐藏计时器
        ct_feedbackTimeout = setTimeout(ct_hideFeedbackIndicator, duration);
        // console.log(`[CT Debug] NEW feedback timeout ID set: ${ct_feedbackTimeout}`);
    }

    function ct_hideFeedbackIndicator(){
        // console.log(`[CT Debug] ct_hideFeedbackIndicator called.`);

        if (!ct_feedbackElement?.isConnected) { /* console.warn("[CT Debug] Hide feedback skipped: element not connected."); */ return; }

        // console.log(`[CT Debug] Scheduling styles to hide feedback.`);
        scheduleStyleUpdate(ct_feedbackElement, { opacity: '0', visibility: 'hidden' });
        setTimeout(() => {
            if (ct_feedbackElement?.style.opacity === '0') {
                // console.log(`[CT Debug] Setting feedback display:none.`);
                scheduleStyleUpdate(ct_feedbackElement, { display: 'none' });
            } else {
                 // console.log(`[CT Debug] Skipped display:none as feedback opacity is not 0.`);
            }
        }, 150);
        ct_feedbackTimeout = null; // 清除 ID
    }
    // --- [ ☆☆ v3.5.5 结束 ☆☆ ] --- (保持不变)


    // --- [ ☆ UI 创建函数 ☆ ] ---
    // --- [ ☆☆ v3.5.5: 修改创建逻辑 ☆☆ ] --- (保持不变)
    function ct_createElements(){
        if (!isFeatureEnabled('copyTagOnHover')) return;
        const ctConf = config.copyTag;
        const baseStylePopup = { position: 'absolute', top: '0', left: '0', zIndex: '10010', display: 'none', opacity: '0', visibility: 'hidden' };
        const baseStyleFeedback = { position: 'absolute', top: '0', left: '0', zIndex: '10011', display: 'none', opacity: '0', visibility: 'hidden', pointerEvents: 'none' };

        // 处理复制按钮
        let existingPopup = document.getElementById(ctConf.popupId);
        if (!ct_copyPopupElement && existingPopup) {
             ct_copyPopupElement = existingPopup;
             if (!elementObserverMap.has(ct_copyPopupElement)) observeElementResize(ct_copyPopupElement);
             // console.log('[CT Debug] Copy popup element reference restored.');
        } else if (!ct_copyPopupElement && !existingPopup) {
            const copyPopup = document.createElement('button');
            copyPopup.id = ctConf.popupId;
            copyPopup.textContent = ctConf.copyIcon;
            Object.assign(copyPopup.style, baseStylePopup);
            copyPopup.addEventListener('click', ct_handleCopyButtonClick);
            copyPopup.addEventListener('mouseenter', () => { clearTimeout(ct_hideTimeout); ct_hideTimeout = null; });
            copyPopup.addEventListener('mouseleave', ct_scheduleHidePopup);
            copyPopup.addEventListener('mousedown', (e) => { e.preventDefault(); e.stopPropagation(); });
            document.body.appendChild(copyPopup);
            ct_copyPopupElement = copyPopup;
            observeElementResize(ct_copyPopupElement);
            // console.log('[CT Debug] Copy popup element created.');
        }

        // 处理反馈元素
        let existingFeedback = document.getElementById(ctConf.feedbackId);
        if (!ct_feedbackElement && existingFeedback) {
            ct_feedbackElement = existingFeedback;
            if (!elementObserverMap.has(ct_feedbackElement)) observeElementResize(ct_feedbackElement);
            // console.log('[CT Debug] Feedback element reference restored.');
        } else if (!ct_feedbackElement && !existingFeedback) {
            const feedback = document.createElement('div');
            feedback.id = ctConf.feedbackId;
            feedback.textContent = ctConf.copiedText;
            Object.assign(feedback.style, baseStyleFeedback);
            document.body.appendChild(feedback);
            ct_feedbackElement = feedback;
            observeElementResize(ct_feedbackElement);
            // console.log('[CT Debug] Feedback element created.');
        }
    }
    // --- [ ☆☆ v3.5.5 结束 ☆☆ ] --- (保持不变)

    function createControlPanel(){ // (保持不变)
        if(document.getElementById(config.sync.topBarId)) return topBarControls;
        try{ const c=document.createElement('div'); c.id=config.sync.topBarId; c.style.display = 'none';
            const l=document.createElement('button');l.className='clear-btn';l.textContent='✕';l.title='清空';
            const p=document.createElement('button');p.className='history-btn';p.textContent='←';p.title='上条';
            const n=document.createElement('button');n.className='history-btn';n.textContent='→';n.title='下条';
            const i=document.createElement('input');i.className='custom-search-input';i.type='search';i.placeholder='筛选';i.setAttribute('autocomplete','off');
            c.append(l,p,n,i); document.body.appendChild(c); topBarControls={container:c,input:i,prevBtn:p,nextBtn:n,clearBtn:l}; return topBarControls;
        } catch(e) { console.error('[Mubu] Create top bar err:',e); topBarControls={}; return topBarControls; }
    }
    function createHistoryPanel(){ // (保持不变)
        if(document.getElementById(config.sync.historyPanelId)) return historyPanel;
        try{ const p=document.createElement('div'); p.id=config.sync.historyPanelId; p.style.display = 'none';
            const l=document.createElement('ul'); l.className='search-history-list'; l.id=config.sync.historyListId;
            p.appendChild(l); document.body.appendChild(p); historyPanel = p; historyListElement = l; return p;
        } catch(e) { console.error('[Mubu] Create hist panel err:',e); historyPanel=null; historyListElement=null; return null; }
    }
    // createSelectPopup 现在只负责创建 "筛选" 按钮元素
    function createSelectPopup(){
        if (!isFeatureEnabled('selectSearchPopup')) return; // 检查功能是否启用
        const popupId = config.select.popupId;
        let existing = document.getElementById(popupId);
        if (existing) {
            popupElement = existing;
            if (!elementObserverMap.has(popupElement)) observeElementResize(popupElement);
            Object.assign(popupElement.style, { display: 'none', opacity: '0', visibility: 'hidden' }); // 确保初始隐藏
            // 确保事件监听器已附加(如果需要的话)
            if (!existing.__clickAttached__) {
                 existing.addEventListener('mousedown', handlePopupClick); // 使用 mousedown 触发更快
                 existing.addEventListener('click', (e) => e.stopPropagation()); // 阻止冒泡
                 existing.__clickAttached__ = true;
            }
            return;
        }
        // 如果不存在,创建新的
        try {
            const btn = document.createElement('button');
            btn.id = popupId;
            btn.textContent = config.select.popupText;
            // 应用基础样式,但定位由 showSelectionActionButtons 处理
            Object.assign(btn.style, {
                position: 'absolute', // 必须是 absolute
                top: '0', left: '0', // 初始位置无所谓
                zIndex: '10010', // 确保层级
                display: 'none', opacity: '0', visibility: 'hidden', // 初始隐藏
                // 样式类在 CSS 中定义
            });
            btn.classList.add('mu-select-popup-btn'); // 添加一个类以便在 CSS 中设置样式

            btn.addEventListener('mousedown', handlePopupClick); // 使用 mousedown 触发更快
            btn.addEventListener('click', (e) => e.stopPropagation()); // 阻止冒泡
            btn.__clickAttached__ = true; // 标记已附加

            document.body.appendChild(btn);
            popupElement = btn;
            observeElementResize(popupElement);
        } catch(e) {
            console.error('[Mubu SS] Create popup err:', e);
            popupElement = null;
        }
    }

    // --- [ ☆ 事件处理函数 ☆ ] ---
    // 同步、历史记录相关 (保持不变)
    function syncFromOriginal(sourceInput) { if(!isFeatureEnabled('syncSearchBox') || !sourceInput) return; const val=sourceInput.value; if(val===lastSyncedValue)return; runSynced(()=>{updateCustomInputAndAddHistory(val,'sync_from_original');}); }
    function syncToOriginal(options = { updateHistory: true }) { if (!isFeatureEnabled('syncSearchBox') || !originalInput || !customInput || !nativeInputValueSetter) return; const val = customInput.value; runSynced(() => { if (originalInput.value !== val) { nativeInputValueSetter.call(originalInput, val); originalInput.dispatchEvent(inputEvent); } historyManager.resetIndexToCurrent(); let hChanged = false; if (options.updateHistory && isHistoryTrackingNeeded() && val) { hChanged = historyManager.add(val); } if (isFeatureEnabled('historyPanel')) { if (hChanged) historyManager.updatePanel(); removeActiveHighlight(); } lastSyncedValue = val; }); }
    function handleOriginalInputForSync(event) { if (!event.isTrusted || !isFeatureEnabled('syncSearchBox') || isSyncing) return; syncFromOriginal(event.target); }
    function handleOriginalInputForHistory(event) { if (!event.isTrusted || isFeatureEnabled('syncSearchBox') || !isFeatureEnabled('historyPanel') || !isHistoryTrackingNeeded() || isSyncing) return; const val = event.target.value; if (val === lastSyncedValue) return; const hChanged = historyManager.add(val); if (hChanged && isFeatureEnabled('historyPanel')) { historyManager.updatePanel(); } if (isFeatureEnabled('historyPanel')) { removeActiveHighlight(); } lastSyncedValue = val; }
    function handleCustomInputChange() { if (!isFeatureEnabled('syncSearchBox') || isSyncing) return; syncToOriginal(); }
    function removeActiveHighlight() { if (!isFeatureEnabled('historyPanel') || !activeHistoryItemElement) return; try { activeHistoryItemElement.classList.remove('search-history-item--active'); } catch (e) { /*...*/ } activeHistoryItemElement = null; }
    function handleHistoryListClick(event) { if (!isFeatureEnabled('historyPanel')) return; const item = event.target.closest('.search-history-item'); if (!item) return; const term = item.dataset.term; const idxStr = item.dataset.historyIndex; if (term === undefined || idxStr === undefined) return; const idx = parseInt(idxStr, 10); if (isNaN(idx)) return; if (isHistoryTrackingNeeded()) { historyManager.setCurrentIndex(idx); } const targetInput = optimizedFindSearchBox(); if (targetInput && nativeInputValueSetter) { try { if (targetInput.value !== term) { nativeInputValueSetter.call(targetInput, term); targetInput.dispatchEvent(inputEvent); } targetInput.focus(); lastSyncedValue = term; } catch (error) { console.error("[Mubu HistClick] Input err:", error); } } else { console.warn("[Mubu HistClick] Input not found."); } if (isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== term) { customInput.value = term; } historyManager.updatePanel(); }
    function handleHistoryNavigation(direction) { throttle((dir) => { if (!isFeatureEnabled('syncSearchBox') || !originalInput || !customInput || !nativeInputValueSetter || !isHistoryTrackingNeeded()) return; const size = historyManager.size(); if (size === 0) return; let curIdx = historyManager.getCurrentIndex(); let nextIdx = (curIdx === -1) ? ((dir === -1) ? size - 1 : -1) : (curIdx + dir); nextIdx = Math.max(-1, Math.min(nextIdx, size - 1)); if (nextIdx === curIdx || (curIdx === -1 && dir === 1 && nextIdx === -1)) return; historyManager.setCurrentIndex(nextIdx); runSynced(() => { let val = (nextIdx === -1) ? (lastSyncedValue ?? '') : (historyManager.get(nextIdx) ?? ''); if (customInput.value !== val) customInput.value = val; if (originalInput.value !== val) { nativeInputValueSetter.call(originalInput, val); originalInput.dispatchEvent(inputEvent); } lastSyncedValue = val; if (isFeatureEnabled('historyPanel')) historyManager.updatePanel(); }); }, config.sync.throttleTime)(direction); }
    function handleClear() { if (!isFeatureEnabled('syncSearchBox') || !originalInput || !customInput || !nativeInputValueSetter) return; if (customInput.value === '' && originalInput.value === '') return; if (customInput.value !== '') customInput.value = ''; syncToOriginal({ updateHistory: false }); if (isFeatureEnabled('historyPanel')) { removeActiveHighlight(); } lastSyncedValue = ''; }
    function setupInputListeners(targetInput){ // (保持不变)
        if (!targetInput){return;} teardownInputListeners(targetInput);
        if (isFeatureEnabled('syncSearchBox') && customInput){ originalInputSyncHandler = handleOriginalInputForSync; targetInput.addEventListener('input', originalInputSyncHandler, { passive: true }); }
        else if (isFeatureEnabled('historyPanel') && isHistoryTrackingNeeded()){ originalInputHistoryHandler = handleOriginalInputForHistory; targetInput.addEventListener('input', originalInputHistoryHandler, { passive: true }); }
    }
    function teardownInputListeners(targetInput){ // (保持不变)
        if (!targetInput) return; if (originalInputSyncHandler){ try { targetInput.removeEventListener('input', originalInputSyncHandler); } catch(e){} } if (originalInputHistoryHandler){ try { targetInput.removeEventListener('input', originalInputHistoryHandler); } catch(e){} } originalInputSyncHandler = null; originalInputHistoryHandler = null;
    }

    // 移除旧的 showSelectPopup, hideSelectPopup - 由新函数处理
    // function showSelectPopup(selection){ ... }
    // function hideSelectPopup(){ ... }

    // "筛选" 按钮的点击处理
    function handlePopupClick(event){ // 这个函数现在只处理筛选按钮的点击逻辑
        if(!isFeatureEnabled('selectSearchPopup')) return;
        event.preventDefault();
        event.stopPropagation(); // 阻止冒泡很重要

        const term = currentSelectedText; // 使用存储的文本
        if(!term){
            hideSelectionActionButtons(); // 没有文本则隐藏
            return;
        }

        const targetInput = optimizedFindSearchBox();
        if(targetInput && nativeInputValueSetter){
            try{
                nativeInputValueSetter.call(targetInput, term);
                targetInput.dispatchEvent(inputEvent);
                targetInput.focus();
                let hChanged = false;
                if(isHistoryTrackingNeeded()) hChanged = historyManager.add(term);
                historyManager.resetIndexToCurrent();
                if(isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== term) customInput.value = term;
                if(isFeatureEnabled('historyPanel')){
                    if(hChanged) historyManager.updatePanel();
                    removeActiveHighlight();
                }
                lastSyncedValue = term;
            } catch(error){
                console.error("[Mubu SS] Trigger err:", error);
                alert(`触发筛选时出错: ${error.message}`);
            }
        } else {
            console.warn("[Mubu SS] Input not found.");
            alert("未找到搜索框!");
        }
        hideSelectionActionButtons(); // 操作完成后隐藏按钮组
    }

    // 用于处理点击页面其他地方隐藏按钮
    function handleMouseDownPopup(event){
        // 如果点击目标不是任何一个操作按钮或开关面板,则隐藏按钮组
        const target = event.target;
        if (target instanceof Node) {
             const isClickOnActionButton = popupElement?.contains(target) ||
                                           tp_triggerButtonRef.element?.contains(target) ||
                                           tp_cutButtonRef.element?.contains(target);
             const isClickOnToggle = togglePanelElement?.contains(target) ||
                                     toggleTriggerElement?.contains(target);
             const isClickOnPasteButton = tp_pasteButtonRef.element?.contains(target);

             if (!isClickOnActionButton && !isClickOnToggle && !isClickOnPasteButton) {
                 hideSelectionActionButtons();
             }
              // 单独处理粘贴按钮的隐藏
              if (!isClickOnPasteButton && !isClickOnToggle) {
                 tp_hidePasteButton();
              }
        } else {
             // 如果 target 不是 Node,可能是一些特殊情况,保险起见隐藏
             hideSelectionActionButtons();
             tp_hidePasteButton();
        }
    }

    // 用于处理选择结束或点击后判断是否显示按钮
    function handleMouseUpSelectionEnd(event){
         // 如果点击发生在任何操作按钮或开关面板上,则不处理(防止按钮立即消失)
         const target = event.target;
          if (target instanceof Node) {
             const isClickOnActionButton = popupElement?.contains(target) ||
                                           tp_triggerButtonRef.element?.contains(target) ||
                                           tp_cutButtonRef.element?.contains(target);
             const isClickOnToggle = togglePanelElement?.contains(target) ||
                                     toggleTriggerElement?.contains(target);
             const isClickOnPasteButton = tp_pasteButtonRef.element?.contains(target); // 粘贴按钮也算

             if (isClickOnActionButton || isClickOnToggle || isClickOnPasteButton) {
                 return;
             }
          }

        // 使用 setTimeout + requestAnimationFrame 确保选区状态稳定
        setTimeout(() => {
            requestAnimationFrame(() => {
                const selection = window.getSelection();
                if (selection && !selection.isCollapsed && selection.toString().trim().length > 0) {
                    // 检查是否在可编辑区域内(showSelectionActionButtons 内部也会检查,但这里预检一次)
                    const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
                     if (range) {
                         const containerNode = range.commonAncestorContainer;
                         const isInEditable = containerNode && (
                             (containerNode.nodeType === Node.ELEMENT_NODE && tp_isElementEditable(containerNode)) ||
                             (containerNode.nodeType === Node.TEXT_NODE && containerNode.parentElement && tp_isElementEditable(containerNode.parentElement))
                         );
                         if (isInEditable) {
                             console.log('[Mubu Helper] Showing buttons after mouseup (normal selection)');
                             // 这里触发的是普通选择,isCtrlA = false
                             showSelectionActionButtons(selection, false);
                         } else {
                             hideSelectionActionButtons(); // 不在可编辑区,隐藏
                         }
                     } else {
                          hideSelectionActionButtons(); // 获取不到 range,隐藏
                     }
                } else if (selection && selection.isCollapsed && (tp_storedHTML || tp_storedText)) {
                     // 如果是光标状态且有缓存内容,尝试显示粘贴按钮(由 tp_handleMouseUp 处理)
                     // 这里不处理,避免逻辑冲突
                }
                 else {
                    // 没有有效选区,隐藏按钮组
                    hideSelectionActionButtons();
                }
            });
        }, config.select.popupAppearDelay); // 使用统一的延迟
    }

    // --- [ ☆☆ 新增/重构:统一的按钮显示/隐藏逻辑 ☆☆ ] ---

    // 辅助函数:隐藏所有选区操作按钮(筛选、复制、剪切)
    function hideSelectionActionButtons() {
        // 隐藏筛选按钮
        if (popupElement?.isConnected && popupElement.style.visibility !== 'hidden') {
            scheduleStyleUpdate(popupElement, { opacity: '0', visibility: 'hidden' });
            setTimeout(() => {
                if (popupElement?.style.opacity === '0') {
                    scheduleStyleUpdate(popupElement, { display: 'none' });
                }
            }, 150); // 动画时间
        }
        // 隐藏复制按钮
        if (tp_triggerButtonRef.element?.isConnected && tp_triggerButtonRef.element.style.visibility !== 'hidden') {
            scheduleStyleUpdate(tp_triggerButtonRef.element, { opacity: '0', visibility: 'hidden' });
             setTimeout(() => {
                if (tp_triggerButtonRef.element?.style.opacity === '0') {
                    scheduleStyleUpdate(tp_triggerButtonRef.element, { display: 'none' });
                }
            }, 150);
        }
        // 隐藏剪切按钮
        if (tp_cutButtonRef.element?.isConnected && tp_cutButtonRef.element.style.visibility !== 'hidden') {
            scheduleStyleUpdate(tp_cutButtonRef.element, { opacity: '0', visibility: 'hidden' });
             setTimeout(() => {
                if (tp_cutButtonRef.element?.style.opacity === '0') {
                    scheduleStyleUpdate(tp_cutButtonRef.element, { display: 'none' });
                }
            }, 150);
        }
        // 清除与筛选按钮关联的文本
        currentSelectedText = '';
    }

    // 核心函数:显示并定位选区操作按钮 (处理函数1和函数2的逻辑)
    function showSelectionActionButtons(selection, isCtrlA = false) {
        // 0. 初始清理和验证
        hideSelectionActionButtons(); // 总是先隐藏旧的
        tp_hidePasteButton(); // 同时隐藏粘贴按钮,避免重叠

        if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
            return;
        }
        const selectionText = selection.toString().trim();
        if (selectionText.length === 0) {
            return;
        }

        // 检查是否在可编辑区域内
        const containerNode = selection.getRangeAt(0).commonAncestorContainer;
        const isInEditable = containerNode && (
            (containerNode.nodeType === Node.ELEMENT_NODE && tp_isElementEditable(containerNode)) ||
            (containerNode.nodeType === Node.TEXT_NODE && containerNode.parentElement && tp_isElementEditable(containerNode.parentElement))
        );
        if (!isInEditable) {
            return;
        }

        // 1. 确定按钮顺序和可能出现的按钮
        // 函数1顺序: filter -> copy -> cut
        // 函数2顺序 (Ctrl+A): copy -> cut (filter 永不出现)
        const buttonOrder = isCtrlA ? ['copy', 'cut'] : ['filter', 'copy', 'cut'];

        // 2. 筛选出启用的、应该出现的按钮,并确保元素存在
        const visibleButtonsData = [];
        let maxHeight = 0;

        buttonOrder.forEach(type => {
            let buttonInfo = null;
            const shouldAppear = (
                (type === 'filter' && !isCtrlA && isFeatureEnabled('selectSearchPopup')) ||
                (type === 'copy' && isFeatureEnabled('transferPasteCopy')) ||
                (type === 'cut' && isFeatureEnabled('transferPasteCut'))
            );

            if (shouldAppear) {
                try { // 包裹 try-catch 以防按钮创建/获取失败
                    if (type === 'filter') {
                        if (!popupElement) createSelectPopup(); // 创建筛选按钮元素
                        if (!popupElement) return; // 创建失败则跳过
                        buttonInfo = {
                            type: 'filter',
                            element: popupElement,
                            fallbackW: config.select.fallbackWidth,
                            fallbackH: config.select.fallbackHeight
                        };
                        // 为筛选按钮关联当前选中的文本
                        currentSelectedText = selectionText; // 在这里设置
                    } else if (type === 'copy') {
                        // 调用 tp_createButton 来获取或创建 "复制" 按钮
                         tp_triggerButtonRef.element = tp_createButton(
                            config.transferPaste.triggerButtonId,
                            config.transferPaste.triggerButtonText,
                            config.transferPaste.btnCopyClass,
                            (button) => { // 点击回调
                                if (!tp_captureSelectionAndStore()) { alert('捕获选区失败!'); }
                                hideSelectionActionButtons(); // 操作后隐藏按钮组
                            }
                        );
                         if (!tp_triggerButtonRef.element) return; // 创建失败则跳过
                        buttonInfo = {
                            type: 'copy',
                            element: tp_triggerButtonRef.element,
                            fallbackW: config.transferPaste.buttonFallbackWidth,
                            fallbackH: config.transferPaste.buttonFallbackHeight
                        };
                    } else if (type === 'cut') {
                        // 调用 tp_createButton 来获取或创建 "剪切" 按钮
                         tp_cutButtonRef.element = tp_createButton(
                            config.transferPaste.cutButtonId,
                            config.transferPaste.cutButtonText,
                            config.transferPaste.btnCutClass,
                            (button) => { // 点击回调
                                const latestSel = window.getSelection();
                                if (latestSel && !latestSel.isCollapsed) {
                                    if (tp_captureSelectionAndStore()) {
                                        try {
                                            // 保留原有的 execCommand 逻辑
                                            if (!document.execCommand('delete', false, null)) {
                                                console.warn("[Mubu TP] execCommand('delete') failed, fallback.");
                                                latestSel.getRangeAt(0).deleteContents();
                                            }
                                        } catch (e) {
                                            console.error('[Mubu TP] Delete err, fallback:', e);
                                            try { latestSel.getRangeAt(0).deleteContents(); }
                                            catch (e2) { console.error('[Mubu TP] Fallback err:', e2); alert('剪切删除失败.'); }
                                        }
                                    } else { alert('捕获失败,无法剪切!'); }
                                } else { alert('选区失效,无法剪切!'); }
                                hideSelectionActionButtons(); // 操作后隐藏按钮组
                            }
                        );
                         if (!tp_cutButtonRef.element) return; // 创建失败则跳过
                        buttonInfo = {
                            type: 'cut',
                            element: tp_cutButtonRef.element,
                            fallbackW: config.transferPaste.buttonFallbackWidth,
                            fallbackH: config.transferPaste.buttonFallbackHeight
                        };
                    }
                } catch (creationError) {
                     console.error(`[Mubu Helper] Error creating/getting button type ${type}:`, creationError);
                     return; // 出错则跳过此按钮
                }
            }

            if (buttonInfo && buttonInfo.element && buttonInfo.element.isConnected) {
                observeElementResize(buttonInfo.element); // 确保被观察
                const dims = elementDimensionsCache.get(buttonInfo.element);
                buttonInfo.width = dims?.width || buttonInfo.fallbackW;
                buttonInfo.height = dims?.height || buttonInfo.fallbackH;
                if (buttonInfo.width > 0 && buttonInfo.height > 0) { // 确保尺寸有效
                     maxHeight = Math.max(maxHeight, buttonInfo.height);
                     visibleButtonsData.push(buttonInfo);
                } else {
                     // 尝试强制获取一次尺寸
                     const ow = buttonInfo.element.offsetWidth;
                     const oh = buttonInfo.element.offsetHeight;
                      if (ow > 0 && oh > 0) {
                          buttonInfo.width = ow;
                          buttonInfo.height = oh;
                          elementDimensionsCache.set(buttonInfo.element, { width: ow, height: oh }); // 更新缓存
                          maxHeight = Math.max(maxHeight, buttonInfo.height);
                          visibleButtonsData.push(buttonInfo);
                      } else {
                           console.warn(`[Mubu Helper] Invalid or zero dimensions for button type: ${buttonInfo.type}`, buttonInfo.element);
                           // 尺寸无效时,暂时不显示此按钮
                      }
                }
            }
        });

        // 3. 如果没有按钮需要显示,则退出
        if (visibleButtonsData.length === 0) {
            return;
        }

        // 4. 计算定位
        const targetRect = getCursorRect(selection);
        if (!targetRect || (targetRect.width === 0 && targetRect.height === 0 && selectionText.length === 0) ) {
            hideSelectionActionButtons(); // 无法获取有效位置则隐藏
            return;
        }

        const totalWidth = visibleButtonsData.reduce((sum, btn) => sum + btn.width, 0) + Math.max(0, visibleButtonsData.length - 1) * BUTTON_GAP;
        const scrollY = window.pageYOffset;
        const scrollX = window.pageXOffset;
        const vpW = window.innerWidth;

        // 统一使用最高的按钮的高度来计算顶部位置,保证对齐
        const groupTop = Math.max(scrollY + 5, scrollY + targetRect.top - maxHeight - config.select.popupAboveGap); // 使用筛选按钮的上边距配置

        const selectionCenterX = targetRect.left + targetRect.width / 2;
        let groupLeftStart = scrollX + selectionCenterX - totalWidth / 2;

        // 防止按钮组溢出视口边界
        groupLeftStart = Math.max(scrollX + 5, groupLeftStart); // 防止左侧溢出
        if (groupLeftStart + totalWidth > scrollX + vpW - 5) { // 防止右侧溢出
            groupLeftStart = scrollX + vpW - totalWidth - 5;
            groupLeftStart = Math.max(scrollX + 5, groupLeftStart); // 再次检查左侧
        }

        // 5. 定位并显示每个按钮
        let currentLeftOffset = 0;
        visibleButtonsData.forEach((buttonInfo, index) => {
            const currentButtonLeft = groupLeftStart + currentLeftOffset;
            // 确保按钮在 DOM 中(tp_createButton 处理了创建,这里保险)
            if (!buttonInfo.element.isConnected) {
                 try { docBody.appendChild(buttonInfo.element); observeElementResize(buttonInfo.element); }
                 catch(e) { console.error("Error re-appending button:", e); return; }
            }
            scheduleStyleUpdate(buttonInfo.element, {
                transform: `translate(${currentButtonLeft.toFixed(1)}px, ${groupTop.toFixed(1)}px)`,
                display: 'inline-block', // 改为 inline-block 以便横向排列
                opacity: '1',
                visibility: 'visible'
            });
            currentLeftOffset += buttonInfo.width + (index < visibleButtonsData.length - 1 ? BUTTON_GAP : 0);
        });
    }
    // --- [ ☆☆ 重构结束 ☆☆ ] ---


    // --- [ 复制标签 (Copy Tag) 相关 ] --- (保持不变)
    async function ct_handleCopyButtonClick(event) {
        if (!isFeatureEnabled('copyTagOnHover')) return;
        event.stopPropagation();
        event.preventDefault();

        if (!ct_currentTagText || !ct_copyPopupElement || !ct_currentHoveredTag) return;

        const text = " " + ct_currentTagText; // 加前缀空格
        // console.log("[CT Debug] Copy button clicked for tag:", ct_currentTagText);

        try {
            await navigator.clipboard.writeText(text);
            // console.log("[CT Debug] Clipboard write successful.");
            ct_showFeedbackIndicator(ct_currentHoveredTag); // 显示反馈
            ct_hideCopyPopupImmediately(false); // 隐藏复制按钮,不清状态以便反馈定位
        } catch (err) {
            console.error("[CT] Clipboard err:", err);
            alert(`复制失败: ${err.message}`);
        }
    }

    // --- [ ☆☆ v3.5.5: 修改 handleMouseOver ☆☆ ] --- (保持不变)
    function ct_handleMouseOver(event) {
        if (!isFeatureEnabled('copyTagOnHover')) return; if (!(event.target instanceof Element)) { return; }
        const relevant = event.target.closest(`${config.copyTag.tagSelector}, #${config.copyTag.popupId}`);

        if (!relevant) {
            if (ct_currentHoveredTag) {
                 ct_scheduleHidePopup();
            }
            return;
        }
        ct_createElements();
        if (!ct_copyPopupElement) { console.warn("[CT] Copy popup element missing in handleMouseOver."); return; }

        const tagEl = relevant.matches(config.copyTag.tagSelector) ? relevant : null;
        const isOverPopup = relevant === ct_copyPopupElement;

        if (tagEl) {
            // console.log("[CT Debug] Mouse over tag:", tagEl.textContent);
            if (tagEl === ct_currentHoveredTag) {
                clearTimeout(ct_hideTimeout); ct_hideTimeout = null;
                // console.log("[CT Debug] Mouse still on the same tag, cancelled hide timeout.");
                if (ct_copyPopupElement.style.visibility === 'hidden' || ct_copyPopupElement.style.opacity === '0') {
                    // console.log("[CT Debug] Copy popup was hidden, reshowing.");
                    ct_showCopyPopup(tagEl);
                }
            } else {
                // console.log("[CT Debug] Mouse moved to new tag:", tagEl.textContent);
                clearTimeout(ct_showTimeout);
                clearTimeout(ct_hideTimeout); ct_hideTimeout = null;

                if (ct_currentHoveredTag && ct_copyPopupElement.style.visibility !== 'hidden' && ct_copyPopupElement.style.opacity !== '0') {
                    // console.log("[CT Debug] Hiding previous copy popup immediately.");
                    ct_hideCopyPopupImmediately(false);
                }
                ct_currentHoveredTag = tagEl;

                // console.log("[CT Debug] Scheduling show for new copy popup.");
                ct_showTimeout = setTimeout(() => {
                    if (ct_currentHoveredTag === tagEl && tagEl.matches(':hover')) {
                        // console.log("[CT Debug] Showing copy popup for new tag after delay.");
                        ct_showCopyPopup(tagEl);
                    } else {
                        // console.log("[CT Debug] Show timeout expired, but mouse no longer on tag.");
                    }
                    ct_showTimeout = null;
                }, config.copyTag.hoverDelay);
            }
        } else if (isOverPopup) {
            // console.log("[CT Debug] Mouse over copy popup.");
            clearTimeout(ct_hideTimeout); ct_hideTimeout = null;
            clearTimeout(ct_showTimeout); ct_showTimeout = null;
            if (ct_copyPopupElement.style.visibility === 'hidden' || ct_copyPopupElement.style.opacity === '0') {
                 // console.log("[CT Debug] Copy popup was hidden while hovering, making visible.");
                 scheduleStyleUpdate(ct_copyPopupElement, { opacity: '1', visibility: 'visible', display: 'block' });
            }
        }
    }
    // --- [ ☆☆ v3.5.5 结束 ☆☆ ] --- (保持不变)

    // TP (中转粘贴) 的事件处理器
    function tp_handleMouseUp(event) { // 这个函数现在主要处理“粘贴”按钮的显示逻辑
        const target = event.target;
         // 如果点击发生在任何操作按钮或开关面板上,则不处理
         if (target instanceof Node) {
             const isClickOnActionButton = popupElement?.contains(target) ||
                                           tp_triggerButtonRef.element?.contains(target) ||
                                           tp_cutButtonRef.element?.contains(target);
             const isClickOnToggle = togglePanelElement?.contains(target) ||
                                     toggleTriggerElement?.contains(target);
             // 注意:这里不检查粘贴按钮本身,因为点击粘贴按钮应该执行粘贴操作

             if (isClickOnActionButton || isClickOnToggle) {
                 return;
             }
          }

        // 延迟以确保选区状态稳定
        setTimeout(() => {
             requestAnimationFrame(() => {
                 const latestSel = window.getSelection();
                 if (!latestSel) return;

                 const hasStoredContent = !!(tp_storedHTML || tp_storedText);
                 const targetEl = event.target instanceof Node ? event.target : null;
                 const targetEditable = tp_isElementEditable(targetEl);

                 // 条件:光标是折叠状态,有缓存内容,且目标可编辑
                 if (latestSel.isCollapsed && hasStoredContent && targetEditable) {
                     // 确保这次点击不是发生在筛选按钮上(虽然上面检查过,双重保险)
                      if (!popupElement || !popupElement.contains(event.target)) {
                           console.log('[Mubu Helper TP] Showing paste button.');
                           hideSelectionActionButtons(); // 隐藏筛选/复制/剪切组
                           tp_showPasteButton(event); // 显示独立的粘贴按钮
                      }
                 }
                 // else if (!latestSel.isCollapsed && latestSel.toString().trim().length > 0) {
                 //   // 选区状态下显示 筛选/复制/剪切 按钮的逻辑已移至 handleMouseUpSelectionEnd
                 // }
                  else {
                     // 其他情况(如选区存在但无缓存内容,或目标不可编辑),确保粘贴按钮隐藏
                     tp_hidePasteButton();
                     // 注意:这里不调用 hideSelectionActionButtons,因为可能刚通过 handleMouseUpSelectionEnd 显示了它们
                 }
             });
        }, config.transferPaste.buttonsAppearDelay); // 使用 TP 的延迟
    }

    // tp_handleMouseDown 已合并到 handleMouseDownPopup 中处理

    function tp_handleKeyDown(event) { // 处理 Ctrl+A 和一些特殊按键
        if (togglePanelElement?.contains(document.activeElement)) return; // 忽略开关面板内的按键

        // 同时检查 TP 和 Select Search 是否启用,因为按钮组现在是统一的
        const anyActionEnabled = isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut') || isFeatureEnabled('selectSearchPopup');
        if (!anyActionEnabled && !tp_pasteButtonRef.element) return; // 如果没有任何相关功能启用且粘贴按钮不存在,则退出

        if ((event.ctrlKey || event.metaKey) && (event.key === 'a' || event.key === 'A')) {
            tp_ctrlApressed = true;
            hideSelectionActionButtons(); // Ctrl+A 时隐藏按钮组
            tp_hidePasteButton();       // 同时隐藏粘贴按钮
        } else {
            if (tp_ctrlApressed && !(event.key === 'Control' || event.key === 'Meta' || event.key === 'Shift' || event.key === 'Alt')) {
                 tp_ctrlApressed = false; // 如果在按下 Ctrl+A 后按了其他键,重置标志
             }

            // 如果粘贴按钮可见,并且按下了某些导航/编辑键,则隐藏它
            if (tp_pasteButtonRef.element?.style.visibility !== 'hidden' && !tp_pasteButtonRef.element.contains(event.target)) {
                if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown', 'Backspace', 'Delete', 'Enter'].includes(event.key)) {
                    tp_hidePasteButton();
                }
            }

             // 如果筛选/复制/剪切按钮组中的任何一个可见,并且按下了删除/退格键,则在延迟后检查选区是否消失
             const actionButtonsVisible = (popupElement?.style.visibility !== 'hidden') ||
                                          (tp_triggerButtonRef.element?.style.visibility !== 'hidden') ||
                                          (tp_cutButtonRef.element?.style.visibility !== 'hidden');
             const targetOnActionButtons = popupElement?.contains(event.target) ||
                                           tp_triggerButtonRef.element?.contains(event.target) ||
                                           tp_cutButtonRef.element?.contains(event.target);

             if (actionButtonsVisible && !targetOnActionButtons) {
                 if (['Backspace', 'Delete'].includes(event.key)) {
                     setTimeout(() => {
                         const selection = window.getSelection();
                         if (!selection || selection.isCollapsed) {
                             hideSelectionActionButtons(); // 如果选区没了,隐藏按钮组
                         }
                     }, 0); // 立即检查下一次事件循环
                 }
                 // 如果按下了移动键或其他可能改变选区的键,也可以考虑隐藏,但这可能过于激进
                  // else if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown'].includes(event.key)) {
                  //      hideSelectionActionButtons();
                  // }
             }
        }
    }

    function tp_handleKeyUp(event) { // 处理 Ctrl+A 释放
        if (togglePanelElement?.contains(document.activeElement)) return;

        const anyActionEnabled = isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut'); // Ctrl+A 只关心复制剪切
        if (!anyActionEnabled) return;

        // 检查是否是 Ctrl 或 A 键的释放,并且之前按下了 Ctrl+A
        if (tp_ctrlApressed && (event.key === 'Control' || event.key === 'Meta' || event.key === 'a' || event.key === 'A')) {
            setTimeout(() => {
                // 再次确认 Ctrl/Meta 键是否已抬起(或者当前键是 A 且修饰键已抬起)
                const modPressed = event.ctrlKey || event.metaKey;
                if ((event.key === 'Control' || event.key === 'Meta') || ((event.key === 'a' || event.key === 'A') && !modPressed)) {
                     // 确认 tp_ctrlApressed 标志仍然为 true (防止重复触发)
                    if (tp_ctrlApressed) {
                        // 获取当前选区
                        const currentSelection = window.getSelection();
                        if (currentSelection && !currentSelection.isCollapsed && currentSelection.toString().trim().length > 0) {
                            // 检查是否在可编辑区
                             const range = currentSelection.rangeCount > 0 ? currentSelection.getRangeAt(0) : null;
                             if (range) {
                                 const containerNode = range.commonAncestorContainer;
                                 const isInEditable = containerNode && (
                                     (containerNode.nodeType === Node.ELEMENT_NODE && tp_isElementEditable(containerNode)) ||
                                     (containerNode.nodeType === Node.TEXT_NODE && containerNode.parentElement && tp_isElementEditable(containerNode.parentElement))
                                 );
                                 if (isInEditable) {
                                      requestAnimationFrame(() => {
                                         console.log('[Mubu Helper] Showing buttons after Ctrl+A keyup');
                                         showSelectionActionButtons(currentSelection, true); // 调用新函数,标记为 Ctrl+A
                                     });
                                 } else {
                                       hideSelectionActionButtons(); // 不在可编辑区
                                 }
                             } else {
                                   hideSelectionActionButtons(); // 获取不到 Range
                             }
                        } else {
                             hideSelectionActionButtons(); // Ctrl+A 后可能没有选区或选区无效
                        }
                        tp_ctrlApressed = false; // 重置标志
                    }
                }
            }, 0); // 延迟到下一个事件循环,确保状态更新
        }
    }


    // --- [ ☆ 初始化与清理 ☆ ] ---
    function tp_initialize() { // (保持不变)
        console.log('[Mubu TP] Initializing, searching for editor container...');
        const tpConfig = config.transferPaste;
        const MAX_RETRIES = tpConfig.initWaitMaxRetries;
        const RETRY_INTERVAL = tpConfig.initWaitRetryInterval;
        let retries = 0;
        const intervalId = setInterval(() => {
            if (tp_editorContainer) {
                 clearInterval(intervalId);
                 return;
            }
            const container = document.querySelector(tpConfig.editorContainerSelector);
            if (container) {
                clearInterval(intervalId);
                tp_editorContainer = container;
                console.log('[Mubu TP] Editor container found:', tp_editorContainer);
                tp_attachListeners(); // 找到容器后尝试附加监听器
            } else {
                retries++;
                if (retries >= MAX_RETRIES) {
                    clearInterval(intervalId);
                    console.error(`[Mubu TP] Init failed: Container "${tpConfig.editorContainerSelector}" not found after ${MAX_RETRIES} retries.`);
                }
            }
        }, RETRY_INTERVAL);
    }
    function tp_attachListeners() { // (保持不变)
        if (!tp_editorContainer) {
            console.warn('[Mubu TP] Attach skipped: Editor container not available yet.');
            return;
        }
        // 现在 TP 监听器也服务于粘贴按钮,所以即使 Copy/Cut 禁用也可能需要
        // if (!isFeatureEnabled('transferPasteCopy') && !isFeatureEnabled('transferPasteCut')) {
        //      console.log('[Mubu TP] Attach skipped: Both TP features are currently disabled.');
        //      return; // 如果不需要粘贴按钮,可以取消注释此行
        // }
        if (tp_listenersAttached) {
            return;
        }
        console.log('[Mubu TP] Attaching listeners to:', tp_editorContainer);
        try {
            // 注意:mousedown 和 mouseup 现在由全局监听器处理统一的按钮组逻辑
            // tp_editorContainer.addEventListener('mouseup', tp_handleMouseUp, true); // 改为全局 document 监听
            // tp_editorContainer.addEventListener('mousedown', tp_handleMouseDown, true); // 改为全局 document 监听
            tp_editorContainer.addEventListener('keydown', tp_handleKeyDown, true); // 保留 keydown
            tp_editorContainer.addEventListener('keyup', tp_handleKeyUp, true);   // 保留 keyup
            tp_listenersAttached = true;
            console.log('[Mubu TP] Key listeners attached successfully.');
        } catch (e) {
             console.error('[Mubu TP] Error attaching key listeners:', e);
             tp_listenersAttached = false;
        }
    }
    function tp_detachListeners() { // (保持不变)
        if (!tp_editorContainer || !tp_listenersAttached) return;
        console.log('[Mubu TP] Detaching listeners from:', tp_editorContainer);
        try {
            // tp_editorContainer.removeEventListener('mouseup', tp_handleMouseUp, true);
            // tp_editorContainer.removeEventListener('mousedown', tp_handleMouseDown, true);
            tp_editorContainer.removeEventListener('keydown', tp_handleKeyDown, true);
            tp_editorContainer.removeEventListener('keyup', tp_handleKeyUp, true);
            tp_listenersAttached = false;
            console.log('[Mubu TP] Key listeners detached successfully.');
        }
        catch (e) { console.warn('[Mubu TP] Detach listeners err:', e); }
    }

    // --- [ ☆ 功能开关面板相关函数 ☆ ] --- (保持不变)
    function hideTogglePanel() { if (togglePanelElement) { scheduleStyleUpdate(togglePanelElement, { transform: `translateX(100%)` }); } }
    function scheduleHideTogglePanel() { clearTimeout(togglePanelHideTimeout); togglePanelHideTimeout = setTimeout(() => { const triggerHover = toggleTriggerElement?.matches(':hover'); const panelHover = togglePanelElement?.matches(':hover'); if (!triggerHover && !panelHover) { hideTogglePanel(); } }, config.togglePanel.hideDelay); }
    function showTogglePanel() { clearTimeout(togglePanelHideTimeout); if (togglePanelElement) { scheduleStyleUpdate(togglePanelElement, { transform: 'translateX(0)' }); } }
    function createTogglePanel() { // (保持不变)
        const panelId = config.togglePanel.panelId; const triggerId = config.togglePanel.triggerId; if (document.getElementById(panelId)) return;
        try { toggleTriggerElement = document.createElement('div'); toggleTriggerElement.id = triggerId; toggleTriggerElement.addEventListener('mouseenter', showTogglePanel); toggleTriggerElement.addEventListener('mouseleave', scheduleHideTogglePanel); document.body.appendChild(toggleTriggerElement);
            togglePanelElement = document.createElement('div'); togglePanelElement.id = panelId; togglePanelElement.innerHTML = '<div class="toggle-panel-title">功能开关</div>'; togglePanelElement.addEventListener('mouseenter', showTogglePanel); togglePanelElement.addEventListener('mouseleave', scheduleHideTogglePanel);
            for (const key in FEATURES) { if (FEATURES.hasOwnProperty(key)) { const feature = FEATURES[key]; const isEnabled = runtimeFeatureState[key];
                    const div = document.createElement('div'); div.className = 'toggle-control'; const label = document.createElement('label'); label.htmlFor = `toggle-${key}`; label.textContent = feature.label; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = `toggle-${key}`; checkbox.checked = isEnabled; checkbox.dataset.featureKey = key;
                    checkbox.addEventListener('change', (event) => { const changedKey = event.target.dataset.featureKey; const newState = event.target.checked; runtimeFeatureState[changedKey] = newState; console.log(`[Mubu] ${FEATURES[changedKey].label} ${newState ? '启用' : '禁用'}`); applyFeatureStateChange(changedKey, newState); });
                    div.appendChild(checkbox); div.appendChild(label); togglePanelElement.appendChild(div); } } document.body.appendChild(togglePanelElement);
        } catch (e) { console.error('[Mubu] Create toggle panel err:', e); togglePanelElement = null; toggleTriggerElement = null; }
    }

    // --- [ ☆ 标记监听器是否已添加 ☆ ] ---
    let customInputListenerAttached = false;
    let historyListClickListenerAttached = false;
    let selectPopupListenersAttached = false; // 全局 mousedown/mouseup 监听器
    let copyTagListenerAttached = false;
    // tp_listenersAttached 现在只标记 keydown/keyup

    /** 应用功能状态变更 */
    function applyFeatureStateChange(featureKey, isEnabled) { // (更新 Select Search 和 TP 的逻辑)
        switch (featureKey) {
            case 'syncSearchBox': // (保持不变)
                if (isEnabled) { if (!topBarControls.container) createControlPanel(); if (topBarControls.container) scheduleStyleUpdate(topBarControls.container, { display: 'flex' }); if (!customInput && topBarControls.input) customInput = topBarControls.input; if(topBarControls.input && !customInputListenerAttached) { topBarControls.prevBtn?.addEventListener('click', historyNavPrevListener); topBarControls.nextBtn?.addEventListener('click', historyNavNextListener); topBarControls.clearBtn?.addEventListener('click', clearBtnListener); topBarControls.input?.addEventListener('input', customInputListener, { passive: true }); customInputListenerAttached = true; } setupInputListeners(originalInput); findAndSetupDebounced(); }
                else { if (topBarControls.container) scheduleStyleUpdate(topBarControls.container, { display: 'none' }); if(customInputListenerAttached) { topBarControls.prevBtn?.removeEventListener('click', historyNavPrevListener); topBarControls.nextBtn?.removeEventListener('click', historyNavNextListener); topBarControls.clearBtn?.removeEventListener('click', clearBtnListener); topBarControls.input?.removeEventListener('input', customInputListener); customInputListenerAttached = false; } customInput = null; setupInputListeners(originalInput); } break;
            case 'historyPanel': // (保持不变)
                if (isEnabled) { if (!historyPanel) createHistoryPanel(); if (historyPanel) scheduleStyleUpdate(historyPanel, { display: 'flex' }); if(historyListElement && !historyListClickListenerAttached) { historyListElement.addEventListener('click', handleHistoryListClick); historyListClickListenerAttached = true; } historyManager.updatePanel(); setupInputListeners(originalInput); }
                else { if (historyPanel) scheduleStyleUpdate(historyPanel, { display: 'none' }); if(historyListClickListenerAttached) { historyListElement?.removeEventListener('click', handleHistoryListClick); historyListClickListenerAttached = false; } setupInputListeners(originalInput); } break;
            case 'selectSearchPopup': // "筛选"按钮 和 统一的 mouseup/mousedown 监听器
            case 'transferPasteCopy': // "复制"按钮
            case 'transferPasteCut':  // "剪切"按钮
                // 这三个功能共享 mousedown/mouseup 监听器来触发按钮组
                 const anyGroupButtonEnabled = isFeatureEnabled('selectSearchPopup') || isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut');
                 const anyPasteEnabled = isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut'); // 用于决定是否附加 TP Key 监听

                 if (anyGroupButtonEnabled) {
                     // 确保创建筛选按钮(如果启用了它)
                     if (isFeatureEnabled('selectSearchPopup') && !popupElement) {
                         createSelectPopup();
                     }
                      // 确保附加全局监听器
                     if (!selectPopupListenersAttached) {
                         try {
                             document.addEventListener('mousedown', handleMouseDownPopup, true); // 处理点击外部隐藏
                             document.addEventListener('mouseup', handleMouseUpSelectionEnd, true); // 处理选区结束显示按钮组
                              document.addEventListener('mouseup', tp_handleMouseUp, true); // 同时监听 TP 的 mouseup 以显示粘贴按钮
                             selectPopupListenersAttached = true;
                             console.log('[Mubu Helper] Global selection listeners attached.');
                         } catch (e) { console.error('[Mubu Helper] Attach global listeners err:', e); }
                     }
                 } else {
                     // 如果所有相关功能都禁用,则移除全局监听器并隐藏按钮
                     if (selectPopupListenersAttached) {
                         try {
                             document.removeEventListener('mousedown', handleMouseDownPopup, true);
                             document.removeEventListener('mouseup', handleMouseUpSelectionEnd, true);
                             document.removeEventListener('mouseup', tp_handleMouseUp, true); // 移除 TP 的 mouseup
                             selectPopupListenersAttached = false;
                             console.log('[Mubu Helper] Global selection listeners detached.');
                         } catch(e) { console.error('[Mubu Helper] Detach global listeners err:', e); }
                     }
                     hideSelectionActionButtons(); // 隐藏按钮组
                 }

                  // 单独处理 TP 的 Key 监听器 (仅当复制或剪切启用时需要)
                  if (anyPasteEnabled) {
                      tp_attachListeners(); // 尝试附加 keydown/keyup
                  } else {
                      tp_detachListeners(); // 移除 keydown/keyup
                      tp_hidePasteButton(); // 隐藏粘贴按钮
                  }

                  // 如果是具体某个按钮被禁用,确保它被隐藏(hideSelectionActionButtons 会在下次触发时处理)
                  if (!isEnabled) {
                      if (featureKey === 'selectSearchPopup' && popupElement) hideSelectionActionButtons(); // 隐藏整个组更安全
                      if (featureKey === 'transferPasteCopy' && tp_triggerButtonRef.element) hideSelectionActionButtons();
                      if (featureKey === 'transferPasteCut' && tp_cutButtonRef.element) hideSelectionActionButtons();
                  }
                break;
            case 'copyTagOnHover': // (保持不变)
                if (isEnabled) {
                    ct_createElements();
                    if (!copyTagListenerAttached) {
                        const target = document.querySelector(config.selectors.copyTagParentContainer) || document.body;
                        if (target) {
                            try {
                                target.addEventListener('mouseover', ct_handleMouseOver, { passive: true });
                                ct_listenerTarget = target;
                                copyTagListenerAttached = true;
                                console.log('[Mubu CT] Listener attached.');
                            } catch (e) { console.error('[Mubu CT] Attach err:', e); ct_listenerTarget = null; }
                        } else { console.warn(`[Mubu CT] Target not found.`); }
                    }
                } else {
                    ct_hideCopyPopupImmediately(true);
                    ct_hideFeedbackIndicator();
                    if (copyTagListenerAttached && ct_listenerTarget) {
                        try {
                            ct_listenerTarget.removeEventListener('mouseover', ct_handleMouseOver);
                            copyTagListenerAttached = false;
                            ct_listenerTarget = null;
                            console.log('[Mubu CT] Listener detached.');
                        } catch(e) { console.error('[Mubu CT] Detach err:', e); }
                    } else {
                         if (copyTagListenerAttached) copyTagListenerAttached = false;
                         ct_listenerTarget = null;
                    }
                    ct_currentHoveredTag = null;
                    ct_currentTagText = '';
                    clearTimeout(ct_showTimeout); ct_showTimeout = null;
                    clearTimeout(ct_hideTimeout); ct_hideTimeout = null;
                    clearTimeout(ct_feedbackTimeout); ct_feedbackTimeout = null;
                }
                break;
            // transferPasteCopy 和 transferPasteCut 的逻辑已合并到 selectSearchPopup 处理中
        }
    }

    function init() { // (更新 CSS 和初始化调用)
        console.log('[Mubu Combined Helper v3.6.0] 开始初始化...');
        try {
            let featuresEnabledCount = 0;

            // --- 前置检查 ---
            if ((FEATURES.syncSearchBox.enabled || FEATURES.selectSearchPopup.enabled || FEATURES.historyPanel.enabled) && !nativeInputValueSetter) { console.error("[Mubu] No native setter!"); }
            if (FEATURES.copyTagOnHover.enabled && (!navigator.clipboard?.writeText)) { console.warn("[Mubu] No clipboard API!"); }

            // --- 注入 CSS ---
             let combinedCSS = "";
             // 同步搜索框 + 历史面板 CSS (保持不变)
            combinedCSS += `#${config.sync.topBarId}{position:fixed;top:1px;left:50%;transform:translateX(-50%);z-index:10001;background:rgba(255,255,255,0.98);padding:6px;border-radius:8px;box-shadow:0 2px 12px rgba(0,0,0,0.15);display:flex;gap:8px;align-items:center;backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px)} #${config.sync.topBarId} .custom-search-input{padding:8px 12px;border:1px solid #dcdfe6;border-radius:20px;width:300px;font-size:14px;transition:all .2s ease-in-out;background:#f8f9fa;color:#303133;box-sizing:border-box}#${config.sync.topBarId} .custom-search-input::-webkit-search-cancel-button,#${config.sync.topBarId} .custom-search-input::-webkit-search-clear-button{display:none;-webkit-appearance:none;appearance:none}#${config.sync.topBarId} .custom-search-input:focus{border-color:#5856d5;outline:0;background:#fff;box-shadow:0 0 0 1px #5856d5}#${config.sync.topBarId} .history-btn,#${config.sync.topBarId} .clear-btn{padding:6px 12px;background:#f0f2f5;border:1px solid #dcdfe6;border-radius:20px;cursor:pointer;transition:all .2s ease-in-out;font-weight:500;color:#606266;flex-shrink:0;user-select:none;line-height:1}#${config.sync.topBarId} .clear-btn{font-weight:bold;padding:6px 10px}#${config.sync.topBarId} .history-btn:hover,#${config.sync.topBarId} .clear-btn:hover{background:#e9e9eb;color:#5856d5;border-color:#c0c4cc}#${config.sync.topBarId} .history-btn:active,#${config.sync.topBarId} .clear-btn:active{transform:scale(.95);background:#dcdfe6}`;
            combinedCSS += ` #${config.sync.historyPanelId}{position:fixed;top:238px;right:0px;transform:translateY(-50%);z-index:10000;width:125px;max-height:380px;background:rgba(248,249,250,0.95);border:1px solid #e0e0e0;border-radius:6px;box-shadow:0 1px 8px rgba(0,0,0,0.1);padding:8px 0;overflow:hidden;backdrop-filter:blur(3px);-webkit-backdrop-filter:blur(3px);display:flex;flex-direction:column}#${config.sync.historyPanelId} .search-history-list{list-style:none;padding:0;margin:0;overflow-y:auto;flex-grow:1;scrollbar-width:thin;scrollbar-color:#ccc #f0f0f0}#${config.sync.historyPanelId} .search-history-list::-webkit-scrollbar{width:6px}#${config.sync.historyPanelId} .search-history-list::-webkit-scrollbar-track{background:#f0f0f0;border-radius:3px}#${config.sync.historyPanelId} .search-history-list::-webkit-scrollbar-thumb{background-color:#ccc;border-radius:3px}#${config.sync.historyPanelId} .search-history-item{padding:6px 12px;font-size:13px;color:#555;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;border-bottom:1px solid #eee;transition:background-color 0.1s ease-in-out,color 0.1s ease-in-out}#${config.sync.historyPanelId} .search-history-item:last-child{border-bottom:none}#${config.sync.historyPanelId} .search-history-item:hover{background-color:#e9e9eb;color:#5856d5}#${config.sync.historyPanelId} .search-history-item:active{background-color:#dcdfe6}#${config.sync.historyPanelId} .search-history-item--active{background-color:${config.sync.activeItemBgColor} !important;color:#000 !important;font-weight:500;}`;
            // 筛选按钮 CSS (使用类名)
            combinedCSS += ` .mu-select-popup-btn { background-color:#5856d5;color:white;border:none;border-radius:5px;padding:4px 8px;font-size:14px;line-height:1;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.3);white-space:nowrap;user-select:none;-webkit-user-select:none; transition: opacity 0.1s ease-in-out, visibility 0.1s ease-in-out, background-color 0.1s ease-in-out; } .mu-select-popup-btn:hover { background-color:#4a48b3; }`;
            // 复制标签 CSS (保持不变)
            combinedCSS += ` #${config.copyTag.popupId}{position:absolute;top:0;left:0;z-index:10010;background-color:#f0f2f5;color:#5856d5;border:1px solid #dcdfe6;border-radius:4px;padding:2px 5px;font-size:12px;line-height:1;cursor:pointer;box-shadow:0 1px 3px rgba(0,0,0,0.15);white-space:nowrap;user-select:none;-webkit-user-select:none;pointer-events:auto; transition: opacity 0.1s ease-in-out, visibility 0.1s ease-in-out;}#${config.copyTag.popupId}:hover{background-color:#e4e7ed;border-color:#c0c4cc}#${config.copyTag.feedbackId}{position:absolute;top:0;left:0;z-index:10011;background-color:#5856d5;color:white;border:1px solid #5856d5;border-radius:4px;padding:2px 5px;font-size:12px;line-height:1;cursor:default;box-shadow:0 1px 3px rgba(0,0,0,0.15);white-space:nowrap;user-select:none;-webkit-user-select:none;pointer-events:none; transition: opacity 0.1s ease-in-out, visibility 0.1s ease-in-out;}`;
            // TP (复制/剪切/粘贴) 按钮 CSS (使用类名)
            const tpCss = config.transferPaste; const tpPref = tpCss.cssPrefix;
            combinedCSS += ` .${tpPref}${tpCss.btnBaseClass} { color:white;border:none;border-radius:5px;padding:4px 8px;font-size:14px;line-height:1;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,0.3);white-space:nowrap;user-select:none;-webkit-user-select:none; transition: opacity 0.1s ease-in-out, visibility 0.1s ease-in-out, background-color 0.1s ease-in-out; }`; // 通用基础样式
            combinedCSS += ` .${tpPref}${tpCss.btnCopyClass} { background-color:#5856d5; } .${tpPref}${tpCss.btnCopyClass}:hover { background-color:#4a48b3; }`; // 复制按钮
            combinedCSS += ` .${tpPref}${tpCss.btnCutClass} { background-color:#d55856; } .${tpPref}${tpCss.btnCutClass}:hover { background-color:#b34a48; }`; // 剪切按钮
            combinedCSS += ` .${tpPref}${tpCss.btnPasteClass} { background-color:#5856d5; } .${tpPref}${tpCss.btnPasteClass}:hover { background-color:#4a48b3; }`; // 粘贴按钮
            // 开关面板 CSS (保持不变)
            const panelConf = config.togglePanel; combinedCSS += ` #${panelConf.triggerId}{position:fixed;bottom:0;right:0;width:${panelConf.triggerWidth}px;height:${panelConf.triggerHeight}px;background:rgba(0,0,0,0.01);cursor:pointer;z-index:19998;border-top-left-radius:5px;} #${panelConf.panelId}{position:fixed;bottom:0;right:0;width:${panelConf.panelWidth}px;max-height:80vh;overflow-y:auto;background:rgba(250,250,250,0.98);border:1px solid #ccc;border-top-left-radius:8px;box-shadow:-2px -2px 10px rgba(0,0,0,0.15);padding:10px;z-index:19999;transform:translateX(100%);transition:transform 0.3s ease-in-out;font-size:14px;color:#333;box-sizing:border-box;scrollbar-width:thin;scrollbar-color:#bbb #eee;} #${panelConf.panelId}::-webkit-scrollbar{width:6px;} #${panelConf.panelId}::-webkit-scrollbar-track{background:#eee;border-radius:3px;} #${panelConf.panelId}::-webkit-scrollbar-thumb{background-color:#bbb;border-radius:3px;} #${panelConf.triggerId}:hover + #${panelConf.panelId}, #${panelConf.panelId}:hover{transform:translateX(0);} #${panelConf.panelId} .toggle-panel-title{font-weight:bold;margin-bottom:10px;padding-bottom:5px;border-bottom:1px solid #eee;text-align:center;} #${panelConf.panelId} .toggle-control{display:flex;align-items:center;margin-bottom:8px;cursor:pointer;} #${panelConf.panelId} .toggle-control input[type="checkbox"]{margin-right:8px;cursor:pointer;appearance:none;-webkit-appearance:none;width:36px;height:20px;background-color:#ccc;border-radius:10px;position:relative;transition:background-color 0.2s ease-in-out;flex-shrink:0;} #${panelConf.panelId} .toggle-control input[type="checkbox"]::before{content:'';position:absolute;width:16px;height:16px;border-radius:50%;background-color:white;top:2px;left:2px;transition:left 0.2s ease-in-out;box-shadow:0 1px 2px rgba(0,0,0,0.2);} #${panelConf.panelId} .toggle-control input[type="checkbox"]:checked{background-color:#5856d5;} #${panelConf.panelId} .toggle-control input[type="checkbox"]:checked::before{left:18px;} #${panelConf.panelId} .toggle-control label{flex-grow:1;user-select:none;cursor:pointer;}`;

            if (combinedCSS) { try { GM_addStyle(combinedCSS); } catch (e) { console.warn('[Mubu] Inject CSS err:', e); } }

            // --- 初始化核心 UI 元素和状态 ---
            createControlPanel(); // 同步搜索框
            createHistoryPanel(); // 历史面板
            // createSelectPopup(); // 筛选按钮的创建推迟到需要时或开关打开时

            // --- 创建开关面板 ---
            createTogglePanel();

            // --- 初始化 TP (寻找编辑容器,但不立即附加监听器) ---
            tp_initialize(); // 这个函数现在只找容器,监听器由 applyFeatureStateChange 控制

            // --- 初始化输入框查找和基础监听 ---
            const needsInputLogic = isFeatureEnabled('syncSearchBox') || isFeatureEnabled('historyPanel') || isFeatureEnabled('selectSearchPopup');
            if (needsInputLogic) {
                 const initialInput = optimizedFindSearchBox();
                 if (initialInput) { originalInput = initialInput; setupInputListeners(originalInput); lastSyncedValue = initialInput.value; if (isHistoryTrackingNeeded()) { historyManager.add(lastSyncedValue); } }
                 else { console.warn("[Mubu] Initial input not found."); }
            }

            // --- 启动 DOM 观察器 (用于同步搜索框) ---
            const needsObserver = isFeatureEnabled('syncSearchBox') || (isFeatureEnabled('historyPanel') && !isFeatureEnabled('syncSearchBox') && isHistoryTrackingNeeded());
            if (needsObserver) {
                 const target = document.querySelector(config.selectors.domObserverTarget) || document.body;
                 if (target) { domObserver = new MutationObserver((mutations) => { if (!isFeatureEnabled('syncSearchBox') && !isFeatureEnabled('historyPanel')) return; let relevant = mutations.some(m => { if (m.type === 'childList') return true; if (m.type === 'attributes' && m.target === originalInput && m.attributeName === 'disabled') return true; if (m.type === 'childList' && m.removedNodes.length > 0) { for (const node of m.removedNodes) { if (node === originalInput || (node instanceof Element && node.contains?.(originalInput))) { return true; } } } return false; }); if(relevant) findAndSetupDebounced(); }); domObserver.observe(target, { childList: true, subtree: true, attributes: true, attributeFilter: ['disabled'] }); }
                 else { console.error(`[Mubu] Observer target "${config.selectors.domObserverTarget}" not found!`); }
            }

            // --- 应用初始功能状态 (会触发监听器的附加) ---
            console.log('[Mubu Helper] Applying initial feature states...');
            let initialEnabledCount = 0;
            for (const key in runtimeFeatureState) {
                if (runtimeFeatureState[key]) {
                    console.log(`[Mubu Helper] Initializing enabled feature: ${key}`);
                    try { applyFeatureStateChange(key, true); initialEnabledCount++; }
                    catch (applyError) { console.error(`[Mubu Helper] Error initializing ${key}:`, applyError); }
                } else {
                    // console.log(`[Mubu Helper] Feature ${key} is initially disabled.`);
                }
            }
            featuresEnabledCount = initialEnabledCount;
            console.log('[Mubu Helper] Initial feature states applied.');

            // --- 初始化完成 ---
            if (featuresEnabledCount > 0 || togglePanelElement) { window.addEventListener('unload', cleanup); console.log(`[Mubu Combined Helper v3.6.0] Init complete (${featuresEnabledCount} features initially active, panel loaded)`); }
            else { window.addEventListener('unload', cleanup); console.log('[Mubu Combined Helper v3.6.0] Init complete (No features initially active, panel loaded)'); }
        } catch (initError) { console.error('[Mubu Combined Helper v3.6.0] FATAL INIT ERROR:', initError); }
    }

    // 清理函数 (更新)
    function cleanup() {
        console.log('[Mubu Combined Helper v3.6.0] Cleaning up...');
        window.removeEventListener('unload', cleanup);
        try {
            domObserver?.disconnect();
            observerInstance.disconnect();
            elementObserverMap.clear();
            domObserver = null;

            // 移除输入框监听器
            teardownInputListeners(originalInput);

            // 移除同步搜索框控件监听器
            if (customInputListenerAttached) { try { topBarControls.prevBtn?.removeEventListener('click', historyNavPrevListener); topBarControls.nextBtn?.removeEventListener('click', historyNavNextListener); topBarControls.clearBtn?.removeEventListener('click', clearBtnListener); topBarControls.input?.removeEventListener('input', customInputListener); } catch(e){} customInputListenerAttached = false; }
            // 移除历史列表监听器
            if (historyListClickListenerAttached) { try { historyListElement?.removeEventListener('click', handleHistoryListClick); } catch(e){} historyListClickListenerAttached = false; }
             // 移除全局 mousedown/mouseup 监听器
             if (selectPopupListenersAttached) {
                 try {
                     document.removeEventListener('mousedown', handleMouseDownPopup, true);
                     document.removeEventListener('mouseup', handleMouseUpSelectionEnd, true);
                     document.removeEventListener('mouseup', tp_handleMouseUp, true); // 移除 TP mouseup
                 } catch(e){}
                 selectPopupListenersAttached = false;
             }
             // 移除悬停复制标签监听器
            if (copyTagListenerAttached && ct_listenerTarget) { try { ct_listenerTarget.removeEventListener('mouseover', ct_handleMouseOver); } catch(e){} copyTagListenerAttached = false; ct_listenerTarget = null; }
            // 移除 TP 的 keydown/keyup 监听器
            tp_detachListeners(); // 会将 tp_listenersAttached 设为 false

            // 清除定时器
            clearTimeout(togglePanelHideTimeout);
            clearTimeout(ct_showTimeout); clearTimeout(ct_hideTimeout); clearTimeout(ct_feedbackTimeout);

            // 移除开关面板监听器
            toggleTriggerElement?.removeEventListener('mouseenter', showTogglePanel); toggleTriggerElement?.removeEventListener('mouseleave', scheduleHideTogglePanel);
            togglePanelElement?.removeEventListener('mouseenter', showTogglePanel); togglePanelElement?.removeEventListener('mouseleave', scheduleHideTogglePanel);

            // 移除动态创建的元素
            try { toggleTriggerElement?.remove(); } catch(e){}
            try { togglePanelElement?.remove(); } catch(e){}
            hideSelectionActionButtons(); // 隐藏并准备移除按钮组
            tp_hidePasteButton(); // 隐藏并准备移除粘贴按钮
             // 延迟一点移除,确保隐藏动画完成(可选)
             setTimeout(() => {
                 try { popupElement?.remove(); } catch(e){}
                 try { tp_triggerButtonRef.element?.remove(); } catch(e){}
                 try { tp_cutButtonRef.element?.remove(); } catch(e){}
                 try { tp_pasteButtonRef.element?.remove(); } catch(e){}
                 try { ct_copyPopupElement?.remove(); } catch(e){}
                 try { ct_feedbackElement?.remove(); } catch(e){}
             }, 200);

            // 移除静态 UI 元素
            try { topBarControls.container?.remove(); } catch(e){}
            try { historyPanel?.remove(); } catch(e){}

            // Reset states... (保持不变)
            isRafScheduled=false; styleUpdateQueue=[]; originalInput=null; lastSyncedValue=null; isSyncing=false; customInput=null; originalInputSyncHandler=null; originalInputHistoryHandler=null; topBarControls={}; historyPanel=null; historyListElement=null; activeHistoryItemElement=null;
            popupElement=null; currentSelectedText=''; // 重置筛选按钮状态
            ct_copyPopupElement=null; ct_feedbackElement=null; ct_currentHoveredTag=null; ct_currentTagText=''; ct_showTimeout=null; ct_hideTimeout=null; ct_feedbackTimeout=null; ct_listenerTarget=null;
            tp_editorContainer=null;
            tp_triggerButtonRef.element=null; tp_cutButtonRef.element=null; tp_pasteButtonRef.element=null; // 重置按钮 Ref
            tp_storedHTML=''; tp_storedText=''; tp_ctrlApressed=false; // tp_listenersAttached 由 detach 处理
            togglePanelElement=null; toggleTriggerElement=null; togglePanelHideTimeout=null;
            // 重置监听器附加标志
            // customInputListenerAttached, historyListClickListenerAttached, selectPopupListenersAttached, copyTagListenerAttached 已在上面处理

        } catch (cleanupError) { console.warn('[Mubu] Cleanup error:', cleanupError); }
         console.log('[Mubu Combined Helper v3.6.0] Cleanup complete.');
    }

    // --- [ ☆ 脚本入口 ☆ ] --- (保持不变)
    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        setTimeout(init, config.initDelay);
    } else {
        window.addEventListener('DOMContentLoaded', () => setTimeout(init, config.initDelay), { once: true });
    }

})();