DailyAssets Plus

记录每日总资产增长,包含详细统计功能:当前资产、日环比、瞬时时薪、近7天均增速、近7天胜率、近30天日均、最佳/最差日、上一次翻倍等,支持数据导入导出(独立版,不依赖MWITools)

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         DailyAssets Plus
// @namespace    http://tampermonkey.net/
// @version      1.0.7
// @description  记录每日总资产增长,包含详细统计功能:当前资产、日环比、瞬时时薪、近7天均增速、近7天胜率、近30天日均、最佳/最差日、上一次翻倍等,支持数据导入导出(独立版,不依赖MWITools)
// @author       Vicky718 (基于 VictoryWinWinWin, PaperCat, SuXingX 的代码增强)
// @match        https://www.milkywayidle.com/*
// @match        https://www.milkywayidlecn.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @license MIT
// ==/UserScript==

(function () {
    'use strict';

    /* =========================
       语言配置
    ========================= */
    const LANGUAGES = {
        zh: {
            // 基础UI
            assetsGrowth: '💰总资产增长',
            historyIcon: '显示详细资产历史图表',
            metricsIconShow: '显示统计指标',
            metricsIconHide: '隐藏统计指标',
            settingsIcon: '图表设置',

            // 额外信息
            avg7Days: '近7天日均',
            lastRecord: '最近记录',

            // 图表弹窗
            chartTitle: '资产历史曲线',
            timeRange: '时间范围:',

            // 设置弹窗
            settingsTitle: '资产图表设置',
            viewMode: '视图模式',
            summaryView: '摘要视图',
            detailedView: '详细视图',

            // 摘要视图选项
            summaryOptions: '摘要视图显示选项',
            showCurrent: '显示流动资产',
            showNonCurrent: '显示非流动资产',

            // 详细视图选项
            detailedOptions: '详细视图显示选项',
            showEquipped: '显示装备价值',
            showInventory: '显示库存价值',
            showMarket: '显示订单价值',
            showHouse: '显示房子价值',
            showAbility: '显示技能价值',

            // 通用选项
            generalOptions: '通用显示选项',
            showTotal: '显示总资产',

            // 单位设置
            unitSettings: '资产显示单位',
            unitMode: '单位模式:',
            unitAuto: '自动',
            unitK: '千(K)',
            unitM: '百万(M)',
            unitB: '十亿(B)',

            // 时间范围设置
            timeRangeSettings: '时间范围设置',
            showTimeRange: '显示时间范围选择器',
            availableTimeRanges: '可用时间范围:',
            currentButtons: '当前可用的时间范围按钮:',

            // 导入导出
            importExport: '📦 数据导入导出',
            exportAll: '导出全部(含设置)',
            exportData: '仅导出数据',
            importData: '导入数据',

            // 导入预览
            preview: '导入预览',
            version: '版本',
            exportTime: '导出时间',
            roleCount: '角色数量',
            totalRecords: '总记录数',
            containsSettings: '包含设置',
            yes: '是',
            no: '否',
            roleStats: '角色数据统计:',
            records: '条记录',
            confirmImport: '确认导入',
            cancel: '取消',

            // 通知
            exportSuccess: '数据已导出到',
            exportFailed: '导出失败',
            importFailed: '导入失败',
            noImportData: '没有可导入的数据',
            invalidFormat: '无效的数据格式',
            unrecognizedVersion: '无法识别的备份文件版本',
            missingAssetData: '备份文件中缺少资产数据',
            previewFailed: '预览失败',

            // 统计指标
            currentAssets: '当前资产',
            recentUpdate: '最近更新',
            dailyChange: '日环比',
            hourlyRate: '瞬时时薪',
            hourlyRateDesc: '按当日平均',
            avg7Growth: '近7天均增速',
            avg7Desc: '日均增率 / 日均净变动',
            winRate7: '近7天胜率',
            avg30Daily: '近30天日均',
            bestWorstDay: '最佳/最差日',
            lastDouble: '上一次翻倍',
            noRecord: '尚无记录',
            doubling: '翻倍',
            target: '目标',
            days: '天',
            profitLossFlat: '盈/亏/平',

            // 价值名称
            equipmentValue: '装备价值',
            inventoryValue: '库存价值',
            marketValue: '订单价值',
            houseValue: '房子价值',
            abilityValue: '技能价值',
            currentAssetsValue: '流动资产',
            nonCurrentAssetsValue: '非流动资产',
            totalAssets: '总资产',
        },
        en: {
            // Base UI
            assetsGrowth: '💰Total Asset Growth',
            historyIcon: 'Show asset history chart',
            metricsIconShow: 'Show statistics',
            metricsIconHide: 'Hide statistics',
            settingsIcon: 'Chart settings',

            // Extra info
            avg7Days: '7-day avg',
            lastRecord: 'Last record',

            // Chart modal
            chartTitle: 'Asset History Chart',
            timeRange: 'Time range:',

            // Settings modal
            settingsTitle: 'Asset Chart Settings',
            viewMode: 'View Mode',
            summaryView: 'Summary View',
            detailedView: 'Detailed View',

            // Summary view options
            summaryOptions: 'Summary View Options',
            showCurrent: 'Show Current Assets',
            showNonCurrent: 'Show Non-Current Assets',

            // Detailed view options
            detailedOptions: 'Detailed View Options',
            showEquipped: 'Show Equipment Value',
            showInventory: 'Show Inventory Value',
            showMarket: 'Show Market Value',
            showHouse: 'Show House Value',
            showAbility: 'Show Ability Value',

            // General options
            generalOptions: 'General Options',
            showTotal: 'Show Total Assets',

            // Unit settings
            unitSettings: 'Asset Display Unit',
            unitMode: 'Unit mode:',
            unitAuto: 'Auto',
            unitK: 'K',
            unitM: 'M',
            unitB: 'B',

            // Time range settings
            timeRangeSettings: 'Time Range Settings',
            showTimeRange: 'Show time range selector',
            availableTimeRanges: 'Available time ranges:',
            currentButtons: 'Current time range buttons:',

            // Import/Export
            importExport: '📦 Data Import/Export',
            exportAll: 'Export All (with settings)',
            exportData: 'Export Data Only',
            importData: 'Import Data',

            // Import preview
            preview: 'Import Preview',
            version: 'Version',
            exportTime: 'Export time',
            roleCount: 'Characters',
            totalRecords: 'Total records',
            containsSettings: 'Contains settings',
            yes: 'Yes',
            no: 'No',
            roleStats: 'Character statistics:',
            records: 'records',
            confirmImport: 'Confirm Import',
            cancel: 'Cancel',

            // Notifications
            exportSuccess: 'Data exported to',
            exportFailed: 'Export failed',
            importFailed: 'Import failed',
            noImportData: 'No data to import',
            invalidFormat: 'Invalid data format',
            unrecognizedVersion: 'Unrecognized backup version',
            missingAssetData: 'Missing asset data in backup',
            previewFailed: 'Preview failed',

            // Metrics
            currentAssets: 'Current Assets',
            recentUpdate: 'Last update',
            dailyChange: 'Daily Change',
            hourlyRate: 'Hourly Rate',
            hourlyRateDesc: 'Based on daily average',
            avg7Growth: '7-day Avg Growth',
            avg7Desc: 'Avg rate / Avg change',
            winRate7: '7-day Win Rate',
            avg30Daily: '30-day Daily Avg',
            bestWorstDay: 'Best/Worst Day',
            lastDouble: 'Last Double',
            noRecord: 'No record',
            doubling: 'Double in',
            target: 'Target',
            days: 'd',
            profitLossFlat: 'Win/Loss/Flat',

            // Value names
            equipmentValue: 'Equipment Value',
            inventoryValue: 'Inventory Value',
            marketValue: 'Market Value',
            houseValue: 'House Value',
            abilityValue: 'Ability Value',
            currentAssetsValue: 'Current Assets',
            nonCurrentAssetsValue: 'Non-Current Assets',
            totalAssets: 'Total Assets',
        }
    };

    // 获取当前语言
    function getCurrentLanguage() {
        // 从设置中获取语言偏好,默认为中文
        const lang = GM_getValue('dailyAssetsLanguage', 'zh');
        console.log('[DailyAssets] Current language:', lang);
        return lang === 'en' ? LANGUAGES.en : LANGUAGES.zh;
    }

    // 切换语言
    function toggleLanguage() {
        const currentLang = GM_getValue('dailyAssetsLanguage', 'zh');
        const newLang = currentLang === 'zh' ? 'en' : 'zh';
        console.log('[DailyAssets] Switching language from', currentLang, 'to', newLang);
        GM_setValue('dailyAssetsLanguage', newLang);

        // 延迟刷新,确保值已保存
        setTimeout(() => {
            location.reload();
        }, 100);
    }

    // 获取当前语言的文本
    function t(key) {
        const lang = getCurrentLanguage();
        return lang[key] || key;
    }

    /* =========================
       常量与存储键定义
    ========================= */
    const STORAGE_KEYS = {
        assetData: 'kbd_asset_data_v2',
        metricsPrefs: 'kbd_metrics_prefs',
        metricsPanel: 'kbd_metrics_panel',
        lastUpdate: 'kbd_last_update_at',
    };

    /* =========================
       样式定义(添加语言切换按钮样式)
    ========================= */
    GM_addStyle(`
        /* 基础样式 */
        .asset-delta-display {
            text-align: left;
            color: #fff;
            font-size: 16px;
            margin: 0px 0;
        }
        .asset-delta-label {
            font-weight: bold;
            margin-right: 5px;
        }
        #showHistoryIcon, #showMetricsIcon, #languageToggle {
            cursor: pointer;
            margin-left: 8px;
            font-size: 16px;
            display: inline-block;
            margin-top: 0px;
            opacity: 0.8;
            transition: opacity 0.2s;
        }
        #showHistoryIcon:hover, #showMetricsIcon:hover, #languageToggle:hover {
            opacity: 1;
        }
        #languageToggle {
            color: #FFD700;
        }
        #settingsToggle {
            cursor: pointer;
            margin-left: 8px;
            font-size: 16px;
            color: #2196F3;
            opacity: 0.8;
            transition: opacity 0.2s;
        }
        #settingsToggle:hover {
            opacity: 1;
        }
        .positive-delta {
            color: #4CAF50;
            font-weight: bold;
        }
        .negative-delta {
            color: #F44336;
            font-weight: bold;
        }
        .neutral-delta {
            color: #9E9E9E;
            font-weight: bold;
        }

        /* 统计指标面板 */
        .ep-metrics-grid {
            display: grid;
            grid-template-columns: repeat(4, minmax(0, 1fr));
            gap: 10px;
            padding: 10px 15px;
            background: #111b2b;
            border-top: 1px solid rgba(255,255,255,0.05);
            border-bottom: 1px solid rgba(255,255,255,0.05);
        }
        @media (max-width: 720px) {
            .ep-metrics-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        }
        @media (max-width: 420px) {
            .ep-metrics-grid { grid-template-columns: 1fr; }
        }

        .ep-metric-card {
            background: rgba(255,255,255,0.06);
            border: 1px solid rgba(255,255,255,0.08);
            border-radius: 8px;
            padding: 8px 10px;
            display: flex;
            flex-direction: column;
            gap: 4px;
            min-width: 0;
        }
        .ep-metric-card h4 {
            font-size: 12px;
            font-weight: normal;
            color: #9fb4d1;
            margin: 0;
        }
        .ep-metric-card strong {
            font-size: 18px;
            color: #f7fafc;
            word-break: break-word;
        }
        .ep-metric-card span {
            font-size: 12px;
            color: #7f8ca3;
            word-break: break-word;
        }

        /* 额外信息行 */
        .ep-delta-extra {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            margin-top: 6px;
            font-size: 14px;
            color: #cfd8e3;
        }
        .ep-delta-extra span {
            background: rgba(255, 255, 255, 0.08);
            border-radius: 4px;
            padding: 3px 8px;
        }

        /* 弹窗样式 */
        #deltaNetworthChartModal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 1200px;
            max-width: 95vw;
            background: #1e1e1e;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.6);
            z-index: 10000;
            display: none;
            flex-direction: column;
            color: #f5f5f5;
            border: 1px solid rgba(255,255,255,0.08);
        }
        #deltaNetworthChartModal.dragging {
            cursor: grabbing;
        }
        #deltaNetworthChartHeader {
            padding: 10px 15px;
            background: #333;
            color: white;
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: move;
            user-select: none;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
        }
        #netWorthChartBody {
            padding: 15px;
            background: #0b1522;
            border-bottom-left-radius: 8px;
            border-bottom-right-radius: 8px;
            border: 1px solid rgba(255,255,255,0.05);
        }
        #netWorthChart {
            width: 100%;
            height: 400px;
            background: radial-gradient(circle at top, rgba(0,198,255,0.08), rgba(2,12,24,0.95));
            border-radius: 6px;
        }

        /* 设置弹窗样式 */
        #assetSettingsModal {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 600px;
            max-width: 95vw;
            background: #1e1e1e;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.6);
            z-index: 10001;
            display: none;
            flex-direction: column;
        }
        #assetSettingsModal.dragging {
            cursor: grabbing;
        }
        #assetSettingsHeader {
            padding: 10px 15px;
            background: #333;
            color: white;
            font-weight: bold;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: default;
            user-select: none;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
        }
        #assetSettingsBody {
            padding: 15px;
            max-height: 70vh;
            overflow-y: auto;
        }

        /* 模态遮罩 */
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            z-index: 9999;
            display: none;
        }

        /* 关闭按钮 */
        .close-btn {
            cursor: pointer;
            font-size: 18px;
            color: #fff;
        }
        .close-btn:hover {
            color: #f44336;
        }

        /* 视图切换 */
        .view-toggle {
            display: flex;
            background: #333;
            border-radius: 4px;
            padding: 2px;
            margin-bottom: 15px;
        }
        .view-option {
            flex: 1;
            text-align: center;
            padding: 8px;
            cursor: pointer;
            border-radius: 3px;
            font-weight: bold;
            transition: all 0.3s ease;
        }
        .view-option.active {
            background: #4CAF50;
            color: white;
        }
        .view-option:not(.active) {
            background: transparent;
            color: #ccc;
        }
        .view-option:not(.active):hover {
            background: #444;
        }

        /* 设置区域 */
        .settings-section {
            background: #2a2a2a;
            padding: 15px;
            margin: 2px 0;
            border-radius: 4px;
        }
        .settings-title {
            color: #fff;
            font-weight: bold;
            font-size: 16px;
            margin-bottom: 5px;
            display: block;
        }
        .settings-group {
            color: #fff;
            margin-bottom: 15px;
        }
        .chart-option {
            margin: 5px 0;
            display: flex;
            align-items: center;
        }
        .chart-option input {
            margin-right: 8px;
        }
        .chart-option label {
            cursor: pointer;
            color: white;
            flex-grow: 1;
        }

        /* 时间范围按钮 */
        .time-range-btn {
            padding: 5px 10px;
            background: rgba(0,0,0,.3);
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin-right: 5px;
            margin-bottom: 5px;
        }
        .time-range-btn:hover {
            background: #555;
        }
        .time-range-btn.active {
            background: rgb(25, 118, 210);
            font-weight: bold;
        }
        .time-range-section {
            margin: 15px 0;
        }

        /* 图表选项容器 */
        #chartOptionsContainer {
            padding: 4px;
            background: #111b2b;
            border-bottom: 0px solid #333;
        }
        #timeRangeOptions {
            margin-top: 10px;
            color: #fff;
        }
        #timeRangeOptions.hidden {
            display: none;
        }
        .time-range-buttons {
            margin-top: 10px;
        }

        /* 单位设置 */
        .unit-toggle {
            display: flex;
            background: #333;
            border-radius: 4px;
            padding: 2px;
            margin-top: 8px;
        }
        .unit-option {
            flex: 1;
            text-align: center;
            padding: 6px;
            cursor: pointer;
            border-radius: 3px;
            font-weight: bold;
            font-size: 14px;
            transition: all 0.3s ease;
        }
        .unit-option.active {
            background: #2196F3;
            color: white;
        }
        .unit-option:not(.active) {
            background: transparent;
            color: #ccc;
        }
        .unit-option:not(.active):hover {
            background: #444;
        }

        /* 导入导出样式 */
        .import-export-section {
            background: #2a2a2a;
            padding: 15px;
            margin: 2px 0;
            border-radius: 4px;
        }

        .import-export-buttons {
            display: flex;
            gap: 10px;
            margin-top: 10px;
            flex-wrap: wrap;
        }

        .import-export-btn {
            padding: 8px 15px;
            background: #2196F3;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: background 0.2s;
            flex: 1;
            min-width: 100px;
        }

        .import-export-btn:hover {
            background: #1976D2;
        }

        .import-export-btn.export-all {
            background: #4CAF50;
        }

        .import-export-btn.export-all:hover {
            background: #45a049;
        }

        .import-export-btn.import {
            background: #FF9800;
        }

        .import-export-btn.import:hover {
            background: #F57C00;
        }

        .file-input-hidden {
            display: none;
        }

        .import-preview {
            margin-top: 15px;
            padding: 10px;
            background: #333;
            border-radius: 4px;
            display: none;
        }

        .import-preview.show {
            display: block;
        }

        .import-preview-title {
            color: #fff;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .import-preview-content {
            max-height: 200px;
            overflow-y: auto;
            color: #ccc;
            font-size: 12px;
        }

        .import-preview-actions {
            display: flex;
            gap: 10px;
            margin-top: 10px;
        }

        .confirm-import-btn {
            background: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px 10px;
            cursor: pointer;
        }

        .cancel-import-btn {
            background: #f44336;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 5px 10px;
            cursor: pointer;
        }

        .notification {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 10px 20px;
            background: #333;
            color: white;
            border-radius: 4px;
            z-index: 10002;
            animation: slideIn 0.3s ease;
        }

        .notification.success {
            background: #4CAF50;
        }

        .notification.error {
            background: #f44336;
        }

        .notification.info {
            background: #2196F3;
        }

        @keyframes slideIn {
            from {
                transform: translateX(100%);
                opacity: 0;
            }
            to {
                transform: translateX(0);
                opacity: 1;
            }
        }
    `);

    /* =========================
       工具函数
    ========================= */
    const safeJsonParse = (raw, fallback) => {
        try { return raw ? JSON.parse(raw) : fallback; } catch { return fallback; }
    };

    const readPrefs = (key) => safeJsonParse(localStorage.getItem(key), {});
    const writePrefs = (key, prefs) => localStorage.setItem(key, JSON.stringify(prefs));

    const getRoleBoolPref = (key, roleId, defaultValue) => {
        const prefs = readPrefs(key);
        if (roleId && Object.prototype.hasOwnProperty.call(prefs, roleId)) return !!prefs[roleId];
        return !!defaultValue;
    };

    const setRoleBoolPref = (key, roleId, value) => {
        if (!roleId) return;
        const prefs = readPrefs(key);
        prefs[roleId] = !!value;
        writePrefs(key, prefs);
    };

    const readRoleLastUpdateMap = () => safeJsonParse(localStorage.getItem(STORAGE_KEYS.lastUpdate), {});
    const writeRoleLastUpdateMap = (map) => localStorage.setItem(STORAGE_KEYS.lastUpdate, JSON.stringify(map || {}));
    const setRoleLastUpdate = (roleId, iso = new Date().toISOString()) => {
        if (!roleId) return;
        const map = readRoleLastUpdateMap();
        map[roleId] = iso;
        writeRoleLastUpdateMap(map);
    };
    const getRoleLastUpdate = (roleId) => {
        if (!roleId) return null;
        const map = readRoleLastUpdateMap();
        return map && map[roleId] ? map[roleId] : null;
    };

    const formatIsoToLocalDateTime = (iso) => {
        if (!iso) return '';
        const d = new Date(iso);
        if (!Number.isFinite(d.getTime())) return String(iso);
        const pad = (n) => String(n).padStart(2, '0');
        return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    };

    // 解析带格式的数字
    function parseFormattedNumber(str) {
        if (!str) return 0;
        const match = String(str).match(/(-?[\d.,]+)\s*([kKmMbBtT]?)/);
        if (!match) return 0;

        let [, numericPart, unit = ''] = match;
        numericPart = numericPart.replace(/\s+/g, '');
        if (!numericPart) return 0;

        const commaCount = (numericPart.match(/,/g) || []).length;
        const dotCount = (numericPart.match(/\./g) || []).length;

        if (commaCount && dotCount) {
            if (numericPart.lastIndexOf('.') > numericPart.lastIndexOf(',')) {
                numericPart = numericPart.replace(/,/g, '');
            } else {
                numericPart = numericPart.replace(/\./g, '');
                numericPart = numericPart.replace(/,/g, '.');
            }
        } else if (commaCount) {
            if (commaCount === 1 && numericPart.split(',')[1]?.length <= 2) numericPart = numericPart.replace(',', '.');
            else numericPart = numericPart.replace(/,/g, '');
        } else if (dotCount > 1) {
            const parts = numericPart.split('.');
            const decimal = parts.pop();
            numericPart = parts.join('') + (decimal ? `.${decimal}` : '');
        }

        const num = parseFloat(numericPart);
        if (isNaN(num)) return 0;

        const multiplierMap = { k: 1e3, m: 1e6, b: 1e9, t: 1e12 };
        const multiplier = multiplierMap[(unit || '').toLowerCase()] || 1;
        return num * multiplier;
    }

    /* =========================
       格式化大数字函数(支持单位模式控制)
    ========================= */
    function formatLargeNumber(num, unitMode = 'auto') {
        const n = Number(num) || 0;
        const abs = Math.abs(n);

        // 根据单位模式格式化
        switch(unitMode) {
            case 'k':
                return (n / 1e3).toFixed(2) + 'K';
            case 'm':
                return (n / 1e6).toFixed(2) + 'M';
            case 'b':
                return (n / 1e9).toFixed(2) + 'B';
            case 'auto':
            default:
                if (abs >= 1e12) return (n / 1e12).toFixed(2) + 'T';
                if (abs >= 1e9) return (n / 1e9).toFixed(2) + 'B';
                if (abs >= 1e6) return (n / 1e6).toFixed(2) + 'M';
                if (abs >= 1e3) return (n / 1e3).toFixed(2) + 'K';
                return n.toFixed(2);
        }
    }

    const formatSignedLargeNumber = (num, unitMode = 'auto') => {
        const n = Number(num) || 0;
        return n > 0 ? `+${formatLargeNumber(n, unitMode)}` : formatLargeNumber(n, unitMode);
    };

    /* =========================
       获取MWITools显示的数据
    ========================= */
    function getMWIToolsValues() {
        return new Promise((resolve) => {
            // 等待netWorthDetails出现
            const checkElement = () => {
                const netWorthDetails = document.getElementById('netWorthDetails');
                if (netWorthDetails && netWorthDetails.children.length > 0) {
                    // 获取所有显示的数值
                    const values = {
                        equippedNetworth: 0,
                        inventoryNetworth: 0,
                        marketListingsNetworth: 0,
                        totalHouseScore: 0,
                        abilityScore: 0
                    };

                    try {
                        // 获取所有div元素
                        const divs = netWorthDetails.querySelectorAll('div');

                        // 遍历所有div,根据文本内容匹配
                        divs.forEach(div => {
                            const text = div.textContent;

                            // 匹配装备价值
                            if (text.includes('装备价值') || text.includes('Equipment value')) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) values.equippedNetworth = parseFormattedNumber(match[1]);
                            }
                            // 匹配库存价值
                            else if (text.includes('库存价值') || text.includes('Inventory value')) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) values.inventoryNetworth = parseFormattedNumber(match[1]);
                            }
                            // 匹配订单价值
                            else if (text.includes('订单价值') || text.includes('Market listing value')) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) values.marketListingsNetworth = parseFormattedNumber(match[1]);
                            }
                            // 匹配房子价值
                            else if (text.includes('房子价值') || text.includes('Houses value')) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) values.totalHouseScore = parseFormattedNumber(match[1]);
                            }
                            // 匹配技能价值
                            else if (text.includes('技能价值') || text.includes('Abilities value')) {
                                const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                if (match) values.abilityScore = parseFormattedNumber(match[1]);
                            }
                        });

                        // 如果没找到具体数值,尝试从currentAssets和nonCurrentAssets中提取
                        const currentAssets = document.querySelector('#currentAssets');
                        const nonCurrentAssets = document.querySelector('#nonCurrentAssets');

                        if (currentAssets) {
                            const currentDivs = currentAssets.querySelectorAll('div');
                            currentDivs.forEach(div => {
                                const text = div.textContent;
                                if (text.includes('装备价值') || text.includes('Equipment value')) {
                                    const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                    if (match) values.equippedNetworth = parseFormattedNumber(match[1]);
                                } else if (text.includes('库存价值') || text.includes('Inventory value')) {
                                    const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                    if (match) values.inventoryNetworth = parseFormattedNumber(match[1]);
                                } else if (text.includes('订单价值') || text.includes('Market listing value')) {
                                    const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                    if (match) values.marketListingsNetworth = parseFormattedNumber(match[1]);
                                }
                            });
                        }

                        if (nonCurrentAssets) {
                            const nonCurrentDivs = nonCurrentAssets.querySelectorAll('div');
                            nonCurrentDivs.forEach(div => {
                                const text = div.textContent;
                                if (text.includes('房子价值') || text.includes('Houses value')) {
                                    const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                    if (match) values.totalHouseScore = parseFormattedNumber(match[1]);
                                } else if (text.includes('技能价值') || text.includes('Abilities value')) {
                                    const match = text.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/);
                                    if (match) values.abilityScore = parseFormattedNumber(match[1]);
                                }
                            });
                        }

                        // 如果还是没找到,尝试从总文本中提取数字
                        if (values.equippedNetworth === 0 && values.inventoryNetworth === 0 &&
                            values.marketListingsNetworth === 0 && values.totalHouseScore === 0 &&
                            values.abilityScore === 0) {

                            const allText = netWorthDetails.textContent;
                            const numbers = allText.match(/([+-]?[\d.,]+\s*[kKmMbBtT]?)/g) || [];

                            if (numbers.length >= 5) {
                                values.equippedNetworth = parseFormattedNumber(numbers[0]);
                                values.inventoryNetworth = parseFormattedNumber(numbers[1]);
                                values.marketListingsNetworth = parseFormattedNumber(numbers[2]);
                                values.totalHouseScore = parseFormattedNumber(numbers[3]);
                                values.abilityScore = parseFormattedNumber(numbers[4]);
                            }
                        }

                        console.log('[DailyAssets] 获取到的资产价值:', values);
                        resolve(values);
                    } catch (error) {
                        console.error('[DailyAssets] 解析价值时出错:', error);
                        resolve({
                            equippedNetworth: 0,
                            inventoryNetworth: 0,
                            marketListingsNetworth: 0,
                            totalHouseScore: 0,
                            abilityScore: 0
                        });
                    }
                } else {
                    // 没找到元素,继续等待
                    setTimeout(checkElement, 1000);
                }
            };

            checkElement();
        });
    }

    /* =========================
       数据存储类
    ========================= */
    class AssetDataStore {
        constructor(storageKey = STORAGE_KEYS.assetData, maxDays = 180, currentRole = 'default') {
            this.storageKey = storageKey;
            this.maxDays = maxDays;
            this.currentRole = currentRole;
            this.data = this.loadFromStorage();
        }

        setRole(roleId) {
            this.currentRole = roleId;
        }

        getRoleData() {
            if (!this.data[this.currentRole]) {
                this.data[this.currentRole] = {};
            }
            return this.data[this.currentRole];
        }

        getTodayKey() {
            const now = new Date();
            const utcPlus8 = new Date(now.getTime() + 8 * 3600000);
            return utcPlus8.toISOString().split('T')[0];
        }

        getYesterdayKey() {
            const now = new Date();
            const yesterday = new Date(now.getTime() - 24 * 3600000);
            const utcPlus8 = new Date(yesterday.getTime() + 8 * 3600000);
            return utcPlus8.toISOString().split('T')[0];
        }

        loadFromStorage() {
            return safeJsonParse(localStorage.getItem(this.storageKey), {});
        }

        saveToStorage() {
            localStorage.setItem(this.storageKey, JSON.stringify(this.data));
        }

        setTodayDetailedValues(equipped, inventory, market, house, ability) {
            const roleData = this.getRoleData();
            const today = this.getTodayKey();

            const totalAssets = equipped + inventory + market + house + ability;
            const currentAssets = equipped + inventory + market;
            const nonCurrentAssets = house + ability;

            roleData[today] = {
                equippedNetworth: equipped,
                inventoryNetworth: inventory,
                marketListingsNetworth: market,
                totalHouseScore: house,
                abilityScore: ability,
                currentAssets: currentAssets,
                nonCurrentAssets: nonCurrentAssets,
                totalAssets: totalAssets,
                timestamp: Date.now()
            };

            this.cleanupOldData();
            this.saveToStorage();
        }

        cleanupOldData() {
            const roleData = this.getRoleData();
            const keys = Object.keys(roleData).sort();
            const cutoff = Date.now() - (this.maxDays * 24 * 3600 * 1000);

            const newData = {};
            keys.forEach(key => {
                if (roleData[key].timestamp > cutoff) {
                    newData[key] = roleData[key];
                }
            });
            this.data[this.currentRole] = newData;
        }

        getTodayDeltas() {
            const roleData = this.getRoleData();
            const todayKey = this.getTodayKey();
            const yesterdayKey = this.getYesterdayKey();

            const todayData = roleData[todayKey] || {
                equippedNetworth: 0, inventoryNetworth: 0, marketListingsNetworth: 0,
                totalHouseScore: 0, abilityScore: 0,
                currentAssets: 0, nonCurrentAssets: 0, totalAssets: 0
            };

            const yesterdayData = roleData[yesterdayKey] || {
                equippedNetworth: 0, inventoryNetworth: 0, marketListingsNetworth: 0,
                totalHouseScore: 0, abilityScore: 0,
                currentAssets: 0, nonCurrentAssets: 0, totalAssets: 0
            };

            return {
                equippedDelta: todayData.equippedNetworth - yesterdayData.equippedNetworth,
                inventoryDelta: todayData.inventoryNetworth - yesterdayData.inventoryNetworth,
                marketDelta: todayData.marketListingsNetworth - yesterdayData.marketListingsNetworth,
                houseDelta: todayData.totalHouseScore - yesterdayData.totalHouseScore,
                abilityDelta: todayData.abilityScore - yesterdayData.abilityScore,
                totalDelta: todayData.totalAssets - yesterdayData.totalAssets,
                totalRatio: yesterdayData.totalAssets > 0 ?
                    (todayData.totalAssets - yesterdayData.totalAssets) / yesterdayData.totalAssets * 100 : 0
            };
        }

        getHistoryData(days = 30) {
            const roleData = this.getRoleData();
            const cutoff = Date.now() - (days * 24 * 3600 * 1000);

            const filtered = Object.entries(roleData)
                .filter(([_, data]) => data.timestamp > cutoff)
                .sort(([a], [b]) => new Date(a) - new Date(b));

            return {
                labels: filtered.map(([date]) => date),
                equippedNetworth: filtered.map(([_, data]) => data.equippedNetworth),
                inventoryNetworth: filtered.map(([_, data]) => data.inventoryNetworth),
                marketListingsNetworth: filtered.map(([_, data]) => data.marketListingsNetworth),
                totalHouseScore: filtered.map(([_, data]) => data.totalHouseScore),
                abilityScore: filtered.map(([_, data]) => data.abilityScore),
                currentAssets: filtered.map(([_, data]) => data.currentAssets),
                nonCurrentAssets: filtered.map(([_, data]) => data.nonCurrentAssets),
                totalAssets: filtered.map(([_, data]) => data.totalAssets)
            };
        }

        getHistoryEntriesSorted() {
            const roleData = this.getRoleData();
            return Object.entries(roleData)
                .filter(([_, data]) => data.totalAssets !== undefined)
                .map(([date, data]) => [date, data.totalAssets])
                .sort(([a], [b]) => new Date(a) - new Date(b));
        }

        getAllRoles() {
            return Object.keys(this.data);
        }

        removeRole(roleId) {
            delete this.data[roleId];
            this.saveToStorage();
        }
    }

    /* =========================
       统计指标计算函数
    ========================= */
    const computeDeltas = (sortedEntries) => {
        const diff = [];
        for (let i = 1; i < sortedEntries.length; i++) {
            const prev = sortedEntries[i - 1][1];
            const curr = sortedEntries[i][1];
            diff.push({
                date: sortedEntries[i][0],
                value: curr - prev,
                growthPct: prev ? ((curr - prev) / prev) * 100 : 0,
            });
        }
        return diff;
    };

    const computeStreaks = (differences) => {
        let bestGain = 0;
        let worstLoss = 0;
        let bestDay = null;
        let worstDay = null;
        let winStreak = 0;
        let loseStreak = 0;
        let currentWin = 0;
        let currentLose = 0;

        differences.forEach((d) => {
            if (d.value >= 0) {
                currentWin += 1;
                currentLose = 0;
                if (d.value > bestGain) {
                    bestGain = d.value;
                    bestDay = d.date;
                }
            } else {
                currentLose += 1;
                currentWin = 0;
                if (d.value < worstLoss) {
                    worstLoss = d.value;
                    worstDay = d.date;
                }
            }
            winStreak = Math.max(winStreak, currentWin);
            loseStreak = Math.max(loseStreak, currentLose);
        });

        return { bestGain, bestDay, worstLoss, worstDay, winStreak, loseStreak };
    };

    const predictDoublingTime = (differences, currentValue, windowDays = 7) => {
        if (!currentValue || differences.length === 0) return null;
        const recent = differences.slice(-windowDays);
        if (!recent.length) return null;
        const avgGrowth = recent.reduce((sum, d) => sum + d.value, 0) / recent.length;
        if (avgGrowth <= 0) return null;
        return Math.ceil((currentValue) / avgGrowth);
    };

    const predictTargetDate = (differences, currentValue, targetValue) => {
        if (!currentValue || currentValue >= targetValue) {
            return { days: 0, targetValue };
        }
        const recent = differences.slice(-7);
        const avgGrowth = recent.reduce((sum, d) => sum + d.value, 0) / (recent.length || 1);
        if (avgGrowth <= 0) return null;
        const remaining = targetValue - currentValue;
        return { days: Math.ceil(remaining / avgGrowth), targetValue };
    };

    const nextRoundNumber = (value) => {
        if (!value) return 0;
        const magnitude = Math.pow(10, Math.max(3, Math.floor(Math.log10(value))));
        return Math.ceil(value / magnitude) * magnitude;
    };

    const computeTotalMetricsFromEntries = (sortedEntries, unitMode = 'auto') => {
        const latestRecordDate = sortedEntries.length ? sortedEntries[sortedEntries.length - 1][0] : '-';
        const valueToPersist = sortedEntries.length ? (sortedEntries[sortedEntries.length - 1][1] || 0) : 0;

        const differences = computeDeltas(sortedEntries);
        const todayDelta = differences.length ? differences[differences.length - 1] : null;
        const growthPct = todayDelta ? (todayDelta.growthPct || 0) : 0;
        const hourlyRate = todayDelta ? (todayDelta.value / 24) : 0;

        const last7 = differences.slice(-7);
        const avgGrowthPct = last7.length ? last7.reduce((sum, d) => sum + (d.growthPct || 0), 0) / last7.length : 0;
        const avgGrowthValue = last7.length ? last7.reduce((sum, d) => sum + (d.value || 0), 0) / last7.length : 0;

        const streaks = computeStreaks(differences);

        const doublingDays = predictDoublingTime(differences, valueToPersist);
        const targetValue = nextRoundNumber(valueToPersist * 1.05);
        const targetPrediction = predictTargetDate(differences, valueToPersist, targetValue);

        let lastDoubleDate = null;
        let lastDoubleDays = null;
        let lastDoubleValue = null;
        if (valueToPersist > 0 && sortedEntries.length) {
            const halfValue = valueToPersist / 2;
            const milestoneEntry = sortedEntries.find(([, v]) => Number.isFinite(v) && v >= halfValue);
            if (milestoneEntry) {
                lastDoubleDate = milestoneEntry[0];
                lastDoubleValue = milestoneEntry[1];
                const diffMs = Date.now() - new Date(lastDoubleDate).getTime();
                lastDoubleDays = Math.max(0, Math.floor(diffMs / 86400000));
            }
        }

        return {
            latestRecordDate,
            valueToPersist,
            differences,
            todayDelta,
            growthPct,
            hourlyRate,
            avgGrowthPct,
            avgGrowthValue,
            streaks,
            lastDoubleDate,
            lastDoubleDays,
            lastDoubleValue,
            doublingDays,
            targetPrediction,
            unitMode
        };
    };

    const buildMetricCards = (metrics, unitMode = 'auto') => {
        if (!metrics) return '';
        const cards = metrics.map((metric) => `
            <div class="ep-metric-card">
                <h4>${metric.title}</h4>
                <strong>${metric.value}</strong>
                ${metric.desc ? `<span>${metric.desc}</span>` : ''}
            </div>
        `).join('');
        return `<div class="ep-metrics-grid">${cards}</div>`;
    };

    /* =========================
       设置系统
    ========================= */
    function getChartOptions() {
        const defaults = {
            viewMode: 'summary',
            summaryShowCurrent: true,
            summaryShowNonCurrent: true,
            detailedShowEquipped: true,
            detailedShowInventory: true,
            detailedShowMarketListings: true,
            detailedShowHouse: true,
            detailedShowAbility: true,
            showTotal: true,
            daysToShow: 30,
            showTimeRangeSettings: true,
            visibleTimeRanges: [3, 7, 30, 60, 90, 180],
            unitMode: 'auto',
            showMetricsPanel: true
        };
        const saved = GM_getValue('chartOptions', defaults);
        return {...defaults, ...saved};
    }

    function saveChartOptions(options) {
        GM_setValue('chartOptions', options);
    }

    /* =========================
       导入导出功能
    ========================= */
    class ImportExportManager {
        constructor(store) {
            this.store = store;
            this.importData = null;
        }

        // 导出数据
        exportData(includeSettings = true) {
            const exportObj = {
                version: GM_info.script.version,
                exportDate: new Date().toISOString(),
                exportType: includeSettings ? 'full' : 'data_only',
                data: {
                    assetData: this.store.data,
                    lastUpdate: readRoleLastUpdateMap()
                }
            };

            // 如果包含设置,也导出设置
            if (includeSettings) {
                exportObj.settings = {
                    chartOptions: GM_getValue('chartOptions', {}),
                    metricsPrefs: readPrefs(STORAGE_KEYS.metricsPrefs),
                    metricsPanel: readPrefs(STORAGE_KEYS.metricsPanel)
                };
            }

            return exportObj;
        }

        // 导出为JSON字符串
        exportToJson(includeSettings = true) {
            const exportObj = this.exportData(includeSettings);
            return JSON.stringify(exportObj, null, 2);
        }

        // 导出并下载文件
        downloadExport(includeSettings = true) {
            const jsonStr = this.exportToJson(includeSettings);
            const blob = new Blob([jsonStr], { type: 'application/json' });
            const url = URL.createObjectURL(blob);

            const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
            const filename = `milkyway_assets_backup_${timestamp}.json`;

            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);

            this.showNotification(`${t('exportSuccess')} ${filename}`, 'success');
        }

        // 验证导入数据
        validateImportData(data) {
            if (!data || typeof data !== 'object') {
                throw new Error(t('invalidFormat'));
            }

            if (!data.version) {
                throw new Error(t('unrecognizedVersion'));
            }

            if (!data.data || !data.data.assetData) {
                throw new Error(t('missingAssetData'));
            }

            return true;
        }

        // 预览导入数据
        previewImport(jsonStr) {
            try {
                const data = JSON.parse(jsonStr);
                this.validateImportData(data);

                const assetData = data.data.assetData;
                const roleCount = Object.keys(assetData).length;
                const totalRecords = Object.values(assetData).reduce((sum, roleData) => {
                    return sum + (roleData ? Object.keys(roleData).length : 0);
                }, 0);

                let previewHtml = `
                    <div class="import-preview show">
                        <div class="import-preview-title">${t('preview')}</div>
                        <div class="import-preview-content">
                            <p>📄 ${t('version')}: ${data.version}</p>
                            <p>📅 ${t('exportTime')}: ${new Date(data.exportDate).toLocaleString()}</p>
                            <p>👥 ${t('roleCount')}: ${roleCount}</p>
                            <p>📊 ${t('totalRecords')}: ${totalRecords}</p>
                            <p>📋 ${t('containsSettings')}: ${data.settings ? t('yes') : t('no')}</p>
                `;

                // 显示每个角色的数据统计
                if (roleCount > 0) {
                    previewHtml += `<p>📈 ${t('roleStats')}</p><ul>`;
                    Object.entries(assetData).forEach(([role, roleData]) => {
                        const recordCount = roleData ? Object.keys(roleData).length : 0;
                        previewHtml += `<li>${role}: ${recordCount}${t('records')}</li>`;
                    });
                    previewHtml += '</ul>';
                }

                previewHtml += `
                        </div>
                        <div class="import-preview-actions">
                            <button class="confirm-import-btn">${t('confirmImport')}</button>
                            <button class="cancel-import-btn">${t('cancel')}</button>
                        </div>
                    </div>
                `;

                this.importData = data;
                return previewHtml;
            } catch (error) {
                throw new Error(`${t('previewFailed')}: ${error.message}`);
            }
        }

        // 执行导入
        executeImport(options = { merge: true }) {
            if (!this.importData) {
                throw new Error(t('noImportData'));
            }

            const data = this.importData;

            // 导入资产数据
            if (options.merge) {
                // 合并模式:保留现有数据,添加新数据
                Object.entries(data.data.assetData).forEach(([role, roleData]) => {
                    if (!this.store.data[role]) {
                        this.store.data[role] = {};
                    }
                    Object.assign(this.store.data[role], roleData);
                });
            } else {
                // 覆盖模式:完全替换
                this.store.data = data.data.assetData;
            }

            // 保存资产数据
            this.store.saveToStorage();

            // 导入最后更新时间
            if (data.data.lastUpdate) {
                const currentLastUpdate = readRoleLastUpdateMap();
                if (options.merge) {
                    Object.assign(currentLastUpdate, data.data.lastUpdate);
                    writeRoleLastUpdateMap(currentLastUpdate);
                } else {
                    writeRoleLastUpdateMap(data.data.lastUpdate);
                }
            }

            // 导入设置
            if (data.settings) {
                if (data.settings.chartOptions) {
                    saveChartOptions(data.settings.chartOptions);
                }
                if (data.settings.metricsPrefs) {
                    writePrefs(STORAGE_KEYS.metricsPrefs, data.settings.metricsPrefs);
                }
                if (data.settings.metricsPanel) {
                    writePrefs(STORAGE_KEYS.metricsPanel, data.settings.metricsPanel);
                }
            }

            this.importData = null;

            // 刷新页面显示
            location.reload();
        }

        // 显示通知
        showNotification(message, type = 'info') {
            const notification = document.createElement('div');
            notification.className = `notification ${type}`;
            notification.textContent = message;
            document.body.appendChild(notification);

            setTimeout(() => {
                notification.remove();
            }, 3000);
        }
    }

    // 创建隐藏的文件输入元素
    function createFileInput() {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = '.json,application/json';
        input.className = 'file-input-hidden';
        input.id = 'importFileInput';
        document.body.appendChild(input);
        return input;
    }

    // 在设置弹窗中添加导入导出部分
    function addImportExportToSettings(settingsModal, importExportManager) {
        const settingsBody = document.getElementById('assetSettingsBody');

        const importExportSection = document.createElement('div');
        importExportSection.className = 'import-export-section';
        importExportSection.innerHTML = `
            <span class="settings-title">${t('importExport')}</span>
            <div class="import-export-buttons">
                <button class="import-export-btn export-all" id="exportFullBtn">${t('exportAll')}</button>
                <button class="import-export-btn" id="exportDataBtn">${t('exportData')}</button>
                <button class="import-export-btn import" id="importBtn">${t('importData')}</button>
            </div>
            <div id="importPreviewContainer"></div>
        `;

        settingsBody.appendChild(importExportSection);

        // 创建文件输入
        const fileInput = createFileInput();

        // 导出全部(含设置)
        document.getElementById('exportFullBtn').addEventListener('click', () => {
            try {
                importExportManager.downloadExport(true);
            } catch (error) {
                importExportManager.showNotification(`${t('exportFailed')}: ${error.message}`, 'error');
            }
        });

        // 仅导出数据
        document.getElementById('exportDataBtn').addEventListener('click', () => {
            try {
                importExportManager.downloadExport(false);
            } catch (error) {
                importExportManager.showNotification(`${t('exportFailed')}: ${error.message}`, 'error');
            }
        });

        // 导入数据
        document.getElementById('importBtn').addEventListener('click', () => {
            fileInput.click();
        });

        // 处理文件选择
        fileInput.addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (!file) return;

            const reader = new FileReader();
            reader.onload = (e) => {
                try {
                    const previewHtml = importExportManager.previewImport(e.target.result);
                    const previewContainer = document.getElementById('importPreviewContainer');
                    previewContainer.innerHTML = previewHtml;

                    // 绑定预览按钮事件
                    const confirmBtn = previewContainer.querySelector('.confirm-import-btn');
                    const cancelBtn = previewContainer.querySelector('.cancel-import-btn');

                    if (confirmBtn) {
                        confirmBtn.addEventListener('click', () => {
                            try {
                                importExportManager.executeImport({ merge: true });
                            } catch (error) {
                                importExportManager.showNotification(`${t('importFailed')}: ${error.message}`, 'error');
                            }
                        });
                    }

                    if (cancelBtn) {
                        cancelBtn.addEventListener('click', () => {
                            previewContainer.innerHTML = '';
                            importExportManager.importData = null;
                            fileInput.value = ''; // 清空文件输入
                        });
                    }
                } catch (error) {
                    importExportManager.showNotification(error.message, 'error');
                    fileInput.value = ''; // 清空文件输入
                }
            };
            reader.readAsText(file);
        });
    }

    /* =========================
       主逻辑
    ========================= */
    window.kbd_calculateTotalNetworth = function kbd_calculateTotalNetworth(
        equippedNetworth,
        inventoryNetworth,
        marketListingsNetworth,
        totalHouseScore,
        abilityScore,
        dom
    ) {
        // 检测角色ID
        const detectRoleId = () => {
            const candidates = [
                document.querySelector('.CharacterName_name__1amXp span'),
                document.querySelector('[class*="CharacterName_name"] span'),
                document.querySelector('[data-testid="character-name"]'),
            ];
            const text = (candidates.find(Boolean)?.textContent || '').replace(/\s+/g, ' ').trim();
            return text || 'default';
        };

        const roleId = detectRoleId();
        const store = new AssetDataStore();
        store.setRole(roleId);

        let chart = null;
        let currentModal = null;
        let metricsPanelVisible = getRoleBoolPref(STORAGE_KEYS.metricsPanel, roleId, true);

        // 创建模态遮罩
        function createOverlay() {
            const overlay = document.createElement('div');
            overlay.className = 'modal-overlay';
            overlay.id = 'modalOverlay';
            overlay.addEventListener('click', (e) => {
                if (e.target === overlay) {
                    hideAllModals();
                }
            });
            document.body.appendChild(overlay);
            return overlay;
        }

        // 显示遮罩
        function showOverlay() {
            const overlay = document.getElementById('modalOverlay') || createOverlay();
            overlay.style.display = 'block';
        }

        // 隐藏遮罩
        function hideOverlay() {
            const overlay = document.getElementById('modalOverlay');
            if (overlay) {
                overlay.style.display = 'none';
            }
        }

        // 隐藏所有弹窗
        function hideAllModals() {
            document.querySelectorAll('#deltaNetworthChartModal, #assetSettingsModal').forEach(modal => {
                modal.style.display = 'none';
            });
            hideOverlay();
            currentModal = null;
        }

        // 设置弹窗拖动
        function setupDrag(modal) {
            let isDragging = false;
            let startX, startY, initialLeft, initialTop;

            const header = modal.querySelector('#deltaNetworthChartHeader') || modal.querySelector('#assetSettingsHeader');

            header.addEventListener('mousedown', (e) => {
                if (e.target.className === 'close-btn') return;

                isDragging = true;
                startX = e.clientX;
                startY = e.clientY;
                const rect = modal.getBoundingClientRect();
                initialLeft = rect.left;
                initialTop = rect.top;
                modal.classList.add('dragging');
                e.preventDefault();
            });

            document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    const dx = e.clientX - startX;
                    const dy = e.clientY - startY;
                    modal.style.left = `${initialLeft + dx}px`;
                    modal.style.top = `${initialTop + dy}px`;
                    modal.style.transform = 'none';
                }
            });

            document.addEventListener('mouseup', () => {
                isDragging = false;
                modal.classList.remove('dragging');
            });
        }

        // 显示统计指标
        function renderMetricsPanel() {
            const entries = store.getHistoryEntriesSorted();
            if (entries.length < 2) return '';

            const options = getChartOptions();
            const mtxAll = computeTotalMetricsFromEntries(entries, options.unitMode);
            const lastUpdateIso = getRoleLastUpdate(roleId);
            const lastUpdateStr = lastUpdateIso ? formatIsoToLocalDateTime(lastUpdateIso) : (mtxAll.latestRecordDate || '-');

            const diffs = Array.isArray(mtxAll.differences) ? mtxAll.differences : [];
            const last7Diffs = diffs.slice(-7);
            const last30Diffs = diffs.slice(-30);

            const winCount7 = last7Diffs.filter((d) => d && Number.isFinite(d.value) && d.value > 0).length;
            const loseCount7 = last7Diffs.filter((d) => d && Number.isFinite(d.value) && d.value < 0).length;
            const flatCount7 = last7Diffs.filter((d) => d && Number.isFinite(d.value) && d.value === 0).length;
            const rate7 = last7Diffs.length ? (winCount7 / last7Diffs.length) * 100 : null;

            const avg30Value = last30Diffs.length ? last30Diffs.reduce((sum, d) => sum + (d.value || 0), 0) / last30Diffs.length : null;
            const avg30Pct = last30Diffs.length ? last30Diffs.reduce((sum, d) => sum + (d.growthPct || 0), 0) / last30Diffs.length : null;

            const bestWorstValue = `${formatSignedLargeNumber(mtxAll.streaks?.bestGain || 0, options.unitMode)} / ${formatSignedLargeNumber(mtxAll.streaks?.worstLoss || 0, options.unitMode)}`;
            const bestWorstDesc = `${mtxAll.streaks?.bestDay || '-'} | ${mtxAll.streaks?.worstDay || '-'}`;

            const predictBits = [];
            if (mtxAll.doublingDays) predictBits.push(`${t('doubling')}: ${mtxAll.doublingDays}${t('days')}`);
            if (mtxAll.targetPrediction) predictBits.push(`${t('target')}: ${mtxAll.targetPrediction.days}${t('days')}→${formatLargeNumber(mtxAll.targetPrediction.targetValue, options.unitMode)}`);

            const metrics = [
                { title: t('currentAssets'), value: formatLargeNumber(mtxAll.valueToPersist || 0, options.unitMode), desc: `${t('recentUpdate')}: ${lastUpdateStr}` },
                {
                    title: t('dailyChange'),
                    value: `${(mtxAll.growthPct >= 0 ? '+' : '')}${(mtxAll.growthPct || 0).toFixed(2)}%`,
                    desc: formatSignedLargeNumber(mtxAll.todayDelta?.value || 0, options.unitMode),
                },
                { title: t('hourlyRate'), value: formatLargeNumber(mtxAll.hourlyRate || 0, options.unitMode), desc: t('hourlyRateDesc') },
                {
                    title: t('avg7Growth'),
                    value: `${(mtxAll.avgGrowthPct >= 0 ? '+' : '')}${(mtxAll.avgGrowthPct || 0).toFixed(2)}% / ${formatSignedLargeNumber(mtxAll.avgGrowthValue || 0, options.unitMode)}`,
                    desc: t('avg7Desc'),
                },
                {
                    title: t('winRate7'),
                    value: rate7 === null ? '—' : `${winCount7}/${last7Diffs.length} (${rate7.toFixed(0)}%)`,
                    desc: `${t('profitLossFlat')}: ${winCount7}/${loseCount7}/${flatCount7}`,
                },
                {
                    title: t('avg30Daily'),
                    value: avg30Value === null ? '—' : `${(avg30Pct >= 0 ? '+' : '')}${(avg30Pct || 0).toFixed(2)}% / ${formatSignedLargeNumber(avg30Value || 0, options.unitMode)}`,
                    desc: predictBits.length ? `${predictBits.join(' | ')}` : t('avg7Desc'),
                },
                { title: t('bestWorstDay'), value: bestWorstValue, desc: bestWorstDesc },
                {
                    title: t('lastDouble'),
                    value: Number.isFinite(mtxAll.lastDoubleDays) ? `${mtxAll.lastDoubleDays} ${t('days')}` : t('noRecord'),
                    desc: mtxAll.lastDoubleDate ? `${mtxAll.lastDoubleDate}: ${formatLargeNumber(mtxAll.lastDoubleValue || 0, options.unitMode)}` : '—',
                },
            ];

            return buildMetricCards(metrics);
        }

        // 更新显示
        const updateDisplay = (isFirst = false) => {
            store.setTodayDetailedValues(
                equippedNetworth,
                inventoryNetworth,
                marketListingsNetworth,
                totalHouseScore,
                abilityScore
            );

            setRoleLastUpdate(roleId);

            const deltas = store.getTodayDeltas();
            const options = getChartOptions();
            const formattedTotalDelta = formatLargeNumber(deltas.totalDelta, options.unitMode);
            const totalDeltaClass = deltas.totalDelta > 0 ? 'positive-delta' :
                                  (deltas.totalDelta < 0 ? 'negative-delta' : 'neutral-delta');

            // 计算近7天日均
            const entries = store.getHistoryEntriesSorted();
            const last7 = entries.slice(-8);
            let avg7 = 0;
            if (last7.length >= 2) {
                let s = 0;
                let c = 0;
                for (let i = 1; i < last7.length; i++) {
                    const d = last7[i][1] - last7[i - 1][1];
                    if (Number.isFinite(d)) { s += d; c += 1; }
                }
                avg7 = c ? s / c : 0;
            }

            const lastUpdateIso = getRoleLastUpdate(roleId);
            const lastUpdateStr = lastUpdateIso ? formatIsoToLocalDateTime(lastUpdateIso) : (entries.length ? entries[entries.length - 1][0] : '—');

            if (isFirst) {
                const metricsHTML = metricsPanelVisible ? renderMetricsPanel() : '';

                dom.insertAdjacentHTML('afterend', `
                    <div id="assetDeltaContainer" style="margin-top: 0px;">
                        <div class="asset-delta-display">
                            <span class="asset-delta-label">${t('assetsGrowth')}:</span>
                            <span class="${totalDeltaClass}">${formattedTotalDelta}</span>
                            <span id="showHistoryIcon" title="${t('historyIcon')}">📊</span>
                            <span id="showMetricsIcon" title="${metricsPanelVisible ? t('metricsIconHide') : t('metricsIconShow')}">📈</span>
                            <span id="languageToggle" title="中文/English">🌐</span>
                            <span id="settingsToggle" title="${t('settingsIcon')}">⚙️</span>
                        </div>
                        <div class="ep-delta-extra">
                            <span>${t('avg7Days')}: ${formatSignedLargeNumber(avg7, options.unitMode)}</span>
                            <span>${t('lastRecord')}: ${lastUpdateStr}</span>
                        </div>
                        ${metricsHTML}
                    </div>
                `);

                // 创建图表弹窗
                const chartModal = document.createElement('div');
                chartModal.id = 'deltaNetworthChartModal';
                chartModal.innerHTML = `
                    <div id="deltaNetworthChartHeader">
                        <span>${t('chartTitle')} (v${GM_info.script.version})</span>
                        <span class="close-btn" id="chartModalCloseBtn">❌</span>
                    </div>
                    <div id="chartOptionsContainer">
                        <div id="timeRangeOptions">
                            <span style="margin-right:10px;font-weight:bold;color:#fff;">${t('timeRange')}</span>
                        </div>
                    </div>
                    <div id="ep-metrics-container"></div>

                    <div id="netWorthChartBody">
                        <canvas id="netWorthChart"></canvas>
                    </div>
                `;
                document.body.appendChild(chartModal);

                // 创建设置弹窗
                const settingsModal = document.createElement('div');
                settingsModal.id = 'assetSettingsModal';
                settingsModal.innerHTML = `
                    <div id="assetSettingsHeader">
                        <span>${t('settingsTitle')} (v${GM_info.script.version})</span>
                        <span class="close-btn" id="settingsModalCloseBtn">❌</span>
                    </div>
                    <div id="assetSettingsBody">
                        <!-- 视图切换 -->
                        <div class="settings-section">
                            <span class="settings-title">${t('viewMode')}</span>
                            <div class="view-toggle">
                                <div class="view-option active" data-view="summary">${t('summaryView')}</div>
                                <div class="view-option" data-view="detailed">${t('detailedView')}</div>
                            </div>
                        </div>

                        <!-- 摘要视图选项 -->
                        <div class="settings-section summary-view-options">
                            <span class="settings-title">${t('summaryOptions')}</span>
                            <div class="chart-option">
                                <input type="checkbox" id="summaryShowCurrent">
                                <label for="summaryShowCurrent">${t('showCurrent')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="summaryShowNonCurrent">
                                <label for="summaryShowNonCurrent">${t('showNonCurrent')}</label>
                            </div>
                        </div>

                        <!-- 详细视图选项 -->
                        <div class="settings-section detailed-view-options hidden">
                            <span class="settings-title">${t('detailedOptions')}</span>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowEquipped">
                                <label for="detailedShowEquipped">${t('showEquipped')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowInventory">
                                <label for="detailedShowInventory">${t('showInventory')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowMarketListings">
                                <label for="detailedShowMarketListings">${t('showMarket')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowHouse">
                                <label for="detailedShowHouse">${t('showHouse')}</label>
                            </div>
                            <div class="chart-option">
                                <input type="checkbox" id="detailedShowAbility">
                                <label for="detailedShowAbility">${t('showAbility')}</label>
                            </div>
                        </div>

                        <!-- 通用选项 -->
                        <div class="settings-section">
                            <span class="settings-title">${t('generalOptions')}</span>
                            <div class="chart-option">
                                <input type="checkbox" id="showTotalOption">
                                <label for="showTotalOption">${t('showTotal')}</label>
                            </div>
                        </div>

                        <!-- 单位设置 -->
                        <div class="settings-section">
                            <span class="settings-title">${t('unitSettings')}</span>
                            <div class="settings-group">
                                <span class="settings-label">${t('unitMode')}</span>
                                <div class="unit-toggle">
                                    <div class="unit-option active" data-unit="auto">${t('unitAuto')}</div>
                                    <div class="unit-option" data-unit="k">${t('unitK')}</div>
                                    <div class="unit-option" data-unit="m">${t('unitM')}</div>
                                    <div class="unit-option" data-unit="b">${t('unitB')}</div>
                                </div>
                            </div>
                        </div>

                        <!-- 时间范围设置 -->
                        <div class="settings-section">
                            <span class="settings-title">${t('timeRangeSettings')}</span>
                            <div class="settings-group">
                                <label class="settings-label">
                                    <input type="checkbox" id="showTimeRangeToggle">
                                    ${t('showTimeRange')}
                                </label>
                            </div>

                            <div class="settings-group">
                                <span class="settings-label">${t('availableTimeRanges')}</span>
                                <div style="margin-top: 5px;">
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange3"> 3${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange7"> 7${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange30"> 30${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange60"> 60${t('days')}
                                    </label>
                                    <label style="color:#fff; margin-right:15px; display:inline-block;">
                                        <input type="checkbox" id="timeRange90"> 90${t('days')}
                                    </label>
                                    <label style="color:#fff; display:inline-block;">
                                        <input type="checkbox" id="timeRange180"> 180${t('days')}
                                    </label>
                                </div>
                            </div>

                            <!-- 添加时间范围按钮容器 -->
                            <div class="settings-group">
                                <span class="settings-label">${t('currentButtons')}</span>
                                <div id="settingsTimeRangeButtons" class="time-range-buttons" style="margin-top: 10px;">
                                    <!-- 时间范围按钮会动态添加到这里 -->
                                </div>
                            </div>
                        </div>
                    </div>
                `;
                document.body.appendChild(settingsModal);

                // 初始化设置
                initSettings();

                // 创建导入导出管理器
                const importExportManager = new ImportExportManager(store);

                // 在设置弹窗中添加导入导出功能
                addImportExportToSettings(settingsModal, importExportManager);

                // 事件监听
                document.getElementById('showHistoryIcon').addEventListener('click', () => showChartModal());
                document.getElementById('settingsToggle').addEventListener('click', () => showSettingsModal());
                document.getElementById('showMetricsIcon').addEventListener('click', toggleMetricsPanel);
                document.getElementById('languageToggle').addEventListener('click', toggleLanguage);

                // 关闭按钮事件
                document.getElementById('chartModalCloseBtn').addEventListener('click', hideAllModals);
                document.getElementById('settingsModalCloseBtn').addEventListener('click', hideAllModals);

                // 视图切换事件
                document.querySelectorAll('.view-option').forEach(option => {
                    option.addEventListener('click', (e) => {
                        const view = e.target.dataset.view;
                        switchView(view);
                    });
                });

                // 单位切换事件
                document.querySelectorAll('.unit-option').forEach(option => {
                    option.addEventListener('click', (e) => {
                        const unit = e.target.dataset.unit;
                        switchUnit(unit);
                    });
                });

                // 选项变化监听
                document.getElementById('summaryShowCurrent').addEventListener('change', updateChartVisibility);
                document.getElementById('summaryShowNonCurrent').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowEquipped').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowInventory').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowMarketListings').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowHouse').addEventListener('change', updateChartVisibility);
                document.getElementById('detailedShowAbility').addEventListener('change', updateChartVisibility);
                document.getElementById('showTotalOption').addEventListener('change', updateChartVisibility);

                // 时间范围设置监听
                document.getElementById('showTimeRangeToggle').addEventListener('change', toggleTimeRangeVisibility);
                document.getElementById('timeRange3').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange7').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange30').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange60').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange90').addEventListener('change', updateTimeRangeSettings);
                document.getElementById('timeRange180').addEventListener('change', updateTimeRangeSettings);

                // 初始化时间范围按钮
                updateSettingsTimeRangeButtons();

                // 设置拖动
                setupDrag(chartModal);
                setupDrag(settingsModal);

            } else {
                const container = document.getElementById('assetDeltaContainer');
                if (container) {
                    const metricsHTML = metricsPanelVisible ? renderMetricsPanel() : '';

                    container.innerHTML = `
                        <div class="asset-delta-display">
                            <span class="asset-delta-label">${t('assetsGrowth')}:</span>
                            <span class="${totalDeltaClass}">${formattedTotalDelta}</span>
                            <span id="showHistoryIcon" title="${t('historyIcon')}">📊</span>
                            <span id="showMetricsIcon" title="${metricsPanelVisible ? t('metricsIconHide') : t('metricsIconShow')}">📈</span>
                            <span id="languageToggle" title="中文/English">🌐</span>
                            <span id="settingsToggle" title="${t('settingsIcon')}">⚙️</span>
                        </div>
                        <div class="ep-delta-extra">
                            <span>${t('avg7Days')}: ${formatSignedLargeNumber(avg7, options.unitMode)}</span>
                            <span>${t('lastRecord')}: ${lastUpdateStr}</span>
                        </div>
                        ${metricsHTML}
                    `;

                    // 重新绑定事件
                    document.getElementById('showHistoryIcon').addEventListener('click', () => showChartModal());
                    document.getElementById('settingsToggle').addEventListener('click', () => showSettingsModal());
                    document.getElementById('showMetricsIcon').addEventListener('click', toggleMetricsPanel);
                    document.getElementById('languageToggle').addEventListener('click', toggleLanguage);
                }
            }
        };

        // 切换统计指标面板
        function toggleMetricsPanel() {
            metricsPanelVisible = !metricsPanelVisible;
            setRoleBoolPref(STORAGE_KEYS.metricsPanel, roleId, metricsPanelVisible);
            updateDisplay();
        }

        // 切换视图模式
        function switchView(viewMode) {
            const options = getChartOptions();
            options.viewMode = viewMode;
            saveChartOptions(options);

            document.querySelectorAll('.view-option').forEach(option => {
                if (option.dataset.view === viewMode) {
                    option.classList.add('active');
                } else {
                    option.classList.remove('active');
                }
            });

            const summaryOptions = document.querySelector('.summary-view-options');
            const detailedOptions = document.querySelector('.detailed-view-options');

            if (viewMode === 'summary') {
                summaryOptions.classList.remove('hidden');
                detailedOptions.classList.add('hidden');
            } else {
                summaryOptions.classList.add('hidden');
                detailedOptions.classList.remove('hidden');
            }

            if (chart) {
                recreateChart();
            }
        }

        // 切换单位模式
        function switchUnit(unitMode) {
            const options = getChartOptions();
            options.unitMode = unitMode;
            saveChartOptions(options);

            document.querySelectorAll('.unit-option').forEach(option => {
                if (option.dataset.unit === unitMode) {
                    option.classList.add('active');
                } else {
                    option.classList.remove('active');
                }
            });

            // 更新显示和图表
            updateDisplay();
            if (chart) {
                chart.destroy();
                chart = null;
                initializeChart();
            }
        }

        // 初始化设置
        function initSettings() {
            const options = getChartOptions();

            // 设置视图模式
            switchView(options.viewMode);

            // 设置摘要视图选项
            document.getElementById('summaryShowCurrent').checked = options.summaryShowCurrent;
            document.getElementById('summaryShowNonCurrent').checked = options.summaryShowNonCurrent;

            // 设置详细视图选项
            document.getElementById('detailedShowEquipped').checked = options.detailedShowEquipped;
            document.getElementById('detailedShowInventory').checked = options.detailedShowInventory;
            document.getElementById('detailedShowMarketListings').checked = options.detailedShowMarketListings;
            document.getElementById('detailedShowHouse').checked = options.detailedShowHouse;
            document.getElementById('detailedShowAbility').checked = options.detailedShowAbility;

            // 设置通用选项
            document.getElementById('showTotalOption').checked = options.showTotal;

            // 设置单位选项
            switchUnit(options.unitMode);

            // 设置时间范围显示选项
            document.getElementById('showTimeRangeToggle').checked = options.showTimeRangeSettings;

            // 设置时间范围复选框
            document.getElementById('timeRange3').checked = options.visibleTimeRanges.includes(3);
            document.getElementById('timeRange7').checked = options.visibleTimeRanges.includes(7);
            document.getElementById('timeRange30').checked = options.visibleTimeRanges.includes(30);
            document.getElementById('timeRange60').checked = options.visibleTimeRanges.includes(60);
            document.getElementById('timeRange90').checked = options.visibleTimeRanges.includes(90);
            document.getElementById('timeRange180').checked = options.visibleTimeRanges.includes(180);

            // 控制时间范围选项的显示
            const timeRangeOptions = document.getElementById('timeRangeOptions');
            if (timeRangeOptions) {
                if (options.showTimeRangeSettings) {
                    timeRangeOptions.classList.remove('hidden');
                } else {
                    timeRangeOptions.classList.add('hidden');
                }
            }

            // 更新设置弹窗中的时间范围按钮
            updateSettingsTimeRangeButtons();
        }

        // 重新创建图表
        function recreateChart() {
            if (chart) {
                chart.destroy();
                chart = null;
            }
            initializeChart();
        }

        // 显示图表弹窗
        function showChartModal() {
            hideAllModals();
            currentModal = document.getElementById('deltaNetworthChartModal');
            showOverlay();
            currentModal.style.display = 'flex';

            // 显示统计指标
            const metricsContainer = document.getElementById('ep-metrics-container');
            if (metricsContainer) {
                metricsContainer.innerHTML = renderMetricsPanel();
            }

            if (!window.Chart) {
                loadChartLibrary().then(() => {
                    initializeChart();
                    updateChartTimeRangeButtons();
                });
            } else if (!chart) {
                initializeChart();
                updateChartTimeRangeButtons();
            } else {
                updateChart();
                updateChartTimeRangeButtons();
            }
        }

        // 显示设置弹窗
        function showSettingsModal() {
            hideAllModals();
            currentModal = document.getElementById('assetSettingsModal');
            showOverlay();
            currentModal.style.display = 'flex';

            // 更新时间范围按钮
            updateSettingsTimeRangeButtons();
        }

        function loadChartLibrary() {
            return new Promise((resolve) => {
                if (window.Chart) {
                    resolve();
                    return;
                }
                const script = document.createElement('script');
                script.src = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js';
                script.onload = resolve;
                document.head.appendChild(script);
            });
        }

        function initializeChart() {
            const options = getChartOptions();
            const historyData = store.getHistoryData(options.daysToShow);

            const ctx = document.getElementById('netWorthChart').getContext('2d');

            const datasets = [];

            if (options.viewMode === 'detailed') {
                datasets.push({
                    id: 'equipped',
                    label: t('equipmentValue'),
                    data: historyData.equippedNetworth,
                    borderColor: 'rgba(75, 192, 192, 1)',
                    backgroundColor: 'rgba(75, 192, 192, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowEquipped
                });
                datasets.push({
                    id: 'inventory',
                    label: t('inventoryValue'),
                    data: historyData.inventoryNetworth,
                    borderColor: 'rgba(255, 159, 64, 1)',
                    backgroundColor: 'rgba(255, 159, 64, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowInventory
                });
                datasets.push({
                    id: 'market',
                    label: t('marketValue'),
                    data: historyData.marketListingsNetworth,
                    borderColor: 'rgba(153, 102, 255, 1)',
                    backgroundColor: 'rgba(153, 102, 255, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowMarketListings
                });
                datasets.push({
                    id: 'house',
                    label: t('houseValue'),
                    data: historyData.totalHouseScore,
                    borderColor: 'rgba(255, 99, 132, 1)',
                    backgroundColor: 'rgba(255, 99, 132, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowHouse
                });
                datasets.push({
                    id: 'ability',
                    label: t('abilityValue'),
                    data: historyData.abilityScore,
                    borderColor: 'rgba(54, 162, 235, 1)',
                    backgroundColor: 'rgba(54, 162, 235, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.detailedShowAbility
                });
            } else {
                datasets.push({
                    id: 'current',
                    label: t('currentAssetsValue'),
                    data: historyData.currentAssets,
                    borderColor: 'rgba(75, 192, 192, 1)',
                    backgroundColor: 'rgba(75, 192, 192, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.summaryShowCurrent
                });
                datasets.push({
                    id: 'nonCurrent',
                    label: t('nonCurrentAssetsValue'),
                    data: historyData.nonCurrentAssets,
                    borderColor: 'rgba(255, 99, 132, 1)',
                    backgroundColor: 'rgba(255, 99, 132, 0.1)',
                    tension: 0.3,
                    fill: false,
                    hidden: !options.summaryShowNonCurrent
                });
            }

            datasets.push({
                id: 'total',
                label: t('totalAssets'),
                data: historyData.totalAssets,
                borderColor: 'rgba(54, 162, 235, 1)',
                backgroundColor: 'rgba(54, 162, 235, 0.1)',
                tension: 0.3,
                fill: false,
                hidden: !options.showTotal,
                borderWidth: 2
            });

            chart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: historyData.labels,
                    datasets: datasets
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            display: true,
                            position: 'top',
                            labels: {
                                usePointStyle: true,
                                boxWidth: 10,
                                color: '#fff'
                            }
                        },
                        tooltip: {
                            callbacks: {
                                label: (context) => {
                                    const label = context.dataset.label || '';
                                    const value = formatLargeNumber(context.raw, options.unitMode);
                                    return `${label}: ${value}`;
                                }
                            },
                            backgroundColor: 'rgba(0, 0, 0, 0.8)',
                            titleColor: '#fff',
                            bodyColor: '#fff'
                        }
                    },
                    scales: {
                        x: {
                            ticks: {
                                color: '#ccc'
                            },
                            grid: {
                                color: 'rgba(255, 255, 255, 0.1)'
                            }
                        },
                        y: {
                            ticks: {
                                color: '#ccc',
                                callback: (value) => formatLargeNumber(value, options.unitMode)
                            },
                            grid: {
                                color: 'rgba(255, 255, 255, 0.1)'
                            }
                        }
                    }
                }
            });
        }

        function updateChart() {
            const options = getChartOptions();
            const historyData = store.getHistoryData(options.daysToShow);

            chart.data.labels = historyData.labels;

            if (options.viewMode === 'detailed') {
                if (chart.data.datasets.length >= 6) {
                    chart.data.datasets[0].data = historyData.equippedNetworth;
                    chart.data.datasets[1].data = historyData.inventoryNetworth;
                    chart.data.datasets[2].data = historyData.marketListingsNetworth;
                    chart.data.datasets[3].data = historyData.totalHouseScore;
                    chart.data.datasets[4].data = historyData.abilityScore;
                }
            } else {
                if (chart.data.datasets.length >= 3) {
                    chart.data.datasets[0].data = historyData.currentAssets;
                    chart.data.datasets[1].data = historyData.nonCurrentAssets;
                }
            }

            const totalIndex = options.viewMode === 'detailed' ? 5 : 2;
            if (chart.data.datasets[totalIndex]) {
                chart.data.datasets[totalIndex].data = historyData.totalAssets;
            }

            // 更新图表配置以反映新的单位模式
            chart.options.scales.y.ticks.callback = (value) => formatLargeNumber(value, options.unitMode);
            chart.options.plugins.tooltip.callbacks.label = (context) => {
                const label = context.dataset.label || '';
                const value = formatLargeNumber(context.raw, options.unitMode);
                return `${label}: ${value}`;
            };

            chart.update();
        }

        function updateChartVisibility() {
            const options = getChartOptions();

            options.summaryShowCurrent = document.getElementById('summaryShowCurrent').checked;
            options.summaryShowNonCurrent = document.getElementById('summaryShowNonCurrent').checked;
            options.detailedShowEquipped = document.getElementById('detailedShowEquipped').checked;
            options.detailedShowInventory = document.getElementById('detailedShowInventory').checked;
            options.detailedShowMarketListings = document.getElementById('detailedShowMarketListings').checked;
            options.detailedShowHouse = document.getElementById('detailedShowHouse').checked;
            options.detailedShowAbility = document.getElementById('detailedShowAbility').checked;
            options.showTotal = document.getElementById('showTotalOption').checked;

            saveChartOptions(options);

            if (chart) {
                const historyData = store.getHistoryData(options.daysToShow);

                if (options.viewMode === 'detailed') {
                    if (chart.data.datasets.length >= 6) {
                        chart.data.datasets[0].hidden = !options.detailedShowEquipped;
                        chart.data.datasets[1].hidden = !options.detailedShowInventory;
                        chart.data.datasets[2].hidden = !options.detailedShowMarketListings;
                        chart.data.datasets[3].hidden = !options.detailedShowHouse;
                        chart.data.datasets[4].hidden = !options.detailedShowAbility;
                        chart.data.datasets[5].hidden = !options.showTotal;
                    }
                } else {
                    if (chart.data.datasets.length >= 3) {
                        chart.data.datasets[0].hidden = !options.summaryShowCurrent;
                        chart.data.datasets[1].hidden = !options.summaryShowNonCurrent;
                        chart.data.datasets[2].hidden = !options.showTotal;
                    }
                }
                chart.update();
            }
        }

        function toggleTimeRangeVisibility() {
            const show = document.getElementById('showTimeRangeToggle').checked;
            const timeRangeOptions = document.getElementById('timeRangeOptions');

            const options = getChartOptions();
            options.showTimeRangeSettings = show;
            saveChartOptions(options);

            if (show) {
                timeRangeOptions.classList.remove('hidden');
            } else {
                timeRangeOptions.classList.add('hidden');
            }
        }

        function updateTimeRangeSettings() {
            const options = getChartOptions();

            const visibleRanges = [];
            if (document.getElementById('timeRange3').checked) visibleRanges.push(3);
            if (document.getElementById('timeRange7').checked) visibleRanges.push(7);
            if (document.getElementById('timeRange30').checked) visibleRanges.push(30);
            if (document.getElementById('timeRange60').checked) visibleRanges.push(60);
            if (document.getElementById('timeRange90').checked) visibleRanges.push(90);
            if (document.getElementById('timeRange180').checked) visibleRanges.push(180);

            options.visibleTimeRanges = visibleRanges;
            saveChartOptions(options);

            updateChartTimeRangeButtons();
            updateSettingsTimeRangeButtons();
        }

        function updateChartTimeRangeButtons() {
            const timeRangeOptions = document.getElementById('timeRangeOptions');
            const options = getChartOptions();

            const titleSpan = timeRangeOptions.querySelector('span');
            timeRangeOptions.innerHTML = '';
            if (titleSpan) {
                timeRangeOptions.appendChild(titleSpan);
            }

            options.visibleTimeRanges.forEach(days => {
                const btn = document.createElement('button');
                btn.id = `btn${days}Days`;
                btn.className = 'time-range-btn';
                if (options.daysToShow === days) {
                    btn.classList.add('active');
                }
                btn.textContent = `${days}${t('days')}`;
                btn.addEventListener('click', () => updateChartTimeRange(days));
                timeRangeOptions.appendChild(btn);
            });
        }

        function updateSettingsTimeRangeButtons() {
            const buttonsContainer = document.getElementById('settingsTimeRangeButtons');
            const options = getChartOptions();

            if (!buttonsContainer) {
                console.error('settingsTimeRangeButtons container not found');
                return;
            }

            buttonsContainer.innerHTML = '';

            options.visibleTimeRanges.forEach(days => {
                const btn = document.createElement('button');
                btn.id = `settingsBtn${days}Days`;
                btn.className = 'time-range-btn';
                if (options.daysToShow === days) {
                    btn.classList.add('active');
                }
                btn.textContent = `${days}${t('days')}`;
                btn.addEventListener('click', () => {
                    updateChartTimeRange(days);
                    updateSettingsTimeRangeButtons();
                    updateChartTimeRangeButtons(); // 同时更新图表弹窗的按钮
                });
                buttonsContainer.appendChild(btn);
            });
        }

        function updateChartTimeRange(days) {
            const options = getChartOptions();
            options.daysToShow = days;
            saveChartOptions(options);

            updateChartTimeRangeButtons();
            updateSettingsTimeRangeButtons(); // 同时更新设置弹窗中的按钮

            if (chart) {
                const historyData = store.getHistoryData(days);
                chart.data.labels = historyData.labels;

                if (options.viewMode === 'detailed') {
                    if (chart.data.datasets.length >= 6) {
                        chart.data.datasets[0].data = historyData.equippedNetworth;
                        chart.data.datasets[1].data = historyData.inventoryNetworth;
                        chart.data.datasets[2].data = historyData.marketListingsNetworth;
                        chart.data.datasets[3].data = historyData.totalHouseScore;
                        chart.data.datasets[4].data = historyData.abilityScore;
                    }
                } else {
                    if (chart.data.datasets.length >= 3) {
                        chart.data.datasets[0].data = historyData.currentAssets;
                        chart.data.datasets[1].data = historyData.nonCurrentAssets;
                    }
                }

                const totalIndex = options.viewMode === 'detailed' ? 5 : 2;
                if (chart.data.datasets[totalIndex]) {
                    chart.data.datasets[totalIndex].data = historyData.totalAssets;
                }

                chart.update();
            }
        }

        // 初始更新
        updateDisplay(true);
        setInterval(() => updateDisplay(false), 10 * 60 * 1000);
    };

    /* =========================
       页面检测与执行
    ========================= */
    const checkAssetsAndRun = async () => {
        console.log('[DailyAssets] 开始检查资产数据...');

        // 等待MWITools加载并显示数据
        const mwValues = await getMWIToolsValues();
        console.log('[DailyAssets] 获取到的资产价值:', mwValues);

        const insertDom = document.getElementById('netWorthDetails');
        if (insertDom && !document.getElementById('assetDeltaContainer')) {
            window.kbd_calculateTotalNetworth?.(
                mwValues.equippedNetworth,
                mwValues.inventoryNetworth,
                mwValues.marketListingsNetworth,
                mwValues.totalHouseScore,
                mwValues.abilityScore,
                insertDom
            );
        }
    };

    // 初始检查和定时检查
    console.log('[DailyAssets] 脚本加载完成,等待页面初始化...');
    console.log('[DailyAssets] 当前语言:', GM_getValue('dailyAssetsLanguage', 'zh'));

    // 等待页面完全加载
    window.addEventListener('load', () => {
        setTimeout(checkAssetsAndRun, 5000); // 等待5秒让MWITools加载
    });

    // 如果页面已经加载完成,立即执行
    if (document.readyState === 'complete') {
        setTimeout(checkAssetsAndRun, 5000);
    }

    // 每60秒检查一次数据更新
    setInterval(checkAssetsAndRun, 60000);
})();