Greasy Fork

[MWI] Ultimate Enhancement Notifier v2.5 (Styled)

Adds floating UI and toggle buttons with DPS panel styling

目前为 2025-04-05 提交的版本。查看 最新版本

// ==UserScript==
// @name         [MWI] Ultimate Enhancement Notifier v2.5 (Styled)
// @namespace    http://tampermonkey.net/
// @version      2.5
// @description  Adds floating UI and toggle buttons with DPS panel styling
// @author       Truth_Light (modified by Nex, styled by Cosmic)
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @license MIT
// @grant        GM_registerMenuCommand
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// ==/UserScript==

(function() {
    'use strict';

    // ======================
    // STYLE CONFIGURATION
    // ======================
    const STYLE = {
        colors: {
            primary: '#0078d4',
            background: 'rgba(0, 0, 0, 0.7)',
            border: '1px solid rgba(255, 255, 255, 0.2)',
            textPrimary: '#ffffff',
            textSecondary: '#a0a0b0',
            accent: '#4a8cff',
            danger: '#ff4a4a',
            success: '#4aff8c',
            headerBg: 'rgba(0, 0, 0, 0.5)',
            epic: '#9933FF',
            legendary: '#FF8C00',
            mythic: '#FF3333',
            blessed: 'linear-gradient(135deg, #FFD700, #FFFFFF, #FFD700)'
        },
        fonts: {
            primary: '14px "Segoe UI", Roboto, sans-serif',
            secondary: '12px "Segoe UI", Roboto, sans-serif',
            header: 'bold 16px "Segoe UI", Roboto, sans-serif',
            milestone: 'bold 18px "Segoe UI", Roboto, sans-serif'
        },
        shadows: {
            panel: '0 4px 12px rgba(0, 0, 0, 0.3)',
            notification: '0 2px 8px rgba(0,0,0,0.3)',
            milestone: '0 2px 12px rgba(0,0,0,0.4)',
            text: '1px 1px 2px rgba(0,0,0,0.5)'
        },
        borderRadius: {
            medium: '16px',
            small: '8px'
        },
        transitions: {
            fast: 'all 0.2s ease',
            medium: 'all 0.3s ease',
            slow: 'all 0.5s ease'
        }
    };

    // Core Variables
    const userLanguage = localStorage.getItem('i18nextLng');
    let enhancementLevel;
    let currentEnhancingIndex = 1;
    let enhancementData = {
        [currentEnhancingIndex]: { "强化数据": {}, "其他数据": {} }
    };
    let item_name_to_hrid;
    let item_hrid_to_name;

    const isZH = userLanguage.startsWith("zh");

    // ======================
    // 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}, #FF0000)`;
            milestone.style.textShadow = "0 0 8px rgba(255, 0, 0, 0.8)";
            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) return;

        try {
            const data = JSON.parse(initClientData);
            if (data.type !== 'init_client_data') return;

            item_hrid_to_name = data.itemDetailMap;
            for (const key in item_hrid_to_name) {
                if (item_hrid_to_name[key]?.name) {
                    item_hrid_to_name[key] = item_hrid_to_name[key].name;
                }
            }
            item_name_to_hrid = Object.fromEntries(
                Object.entries(item_hrid_to_name).map(([key, value]) => [value, key])
            );
        } catch (error) {
            console.error('数据解析失败:', error);
        }
    }

    function handleEnhancement(action) {
        const newLevel = parseInt(action.primaryItemHash.match(/::(\d+)$/)[1]);
        const targetLevel = action.enhancingMaxLevel;
        const currentCount = action.currentCount;
        const wasBlessed = enhancementLevel !== undefined && (newLevel - enhancementLevel) >= 2;

        if (enhancementLevel !== undefined) {
            if (currentCount < enhancementData[currentEnhancingIndex]["强化次数"]) {
                startNewEnhancementSession();
                return;
            }

            const currentItem = enhancementData[currentEnhancingIndex]["强化数据"];
            if (!currentItem[enhancementLevel]) {
                currentItem[enhancementLevel] = { "成功次数": 0, "失败次数": 0, "成功率": 0 };
            }

            if (newLevel > enhancementLevel) {
                handleSuccess(currentItem, newLevel, wasBlessed);
            } else {
                handleFailure(action, currentItem);
            }

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

            updateStats(currentItem);
            enhancementData[currentEnhancingIndex]["强化状态"] = getEnhancementState(currentItem);
        } else {
            initializeNewItem(action, newLevel);
        }

        enhancementLevel = newLevel;
        enhancementData[currentEnhancingIndex]["强化次数"] = currentCount;
        updateDisplay();
        updateFloatingUI();
    }

    function startNewEnhancementSession() {
        currentEnhancingIndex++;
        enhancementData[currentEnhancingIndex] = { "强化数据": {}, "其他数据": {} };
        enhancementLevel = undefined;
    }

    function handleSuccess(currentItem, newLevel, wasBlessed) {
        currentItem[enhancementLevel]["成功次数"]++;
        const message = isZH ? `强化成功 +${newLevel}` : `Success +${newLevel}`;
        showNotification(
            wasBlessed ? (isZH ? `祝福强化! +${newLevel}` : `BLESSED! +${newLevel}`) : message,
            "success",
            newLevel,
            wasBlessed
        );
    }

    function handleFailure(action, currentItem) {
        currentItem[enhancementLevel]["失败次数"]++;
        showNotification(isZH ? "强化失败!" : "Failed!", "failure", 0);
        if (action.enhancingProtectionMinLevel >= 2 && enhancementLevel >= action.enhancingProtectionMinLevel) {
            enhancementData[currentEnhancingIndex]["其他数据"]["保护消耗总数"]++;
        }
    }

    function updateStats(currentItem) {
        const success = currentItem[enhancementLevel]["成功次数"];
        const failure = currentItem[enhancementLevel]["失败次数"];
        currentItem[enhancementLevel]["成功率"] = success / (success + failure);
    }

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

    function initializeNewItem(action, newLevel) {
        const itemName = item_hrid_to_name[action.primaryItemHash.match(/::([^:]+)::[^:]*$/)[1]];
        enhancementData[currentEnhancingIndex]["其他数据"] = {
            "物品名称": itemName,
            "目标强化等级": action.enhancingMaxLevel,
            "保护消耗总数": 0,
        };
    }

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

    // ======================
    // DISPLAY SYSTEM
    // ======================

    function updateDisplay() {
        const targetElement = document.querySelector(".SkillActionDetail_enhancingComponent__17bOx");
        if (!targetElement) return;

        let parentContainer = document.querySelector("#enhancementParentContainer");
        if (!parentContainer) {
            parentContainer = createDisplayContainer(targetElement);
        }

        updateDropdown(parentContainer);
    }

    function createDisplayContainer(targetElement) {
        const parentContainer = document.createElement("div");
        parentContainer.id = "enhancementParentContainer";
        Object.assign(parentContainer.style, {
            display: "block",
            borderLeft: `2px solid ${STYLE.colors.border}`,
            padding: "0 4px"
        });

        const title = document.createElement("div");
        title.textContent = isZH ? "强化数据" : "Enhancement Data";
        Object.assign(title.style, {
            fontWeight: "bold",
            marginBottom: "10px",
            textAlign: "center",
            color: STYLE.colors.textSecondary,
            font: STYLE.fonts.header
        });
        parentContainer.appendChild(title);

        const dropdownContainer = document.createElement("div");
        dropdownContainer.style.marginBottom = "10px";
        const dropdown = document.createElement("select");
        dropdown.id = "enhancementDropdown";
        Object.assign(dropdown.style, {
            width: "100%",
            padding: "4px",
            borderRadius: STYLE.borderRadius.small,
            background: STYLE.colors.background,
            color: STYLE.colors.textPrimary,
            border: STYLE.colors.border
        });
        dropdown.addEventListener("change", function() {
            renderStats(this.value);
            updateDropdownColor();
        });
        dropdownContainer.appendChild(dropdown);
        parentContainer.appendChild(dropdownContainer);

        const statsContainer = document.createElement("div");
        statsContainer.id = "enhancementStatsContainer";
        Object.assign(statsContainer.style, {
            display: "grid",
            gridTemplateColumns: "repeat(4, 1fr)",
            gap: "10px",
            textAlign: "center",
            marginTop: "10px",
            font: STYLE.fonts.secondary
        });
        parentContainer.appendChild(statsContainer);

        targetElement.appendChild(parentContainer);
        return parentContainer;
    }

    function updateDropdown(parentContainer) {
        const dropdown = parentContainer.querySelector("#enhancementDropdown");
        const previousSelectedValue = dropdown.value;
        dropdown.innerHTML = "";

        Object.keys(enhancementData).forEach(key => {
            const item = enhancementData[key];
            if (!item["其他数据"]) return;

            const option = document.createElement("option");
            const itemName = item["其他数据"]["物品名称"] || "Unknown";
            const targetLevel = item["其他数据"]["目标强化等级"] || 0;
            const currentLevel = Math.max(...Object.keys(item["强化数据"] || {}).map(Number).filter(n => !isNaN(n))) || 0;
            const enhancementState = item["强化状态"] || "";

            option.text = isZH
                ? `${itemName} (目标: ${targetLevel}, 总计: ${item["强化次数"] || 0}${item["其他数据"]["保护消耗总数"] > 0 ? `, 垫子: ${item["其他数据"]["保护消耗总数"]}` : ""})`
                : `${itemName} (Target: ${targetLevel}, Total: ${item["强化次数"] || 0}${item["其他数据"]["保护消耗总数"] > 0 ? `, Protectors: ${item["其他数据"]["保护消耗总数"]}` : ""})`;

            option.value = key;
            option.style.color = enhancementState === "强化成功" ? STYLE.colors.success
                : (currentLevel < targetLevel && Object.keys(enhancementData).indexOf(key) === Object.keys(enhancementData).length - 1) ? STYLE.colors.accent
                : STYLE.colors.danger;

            dropdown.appendChild(option);
        });

        if (Object.keys(enhancementData).length > 0) {
            dropdown.value = previousSelectedValue || Object.keys(enhancementData)[0];
            updateDropdownColor();
            renderStats(dropdown.value);
        }
    }

    function updateDropdownColor() {
        const dropdown = document.querySelector("#enhancementDropdown");
        if (!dropdown) return;
        const selectedOption = dropdown.options[dropdown.selectedIndex];
        dropdown.style.color = selectedOption ? selectedOption.style.color : STYLE.colors.textPrimary;
    }

    function renderStats(selectedKey) {
        const statsContainer = document.querySelector("#enhancementStatsContainer");
        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 (UPDATED)
    // ======================

    function createFloatingUI() {
        const 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: STYLE.shadows.panel,
            overflow: "hidden",
            width: "320px",
            minHeight: "auto",
            background: STYLE.colors.background,
            border: `1px solid ${STYLE.colors.border}`,
            color: STYLE.colors.textPrimary
        });

        // Header with drag and toggle functionality
        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}`
        });

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

        const closeBtn = document.createElement("button");
        closeBtn.textContent = isZH ? "收起" : "Hide";
        Object.assign(closeBtn.style, {
            backgroundColor: STYLE.colors.primary,
            color: "white",
            border: "none",
            padding: "5px 10px",
            borderRadius: "4px",
            cursor: "pointer"
        });

        header.appendChild(title);
        header.appendChild(closeBtn);
        floatingUI.appendChild(header);

        // Content area
        const content = document.createElement("div");
        content.id = "enhancementPanelContent";
        Object.assign(content.style, {
            padding: "15px",
            maxHeight: "500px",
            overflowY: "auto"
        });
        floatingUI.appendChild(content);

        // Drag functionality
        let isDragging = false;
        let offsetX, offsetY;

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

        document.addEventListener("mousemove", (e) => {
            if (!isDragging) return;
            floatingUI.style.left = `${e.clientX - offsetX}px`;
            floatingUI.style.top = `${e.clientY - offsetY}px`;
        });

        document.addEventListener("mouseup", () => {
            isDragging = false;
        });

        // Toggle functionality
        closeBtn.addEventListener("click", () => {
            const isHidden = content.style.display === "none";
            content.style.display = isHidden ? "block" : "none";
            closeBtn.textContent = isHidden ? (isZH ? "收起" : "Hide") : (isZH ? "展开" : "Show");
            localStorage.setItem("enhancementUIVisible", String(!isHidden));
        });

        // Initialize visibility
        const isVisible = localStorage.getItem("enhancementUIVisible") !== "false";
        content.style.display = isVisible ? "block" : "none";
        closeBtn.textContent = isVisible ? (isZH ? "收起" : "Hide") : (isZH ? "展开" : "Show");

        document.body.appendChild(floatingUI);
        return floatingUI;
    }

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

        if (currentEnhancingIndex === -1 || !enhancementData[currentEnhancingIndex]) {
            content.innerHTML = isZH ? "没有活跃的强化数据" : "No active enhancement data";
            return;
        }

        const session = enhancementData[currentEnhancingIndex];
        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;

        // Generate HTML content
        content.innerHTML = `
            <div style="margin-bottom: 15px;">
                <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
                    <span>${isZH ? "物品" : "Item"}:</span>
                    <strong>${session["其他数据"]["物品名称"]}</strong>
                </div>
                <div style="display: flex; justify-content: space-between;">
                    <span>${isZH ? "目标等级" : "Target Level"}:</span>
                    <span>${session["其他数据"]["目标强化等级"]}</span>
                </div>
            </div>

            <table style="width: 100%; border-collapse: collapse; margin-bottom: 15px;">
                <thead>
                    <tr style="border-bottom: 1px solid ${STYLE.colors.border}">
                        <th style="padding: 6px 0; text-align: left;">${isZH ? "等级" : "Level"}</th>
                        <th style="padding: 6px 0; text-align: right;">${isZH ? "成功" : "Success"}</th>
                        <th style="padding: 6px 0; text-align: right;">${isZH ? "失败" : "Failure"}</th>
                        <th style="padding: 6px 0; text-align: right;">${isZH ? "概率" : "Rate"}</th>
                    </tr>
                </thead>
                <tbody>
                    ${Object.keys(session["强化数据"])
                        .sort((a, b) => b - a)
                        .map(level => {
                            const levelData = session["强化数据"][level];
                            const rate = ((levelData["成功率"] || 0) * 100).toFixed(2);
                            return `
                            <tr ${level == enhancementLevel ? 'style="font-weight: bold;"' : ''}>
                                <td style="padding: 6px 0;">${level}</td>
                                <td style="padding: 6px 0; text-align: right;">${levelData["成功次数"] || 0}</td>
                                <td style="padding: 6px 0; text-align: right;">${levelData["失败次数"] || 0}</td>
                                <td style="padding: 6px 0; text-align: right;">${rate}%</td>
                            </tr>`;
                        }).join('')}
                </tbody>
            </table>

            <table style="width: 100%; border-collapse: collapse; border-top: 1px solid ${STYLE.colors.border}; padding-top: 10px;">
                <tr>
                    <td style="padding: 6px 0;">${isZH ? "总尝试" : "Total Attempts"}:</td>
                    <td style="padding: 6px 0; text-align: right;">${totalAttempts}</td>
                </tr>
                <tr>
                    <td style="padding: 6px 0;">${isZH ? "总成功" : "Total Success"}:</td>
                    <td style="padding: 6px 0; text-align: right;">${totalSuccess}</td>
                </tr>
                <tr>
                    <td style="padding: 6px 0;">${isZH ? "总失败" : "Total Failure"}:</td>
                    <td style="padding: 6px 0; text-align: right;">${totalFailure}</td>
                </tr>
                <tr>
                    <td style="padding: 6px 0;">${isZH ? "成功率" : "Success Rate"}:</td>
                    <td style="padding: 6px 0; text-align: right;">${totalRate}%</td>
                </tr>
                ${session["其他数据"]["保护消耗总数"] > 0 ? `
                <tr>
                    <td style="padding: 6px 0;">${isZH ? "保护消耗" : "Protectors Used"}:</td>
                    <td style="padding: 6px 0; text-align: right;">${session["其他数据"]["保护消耗总数"]}</td>
                </tr>` : ''}
            </table>`;
    }

    // ======================
    // 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"));
        }

        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};
            }
            #enhancementFloatingUI {
                transition: ${STYLE.transitions.medium};
            }
            #enhancementFloatingUI:hover {
                box-shadow: 0 6px 16px rgba(0,0,0,0.4);
                transform: translateY(-2px);
            }
            #enhancementPanelContent {
                max-height: 300px;
                overflow-y: auto;
            }
            #enhancementPanelContent::-webkit-scrollbar {
                width: 6px;
            }
            #enhancementPanelContent::-webkit-scrollbar-thumb {
                background-color: ${STYLE.colors.border};
                border-radius: 3px;
            }
        `;
        document.head.appendChild(style);
    }

    function initializeFloatingUI() {
        const checkReady = setInterval(() => {
            if (document.body && typeof item_hrid_to_name !== "undefined") {
                clearInterval(checkReady);
                createFloatingUI();
                addToggleControls();
                updateFloatingUI();
            }
        }, 500);
    }

    // Start everything
    addGlobalStyles();
    initializeTesting();
    initializeFloatingUI();
    hookWS();


    console.log("Enhancement Notifier v2.5 loaded - Styled version");
})();