MubuPlus v4.1 Manual

v4

// ==UserScript==
// @name         MubuPlus v4.1 Manual 
// @namespace    http://tampermonkey.net/
// @version      4.1 // Added history item deletion
// @author       Yeeel (Enhanced by Assistant based on request)
// @match        *://mubu.com/*
// @match        *://*.mubu.com/*
// @grant        GM_addStyle
// @grant        unsafeWindow // Needed to access window.mysearch
// @run-at       document-idle
// @icon         https://mubu.com/favicon.ico
// @license      MIT
// @description v4
// ==/UserScript==

(function () {
    'use strict';

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

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

    // --- [ ☆ 配置项 ☆ ] ---
    const config = {
        cacheTTL: 3000,                 // 元素缓存时间 (ms)
        initDelay: 2500,                // 脚本初始化延迟 (ms)
        interfaceCheckDelay: 3500,      // 幕布接口首次检查延迟 (ms)
        interfaceCheckInterval: 5000,   // 幕布接口检查重试间隔 (ms)
        interfaceCheckMaxAttempts: 5,   // 幕布接口检查最大尝试次数
        selectors: {
            originalInput: 'input[placeholder="搜索关键词"]:not([disabled])',
            domObserverTarget: 'div.search-wrap',       // 监听DOM变化的区域
            tagElement: 'span.tag',                     // 标签元素
            tagClickArea: 'div.outliner-page',          // 标签可点击的区域
            copyTagParentContainer: 'div.outliner-page',// 悬停复制标签的父容器
        },
        sync: {
            historySize: 30,                    // 历史记录条数
            mutationDebounce: 5,                // DOM变化检测防抖 (ms)
            throttleTime: 10,                   // 历史导航节流 (ms)
            activeItemBgColor: '#e9e8f9',       // 历史记录当前项背景色
            persistHighlightBgColor: '#ffe8cc', // 历史记录固定高亮背景色
            topBarId: 'custom-search-sync-container-v35',
            historyPanelId: 'search-history-panel-v35',
            historyListId: 'search-history-list-v35',
            simulatedClickRecoveryDelay: 1,     // 模拟标签点击后恢复状态延迟 (ms)
            instantSearchDelay: 1,              // 自定义输入框触发搜索延迟 (ms)
            historyItemDeleteBtnClass: 'search-history-delete-btn', // 历史记录删除按钮类名
        },
        select: {
            popupId: 'mubu-select-search-popup-v35',
            popupText: '🔍',                    // 选中筛选按钮文字
            popupAboveGap: 5,                   // 按钮距离选区上方的距离 (px)
            fallbackWidth: 35,                  // 按钮后备宽度 (px)
            fallbackHeight: 22,                 // 按钮后备高度 (px)
            popupAppearDelay: 50,               // 按钮出现延迟 (ms)
        },
        copyTag: {
            popupId: 'mubu-copy-tag-popup-hover-v35',
            feedbackId: 'mubu-copy-tag-feedback-v35',
            copyIcon: '📋',                     // 复制按钮图标
            copiedText: '✅ 已复制',             // 复制成功提示文字
            popupMarginBottom: 0,               // 复制按钮距离标签下方距离 (px)
            hoverDelay: 10,                     // 悬停显示复制按钮延迟 (ms)
            hideDelay: 50,                      // 鼠标移开后隐藏按钮延迟 (ms)
            copiedMessageDuration: 500,         // 复制成功提示显示时长 (ms)
            tagSelector: 'span.tag',            // 标签选择器
            popupFallbackWidth: 25,             // 复制按钮后备宽度
            popupFallbackHeight: 18,            // 复制按钮后备高度
            feedbackFallbackWidth: 60,          // 提示信息后备宽度
            feedbackFallbackHeight: 18,         // 提示信息后备高度
        },
        transferPaste: {
            editorContainerSelector: '#js-outliner', // 监听键盘事件的编辑器容器
            triggerButtonId: 'mu-transfer-copy-button-v35',
            cutButtonId: 'mu-transfer-cut-button-v35',
            pasteButtonId: 'mu-transfer-paste-button-v35',
            triggerButtonText: '📄',            // 复制按钮文字
            cutButtonText: '✂️',                 // 剪切按钮文字
            pasteButtonText: '📝',              // 粘贴按钮文字
            buttonHorizontalGap: 2,             // 按钮间水平间距 (px)
            cssPrefix: 'mu-transfer-paste-v35-',// CSS类名前缀
            btnBaseClass: 'btn-base',
            btnCopyClass: 'btn-copy',
            btnCutClass: 'btn-cut',
            btnPasteClass: 'btn-paste',
            buttonBaseStyleInline: {
                position: 'absolute', zIndex: '29998', top: '0', left: '0',
                opacity: '0', display: 'none', visibility: 'hidden',
            },
            initWaitMaxRetries: 15,             // 编辑器容器查找重试次数
            initWaitRetryInterval: 700,         // 编辑器容器查找重试间隔 (ms)
            buttonFallbackWidth: 35,            // 按钮后备宽度
            buttonFallbackHeight: 22,           // 按钮后备高度
            buttonsAppearDelay: 50,             // 粘贴按钮出现延迟 (ms)
        },
        togglePanel: {
            panelId: 'mubu-helper-toggle-panel-v35',
            triggerId: 'mubu-helper-toggle-trigger-v35',
            panelWidth: 160,                    // 开关面板宽度 (px)
            triggerWidth: 20,                   // 触发区域宽度 (px)
            triggerHeight: 230,                 // 触发区域高度 (px)
            hideDelay: 100,                     // 鼠标移开后隐藏面板延迟 (ms)
        },
        pushContent: {
            pushMarginLeft: 75,                 // 推开内容距离 (px)
            contentSelector: '#js-outliner',    // 主内容区域选择器
            pushClass: 'mu-content-pushed-v37', // 推开时添加的类名
            transitionDuration: '0.1s'          // 推开动画时长
        }
    };
    const BUTTON_GAP = config.transferPaste.buttonHorizontalGap;

    // --- [ ☆ rAF 样式批量处理 ☆ ] ---
    // 使用 requestAnimationFrame 批量更新元素样式,提高性能
    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) { /* silenced */ }
            }
        });
        isRafScheduled = false;
    }

    function scheduleStyleUpdate(element, styles) {
        if (!element) return;
        styleUpdateQueue.push({ element, styles });
        if (!isRafScheduled) {
            isRafScheduled = true;
            requestAnimationFrame(processStyleUpdates);
        }
    }

    // --- [ ☆ ResizeObserver 尺寸缓存 ☆ ] ---
    // 使用 ResizeObserver 监听元素尺寸变化并缓存,避免频繁读取 offsetWidth/offsetHeight
    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;
            }
            // Fallback to offsetWidth/offsetHeight if ResizeObserver doesn't provide valid dimensions
            if (width <= 0 || height <= 0) {
                 if (entry.target.offsetWidth > 0 && entry.target.offsetHeight > 0) {
                    width = entry.target.offsetWidth;
                    height = entry.target.offsetHeight;
                 }
            }
            if (width > 0 && height > 0) {
                elementDimensionsCache.set(entry.target, { width, height });
            }
        }
    };
    const observerInstance = new ResizeObserver(resizeObserverCallback);

    function observeElementResize(element) {
        if (!element || elementObserverMap.has(element)) return;
        try {
            observerInstance.observe(element);
            elementObserverMap.set(element, observerInstance);
        } catch (e) { /* silenced */ }
    }

    function unobserveElementResize(element) {
        if (!element || !elementObserverMap.has(element)) return;
        try {
            observerInstance.unobserve(element);
            elementObserverMap.delete(element);
        } catch (e) { /* silenced */ }
    }

    // --- [ ☆ 内部状态与工具函数 ☆ ] ---

    // State Variables
    let originalInput = null, lastSyncedValue = null, isSyncing = false, domObserver = null, customInput = null;
    let originalInputHistoryHandler = null; // Listener for original input's 'input' event
    let topBarControls = { container: null, input: null, prevBtn: null, nextBtn: null, clearBtn: null };
    let historyPanel = null, historyListElement = null, activeHistoryItemElement = null;
    let isSimulatingClick = false; // Flag to prevent history updates during tag click simulation
    let persistHighlightedTerm = null, persistHighlightedIndex = null; // For double-click persistent highlight
    let isInterfaceAvailable = false; let interfaceCheckAttempts = 0; let interfaceCheckTimer = null; // For Mubu API check
    let customInputSearchTimeoutId = null; // Timeout ID for custom input debounce
    let isProgrammaticValueChange = false; // Flag to distinguish user input vs script changes

    // Other Feature State
    let popupElement = null, currentSelectedText = ''; // Select Search Popup
    let ct_copyPopupElement = null, ct_feedbackElement = null, ct_currentHoveredTag = null, ct_currentTagText = ''; // Copy Tag
    let ct_showTimeout = null, ct_hideTimeout = null, ct_feedbackTimeout = null, ct_listenerTarget = null; // Copy Tag timeouts & target
    let tp_editorContainer = null, tp_storedHTML = '', tp_storedText = '', tp_ctrlApressed = false, tp_listenersAttached = false; // Transfer Paste
    let togglePanelElement = null, toggleTriggerElement = null, togglePanelHideTimeout = null; // Feature Toggle Panel
    const tp_triggerButtonRef = { element: null }; // Transfer Paste button refs
    const tp_cutButtonRef = { element: null };
    const tp_pasteButtonRef = { element: null };

    // Shared Utilities
    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 } })(); // Cached search box finder
    const docBody = document.body;
    const isHistoryTrackingNeeded = () => isFeatureEnabled('syncSearchBox') || isFeatureEnabled('historyPanel'); // Helper to check if history logic is needed

    // --- [ ☆ 历史记录管理器 ☆ ] ---
    const historyManager = (() => {
        const history = new Array(config.sync.historySize);
        let writeIndex = 0; // Index to write the next item
        let count = 0;      // Current number of items in history
        let navIndex = -1;  // Index for keyboard navigation (-1 means not navigating)
        const selectedIndices = new Set(); // Stores LOGICAL indices of multi-selected items

        /** Adds a term to the history buffer. */
        const add = (value, source = 'unknown') => {
            if (!isHistoryTrackingNeeded()) return false;
            const term = String(value).trim();
            if (!term) return false;

            // Avoid adding duplicates if it's the same as the very last *added* item
            const lastAddedIdx = (writeIndex - 1 + config.sync.historySize) % config.sync.historySize;
            if (count > 0 && history[lastAddedIdx] === term) {
                navIndex = -1; // Still reset nav index if user types the same thing
                return false; // History list itself didn't change
            }

            // Adjust persistent highlight and multi-selection if buffer is full and oldest item is removed
            if (count === config.sync.historySize) {
                const oldestLogicalIndex = 0;
                if (persistHighlightedIndex !== null) {
                    if (persistHighlightedIndex === oldestLogicalIndex) {
                        persistHighlightedTerm = null;
                        persistHighlightedIndex = null;
                    } else {
                        persistHighlightedIndex--; // Shift down
                    }
                }
                if (selectedIndices.has(oldestLogicalIndex)) {
                    selectedIndices.delete(oldestLogicalIndex);
                }
                const newSelectedIndices = new Set();
                selectedIndices.forEach(idx => {
                    if (idx > oldestLogicalIndex) {
                        newSelectedIndices.add(idx - 1); // Shift down
                    }
                });
                selectedIndices.clear();
                newSelectedIndices.forEach(idx => selectedIndices.add(idx));
            }

            // Add new term
            history[writeIndex] = term;
            writeIndex = (writeIndex + 1) % config.sync.historySize;
            count = Math.min(count + 1, config.sync.historySize);
            navIndex = -1; // Reset navigation index on new addition
            // console.log(`History Add (${source}): "${term}", New Count: ${count}, WriteIdx: ${writeIndex}`);
            return true; // History list modified
        };

        /** Gets a term by its logical index (0 = oldest, count-1 = newest). */
        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; };

        /** Deletes an item at the specified logical index. */
        const deleteAt = (logicalIndex) => {
            if (!isHistoryTrackingNeeded() || logicalIndex < 0 || logicalIndex >= count) return false;
            const termToDelete = get(logicalIndex); // Get term before restructuring

            // Create a new array without the deleted item
            const newHistoryArray = [];
            for (let i = 0; i < count; i++) {
                if (i !== logicalIndex) {
                    newHistoryArray.push(get(i));
                }
            }

            // Clear old history and refill
            for (let i = 0; i < config.sync.historySize; i++) history[i] = undefined;
            writeIndex = 0;
            count = 0;
            for (const term of newHistoryArray) {
                history[writeIndex] = term;
                writeIndex = (writeIndex + 1) % config.sync.historySize;
                count++;
            }

            // Adjust persistent highlight
            if (persistHighlightedIndex !== null) {
                if (logicalIndex === persistHighlightedIndex) {
                    persistHighlightedTerm = null;
                    persistHighlightedIndex = null;
                } else if (logicalIndex < persistHighlightedIndex) {
                    persistHighlightedIndex--;
                }
            }

            // Adjust multi-selection indices
            if (selectedIndices.has(logicalIndex)) {
                selectedIndices.delete(logicalIndex);
            }
            const newSelectedIndices = new Set();
            selectedIndices.forEach(idx => {
                if (idx > logicalIndex) {
                    newSelectedIndices.add(idx - 1); // Shift down
                } else if (idx < logicalIndex) {
                    newSelectedIndices.add(idx); // Keep as is
                }
            });
            selectedIndices.clear();
            newSelectedIndices.forEach(idx => selectedIndices.add(idx));

            navIndex = -1; // Reset nav index as indices changed
            // console.log(`History Deleted: Index ${logicalIndex}, Term "${termToDelete}", New Count: ${count}, WriteIdx: ${writeIndex}, PersistIdx: ${persistHighlightedIndex}`);
            return true;
        };

        /** Toggles the multi-selection state for a given logical index. */
        const toggleSelection = (logicalIndex) => {
            if (!isHistoryTrackingNeeded() || logicalIndex < 0 || logicalIndex >= count) return false;
            if (selectedIndices.has(logicalIndex)) {
                selectedIndices.delete(logicalIndex);
            } else {
                selectedIndices.add(logicalIndex);
            }
            // console.log("Selected Indices:", selectedIndices);
            return true;
        };

        /** Clears all multi-selections. */
        const clearSelection = () => {
            if (selectedIndices.size > 0) {
                selectedIndices.clear();
                return true;
            }
            return false;
        };

        /** Returns the set of currently selected logical indices. */
        const getSelectedIndices = () => {
            return selectedIndices;
        };

        /** Deletes all items EXCEPT those whose logical indices are in the selectedIndices set. */
        const deleteNonSelected = () => {
            if (!isHistoryTrackingNeeded() || count === 0) return false; // Nothing to do if empty
            if (selectedIndices.size === count) return false; // Nothing to delete if all selected

            const termsToKeep = [];
            const keptIndicesMap = new Map(); // Map: old index -> new index

            // Iterate current history and decide what to keep
            for (let i = 0; i < count; i++) {
                const logicalIndex = i;
                if (selectedIndices.has(logicalIndex)) {
                    const term = get(logicalIndex);
                    if (term !== null && term !== undefined) {
                        keptIndicesMap.set(logicalIndex, termsToKeep.length); // Store mapping
                        termsToKeep.push(term);
                    }
                }
            }

            // Clear old history and refill with kept items
            for (let i = 0; i < config.sync.historySize; i++) history[i] = undefined;
            writeIndex = 0;
            count = 0;
            for (const term of termsToKeep) {
                history[writeIndex] = term;
                writeIndex = (writeIndex + 1) % config.sync.historySize;
                count++;
            }

            // Adjust persistent highlight if the highlighted item was kept
            if (persistHighlightedIndex !== null && keptIndicesMap.has(persistHighlightedIndex)) {
                persistHighlightedIndex = keptIndicesMap.get(persistHighlightedIndex);
            } else {
                persistHighlightedTerm = null;
                persistHighlightedIndex = null;
            }

            navIndex = -1;
            selectedIndices.clear(); // Clear selection after the operation

            // console.log(`History Deleted Non-Selected. Kept: ${count}, New WriteIdx: ${writeIndex}, New PersistIdx: ${persistHighlightedIndex}`);
            return true; // Deletion happened
        };

        /** Clears the entire history. */
        const clearAll = () => { // Kept for potential internal use, button uses deleteNonSelected
            if (!isHistoryTrackingNeeded()) return false;
            for (let i = 0; i < config.sync.historySize; i++) history[i] = undefined;
            writeIndex = 0; count = 0; navIndex = -1;
            persistHighlightedTerm = null; persistHighlightedIndex = null;
            selectedIndices.clear();
            // console.log("History Cleared All. New Count: 0");
            return true;
        };

        /** Updates the history panel UI. */
        const updatePanel = () => {
            if (!isFeatureEnabled('historyPanel') || !historyPanel || !historyListElement) return;

            const scrollTop = historyListElement.scrollTop; // Preserve scroll position
            historyListElement.innerHTML = '';
            const numItems = historyManager.size();
            const currentNavIndex = historyManager.getCurrentIndex();
            const deleteBtnClass = config.sync.historyItemDeleteBtnClass;
            let newlyActiveElement = null;
            let matchFoundForLastSyncedValue = false;
            const fragment = document.createDocumentFragment();

            for (let i = 0; i < numItems; i++) {
                const logicalIndex = numItems - 1 - i; // Iterate newest (top) to oldest (bottom)
                const term = historyManager.get(logicalIndex);
                if (term === null || term === undefined) continue;

                const li = document.createElement('li');
                li.className = 'search-history-item';
                li.title = term;
                li.dataset.term = term;
                li.dataset.historyIndex = String(logicalIndex);

                const textSpan = document.createElement('span');
                textSpan.className = 'search-history-item-text';
                textSpan.textContent = term;
                li.appendChild(textSpan);

                const deleteBtn = document.createElement('span');
                deleteBtn.className = deleteBtnClass;
                deleteBtn.textContent = '✕';
                deleteBtn.title = '删除此条记录';
                deleteBtn.dataset.deleteIndex = String(logicalIndex);
                li.appendChild(deleteBtn);

                // Apply highlighting classes
                if (selectedIndices.has(logicalIndex)) {
                    li.classList.add('search-history-item--multi-selected');
                }
                if (logicalIndex === currentNavIndex) {
                    li.classList.add('search-history-item--active');
                    newlyActiveElement = li;
                    matchFoundForLastSyncedValue = true; // Nav index takes precedence
                } else if (currentNavIndex === -1 && !matchFoundForLastSyncedValue && term && lastSyncedValue && term === lastSyncedValue && !selectedIndices.has(logicalIndex)) {
                    // Highlight if it matches current input, not navigating, and not multi-selected
                    li.classList.add('search-history-item--active');
                    matchFoundForLastSyncedValue = true;
                }
                if (persistHighlightedIndex !== null && logicalIndex === persistHighlightedIndex && term === persistHighlightedTerm) {
                    li.classList.add('search-history-item--persist-highlight');
                }

                fragment.appendChild(li);
            }

            historyListElement.appendChild(fragment);
            try { historyListElement.scrollTop = scrollTop; } catch (e) { /* ignore */ }
            activeHistoryItemElement = newlyActiveElement;

            // Update "Selective Clear" button state and text
            const clearSelectiveButton = document.getElementById('history-clear-all-btn');
            if (clearSelectiveButton) {
                clearSelectiveButton.disabled = (numItems === 0);
                const currentSelectedCount = selectedIndices.size;
                if (currentSelectedCount > 0 && currentSelectedCount < count) { // Only show count if some but not all are selected
                    clearSelectiveButton.textContent = `删除未选 (${count - currentSelectedCount})`;
                    clearSelectiveButton.title = `删除未选中的 (${count - currentSelectedCount}) 条记录,保留选中的 ${currentSelectedCount} 条`;
                } else {
                    clearSelectiveButton.textContent = "删除未选记录";
                    clearSelectiveButton.title = "清空所有历史记录 (未选中任何条目)";
                }
            }
        };

        return {
            add, get, size, getCurrentIndex, setCurrentIndex, resetIndexToCurrent, updatePanel,
            deleteAt, clearAll, toggleSelection, clearSelection, getSelectedIndices, deleteNonSelected
        };
    })();

    // --- [ ☆ Tag Click Simulation Logic ☆ ] ---
    /** Finds a visible tag by name and simulates a click on it. */
    const findAndClickTag = (tagName) => {
        if (!tagName || !tagName.startsWith('#')) return false;
        const searchArea = document.querySelector(config.selectors.tagClickArea);
        if (!searchArea) return false;

        const tags = searchArea.querySelectorAll(config.selectors.tagElement);
        if (!tags || tags.length === 0) return false;

        let foundElement = null;
        const trimmedTagName = tagName.trim();
        for (const tagElement of tags) {
            if (tagElement.textContent.trim() === trimmedTagName) {
                let isVisible = false;
                try { // Check visibility robustly
                    const rect = tagElement.getBoundingClientRect();
                    isVisible = rect && rect.width > 0 && rect.height > 0 && tagElement.offsetParent !== null;
                } catch (visError) { isVisible = false; }

                if (isVisible) {
                    foundElement = tagElement;
                    break;
                }
            }
        }

        if (foundElement) {
            isSimulatingClick = true; // Set flag before click
            try {
                foundElement.click();
                return true;
            } catch (e) {
                return false;
            } finally {
                // Reset flag slightly after the click event finishes processing
                setTimeout(() => { isSimulatingClick = false; }, config.sync.simulatedClickRecoveryDelay);
            }
        } else {
            return false;
        }
    };

    // --- [ ☆ Instant Search Interface Logic ☆ ] ---
    /** Triggers Mubu's internal search API if available. */
    function triggerInstantSearch(searchTerm) {
        if (!isInterfaceAvailable) {
            if (customInput) customInput.placeholder = '控制台输入: window.mysearch = t';
            return false;
        }
        if (typeof unsafeWindow === 'undefined' || typeof unsafeWindow.mysearch?.getService !== 'function') {
            if (customInput) customInput.placeholder = '接口结构错误,请刷新重试';
            return false;
        }

        try {
            const searchService = unsafeWindow.mysearch.getService("Search");
            if (!searchService) return false;

            const termToSearch = String(searchTerm).trim();
            if (termToSearch === '') {
                if (typeof searchService.clear === 'function') {
                    searchService.clear(); return true;
                }
            } else {
                if (typeof searchService.search === 'function') {
                    searchService.search(termToSearch); return true;
                }
            }
            return false; // Method not found
        } catch (error) {
            return false;
        }
    }

    /** Periodically checks if Mubu's search interface (window.mysearch) is available. */
    function checkMubuInterface() {
        clearTimeout(interfaceCheckTimer);
        let found = false;
        if (typeof unsafeWindow !== 'undefined' && typeof unsafeWindow.mysearch?.getService === 'function') {
            try {
                const searchService = unsafeWindow.mysearch.getService("Search");
                // Check if *either* search or clear exists, as sometimes only one might be needed initially
                if (searchService && (typeof searchService.search === 'function' || typeof searchService.clear === 'function')) {
                    found = true;
                }
            } catch (e) { /* Ignore errors during check */ }
        }

        isInterfaceAvailable = found;

        if (customInput) {
            customInput.disabled = !isInterfaceAvailable;
            customInput.placeholder = isInterfaceAvailable ? '筛选 (接口可用)' : '控制台输入: window.mysearch = t';
        }

        if (!isInterfaceAvailable) {
            interfaceCheckAttempts++;
            if (interfaceCheckAttempts < config.interfaceCheckMaxAttempts) {
                interfaceCheckTimer = setTimeout(checkMubuInterface, config.interfaceCheckInterval);
            }
        }
    }

    // --- [ ☆ 同步与历史记录核心逻辑 ☆ ] ---

    /**
     * Central function to update the custom input, update internal state (lastSyncedValue),
     * add a term to history, reset navigation, and update the history panel UI.
     * @param {string} newValue - The new search term.
     * @param {string} [source='unknown'] - Identifier for debugging (e.g., 'native_input', 'custom_input').
     */
    const updateCustomInputAndAddHistory = (newValue, source = 'unknown') => {
        // 1. Update custom input UI (if feature enabled and value differs)
        if (isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== newValue) {
            customInput.value = newValue;
        }

        // 2. Update internal state
        const oldValue = lastSyncedValue;
        const valueChanged = newValue !== oldValue;
        lastSyncedValue = newValue;

        let historyChanged = false;
        if (isHistoryTrackingNeeded()) {
            // 3. Add to history (returns true if list modified)
            historyChanged = historyManager.add(newValue, source);

            // 4. Reset navigation index if history was added OR the value changed
            // (Prevents resetting if user types the same thing as the last history entry)
            if (historyChanged || valueChanged) {
                historyManager.resetIndexToCurrent();
            }
        }

        // 5. Update history panel UI if feature enabled AND (history list changed OR displayed value changed)
        if (isFeatureEnabled('historyPanel') && (historyChanged || valueChanged)) {
            historyManager.updatePanel();
        }
        // console.log(`Update Func (${source}): New: "${newValue}", Old: "${oldValue}", ValChg: ${valueChanged}, HistChg: ${historyChanged}, NavIdx: ${historyManager.getCurrentIndex()}`);
    };

    /** Debounced function to find the original input and set it up after DOM changes. */
    const findAndSetupDebounced = debounce(() => {
        if (!isHistoryTrackingNeeded()) return; // Only run if needed

        const foundInput = optimizedFindSearchBox();

        if (foundInput) {
            const currentValue = foundInput.value;

            if (foundInput !== originalInput) {
                // Input element instance has changed (e.g., page navigation, re-render)
                teardownInputListeners(originalInput); // Clean up old listeners
                originalInput = foundInput;
                lastSyncedValue = currentValue; // Initialize state for the new input

                // Sync custom input if enabled
                if (isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== currentValue) {
                    customInput.value = currentValue;
                }
                setupInputListeners(originalInput); // Attach listeners to the new input
                historyManager.resetIndexToCurrent(); // Reset nav index

                // Add initial value of the new input to history if needed
                if (currentValue && isHistoryTrackingNeeded()) {
                    // Source indicates finding a new input element
                    updateCustomInputAndAddHistory(currentValue, 'observer_new_input');
                } else if (isFeatureEnabled('historyPanel')) {
                    // Update panel even if value is empty (e.g., to clear active state)
                    historyManager.updatePanel();
                }
            } else if (currentValue !== lastSyncedValue && !isSyncing && !isSimulatingClick && !isProgrammaticValueChange) {
                // Input element is the same, value changed externally (e.g., tag click finished, manual edit)
                // Source indicates external change detected by observer
                updateCustomInputAndAddHistory(currentValue, 'observer_external_change');
            }
            // Else: Input is the same, value hasn't changed relevantly, or change was programmatic/syncing - do nothing

        } else if (originalInput) { // No input found, but we previously had one
            // Input element disappeared
            teardownInputListeners(originalInput);
            originalInput = null;
            lastSyncedValue = null;
            if (isFeatureEnabled('syncSearchBox')) isSyncing = false;
            if (isFeatureEnabled('historyPanel')) historyManager.updatePanel(); // Update panel (likely to empty/inactive state)
        }
    }, config.sync.mutationDebounce);


    /** Programmatically sets the value of the original input element and dispatches an input event. */
    function setOriginalInputValue(value) {
        if (!originalInput || !nativeInputValueSetter) return;
        const valueToSet = String(value);
        if (originalInput.value === valueToSet) return; // Avoid unnecessary event dispatch

        isProgrammaticValueChange = true; // Set flag before change
        try {
            nativeInputValueSetter.call(originalInput, valueToSet);
            originalInput.dispatchEvent(inputEvent);
        } finally {
            // Reset the flag after the current execution context finishes
            queueMicrotask(() => { isProgrammaticValueChange = false; });
        }
    }

    // --- [ ☆ 事件处理函数 ☆ ] ---

    /** Listener for the 'input' event on the ORIGINAL Mubu search box. Adds to history ONLY if user-initiated. */
    function handleOriginalInputForHistory(event) {
        // Ignore if: event not trusted, history disabled, syncing in progress, simulating click, or change was programmatic
        if (!event.isTrusted || !isHistoryTrackingNeeded() || isSyncing || isSimulatingClick || isProgrammaticValueChange) {
            // console.log(`Original Input ignored: trusted=${event.isTrusted}, hist=${isHistoryTrackingNeeded()}, sync=${isSyncing}, simClk=${isSimulatingClick}, progChg=${isProgrammaticValueChange}`);
            return;
        }
        const val = event.target.value;
        if (val === lastSyncedValue) return; // No change needed if value is already what we expect

        // console.log(`Original Input History Triggered: "${val}"`);
        // Source 'native_input' indicates user typing directly or a tag click finishing
        updateCustomInputAndAddHistory(val, 'native_input');
    }

    /** Listener for the 'input' event on the CUSTOM top bar search box. Triggers instant search and updates history. */
    function handleCustomInputChange() {
        if (!isFeatureEnabled('syncSearchBox') || !customInput) return;

        const currentSearchTerm = customInput.value;
        if (customInputSearchTimeoutId) clearTimeout(customInputSearchTimeoutId);

        customInputSearchTimeoutId = setTimeout(() => {
            // Verify the value hasn't changed again during the delay
            if (customInput.value === currentSearchTerm) {
                const termToProcess = currentSearchTerm.trim();
                // console.log(`Custom Input Change Handled: "${termToProcess}"`);

                // 1. Trigger the fast search API
                triggerInstantSearch(termToProcess);

                // 2. Update history and internal state (source 'custom_input')
                updateCustomInputAndAddHistory(termToProcess, 'custom_input');

                // 3. Sync the value back to the original input (programmatically)
                setOriginalInputValue(termToProcess); // This sets isProgrammaticValueChange flag internally
            }
            customInputSearchTimeoutId = null;
        }, config.sync.instantSearchDelay);
    }

    /** Listener for clicks on the history list (items and delete buttons). Handles search, multi-select, and deletion. */
    function handleHistoryListClick(event) {
        if (!isFeatureEnabled('historyPanel')) return;
        const item = event.target.closest('.search-history-item');
        if (!item) return; // Clicked outside any item

        // 1. Check for Delete Button Click
        const deleteButton = event.target.closest(`.${config.sync.historyItemDeleteBtnClass}`);
        if (deleteButton) {
            event.stopPropagation(); // Prevent item click behavior
            event.preventDefault();  // Prevent default span behavior
            const indexStr = deleteButton.dataset.deleteIndex;
            if (indexStr !== undefined) {
                const indexToDelete = parseInt(indexStr, 10);
                if (!isNaN(indexToDelete)) {
                    // console.log(`History Delete Click: Index ${indexToDelete}`);
                    const deletedTerm = item?.dataset?.term; // Get term *before* deleting
                    if (historyManager.deleteAt(indexToDelete)) {
                        // If the deleted item was the currently active search term, clear inputs
                        if (lastSyncedValue && deletedTerm === lastSyncedValue) {
                            // console.log("Deleted active item, clearing inputs.");
                            handleClear(); // Use handleClear to reset everything
                        }
                        historyManager.updatePanel(); // Refresh UI *after* potential clear
                    }
                }
            }
            return; // Stop processing for delete clicks
        }

        // 2. Check for Multi-Select Click (Ctrl/Cmd + Click)
        const isMultiSelectClick = event.ctrlKey || event.metaKey;
        if (isMultiSelectClick) {
            event.stopPropagation(); // Prevent search behavior
            event.preventDefault();  // Prevent default browser behavior (e.g., opening link)
            let idxStr, idx;
            try {
                idxStr = item.dataset.historyIndex;
                if (idxStr === undefined) return;
                idx = parseInt(idxStr, 10);
                if (isNaN(idx)) return;
            } catch (e) { return; }

            if (historyManager.toggleSelection(idx)) {
                historyManager.updatePanel(); // Update UI to show selection change
            }
            return; // Stop processing, only toggled selection
        }

        // 3. Handle Normal Click (Single Click without Ctrl/Meta, not delete)
        let term, idxStr, idx;
        try {
            term = item.dataset.term;
            idxStr = item.dataset.historyIndex;
            if (term === undefined || idxStr === undefined) return;
            idx = parseInt(idxStr, 10);
            if (isNaN(idx)) return;
        } catch (e) { return; }
        // console.log(`History Click (Single): Term="${term}", Index=${idx}`);

        // Clear any previous multi-selection on a normal click
        historyManager.clearSelection();

        // Update internal state & UI (highlight, nav index)
        try {
            if (activeHistoryItemElement && activeHistoryItemElement !== item && activeHistoryItemElement.isConnected) {
                activeHistoryItemElement.classList.remove('search-history-item--active');
            }
            if (!item.classList.contains('search-history-item--active')) {
                item.classList.add('search-history-item--active');
            }
            activeHistoryItemElement = item;

            if (isHistoryTrackingNeeded()) {
                historyManager.setCurrentIndex(idx);
                lastSyncedValue = term; // Update last synced value directly
                // Update panel to reflect new active index and cleared selection
                historyManager.updatePanel();
            }
            // Update custom input if enabled
            if (isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== term) {
                customInput.value = term;
            }
        } catch (e) { /* silenced */ }

        // Trigger search (DOES NOT add history here, term already exists)
        const searchSuccess = triggerInstantSearch(term);

        // Update original input (programmatically)
        setOriginalInputValue(term);

        // Try to focus original input for better UX
        if (searchSuccess && originalInput) {
            try { setTimeout(() => { if (document.activeElement !== originalInput) originalInput.focus(); }, 50); }
            catch (fe) { /* silenced */ }
        }
    }

    /** Toggles the persistent highlight state for a history item. */
    function togglePersistentHighlight(itemElement, term, logicalIndex) {
        if (!itemElement || term === undefined || isNaN(logicalIndex)) return false;

        const previouslyHighlightedElement = historyListElement?.querySelector('.search-history-item--persist-highlight');

        if (persistHighlightedIndex === logicalIndex && persistHighlightedTerm === term) {
            // Already highlighted, toggle off
            itemElement.classList.remove('search-history-item--persist-highlight');
            persistHighlightedTerm = null;
            persistHighlightedIndex = null;
        } else {
            // Remove previous highlight if exists
            if (previouslyHighlightedElement && previouslyHighlightedElement !== itemElement && previouslyHighlightedElement.isConnected) {
                try { previouslyHighlightedElement.classList.remove('search-history-item--persist-highlight'); } catch (e) {}
            }
            // Apply new highlight
            itemElement.classList.add('search-history-item--persist-highlight');
            persistHighlightedTerm = term;
            persistHighlightedIndex = logicalIndex;
        }
        return true;
    }

    /** Listener for double-clicks on history items to toggle persistent highlight. */
    function handleHistoryListDblClick(event) {
        // Prevent double-click on delete button from triggering highlight
        if (event.target.closest(`.${config.sync.historyItemDeleteBtnClass}`)) {
            event.stopPropagation();
            event.preventDefault();
            return;
        }
        if (!isFeatureEnabled('historyPanel') || !historyListElement) 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;

        togglePersistentHighlight(item, term, idx);
    }

    /** Listener for the "Selective Clear" button in the history panel. */
    function handleSelectiveHistoryClear(event) {
        event.stopPropagation();
        if (!isFeatureEnabled('historyPanel')) return;
        const numItems = historyManager.size();
        if (numItems === 0) return;

        const selectedCount = historyManager.getSelectedIndices().size;
        let confirmMessage = "确定要删除所有未选中的历史记录吗?\n选中的条目将被保留。";
        if (selectedCount === 0) {
            confirmMessage = "未选中任何历史记录。\n确定要清空所有历史记录吗?此操作不可撤销。";
        }

        if (!confirm(confirmMessage)) return;

        // console.log("Selective History Clear initiated");
        if (historyManager.deleteNonSelected()) {
            historyManager.updatePanel(); // Update UI to show remaining (or empty) list
            // Don't clear input fields here, user might want to keep searching related terms
        }
    }

    /** Handler for the top bar Previous/Next history buttons. Navigates history and performs search. */
    function handleHistoryNavigation(direction) { // direction: 1 for prev (older), -1 for next (newer)
        throttle((dir) => {
            if (!isHistoryTrackingNeeded()) return;
            const size = historyManager.size();
            if (size === 0) return;

            let currentActualIndex = historyManager.getCurrentIndex();
            let referenceIndex = currentActualIndex; // Start from current navigation index

            // If not currently navigating (-1), find current input value in history to establish context
            if (referenceIndex === -1 && lastSyncedValue) {
                let matchedIndex = -1;
                for (let i = 0; i < size; i++) {
                    const logicalIndex = size - 1 - i; // Check newest to oldest
                    if (historyManager.get(logicalIndex) === lastSyncedValue) {
                        matchedIndex = logicalIndex; break;
                    }
                }
                if (matchedIndex !== -1) referenceIndex = matchedIndex;
                // console.log(`History Nav Start: Context index ${referenceIndex} for "${lastSyncedValue}"`);
            }

            let nextIdx;
            if (dir === 1) { // Prev button (<-): Go to NEWER item (HIGHER logical index)
                if (referenceIndex === -1) nextIdx = size - 1; // Not navigating, go to newest
                else if (referenceIndex === size - 1) nextIdx = 0; // At newest, wrap to oldest
                else nextIdx = referenceIndex + 1; // Go one index higher (newer)
            } else { // Next button (->): Go to OLDER item (LOWER logical index) (dir === -1)
                if (referenceIndex === -1) nextIdx = size > 0 ? size - 1 : 0; // Not navigating, go to newest
                else if (referenceIndex === 0) nextIdx = size - 1; // At oldest, wrap to newest
                else nextIdx = referenceIndex - 1; // Go one index lower (older)
            }

            // Validate index (important if history was modified)
            if (nextIdx < 0 || nextIdx >= size) nextIdx = Math.max(0, Math.min(nextIdx, size - 1));
            if (size === 0) return; // Should not happen if checked earlier, but safe guard

            // Update internal state
            historyManager.setCurrentIndex(nextIdx);
            const navigatedValue = historyManager.get(nextIdx) ?? '';
            lastSyncedValue = navigatedValue; // Update last synced value
            // console.log(`History Navigated (${dir}): From ${referenceIndex} to ${nextIdx}, Value="${navigatedValue}"`);

            // Update custom input UI
            if (isFeatureEnabled('syncSearchBox') && customInput && customInput.value !== navigatedValue) {
                customInput.value = navigatedValue;
            }

            // Trigger search
            const searchSuccess = triggerInstantSearch(navigatedValue);

            // Update original input (programmatically)
            setOriginalInputValue(navigatedValue);

            // Update history panel UI and scroll
            if (isFeatureEnabled('historyPanel')) {
                historyManager.updatePanel();
                setTimeout(() => {
                    const activeItem = historyListElement?.querySelector('.search-history-item--active');
                    if (activeItem) {
                        try { activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }
                        catch (scrollError) { try { activeItem.scrollIntoView({ block: 'nearest' }); } catch (e) {} }
                    }
                }, 50);
            }

            // Try to focus original input
            if (searchSuccess && originalInput) {
                try { setTimeout(() => { if (document.activeElement !== originalInput) originalInput.focus(); }, 50); }
                catch (fe) { /* silenced */ }
            }

        }, config.sync.throttleTime)(direction);
    }

    /** Listener for Ctrl+A (or Cmd+A) when hovering over a history item to toggle persistent highlight. */
    function handleHistoryItemHighlightKey(event) {
        if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'a') {
            if (!isFeatureEnabled('historyPanel') || !historyListElement) return;
            const hoveredItem = historyListElement.querySelector('.search-history-item:hover');
            if (hoveredItem) {
                event.preventDefault();
                event.stopPropagation();
                const term = hoveredItem.dataset.term;
                const idxStr = hoveredItem.dataset.historyIndex;
                if (term === undefined || idxStr === undefined) return;
                const idx = parseInt(idxStr, 10);
                if (isNaN(idx)) return;
                togglePersistentHighlight(hoveredItem, term, idx);
            }
        }
    }

    /** Handler for the top bar Clear button (✕). Clears search inputs and resets state. */
    function handleClear() {
        // Only needs syncSearchBox enabled as it clears the custom input primarily
        if (!isFeatureEnabled('syncSearchBox') || !customInput) return;
        // console.log("Clear button clicked");

        // Clear custom input
        if (customInput.value !== '') customInput.value = '';

        // Reset internal state
        lastSyncedValue = '';
        historyManager.resetIndexToCurrent();
        // Don't clear persistent highlight on simple clear

        // Trigger clear via the Mubu API
        triggerInstantSearch('');

        // Clear the original input (programmatically)
        setOriginalInputValue('');

        // Update history panel (to remove active highlight)
        if (isFeatureEnabled('historyPanel')) {
            historyManager.updatePanel();
        }
    }

    /** Handler for clicking the search popup button after selecting text. Performs search and adds to history. */
    function handlePopupClick(event) {
        if (!isFeatureEnabled('selectSearchPopup')) return;
        event.preventDefault();
        event.stopPropagation();
        const term = currentSelectedText;
        if (!term) {
            hideSelectionActionButtons();
            return;
        }
        // console.log(`Select Popup Click: Term="${term}"`);

        // Add the selected term to history (source 'select_popup')
        updateCustomInputAndAddHistory(term, 'select_popup');

        // Trigger the fast search
        const searchSuccess = triggerInstantSearch(term);

        // Update the original input (programmatically)
        setOriginalInputValue(term);

        // Try to focus the original input
        if (searchSuccess && originalInput) {
            try { setTimeout(() => { if (document.activeElement !== originalInput) originalInput.focus(); }, 50); }
            catch (error) { /* silenced */ }
        }
        hideSelectionActionButtons(); // Hide popup after action
    }


    // --- [ ☆ Helper Functions for UI Elements (Popup, Transfer Paste, Copy Tag) ☆ ] ---

    function setupInputListeners(targetInput) {
        if (!targetInput) return;
        teardownInputListeners(targetInput); // Ensure no duplicate listeners
        // Attach listener for history tracking if needed
        if (isHistoryTrackingNeeded()) {
            originalInputHistoryHandler = handleOriginalInputForHistory;
            targetInput.addEventListener('input', originalInputHistoryHandler, { passive: true });
        }
    }

    function teardownInputListeners(targetInput) {
        if (!targetInput || !originalInputHistoryHandler) return;
        try { targetInput.removeEventListener('input', originalInputHistoryHandler); }
        catch (e) { /* silenced */ }
        originalInputHistoryHandler = null;
    }

    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 = '';
    }

    function 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);
        }
    }

    function getCursorRect(selection) {
        if (!selection || !selection.focusNode || selection.rangeCount === 0) return null;
        const range = document.createRange();
        try {
            // Create a collapsed range at the focus point
            range.setStart(selection.focusNode, selection.focusOffset);
            range.setEnd(selection.focusNode, selection.focusOffset);
            const rect = range.getBoundingClientRect();
            // If rect is 0x0 (happens at line breaks?), try getting rects from the selection range itself
            if (rect.width === 0 && rect.height === 0 && selection.toString().trim().length > 0) {
                const selRange = selection.getRangeAt(0);
                const clientRects = selRange.getClientRects();
                return clientRects.length > 0 ? clientRects[clientRects.length - 1] : selRange.getBoundingClientRect(); // Use last rect for multi-line
            }
            return rect; // Return collapsed range rect otherwise
        } catch (e) {
            try { // Fallback to the selection's bounding rect
                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 container = document.createElement("div");
            container.appendChild(range.cloneContents());
            tp_storedHTML = container.innerHTML;
            tp_storedText = selection.toString();
            return true;
        } catch (error) {
            tp_storedHTML = ""; tp_storedText = ""; return false;
        }
    }

    function tp_isElementEditable(element) {
        if (!element) return false;
        if (element instanceof Element && element.closest) {
            // Check common editable patterns in Mubu or standard contenteditable
            return !!element.closest('[contenteditable="true"], .mm-editor');
        } else if (element instanceof HTMLElement && ["INPUT", "TEXTAREA"].includes(element.tagName)) {
            return !element.readOnly && !element.disabled;
        }
        return false;
    }

    function tp_createButton(id, text, btnClass, clickHandler) {
        const cfg = config.transferPaste;
        let button = document.getElementById(id);
        if (button) { // Reuse existing button
            button.textContent = text;
            // Remove old listeners before adding new ones to prevent duplicates
            if (button.__clickHandler__) button.removeEventListener("click", button.__clickHandler__);
            if (button.__mousedownHandler__) button.removeEventListener("mousedown", button.__mousedownHandler__);
        } else { // Create new button
            button = document.createElement("button");
            button.id = id;
            button.textContent = text;
            Object.assign(button.style, cfg.buttonBaseStyleInline);
            document.body.appendChild(button);
        }
        // Apply styles and classes
        button.className = ""; // Clear existing classes
        button.classList.add(cfg.cssPrefix + cfg.btnBaseClass, cfg.cssPrefix + btnClass);

        // Define event handlers
        const stopPropagationHandler = (e) => e.stopPropagation();
        const clickWrapper = (e) => {
            e.stopPropagation();
            clickHandler(button); // Pass the button element to the handler
        };

        // Attach listeners and store references for removal
        button.addEventListener("mousedown", stopPropagationHandler);
        button.addEventListener("click", clickWrapper);
        button.__clickHandler__ = clickWrapper;
        button.__mousedownHandler__ = stopPropagationHandler;

        observeElementResize(button); // Observe for size changes
        return button;
    }

    function tp_showPasteButton(mouseEvent) {
        const cfg = config.transferPaste;
        hideSelectionActionButtons(); // Hide other buttons first
        tp_hidePasteButton(); // Hide existing paste button if any
        if (!tp_storedHTML && !tp_storedText) return; // Nothing to paste

        const target = mouseEvent.target instanceof Node ? mouseEvent.target : document.elementFromPoint(mouseEvent.clientX, mouseEvent.clientY);
        if (!tp_isElementEditable(target)) return; // Can only paste into editable areas

        // Use mouse click coordinates as the reference point
        const targetRect = { top: mouseEvent.clientY, left: mouseEvent.clientX, bottom: mouseEvent.clientY, right: mouseEvent.clientX, width: 0, height: 0 };

        const positionButton = (buttonEl, refRect) => {
            try {
                if (!buttonEl || !refRect || !buttonEl.isConnected) return false;
                const dims = elementDimensionsCache.get(buttonEl);
                const buttonW = dims?.width || cfg.buttonFallbackWidth;
                const buttonH = dims?.height || cfg.buttonFallbackHeight;
                const vpW = window.innerWidth;
                const scrollY = window.pageYOffset;
                const scrollX = window.pageXOffset;

                // Position below the click point
                let top = scrollY + refRect.top + 10;
                // Center horizontally relative to the click point
                let left = scrollX + refRect.left - (buttonW / 2);

                // Keep within viewport bounds
                top = Math.max(scrollY + 5, top);
                left = Math.max(scrollX + 5, Math.min(left, scrollX + vpW - buttonW - 5));

                scheduleStyleUpdate(buttonEl, {
                    transform: `translate(${left.toFixed(1)}px, ${top.toFixed(1)}px)`,
                    display: "inline-block",
                    opacity: "1",
                    visibility: "visible"
                });
                return true;
            } catch (error) {
                if (buttonEl) scheduleStyleUpdate(buttonEl, { display: "none", opacity: "0", visibility: "hidden" });
                return false;
            }
        };

        // Create the paste button instance
        tp_pasteButtonRef.element = tp_createButton(cfg.pasteButtonId, cfg.pasteButtonText, cfg.btnPasteClass, (button) => {
            const selection = window.getSelection();
            const range = selection?.rangeCount > 0 ? selection.getRangeAt(0) : null;
            let pasteTarget = null;

            // Determine paste target: Use selection's container or fallback to clicked element
            if (range) {
                 pasteTarget = range.startContainer.nodeType === Node.ELEMENT_NODE
                    ? range.startContainer
                    : range.startContainer.parentElement;
                 if (!tp_isElementEditable(pasteTarget)) {
                    pasteTarget = document.elementFromPoint(mouseEvent.clientX, mouseEvent.clientY); // Fallback
                 }
            } else {
                 pasteTarget = document.elementFromPoint(mouseEvent.clientX, mouseEvent.clientY);
            }

            if (!tp_isElementEditable(pasteTarget)) {
                 alert("无法粘贴:位置不可编辑。");
                 tp_hidePasteButton();
                 return;
            }

            try {
                let success = false;
                // Try inserting HTML first
                if (tp_storedHTML && document.queryCommandSupported("insertHTML")) {
                    try {
                        // Restore selection if needed, then execute command
                        if (range) { selection.removeAllRanges(); selection.addRange(range); }
                        else if (pasteTarget instanceof HTMLElement) pasteTarget.focus();

                        if (document.execCommand("insertHTML", false, tp_storedHTML)) {
                            success = true;
                        } else { /* console.warn("insertHTML command failed"); */ }
                    } catch (cmdError) { /* console.warn("insertHTML error:", cmdError); */ }
                }
                // Fallback to inserting plain text
                if (!success && tp_storedText && document.queryCommandSupported("insertText")) {
                     try {
                        if (range) { selection.removeAllRanges(); selection.addRange(range); }
                        else if (pasteTarget instanceof HTMLElement) pasteTarget.focus();

                        if (document.execCommand("insertText", false, tp_storedText)) {
                            success = true;
                        } else { /* console.warn("insertText command failed"); */ }
                     } catch (cmdError) { /* console.warn("insertText error:", cmdError); */ }
                }

                if (!success) {
                    alert("粘贴失败。请尝试手动 Ctrl+V。");
                } else {
                    // Clear stored content after successful paste
                    tp_storedHTML = ""; tp_storedText = "";
                }
            } catch (error) {
                alert(`粘贴时出错: ${error.message}`);
            } finally {
                tp_hidePasteButton(); // Hide button after attempt
            }
        });

        // Position and show the button
        if (!positionButton(tp_pasteButtonRef.element, targetRect)) {
            tp_hidePasteButton(); // Hide if positioning failed
        }
    }

    function calculateTransformForPopup(element, targetRect, marginBottom = 0) {
        if (!element || !targetRect || !element.isConnected) return null;
        const cfg = config.copyTag;
        const dims = elementDimensionsCache.get(element);
        let popupW, popupH;

        // Determine dimensions based on element type
        if (element === ct_copyPopupElement) {
            popupW = dims?.width || cfg.popupFallbackWidth;
            popupH = dims?.height || cfg.popupFallbackHeight;
        } else if (element === ct_feedbackElement) {
            popupW = dims?.width || cfg.feedbackFallbackWidth;
            popupH = dims?.height || cfg.feedbackFallbackHeight;
        } else { // Generic fallback
            popupW = dims?.width || 30;
            popupH = dims?.height || 20;
        }

        // Calculate position: Above and centered horizontally to the targetRect
        const scrollX = window.pageXOffset;
        const scrollY = window.pageYOffset;
        const targetCenterX = targetRect.left + targetRect.width / 2;
        const top = scrollY + targetRect.top - popupH - marginBottom;
        const left = scrollX + targetCenterX - popupW / 2;

        return `translate(${left.toFixed(1)}px, ${top.toFixed(1)}px)`;
    }

    function ct_showCopyPopup(tagElement) {
        if (!isFeatureEnabled("copyTagOnHover")) return;
        ct_createElements(); // Ensure elements exist
        if (!ct_copyPopupElement || !tagElement || !ct_copyPopupElement.isConnected) return;

        const tagText = tagElement.textContent?.trim();
        if (!tagText) return;
        ct_currentTagText = tagText; // Store text for the click handler

        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 { // Fallback position if calculation fails
            scheduleStyleUpdate(ct_copyPopupElement, {
                transform: "translate(0px, -20px)", // Arbitrary offset
                display: "block",
                opacity: "1",
                visibility: "visible"
            });
        }
    }

    function ct_hideCopyPopupImmediately(resetHoverState = 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 (resetHoverState) {
            ct_currentHoveredTag = null;
            ct_currentTagText = "";
        }
    }

    function ct_scheduleHidePopup() {
        clearTimeout(ct_showTimeout); ct_showTimeout = null;
        clearTimeout(ct_hideTimeout);
        ct_hideTimeout = setTimeout(() => {
            // Check if mouse is still over the tag or the popup itself
            const isOverTag = ct_currentHoveredTag?.matches(":hover");
            const isOverPopup = ct_copyPopupElement?.matches(":hover");
            if (!isOverTag && !isOverPopup) {
                ct_hideCopyPopupImmediately(true); // Hide and reset state
            }
            ct_hideTimeout = null;
        }, config.copyTag.hideDelay);
    }

    function ct_showFeedbackIndicator(tagElement) {
        if (!isFeatureEnabled("copyTagOnHover")) return;
        ct_createElements(); // Ensure elements exist
        if (!ct_feedbackElement || !tagElement || !ct_feedbackElement.isConnected) return;

        const duration = config.copyTag.copiedMessageDuration;
        clearTimeout(ct_feedbackTimeout); // Clear previous timeout

        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 {
            scheduleStyleUpdate(ct_feedbackElement, {
                transform: "translate(0px, -20px)", // Fallback
                display: "block",
                opacity: "1",
                visibility: "visible"
            });
        }
        // Schedule hiding the feedback
        ct_feedbackTimeout = setTimeout(ct_hideFeedbackIndicator, duration);
    }

    function ct_hideFeedbackIndicator() {
        if (!ct_feedbackElement?.isConnected) return;
        scheduleStyleUpdate(ct_feedbackElement, { opacity: "0", visibility: "hidden" });
        setTimeout(() => { if (ct_feedbackElement?.style.opacity === '0') scheduleStyleUpdate(ct_feedbackElement, { display: "none" }) }, 150);
        ct_feedbackTimeout = null;
    }

    // --- [ ☆ UI 创建函数 ☆ ] ---

    /** Creates or retrieves the Copy Tag popup and feedback elements. */
    function ct_createElements() {
        if (!isFeatureEnabled("copyTagOnHover")) return;
        const cfg = config.copyTag;
        const baseStyle = { position: "absolute", top: "0", left: "0", zIndex: "10010", display: "none", opacity: "0", visibility: "hidden" };
        const feedbackStyle = { ...baseStyle, zIndex: "10011", pointerEvents: "none" };

        // Create/Get Copy Button
        if (!ct_copyPopupElement) {
            let existing = document.getElementById(cfg.popupId);
            if (existing) {
                ct_copyPopupElement = existing;
            } else {
                const button = document.createElement("button");
                button.id = cfg.popupId;
                button.textContent = cfg.copyIcon;
                Object.assign(button.style, baseStyle);
                button.addEventListener("click", ct_handleCopyButtonClick);
                button.addEventListener("mouseenter", () => { clearTimeout(ct_hideTimeout); ct_hideTimeout = null; }); // Keep open if mouse enters popup
                button.addEventListener("mouseleave", ct_scheduleHidePopup); // Schedule hide if mouse leaves popup
                button.addEventListener("mousedown", e => { e.preventDefault(); e.stopPropagation(); }); // Prevent text selection
                document.body.appendChild(button);
                ct_copyPopupElement = button;
            }
            if (!elementObserverMap.has(ct_copyPopupElement)) observeElementResize(ct_copyPopupElement);
        }

        // Create/Get Feedback Indicator
        if (!ct_feedbackElement) {
             let existing = document.getElementById(cfg.feedbackId);
             if (existing) {
                ct_feedbackElement = existing;
             } else {
                const feedback = document.createElement("div");
                feedback.id = cfg.feedbackId;
                feedback.textContent = cfg.copiedText;
                Object.assign(feedback.style, feedbackStyle);
                document.body.appendChild(feedback);
                ct_feedbackElement = feedback;
             }
             if (!elementObserverMap.has(ct_feedbackElement)) observeElementResize(ct_feedbackElement);
        }
    }

    /** Creates or retrieves the top search bar controls. */
    function createControlPanel() {
        if (document.getElementById(config.sync.topBarId)) return topBarControls; // Already exists
        try {
            const container = document.createElement("div");
            container.id = config.sync.topBarId;
            container.style.display = "none"; // Initially hidden

            const clearBtn = document.createElement("button");
            clearBtn.className = "clear-btn";
            clearBtn.textContent = "✕";
            clearBtn.title = "清空";

            const prevBtn = document.createElement("button");
            prevBtn.className = "history-btn";
            prevBtn.textContent = "←";
            prevBtn.title = "上条";

            const nextBtn = document.createElement("button");
            nextBtn.className = "history-btn";
            nextBtn.textContent = "→";
            nextBtn.title = "下条";

            const input = document.createElement("input");
            input.className = "custom-search-input";
            input.type = "search";
            input.placeholder = "筛选 (检查接口...)";
            input.setAttribute("autocomplete", "off");
            input.disabled = true; // Disabled until interface check passes

            container.append(clearBtn, prevBtn, nextBtn, input);
            document.body.appendChild(container);

            topBarControls = { container, input, prevBtn, nextBtn, clearBtn };
            observeElementResize(container);
            return topBarControls;
        } catch (e) {
            // Reset refs if creation failed
            topBarControls = { container: null, input: null, prevBtn: null, nextBtn: null, clearBtn: null };
            return topBarControls;
        }
    }

    /** Creates or retrieves the history panel elements. */
    function createHistoryPanel() {
        const panelId = config.sync.historyPanelId;
        if (document.getElementById(panelId)) return historyPanel; // Already exists

        try {
            const panel = document.createElement("div");
            panel.id = panelId;
            panel.style.display = "none"; // Initially hidden

            const list = document.createElement("ul");
            list.className = "search-history-list";
            list.id = config.sync.historyListId;
            panel.appendChild(list);

            // Create the "Selective Clear" button
            const clearSelectiveButton = document.createElement("button");
            clearSelectiveButton.id = 'history-clear-all-btn'; // Keep ID consistent
            clearSelectiveButton.className = 'history-clear-all-button'; // Keep class consistent
            clearSelectiveButton.textContent = "删除未选记录";
            clearSelectiveButton.title = "清空所有历史记录 (未选中任何条目)";
            clearSelectiveButton.disabled = true; // Initially disabled
            panel.appendChild(clearSelectiveButton); // Add button after the list

            document.body.appendChild(panel);
            historyPanel = panel;
            historyListElement = list;
            observeElementResize(panel); // Observe panel size

            // Attach listener to the selective clear button
            clearSelectiveButton.addEventListener('click', handleSelectiveHistoryClear);

            return panel;
        } catch (e) {
            historyPanel = null;
            historyListElement = null;
            return null;
        }
    }

    /** Creates or retrieves the select-to-search popup button. */
    function createSelectPopup() {
        if (!isFeatureEnabled("selectSearchPopup")) return;
        const popupId = config.select.popupId;
        let button = document.getElementById(popupId);

        if (button) { // Reuse existing
            popupElement = button;
            if (!elementObserverMap.has(popupElement)) observeElementResize(popupElement);
            Object.assign(popupElement.style, { display: "none", opacity: "0", visibility: "hidden" }); // Ensure hidden initially
            // Ensure listener is attached only once
            if (!button.__clickAttached__) {
                button.addEventListener("mousedown", handlePopupClick); // Use mousedown for faster response
                button.addEventListener("click", e => e.stopPropagation()); // Prevent potential bubbling issues
                button.__clickAttached__ = true;
            }
            return;
        }

        try { // Create new button
            const newButton = document.createElement("button");
            newButton.id = popupId;
            newButton.textContent = config.select.popupText;
            Object.assign(newButton.style, {
                position: "absolute", top: "0", left: "0", zIndex: "10010",
                display: "none", opacity: "0", visibility: "hidden"
            });
            newButton.classList.add("mu-select-popup-btn");
            newButton.addEventListener("mousedown", handlePopupClick);
            newButton.addEventListener("click", e => e.stopPropagation());
            newButton.__clickAttached__ = true; // Mark listener as attached
            document.body.appendChild(newButton);
            popupElement = newButton;
            observeElementResize(popupElement);
        } catch (e) {
            popupElement = null;
        }
    }

    /** Creates or retrieves the feature toggle panel and its trigger. */
    function createTogglePanel() {
        const panelId = config.togglePanel.panelId;
        const triggerId = config.togglePanel.triggerId;
        if (document.getElementById(panelId)) return; // Already exists

        try {
            // Create Trigger Area
            toggleTriggerElement = document.createElement('div');
            toggleTriggerElement.id = triggerId;
            toggleTriggerElement.addEventListener('mouseenter', showTogglePanel);
            toggleTriggerElement.addEventListener('mouseleave', scheduleHideTogglePanel);
            document.body.appendChild(toggleTriggerElement);

            // Create Panel
            togglePanelElement = document.createElement('div');
            togglePanelElement.id = panelId;
            togglePanelElement.innerHTML = '<div class="toggle-panel-title">功能开关</div>';
            togglePanelElement.addEventListener('mouseenter', showTogglePanel);
            togglePanelElement.addEventListener('mouseleave', scheduleHideTogglePanel);

            // Add Toggles for each feature
            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 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;
                        applyFeatureStateChange(changedKey, newState); // Apply the change immediately
                    });

                    const label = document.createElement('label');
                    label.htmlFor = `toggle-${key}`;
                    label.textContent = feature.label;

                    div.appendChild(checkbox);
                    div.appendChild(label);
                    togglePanelElement.appendChild(div);
                }
            }
            document.body.appendChild(togglePanelElement);
        } catch (e) {
            togglePanelElement = null;
            toggleTriggerElement = null;
        }
    }

    // --- [ ☆ Feature-Specific Event Handlers & Logic ☆ ] ---

    // --- Select Search / Transfer Paste Interaction ---
    function handleMouseDownPopup(event) {
        const target = event.target;
        if (target instanceof Node) {
            // Check if the click is on any known interactive UI element from this script
            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);
            const isClickOnCopyTag = ct_copyPopupElement?.contains(target);
            const isClickOnHistory = historyPanel?.contains(target); // Check history panel

            // Hide selection buttons if clicking outside relevant UI
            if (!isClickOnActionButton && !isClickOnToggle && !isClickOnPasteButton && !isClickOnCopyTag && !isClickOnHistory) {
                hideSelectionActionButtons();
            }
            // Hide paste button specifically if click is not on it or toggle/history
            if (!isClickOnPasteButton && !isClickOnToggle && !isClickOnHistory) {
                tp_hidePasteButton();
            }
            // Hide copy tag popup if clicking outside relevant UI
             if (!isClickOnCopyTag && ct_copyPopupElement?.style.visibility !== 'hidden' && !isClickOnHistory) {
                 ct_hideCopyPopupImmediately(true);
             }
        } else { // Click target is not a Node, hide everything as a fallback
            hideSelectionActionButtons();
            tp_hidePasteButton();
            ct_hideCopyPopupImmediately(true);
        }
    }

    function handleMouseUpSelectionEnd(event) {
        const target = event.target;
        // Ignore mouseup events originating from script's own UI elements
        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);
            const isClickOnCopyTag = ct_copyPopupElement?.contains(target);
            const isClickOnHistory = historyPanel?.contains(target);
            if (isClickOnActionButton || isClickOnToggle || isClickOnPasteButton || isClickOnCopyTag || isClickOnHistory) {
                return; // Don't show selection buttons if interacting with script UI
            }
        }

        // Use timeout + rAF to check selection state *after* mouseup processing
        setTimeout(() => {
            requestAnimationFrame(() => {
                const selection = window.getSelection();
                if (selection && !selection.isCollapsed && selection.toString().trim().length > 0) {
                    const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
                    if (range) {
                        // Check if selection is within an editable context
                        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) {
                            showSelectionActionButtons(selection, false); // Show standard buttons
                        } else {
                            hideSelectionActionButtons(); // Selection not editable
                        }
                    } else {
                        hideSelectionActionButtons(); // Range error
                    }
                } else {
                    hideSelectionActionButtons(); // No selection or empty selection
                }
            });
        }, config.select.popupAppearDelay);
    }

    function showSelectionActionButtons(selection, isCtrlA = false) {
        hideSelectionActionButtons(); // Clear previous
        tp_hidePasteButton();         // Hide paste button
        if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;

        const selectionText = selection.toString().trim();
        if (selectionText.length === 0) return;

        // Ensure selection is in an editable area before showing buttons
        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;

        // Define button order and which ones to show based on features and context (Ctrl+A)
        const buttonOrder = isCtrlA ? ['copy', 'cut'] : ['filter', 'copy', 'cut'];
        const visibleButtonsData = []; // Store data for buttons to be shown
        let maxHeight = 0;

        // Create/prepare buttons that should be visible
        buttonOrder.forEach(type => {
            let buttonInfo = null;
            const shouldAppear = (
                (type === 'filter' && !isCtrlA && isFeatureEnabled('selectSearchPopup')) ||
                (type === 'copy' && isFeatureEnabled('transferPasteCopy')) ||
                (type === 'cut' && isFeatureEnabled('transferPasteCut'))
            );

            if (shouldAppear) {
                try {
                    if (type === 'filter') {
                        if (!popupElement) createSelectPopup(); // Ensure popup exists
                        if (!popupElement) return; // Creation failed
                        buttonInfo = { type: 'filter', element: popupElement, fallbackW: config.select.fallbackWidth, fallbackH: config.select.fallbackHeight };
                        currentSelectedText = selectionText; // Store text for filter button
                    } else if (type === 'copy') {
                        tp_triggerButtonRef.element = tp_createButton(
                            config.transferPaste.triggerButtonId, config.transferPaste.triggerButtonText, config.transferPaste.btnCopyClass,
                            (button) => { // Click handler for Copy
                                if (!tp_captureSelectionAndStore()) alert('捕获选区失败!');
                                hideSelectionActionButtons();
                            }
                        );
                        if (!tp_triggerButtonRef.element) return; // Creation failed
                        buttonInfo = { type: 'copy', element: tp_triggerButtonRef.element, fallbackW: config.transferPaste.buttonFallbackWidth, fallbackH: config.transferPaste.buttonFallbackHeight };
                    } else if (type === 'cut') {
                         tp_cutButtonRef.element = tp_createButton(
                             config.transferPaste.cutButtonId, config.transferPaste.cutButtonText, config.transferPaste.btnCutClass,
                             (button) => { // Click handler for Cut
                                const latestSel = window.getSelection();
                                if (latestSel && !latestSel.isCollapsed) {
                                    if (tp_captureSelectionAndStore()) { // Store first
                                        try { // Then delete
                                            if (!document.execCommand('delete', false, null)) {
                                                // Fallback if execCommand fails
                                                latestSel.getRangeAt(0).deleteContents();
                                            }
                                        } catch (e) {
                                            try { latestSel.getRangeAt(0).deleteContents(); } // Second fallback
                                            catch (e2) { alert('剪切删除失败.'); }
                                        }
                                    } else { alert('捕获失败,无法剪切!'); }
                                } else { alert('选区失效,无法剪切!'); }
                                hideSelectionActionButtons();
                             }
                         );
                         if (!tp_cutButtonRef.element) return; // Creation failed
                         buttonInfo = { type: 'cut', element: tp_cutButtonRef.element, fallbackW: config.transferPaste.buttonFallbackWidth, fallbackH: config.transferPaste.buttonFallbackHeight };
                    }
                } catch (creationError) { return; } // Skip button if creation fails
            }

            // If button created/found, get its dimensions and add to list
            if (buttonInfo && buttonInfo.element && buttonInfo.element.isConnected) {
                observeElementResize(buttonInfo.element); // Ensure observer is attached
                const dims = elementDimensionsCache.get(buttonInfo.element);
                buttonInfo.width = dims?.width || buttonInfo.fallbackW;
                buttonInfo.height = dims?.height || buttonInfo.fallbackH;

                // Final check for valid dimensions (might not be available immediately)
                if (buttonInfo.width <= 0 || buttonInfo.height <= 0) {
                    const ow = buttonInfo.element.offsetWidth; const oh = buttonInfo.element.offsetHeight;
                    if (ow > 0 && oh > 0) { // Fallback read
                        buttonInfo.width = ow; buttonInfo.height = oh;
                        elementDimensionsCache.set(buttonInfo.element, { width: ow, height: oh }); // Cache fallback
                    } else {
                        return; // Skip if dimensions are invalid
                    }
                }

                maxHeight = Math.max(maxHeight, buttonInfo.height);
                visibleButtonsData.push(buttonInfo);
            }
        });

        if (visibleButtonsData.length === 0) return; // No buttons to show

        // Calculate position for the group of buttons
        const targetRect = getCursorRect(selection); // Use cursor position for placement reference
        // Check if targetRect is valid, sometimes it might be 0x0 even with selection
        if (!targetRect || (targetRect.width === 0 && targetRect.height === 0 && selectionText.length === 0)) {
             hideSelectionActionButtons(); return; // Invalid target rect
        }

        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;

        // Position group above the selection end cursor
        const groupTop = Math.max(scrollY + 5, scrollY + targetRect.top - maxHeight - config.select.popupAboveGap);
        // Center the group horizontally based on the cursor position
        const selectionCenterX = targetRect.left + targetRect.width / 2; // Use cursor X for center
        let groupLeftStart = scrollX + selectionCenterX - totalWidth / 2;

        // Keep group within viewport bounds
        groupLeftStart = Math.max(scrollX + 5, groupLeftStart);
        if (groupLeftStart + totalWidth > scrollX + vpW - 5) {
            groupLeftStart = scrollX + vpW - totalWidth - 5; // Adjust if overflows right
            groupLeftStart = Math.max(scrollX + 5, groupLeftStart); // Ensure doesn't underflow left
        }

        // Position each button within the group
        let currentLeftOffset = 0;
        visibleButtonsData.forEach((buttonInfo, index) => {
            const currentButtonLeft = groupLeftStart + currentLeftOffset;
            // Ensure element is in DOM (might be newly created)
            if (!buttonInfo.element.isConnected) {
                try { docBody.appendChild(buttonInfo.element); observeElementResize(buttonInfo.element); }
                catch(e) { return; } // Skip if append fails
            }
            scheduleStyleUpdate(buttonInfo.element, {
                transform: `translate(${currentButtonLeft.toFixed(1)}px, ${groupTop.toFixed(1)}px)`,
                display: 'inline-block', // Use inline-block for layout
                opacity: '1',
                visibility: 'visible'
            });
            currentLeftOffset += buttonInfo.width + (index < visibleButtonsData.length - 1 ? BUTTON_GAP : 0);
        });
    }

    // --- Transfer Paste Specific Handlers ---
    function tp_handleMouseUp(event) {
        const target = event.target;
        // Ignore if mouseup is on script's own UI elements
        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 isClickOnCopyTag = ct_copyPopupElement?.contains(target);
            const isClickOnHistory = historyPanel?.contains(target);
            if (isClickOnActionButton || isClickOnToggle || isClickOnCopyTag || isClickOnHistory) return;
        }

        // Delay slightly to allow selection state to finalize
        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);

                // Show paste button if: selection is collapsed, there's content to paste, and target is editable
                if (latestSel.isCollapsed && hasStoredContent && targetEditable) {
                    // Don't show if mouseup was on the selection buttons (already handled)
                    if (!popupElement || !popupElement.contains(event.target)) {
                         hideSelectionActionButtons(); // Ensure others are hidden
                         tp_showPasteButton(event); // Show the paste button at click location
                    }
                } else {
                    tp_hidePasteButton(); // Hide paste button if conditions not met
                }
            });
        }, config.transferPaste.buttonsAppearDelay);
    }

    function tp_handleKeyDown(event) {
        // Ignore key events if focus is inside the toggle panel
        if (togglePanelElement?.contains(document.activeElement)) return;

        const anyActionEnabled = isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut') || isFeatureEnabled('selectSearchPopup');
        // Optimization: Don't process if no relevant features are enabled AND paste button isn't visible
        if (!anyActionEnabled && (!tp_pasteButtonRef.element || tp_pasteButtonRef.element.style.visibility === 'hidden')) return;

        // Detect Ctrl+A / Cmd+A for special handling
        if ((event.ctrlKey || event.metaKey) && (event.key === 'a' || event.key === 'A')) {
            tp_ctrlApressed = true;
            // Hide buttons immediately on Ctrl+A start
            hideSelectionActionButtons();
            tp_hidePasteButton();
        } else {
            // Reset Ctrl+A flag if another key is pressed (excluding modifiers)
            if (tp_ctrlApressed && !(event.key === 'Control' || event.key === 'Meta' || event.key === 'Shift' || event.key === 'Alt')) {
                tp_ctrlApressed = false;
            }
            // Hide paste button on navigation/editing keys if it's visible
            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();
                }
            }
            // Hide selection buttons if Backspace/Delete is pressed while they are visible
            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)) {
                    // Check selection state *after* the keydown event allows potential deletion
                    setTimeout(() => {
                        const selection = window.getSelection();
                        if (!selection || selection.isCollapsed) {
                             hideSelectionActionButtons();
                        }
                    }, 0);
                }
            }
        }
    }

    function tp_handleKeyUp(event) {
        // Ignore key events if focus is inside the toggle panel
        if (togglePanelElement?.contains(document.activeElement)) return;

        const anyActionEnabled = isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut');
        if (!anyActionEnabled) return; // Only needed for Ctrl+A related button showing

        // Handle keyup for Ctrl+A (or Cmd+A) to show relevant buttons after selection
        if (tp_ctrlApressed && (event.key === 'Control' || event.key === 'Meta' || event.key === 'a' || event.key === 'A')) {
            // Use timeout to check state *after* keyup finishes
            setTimeout(() => {
                // Check if modifier key was released or if 'A' key was released without modifier still pressed
                const modPressed = event.ctrlKey || event.metaKey;
                if ((event.key === 'Control' || event.key === 'Meta') || ((event.key === 'a' || event.key === 'A') && !modPressed)) {
                    if (tp_ctrlApressed) { // Check flag again inside timeout
                        const currentSelection = window.getSelection();
                        // Check if there's a valid selection after Ctrl+A finishes
                        if (currentSelection && !currentSelection.isCollapsed && currentSelection.toString().trim().length > 0) {
                            const range = currentSelection.rangeCount > 0 ? currentSelection.getRangeAt(0) : null;
                            if (range) {
                                // Verify selection is editable before showing buttons
                                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(() => { showSelectionActionButtons(currentSelection, true); }); // Show Copy/Cut only
                                } else { hideSelectionActionButtons(); }
                            } else { hideSelectionActionButtons(); }
                        } else { hideSelectionActionButtons(); }
                        tp_ctrlApressed = false; // Reset flag
                    }
                }
            }, 0);
        }
    }

    function tp_initialize() {
        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; } // Already found

            const container = document.querySelector(tpConfig.editorContainerSelector);
            if (container) {
                clearInterval(intervalId);
                tp_editorContainer = container;
                // Attach listeners immediately if features are already enabled
                if (isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut')) {
                    tp_attachListeners();
                }
            } else {
                retries++;
                if (retries >= MAX_RETRIES) {
                    clearInterval(intervalId);
                    // console.warn("MubuPlus: Transfer Paste editor container not found after retries.");
                }
            }
        }, RETRY_INTERVAL);
    }

    function tp_attachListeners() {
        if (!tp_editorContainer || tp_listenersAttached) return;
        try {
            // Use capture phase to catch events early
            tp_editorContainer.addEventListener('keydown', tp_handleKeyDown, true);
            tp_editorContainer.addEventListener('keyup', tp_handleKeyUp, true);
            tp_listenersAttached = true;
        } catch (e) {
            // console.error("MubuPlus: Failed to attach Transfer Paste listeners.", e);
            tp_listenersAttached = false;
        }
    }

    function tp_detachListeners() {
        if (!tp_editorContainer || !tp_listenersAttached) return;
        try {
            tp_editorContainer.removeEventListener('keydown', tp_handleKeyDown, true);
            tp_editorContainer.removeEventListener('keyup', tp_handleKeyUp, true);
            tp_listenersAttached = false;
        } catch (e) {
            // console.error("MubuPlus: Failed to detach Transfer Paste listeners.", e);
            // Keep flag as true if removal fails? Or set false anyway? Let's set false.
            tp_listenersAttached = false;
        }
    }


    // --- Copy Tag Specific Handlers ---
    async function ct_handleCopyButtonClick(event) {
        if (!isFeatureEnabled('copyTagOnHover')) return;
        event.stopPropagation();
        event.preventDefault(); // Prevent any default button behavior
        if (!ct_currentTagText || !ct_copyPopupElement || !ct_currentHoveredTag) return;

        const text = " " + ct_currentTagText; // Prepend space as per original intent?
        try {
            await navigator.clipboard.writeText(text);
            ct_showFeedbackIndicator(ct_currentHoveredTag); // Show feedback relative to the tag
            ct_hideCopyPopupImmediately(false); // Hide popup but keep hover state
        } catch (err) {
            alert(`复制失败: ${err.message}`);
        }
    }

    function ct_handleMouseOver(event) {
        if (!isFeatureEnabled('copyTagOnHover')) return;
        if (!(event.target instanceof Element)) return; // Ensure target is an element

        // Find the closest relevant element (tag or the popup button itself)
        const relevant = event.target.closest(`${config.copyTag.tagSelector}, #${config.copyTag.popupId}`);

        if (!relevant) {
            // Mouse is over neither tag nor popup, schedule hide if a tag was previously hovered
            if (ct_currentHoveredTag) {
                ct_scheduleHidePopup();
            }
            return;
        }

        ct_createElements(); // Ensure elements exist
        if (!ct_copyPopupElement) return;

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

        if (tagEl) { // Hovering over a tag
            if (tagEl === ct_currentHoveredTag) {
                // Still hovering the same tag, cancel any pending hide timeout
                clearTimeout(ct_hideTimeout); ct_hideTimeout = null;
                // If popup somehow got hidden, reshow it
                if (ct_copyPopupElement.style.visibility === 'hidden' || ct_copyPopupElement.style.opacity === '0') {
                    ct_showCopyPopup(tagEl);
                }
            } else {
                // Hovering over a *new* tag
                clearTimeout(ct_showTimeout); // Cancel pending show for previous tag
                clearTimeout(ct_hideTimeout); ct_hideTimeout = null; // Cancel pending hide
                // Hide immediately if popup for old tag was visible
                if (ct_currentHoveredTag && ct_copyPopupElement.style.visibility !== 'hidden' && ct_copyPopupElement.style.opacity !== '0') {
                    ct_hideCopyPopupImmediately(false); // Hide old popup, keep potential new hover state
                }
                ct_currentHoveredTag = tagEl; // Update current hovered tag
                // Schedule showing the popup for the new tag after a short delay
                ct_showTimeout = setTimeout(() => {
                    // Verify the mouse is *still* over the same tag after the delay
                    if (ct_currentHoveredTag === tagEl && tagEl.matches(':hover')) {
                        ct_showCopyPopup(tagEl);
                    }
                    ct_showTimeout = null;
                }, config.copyTag.hoverDelay);
            }
        } else if (isOverPopup) { // Hovering over the popup button itself
            // Cancel any pending hide/show timeouts
            clearTimeout(ct_hideTimeout); ct_hideTimeout = null;
            clearTimeout(ct_showTimeout); ct_showTimeout = null;
            // Ensure popup remains visible if mouse enters it
             if (ct_copyPopupElement.style.visibility === 'hidden' || ct_copyPopupElement.style.opacity === '0') {
                 scheduleStyleUpdate(ct_copyPopupElement, { opacity: '1', visibility: 'visible', display: 'block'});
             }
        }
    }

    // --- Toggle Panel ---
    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) { // Hide only if mouse is outside both trigger and panel
                hideTogglePanel();
            }
        }, config.togglePanel.hideDelay);
    }
    function showTogglePanel() {
        clearTimeout(togglePanelHideTimeout); // Cancel any pending hide
        if (togglePanelElement) {
            scheduleStyleUpdate(togglePanelElement, { transform: 'translateX(0)' }); // Slide in
        }
    }

    // --- [ ☆ Feature State Change Application ☆ ] ---
    // Flags to track if listeners are currently attached
    let customInputListenerAttached = false;
    let historyListClickListenerAttached = false;
    let historyListDblClickListenerAttached = false;
    let selectPopupListenersAttached = false; // Covers mousedown, mouseup for selection buttons
    let copyTagListenerAttached = false;
    let historyItemKeyListenerAttached = false; // For Ctrl+A highlight

    /** Applies changes when a feature is enabled or disabled via the toggle panel. */
    function applyFeatureStateChange(featureKey, isEnabled) {
        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;
                        checkMubuInterface(); // Check interface status when enabling
                    }
                    // Attach listeners if not already attached
                    if (topBarControls.input && !customInputListenerAttached) {
                        topBarControls.prevBtn?.addEventListener('click', () => handleHistoryNavigation(-1)); // Newer
                        topBarControls.nextBtn?.addEventListener('click', () => handleHistoryNavigation(1)); // Older
                        topBarControls.clearBtn?.addEventListener('click', handleClear);
                        topBarControls.input?.addEventListener('input', handleCustomInputChange, { passive: true });
                        customInputListenerAttached = true;
                    }
                    // Sync initial value from original or last known state
                    const currentVal = lastSyncedValue ?? originalInput?.value ?? '';
                    setOriginalInputValue(currentVal); // Sync original (programmatic)
                    if (customInput) customInput.value = currentVal; // Sync custom input UI

                } else { // Disabling
                    if (topBarControls.container) scheduleStyleUpdate(topBarControls.container, { display: 'none' });
                    // Detach listeners
                    if (customInputListenerAttached) {
                        topBarControls.prevBtn?.removeEventListener('click', () => handleHistoryNavigation(1));
                        topBarControls.nextBtn?.removeEventListener('click', () => handleHistoryNavigation(-1));
                        topBarControls.clearBtn?.removeEventListener('click', handleClear);
                        topBarControls.input?.removeEventListener('input', handleCustomInputChange);
                        if (customInputSearchTimeoutId) clearTimeout(customInputSearchTimeoutId);
                        customInputSearchTimeoutId = null;
                        customInputListenerAttached = false;
                    }
                    customInput = null; // Clear ref
                }
                // Re-run setup logic for related features (history, DOM observer)
                setupInputListeners(originalInput);
                setupDomObserver();
                break;

            case 'historyPanel':
                if (isEnabled) {
                    if (!historyPanel) createHistoryPanel();
                    if (historyPanel) scheduleStyleUpdate(historyPanel, { display: 'flex' });
                    if (historyListElement) {
                        // Attach listeners if not attached
                        if (!historyListClickListenerAttached) {
                            historyListElement.addEventListener('click', handleHistoryListClick);
                            historyListClickListenerAttached = true;
                        }
                        if (!historyListDblClickListenerAttached) {
                             historyListElement.addEventListener('dblclick', handleHistoryListDblClick);
                             historyListDblClickListenerAttached = true;
                        }
                        if (!historyItemKeyListenerAttached) {
                             // Attach to body with capture to catch events early if list items are dynamically changing
                             document.body.addEventListener('keydown', handleHistoryItemHighlightKey, true);
                             historyItemKeyListenerAttached = true;
                        }
                    }
                    historyManager.updatePanel(); // Initial population/update
                } else { // Disabling
                    if (historyPanel) scheduleStyleUpdate(historyPanel, { display: 'none' });
                    // Detach listeners
                    if (historyListElement) {
                        if (historyListClickListenerAttached) {
                             historyListElement.removeEventListener('click', handleHistoryListClick);
                             historyListClickListenerAttached = false;
                        }
                        if (historyListDblClickListenerAttached) {
                             historyListElement.removeEventListener('dblclick', handleHistoryListDblClick);
                             historyListDblClickListenerAttached = false;
                        }
                        if (historyItemKeyListenerAttached) {
                             document.body.removeEventListener('keydown', handleHistoryItemHighlightKey, true);
                             historyItemKeyListenerAttached = false;
                        }
                    }
                    // Clear persistent highlight state when panel is disabled
                    persistHighlightedTerm = null; persistHighlightedIndex = null;
                }
                // Re-run setup logic for related features
                setupInputListeners(originalInput);
                setupDomObserver();
                break;

            case 'pushContent':
                try {
                    const pcConfig = config.pushContent;
                    const contentElement = document.querySelector(pcConfig.contentSelector);
                    if (contentElement) {
                        if (isEnabled) {
                            contentElement.classList.add(pcConfig.pushClass);
                        } else {
                            contentElement.classList.remove(pcConfig.pushClass);
                        }
                    }
                } catch (e) { /* silenced */ }
                break;

            case 'selectSearchPopup':
            case 'transferPasteCopy':
            case 'transferPasteCut':
                const anyGroupButtonEnabled = isFeatureEnabled('selectSearchPopup') || isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut');
                const anyPasteEnabled = isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut');

                if (anyGroupButtonEnabled || anyPasteEnabled) {
                    // Ensure required buttons exist if their features are enabled
                    if (isFeatureEnabled('selectSearchPopup') && !popupElement) createSelectPopup();
                    // Create TP buttons with dummy handlers just to ensure they exist for size calc etc.
                    // Real handlers are attached/used in showSelectionActionButtons / tp_showPasteButton
                    if (isFeatureEnabled('transferPasteCopy') && !tp_triggerButtonRef.element) {
                         tp_triggerButtonRef.element = tp_createButton(config.transferPaste.triggerButtonId, config.transferPaste.triggerButtonText, config.transferPaste.btnCopyClass, ()=>{});
                    }
                    if (isFeatureEnabled('transferPasteCut') && !tp_cutButtonRef.element) {
                         tp_cutButtonRef.element = tp_createButton(config.transferPaste.cutButtonId, config.transferPaste.cutButtonText, config.transferPaste.btnCutClass, ()=>{});
                    }
                    if (anyPasteEnabled && !tp_pasteButtonRef.element) {
                         tp_pasteButtonRef.element = tp_createButton(config.transferPaste.pasteButtonId, config.transferPaste.pasteButtonText, config.transferPaste.btnPasteClass, ()=>{});
                    }

                    // Attach global mouse listeners if not already attached
                    if (!selectPopupListenersAttached) {
                        try {
                            document.addEventListener('mousedown', handleMouseDownPopup, true); // Capture phase
                            document.addEventListener('mouseup', handleMouseUpSelectionEnd, true); // Capture for selection end
                            document.addEventListener('mouseup', tp_handleMouseUp, true);      // Capture for paste button show
                            selectPopupListenersAttached = true;
                        } catch (e) { /* silenced */ }
                    }
                } else { // No relevant features enabled
                    // Detach global mouse listeners
                    if (selectPopupListenersAttached) {
                        try {
                            document.removeEventListener('mousedown', handleMouseDownPopup, true);
                            document.removeEventListener('mouseup', handleMouseUpSelectionEnd, true);
                            document.removeEventListener('mouseup', tp_handleMouseUp, true);
                            selectPopupListenersAttached = false;
                        } catch (e) { /* silenced */ }
                    }
                    // Hide any potentially visible buttons
                    hideSelectionActionButtons();
                    tp_hidePasteButton();
                }

                // Attach/detach editor-specific listeners based on Copy/Cut enable state
                if (anyPasteEnabled) {
                    tp_attachListeners();
                } else {
                    tp_detachListeners();
                }

                // Explicitly hide buttons if the specific feature being toggled is disabled
                if (!isEnabled) {
                     hideSelectionActionButtons();
                     if (!anyPasteEnabled) { // Hide paste button only if BOTH copy and cut are off
                        tp_hidePasteButton();
                     }
                }
                break;

            case 'copyTagOnHover':
                if (isEnabled) {
                    ct_createElements(); // Ensure elements exist
                    // Attach listener if not already attached
                    if (!copyTagListenerAttached) {
                        // Attach to a stable parent container if possible, fallback to body
                        const target = document.querySelector(config.selectors.copyTagParentContainer) || document.body;
                        if (target) {
                            try {
                                // Use passive listener for mouseover performance
                                target.addEventListener('mouseover', ct_handleMouseOver, { passive: true });
                                ct_listenerTarget = target; // Store target for removal
                                copyTagListenerAttached = true;
                            } catch (e) { ct_listenerTarget = null; }
                        }
                    }
                } else { // Disabling
                    ct_hideCopyPopupImmediately(true); // Hide UI
                    ct_hideFeedbackIndicator();
                    // Detach listener
                    if (copyTagListenerAttached && ct_listenerTarget) {
                        try { ct_listenerTarget.removeEventListener('mouseover', ct_handleMouseOver); }
                        catch (e) { /* silenced */ }
                        copyTagListenerAttached = false;
                        ct_listenerTarget = null;
                    } else { // Fallback if target somehow became null
                         if (copyTagListenerAttached) copyTagListenerAttached = false;
                         ct_listenerTarget = null;
                    }
                    // Reset state
                    ct_currentHoveredTag = null; ct_currentTagText = '';
                    clearTimeout(ct_showTimeout); ct_showTimeout = null;
                    clearTimeout(ct_hideTimeout); ct_hideTimeout = null;
                    clearTimeout(ct_feedbackTimeout); ct_feedbackTimeout = null;
                }
                break;
        }
    }

    // --- [ ☆ DOM Observer Setup ☆ ] ---
    /** Sets up or disconnects the MutationObserver based on whether history tracking is needed. */
    function setupDomObserver() {
        const needsObserver = isHistoryTrackingNeeded();

        if (needsObserver && !domObserver) {
            // Find the target element to observe (search container or body)
            const target = document.querySelector(config.selectors.domObserverTarget) || document.body;
            if (target) {
                domObserver = new MutationObserver((mutations) => {
                    // Re-check if observer is still needed inside the callback
                    if (!isHistoryTrackingNeeded()) { disconnectDomObserver(); return; }
                    // Ignore mutations during simulated click
                    if (isSimulatingClick) return;

                    // Check if mutations are relevant (child changes, input disabled change, input removed)
                    let relevant = mutations.some(m => {
                        if (m.type === 'childList') return true; // Added/removed nodes
                        if (m.type === 'attributes' && m.target === originalInput && m.attributeName === 'disabled') return true; // Input disabled state changed
                         // Check if the original input node was removed
                        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(); // Check for input changes
                    }
                });

                domObserver.observe(target, {
                    childList: true,    // Detect added/removed nodes (like the input itself)
                    subtree: true,      // Check within the target's descendants
                    attributes: true,   // Detect attribute changes
                    attributeFilter: ['disabled'] // Specifically watch the 'disabled' attribute on the input
                });
            } else {
                // console.warn("MubuPlus: DOM Observer target not found.");
            }
        } else if (!needsObserver && domObserver) {
            disconnectDomObserver(); // Disconnect if no longer needed
        }
    }

    function disconnectDomObserver() {
        if (domObserver) {
            try { domObserver.disconnect(); } catch (e) {}
            domObserver = null;
        }
    }

    // --- [ ☆ Initialization ☆ ] ---
    function init() {
        try {
            // Basic environment checks (optional, can be removed if not critical)
            if (!nativeInputValueSetter) console.warn("MubuPlus: Native input setter not found.");
            if (isFeatureEnabled('copyTagOnHover') && (!navigator.clipboard?.writeText)) console.warn("MubuPlus: Clipboard API unavailable for Copy Tag.");
            if ((isFeatureEnabled('transferPasteCopy') || isFeatureEnabled('transferPasteCut')) && (!document.execCommand)) console.warn("MubuPlus: document.execCommand unavailable for Transfer Paste.");

            // --- CSS Injection ---
            // Inject all CSS rules needed for the UI elements
            let combinedCSS = `
                /* Top Search Bar */
                #${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} .custom-search-input:disabled { background:#eee; cursor:not-allowed; opacity:0.7; }
                #${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; }

                /* History Panel */
                #${config.sync.historyPanelId} { position:fixed; top:460px; left:0px; transform:translateY(-50%); z-index:10000; width:152px; max-height:436px; 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 4px 0; /* Reduced bottom padding slightly */ 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 0 4px 0; /* Add small bottom margin */ flex-grow:1; overflow-y:auto; 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; }

                /* History Item & Delete Button */
                #${config.sync.historyPanelId} .search-history-item { position: relative; padding: 6px 10px 6px 28px; /* Padding: top right bottom left */ 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.15s ease-in-out, color 0.15s ease-in-out; box-sizing: border-box; }
                #${config.sync.historyPanelId} .search-history-item-text { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; }
                #${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; }
                #${config.sync.historyPanelId} .${config.sync.historyItemDeleteBtnClass} { position: absolute; left: 6px; top: 50%; transform: translateY(-50%); display: none; cursor: pointer; color: #aaa; font-size: 14px; line-height: 1; padding: 3px 5px; border-radius: 3px; background: rgba(0,0,0,0.02); z-index: 1; transition: color 0.15s ease, background-color 0.15s ease; }
                #${config.sync.historyPanelId} .search-history-item:hover .${config.sync.historyItemDeleteBtnClass} { display: inline-block; }
                #${config.sync.historyPanelId} .search-history-item .${config.sync.historyItemDeleteBtnClass}:hover { color: #f55; background: rgba(0,0,0,0.08); }
                #${config.sync.historyPanelId} .search-history-item--persist-highlight { background-color:${config.sync.persistHighlightBgColor} !important; color:#333 !important; font-weight: bold; }

                /* History "Selective Clear" Button */
                #${config.sync.historyPanelId} .history-clear-all-button { display: block; width: calc(100% - 16px); margin: 8px auto 4px auto; padding: 5px 10px; font-size: 12px; text-align: center; cursor: pointer; background-color: #f8f9fa; border: 1px solid #dcdfe6; color: #dc3545; border-radius: 4px; transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, opacity 0.2s ease; flex-shrink: 0; opacity: 1; }
                #${config.sync.historyPanelId} .history-clear-all-button:hover:not(:disabled) { background-color: #e9ecef; border-color: #c0c4cc; color: #a0202e; }
                #${config.sync.historyPanelId} .history-clear-all-button:disabled { color: #adb5bd; background-color: #f1f3f5; border-color: #e9ecef; cursor: not-allowed; opacity: 0.6; }

                /* History Multi-Select Highlight */
                #${config.sync.historyPanelId} .search-history-item--multi-selected { background-color: #e0f2e0 !important; color: #0b6e0b !important; font-weight: normal !important; border-left: 3px solid #4CAF50; padding-left: 25px; /* Adjust padding for border */ }
                #${config.sync.historyPanelId} .search-history-item--multi-selected:hover { background-color: #c8e8c8 !important; }
                #${config.sync.historyPanelId} .search-history-item--multi-selected.search-history-item--active { background-color: #c8e8c8 !important; color: #005000 !important; font-weight: 500 !important; }
                #${config.sync.historyPanelId} .search-history-item--multi-selected.search-history-item--persist-highlight { background-color: #c8e8c8 !important; color: #005000 !important; font-weight: bold !important; }
                #${config.sync.historyPanelId} .search-history-item--multi-selected .${config.sync.historyItemDeleteBtnClass} { left: 3px; /* Adjust delete button position for border */ }

                /* Select Search Popup Button */
                .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; }

                /* Copy Tag Popup & Feedback */
                #${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; }

                /* Transfer Paste Buttons (Base & Specific) */
                .${config.transferPaste.cssPrefix}${config.transferPaste.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; }
                .${config.transferPaste.cssPrefix}${config.transferPaste.btnCopyClass} { background-color:#5856d5; }
                .${config.transferPaste.cssPrefix}${config.transferPaste.btnCopyClass}:hover { background-color:#4a48b3; }
                .${config.transferPaste.cssPrefix}${config.transferPaste.btnCutClass} { background-color:#d55856; }
                .${config.transferPaste.cssPrefix}${config.transferPaste.btnCutClass}:hover { background-color:#b34a48; }
                .${config.transferPaste.cssPrefix}${config.transferPaste.btnPasteClass} { background-color:#5856d5; }
                .${config.transferPaste.cssPrefix}${config.transferPaste.btnPasteClass}:hover { background-color:#4a48b3; }

                /* Feature Toggle Panel */
                #${config.togglePanel.triggerId} { position:fixed; bottom:0; right:0; width:${config.togglePanel.triggerWidth}px; height:${config.togglePanel.triggerHeight}px; background:rgba(0,0,0,0.01); cursor:pointer; z-index:19998; border-top-left-radius:5px; }
                #${config.togglePanel.panelId} { position:fixed; bottom:0; right:0; width:${config.togglePanel.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; }
                #${config.togglePanel.panelId}::-webkit-scrollbar { width:6px; }
                #${config.togglePanel.panelId}::-webkit-scrollbar-track { background:#eee; border-radius:3px; }
                #${config.togglePanel.panelId}::-webkit-scrollbar-thumb { background-color:#bbb; border-radius:3px; }
                #${config.togglePanel.triggerId}:hover + #${config.togglePanel.panelId}, #${config.togglePanel.panelId}:hover { transform:translateX(0); }
                #${config.togglePanel.panelId} .toggle-panel-title { font-weight:bold; margin-bottom:10px; padding-bottom:5px; border-bottom:1px solid #eee; text-align:center; }
                #${config.togglePanel.panelId} .toggle-control { display:flex; align-items:center; margin-bottom:8px; cursor:pointer; }
                #${config.togglePanel.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; }
                #${config.togglePanel.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); }
                #${config.togglePanel.panelId} .toggle-control input[type="checkbox"]:checked { background-color:#5856d5; }
                #${config.togglePanel.panelId} .toggle-control input[type="checkbox"]:checked::before { left:18px; }
                #${config.togglePanel.panelId} .toggle-control label { flex-grow:1; user-select:none; cursor:pointer; }

                 /* Push Content */
                ${config.pushContent.contentSelector} { transition: margin-left ${config.pushContent.transitionDuration} ease-in-out !important; box-sizing: border-box; }
                ${config.pushContent.contentSelector}.${config.pushContent.pushClass} { margin-left: ${config.pushContent.pushMarginLeft}px !important; }
            `;
            if (combinedCSS) { try { GM_addStyle(combinedCSS); } catch (e) { console.error("MubuPlus: Failed to inject CSS.", e); } }

            // Create UI Elements (they are initially hidden by CSS/logic)
            createControlPanel();
            createHistoryPanel();
            createTogglePanel();
            // Select Popup, Copy Tag, TP buttons are created on demand or by applyFeatureStateChange

            // Initialize subsystems
            tp_initialize(); // Start looking for the editor container

            // Find initial input & setup listeners EARLY
            const initialInput = optimizedFindSearchBox();
            if (initialInput) {
                originalInput = initialInput;
                lastSyncedValue = initialInput.value;
                setupInputListeners(originalInput);
                // Add initial value to history if present and tracking needed
                if (lastSyncedValue && isHistoryTrackingNeeded()) {
                    updateCustomInputAndAddHistory(lastSyncedValue, 'init_load');
                } else if (isFeatureEnabled('historyPanel')) {
                    // Update panel state even if input is empty initially
                    historyManager.updatePanel();
                }
            } else {
                // console.warn("MubuPlus: Original search input not found on initial load.");
            }

            // Setup DOM observer to watch for input changes/appearance
            setupDomObserver();

            // Schedule the check for Mubu's internal search API
            setTimeout(checkMubuInterface, config.interfaceCheckDelay);

            // Apply initial feature states based on default settings
            let initialEnabledCount = 0;
            for (const key in runtimeFeatureState) {
                if (runtimeFeatureState[key]) { // If feature is enabled by default
                    try {
                        applyFeatureStateChange(key, true); // Activate the feature
                        initialEnabledCount++;
                    } catch (applyError) {
                        console.error(`MubuPlus: Error applying initial state for feature "${key}":`, applyError);
                    }
                }
            }
            // console.log(`MubuPlus: Initialized with ${initialEnabledCount} features enabled.`);

            // Add unload listener for cleanup
            window.addEventListener('unload', cleanup);

        } catch (initError) {
            console.error("MubuPlus: Initialization failed.", initError);
        }
    }

    // --- [ ☆ Cleanup ☆ ] ---
    function cleanup() {
        // console.log("MubuPlus: Cleaning up...");
        window.removeEventListener('unload', cleanup);

        // Disconnect observers
        try { disconnectDomObserver(); } catch(e){}
        try { observerInstance.disconnect(); } catch(e){}
        elementObserverMap.clear();

        // Remove listeners
        try { teardownInputListeners(originalInput); } catch(e){}
        if (customInputListenerAttached) {
            try { topBarControls.prevBtn?.removeEventListener('click', () => handleHistoryNavigation(1)); } catch(e){}
            try { topBarControls.nextBtn?.removeEventListener('click', () => handleHistoryNavigation(-1)); } catch(e){}
            try { topBarControls.clearBtn?.removeEventListener('click', handleClear); } catch(e){}
            try { topBarControls.input?.removeEventListener('input', handleCustomInputChange); } catch(e){}
            customInputListenerAttached = false;
        }
        if (historyListClickListenerAttached) {
            try { historyListElement?.removeEventListener('click', handleHistoryListClick); } catch(e){}
            historyListClickListenerAttached = false;
        }
        if (historyListDblClickListenerAttached) {
             try { historyListElement?.removeEventListener('dblclick', handleHistoryListDblClick); } catch(e){}
             historyListDblClickListenerAttached = false;
        }
        if (historyItemKeyListenerAttached) {
            try { document.body.removeEventListener('keydown', handleHistoryItemHighlightKey, true); } catch(e){}
            historyItemKeyListenerAttached = false;
        }
        try {
            const clearAllBtn = document.getElementById('history-clear-all-btn');
            clearAllBtn?.removeEventListener('click', handleSelectiveHistoryClear);
        } catch (e) { /* silenced */ }
        if (selectPopupListenersAttached) {
            try { document.removeEventListener('mousedown', handleMouseDownPopup, true); } catch(e){}
            try { document.removeEventListener('mouseup', handleMouseUpSelectionEnd, true); } catch(e){}
            try { document.removeEventListener('mouseup', tp_handleMouseUp, true); } catch(e){}
            selectPopupListenersAttached = false;
        }
        if (copyTagListenerAttached && ct_listenerTarget) {
            try { ct_listenerTarget.removeEventListener('mouseover', ct_handleMouseOver); } catch(e){}
            copyTagListenerAttached = false; ct_listenerTarget = null;
        }
        try { tp_detachListeners(); } catch(e){}
        try { toggleTriggerElement?.removeEventListener('mouseenter', showTogglePanel); } catch(e){}
        try { toggleTriggerElement?.removeEventListener('mouseleave', scheduleHideTogglePanel); } catch(e){}
        try { togglePanelElement?.removeEventListener('mouseenter', showTogglePanel); } catch(e){}
        try { togglePanelElement?.removeEventListener('mouseleave', scheduleHideTogglePanel); } catch(e){}


        // Clear timeouts
        clearTimeout(customInputSearchTimeoutId);
        clearTimeout(togglePanelHideTimeout);
        clearTimeout(ct_showTimeout);
        clearTimeout(ct_hideTimeout);
        clearTimeout(ct_feedbackTimeout);
        clearTimeout(interfaceCheckTimer);

        // Hide UI elements immediately
        hideSelectionActionButtons();
        tp_hidePasteButton();
        ct_hideCopyPopupImmediately(true);
        ct_hideFeedbackIndicator();
        hideTogglePanel();
        try {
            const pcConfig = config.pushContent;
            const contentElement = document.querySelector(pcConfig.contentSelector);
            contentElement?.classList.remove(pcConfig.pushClass); // Remove push class
        } catch (e) { /* silenced */ }

        // Schedule removal of dynamically added elements after a short delay
        // This helps prevent errors if other scripts are still interacting
        setTimeout(() => {
            try { unobserveElementResize(popupElement); popupElement?.remove(); } catch (e) { }
            try { unobserveElementResize(tp_triggerButtonRef.element); tp_triggerButtonRef.element?.remove(); } catch (e) { }
            try { unobserveElementResize(tp_cutButtonRef.element); tp_cutButtonRef.element?.remove(); } catch (e) { }
            try { unobserveElementResize(tp_pasteButtonRef.element); tp_pasteButtonRef.element?.remove(); } catch (e) { }
            try { unobserveElementResize(ct_copyPopupElement); ct_copyPopupElement?.remove(); } catch (e) { }
            try { unobserveElementResize(ct_feedbackElement); ct_feedbackElement?.remove(); } catch (e) { }
            try { unobserveElementResize(topBarControls.container); topBarControls.container?.remove(); } catch (e) { }
            try { unobserveElementResize(historyPanel); historyPanel?.remove(); } catch (e) { }
            try { toggleTriggerElement?.remove(); } catch (e) { }
            try { togglePanelElement?.remove(); } catch (e) { }
        }, 200); // Delay removal slightly

        // Reset state variables
        isRafScheduled = false; styleUpdateQueue = [];
        originalInput = null; lastSyncedValue = null; isSyncing = false; customInput = null;
        originalInputHistoryHandler = null;
        topBarControls = { container: null, input: null, prevBtn: null, nextBtn: null, clearBtn: null };
        historyPanel = null; historyListElement = null; activeHistoryItemElement = null;
        isSimulatingClick = false; persistHighlightedTerm = null; persistHighlightedIndex = null;
        isInterfaceAvailable = false; interfaceCheckTimer = null; customInputSearchTimeoutId = null;
        isProgrammaticValueChange = false; 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;
        tp_storedHTML = ''; tp_storedText = ''; tp_ctrlApressed = false; tp_listenersAttached = false;
        togglePanelElement = null; toggleTriggerElement = null; togglePanelHideTimeout = null;
    }

    // --- Initialization Trigger ---
    // Wait for the document to be ready, then initialize after a delay
    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        setTimeout(init, config.initDelay);
    } else {
        window.addEventListener('DOMContentLoaded', () => setTimeout(init, config.initDelay), { once: true });
    }

})();