🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

拯救deric的懒癌

按角色存储 + 全技能整合收藏 + 多选筛选 + 完整物品卡片(支持跨技能拖拽自定义排序)+ Tab记忆 + 一键清除

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         拯救deric的懒癌
// @namespace    https://www.milkywayidle.com/
// @version      0.512
// @description  按角色存储 + 全技能整合收藏 + 多选筛选 +  完整物品卡片(支持跨技能拖拽自定义排序)+ Tab记忆 + 一键清除
// @author       baozhi powerby_Grok
// @match        https://www.milkywayidle.com/*
// @match        https://www.milkywayidlecn.com/*
// @match        https://test.milkywayidle.com/*
// @grant        none
// @icon         https://www.milkywayidle.com/favicon.svg
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 1. 扩展允许的技能列表,新增 '挤奶' 和 '伐木'
    const allowedSkills = ['采摘', '奶酪锻造', '制作', '缝纫', '烹饪', '冲泡', '挤奶', '伐木'];

    // 统一存储键
    const MWI_MAIN_STORAGE_KEY = 'mwi_main_data_';

    // 旧的存储键(用于数据迁移)
    const OLD_STORAGE_KEY = 'mwi_all_favorites_';
    const OLD_LAST_TAB_KEY = 'mwi_last_tabs_';
    const OLD_SELECTED_SKILLS_KEY = 'mwi_selected_skills_';
    const CACHE_KEY = 'mwi_item_cache_';

    const PENDING_CLICK_KEY = 'mwi_pending_global_click';

    let currentCharacterId = null;
    let selectedSkills = new Set();
    let isProcessingClick = false;
    let updateTimeout;
    let tabRestoreTimeout;
    let lastObservedSkill = null; // V4.1: 新增状态跟踪,用于判断是否为新的技能面板加载

    // ==================== 辅助函数:防抖 ====================
    /**
     * 防抖更新 UI,用于合并短时间内的多次收藏/取消收藏操作。
     */
    function debounceUpdate() {
        clearTimeout(updateTimeout);
        updateTimeout = setTimeout(() => {
            updateFavoritesPanelIfOpen();
            updateAllFavoriteButtons();
        }, 200);
    }

    // ==================== 角色ID获取 ====================
    function hookCharacterId() {
        const originalGet = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data").get;
        function hookedGet() {
            const result = originalGet.call(this);
            if (this.currentTarget instanceof WebSocket) {
                try {
                    const msg = JSON.parse(result);
                    if (msg?.type === "init_character_data" && msg.character?.id) {
                        const newId = msg.character.id.toString();
                        if (newId !== currentCharacterId) {
                            currentCharacterId = newId;
                            setTimeout(() => {
                                selectedSkills = new Set(getSelectedSkills());
                                debounceUpdate();
                            }, 300);
                        }
                    }
                } catch (e) { /* ignore */ }
            }
            return result;
        }
        Object.defineProperty(MessageEvent.prototype, "data", {
            get: hookedGet,
            configurable: true
        });
    }

    function getCharacterId() {
        if (currentCharacterId) return currentCharacterId;
        if (window.mwi?.character?.id) return window.mwi.character.id.toString();
        return 'default';
    }

    // ==================== 统一存储操作 (主存储) ====================
    function getMainStorageKey() {
        return `${MWI_MAIN_STORAGE_KEY}${getCharacterId()}`;
    }

    // 将 allFavorites 结构根据 globalOrder 同步,以确保数据一致性 (谁拥有哪个物品)
    function syncFavoritesToGlobalOrder(data) {
        // 1. 映射所有现有物品到它们的技能
        const itemToSkillMap = {};
        for (const skill in data.allFavorites) {
            data.allFavorites[skill].forEach(name => {
                itemToSkillMap[name] = skill;
            });
        }

        // 2. 清空 allFavorites 并根据 globalOrder 和 itemToSkillMap 重建
        const newFavorites = {};
        allowedSkills.forEach(skill => newFavorites[skill] = []);

        data.globalOrder.forEach(name => {
            const skill = itemToSkillMap[name]; // 使用物品的原始技能
            if (skill && allowedSkills.includes(skill)) {
                newFavorites[skill].push(name);
            }
        });

        data.allFavorites = newFavorites;
    }

    // 获取所有角色数据,并处理迁移和 globalOrder 初始化
    function getCharacterData() {
        const key = getMainStorageKey();
        let data = {
            allFavorites: {},
            globalOrder: [],
            lastTabs: {},
            selectedSkills: []
        };

        try {
            const raw = localStorage.getItem(key);
            if (raw) {
                data = JSON.parse(raw);
            }
        } catch (e) { /* ignore */ }

        allowedSkills.forEach(skill => {
            data.allFavorites[skill] = data.allFavorites[skill] || [];
        });

        data.globalOrder = data.globalOrder || [];

        const isMigrated = migrateOldData(data);

        // 如果 globalOrder 是空的,则用 allFavorites 初始化它 (保持 skill -> item 顺序)
        if (data.globalOrder.length === 0) {
            let uniqueItems = new Set();
            allowedSkills.forEach(skill => {
                (data.allFavorites[skill] || []).forEach(name => {
                    if (!uniqueItems.has(name)) {
                        data.globalOrder.push(name);
                        uniqueItems.add(name);
                    }
                });
            });
            if (data.globalOrder.length > 0) {
                 setCharacterData(data);
            }
        } else if (isMigrated) {
            syncFavoritesToGlobalOrder(data);
            setCharacterData(data);
        }

        return data;
    }

    function setCharacterData(data) {
        localStorage.setItem(getMainStorageKey(), JSON.stringify(data));
    }

    function migrateOldData(data) {
        let isMigrated = false;

        const oldFavs = localStorage.getItem(`${OLD_STORAGE_KEY}${getCharacterId()}`);
        if (oldFavs) {
            try {
                const parsed = JSON.parse(oldFavs);
                const fullFavorites = {};
                allowedSkills.forEach(skill => {
                    fullFavorites[skill] = parsed[skill] || [];
                });
                data.allFavorites = fullFavorites;
                localStorage.removeItem(`${OLD_STORAGE_KEY}${getCharacterId()}`);
                isMigrated = true;
            } catch (e) { /* ignore */ }
        }

        const oldSelected = localStorage.getItem(`${OLD_SELECTED_SKILLS_KEY}${getCharacterId()}`);
        if (oldSelected) {
            try {
                data.selectedSkills = JSON.parse(oldSelected);
                localStorage.removeItem(`${OLD_SELECTED_SKILLS_KEY}${getCharacterId()}`);
                isMigrated = true;
            } catch (e) { /* ignore */ }
        }

        const oldTabs = localStorage.getItem(`${OLD_LAST_TAB_KEY}${getCharacterId()}`);
        if (oldTabs) {
            try {
                data.lastTabs = JSON.parse(oldTabs);
                localStorage.removeItem(`${OLD_LAST_TAB_KEY}${getCharacterId()}`);
                isMigrated = true;
            } catch (e) { /* ignore */ }
        }

        return isMigrated;
    }

    // ==================== 全局排序操作 ====================

    function getGlobalOrder() {
        return getCharacterData().globalOrder;
    }

    function setGlobalOrder(newOrderArray) {
        const data = getCharacterData();
        data.globalOrder = newOrderArray;
        syncFavoritesToGlobalOrder(data);
        setCharacterData(data);
    }

    function findItemSkill(itemName) {
        const allFavorites = getCharacterData().allFavorites;
        for (const [skill, items] of Object.entries(allFavorites)) {
            if (items.includes(itemName)) {
                return skill;
            }
        }
        return null;
    }

    function getFavoritesForSkill(skill) {
        return getCharacterData().allFavorites[skill] || [];
    }

    function setFavoritesForSkill(skill, arr) {
        const data = getCharacterData();
        data.allFavorites[skill] = arr;
        setCharacterData(data);
    }

    function addToGlobalOrder(name) {
        const data = getCharacterData();
        if (!data.globalOrder.includes(name)) {
            data.globalOrder.push(name);
            syncFavoritesToGlobalOrder(data);
            setCharacterData(data);
        }
    }

    function removeFromGlobalOrder(name) {
        const data = getCharacterData();
        const index = data.globalOrder.indexOf(name);
        if (index > -1) {
            data.globalOrder.splice(index, 1);
            syncFavoritesToGlobalOrder(data);
            setCharacterData(data);
        }
    }

    function saveSelectedSkills(skills) {
        const data = getCharacterData();
        data.selectedSkills = skills;
        setCharacterData(data);
    }

    function getSelectedSkills() {
        const data = getCharacterData();

        const allFavorites = data.allFavorites;
        const skillsWithFavorites = allowedSkills.filter(skill => (allFavorites[skill]?.length || 0) > 0);

        let validSkills = [];
        if (data.selectedSkills && data.selectedSkills.length > 0) {
            validSkills = data.selectedSkills.filter(skill => skillsWithFavorites.includes(skill));
        }

        if (validSkills.length === 0 && skillsWithFavorites.length > 0) {
            validSkills = skillsWithFavorites;
        }

        return validSkills;
    }

    // ==================== 物品卡片缓存 (独立存储) ====================

    function getCacheKey() {
        return `${CACHE_KEY}${getCharacterId()}`;
    }

    function cacheItemCard(item, skill) {
        const nameEl = item.querySelector('.SkillAction_name__2VPXa');
        if (!nameEl) return;
        const name = nameEl.textContent.trim();

        const isFavorited = !!findItemSkill(name);

        if (!isFavorited) return;

        const cleanItem = item.cloneNode(true);
        cleanItem.querySelector('.mwi-fav-btn')?.remove();

        const clone = cleanItem.cloneNode(true);

        try {
            const cache = JSON.parse(localStorage.getItem(getCacheKey()) || '{}');
            if (!cache[skill]) cache[skill] = {};
            cache[skill][name] = clone.outerHTML;
            localStorage.setItem(getCacheKey(), JSON.stringify(cache));
        } catch (e) { /* ignore */ }
    }

    function getCachedItem(skill, name) {
        try {
            const cache = JSON.parse(localStorage.getItem(getCacheKey()) || '{}');
            let cachedHtml = cache[skill]?.[name] || null;
            if (!cachedHtml) {
                for (const sk in cache) {
                    if (cache[sk][name]) {
                        cachedHtml = cache[sk][name];
                        break;
                    }
                }
            }
            return cachedHtml;
        } catch (e) {
            return null;
        }
    }

    function removeCacheForItem(skill, name) {
        try {
            const cache = JSON.parse(localStorage.getItem(getCacheKey()) || '{}');
            if (cache[skill] && cache[skill][name]) {
                delete cache[skill][name];
                localStorage.setItem(getCacheKey(), JSON.stringify(cache));
            }
        } catch (e) { /* ignore */ }
    }

    // ==================== 收藏按钮(技能页面)====================
    function addFavoriteButton(item, currentSkill) {
        if (item.querySelector('.mwi-fav-btn')) return;
        const nameEl = item.querySelector('.SkillAction_name__2VPXa');
        if (!nameEl) return;
        const name = nameEl.textContent.trim();

        const btn = document.createElement('button');
        btn.className = 'mwi-fav-btn';

        let itemSkill = findItemSkill(name);
        const isFavorited = !!itemSkill;

        btn.textContent = isFavorited ? '⭐' : '☆';

        Object.assign(btn.style, {
            position: 'absolute', top: '4px', right: '4px', background: 'none',
            border: 'none', fontSize: '18px', cursor: 'pointer', zIndex: '10',
            color: isFavorited ? 'rgb(255, 215, 0)' : 'rgb(170, 170, 170)'
        });

        btn.onclick = e => {
            e.stopPropagation();

            const itemSkill = findItemSkill(name);

            if (itemSkill) {
                // 取消收藏
                const arr = getFavoritesForSkill(itemSkill);
                const idx = arr.indexOf(name);
                if (idx > -1) {
                    arr.splice(idx, 1);
                    setFavoritesForSkill(itemSkill, arr);
                    removeFromGlobalOrder(name);
                    removeCacheForItem(itemSkill, name);

                    btn.textContent = '☆';
                    btn.style.color = 'rgb(170, 170, 170)';

                    debounceUpdate();
                    updateOtherSkillFavoriteButtons(name, false);
                }
            } else {
                // 添加收藏到当前技能
                const arr = getFavoritesForSkill(currentSkill);
                if (!arr.includes(name)) {
                    arr.push(name);
                    setFavoritesForSkill(currentSkill, arr);
                    addToGlobalOrder(name);

                    cacheItemCard(item, currentSkill);

                    btn.textContent = '⭐';
                    btn.style.color = 'rgb(255, 215, 0)';

                    debounceUpdate();
                }
            }
        };

        item.style.position = 'relative';
        item.appendChild(btn);
    }

    // 更新其他技能的收藏按钮状态
    function updateOtherSkillFavoriteButtons(itemName, isFavorited = null) {
        if (isFavorited === null) {
            isFavorited = !!findItemSkill(itemName);
        }

        const allItems = document.querySelectorAll('.SkillAction_skillAction__1esCp:not(.SkillAction_opaque__s9Yeq)');

        allItems.forEach(item => {
            const nameEl = item.querySelector('.SkillAction_name__2VPXa');
            if (nameEl && nameEl.textContent.trim() === itemName) {
                const btn = item.querySelector('.mwi-fav-btn');
                if (btn) {
                    btn.textContent = isFavorited ? '⭐' : '☆';
                    btn.style.color = isFavorited ? 'rgba(255, 254, 249, 1)' : 'rgb(170, 170, 170)';
                }
            }
        });
    }

    function updateAllFavoriteButtons() {
        const allItems = document.querySelectorAll('.SkillAction_skillAction__1esCp:not(.SkillAction_opaque__s9Yeq)');
        const currentSkill = document.querySelector('.NavigationBar_active__3R-QS .NavigationBar_label__1uH-y')?.textContent.trim();

        allItems.forEach(item => {
            const btn = item.querySelector('.mwi-fav-btn');
            if (btn) {
                const nameEl = item.querySelector('.SkillAction_name__2VPXa');
                if (nameEl) {
                    const name = nameEl.textContent.trim();
                    const isFavorited = !!findItemSkill(name);

                    btn.textContent = isFavorited ? '⭐' : '☆';
                    btn.style.color = isFavorited ? 'rgb(255, 215, 0)' : 'rgb(170, 170, 170)';

                    if (isFavorited && currentSkill && !getCachedItem(currentSkill, name)) {
                        setTimeout(() => {
                            cacheItemCard(item, currentSkill);
                        }, 10);
                    }
                }
            }
        });
    }

    // ==================== Tab 切换和跨技能跳转 ====================

    function getLastTabIndex(skill) {
        const data = getCharacterData();
        return data.lastTabs[skill] !== undefined ? data.lastTabs[skill] : 0;
    }

    function setLastTabIndex(skill, idx) {
        const data = getCharacterData();
        data.lastTabs[skill] = idx;
        setCharacterData(data);
    }

    /**
     * 为“挤奶”和“伐木”这种没有原生标签页的技能注入 Mui Tab 结构
     */
    function injectTabStructure(root, skill) {
        // 检查是否已经注入
        if (root.querySelector('.MuiTabs-root')) {
            return;
        }

        const tabsComponentContainer = root.querySelector('.GatheringProductionSkillPanel_tabsComponentContainer__3Ua1T');
        if (!tabsComponentContainer) return;

        const tabsComponent = tabsComponentContainer.querySelector('.TabsComponent_tabsComponent__3PqGp');
        const panelsContainer = tabsComponentContainer.querySelector('.TabsComponent_tabPanelsContainer__26mzo');

        if (!tabsComponent || !panelsContainer) return;

        // 1. 创建 Tab 结构
        const tabsContainer = document.createElement('div');
        tabsContainer.className = 'TabsComponent_tabsContainer__3BDUp TabsComponent_wrap__3fEC7';

        const muiTabsRoot = document.createElement('div');
        muiTabsRoot.className = 'MuiTabs-root css-orq8zk'; // 使用通用的 Mui Tabs 类

        const muiTabsScroller = document.createElement('div');
        muiTabsScroller.className = 'MuiTabs-scroller MuiTabs-fixed css-1anid1y';
        muiTabsScroller.style.overflow = 'hidden';
        muiTabsScroller.style.marginBottom = '0px';

        const muiTabsFlexContainer = document.createElement('div');
        muiTabsFlexContainer.className = 'MuiTabs-flexContainer css-k008qs';
        muiTabsFlexContainer.setAttribute('role', 'tablist');

        const indicator = document.createElement('span');
        indicator.className = 'MuiTabs-indicator css-ttwr4n';
        indicator.style.left = '0px';
        indicator.style.width = '0px';

        // 2. 创建第一个 Tab 按钮(原操作列表)
        const originalTabButton = document.createElement('button');
        originalTabButton.className = 'MuiButtonBase-root MuiTab-root MuiTab-textColorPrimary css-1q2h7u5 Mui-selected';
        originalTabButton.setAttribute('role', 'tab');
        originalTabButton.setAttribute('aria-selected', 'true');
        originalTabButton.setAttribute('tabindex', '0');
        // V3.9: 添加自定义标识
        originalTabButton.setAttribute('data-mwi-custom-tab', 'true');
        originalTabButton.innerHTML = `<span class="MuiBadge-root TabsComponent_badge__1Du26 css-1rzb3uu">${skill}<span class="MuiBadge-badge MuiBadge-standard MuiBadge-invisible MuiBadge-anchorOriginTopRight MuiBadge-anchorOriginTopRightRectangular MuiBadge-overlapRectangular css-vwo4eg"></span></span><span class="MuiTouchRipple-root css-w0pj6f"></span>`;

        // 3. 组装 Tab 栏
        muiTabsFlexContainer.appendChild(originalTabButton);
        muiTabsScroller.appendChild(muiTabsFlexContainer);
        muiTabsScroller.appendChild(indicator);
        muiTabsRoot.appendChild(muiTabsScroller);
        tabsContainer.appendChild(muiTabsRoot);

        // 4. 注入 Tab 栏结构
        tabsComponent.insertBefore(tabsContainer, panelsContainer);

        // 5. 调整原有的内容面板:使其成为第一个 Tab 的面板并显示
        const originalPanel = panelsContainer.querySelector('.TabPanel_tabPanel__tXMJF');
        if (originalPanel) {
            // 确保第一个面板可见
            originalPanel.classList.remove('TabPanel_hidden__26UM3');
        }
    }


    // 仅在有 Tab 结构的技能面板中创建“收藏”Tab
    function ensureFavoritesTab(tabsContainer, skill) {
        let favTab = tabsContainer.querySelector('.mwi-fav-tab');
        if (favTab) return;

        const tabsFlex = tabsContainer.querySelector('.MuiTabs-flexContainer');
        if (!tabsFlex) return;

        const tabsComponentContainer = tabsContainer.closest('.GatheringProductionSkillPanel_tabsComponentContainer__3Ua1T');
        if (!tabsComponentContainer) return;

        const panelsContainer = tabsComponentContainer.querySelector('.TabsComponent_tabPanelsContainer__26mzo');
        if (!panelsContainer) return;

        // --- 1. 创建 Tab 按钮 ---
        const tab = document.createElement('button');
        tab.className = 'MuiButtonBase-root MuiTab-root MuiTab-textColorPrimary css-1q2h7u5 mwi-fav-tab';
        tab.setAttribute('role', 'tab');
        tab.setAttribute('aria-selected', 'false');
        tab.setAttribute('tabindex', '-1');
        // V3.9: 添加自定义标识
        tab.setAttribute('data-mwi-custom-tab', 'true');
        tab.innerHTML = `<span style="position:relative; display:inline-flex; align-items:center;">收藏</span><span class="MuiTouchRipple-root css-w0pj6f"></span>`;
        tabsFlex.appendChild(tab);

        // --- 2. 创建 Tab 面板 ---
        const panel = document.createElement('div');
        panel.className = 'TabPanel_tabPanel__tXMJF TabPanel_hidden__26UM3 mwi-fav-panel';
        panel.style.padding = '24px';
        panel.style.position = 'relative';
        panelsContainer.appendChild(panel);
    }

    function switchToTab(skill, targetIdx) {
        const root = document.querySelector('.GatheringProductionSkillPanel_gatheringProductionSkillPanel__vG4M7');
        if (!root) return;

        const tabsContainer = root.querySelector('.MuiTabs-root');
        if (!tabsContainer) return;

        const tabsFlex = tabsContainer.querySelector('.MuiTabs-flexContainer');
        if (!tabsFlex) return;

        const tabs = Array.from(tabsFlex.children);
        const indicator = tabsContainer.querySelector('.MuiTabs-indicator');
        // 确保获取到所有面板,包括原生的和自定义的
        const panels = root.querySelectorAll('.TabsComponent_tabPanelsContainer__26mzo > .TabPanel_tabPanel__tXMJF');

        // 再次检查边界,防止出现越界错误
        if (targetIdx < 0 || targetIdx >= tabs.length) {
             console.error(`[MWI Fav] Invalid target index: ${targetIdx} for skill ${skill}`);
             return;
        }

        // 更新按钮状态
        tabs.forEach((t, i) => {
            t.classList.toggle('Mui-selected', i === targetIdx);
            t.setAttribute('aria-selected', i === targetIdx);
            t.setAttribute('tabindex', i === targetIdx ? '0' : '-1');
        });

        // 更新面板状态 (原生面板 + 收藏面板)
        panels.forEach((p, i) => {
            p.classList.toggle('TabPanel_hidden__26UM3', i !== targetIdx);
        });

        // 更新指示器位置
        if (indicator && tabs[targetIdx]) {
            // 计算目标 Tab 之前所有 Tab 的宽度总和 (用于确定 translateX)
            const left = tabs.slice(0, targetIdx).reduce((s, t) => s + t.offsetWidth, 0);

            const tabsRoot = root.querySelector('.MuiTabs-root');
            // 获取 MuiTabs-root 的滚动位置,确保指示器在滚动时位置正确
            const scrollLeft = tabsRoot ? tabsRoot.scrollLeft : 0;

            const indicatorX = left - scrollLeft;
            const indicatorW = tabs[targetIdx].offsetWidth;

            indicator.style.transform = `translateX(${indicatorX}px)`;
            indicator.style.width = `${indicatorW}px`;

            // 确保指示器的颜色也正确 (游戏原始颜色)
            indicator.style.backgroundColor = 'rgb(240, 240, 240)';
        }

        // 关键:保存正确的 Tab 索引
        setLastTabIndex(skill, targetIdx);
        if (targetIdx === tabs.length - 1 && targetIdx >= 0) {
            updateFavoritesPanel(skill);
        }
    }

    function setPendingClick(skill, itemName, selectedSkillsForTarget) {
        if (isProcessingClick) return;

        sessionStorage.setItem(PENDING_CLICK_KEY, JSON.stringify({
            skill,
            itemName,
            selectedSkills: Array.from(selectedSkillsForTarget)
        }));
    }

    function getPendingClick() {
        const data = sessionStorage.getItem(PENDING_CLICK_KEY);
        sessionStorage.removeItem(PENDING_CLICK_KEY);
        return data ? JSON.parse(data) : null;
    }

    function handlePendingClick() {
        if (isProcessingClick) return;

        const pending = getPendingClick();
        if (!pending) return;

        const { skill, itemName, selectedSkills: savedSelectedSkills } = pending;

        const currentSkillLabel = document.querySelector('.NavigationBar_active__3R-QS .NavigationBar_label__1uH-y');
        if (!currentSkillLabel || currentSkillLabel.textContent.trim() !== skill) return;

        isProcessingClick = true;

        if (savedSelectedSkills && savedSelectedSkills.length > 0) {
            selectedSkills.clear();
            savedSelectedSkills.forEach(s => selectedSkills.add(s));
        }

        // V4.0: 使用 setTimeout 延迟,确保页面导航和 Tab 结构加载完成
        setTimeout(() => {
            try {
                const root = document.querySelector('.GatheringProductionSkillPanel_gatheringProductionSkillPanel__vG4M7');

                // 确保 Tab 结构已存在,特别是对于挤奶/伐木
                if ((skill === '挤奶' || skill === '伐木') && !root.querySelector('.MuiTabs-root')) {
                    injectTabStructure(root, skill);
                }

                const tabsContainer = root?.querySelector('.MuiTabs-root');

                // 只有有 tab 的技能才需要切换 tab
                if (tabsContainer) {
                    const tabsFlex = tabsContainer.querySelector('.MuiTabs-flexContainer');
                    if (tabsFlex) {
                        const favTabIdx = tabsFlex.children.length - 1;
                        const favTab = tabsFlex.children[favTabIdx];
                        if (favTab) {
                            // 强制切换到收藏页
                            switchToTab(skill, favTabIdx);
                        }
                    }
                }

                setTimeout(() => {
                    clickTargetItem(itemName, skill);
                    isProcessingClick = false;
                }, 50); // 再次延迟 50ms 确保收藏 Tab 内容渲染完毕
            } catch (e) {
                isProcessingClick = false;
            }
        }, 100); // 延迟 100ms 等待技能页面完全加载
    }

    function clickTargetItem(itemName, skill) {
        const itemEl = Array.from(document.querySelectorAll('.SkillAction_skillAction__1esCp')).find(item =>
            item.querySelector('.SkillAction_name__2VPXa')?.textContent.trim() === itemName
        );

        if (itemEl && !itemEl.classList.contains('SkillAction_opaque__s9Yeq')) {
            itemEl.click();
        }
    }

    // ==================== 收藏面板(多选版本)====================
    function updateFavoritesPanel(currentSkill) {
        const panel = document.querySelector('.mwi-fav-panel');
        if (!panel) return;

        const scrollTop = panel.scrollTop;
        panel.innerHTML = '';

        const allFavorites = getCharacterData().allFavorites;
        const skillCounts = allowedSkills.reduce((acc, skill) => {
            acc[skill] = allFavorites[skill]?.length || 0;
            return acc;
        }, {});

        const skillsWithFavorites = allowedSkills.filter(skill => skillCounts[skill] > 0);

        // --- 技能选择逻辑 ---
        if (selectedSkills.size === 0) {
            const savedSkills = getSelectedSkills();
            savedSkills.forEach(skill => selectedSkills.add(skill));
            if (selectedSkills.size > 0 && savedSkills.length !== Array.from(selectedSkills).length) {
                 saveSelectedSkills(Array.from(selectedSkills));
            }
        } else {
            selectedSkills.forEach(skill => {
                if (!skillsWithFavorites.includes(skill)) {
                    selectedSkills.delete(skill);
                }
            });
            if (selectedSkills.size === 0 && skillsWithFavorites.length > 0) {
                 selectedSkills.add(skillsWithFavorites[0]);
            }
            saveSelectedSkills(Array.from(selectedSkills));
        }

        // --- 筛选容器 ---
        const filterContainer = document.createElement('div');
        filterContainer.style.marginBottom = '20px';
        filterContainer.style.display = 'flex';
        filterContainer.style.gap = '8px';
        filterContainer.style.flexWrap = 'wrap';
        filterContainer.style.alignItems = 'center';
        filterContainer.className = 'mwi-filter-container';

        const createFilterButton = (text, isSelected, clickHandler) => {
            const btn = document.createElement('button');
            btn.className = 'MuiButtonBase-root MuiTab-root MuiTab-textColorPrimary mwi-filter-btn';
            btn.textContent = text;
            Object.assign(btn.style, {
                minWidth: '0', padding: '6px 12px', margin: '0', border: 'none', cursor: 'pointer',
                background: isSelected ? '#3a3a3a' : 'none', color: isSelected ? 'white' : 'rgba(255, 255, 255, 0.7)',
                borderRadius: '4px', transition: 'all 0.2s', fontWeight: isSelected ? '500' : '400',
                textTransform: 'none'
            });
            btn.addEventListener('mouseenter', () => { btn.style.background = isSelected ? '#3a3a3a' : 'rgba(255, 255, 255, 0.08)'; });
            btn.addEventListener('mouseleave', () => { btn.style.background = isSelected ? '#3a3a3a' : 'none'; });
            btn.addEventListener('click', clickHandler);
            return btn;
        };

        const isAllSelected = selectedSkills.size === skillsWithFavorites.length && skillsWithFavorites.length > 0;
        filterContainer.appendChild(createFilterButton('全选', isAllSelected, () => {
            if (isAllSelected) { selectedSkills.clear(); } else { selectedSkills.clear(); skillsWithFavorites.forEach(skill => selectedSkills.add(skill)); }
            saveSelectedSkills(Array.from(selectedSkills)); updateFavoritesPanel(currentSkill);
        }));

        filterContainer.appendChild(createFilterButton('反选', false, () => {
            const allSelected = Array.from(selectedSkills);
            selectedSkills.clear();
            skillsWithFavorites.forEach(skill => {
                if (!allSelected.includes(skill)) { selectedSkills.add(skill); }
            });
            if (selectedSkills.size === 0 && skillsWithFavorites.length > 0) { selectedSkills.add(skillsWithFavorites[0]); }
            saveSelectedSkills(Array.from(selectedSkills)); updateFavoritesPanel(currentSkill);
        }));

        skillsWithFavorites.forEach(skill => {
            const isSelected = selectedSkills.has(skill);
            filterContainer.appendChild(createFilterButton(`${skill} (${skillCounts[skill]})`, isSelected, () => {
                if (selectedSkills.has(skill)) { if (selectedSkills.size > 1) { selectedSkills.delete(skill); } } else { selectedSkills.add(skill); }
                saveSelectedSkills(Array.from(selectedSkills)); updateFavoritesPanel(currentSkill);
            }));
        });

        panel.appendChild(filterContainer);

        // 清空选中收藏按钮
        const clearBtn = document.createElement('button');
        clearBtn.className = 'mwi-clear-favs-btn';
        clearBtn.textContent = '×';
        clearBtn.title = '清空选中技能的收藏';
        Object.assign(clearBtn.style, {
            position: 'absolute', top: '4px', right: '4px', background: 'none', border: 'none',
            color: '#999', fontSize: '20px', cursor: 'pointer', width: '24px', height: '24px',
            borderRadius: '12px', display: 'flex', alignItems: 'center', justifyContent: 'center',
            padding: '0', zIndex: '100', opacity: '0.5', transition: 'opacity 0.2s'
        });

        clearBtn.addEventListener('mouseenter', () => { clearBtn.style.opacity = '1'; clearBtn.style.color = '#ff4444'; });
        clearBtn.addEventListener('mouseleave', () => { clearBtn.style.opacity = '0.5'; clearBtn.style.color = '#999'; });

        clearBtn.addEventListener('click', () => {
            if (selectedSkills.size === 0) return;

            const skillsList = Array.from(selectedSkills).join('、');
            if (confirm(`确定要清空选中技能(${skillsList})的所有收藏吗?`)) {

                selectedSkills.forEach(skill => {
                    getFavoritesForSkill(skill)?.forEach(name => {
                         removeFromGlobalOrder(name);
                         removeCacheForItem(skill, name);
                    });
                    setFavoritesForSkill(skill, []);
                });

                debounceUpdate();
                selectedSkills.clear();
                saveSelectedSkills(Array.from(selectedSkills));
                updateFavoritesPanel(currentSkill);
            }
        });

        panel.appendChild(clearBtn);

        // 创建物品容器
        const grid = document.createElement('div');
        grid.className = 'SkillActionGrid_skillActionGrid__1tJFk';
        panel.appendChild(grid);

        // 使用 globalOrder 作为排序来源
        const globalOrder = getGlobalOrder();
        const displayItems = [];

        globalOrder.forEach(name => {
            const originalSkill = findItemSkill(name);
            if (originalSkill && selectedSkills.has(originalSkill)) {
                displayItems.push({ name: name, skill: originalSkill });
            }
        });

        if (displayItems.length === 0) {
            grid.innerHTML = `<div style="color:#aaa;padding:40px;text-align:center;">
                                ${skillsWithFavorites.length === 0 ? '暂无任何收藏物品' : (selectedSkills.size === 0 ? '请选择要显示的技能' : '选中的技能暂无收藏物品')}
                            </div>`;
            return;
        }

        // 获取仓库物品数据
        const inventoryItems = getInventoryItems();
        
        // 显示物品卡片
        displayItems.forEach((item) => {
            let clone;
            const cachedHtml = getCachedItem(item.skill, item.name);
            if (cachedHtml) {
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = cachedHtml;
                clone = tempDiv.firstChild;
            } else {
                clone = createSimpleItemCard(item.name);
            }

            if (!clone || clone.nodeType !== 1) { clone = createSimpleItemCard(item.name); }

            clone.dataset.name = item.name;
            clone.dataset.skill = item.skill;
            clone.style.position = 'relative';
            clone.style.opacity = '1';
            
            // 获取物品的仓库数量
            const itemCount = inventoryItems[item.name] || '0';
            
            // 移除已存在的数量显示元素(如果有)
            const existingCount = clone.querySelector('.mwi-item-count');
            if (existingCount) {
                existingCount.remove();
            }
            
            // 创建数量显示元素
            const countElement = document.createElement('div');
            countElement.className = 'mwi-item-count';
            countElement.textContent = itemCount;
            Object.assign(countElement.style, {
                position: 'absolute',
                bottom: '10px', // 垂直上移
                left: '0',
                right: '0',
                width: '100%', // 水平居中
                background: 'none', // 无背景
                color: '#cccccc', // 浅灰色
                fontSize: '11px',
                fontWeight: 'bold',
                padding: '1px 4px',
                textAlign: 'center',
                zIndex: '5'
            });
            
            // 将数量显示元素添加到物品卡片中
            clone.appendChild(countElement);

            const existingBtns = clone.querySelectorAll('.mwi-fav-btn');
            existingBtns.forEach(btn => btn.remove());

            const favBtn = document.createElement('button');
            favBtn.className = 'mwi-fav-btn mwi-panel-fav-btn';
            favBtn.textContent = '⭐';
            Object.assign(favBtn.style, {
                position: 'absolute', top: '4px', right: '4px', background: 'none',
                border: 'none', fontSize: '18px', cursor: 'pointer', zIndex: '10',
                color: 'rgb(255, 215, 0)'
            });

            favBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                const itemSkill = findItemSkill(item.name);
                if (itemSkill) {
                    const arr = getFavoritesForSkill(itemSkill);
                    const index = arr.indexOf(item.name);
                    if (index > -1) {
                        arr.splice(index, 1);
                        setFavoritesForSkill(itemSkill, arr);
                        removeFromGlobalOrder(item.name);
                        removeCacheForItem(itemSkill, item.name);

                        updateFavoritesPanel(currentSkill);
                        debounceUpdate();
                    }
                }
            });

            clone.appendChild(favBtn);
            clone.addEventListener('click', (e) => {
                if (e.target.closest('.mwi-fav-btn')) return;
                e.stopPropagation();

                const itemSkill = item.skill;
                const currentActiveSkill = document.querySelector('.NavigationBar_active__3R-QS .NavigationBar_label__1uH-y')?.textContent.trim();

                if (itemSkill === currentActiveSkill) {
                    const itemEl = Array.from(document.querySelectorAll('.SkillAction_skillAction__1esCp')).find(itemEl =>
                        itemEl.querySelector('.SkillAction_name__2VPXa')?.textContent.trim() === item.name
                    );
                    if (itemEl) itemEl.click();
                } else {
                    setPendingClick(itemSkill, item.name, selectedSkills);

                    const skillNavs = document.querySelectorAll('.NavigationBar_button__3L1eA, .NavigationBar_navigationLink__3eAHA');
                    const targetNav = Array.from(skillNavs).find(nav => {
                        const label = nav.querySelector('.NavigationBar_label__1uH-y');
                        return label && label.textContent.trim() === itemSkill;
                    });

                    if (targetNav) targetNav.click();
                }
            });

            // --- 跨技能拖拽排序逻辑 ---
            clone.draggable = true;

            clone.addEventListener('dragstart', (e) => {
                e.dataTransfer.setData('text/plain', JSON.stringify({ name: item.name }));
                clone.classList.add('dragging');
                clone.style.opacity = '0.4';
            });

            clone.addEventListener('dragend', () => {
                clone.classList.remove('dragging');
                clone.style.opacity = '1';
                grid.querySelectorAll('.SkillAction_skillAction__1esCp').forEach(c => { c.style.border = ''; });
            });

            clone.addEventListener('dragenter', (e) => {
                e.preventDefault();
                if (clone.classList.contains('dragging')) return;
                clone.style.border = '2px solid #555';
            });

            clone.addEventListener('dragleave', () => {
                clone.style.border = '';
            });

            grid.appendChild(clone);
        });

        // 拖拽释放(处理跨技能排序)
        grid.addEventListener('dragover', (e) => e.preventDefault());

        grid.addEventListener('drop', (e) => {
            e.preventDefault();

            const draggedCard = document.querySelector('.SkillAction_skillAction__1esCp.dragging');
            if (draggedCard) draggedCard.style.opacity = '1';

            try {
                const data = JSON.parse(e.dataTransfer.getData('text/plain'));
                const draggedName = data.name;

                const target = e.target.closest('.SkillAction_skillAction__1esCp');
                if (!target || target.dataset.name === draggedName) return;

                const targetName = target.dataset.name;

                const currentGlobalOrder = getGlobalOrder();

                const fromIndex = currentGlobalOrder.indexOf(draggedName);
                const toIndex = currentGlobalOrder.indexOf(targetName);

                if (fromIndex > -1 && toIndex > -1) {
                    const [movedItem] = currentGlobalOrder.splice(fromIndex, 1);
                    currentGlobalOrder.splice(toIndex, 0, movedItem);

                    setGlobalOrder(currentGlobalOrder);

                    updateFavoritesPanel(currentSkill);
                }
            } catch (e) {
                console.error("拖拽错误:", e);
             } finally {
                grid.querySelectorAll('.SkillAction_skillAction__1esCp').forEach(c => { c.style.border = ''; });
             }
        });

        panel.scrollTop = scrollTop;
    }

    function createSimpleItemCard(itemName) {
        const card = document.createElement('div');
        card.className = 'SkillAction_skillAction__1esCp';
        card.innerHTML = `<div class="SkillAction_container__2Qbcu" style="height: 100%;">
                <div class="SkillAction_iconWrapper__mjk5b" style="width: 48px; height: 48px;">
                    <div class="SkillAction_iconBackground__3LSVo"></div>
                    <div class="SkillAction_icon__3JYqX" style="font-size: 24px;">⚠️</div>
                </div>
                <div class="SkillAction_textContent__2k4Vw">
                    <div class="SkillAction_name__2VPXa">${itemName}</div>
                    <div class="SkillAction_description__1q5Xp" style="color: #ffaa00; font-size: 12px;">缓存丢失</div>
                </div>
            </div>`;
        card.style.cursor = 'pointer';
        return card;
    }

    function updateFavoritesPanelIfOpen() {
        const panel = document.querySelector('.mwi-fav-panel');
        if (panel && !panel.classList.contains('TabPanel_hidden__26UM3')) {
            const skill = document.querySelector('.NavigationBar_active__3R-QS .NavigationBar_label__1uH-y')?.textContent.trim();
            if (skill && allowedSkills.includes(skill)) {
                // 只有 Tabbed 技能才需要更新面板
                if (document.querySelector('.GatheringProductionSkillPanel_gatheringProductionSkillPanel__vG4M7')) {
                     updateFavoritesPanel(skill);
                }
            }
        }
    }

    // ==================== 仓库物品数量获取 ====================
    /**
     * 获取仓库中所有物品的名称和数量
     * @returns {Object} 物品名称到数量的映射
     */
    // 仓库物品数据缓存
    let inventoryDataCache = {};

    // 从DOM中获取仓库物品数据
    function getInventoryItems() {
        const inventory = {};
        
        // 查找仓库容器(使用更通用的选择器,不依赖其他脚本添加的类)
        const inventoryContainer = document.querySelector('.Inventory_items__6SXv0');
        if (!inventoryContainer) {
            return inventory;
        }
        
        // 遍历所有物品(不依赖分类结构,直接查找所有物品容器)
        const allItems = inventoryContainer.querySelectorAll('.Item_itemContainer__x7kH1');
        
        allItems.forEach((item, index) => {
            // 获取物品名称(检查所有可能的路径)
            let iconContainer = item.querySelector('.Item_iconContainer__5z7j4');
            if (!iconContainer) {
                // 尝试更深层的路径
                iconContainer = item.querySelector('.Item_item__2De2O .Item_iconContainer__5z7j4');
            }
            if (!iconContainer) {
                return;
            }
            
            // 首先尝试直接从iconContainer获取aria-label
            let itemName = iconContainer.getAttribute('aria-label');
            
            // 如果没找到,检查内部的svg元素
            if (!itemName) {
                const svgElement = iconContainer.querySelector('svg[role="img"]');
                if (svgElement) {
                    itemName = svgElement.getAttribute('aria-label');
                }
            }
            
            // 如果还是没找到,尝试检查所有子元素
            if (!itemName) {
                const childWithAriaLabel = iconContainer.querySelector('[aria-label]');
                if (childWithAriaLabel) {
                    itemName = childWithAriaLabel.getAttribute('aria-label');
                }
            }
            
            if (!itemName) {
                return;
            }
            
            // 获取物品数量
            let countElement = item.querySelector('.Item_count__1HVvv');
            if (!countElement) {
                // 尝试更深层的路径
                countElement = item.querySelector('.Item_item__2De2O .Item_count__1HVvv');
            }
            const count = countElement ? countElement.textContent.trim() : '0';
            
            // 存储到映射中
            inventory[itemName] = count;
        });
        
        // 更新缓存
        inventoryDataCache = inventory;
        return inventory;
    }

    // 监听仓库变化的Observer
    const inventoryObserver = new MutationObserver(() => {
        getInventoryItems();
        
        // 仓库数据变化时,自动更新收藏面板的物品数量显示
        updateFavoritesPanelIfOpen();
    });

    // 启动仓库监听
    function startInventoryObserver() {
        // 先尝试直接获取一次数据
        getInventoryItems();
        
        // 设置定时器定期检查仓库面板是否打开
        const checkInventoryInterval = setInterval(() => {
            const inventoryContainer = document.querySelector('.Inventory_items__6SXv0');
            if (inventoryContainer) {
                // 观察仓库容器的变化
                inventoryObserver.observe(inventoryContainer, {
                    childList: true,
                    subtree: true,
                    characterData: true
                });
                
                // 清除定时器
                clearInterval(checkInventoryInterval);
            }
        }, 2000); // 每2秒检查一次
    }

    // ==================== 主循环 ====================
    hookCharacterId();
    selectedSkills = new Set(getSelectedSkills());
    
    // 启动仓库数据监听
    startInventoryObserver();

    const observer = new MutationObserver(() => {
        handlePendingClick();

        const skillLabel = document.querySelector('.NavigationBar_active__3R-QS .NavigationBar_label__1uH-y');
        const skill = skillLabel?.textContent.trim();
        if (!skill || !allowedSkills.includes(skill)) return;

        const root = document.querySelector('.GatheringProductionSkillPanel_gatheringProductionSkillPanel__vG4M7');
        if (!root) {
            // V4.1: 如果技能面板根元素不存在,将 lastObservedSkill 置空,确保下次加载时被视为新技能
            lastObservedSkill = null;
            return;
        }

        // V4.1 FIX: 检查技能是否切换
        const isNewSkillLoad = skill !== lastObservedSkill;


        // **新逻辑:为挤奶和伐木注入 Tab 结构**
        if (skill === '挤奶' || skill === '伐木') {
            injectTabStructure(root, skill);
        }

        const tabsContainer = root.querySelector('.MuiTabs-root');
        const isTabbedSkill = !!tabsContainer;

        // 2. 无论是否为 Tabbed 技能,都为物品卡片添加收藏按钮
        document.querySelectorAll('.SkillAction_skillAction__1esCp:not(.SkillAction_opaque__s9Yeq)')
            .forEach(item => addFavoriteButton(item, skill));

        // 3. 只有 Tabbed 技能才执行 Tab 相关的逻辑
        if (isTabbedSkill) {

            ensureFavoritesTab(tabsContainer, skill);

            const tabsFlex = tabsContainer.querySelector('.MuiTabs-flexContainer');
            if (!tabsFlex) return;

            const tabs = tabsFlex.querySelectorAll('button');
            const total = tabs.length;

            // V4.2 FIX: 重新添加事件监听器,确保兼容原生行为并修正自定义 Tab 的状态
            tabs.forEach((tab, i) => {
                // 检查是否已添加监听器
                if (tab.hasAttribute('data-mwi-listener-added')) return;

                tab.setAttribute('data-mwi-listener-added', 'true');
                const isCustomTab = tab.hasAttribute('data-mwi-custom-tab');
                const isFavTab = tab.classList.contains('mwi-fav-tab');

                // 备份原有的点击事件(仅对原生Tab有效)
                const originalClickListener = tab.onclick;
                tab.onclick = null; // 清除原有的 onclick 属性

                tab.addEventListener('click', (e) => {
                    // 获取最新的标签和面板列表
                    const updatedTabs = Array.from(tabsFlex.children);
                    const updatedPanels = root.querySelectorAll('.TabsComponent_tabPanelsContainer__26mzo > .TabPanel_tabPanel__tXMJF');
                    const currentIdx = updatedTabs.indexOf(tab);
                    
                    if (isFavTab) {
                        // 场景 1: 点击的是收藏 Tab
                        e.stopPropagation();
                        switchToTab(skill, currentIdx);
                    } else {
                        // 场景 2: 点击的是原生 Tab(包括挤奶/伐木的原生 Tab)
                        
                        // 1. 触发原生逻辑,让 React 更新其内部状态
                        if (originalClickListener) {
                             try {
                                 originalClickListener.call(tab, e);
                             } catch (err) {
                                 // 忽略原生事件可能的错误
                             }
                        }

                        // 2. 强制更新所有标签状态
                        updatedTabs.forEach((t, idx) => {
                            const isSelected = idx === currentIdx;
                            t.classList.toggle('Mui-selected', isSelected);
                            t.setAttribute('aria-selected', isSelected);
                            t.setAttribute('tabindex', isSelected ? '0' : '-1');
                        });

                        // 3. 强制更新所有面板状态
                        updatedPanels.forEach((p, idx) => {
                            p.classList.toggle('TabPanel_hidden__26UM3', idx !== currentIdx);
                        });

                        // 4. 修复收藏 Tab 状态
                        const favTab = root.querySelector('.mwi-fav-tab');
                        if (favTab) {
                            favTab.classList.remove('Mui-selected');
                            favTab.setAttribute('aria-selected', 'false');
                            favTab.setAttribute('tabindex', '-1');
                        }

                        // 5. 持久化索引
                        setLastTabIndex(skill, currentIdx);
                    }
                });
            });


            // 标签页加载持久化逻辑
            const savedIdx = Math.min(getLastTabIndex(skill), total - 1);
            const currentIdx = Array.from(tabs).findIndex(t => t.classList.contains('Mui-selected'));

            // V4.3 FIX: 核心逻辑:只有在技能刚刚加载时,且目标索引与当前不一致才执行强制切换。
            // 依赖 isNewSkillLoad 确保仅在通过导航栏切换技能时触发。
            if (isNewSkillLoad && currentIdx !== savedIdx) {
                tabRestoreTimeout = setTimeout(() => {
                    // 再次检查当前激活的技能是否一致,以防用户在延迟期间切换了技能
                    const currentSkillCheck = document.querySelector('.NavigationBar_active__3R-QS .NavigationBar_label__1uH-y')?.textContent.trim();
                    if (currentSkillCheck !== skill) return;

                    switchToTab(skill, savedIdx);

                }, 100); // 增加延迟到 100ms 解决可能的竞态条件 (Race Condition)
            }
        }

        // V4.1 核心:在 observer 结束前更新 lastObservedSkill,以检测下一次的技能切换。
        if(skill) {
             lastObservedSkill = skill;
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();