Greasy Fork

Greasy Fork is available in English.

[MWI] Ultimate Enhancement Tracker v3.5.0

Now with Enhancelation

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         [MWI] Ultimate Enhancement Tracker v3.5.0
// @namespace    http://tampermonkey.net/
// @version      3.5.2
// @description  Now with Enhancelation
// @author       Nex (Base tracker by Truth_Light, enhancelator by Dohnuts)
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/mathjs/10.6.4/math.min.js
// @license      MIT
// @grant        GM_registerMenuCommand
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// ==/UserScript==

/*
    Market price fetching relies on having MWI Tools installed, get it here:
    http://greasyfork.icu/en/scripts/494467-mwitools

    获取市场价格取决于是否安装了 MWI 工具,在此获取:
    https://greasyfork.cc/zh-CN/scripts/494467-mwitools

    MilkyWayIdle Steam game client players should also install this script:
    https://raw.githubusercontent.com/YangLeda/Userscripts-For-MilkyWayIdle/refs/heads/main/MWITools%20addon%20for%20Steam%20version.js
    (Web browser players do NOT need to install the above script.)
*/

(function() {
    'use strict';
    // ======================
    // STYLE CONFIGURATION
    // ======================
    const STYLE = {
      colors: {
        primary: '#00ffe7', // Bright neon cyan
        background: 'rgba(5, 5, 15, 0.95)', // Deep black-blue
        border: '1px solid rgba(0, 255, 234, 0.4)', // Glowing border
        textPrimary: '#e0f7ff', // Soft neon white-blue
        textSecondary: '#9b9bff', // Neon purple tint
        accent: '#ff00d4', // Vibrant pink-magenta
        danger: '#ff0055', // Electric red
        success: '#00ff99', // Neon green
        headerBg: 'rgba(15, 5, 35, 0.7)', // Dark purple gradient tint
        epic: '#c63dff', // Deep neon purple
        legendary: '#ff6f1a', // Bright orange neon
        mythic: '#ff0033', // Intense mythic red
        blessed: 'linear-gradient(135deg, #ff00d4, #c63dff, #00ffe7)', // Trippy neon gradient
        gold: '#FFD700' // Gold color for cost display
      },
      fonts: {
        primary: '14px "Orbitron", "Segoe UI", Roboto, sans-serif', // Futuristic font
        secondary: '12px "Orbitron", "Segoe UI", Roboto, sans-serif',
        header: 'bold 16px "Orbitron", "Segoe UI", Roboto, sans-serif',
        milestone: 'bold 18px "Orbitron", "Segoe UI", Roboto, sans-serif',
        cost: 'bold 13px "Orbitron", "Segoe UI", Roboto, sans-serif'
      },
      shadows: {
        panel: '0 0 20px rgba(0, 255, 234, 0.3)', // Neon glow panel
        notification: '0 0 15px rgba(255, 0, 212, 0.3)', // Pink neon soft glow
        milestone: '0 0 25px rgba(198, 61, 255, 0.4)', // Epic purple glow
        text: '0 0 6px rgba(0, 255, 234, 0.3)', // Soft text glow
        gold: '0 0 8px rgba(255, 215, 0, 0.7)' // Gold glow
      },
      borderRadius: {
        medium: '14px', // Rounded with a modern edge
        small: '8px'
      },
      transitions: {
        fast: 'all 0.15s ease-in-out',
        medium: 'all 0.3s ease-in-out',
        slow: 'all 0.5s ease-in-out'
      },
    };

    STYLE.scrollable = {
        maxHeight: '50vh',  // Maximum height before scrolling kicks in
        overflowY: 'auto',
        scrollbarWidth: 'thin',
        scrollbarColor: `${STYLE.colors.primary} transparent`
    };

    // Core Variables
    const userLanguage = localStorage.getItem('i18nextLng');
    // Load enhancementData from localStorage if available
    let enhancementData = JSON.parse(localStorage.getItem('enhancementData')) || {};
    let currentTrackingIndex = parseInt(localStorage.getItem('enhancementCurrentTrackingIndex')) || 0;
    let currentViewingIndex = parseInt(localStorage.getItem('enhancementCurrentViewingIndex')) || 0;

    // Add session validation
    function validateSession(session) {
        if (!session) return;

        // Ensure all required fields exist
        if (!session["硬币消耗"]) session["硬币消耗"] = { count: 0, totalCost: 0 };
        if (!session["总成本"]) session["总成本"] = 0;

        // Reconstruct total from components if needed
        const calculatedTotal =
            Object.values(session["材料消耗"] || {}).reduce((sum, m) => sum + (m.totalCost || 0), 0) +
            (session["硬币消耗"]?.totalCost || 0) +
            (session["其他数据"]["保护总成本"] || 0);

        if (Math.abs(calculatedTotal - session["总成本"]) > 1) {
            console.log(`Correcting total cost drift: ${session["总成本"]} → ${calculatedTotal}`);
            session["总成本"] = calculatedTotal;
        }
    }

    // Validate all sessions on load
    Object.values(enhancementData).forEach(validateSession);

    // Add data migration for existing sessions
    Object.values(enhancementData).forEach(session => {
        if (!session["硬币消耗"]) {
            session["硬币消耗"] = { count: 0, totalCost: 0 };
        }
        // Recalculate total cost if needed
        if (session["总成本"] === undefined) {
            const materialsCost = Object.values(session["材料消耗"] || {}).reduce((sum, m) => sum + m.totalCost, 0);
            const coinsCost = session["硬币消耗"]?.totalCost || 0;
            const protectionCost = session["其他数据"]["保护总成本"] || 0;
            session["总成本"] = materialsCost + coinsCost + protectionCost;
        }
    });

    cleanSessionIndices();

    // Function to save enhancementData to localStorage
    function saveEnhancementData() {
        // Ensure all sessions have the correct structure
        Object.values(enhancementData).forEach(session => {
            if (!session["硬币消耗"]) session["硬币消耗"] = { count: 0, totalCost: 0 };
        });

        localStorage.setItem('enhancementData', JSON.stringify(enhancementData));
        localStorage.setItem('enhancementCurrentTrackingIndex', currentTrackingIndex);
        localStorage.setItem('enhancementCurrentViewingIndex', currentViewingIndex);
    }

    function clearAllSessions() {
    if (confirm(isZH ? "确定要清除所有强化会话数据吗?此操作不可撤销。" : "Are you sure you want to clear all enhancement sessions? This cannot be undone.")) {
        enhancementData = {};
        currentTrackingIndex = 0;
        currentViewingIndex = 0;
        localStorage.removeItem('enhancementData');
        debouncedUpdateFloatingUI();
        showUINotification(isZH ? "已清除所有会话数据" : "All session data cleared");
    }
}

    currentViewingIndex = currentTrackingIndex; // Start viewing current session
    let item_name_to_hrid;
    let item_hrid_to_name;

    var isZH = userLanguage.startsWith("zh");
    // Add this near your language detection
    let lastLangCheck = userLanguage;
    setInterval(() => {
        const currentLang = localStorage.getItem('i18nextLng');
        if (currentLang !== lastLangCheck) {
            lastLangCheck = currentLang;
            isZH = currentLang.startsWith("zh");

            // Force refresh all displayed names
            Object.keys(enhancementData).forEach(key => {
                const session = enhancementData[key];
                if (session["其他数据"]) {
                    session["其他数据"]["物品名称"] = translateItemName(
                        session["其他数据"]["物品HRID"],
                        session["其他数据"]["物品名称"]
                    );
                }
            });

            debouncedUpdateFloatingUI();
            saveEnhancementData(); // Save after updating names
        }
    }, 1000);

    cleanSessionIndices();

    function cleanSessionIndices() {
        const sortedIndices = Object.keys(enhancementData)
            .map(Number)
            .sort((a, b) => a - b);

        if (sortedIndices.some((v, i) => v !== i + 1)) {
            const newData = {};
            sortedIndices.forEach((oldIndex, i) => {
                newData[i + 1] = enhancementData[oldIndex];
            });
            enhancementData = newData;
            currentTrackingIndex = sortedIndices.length > 0 ?
                Math.max(...Object.keys(newData).map(Number)) : 1;
            currentViewingIndex = currentTrackingIndex;
            saveEnhancementData();
        }

        // Ensure loaded indices are valid
        if (!enhancementData[currentViewingIndex]) {
            currentViewingIndex = currentTrackingIndex;
        }
        if (!enhancementData[currentTrackingIndex]) {
            currentTrackingIndex = sortedIndices.length > 0 ? Math.max(...sortedIndices) : 0;
        }
    }

    // ======================
    // MARKET DATA HANDLING
    // ======================

    let MWITools_marketAPI_json = null;
    let lastMarketUpdate = 0;
    const MARKET_REFRESH_INTERVAL = 30000; // 30 seconds

    function loadMarketData() {
        try {
            // Try to load from localStorage
            const marketDataStr = localStorage.getItem('MWITools_marketAPI_json');
            if (marketDataStr) {
                MWITools_marketAPI_json = JSON.parse(marketDataStr);
                lastMarketUpdate = MWITools_marketAPI_json?.time || 0;
                // console.log('[Market] Loaded market data from storage', lastMarketUpdate);
            }

            // Set up periodic refresh
            setInterval(refreshMarketData, MARKET_REFRESH_INTERVAL);

            // Initial refresh
            refreshMarketData();
        } catch (e) {
            console.error('[Market] Error loading market data:', e);
        }
    }

    function refreshMarketData() {
        try {
            // Check if we have the market data object in memory
            if (typeof window.MWITools_marketAPI_json !== 'undefined') {
                MWITools_marketAPI_json = window.MWITools_marketAPI_json;
                lastMarketUpdate = MWITools_marketAPI_json?.time || 0;
                localStorage.setItem('MWITools_marketAPI_json', JSON.stringify(MWITools_marketAPI_json));
                // console.log('[Market] Updated market data from memory', lastMarketUpdate);
            }
        } catch (e) {
            console.error('[Market] Error refreshing market data:', e);
        }
    }

    function getMarketPrice(itemHRID) {
        try {
            // Special case for coins - always worth 1 gold
            if (itemHRID === '/items/coin' || itemHRID === '/items/coins') {
                return 1; // Coins are tracked separately
            }

            // Validate input
            if (!itemHRID || typeof itemHRID !== 'string') {
                console.log('[Market] Invalid item HRID:', itemHRID);
                return 0;
            }

            // Refresh market data if stale
            if (Date.now() - lastMarketUpdate > MARKET_REFRESH_INTERVAL) {
                refreshMarketData();
            }

            // Check if market data is available
            if (!MWITools_marketAPI_json?.market) {
                console.log('[Market] No market data available');
                return 0;
            }

            // Try to get the display name (either from dictionary or HRID)
            let itemName = item_hrid_to_name?.[itemHRID];
            if (!itemName) {
                // Extract from HRID format: /items/aqua_essence → Aqua Essence
                const parts = itemHRID.split('/');
                const lastPart = parts[parts.length - 1] || '';
                itemName = lastPart
                    .replace(/_/g, ' ')
                    .replace(/(^|\s)\S/g, t => t.toUpperCase())
                    .trim();
            }

            // Return 0 if we still don't have a name
            if (!itemName) {
                console.log('[Market] Could not determine item name for:', itemHRID);
                return 0;
            }

            // Find market entry (case insensitive)
            const marketEntry = Object.entries(MWITools_marketAPI_json.market).find(
                ([name]) => name.toLowerCase() === itemName.toLowerCase()
            );

            if (!marketEntry) {
                console.log('[Market] No entry for:', itemName);
                return 0;
            }

            // Use ask price if available, otherwise bid price
            const price = marketEntry[1]?.ask || marketEntry[1]?.bid || 0;
            // console.log('[Market] Price for', itemName, ':', price);

            return price;
        } catch (e) {
            console.error("[Market] Error getting price for", itemHRID, ":", e);
            return 0;
        }
    }

    // ======================
    // XP GAIN TRACKING
    // ======================

    function getBaseItemLevel(itemHRID) {
      try {
          // Get initClientData from localStorage
          const initClientData = JSON.parse(localStorage.getItem('initClientData'));
          if (!initClientData?.itemDetailMap) return 0;

          // Find the item in the detail map
          const itemDetails = initClientData.itemDetailMap[itemHRID];
          if (!itemDetails) return 0;

          // Return the item level if available
          return itemDetails.itemLevel || 0;

      } catch (e) {
          console.error("Error getting base item level:", e);
          return 0;
      }
    }

      function getWisdomBuff() {
          try {
              const charData = JSON.parse(localStorage.getItem('init_character_data'));
              if (!charData) return { flatBoost: 0 };

              let totalFlatBoost = 0;

              // 1. Community Buffs
              const communityEnhancingBuffs = charData.communityActionTypeBuffsMap?.["/action_types/enhancing"];
              if (Array.isArray(communityEnhancingBuffs)) {
                  communityEnhancingBuffs.forEach(buff => {
                      if (buff.typeHrid === "/buff_types/wisdom") {
                          totalFlatBoost += buff.flatBoost || 0;
                      }
                  });
              }

              // 2. Equipment Buffs
              const equipmentEnhancingBuffs = charData.equipmentActionTypeBuffsMap?.["/action_types/enhancing"];
              if (Array.isArray(equipmentEnhancingBuffs)) {
                  equipmentEnhancingBuffs.forEach(buff => {
                      if (buff.typeHrid === "/buff_types/wisdom") {
                          totalFlatBoost += buff.flatBoost || 0;
                      }
                  });
              }

              // 3. House Buffs
              const houseEnhancingBuffs = charData.houseActionTypeBuffsMap?.["/action_types/enhancing"];
              if (Array.isArray(houseEnhancingBuffs)) {
                  houseEnhancingBuffs.forEach(buff => {
                      if (buff.typeHrid === "/buff_types/wisdom") {
                          totalFlatBoost += buff.flatBoost || 0;
                      }
                  });
              }

              // 4. Consumable Buffs (NEW - from wisdom tea etc)
              const consumableEnhancingBuffs = charData.consumableActionTypeBuffsMap?.["/action_types/enhancing"];
              if (Array.isArray(consumableEnhancingBuffs)) {
                  consumableEnhancingBuffs.forEach(buff => {
                      if (buff.typeHrid === "/buff_types/wisdom") {
                          totalFlatBoost += buff.flatBoost || 0;
                      }
                  });
              }

              return { flatBoost: totalFlatBoost }

          } catch (e) {
              console.error("Error calculating wisdom buff:", e);
              return { flatBoost: 0 };
          }
      }

      function calculateSuccessXP(previousLevel, itemHrid) {
          const baseLevel = getBaseItemLevel(itemHrid);
          const wisdomBuff = getWisdomBuff();

          // Special handling for enhancement level 0 (base items)
          const enhancementMultiplier = previousLevel === 0
              ? 1.0  // Base value for unenhanced items
              : (previousLevel + 1);  // Normal progression

          return Math.floor(
              1.4 *
              (1 + (wisdomBuff?.flatBoost || 0)) *
              enhancementMultiplier *
              (10 + baseLevel)
          );
      }

    function calculateFailureXP(previousLevel, itemHrid) {
        return Math.floor(calculateSuccessXP(previousLevel, itemHrid) * 0.1); // 10% of success XP
    }

    // ======================
    // ENHANCEMENT COST TRACKING (updated to use new market functions)
    // ======================

    function getEnhancementMaterials(itemHRID) {
        try {
            const initData = JSON.parse(localStorage.getItem('initClientData'));
            const itemData = initData.itemDetailMap?.[itemHRID];

            if (!itemData) {
                console.log('[Materials] Item not found:', itemHRID);
                return null;
            }

            // Get the costs array (try different possible property names)
            const costs = itemData.enhancementCosts || itemData.upgradeCosts ||
                         itemData.enhanceCosts || itemData.enhanceCostArray;

            if (!costs) {
                console.log('[Materials] No enhancement costs found for:', itemHRID);
                return null;
            }

            // Convert to our desired format
            let materials = [];

            // Case 1: Array of objects (current format)
            if (Array.isArray(costs) && costs.length > 0 && typeof costs[0] === 'object') {
                materials = costs.map(cost => [cost.itemHrid, cost.count]);
            }
            // Case 2: Already in correct format [["/items/foo", 30], ["/items/bar", 20]]
            else if (Array.isArray(costs) && costs.length > 0 && Array.isArray(costs[0])) {
                materials = costs;
            }
            // Case 3: Simple array ["/items/foo", 30]
            else if (Array.isArray(costs) && costs.length === 2 && typeof costs[0] === 'string') {
                materials = [costs];
            }
            // Case 4: Object format {"/items/foo": 30, "/items/bar": 20}
            else if (typeof costs === 'object' && !Array.isArray(costs)) {
                materials = Object.entries(costs);
            }

            // Filter out any invalid entries
            materials = materials.filter(m =>
                Array.isArray(m) &&
                m.length === 2 &&
                typeof m[0] === 'string' &&
                typeof m[1] === 'number'
            );

            // console.log('[Materials] Processed costs:', materials);
            return materials.length > 0 ? materials : null;
        } catch (e) {
            console.error('[Materials] Error:', e);
            return null;
        }
    }

      // Updated trackMaterialCosts():
      function trackMaterialCosts(itemHRID) {
          const session = enhancementData[currentTrackingIndex];
          const materials = getEnhancementMaterials(itemHRID) || [];
          let materialCost = 0;
          let coinCost = 0;

          materials.forEach(([hrid, count]) => {
              if (hrid.includes('/items/coin')) {
                  // Track coins for THIS ATTEMPT ONLY
                  coinCost = count; // Coins are 1:1 value (1 coin = 1 gold)
                  session["硬币消耗"].count += count;
                  session["硬币消耗"].totalCost += count;
                  return;
              }

              const cost = getMarketPrice(hrid) * count;
              materialCost += cost;

              if (!session["材料消耗"][hrid]) {
                  session["材料消耗"][hrid] = {
                      name: item_hrid_to_name[hrid] || hrid,
                      count: 0,
                      totalCost: 0
                  };
              }

              session["材料消耗"][hrid].count += count;
              session["材料消耗"][hrid].totalCost += cost;
          });

          return { materialCost, coinCost }; // Return both values
      }

    // ======================
    // NOTIFICATION SYSTEM
    // ======================

    function createNotificationContainer(headerElement) {
        const container = document.createElement("div");
        container.id = "enhancementNotificationContainer";
        Object.assign(container.style, {
            position: "absolute",
            top: "calc(100% + 5px)",
            left: "50%",
            transform: "translateX(-50%)",
            zIndex: "9999",
            display: "flex",
            flexDirection: "column",
            gap: "5px",
            width: "220px",
            pointerEvents: "none"
        });

        if (getComputedStyle(headerElement).position === "static") {
            headerElement.style.position = "relative";
        }

        headerElement.appendChild(container);
        return container;
    }

    function showNotification(message, type, level, isBlessed = false) {
        const headerElement = document.querySelector('[class^="Header_myActions"]');
        if (!headerElement) return;

        let container = document.getElementById("enhancementNotificationContainer");
        if (!container) {
            container = createNotificationContainer(headerElement);
        }

        if (type === "success") {
            createStandardNotification(container, level, isBlessed);

            if (level >= 10) {
                setTimeout(() => {
                    createMilestoneNotification(container, level);
                }, 300);
            }
        } else {
            createFailureNotification(container);
        }
    }

    function createStandardNotification(container, level, isBlessed) {
        const notification = document.createElement("div");
        notification.className = "enhancement-notification standard";

        Object.assign(notification.style, {
            padding: "10px 15px",
            borderRadius: STYLE.borderRadius.small,
            color: "white",
            fontWeight: "bold",
            boxShadow: STYLE.shadows.notification,
            transform: "translateY(-20px)",
            opacity: "0",
            transition: STYLE.transitions.medium,
            textAlign: "center",
            marginBottom: "5px",
            position: "relative",
            overflow: "hidden",
            pointerEvents: "auto",
            textShadow: STYLE.shadows.text
        });

        if (isBlessed) {
            Object.assign(notification.style, {
                background: STYLE.colors.blessed,
                color: "#8B4513"
            });
            notification.textContent = isZH ? `祝福强化! +${level}` : `BLESSED! +${level}`;
            addHolyEffects(notification);
        } else {
            notification.style.background = getLevelGradient(level);
            notification.textContent = isZH ? `强化成功 +${level}` : `Success +${level}`;
        }

        animateNotification(notification, container, level, isBlessed);
    }

    function createMilestoneNotification(container, level) {
        const milestone = document.createElement("div");
        milestone.className = "enhancement-notification milestone";

        Object.assign(milestone.style, {
            padding: "12px 15px",
            borderRadius: STYLE.borderRadius.small,
            color: "white",
            fontWeight: "bolder",
            boxShadow: STYLE.shadows.milestone,
            transform: "translateY(-20px)",
            opacity: "0",
            transition: STYLE.transitions.medium,
            textAlign: "center",
            marginBottom: "5px",
            animation: "pulse 2s infinite",
            position: "relative",
            overflow: "hidden",
            pointerEvents: "auto",
            textShadow: STYLE.shadows.text,
            font: STYLE.fonts.milestone
        });

        if (level >= 20) {
            milestone.style.background = `
                linear-gradient(
                    135deg,
                    ${STYLE.colors.mythic},
                    #ff0044,
                    #ff2200,
                    #ff0055
                )
            `;
            milestone.style.backgroundSize = "400% 400%";
            milestone.style.animation = "mythicPulse 2.5s ease-in-out infinite";

            milestone.style.color = "#fff";
            milestone.style.fontWeight = "bold";
            milestone.style.textShadow = `
                0 0 4px #ff0044,
                0 0 8px #ff0044,
                0 0 12px #ff0044
            `;
            milestone.textContent = isZH ? "命中注定!" : "IT. IS. DESTINY.";
        }
        else if (level >= 15) {
            milestone.style.background = `linear-gradient(135deg, ${STYLE.colors.legendary}, #FF6600)`;
            milestone.style.textShadow = "0 0 8px rgba(255, 102, 0, 0.8)";
            milestone.textContent = isZH ? "奇迹发生!" : "IT'S A MIRACLE!";
        }
        else if (level >= 10) {
            milestone.style.background = `linear-gradient(135deg, ${STYLE.colors.epic}, #8000FF)`;
            milestone.style.textShadow = "0 0 8px rgba(153, 51, 255, 0.8)";
            milestone.textContent = isZH ? "运气不错!" : "NICE LUCK!";
        }

        animateNotification(milestone, container, level, false);
    }

    function createFailureNotification(container) {
        const notification = document.createElement("div");
        notification.className = "enhancement-notification failure";

        Object.assign(notification.style, {
            padding: "10px 15px",
            borderRadius: STYLE.borderRadius.small,
            color: "white",
            fontWeight: "bold",
            boxShadow: STYLE.shadows.notification,
            transform: "translateY(-20px)",
            opacity: "0",
            transition: STYLE.transitions.medium,
            textAlign: "center",
            marginBottom: "5px",
            position: "relative",
            overflow: "hidden",
            pointerEvents: "auto",
            textShadow: STYLE.shadows.text,
            backgroundColor: STYLE.colors.danger,
            borderTop: "3px solid #C62828"
        });
        notification.textContent = isZH ? "强化失败!" : "Failed!";

        animateNotification(notification, container, 0, false);
    }

    function animateNotification(notification, container, level, isBlessed) {
        container.appendChild(notification);

        setTimeout(() => {
            notification.style.transform = "translateY(0)";
            notification.style.opacity = "1";
        }, 10);

        setTimeout(() => {
            notification.style.transform = "translateY(20px)";
            notification.style.opacity = "0";
            setTimeout(() => notification.remove(), 300);
        }, getNotificationDuration(level, isBlessed));

        notification.addEventListener("click", () => {
            notification.style.transform = "translateY(20px)";
            notification.style.opacity = "0";
            setTimeout(() => notification.remove(), 300);
        });
    }

    function addHolyEffects(notification) {
        const rays = document.createElement("div");
        rays.className = "holy-rays";
        Object.assign(rays.style, {
            position: "absolute",
            top: "0",
            left: "0",
            width: "100%",
            height: "100%",
            background: "radial-gradient(circle, transparent 20%, rgba(255,215,0,0.3) 70%)",
            pointerEvents: "none",
            animation: "raysRotate 4s linear infinite"
        });
        notification.appendChild(rays);

        for (let i = 0; i < 4; i++) {
            const cross = document.createElement("div");
            cross.textContent = "✝";
            Object.assign(cross.style, {
                position: "absolute",
                fontSize: "16px",
                animation: `floatUp ${3 + Math.random() * 2}s linear infinite`,
                opacity: "0.7",
                left: `${10 + (i * 25)}%`,
                top: "100%"
            });
            notification.appendChild(cross);
        }
    }

    function getLevelGradient(level) {
        if (level >= 20) {
            return `linear-gradient(135deg, ${STYLE.colors.mythic}, #FF0000)`;
        }
        else if (level >= 15) {
            return `linear-gradient(135deg, ${STYLE.colors.legendary}, #FF6600)`;
        }
        else if (level >= 10) {
            return `linear-gradient(135deg, ${STYLE.colors.epic}, #8000FF)`;
        }
        else if (level >= 5) {
            return "linear-gradient(135deg, #3399FF, #0066FF)";
        }
        else if (level >= 1) {
            return "linear-gradient(135deg, #33CC33, #009900)";
        }
        else {
            return "linear-gradient(135deg, #CCCCCC, #999999)";
        }
    }

    function getNotificationDuration(level, isBlessed) {
        if (isBlessed) return 5000;
        if (level >= 20) return 5000;
        if (level >= 15) return 4000;
        if (level >= 10) return 3000;
        return 2500;
    }

    // ======================
    // ENHANCEMENT TRACKING
    // ======================

    function hookWS() {
        const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
        const oriGet = dataProperty.get;

        dataProperty.get = hookedGet;
        Object.defineProperty(MessageEvent.prototype, "data", dataProperty);

        function hookedGet() {
            const socket = this.currentTarget;
            if (!(socket instanceof WebSocket)) return oriGet.call(this);
            if (!socket.url.includes("api.milkywayidle.com/ws") && !socket.url.includes("api-test.milkywayidle.com/ws")) {
                return oriGet.call(this);
            }

            const message = oriGet.call(this);
            Object.defineProperty(this, "data", { value: message });
            return handleMessage(message);
        }
    }

    function handleMessage(message) {
        try {
            const obj = JSON.parse(message);
            if (!obj) return message;

            if (obj.type === "init_character_data") {
                handleInitData();
            } else if (obj.type === "action_completed" && obj.endCharacterAction?.actionHrid === "/actions/enhancing/enhance") {
                handleEnhancement(obj.endCharacterAction);
            }
        } catch (error) {
            console.error("Error processing message:", error);
        }
        return message;
    }

    function handleInitData() {
        const initClientData = localStorage.getItem('initClientData');
        if (!initClientData) {
            setTimeout(handleInitData, 500); // Retry if not ready
            return;
        }

        try {
            const data = JSON.parse(initClientData);
            item_hrid_to_name = {};

            // Properly map all item names
            for (const [hrid, details] of Object.entries(data.itemDetailMap)) {
                item_hrid_to_name[hrid] = details.name;
            }

            console.log("Translations loaded:", item_hrid_to_name);
        } catch (error) {
            console.error('Data parsing failed:', error);
        }
    }

    function parseItemHash(primaryItemHash) {
      try {
          // Handle different possible formats:
          // 1. "/item_locations/inventory::/items/enhancers_bottoms::0" (level 0)
          // 2. "161296::/item_locations/inventory::/items/enhancers_bottoms::5" (level 5)
          // 3. Direct HRID like "/items/enhancers_bottoms" (no level)

          let itemHrid = null;
          let enhancementLevel = 0; // Default to 0 if not specified

          // Split by :: to parse components
          const parts = primaryItemHash.split('::');

          // Find the part that starts with /items/
          const itemPart = parts.find(part => part.startsWith('/items/'));
          if (itemPart) {
              itemHrid = itemPart;
          }
          // If no /items/ found but it's a direct HRID
          else if (primaryItemHash.startsWith('/items/')) {
              itemHrid = primaryItemHash;
          }

          // Try to extract enhancement level (last part after ::)
          const lastPart = parts[parts.length - 1];
          if (lastPart && !isNaN(lastPart)) {
              enhancementLevel = parseInt(lastPart, 10);
          }

          if (!itemHrid) {
              console.log('[Parse] Could not find item HRID in:', primaryItemHash);
              return { hrid: null, level: 0 };
          }

          return {
              hrid: itemHrid,
              level: enhancementLevel
          };
      } catch (e) {
          console.error('[Parse] Error parsing primaryItemHash:', e);
          return { hrid: null, level: 0 };
      }
  }

  function calculateSessionDuration(session) {
      if (!session["会话数据"]?.开始时间) return 0;
      const now = Date.now();
      return now - session["会话数据"].开始时间;
  }

  function calculateXpPerHour(session) {

      // Don't calculate for completed sessions
      if (session["强化状态"] === "已完成") {
          return session["会话数据"]?.["每小时经验"] || 0;
      }

      const durationMs = calculateSessionDuration(session);
      const totalXP = session["会话数据"]?.["总经验"] || 0;

      // Don't show rate until we have meaningful data
      if (durationMs < 60000 || totalXP < 1000) { // First minute or <1k XP
          return 0; // Or return null and hide the display
      }

      // Weighted average that becomes more accurate over time
      const weight = Math.min(1, session["尝试历史"]?.length / 10); // 0-1 based on 10 attempts
      const rawRate = (totalXP / (durationMs / (1000 * 60 * 60)));

      // For first few attempts, blend with a conservative estimate
      if (session["尝试历史"]?.length < 5) {
          const baseRate = 50000; // Conservative base rate (adjust per your game)
          return Math.floor((rawRate * weight) + (baseRate * (1 - weight)));
      }

      return Math.floor(rawRate);
  }

  function formatDuration(ms) {
      const seconds = Math.floor((ms / 1000) % 60);
      const minutes = Math.floor((ms / (1000 * 60)) % 60);
      const hours = Math.floor(ms / (1000 * 60 * 60));

      return `${hours}h ${minutes}m ${seconds}s`;
  }

  // Source: https://doh-nuts.github.io/Enhancelator/
  function Enhancelate(input_data, protect_at) {
      const success_rate = [
          50, //+1
          45, //+2
          45, //+3
          40, //+4
          40, //+5
          40, //+6
          35, //+7
          35, //+8
          35, //+9
          35, //+10
          30, //+11
          30, //+12
          30, //+13
          30, //+14
          30, //+15
          30, //+16
          30, //+17
          30, //+18
          30, //+19
          30, //+20
      ];

      // Get base item level
      const itemLevel = getBaseItemLevel(input_data.item_hrid);

      const guzzling_bonus = Number(input_data.guzzling_bonus);

      // Calculate enhancer bonus (matches update_values() logic)
      const enhancerTemp = input_data.enhancer_bonus;
      const enhancer_bonus = Number(enhancerTemp.toFixed(4) * 100);


      // Calculate effective level with tea bonuses (matches update_values() logic)
      const effective_level = input_data.enhancing_level +
          (input_data.tea_enhancing ? 3 * guzzling_bonus : 0) +
          (input_data.tea_super_enhancing ? 6 * guzzling_bonus : 0) +
          (input_data.tea_ultra_enhancing ? 8 * guzzling_bonus : 0);

      // Calculate total bonus (matches update_values() logic)
      let total_bonus;
      if (effective_level >= itemLevel) {
          total_bonus = 1 + (0.05 * (effective_level + input_data.observatory_level - itemLevel) + enhancer_bonus) / 100;
      } else {
          total_bonus = (1 - (0.5 * (1 - (effective_level) / itemLevel))) + ((0.05 * input_data.observatory_level) + enhancer_bonus) / 100;
      }


      // Simulation - create 21x21 matrix (0-20)
      let markov = math.zeros(20, 20);
      const success_chances = success_rate.map(rate => (rate / 100.0) * total_bonus);

      for (let i = 0; i < input_data.stop_at; i++) {
        const success_chance = success_chances[i];
        let remaining_success_chance = success_chance;
        let remaining_fail_chance = 1.0 - success_chance;


          // If protect_at < 2, we skip protections and set the destination to 0
          const destination = (protect_at >= 2 && i >= protect_at) ? i - 1 : 0;

          // Handle blessed tea (+2 enhancement chance)
           if (input_data.tea_blessed) {
               const plus2_chance = success_chance * 0.01 * guzzling_bonus;
               markov.set([i, Math.min(i + 2, input_data.stop_at)], plus2_chance);
               remaining_success_chance -= plus2_chance;
           }

           // Set remaining probabilities
           markov.set([i, Math.min(i + 1, input_data.stop_at)], remaining_success_chance);
           markov.set([i, destination], remaining_fail_chance);
      }

      // Set absorbing state
      markov.set([input_data.stop_at, input_data.stop_at], 1.0);

      // Calculate expected attempts
      let Q = markov.subset(math.index(math.range(0, input_data.stop_at), math.range(0, input_data.stop_at)));
      let M = math.inv(math.subtract(math.identity(input_data.stop_at), Q));
      let attemptsArray = M.subset(math.index(math.range(0, 1), math.range(0, input_data.stop_at)));
      let attempts = math.sum(attemptsArray);

      // Calculate expected protections
      let protects = 0;
      if (protect_at >= 2) {
          let protectAttempts = M.subset(math.index(math.range(0, 1), math.range(protect_at, input_data.stop_at)));
          let protectAttemptsArray = (typeof protectAttempts === 'number') ?
              [protectAttempts] :
              math.flatten(math.row(protectAttempts, 0).valueOf());
          protects = protectAttemptsArray.map((a, i) =>
              a * markov.get([i + protect_at, i + protect_at - 1])
          ).reduce((a, b) => a + b, 0);
      }

      // Calculate action time (matches original logic)
      const tea_speed_bonus = input_data.tea_enhancing ? 2 * guzzling_bonus :
                             input_data.tea_super_enhancing ? 4 * guzzling_bonus :
                             input_data.tea_ultra_enhancing ? 6 * guzzling_bonus : 0;

      const perActionTimeSec = (
          12 /
          (1 +
              (input_data.enhancing_level > itemLevel
                  ? ((input_data.effective_level + input_data.observatory_level - itemLevel + input_data.glove_bonus + tea_speed_bonus) / 100)
                  : ((input_data.observatory_level + input_data.glove_bonus + tea_speed_bonus) / 100)
              )
          )
      ).toFixed(2);

      const result = {
          actions: attempts,
          protect_count: protects,
          totalActionTimeSec: perActionTimeSec * attempts,
          totalActionTimeStr: formatDuration(perActionTimeSec * attempts * 1000),
          success_rate: success_chances[input_data.stop_at - 1] // Success rate at target level
      };

      return result;
  }

  function initializeEnhancelation(itemHRID, action) {
      const charData = JSON.parse(localStorage.getItem('init_character_data'));
      const clientData = JSON.parse(localStorage.getItem('initClientData'));

      // Extract active enhancing teas
      const enhancingTeas = charData.actionTypeDrinkSlotsMap?.['/action_types/enhancing'] || [];
      const activeTeas = enhancingTeas.filter(tea => tea?.isActive);

      // Map tea HRIDs to their types
      const teaMap = {
          '/items/wisdom_tea': 'wisdom',
          '/items/tea_enhancing': 'regular',
          '/items/super_enhancing_tea': 'super',
          '/items/ultra_enhancing_tea': 'ultra',
          '/items/blessed_tea': 'blessed'
      };

      // Check for specific teas
      let hasRegular = false;
      let hasSuper = false;
      let hasUltra = false;
      let hasBlessed = false;

      activeTeas.forEach(tea => {
          const teaType = teaMap[tea.itemHrid];
          if (teaType === 'regular') hasRegular = true;
          if (teaType === 'super') hasSuper = true;
          if (teaType === 'ultra') hasUltra = true;
          if (teaType === 'blessed') hasBlessed = true;
      });

      // Map player data to input_data structure
      let input_data = {
          item_hrid: itemHRID,
          stop_at: action.enhancingMaxLevel,
          enhancing_level: charData?.characterSkills.find(skill => skill.skillHrid === '/skills/enhancing').level || 100,
          observatory_level: charData?.characterHouseRoomMap['/house_rooms/observatory'].level || 0,
          enhancer_bonus: calculateEnhancerBonus(charData),
          glove_bonus: calculateGloveBonus(charData),
          tea_enhancing: hasRegular,
          tea_super_enhancing: hasSuper,
          tea_ultra_enhancing: hasUltra,
          tea_blessed: hasBlessed,
          guzzling_bonus: 1 + (charData.noncombatStats.drinkConcentration ?? 0.0),
          priceAskBidRatio: 1
      };

    // Helper functions to calculate equipment bonuses
    function calculateEnhancerBonus(charData) {
        // Check for enhancing tool bonuses
        const equipmentBuffs = charData.equipmentActionTypeBuffsMap?.['/action_types/enhancing'] || [];
        const enhancerBuff = equipmentBuffs.find(buff =>
            buff.typeHrid === '/buff_types/enhancing_success'
        );

        // Default values if no buff found
        return enhancerBuff?.ratioBoost || 0.0; // no bonus from tool
    }

    function calculateGloveBonus(charData) {
        // Check for glove speed bonuses
        const equipmentBuffs = charData.equipmentActionTypeBuffsMap?.['/action_types/enhancing'] || [];
        const speedBuff = equipmentBuffs.find(buff =>
            buff.typeHrid === '/buff_types/action_speed'
        );

        // Default values if no buff found
        return speedBuff?.flatBoost || 10; // Base value for level 0 gloves
    }

    return input_data
}

function calculateEnhancementProbabilities(input_data, itemHrid, currentLevel, targetLevel, protectAtLevel) {
    // Validate inputs
    if (!itemHrid || targetLevel <= currentLevel) return null;

    try {
        const results = Enhancelate(
            {
                ...input_data,
                item_hrid: itemHrid,
                stop_at: targetLevel
            },
            protectAtLevel
        );

        return {
            expectedAttempts: Math.round(results.actions),
            expectedProtections: Math.round(results.protect_count),
            expectedTime: results.totalActionTimeStr,
            successChance: `${((results.successRate || 0) * 100).toFixed(1)}%`,
            rawData: results // Keep original for debugging
        };
    } catch (error) {
        console.error("[Enhancement] Calculation failed", { itemHrid, currentLevel, targetLevel, error });
        return null;
    }
}

    // Updated handleEnhancement with cost tracking and localStorage saving
    function handleEnhancement(action) {
        'use strict';
        // console.log(action);

        // 1. Extract enhancement data
        const { hrid: itemHRID, level: newLevel } = parseItemHash(action.primaryItemHash);
        const currentCount = action.currentCount;

        if (!itemHRID) return;

        // 2. Check if this is a new item (which means new session)
        const isNewItemSession = currentCount === 1 ||
                               !enhancementData[currentTrackingIndex] ||
                               currentCount <= enhancementData[currentTrackingIndex]["强化次数"];

        // 3. Start new session if needed
        if (currentTrackingIndex === 0 || isNewItemSession) {
            startNewItemSession(action);
        }

        const session = enhancementData[currentTrackingIndex];

        // 8. Track costs
        const preUpdateTotal = session["总成本"];
        const { materialCost, coinCost } = trackMaterialCosts(itemHRID);
        const existingProtectionCost = session["其他数据"]["保护总成本"] || 0;

        // 4. Special case: First attempt should be a no-op (just record the initial state)
        if (currentCount === 1) {
            // Record initial state but don't count as success/failure
            session["尝试历史"].push({
                attemptNumber: currentCount,
                previousLevel: newLevel,
                newLevel: newLevel,
                timestamp: Date.now(),
                wasSuccess: false,
                wasBlessed: false,
                isInitialState: true  // New flag to mark initial state
            });

            // Initialize level data
            if (!session["强化数据"][newLevel]) {
                session["强化数据"][newLevel] = {
                    "成功次数": 0,
                    "失败次数": 0,
                    "成功率": 0
                };
            }

            // Update session metadata
            session["强化次数"] = currentCount;
            session["最后更新时间"] = Date.now();
            session["会话数据"].最后更新时间 = Date.now();
            session["会话数据"].持续时间 = calculateSessionDuration(session);

            saveEnhancementData();
            updateStatsOnly();
            return;  // Early return for first attempt
        }

        // 5. Determine previous level for subsequent attempts
        const previousLevel = session["尝试历史"]?.length > 0
            ? session["尝试历史"][session["尝试历史"].length - 1].newLevel
            : newLevel; // Fallback to current level

        // 6. Record this attempt
        const wasBlessed = (newLevel - previousLevel) >= 2;
        const isFailure = (newLevel < previousLevel) || (previousLevel === 0 && newLevel === 0);
        session["尝试历史"].push({
            attemptNumber: currentCount,
            previousLevel: previousLevel,
            newLevel: newLevel,
            timestamp: Date.now(),
            wasSuccess: newLevel > previousLevel,
            wasBlessed: wasBlessed
        });

        // 7. Initialize level data if needed
        if (!session["强化数据"][newLevel]) {
            session["强化数据"][newLevel] = {
                "成功次数": 0,
                "失败次数": 0,
                "成功率": 0
            };
        }

        // 9. Handle success/failure
        if (newLevel > previousLevel) {
            handleSuccess(session["强化数据"][previousLevel], newLevel, wasBlessed, session);
        } else if (isFailure) {
            handleFailure(action, session["强化数据"][previousLevel], session);
        }
        // No else case - if newLevel === previousLevel and not level 0, it's neither success nor failure

        // 10. Update protection costs if failure occurred
        const newProtectionCost = session["其他数据"]["保护总成本"] || 0;
        const protectionCostDelta = newProtectionCost - existingProtectionCost;

        // 11. Update session data
        session["总成本"] = preUpdateTotal + materialCost + coinCost + protectionCostDelta;
        session["强化次数"] = currentCount;
        session["最后更新时间"] = Date.now();
        session["会话数据"].最后更新时间 = Date.now();
        session["会话数据"].持续时间 = calculateSessionDuration(session);

        // 12. Check for target achievement
        if (newLevel >= session["其他数据"]["目标强化等级"]) {
            finalizeCurrentSession();
            showTargetAchievedCelebration(newLevel, session["其他数据"]["目标强化等级"]);
            session["强化状态"] = "已完成";
        }

        updateStats(session["强化数据"][previousLevel]);

        // 13. Save and update UI
        saveEnhancementData();
        updateStatsOnly();
    }

    function finalizeCurrentSession() {
        const session = enhancementData[currentTrackingIndex];
        if (!session) return;

        // Calculate final stats
        const lastAttempt = session["尝试历史"].slice(-1)[0];
        const finalDuration = lastAttempt.timestamp - session["会话数据"].开始时间;
        const totalXP = session["会话数据"].总经验 || 0;
        const xpPerHour = Math.floor(totalXP / (finalDuration / (1000 * 60 * 60))) || 0;

        // Freeze the session data
        session["会话数据"] = {
            ...session["会话数据"],
            "finalDuration": finalDuration,
            "finalXpPerHour": xpPerHour,
            "持续时间": finalDuration,
            "每小时经验": xpPerHour,
            "最后更新时间": Date.now()
        };

        session["强化状态"] = "已完成";
        session["isLive"] = false;

        saveEnhancementData();
    }

    function startNewItemSession(action) {
        // Finalize previous session if exists
        if (enhancementData[currentTrackingIndex]) {
            finalizeCurrentSession();
        }

        const { hrid: itemHRID, level: newLevel } = parseItemHash(action.primaryItemHash);
        if (!itemHRID) return;

        currentTrackingIndex++;
        enhancementData[currentTrackingIndex] = createItemSessionData(action, itemHRID, newLevel);
        currentViewingIndex = currentTrackingIndex;

        debouncedUpdateFloatingUI();
    }

    function createItemSessionData(action, itemHRID, initialLevel) {
        const protectionHrid = getProtectionItemHrid(action);
        const isProtected = protectionHrid !== null;
        const now = Date.now();
        let input_data = initializeEnhancelation(itemHRID, action);
        // Calculate initial probabilities
         const probabilities = calculateEnhancementProbabilities(
             input_data,
             itemHRID,
             0, // Starting from +0
             action.enhancingMaxLevel,
             action.enhancingProtectionMinLevel
         );

        return {
            "强化数据": {
                [initialLevel]: {
                    "成功次数": 0,
                    "失败次数": 0,
                    "成功率": 0
                }
            },
            "其他数据": {
                "物品HRID": itemHRID,
                "物品名称": item_hrid_to_name[itemHRID] || "Unknown",
                "目标强化等级": action.enhancingMaxLevel,
                "是否保护": isProtected,
                "保护物品HRID": protectionHrid,
                "保护物品名称": isProtected ? (item_hrid_to_name[protectionHrid] || protectionHrid) : null,
                "保护消耗总数": 0,
                "保护总成本": 0,
                "保护最小等级": action.enhancingProtectionMinLevel,
                "初始概率预测": probabilities // Store the initial calculation
            },
            "材料消耗": {},
            "硬币消耗": { count: 0, totalCost: 0 },
            "总成本": 0,
            "强化次数": 0,
            "尝试历史": [],
            "会话数据": {
                "开始时间": now,
                "最后更新时间": now,
                "总经验": 0,
                "持续时间": 0,
                "每小时经验": 0,  // Add this new field
                "finalDuration": null,  // Add this for completed sessions
                "finalXpPerHour": null  // Add this for completed sessions
            },
            "强化状态": "进行中",
            "isLive": true  // Add this flag to track live sessions
        };
    }

    // Keep handleSuccess as is but ensure it's safe
    function handleSuccess(levelData, newLevel, wasBlessed, session) {
        try {
            // 1. Update success count
            levelData["成功次数"] = (levelData["成功次数"] || 0) + 1;

            // 2. Calculate XP gain from last attempt
            const xpGain = session["尝试历史"]?.length > 0
                ? calculateSuccessXP(
                    session["尝试历史"][session["尝试历史"].length - 1].previousLevel,
                    session["其他数据"]["物品HRID"]
                  )
                : calculateSuccessXP(newLevel, session["其他数据"]["物品HRID"]); // Fallback for first attempt

            // 3. Update XP tracking (using new 会话数据 structure)
            session["会话数据"].总经验 = (session["会话数据"].总经验 || 0) + xpGain;

            // 4. Show appropriate notification
            showNotification(
                wasBlessed
                    ? (isZH ? `祝福强化! +${newLevel}` : `BLESSED! +${newLevel}`)
                    : (isZH ? `强化成功 +${newLevel}` : `Success +${newLevel}`),
                "success",
                newLevel,
                wasBlessed
            );

        } catch (e) {
            console.error("Error in handleSuccess:", e);
            showNotification(isZH ? "强化跟踪错误" : "Enhancement tracking error", "failure", 0);
        }
    }

    function handleFailure(action, levelData, session) {
        try {
            // 1. Update failure count
            levelData["失败次数"] = (levelData["失败次数"] || 0) + 1;

            // 2. Calculate XP gain from last attempt
            const xpGain = session["尝试历史"]?.length > 0
                ? calculateFailureXP(
                    session["尝试历史"][session["尝试历史"].length - 1].previousLevel,
                    session["其他数据"]["物品HRID"]
                  )
                : calculateFailureXP(0, session["其他数据"]["物品HRID"]); // Fallback for first attempt

            // 3. Update XP tracking
            session["会话数据"].总经验 = (session["会话数据"].总经验 || 0) + xpGain;
            const currentLevel = session["尝试历史"]?.slice(-1)[0]?.previousLevel || 0;

            // 4. Handle protection if enabled
            if (session["其他数据"]?.["是否保护"] &&
                currentLevel >= session["其他数据"]?.["保护最小等级"]) {

                const protectionHrid = session["其他数据"]["保护物品HRID"];
                if (protectionHrid) {
                    // Initialize protection tracking if needed
                    session["保护消耗"] = session["保护消耗"] || {};
                    session["保护消耗"][protectionHrid] = session["保护消耗"][protectionHrid] || {
                        name: session["其他数据"]["保护物品名称"] || protectionHrid,
                        count: 0,
                        totalCost: 0
                    };

                    // Update protection costs
                    const protectionCost = getMarketPrice(protectionHrid) || 0;
                    session["其他数据"]["保护消耗总数"] = (session["其他数据"]["保护消耗总数"] || 0) + 1;
                    session["其他数据"]["保护总成本"] = (session["其他数据"]["保护总成本"] || 0) + protectionCost;

                    session["保护消耗"][protectionHrid].count += 1;
                    session["保护消耗"][protectionHrid].totalCost += protectionCost;
                }
            }

            showNotification(isZH ? "强化失败!" : "Failed!", "failure", 0);
        } catch (e) {
            console.error("Error in handleFailure:", e);
            showNotification(isZH ? "强化跟踪错误" : "Enhancement tracking error", "failure", 0);
        }
    }

    function updateStats(levelData) {
        // Safe access with default values
        const success = levelData["成功次数"] || 0;
        const failure = levelData["失败次数"] || 0;
        levelData["成功率"] = (success + failure) > 0 ? success / (success + failure) : 0;
    }

    function getEnhancementState(currentItem) {
        const highestSuccessLevel = Math.max(...Object.keys(currentItem).filter(level => currentItem[level]["成功次数"] > 0));
        return (highestSuccessLevel + 1 >= enhancementData[currentTrackingIndex]["其他数据"]["目标强化等级"]) ? "强化成功" : "强化失败";
    }

    function getProtectionItemHrid(action) {
        // If protection is disabled (min level < 2)
        if (action.enhancingProtectionMinLevel < 2) {
            return null;
        }

        // Extract protection item from secondaryItemHash
        if (action.secondaryItemHash) {
            const parts = action.secondaryItemHash.split('::');
            if (parts.length >= 3 && parts[2].startsWith('/items/')) {
                return parts[2];
            }
        }

        // No protection being used
        return null;
    }

    function translateItemName(hrid, fallbackName) {

        if (!isZH) {
            return fallbackName;
        }

        try {
            const gameData = JSON.parse(localStorage.getItem('initClientData'));

            if (gameData?.itemDetailMap?.[hrid]?.name) {
                const translated = gameData.itemDetailMap[hrid].name;
                return translated;
            }
        } catch (e) {
            console.error("Translation error:", e);
        }

        return item_hrid_to_name?.[hrid] || fallbackName || "Unknown";
    }

    function getCurrentItemName(session) {
        if (!session["其他数据"] || !session["其他数据"]["物品HRID"]) return "Unknown";
        const itemHRID = session["其他数据"]["物品HRID"];

        // Always get fresh translation from game data
        if (item_hrid_to_name && item_hrid_to_name[itemHRID]) {
            return item_hrid_to_name[itemHRID];
        }

        // Fallback to English if needed
        const initData = JSON.parse(localStorage.getItem('initClientData') || '{}');
        if (initData.itemDetailMap && initData.itemDetailMap[itemHRID]) {
            return initData.itemDetailMap[itemHRID].name;
        }

        return "Unknown";
    }

    function showTargetAchievedCelebration(achievedLevel, targetLevel) {
        const celebration = document.createElement("div");
        celebration.id = "enhancementCelebration";
        Object.assign(celebration.style, {
            position: "fixed",
            top: "0",
            left: "0",
            width: "100%",
            height: "100%",
            zIndex: "99999",
            pointerEvents: "none",
            display: "flex",
            flexDirection: "column",
            justifyContent: "center",
            alignItems: "center",
            background: "radial-gradient(circle, rgba(0,0,0,0.7), transparent 70%)"
        });

        const text = document.createElement("div");
        text.textContent = isZH ? `目标达成! +${achievedLevel}` : `TARGET ACHIEVED! +${achievedLevel}`;
        Object.assign(text.style, {
            fontSize: "3rem",
            fontWeight: "900",
            color: "#FFD700",
            textShadow: "0 0 20px #FF0000, 0 0 30px #FF8C00",
            animation: "celebrateText 2s ease-out both"
        });
        celebration.appendChild(text);

        for (let i = 0; i < 100; i++) {
            const confetti = document.createElement("div");
            Object.assign(confetti.style, {
                position: "absolute",
                width: "10px",
                height: "10px",
                backgroundColor: getRandomColor(),
                borderRadius: "50%",
                left: "50%",
                top: "50%",
                opacity: "0",
                animation: `confettiFly ${Math.random() * 2 + 1}s ease-out ${Math.random() * 0.5}s both`
            });
            celebration.appendChild(confetti);
        }

        for (let i = 0; i < 8; i++) {
            setTimeout(() => {
                createFireworkBurst(celebration);
            }, i * 300);
        }

        document.body.appendChild(celebration);

        setTimeout(() => {
            celebration.style.opacity = "0";
            celebration.style.transition = "opacity 1s ease-out";
            setTimeout(() => celebration.remove(), 1000);
        }, 4000);
    }

    function createFireworkBurst(container) {
        const burst = document.createElement("div");
        Object.assign(burst.style, {
            position: "absolute",
            left: `${Math.random() * 80 + 10}%`,
            top: `${Math.random() * 80 + 10}%`,
            width: "5px",
            height: "5px",
            borderRadius: "50%",
            background: getRandomColor(),
            boxShadow: `0 0 10px 5px ${getRandomColor()}`,
            animation: `fireworkExpand 0.8s ease-out both`
        });
        container.appendChild(burst);

        for (let i = 0; i < 30; i++) {
            setTimeout(() => {
                const particle = document.createElement("div");
                Object.assign(particle.style, {
                    position: "absolute",
                    left: burst.style.left,
                    top: burst.style.top,
                    width: "3px",
                    height: "3px",
                    backgroundColor: burst.style.background,
                    borderRadius: "50%",
                    animation: `fireworkTrail ${Math.random() * 0.5 + 0.5}s ease-out both`
                });
                container.appendChild(particle);
            }, i * 20);
        }
    }

    function getRandomColor() {
        const colors = ["#FF0000", "#FF8C00", "#FFD700", "#4CAF50", "#2196F3", "#9C27B0"];
        return colors[Math.floor(Math.random() * colors.length)];
    }

    function renderStats(selectedKey) {
        const statsContainer = document.querySelector("#ultimateEnhancementStatsContainer");
        if (!statsContainer) return;
        statsContainer.innerHTML = "";

        const item = enhancementData[selectedKey];
        if (!item || !item["强化数据"]) return;

        const headers = ["等级", "成功", "失败", "概率"];
        headers.forEach(headerText => {
            const headerDiv = document.createElement("div");
            headerDiv.style.fontWeight = "bold";
            headerDiv.textContent = isZH ? headerText :
                headerText === "等级" ? "Level" :
                headerText === "成功" ? "Success" :
                headerText === "失败" ? "Failure" :
                "Success Rate";
            statsContainer.appendChild(headerDiv);
        });

        const totalSuccess = Object.values(item["强化数据"]).reduce((acc, val) => acc + (val["成功次数"] || 0), 0);
        const totalFailure = Object.values(item["强化数据"]).reduce((acc, val) => acc + (val["失败次数"] || 0), 0);
        const totalCount = totalSuccess + totalFailure;
        const totalRate = totalCount > 0 ? (totalSuccess / totalCount * 100).toFixed(2) : "0.00";

        ["总计", totalSuccess, totalFailure, `${totalRate}%`].forEach((totalText, index) => {
            const totalDiv = document.createElement("div");
            totalDiv.textContent = isZH ? totalText : index === 0 ? "Total" : totalText;
            statsContainer.appendChild(totalDiv);
        });

        Object.keys(item["强化数据"])
            .map(Number)
            .sort((a, b) => b - a)
            .forEach(level => {
                const levelData = item["强化数据"][level];
                const levelDivs = [
                    level,
                    levelData["成功次数"] || 0,
                    levelData["失败次数"] || 0,
                    `${((levelData["成功率"] || 0) * 100).toFixed(2)}%`
                ];

                levelDivs.forEach(data => {
                    const dataDiv = document.createElement("div");
                    dataDiv.textContent = data;
                    statsContainer.appendChild(dataDiv);
                });
            });
    }

    // ======================
    // FLOATING UI SYSTEM (F9 TOGGLE)
    // ======================

    let floatingUI = null;
    let cleanupFunctions = [];

    function createFloatingUI() {
        if (floatingUI && document.body.contains(floatingUI)) {
            return floatingUI;
        }

        // Create main container (existing code remains the same)
        floatingUI = document.createElement("div");
        floatingUI.id = "enhancementFloatingUI";
        Object.assign(floatingUI.style, {
            position: "fixed",
            top: "50px",
            left: "50px",
            zIndex: "9998",
            fontSize: "14px",
            padding: "0",
            borderRadius: STYLE.borderRadius.medium,
            boxShadow: '0 8px 32px rgba(0, 0, 0, 0.6)',
            overflow: "hidden",
            width: "350px",
            minHeight: "auto",
            background: 'rgba(25, 0, 35, 0.92)',
            backdropFilter: 'blur(12px)',
            border: `1px solid ${STYLE.colors.primary}`,
            color: STYLE.colors.textPrimary,
            willChange: "transform",
            transform: "translateZ(0)",
            backfaceVisibility: "hidden",
            perspective: "1000px",
            display: "flex",
            flexDirection: "column"
        });

        // Create header
        const header = document.createElement("div");
        header.id = "enhancementPanelHeader";
        Object.assign(header.style, {
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            cursor: "move",
            padding: "10px 15px",
            background: STYLE.colors.headerBg,
            borderBottom: `1px solid ${STYLE.colors.border}`,
            userSelect: "none",
            WebkitUserSelect: "none",
            flexShrink: "0"
        });

        // Create title with session counter
        const titleContainer = document.createElement("div");
        titleContainer.style.display = "flex";
        titleContainer.style.alignItems = "center";
        titleContainer.style.gap = "10px";

        const title = document.createElement("span");
        title.textContent = isZH ? "强化追踪器" : "Enhancement Tracker";
        title.style.fontWeight = "bold";

        const sessionCounter = document.createElement("span");
        sessionCounter.id = "enhancementSessionCounter";
        sessionCounter.style.fontSize = "12px";
        sessionCounter.style.opacity = "0.7";
        sessionCounter.style.marginLeft = "5px";

        titleContainer.appendChild(title);
        titleContainer.appendChild(sessionCounter);

        // Create navigation arrows container
        const navContainer = document.createElement("div");
        Object.assign(navContainer.style, {
            display: "flex",
            gap: "5px",
            alignItems: "center",
            marginLeft: "auto"
        });

        // Create clear sessions button
         const clearButton = document.createElement("button");
         clearButton.innerHTML = "🗑️";
         clearButton.title = isZH ? "清除所有会话" : "Clear all sessions";
         Object.assign(clearButton.style, {
             background: "none",
             border: "none",
             color: STYLE.colors.textPrimary,
             cursor: "pointer",
             fontSize: "14px",
             padding: "2px 8px",
             borderRadius: "3px",
             transition: STYLE.transitions.fast,
             marginRight: "5px"
         });
         clearButton.addEventListener("mouseover", () => {
             clearButton.style.color = STYLE.colors.danger;
             clearButton.style.background = "rgba(255, 0, 0, 0.1)";
         });
         clearButton.addEventListener("mouseout", () => {
             clearButton.style.color = STYLE.colors.textPrimary;
             clearButton.style.background = "none";
         });
         clearButton.addEventListener("click", (e) => {
             e.stopPropagation();
             clearAllSessions();
         });

        // Create previous arrow
        const prevArrow = document.createElement("button");
        prevArrow.innerHTML = "&larr;";
        Object.assign(prevArrow.style, {
            background: "none",
            border: "none",
            color: STYLE.colors.textPrimary,
            cursor: "pointer",
            fontSize: "14px",
            padding: "2px 8px",
            borderRadius: "3px",
            transition: STYLE.transitions.fast
        });
        prevArrow.addEventListener("mouseover", () => {
            prevArrow.style.color = STYLE.colors.accent;
            prevArrow.style.background = "rgba(255, 255, 255, 0.1)";
        });
        prevArrow.addEventListener("mouseout", () => {
            prevArrow.style.color = STYLE.colors.textPrimary;
            prevArrow.style.background = "none";
        });
        prevArrow.addEventListener("click", (e) => {
            e.stopPropagation();
            navigateSessions(-1);
        });

        // Create next arrow
        const nextArrow = document.createElement("button");
        nextArrow.innerHTML = "&rarr;";
        Object.assign(nextArrow.style, {
            background: "none",
            border: "none",
            color: STYLE.colors.textPrimary,
            cursor: "pointer",
            fontSize: "14px",
            padding: "2px 8px",
            borderRadius: "3px",
            transition: STYLE.transitions.fast
        });
        nextArrow.addEventListener("mouseover", () => {
            nextArrow.style.color = STYLE.colors.accent;
            nextArrow.style.background = "rgba(255, 255, 255, 0.1)";
        });
        nextArrow.addEventListener("mouseout", () => {
            nextArrow.style.color = STYLE.colors.textPrimary;
            nextArrow.style.background = "none";
        });
        nextArrow.addEventListener("click", (e) => {
            e.stopPropagation();
            navigateSessions(1);
        });

        // Add toggle button
        const toggleButton = document.createElement("button");
        toggleButton.innerHTML = "👁️"; // Down arrow icon (will change based on state)
        toggleButton.title = isZH ? "切换面板显示" : "Toggle panel display";
        Object.assign(toggleButton.style, {
            background: "none",
            border: "none",
            color: STYLE.colors.textPrimary,
            cursor: "pointer",
            fontSize: "14px",
            padding: "2px 8px",
            borderRadius: "3px",
            transition: STYLE.transitions.fast,
            marginLeft: "5px"
        });
        toggleButton.addEventListener("mouseover", () => {
            toggleButton.style.color = STYLE.colors.accent;
            toggleButton.style.background = "rgba(255, 255, 255, 0.1)";
        });
        toggleButton.addEventListener("mouseout", () => {
            toggleButton.style.color = STYLE.colors.textPrimary;
            toggleButton.style.background = "none";
        });
        toggleButton.addEventListener("click", (e) => {
            e.stopPropagation();
            toggleFloatingUI();
        });


        // Add elements to header
        header.appendChild(clearButton);
        header.appendChild(titleContainer);
        navContainer.appendChild(toggleButton);
        navContainer.appendChild(prevArrow);
        navContainer.appendChild(nextArrow);
        header.appendChild(navContainer);

        // Rest of the existing code (drag functionality, content area, etc.) remains the same
        let isDragging = false;
        let offsetX, offsetY;
        let animationFrameId;

        header.addEventListener("mousedown", (e) => {
            isDragging = true;
            offsetX = e.clientX - floatingUI.offsetLeft;
            offsetY = e.clientY - floatingUI.offsetTop;
            floatingUI.classList.add("dragging");
            e.preventDefault();
        });

        const mouseMoveHandler = (e) => {
            if (!isDragging) return;
            cancelAnimationFrame(animationFrameId);
            animationFrameId = requestAnimationFrame(() => {
                floatingUI.style.left = `${e.clientX - offsetX}px`;
                floatingUI.style.top = `${e.clientY - offsetY}px`;
            });
        };

        const mouseUpHandler = () => {
            if (!isDragging) return;
            isDragging = false;
            floatingUI.classList.remove("dragging");
            cancelAnimationFrame(animationFrameId);
        };

        document.addEventListener("mousemove", mouseMoveHandler, { passive: true });
        document.addEventListener("mouseup", mouseUpHandler, { passive: true });

        cleanupFunctions.push(() => {
            document.removeEventListener("mousemove", mouseMoveHandler);
            document.removeEventListener("mouseup", mouseUpHandler);
        });

        floatingUI.appendChild(header);

        // Create content area
        const content = document.createElement("div");
        content.id = "enhancementPanelContent";
        Object.assign(content.style, {
            padding: "8px",
            overflowY: "hidden",
            flexGrow: "1",
            minHeight: "0",
            contain: "strict",
            boxSizing: "border-box",
            display: "flex",
            flexDirection: "column"
        });

        const resizeObserver = new ResizeObserver((entries) => {
                cancelAnimationFrame(animationFrameId);
                animationFrameId = requestAnimationFrame(() => {
                    const headerHeight = header.offsetHeight;
                    const contentHeight = content.scrollHeight;
                    const newHeight = headerHeight + contentHeight;

                    // Disable transitions temporarily when shrinking
                    if (newHeight < parseInt(floatingUI.style.height || 0)) {
                        floatingUI.style.transition = 'none';
                        floatingUI.style.height = `${newHeight}px`;
                        // Force reflow before re-enabling transitions
                        void floatingUI.offsetHeight;
                        floatingUI.style.transition = STYLE.transitions.medium;
                    } else {
                        floatingUI.style.height = `${newHeight}px`;
                    }
                });
            });
            resizeObserver.observe(content);

        cleanupFunctions.push(() => resizeObserver.disconnect());
        floatingUI.appendChild(content);

        document.body.appendChild(floatingUI);
        // Initial class for empty state
        floatingUI.classList.toggle('has-data', false);
        return floatingUI;
    }

    function navigateSessions(direction) {
        const sessionKeys = Object.keys(enhancementData).map(Number).sort((a, b) => a - b);
        if (sessionKeys.length <= 1) return;

        const currentIndex = sessionKeys.indexOf(currentViewingIndex);
        const newIndex = currentIndex + direction;

        if (newIndex >= 0 && newIndex < sessionKeys.length) {
            currentViewingIndex = sessionKeys[newIndex];
            saveEnhancementData(); // Save the new viewing index
            updateSessionCounter();
            debouncedUpdateFloatingUI();
        }
    }

    function updateSessionCounter() {
        const sessionCounter = document.getElementById("enhancementSessionCounter");
        if (!sessionCounter) return;

        const sessionKeys = Object.keys(enhancementData).map(Number).sort((a, b) => a - b);
        const currentPosition = sessionKeys.indexOf(currentViewingIndex) + 1;
        const totalSessions = sessionKeys.length;

        sessionCounter.textContent = isZH
            ? `(${currentPosition}/${totalSessions})`
            : `(${currentPosition}/${totalSessions})`;

        // Visual indicator
        sessionCounter.style.color = currentViewingIndex === currentTrackingIndex ?
            STYLE.colors.accent : STYLE.colors.textSecondary;
        sessionCounter.style.fontWeight = currentViewingIndex === currentTrackingIndex ?
            "bold" : "normal";
    }


    // Define table styles
    const compactTableStyle = `
        width: 100%;
        border-collapse: separate;
        border-spacing: 0;
        font-size: 13px;
        margin: 5px 0;
        background: rgba(30, 0, 40, 0.6);
        border-radius: ${STYLE.borderRadius.small};
        overflow: hidden;
    `;

    const compactCellStyle = `
        padding: 4px 8px;
        line-height: 1.3;
        border-bottom: 1px solid rgba(126, 87, 194, 0.2);
    `;

    const compactHeaderStyle = `
        ${compactCellStyle}
        font-weight: bold;
        text-align: center;
        background: ${STYLE.colors.headerBg};
        color: ${STYLE.colors.textPrimary};
        border-bottom: 2px solid ${STYLE.colors.primary};
    `;

    let updateDebounce;

    function debouncedUpdateFloatingUI() {
        clearTimeout(updateDebounce);
        updateDebounce = setTimeout(updateFloatingUI, 100);
    }

    function updateFloatingUI() {
        updateSessionCounter();

        const floatingUI = document.getElementById("enhancementFloatingUI") || createFloatingUI();
        const content = document.getElementById("enhancementPanelContent");

        const UI_DIMENSIONS = {
            scrollAreaMinHeight: '450px',
            scrollAreaMaxHeight: '700px',
            floatingUIHeight: '750px',
            floatingUIMinHeight: '200px',
            floatingUIMaxHeight: '85vh'
        };

        // Save current scroll position before wiping content
        const scrollContainer = content.querySelector("div > div:last-child");
        const oldScrollTop = scrollContainer?.scrollTop || 0;

        // Clear previous content
        content.innerHTML = '';

        // No enhancement data to show
        if (currentViewingIndex === 0 || !enhancementData[currentViewingIndex]) {
            floatingUI.classList.toggle('has-data', false);
            content.innerHTML = `
                <div class="empty-state">
                    <div class="empty-icon">✧</div>
                    <div class="empty-text">${isZH ? "开始强化以记录数据" : "Begin enhancing to populate data"}</div>
                </div>
            `;
            floatingUI.style.height = 'auto';
            return;
        }

        const session = enhancementData[currentViewingIndex];
        if (!session || !session["其他数据"]) {
            content.innerHTML = isZH ? "没有活跃的强化数据" : "No active enhancement data";
            floatingUI.style.height = 'auto';
            return;
        }

        floatingUI.classList.toggle('has-data', true);

        // Build header info
        const marketStatus = lastMarketUpdate > 0
            ? `${isZH ? '市场数据' : 'Market data'} ${new Date(lastMarketUpdate * 1000).toLocaleTimeString()}`
            : `${isZH ? '无市场数据' : 'No market data'}`;

        const itemData = session["其他数据"] || {};
        const itemName = translateItemName(itemData["物品HRID"], itemData["物品名称"]) || "Unknown";
        const targetLevel = itemData["目标强化等级"] || 0;

        // Get the appropriate display data based on session state
        const displayData = getSessionDisplayData(session);

        // Main container
        const container = document.createElement("div");
        container.style.display = 'flex';
        container.style.flexDirection = 'column';
        container.style.height = '100%';

        // Header section
        const headerContent = document.createElement("div");
        headerContent.style.flexShrink = '0';


        // Status display logic
         const statusDisplay = session["强化状态"] === "已完成"
             ? `<div style="display: flex; justify-content: space-between; margin-top: 5px; color: ${STYLE.colors.success}">
                   <span>${isZH ? "状态" : "Status"}:</span>
                   <strong>${isZH ? "已完成" : "Completed"}</strong>
                </div>`
             : `<div style="display: flex; justify-content: space-between; margin-top: 5px; color: ${STYLE.colors.accent}">
                   <span>${isZH ? "状态" : "Status"}:</span>
                   <strong>${isZH ? "进行中" : "In Progress"}</strong>
                </div>`;

        headerContent.innerHTML = `
            <div style="margin-bottom: 5px; font-size: 12px; color: ${lastMarketUpdate > 0 ? STYLE.colors.success : STYLE.colors.danger};">
                ${marketStatus}
            </div>
            <div style="margin-bottom: 10px; font-size: 13px;">
                <div style="display: flex; justify-content: space-between;">
                    <span>${isZH ? "物品" : "Item"}:</span>
                    <strong>${itemName}</strong>
                </div>
                <div style="display: flex; justify-content: space-between;">
                    <span>${isZH ? "目标" : "Target"}:</span>
                    <span>+${targetLevel}</span>
                </div>
                ${statusDisplay}
            </div>
        `;

        // Scrollable data area
        const scrollContent = document.createElement("div");
        scrollContent.style.flexGrow = '1';
        scrollContent.style.overflowY = 'auto';
        scrollContent.style.minHeight = UI_DIMENSIONS.scrollAreaMinHeight;
        scrollContent.style.maxHeight = UI_DIMENSIONS.scrollAreaMaxHeight;
        scrollContent.style.paddingRight = '5px';

        // Stats container with proper session data
        const statsContainer = document.createElement("div");
        statsContainer.id = "ultimateEnhancementStatsContainer";

        const totalAttempts = session["强化次数"];
        const totalSuccess = Object.values(session["强化数据"]).reduce((sum, level) => sum + (level["成功次数"] || 0), 0);
        const totalFailure = Object.values(session["强化数据"]).reduce((sum, level) => sum + (level["失败次数"] || 0), 0);
        const totalRate = totalAttempts > 0 ? (totalSuccess / totalAttempts * 100).toFixed(2) : 0;
        const shouldDisplayRate = displayData.isLive ?
            (displayData.xpPerHour > 0) :
            (displayData.xpPerHour !== null);

        // Get stored probabilities if they exist
        const probabilities = session["其他数据"]["初始概率预测"] || null;

        statsContainer.innerHTML = `
            <table style="${compactTableStyle}">
                <thead>
                    <tr>
                        <th style="${compactHeaderStyle}">${isZH ? "等级" : "Lvl"}</th>
                        <th style="${compactHeaderStyle}">${isZH ? "成功" : "Success"}</th>
                        <th style="${compactHeaderStyle}">${isZH ? "失败" : "Fail"}</th>
                        <th style="${compactHeaderStyle}">%</th>
                    </tr>
                </thead>
                <tbody>
                    ${Object.keys(session["强化数据"])
                        .sort((a, b) => b - a)
                        .map(level => {
                            const levelData = session["强化数据"][level];
                            const rate = ((levelData["成功率"] || 0) * 100).toFixed(1);
                            const sessionLevel = session["尝试历史"]?.slice(-1)[0]?.newLevel || 0;
                            const isCurrent = (level == sessionLevel) && (currentTrackingIndex == currentViewingIndex);
                            return `
                            <tr ${isCurrent ? `
                                style="
                                    background: linear-gradient(90deg, rgba(126, 87, 194, 0.25), rgba(0, 242, 255, 0.1));
                                    box-shadow: 0 0 12px rgba(126, 87, 194, 0.5), inset 0 0 6px rgba(0, 242, 255, 0.3);
                                    border-left: 3px solid ${STYLE.colors.accent};
                                    font-weight: bold;
                                "` : ''}>
                                <td style="${compactCellStyle} text-align: center;">${level}</td>
                                <td style="${compactCellStyle} text-align: right;">${levelData["成功次数"] || 0}</td>
                                <td style="${compactCellStyle} text-align: right;">${levelData["失败次数"] || 0}</td>
                                <td style="${compactCellStyle} text-align: right;">${rate}%</td>
                            </tr>`;
                        }).join('')}
                </tbody>
            </table>

            <div style="margin-top: 8px;">
                <div style="display: flex; justify-content: space-between; font-size: 13px;">
                    <div>
                        <span>${isZH ? "总计尝试次数" : "Total Attempts"}:</span>
                        <strong> ${totalAttempts}</strong>
                    </div>
                    <div>
                        <span>${isZH ? "保护石使用" : "Prots Used"}:</span>
                        <strong> ${session["其他数据"]["保护消耗总数"] || 0}</strong>
                    </div>
                </div>

                <div style="display: flex; justify-content: space-between; font-size: 13px; margin-top: 2px;">
                ${probabilities ? `
                <div style="color: ${STYLE.colors.textSecondary}; font-size: 12px;">
                    <span>${isZH ? "预计尝试" : "Expected Attempts"}:</span>
                    <span>${Math.round(probabilities.expectedAttempts)}</span>
                </div>
                ` : ''}
                    ${probabilities ? `
                    <div style="color: ${STYLE.colors.textSecondary}; font-size: 12px;">
                        <span>${isZH ? "预计保护石" : "Expected Prots"}:</span>
                        <span>${Math.round(probabilities.expectedProtections)}</span>
                    </div>
                    ` : ''}
                </div>
            </div>

            <div style="margin-top: 8px; display: flex; justify-content: space-between; font-size: 13px;">
                <span>${isZH ? "总获得经验" : "Total XP Gained"}:</span>
                <strong>${formatNumber(displayData.totalXP)}</strong>
            </div>

            <div style="margin-top: 8px; display: flex; justify-content: space-between; font-size: 13px;">
                <span>${isZH ? "会话时长" : "Session Duration"}:</span>
                <strong>${formatDuration(displayData.duration)}</strong>
            </div>

            <div style="margin-top: 8px; display: flex; justify-content: space-between; font-size: 13px;">
                <span>${isZH ? "经验/小时" : "XP/Hour"}:</span>
                <strong>
                    ${shouldDisplayRate ? formatNumber(displayData.xpPerHour) : (isZH ? "计算中..." : "Calculating...")}
                </strong>
            </div>
        `;

        if (session["材料消耗"] && Object.keys(session["材料消耗"]).length > 0) {
            statsContainer.innerHTML += generateMaterialCostsHTML(session);
        }

        scrollContent.appendChild(statsContainer);

        // Build complete UI
        container.appendChild(headerContent);
        container.appendChild(scrollContent);
        content.appendChild(container);

        // Style the floating UI
        floatingUI.style.height = UI_DIMENSIONS.floatingUIHeight;
        floatingUI.style.minHeight = UI_DIMENSIONS.floatingUIMinHeight;
        floatingUI.style.maxHeight = UI_DIMENSIONS.floatingUIMaxHeight;
        floatingUI.style.overflow = 'hidden';

        // Restore previous scroll position (after DOM renders)
        setTimeout(() => {
            scrollContent.scrollTop = oldScrollTop;
        }, 0);
    }

    // Helper function to get proper display data
    function getSessionDisplayData(session) {
        if (session["强化状态"] === "已完成") {
            // For completed sessions, use frozen values
            return {
                duration: session["会话数据"].finalDuration || session["会话数据"].持续时间,
                xpPerHour: session["会话数据"].finalXpPerHour || session["会话数据"].每小时经验,
                totalXP: session["会话数据"].总经验 || 0,
                isLive: false
            };
        } else {
            // For live sessions, calculate current values
            return {
                duration: calculateSessionDuration(session),
                xpPerHour: calculateXpPerHour(session),
                totalXP: session["会话数据"].总经验 || 0,
                isLive: true
            };
        }
    }

    function updateStatsOnly() {
        const session = enhancementData[currentViewingIndex];
        if (!session || !session["其他数据"]) return;

        const statsContainer = document.getElementById("ultimateEnhancementStatsContainer");
        if (!statsContainer) return;

        statsContainer.innerHTML = buildTableHTML(session);

        if (session["材料消耗"] && Object.keys(session["材料消耗"]).length > 0) {
            statsContainer.innerHTML += generateMaterialCostsHTML(session);
        }
    }

    function getSessionDisplayData(session) {
        if (session["强化状态"] === "已完成") {
            // For completed sessions, always use frozen values
            return {
                duration: session["会话数据"].finalDuration,
                xpPerHour: session["会话数据"].finalXpPerHour,
                totalXP: session["会话数据"].总经验,
                isLive: false
            };
        } else {
            // For live sessions, calculate current values
            return {
                duration: calculateSessionDuration(session),
                xpPerHour: calculateXpPerHour(session),
                totalXP: session["会话数据"].总经验 || 0,
                isLive: true
            };
        }
    }


    function buildTableHTML(session) {
        const totalAttempts = session["强化次数"];
        const totalSuccess = Object.values(session["强化数据"]).reduce((sum, level) => sum + (level["成功次数"] || 0), 0);
        const totalFailure = Object.values(session["强化数据"]).reduce((sum, level) => sum + (level["失败次数"] || 0), 0);
        const totalRate = totalAttempts > 0 ? (totalSuccess / totalAttempts * 100).toFixed(2) : 0;

        // Get the appropriate display data
        const displayData = getSessionDisplayData(session);
        const shouldDisplayRate = displayData.isLive ?
            (displayData.xpPerHour > 0) :
            (displayData.xpPerHour !== null);

      // Get stored probabilities if they exist
       const probabilities = session["其他数据"]["初始概率预测"] || null;


        return `
            <table style="${compactTableStyle}">
                <thead>
                    <tr>
                        <th style="${compactHeaderStyle}">${isZH ? "等级" : "Lvl"}</th>
                        <th style="${compactHeaderStyle}">${isZH ? "成功" : "Success"}</th>
                        <th style="${compactHeaderStyle}">${isZH ? "失败" : "Fail"}</th>
                        <th style="${compactHeaderStyle}">%</th>
                    </tr>
                </thead>
                <tbody>
                    ${Object.keys(session["强化数据"])
                        .sort((a, b) => b - a)
                        .map(level => {
                            const levelData = session["强化数据"][level];
                            const rate = ((levelData["成功率"] || 0) * 100).toFixed(1);
                            const sessionLevel = session["尝试历史"]?.slice(-1)[0]?.newLevel || 0;
                            const isCurrent = (level == sessionLevel) && (currentTrackingIndex == currentViewingIndex);
                            return `
                            <tr ${isCurrent ? `
                                style="
                                    background: linear-gradient(90deg, rgba(126, 87, 194, 0.25), rgba(0, 242, 255, 0.1));
                                    box-shadow: 0 0 12px rgba(126, 87, 194, 0.5), inset 0 0 6px rgba(0, 242, 255, 0.3);
                                    border-left: 3px solid ${STYLE.colors.accent};
                                    font-weight: bold;
                                "` : ''}>
                                <td style="${compactCellStyle} text-align: center;">${level}</td>
                                <td style="${compactCellStyle} text-align: right;">${levelData["成功次数"] || 0}</td>
                                <td style="${compactCellStyle} text-align: right;">${levelData["失败次数"] || 0}</td>
                                <td style="${compactCellStyle} text-align: right;">${rate}%</td>
                            </tr>`;
                        }).join('')}
                </tbody>
            </table>

            <div style="margin-top: 8px;">
                <div style="display: flex; justify-content: space-between; font-size: 13px;">
                    <div>
                        <span>${isZH ? "总计尝试次数" : "Total Attempts"}:</span>
                        <strong> ${totalAttempts}</strong>
                    </div>
                    <div>
                        <span>${isZH ? "保护石使用" : "Prots Used"}:</span>
                        <strong> ${session["其他数据"]["保护消耗总数"] || 0}</strong>
                    </div>
                </div>

                <div style="display: flex; justify-content: space-between; font-size: 13px; margin-top: 2px;">
                ${probabilities ? `
                <div style="color: ${STYLE.colors.textSecondary}; font-size: 12px;">
                    <span>${isZH ? "预计尝试" : "Expected Attempts"}:</span>
                    <span>${Math.round(probabilities.expectedAttempts)}</span>
                </div>
                ` : ''}
                    ${probabilities ? `
                    <div style="color: ${STYLE.colors.textSecondary}; font-size: 12px;">
                        <span>${isZH ? "预计保护石" : "Expected Prots"}:</span>
                        <span>${Math.round(probabilities.expectedProtections)}</span>
                    </div>
                    ` : ''}
                </div>
            </div>

            <div style="margin-top: 8px; display: flex; justify-content: space-between; font-size: 13px;">
                <span>${isZH ? "总获得经验" : "Total XP Gained"}:</span>
                <strong>${formatNumber(session["会话数据"]?.["总经验"] || 0)}</strong>
            </div>

            <div style="margin-top: 8px; display: flex; justify-content: space-between; font-size: 13px;">
                 <span>${isZH ? "会话时长" : "Session Duration"}:</span>
                 <strong>${formatDuration(displayData.duration)}</strong>
             </div>

             <div style="margin-top: 8px; display: flex; justify-content: space-between; font-size: 13px;">
                 <span>${isZH ? "经验/小时" : "XP/Hour"}:</span>
                 <strong>
                     ${shouldDisplayRate ? formatNumber(displayData.xpPerHour) : (isZH ? "计算中..." : "Calculating...")}
                 </strong>
             </div>
         `;
    }

    function formatNumber(num) {
        if (typeof num !== 'number') return '0';
        return num.toLocaleString('en-US', { maximumFractionDigits: 0 });
    }

    function toggleFloatingUI() {
        if (!floatingUI || !document.body.contains(floatingUI)) {
            createFloatingUI();
            debouncedUpdateFloatingUI();
            floatingUI.style.display = "block";
            showUINotification(
                isZH ? "强化追踪器已启用" : "Enhancement Tracker Enabled"
            );
        } else {
            const willShow = floatingUI.style.display === "none";
            floatingUI.style.display = willShow ? "flex" : "none";
            showUINotification(
                isZH ? `强化追踪器${willShow ? "已显示" : "已隐藏"}` :
                      `Enhancement Tracker ${willShow ? "Shown" : "Hidden"}`
            );
        }
        localStorage.setItem("enhancementUIVisible", floatingUI.style.display !== "none");
    }

    function generateMaterialCostsHTML(session) {
        const totalCost = session["总成本"] || 0;
        let html = `
            <div style="margin-top: 10px; border-top: 1px solid ${STYLE.colors.border}; padding-top: 8px;">
                <div style="display: flex; justify-content: space-between; margin-bottom: 5px;">
                    <span>${isZH ? "材料成本" : "Material Costs"}:</span>
                    <strong style="color: ${STYLE.colors.gold}; text-shadow: ${STYLE.shadows.gold};">${formatNumber(totalCost)}</strong>
                </div>
                <table style="${compactTableStyle}">
                    <thead>
                        <tr>
                            <th style="${compactHeaderStyle}">${isZH ? "材料" : "Material"}</th>
                            <th style="${compactHeaderStyle}">${isZH ? "数量" : "Qty"}</th>
                            <th style="${compactHeaderStyle}">${isZH ? "成本" : "Cost"}</th>
                        </tr>
                    </thead>
                    <tbody>
        `;

        // 1. Add regular materials sorted by cost (descending)
        html += Object.entries(session["材料消耗"] || {})
            .sort(([hridA, dataA], [hridB, dataB]) => dataB.totalCost - dataA.totalCost)
            .map(([hrid, data]) => `
                <tr>
                    <td style="${compactCellStyle}">${translateItemName(hrid, data.name)}</td>
                    <td style="${compactCellStyle} text-align: right;">${formatNumber(data.count)}</td>
                    <td style="${compactCellStyle} text-align: right; color: ${STYLE.colors.gold}">${formatNumber(data.totalCost)}</td>
                </tr>
            `).join('');

        // 2. Add coins LAST if they exist
        if (session["硬币消耗"] && session["硬币消耗"].count > 0) {
            html += `
                <tr>
                    <td style="${compactCellStyle}">${isZH ? "金币" : "Coins"}</td>
                    <td style="${compactCellStyle} text-align: right;"></td>
                    <td style="${compactCellStyle} text-align: right; color: ${STYLE.colors.gold}">${formatNumber(session["硬币消耗"].totalCost)}</td>
                </tr>
            `;
        }

        html += `
                    </tbody>
                </table>
        `;

        // Protection cost display remains unchanged
        if (session["其他数据"]["保护消耗总数"] > 0) {
            html += `
                <div style="margin-top: 8px; padding-top: 8px; border-top: 1px dashed ${STYLE.colors.border}">
                    <div style="display: flex; justify-content: space-between;">
                        <span>${isZH ? "保护物品" : "Protection Item"}:</span>
                        <span>${translateItemName(
                            session["其他数据"]["保护物品HRID"],
                            session["其他数据"]["保护物品名称"]
                        )}</span>
                    </div>
                    <div style="display: flex; justify-content: space-between;">
                        <span>${isZH ? "使用数量" : "Used"}:</span>
                        <span>${session["其他数据"]["保护消耗总数"]}</span>
                    </div>
                    <div style="display: flex; justify-content: space-between;">
                        <span>${isZH ? "保护总成本" : "Protection Cost"}:</span>
                        <strong style="color: ${STYLE.colors.gold}">${formatNumber(session["其他数据"]["保护总成本"])}</strong>
                    </div>
                </div>
            `;
        }

        return html;
    }


function cleanupFloatingUI() {
    if (floatingUI && document.body.contains(floatingUI)) {
        floatingUI.remove();
    }
    cleanupFunctions.forEach(fn => fn());
    cleanupFunctions = [];
    floatingUI = null;
}

// Add this function to create/manage the toggle button
function addEnhancementPanelToggle() {
    // Find the target container
    const gamePanel = document.querySelector('[class^="EnhancingPanel_skillActionDetailContainer"]');
    if (!gamePanel) return;

    // Check if we already added our button
    if (gamePanel.querySelector('.floating-ui-toggle-button')) return;

    // Create the toggle button
    const toggleBtn = document.createElement('button');
    toggleBtn.className = 'floating-ui-toggle-button';
    toggleBtn.innerHTML = '📊'; // Icon indicating stats/UI
    toggleBtn.title = isZH ? "切换浮动面板" : "Toggle Enhancement Tracker";

    // Style the button to match the game's UI
    Object.assign(toggleBtn.style, {
        position: 'absolute',
        bottom: '5px',
        right: '5px',
        width: '24px',
        height: '24px',
        borderRadius: '4px',
        background: 'rgba(0, 0, 0, 0.7)',
        border: `1px solid ${STYLE.colors.primary}`,
        color: STYLE.colors.textPrimary,
        cursor: 'pointer',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '12px',
        zIndex: '10'
    });

    // Hover effects
    toggleBtn.addEventListener('mouseenter', () => {
        toggleBtn.style.background = 'rgba(0, 0, 0, 0.9)';
        toggleBtn.style.color = STYLE.colors.accent;
    });
    toggleBtn.addEventListener('mouseleave', () => {
        toggleBtn.style.background = 'rgba(0, 0, 0, 0.7)';
        toggleBtn.style.color = STYLE.colors.textPrimary;
    });

    // Click handler: Toggles our floating UI
    toggleBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        toggleFloatingUI(); // This is the existing function from your script
    });

    // Ensure the game panel has relative positioning
    if (getComputedStyle(gamePanel).position === 'static') {
        gamePanel.style.position = 'relative';
    }

    // Add the button to the game panel
    gamePanel.appendChild(toggleBtn);
}

// ======================
// UI NOTIFICATION SYSTEM
// ======================

function showUINotification(message, duration = 2000) {
    const notification = document.createElement("div");
    Object.assign(notification.style, {
        position: "fixed",
        bottom: "20px",
        left: "50%",
        transform: "translateX(-50%)",
        padding: '8px 12px',
        borderRadius: "4px",
        backdropFilter: 'blur(4px)',
        border: `1px solid ${STYLE.colors.primary}`,
        background: 'rgba(40, 0, 60, 0.9)',
        backgroundColor: "rgba(0, 0, 0, 0.8)",
        color: "white",
        zIndex: "10000",
        fontSize: "14px",
        animation: "fadeInOut 2s ease-in-out",
        pointerEvents: "none"
    });
    notification.textContent = message;
    document.body.appendChild(notification);

    setTimeout(() => {
        notification.style.opacity = "0";
        setTimeout(() => notification.remove(), 300);
    }, duration);
}

// ======================
// KEYBOARD SHORTCUT
// ======================

function setupKeyboardShortcut() {
    document.addEventListener("keydown", (e) => {
        if (e.key === "F9") {
            e.preventDefault();
            toggleFloatingUI();
        }
    });
}

    // ======================
    // TESTING SYSTEM
    // ======================

    function initializeTesting() {
        if (typeof GM_registerMenuCommand !== 'undefined') {
            GM_registerMenuCommand("🔧 Test Success Notifications", () => testNotifications("success"));
            GM_registerMenuCommand("🔧 Test Failure Notifications", () => testNotifications("failure"));
            GM_registerMenuCommand("✨ Test Blessed Success", () => testNotifications("blessed"));
            GM_registerMenuCommand("🌀 Test All Notifications", () => testNotifications("all"));

            GM_registerMenuCommand("💎 Test Hitting Target Level", () => {
                const level = 15;
                const type = "success";
                const targetLevel = 15;

                const isBlessed = type === "blessed";
                const isSuccess = type !== "failure";
                showNotification(
                    isBlessed ? "BLESSED!" : isSuccess ? "Success" : "Failed!",
                    isSuccess ? "success" : "failure",
                    level,
                    isBlessed
                );

                if (level >= targetLevel) {
                    showTargetAchievedCelebration(level, targetLevel);
                }
            });
        }

        window.testEnhancement = {
            success: () => testNotifications("success"),
            allSuccess: () => testNotifications("allSuccess"),
            failure: () => testNotifications("failure"),
            blessed: () => testNotifications("blessed"),
            all: () => testNotifications("all"),
            custom: (level, type, targetLevel) => {
                const isBlessed = type === "blessed";
                const isSuccess = type !== "failure";
                showNotification(
                    isBlessed ? "BLESSED!" : isSuccess ? "Success" : "Failed!",
                    isSuccess ? "success" : "failure",
                    level,
                    isBlessed
                );

                if (level >= targetLevel) {
                    showTargetAchievedCelebration(level, targetLevel);
                }
            }
        };
    }

    function testNotifications(type) {
        const tests = {
            success: [
                { level: 1, blessed: false },
                { level: 5, blessed: false },
                { level: 10, blessed: false },
                { level: 15, blessed: false },
                { level: 20, blessed: false }
            ],
            allSuccess: [
                { level: 1, blessed: false },
                { level: 2, blessed: false },
                { level: 3, blessed: false },
                { level: 4, blessed: false },
                { level: 5, blessed: false },
                { level: 6, blessed: false },
                { level: 7, blessed: false },
                { level: 8, blessed: false },
                { level: 9, blessed: false },
                { level: 10, blessed: false },
                { level: 11, blessed: false },
                { level: 12, blessed: false },
                { level: 13, blessed: false },
                { level: 14, blessed: false },
                { level: 15, blessed: false },
                { level: 16, blessed: false },
                { level: 17, blessed: false },
                { level: 18, blessed: false },
                { level: 19, blessed: false },
                { level: 20, blessed: false }
            ],
            blessed: [
                { level: 3, blessed: true },
                { level: 8, blessed: true },
                { level: 12, blessed: true },
                { level: 17, blessed: true },
                { level: 22, blessed: true }
            ],
            failure: [
                { level: 0, blessed: false }
            ],
            all: [
                { level: 1, blessed: false },
                { level: 0, blessed: false },
                { level: 3, blessed: true },
                { level: 0, blessed: false },
                { level: 10, blessed: false },
                { level: 12, blessed: true },
                { level: 15, blessed: false },
                { level: 0, blessed: false },
                { level: 20, blessed: false }
            ]
        };

        tests[type].forEach((test, i) => {
            setTimeout(() => {
                const message = test.blessed ?
                    (isZH ? `祝福强化! +${test.level}` : `BLESSED! +${test.level}`) :
                    (test.level > 0 ?
                        (isZH ? `强化成功 +${test.level}` : `Success +${test.level}`) :
                        (isZH ? "强化失败!" : "Failed!"));

                showNotification(
                    message,
                    test.level > 0 ? "success" : "failure",
                    test.level,
                    test.blessed
                );
            }, i * 800);
        });
    }

    // ======================
    // INITIALIZATION
    // ======================

    function addGlobalStyles() {
        const style = document.createElement("style");
        style.textContent = `
            @keyframes pulse {
                0% { transform: scale(1); }
                50% { transform: scale(1.05); }
                100% { transform: scale(1); }
            }
            @keyframes holyGlow {
                0% { box-shadow: 0 0 10px #FFD700; }
                50% { box-shadow: 0 0 25px #FFD700, 0 0 40px white; }
                100% { box-shadow: 0 0 10px #FFD700; }
            }
            @keyframes raysRotate {
                from { transform: rotate(0deg); }
                to { transform: rotate(360deg); }
            }
            @keyframes floatUp {
                0% { transform: translateY(0) rotate(0deg); opacity: 0; }
                10% { opacity: 0.7; }
                90% { opacity: 0.7; }
                100% { transform: translateY(-100px) rotate(20deg); opacity: 0; }
            }
            @keyframes celebrateText {
                0% { transform: scale(0.5); opacity: 0; }
                50% { transform: scale(1.2); opacity: 1; }
                100% { transform: scale(1); }
            }
            @keyframes confettiFly {
                0% { transform: translate(0,0) rotate(0deg); opacity: 1; }
                100% { transform: translate(${Math.random() > 0.5 ? '-' : ''}${Math.random() * 300 + 100}px, ${Math.random() * 300 + 100}px) rotate(360deg); opacity: 0; }
            }
            @keyframes fireworkExpand {
                0% { transform: scale(0); opacity: 1; }
                100% { transform: scale(20); opacity: 0; }
            }
            @keyframes fireworkTrail {
                0% { transform: translate(0,0); opacity: 1; }
                100% { transform: translate(${Math.random() > 0.5 ? '-' : ''}${Math.random() * 100 + 50}px, ${Math.random() * 100 + 50}px); opacity: 0; }
            }
            .enhancement-notification {
                position: relative;
                overflow: hidden;
                transition: ${STYLE.transitions.medium};
                backdrop-filter: blur(4px);
                -webkit-backdrop-filter: blur(4px);
                background: 'rgba(255, 255, 255, 0.1)',
                border: '1px solid rgba(255, 255, 255, 0.2)',
            }
            #enhancementFloatingUI {
              transition: height 0.15s ease-out, opacity 0.2s ease, transform 0.2s ease;
              height: auto;
              max-height: 80vh;
              backdrop-filter: blur(8px);
              -webkit-backdrop-filter: blur(8px);
              background: 'rgba(255, 255, 255, 0.1)',
              border: '1px solid rgba(255, 255, 255, 0.2)',
            }
            #enhancementFloatingUI.no-transition {
                transition: none !important;
            }
            #enhancementPanelContent {
                scrollbar-width: thin;
                scrollbar-color: ${STYLE.colors.border} transparent;
            }
            #enhancementPanelContent::-webkit-scrollbar {
                width: 6px;
            }

            #enhancementPanelContent::-webkit-scrollbar-thumb {
                background: ${STYLE.colors.primary};
                border-radius: 3px;
            }

            #enhancementPanelContent::-webkit-scrollbar-track {
                background: rgba(30, 0, 40, 0.4);
            }
            #enhancementFloatingUI[style*="display: none"] {
                display: block !important;
                opacity: 0;
                pointer-events: none;
                transform: translateY(10px);
            }
            #enhancementFloatingUI.dragging {
                cursor: grabbing;
                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
                transition: none;
            }
            @keyframes floatIn {
                0% { opacity: 0; transform: translateY(10px); }
                100% { opacity: 1; transform: translateY(0); }
            }
            #enhancementFloatingUI:not([style*="display: none"]) {
                animation: floatIn 0.2s ease-out;
            }
            @keyframes fadeInOut {
                0% { opacity: 0; transform: translateX(-50%) translateY(10px); }
                20% { opacity: 1; transform: translateX(-50%) translateY(0); }
                80% { opacity: 1; transform: translateX(-50%) translateY(0); }
                100% { opacity: 0; transform: translateX(-50%) translateY(-10px); }
            }
            @keyframes mythicPulse {
                0% {
                    background-position: 0% 50%;
                    transform: scale(1);
                    box-shadow: 0 0 8px #ff0033;
                }
                50% {
                    background-position: 100% 50%;
                    transform: scale(1.05);
                    box-shadow: 0 0 16px #ff2200, 0 0 24px #ff2200;
                }
                100% {
                    background-position: 0% 50%;
                    transform: scale(1);
                    box-shadow: 0 0 8px #ff0033;
                }
            }
            #enhancementPanelHeader button {
                background: none;
                border: none;
                color: ${STYLE.colors.textPrimary};
                cursor: pointer;
                font-size: 14px;
                padding: 2px 8px;
                border-radius: 3px;
                transition: ${STYLE.transitions.fast};
                display: flex;
                align-items: center;
                justify-content: center;
                width: 24px;
                height: 24px;
            }
            #enhancementPanelHeader button:hover {
                color: ${STYLE.colors.accent};
                background: rgba(255, 255, 255, 0.1);
            }
            #enhancementPanelHeader button:active {
                transform: scale(0.9);
            }
            #enhancementPanelHeader .nav-arrows {
                display: flex;
                gap: 5px;
                margin-left: auto;
            }
            /* Enhanced scrollbar styling */
            #enhancementPanelContent > div::-webkit-scrollbar {
                width: 6px;
                height: 6px;
            }

            #enhancementPanelContent > div::-webkit-scrollbar-thumb {
                background-color: ${STYLE.colors.primary};
                border-radius: 3px;
            }

            #enhancementPanelContent > div::-webkit-scrollbar-track {
                background-color: rgba(30, 0, 40, 0.4);
                border-radius: 3px;
            }

            #enhancementPanelContent > div {
                scrollbar-width: thin;
                scrollbar-color: ${STYLE.colors.primary} rgba(30, 0, 40, 0.4);
            }

            /* Better empty state for scroll container */
            .empty-state {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                height: 100px;
                color: ${STYLE.colors.textSecondary};
                padding: 20px;
                text-align: center;
            }
            .empty-icon {
                font-size: 32px;
                margin-bottom: 10px;
                opacity: 0.5;
            }

            .empty-text {
                font-size: 14px;
            }

            #enhancementFloatingUI:not(.has-data) {
                height: 150px !important; /* Fixed height for empty state */
            }
        `;
        document.head.appendChild(style);
    }

    function initializeFloatingUI() {
        const checkReady = setInterval(() => {
            if (document.body && typeof item_hrid_to_name !== "undefined") {
                clearInterval(checkReady);
                setupKeyboardShortcut();

                if (localStorage.getItem("enhancementUIVisible") !== "false") {
                    createFloatingUI();
                    debouncedUpdateFloatingUI();
                }
            }
        }, 500);
    }

    function initializeFloatingUIToggle() {
        // Try immediately in case the panel is already loaded
        addEnhancementPanelToggle();

        // Set up an observer to detect when the panel loads dynamically
        const observer = new MutationObserver((mutations) => {
            if (document.querySelector('[class^="EnhancingPanel_skillActionDetailContainer"]')) {
                addEnhancementPanelToggle();
            }
        });

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

    // Start everything
    addGlobalStyles();
    initializeTesting();
    initializeFloatingUI();
    // Initialize market data loading when script starts
    loadMarketData();
    initializeFloatingUIToggle();
    hookWS();

    console.log("Enhancement Notifier v3.5.2 loaded");
})();