// static/app-enhanced.js - 修复版，正确实现Token实时更新

function updateViewportHeightVar() {
    const docEl = document.documentElement;
    const visualViewport = window.visualViewport;

    if (visualViewport) {
        const vh = visualViewport.height;
        const bottomInset = Math.max(
            0,
            (window.innerHeight || docEl.clientHeight || vh) - visualViewport.height - visualViewport.offsetTop
        );
        docEl.style.setProperty('--app-viewport', `${vh}px`);
        docEl.style.setProperty('--app-bottom-inset', `${bottomInset}px`);
    } else {
        const height = window.innerHeight || docEl.clientHeight;
        if (height) {
            docEl.style.setProperty('--app-viewport', `${height}px`);
        }
        docEl.style.setProperty('--app-bottom-inset', 'env(safe-area-inset-bottom, 0px)');
    }
}

updateViewportHeightVar();
window.addEventListener('resize', updateViewportHeightVar);
window.addEventListener('orientationchange', updateViewportHeightVar);
window.addEventListener('pageshow', updateViewportHeightVar);
if (window.visualViewport) {
    window.visualViewport.addEventListener('resize', updateViewportHeightVar);
    window.visualViewport.addEventListener('scroll', updateViewportHeightVar);
}

const SOCKET_IO_CDN_SOURCES = [
    'https://cdn.socket.io/4.7.5/socket.io.min.js',
    'https://cdn.jsdelivr.net/npm/socket.io-client@4.7.5/dist/socket.io.min.js',
    'https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.5/socket.io.min.js'
];

const ICONS = Object.freeze({
    bot: '/static/icons/bot.svg',
    book: '/static/icons/book.svg',
    brain: '/static/icons/brain.svg',
    camera: '/static/icons/camera.svg',
    check: '/static/icons/check.svg',
    checkbox: '/static/icons/checkbox.svg',
    circleAlert: '/static/icons/circle-alert.svg',
    clipboard: '/static/icons/clipboard.svg',
    clock: '/static/icons/clock.svg',
    eye: '/static/icons/eye.svg',
    file: '/static/icons/file.svg',
    flag: '/static/icons/flag.svg',
    folder: '/static/icons/folder.svg',
    folderOpen: '/static/icons/folder-open.svg',
    globe: '/static/icons/globe.svg',
    hammer: '/static/icons/hammer.svg',
    info: '/static/icons/info.svg',
    laptop: '/static/icons/laptop.svg',
    menu: '/static/icons/menu.svg',
    monitor: '/static/icons/monitor.svg',
    octagon: '/static/icons/octagon.svg',
    pencil: '/static/icons/pencil.svg',
    python: '/static/icons/python.svg',
    recycle: '/static/icons/recycle.svg',
    save: '/static/icons/save.svg',
    search: '/static/icons/search.svg',
    settings: '/static/icons/settings.svg',
    sparkles: '/static/icons/sparkles.svg',
    stickyNote: '/static/icons/sticky-note.svg',
    terminal: '/static/icons/terminal.svg',
    trash: '/static/icons/trash.svg',
    triangleAlert: '/static/icons/triangle-alert.svg',
    user: '/static/icons/user.svg',
    wrench: '/static/icons/wrench.svg',
    x: '/static/icons/x.svg',
    zap: '/static/icons/zap.svg'
});

const TOOL_ICON_MAP = Object.freeze({
    append_to_file: 'pencil',
    close_sub_agent: 'octagon',
    create_file: 'file',
    create_folder: 'folder',
    create_sub_agent: 'bot',
    delete_file: 'trash',
    extract_webpage: 'globe',
    focus_file: 'eye',
    modify_file: 'pencil',
    ocr_image: 'camera',
    read_file: 'book',
    rename_file: 'pencil',
    run_command: 'terminal',
    run_python: 'python',
    save_webpage: 'save',
    sleep: 'clock',
    todo_create: 'stickyNote',
    todo_finish: 'flag',
    todo_finish_confirm: 'circleAlert',
    todo_update_task: 'check',
    terminal_input: 'terminal',
    terminal_reset: 'recycle',
    terminal_session: 'monitor',
    terminal_snapshot: 'clipboard',
    unfocus_file: 'eye',
    update_memory: 'brain',
    wait_sub_agent: 'clock',
    web_search: 'search',
    trigger_easter_egg: 'sparkles'
});

const TOOL_CATEGORY_ICON_MAP = Object.freeze({
    network: 'globe',
    file_edit: 'pencil',
    read_focus: 'eye',
    terminal_realtime: 'monitor',
    terminal_command: 'terminal',
    memory: 'brain',
    todo: 'stickyNote',
    sub_agent: 'bot',
    easter_egg: 'sparkles'
});

function injectScriptSequentially(urls, onSuccess, onFailure) {
    let index = 0;
    const tryLoad = () => {
        if (index >= urls.length) {
            onFailure();
            return;
        }
        const url = urls[index];
        const script = document.createElement('script');
        script.src = url;
        script.async = false;
        script.onload = () => {
            if (typeof io !== 'undefined') {
                console.log(`Socket.IO 已从 ${url} 加载`);
                onSuccess();
            } else {
                index += 1;
                tryLoad();
            }
        };
        script.onerror = () => {
            console.warn(`无法从 ${url} 加载 Socket.IO，尝试下一个源`);
            index += 1;
            tryLoad();
        };
        document.head.appendChild(script);
    };
    tryLoad();
}

async function ensureSocketIOLoaded() {
    if (typeof io !== 'undefined') {
        return true;
    }
    return await new Promise((resolve) => {
        injectScriptSequentially(
            SOCKET_IO_CDN_SOURCES,
            () => resolve(true),
            () => resolve(false)
        );
    });
}

async function bootstrapApp() {
    // 检查必要的库是否加载
    if (typeof Vue === 'undefined') {
        console.error('错误：Vue.js 未加载');
        document.body.innerHTML = '<h1 style="color:red;">Vue.js 加载失败，请刷新页面</h1>';
        return;
    }
    
    if (typeof io === 'undefined') {
        const loaded = await ensureSocketIOLoaded();
        if (!loaded || typeof io === 'undefined') {
            console.error('错误：Socket.IO 未加载');
            document.body.innerHTML = '<h1 style="color:red;">Socket.IO 加载失败，请检查网络后刷新页面</h1>';
            return;
        }
    }
    
    console.log('所有依赖加载成功，初始化Vue应用...');
    
    const { createApp } = Vue;
    
    const app = createApp({
        data() {
            return {
                // 连接状态
                isConnected: false,
                socket: null,
                
                // 系统信息
                projectPath: '',
                agentVersion: '',
                thinkingMode: true, // true=思考模式, false=快速模式
                
                // 消息相关
                messages: [],
                inputMessage: '',
                
                // 当前消息索引
                currentMessageIndex: -1,
                streamingMessage: false,
                
                // 停止功能状态
                stopRequested: false,

                // 路由相关
                initialRouteResolved: false,
                
                // 文件相关
                fileTree: [],
                focusedFiles: {},
                expandedFolders: {},

                // 展开状态管理
                expandedBlocks: new Set(),
                
                // 滚动控制
                userScrolling: false,
                autoScrollEnabled: true,
                
                // 面板宽度控制
                leftWidth: 350,
                rightWidth: 420,
                rightCollapsed: true,
                isResizing: false,
                resizingPanel: null,
                minPanelWidth: 350,
                maxPanelWidth: 600,
                
                // 工具状态跟踪
                preparingTools: new Map(),
                activeTools: new Map(),
                toolActionIndex: new Map(),
                toolActionIndex: new Map(),
                toolStacks: new Map(),
                
                // ==========================================
                // 对话管理相关状态
                // ==========================================
                
                // 对话历史侧边栏
                sidebarCollapsed: true,  // 默认收起对话侧边栏
                panelMode: 'files', // files | todo | subAgents
                panelMenuOpen: false,
                subAgents: [],
                subAgentPollTimer: null,
                conversations: [],
                conversationsLoading: false,
                hasMoreConversations: false,
                loadingMoreConversations: false,
                currentConversationId: null,
                currentConversationTitle: '当前对话',
                personalPageVisible: false,
                personalizationLoading: false,
                personalizationSaving: false,
                personalizationLoaded: false,
                personalizationStatus: '',
                personalizationError: '',
                personalizationMaxConsiderations: 10,
                personalizationToggleUpdating: false,
                overlayPressActive: false,
                personalForm: {
                    enabled: false,
                    self_identify: '',
                    user_name: '',
                    profession: '',
                    tone: '',
                    considerations: []
                },
                tonePresets: ['健谈', '幽默', '直言不讳', '鼓励性', '诗意', '企业商务', '打破常规', '同理心'],
                newConsideration: '',
                draggedConsiderationIndex: null,
                
                // 搜索功能
                searchQuery: '',
                searchTimer: null,
                
                // 分页
                conversationsOffset: 0,
                conversationsLimit: 20,
                
                // ==========================================
                // Token统计相关状态（修复版）
                // ==========================================
                
                // 当前上下文Token（动态计算，包含完整prompt）
                currentContextTokens: 0,
                
                // 累计Token统计（从对话文件和WebSocket获取）
                currentConversationTokens: {
                    // 累计统计字段
                    cumulative_input_tokens: 0,
                    cumulative_output_tokens: 0,
                    cumulative_total_tokens: 0
                    
                },
                // Token面板折叠状态
                tokenPanelCollapsed: true,
                projectStorage: {
                    used_bytes: 0,
                    limit_bytes: null,
                    limit_label: '',
                    usage_percent: null
                },
                containerStatus: null,
                containerStatsTimer: null,
                projectStorageTimer: null,
                lastContainerSample: null,
                containerNetRate: {
                    down_bps: null,
                    up_bps: null
                },
                usageQuota: {
                    fast: { count: 0, limit: 0, reset_at: null, window_start: null },
                    thinking: { count: 0, limit: 0, reset_at: null, window_start: null },
                    search: { count: 0, limit: 0, reset_at: null, window_start: null },
                    role: 'user'
                },
                usageQuotaTimer: null,
                quotaToast: null,
                quotaToastTimer: null,
                toastQueue: [],
                nextToastId: 1,
                pendingConfirmResolver: null,
                confirmDialog: null,

                // 对话压缩状态
                compressing: false,

                // 输入/快捷菜单状态
                settingsOpen: false,
                quickMenuOpen: false,
                inputLineCount: 1,
                inputIsMultiline: false,
                inputIsFocused: false,
                // 思考块滚动锁
                thinkingScrollLocks: new Map(),

                // 工具控制菜单
                toolMenuOpen: false,
                toolSettings: [],
                toolSettingsLoading: false,

                // 文件上传状态
                uploading: false,

                // TODO 列表
                todoList: null,
                icons: ICONS,
                toolCategoryIcons: TOOL_CATEGORY_ICON_MAP,
                easterEgg: {
                    active: false,
                    effect: null,
                    payload: null,
                    instance: null,
                    cleanupTimer: null,
                    destroying: false,
                    destroyPromise: null
                },

                // 右键菜单相关
                contextMenu: {
                    visible: false,
                    x: 0,
                    y: 0,
                    node: null
                },
                onDocumentClick: null,
                onWindowScroll: null,
                onKeydownListener: null
            }
        },
        
        async mounted() {
            console.log('Vue应用已挂载');
            if (window.ensureCsrfToken) {
                window.ensureCsrfToken().catch((err) => {
                    console.warn('CSRF token 初始化失败:', err);
                });
            }
            await this.bootstrapRoute();
            await this.initSocket();
            this.initScrollListener();
            
            // 延迟加载初始数据
            setTimeout(() => {
                this.loadInitialData();
            }, 500);
            
            document.addEventListener('click', this.handleClickOutsideQuickMenu);
            document.addEventListener('click', this.handleClickOutsidePanelMenu);
            window.addEventListener('popstate', this.handlePopState);

            this.onDocumentClick = (event) => {
                if (!this.contextMenu.visible) {
                    return;
                }
                if (event.target && event.target.closest && event.target.closest('.context-menu')) {
                    return;
                }
                this.hideContextMenu();
            };

            this.onWindowScroll = () => {
                if (this.contextMenu.visible) {
                    this.hideContextMenu();
                }
            };

            this.onKeydownListener = (event) => {
                if (event.key === 'Escape' && this.contextMenu.visible) {
                    this.hideContextMenu();
                }
            };

            document.addEventListener('click', this.onDocumentClick);
            window.addEventListener('scroll', this.onWindowScroll, true);
            document.addEventListener('keydown', this.onKeydownListener);

            this.fetchSubAgents();
            this.subAgentPollTimer = setInterval(() => {
                if (this.panelMode === 'subAgents') {
                    this.fetchSubAgents();
                }
            }, 5000);

            this.$nextTick(() => {
                this.autoResizeInput();
            });
            this.startContainerStatsPolling();
            this.startProjectStoragePolling();
            this.startUsageQuotaPolling();
        },

        beforeUnmount() {
            document.removeEventListener('click', this.handleClickOutsideQuickMenu);
            document.removeEventListener('click', this.handleClickOutsidePanelMenu);
            window.removeEventListener('popstate', this.handlePopState);
            if (this.onDocumentClick) {
                document.removeEventListener('click', this.onDocumentClick);
                this.onDocumentClick = null;
            }
            if (this.onWindowScroll) {
                window.removeEventListener('scroll', this.onWindowScroll, true);
                this.onWindowScroll = null;
            }
            if (this.onKeydownListener) {
                document.removeEventListener('keydown', this.onKeydownListener);
                this.onKeydownListener = null;
            }
            if (this.subAgentPollTimer) {
                clearInterval(this.subAgentPollTimer);
                this.subAgentPollTimer = null;
            }
            this.stopContainerStatsPolling();
            this.stopProjectStoragePolling();
            this.stopUsageQuotaPolling();
            if (this.quotaToastTimer) {
                clearTimeout(this.quotaToastTimer);
                this.quotaToastTimer = null;
            }
            const cleanup = this.destroyEasterEggEffect(true);
            if (cleanup && typeof cleanup.catch === 'function') {
                cleanup.catch(() => {});
            }
        },

        watch: {
            inputMessage() {
                this.autoResizeInput();
            }
        },
        
        methods: {
            iconStyle(iconKey, size) {
                const iconPath = this.icons ? this.icons[iconKey] : null;
                if (!iconPath) {
                    return {};
                }
                const style = { '--icon-src': `url(${iconPath})` };
                if (size) {
                    style['--icon-size'] = size;
                }
                return style;
            },

            toolCategoryIcon(categoryId) {
                return this.toolCategoryIcons[categoryId] || 'settings';
            },

            openGuiFileManager() {
                window.open('/file-manager', '_blank');
            },

            findMessageByAction(action) {
                if (!action) {
                    return null;
                }
                for (const message of this.messages) {
                    if (!message.actions) {
                        continue;
                    }
                    if (message.actions.includes(action)) {
                        return message;
                    }
                }
                return null;
            },

            async bootstrapRoute() {
                const path = window.location.pathname.replace(/^\/+/, '');
                if (!path || path === 'new') {
                    this.currentConversationId = null;
                    this.currentConversationTitle = '新对话';
                    this.initialRouteResolved = true;
                    return;
                }

                const convId = path.startsWith('conv_') ? path : `conv_${path}`;
                try {
                    const resp = await fetch(`/api/conversations/${convId}/load`, { method: 'PUT' });
                    const result = await resp.json();
                    if (result.success) {
                        this.currentConversationId = convId;
                        this.currentConversationTitle = result.title || '对话';
                        history.replaceState({ conversationId: convId }, '', `/${this.stripConversationPrefix(convId)}`);
                    } else {
                        history.replaceState({}, '', '/new');
                        this.currentConversationId = null;
                        this.currentConversationTitle = '新对话';
                    }
                } catch (error) {
                    console.warn('初始化路由失败:', error);
                    history.replaceState({}, '', '/new');
                    this.currentConversationId = null;
                    this.currentConversationTitle = '新对话';
                } finally {
                    this.initialRouteResolved = true;
                }
            },

            handlePopState(event) {
                const state = event.state || {};
                const convId = state.conversationId;
                if (!convId) {
                    this.currentConversationId = null;
                    this.currentConversationTitle = '新对话';
                    this.messages = [];
                    this.resetAllStates();
                    this.resetTokenStatistics();
                    return;
                }
                this.loadConversation(convId);
            },

            stripConversationPrefix(conversationId) {
                if (!conversationId) return '';
                return conversationId.startsWith('conv_') ? conversationId.slice(5) : conversationId;
            },

            showContextMenu(payload) {
                if (!payload || !payload.node) {
                    return;
                }
                const { node, event } = payload;
                console.log('context menu', node.path, node.type);
                if (event && typeof event.preventDefault === 'function') {
                    event.preventDefault();
                }
                if (event && typeof event.stopPropagation === 'function') {
                    event.stopPropagation();
                }
                if (!node.path && node.path !== '') {
                    this.hideContextMenu();
                    return;
                }
                if (node.type !== 'file' && node.type !== 'folder') {
                    this.hideContextMenu();
                    return;
                }

                this.hideContextMenu();

                let x = (event && event.clientX) || 0;
                let y = (event && event.clientY) || 0;
                const menuWidth = 200;
                const menuHeight = 50;
                const viewportWidth = window.innerWidth;
                const viewportHeight = window.innerHeight;

                if (x + menuWidth > viewportWidth) {
                    x = viewportWidth - menuWidth - 8;
                }
                if (y + menuHeight > viewportHeight) {
                    y = viewportHeight - menuHeight - 8;
                }

                this.contextMenu.visible = true;
                this.contextMenu.x = Math.max(8, x);
                this.contextMenu.y = Math.max(8, y);
                this.contextMenu.node = node;
            },

            hideContextMenu() {
                if (!this.contextMenu.visible) {
                    return;
                }
                this.contextMenu.visible = false;
                this.contextMenu.node = null;
            },

            async downloadFile(path) {
                if (!path) {
                    this.hideContextMenu();
                    return;
                }
                const url = `/api/download/file?path=${encodeURIComponent(path)}`;
                const name = path.split('/').pop() || 'file';
                await this.downloadResource(url, name);
            },

            async downloadFolder(path) {
                if (!path) {
                    this.hideContextMenu();
                    return;
                }
                const url = `/api/download/folder?path=${encodeURIComponent(path)}`;
                const segments = path.split('/').filter(Boolean);
                const folderName = segments.length ? segments.pop() : 'folder';
                await this.downloadResource(url, `${folderName}.zip`);
            },

            async downloadResource(url, filename) {
                try {
                    const response = await fetch(url);
                    if (!response.ok) {
                        let message = response.statusText;
                        try {
                            const errorData = await response.json();
                            message = errorData.error || errorData.message || message;
                        } catch (err) {
                            message = await response.text();
                        }
                        this.pushToast({
                            title: '下载失败',
                            message: message || '无法完成下载',
                            type: 'error'
                        });
                        return;
                    }

                    const blob = await response.blob();
                    const downloadName = filename || 'download';
                    const link = document.createElement('a');
                    const href = URL.createObjectURL(blob);
                    link.href = href;
                    link.download = downloadName;
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    URL.revokeObjectURL(href);
                } catch (error) {
                    console.error('下载失败:', error);
                    this.pushToast({
                        title: '下载失败',
                        message: error.message || String(error),
                        type: 'error'
                    });
                } finally {
                    this.hideContextMenu();
                }
            },

            initScrollListener() {
                const messagesArea = this.$refs.messagesArea;
                if (!messagesArea) {
                    console.warn('消息区域未找到');
                    return;
                }
                
                let isProgrammaticScroll = false;
                const bottomThreshold = 12;
                
                this._setScrollingFlag = (flag) => {
                    isProgrammaticScroll = !!flag;
                };
                
                messagesArea.addEventListener('scroll', () => {
                    if (isProgrammaticScroll) {
                        return;
                    }
                    
                    const scrollTop = messagesArea.scrollTop;
                    const scrollHeight = messagesArea.scrollHeight;
                    const clientHeight = messagesArea.clientHeight;
                    const isAtBottom = scrollHeight - scrollTop - clientHeight < bottomThreshold;
                    
                    if (isAtBottom) {
                        this.userScrolling = false;
                        this.autoScrollEnabled = true;
                    } else {
                        this.userScrolling = true;
                        this.autoScrollEnabled = false;
                    }
                });
            },
            
            async initSocket() {
                try {
                    console.log('初始化WebSocket连接...');
                    
                    const usePollingOnly = window.location.hostname !== 'localhost' &&
                        window.location.hostname !== '127.0.0.1';
                    
                    const socketOptions = usePollingOnly ? {
                        transports: ['polling'],
                        upgrade: false,
                        autoConnect: false
                    } : {
                        transports: ['websocket', 'polling'],
                        autoConnect: false
                    };
                    
                    this.socket = io('/', socketOptions);

                    const assignSocketToken = async () => {
                        if (typeof window.requestSocketToken !== 'function') {
                            console.warn('缺少 requestSocketToken()，无法获取实时连接凭证');
                            return false;
                        }
                        try {
                            const freshToken = await window.requestSocketToken();
                            this.socket.auth = { socket_token: freshToken };
                            return true;
                        } catch (error) {
                            console.error('获取 WebSocket token 失败:', error);
                            return false;
                        }
                    };

                    if (this.socket.io && typeof this.socket.io.on === 'function') {
                        this.socket.io.on('reconnect_attempt', async () => {
                            await assignSocketToken();
                        });
                    }
                    
                    // 连接事件
                    this.socket.on('connect', () => {
                        this.isConnected = true;
                        console.log('WebSocket已连接');
                        // 连接时重置所有状态
                        this.resetAllStates();
                    });
                    
                    this.socket.on('disconnect', () => {
                        this.isConnected = false;
                        console.log('WebSocket已断开');
                        // 断线时也重置状态，防止状态混乱
                        this.resetAllStates();
                    });
                    
                    this.socket.on('connect_error', (error) => {
                        console.error('WebSocket连接错误:', error.message);
                    });

                    this.socket.on('quota_update', (data) => {
                        if (data && data.quotas) {
                            this.usageQuota = this.normalizeUsageQuota({ quotas: data.quotas });
                        } else {
                            this.fetchUsageQuota();
                        }
                    });

                    this.socket.on('quota_notice', (data) => {
                        this.showQuotaToast(data || {});
                        this.fetchUsageQuota();
                    });

                    this.socket.on('quota_exceeded', (data) => {
                        this.showQuotaToast(data || {});
                        this.fetchUsageQuota();
                    });
                    
                    this.socket.on('reconnect_attempt', async () => {
                        await assignSocketToken();
                    });

                    const ready = await assignSocketToken();
                    if (!ready) {
                        console.error('无法获取实时连接凭证，WebSocket 初始化中止。');
                        return;
                    }
                    this.socket.connect();
                    
                    // ==========================================
                    // Token统计WebSocket事件处理（修复版）
                    // ==========================================
                    
                    this.socket.on('token_update', (data) => {
                        console.log('收到token更新事件:', data);
                        
                        // 只处理当前对话的token更新
                        if (data.conversation_id === this.currentConversationId) {
                            // 更新累计统计（使用后端提供的准确字段名）
                            this.currentConversationTokens.cumulative_input_tokens = data.cumulative_input_tokens || 0;
                            this.currentConversationTokens.cumulative_output_tokens = data.cumulative_output_tokens || 0;
                            this.currentConversationTokens.cumulative_total_tokens = data.cumulative_total_tokens || 0;
                            
                            console.log(`累计Token统计更新: 输入=${data.cumulative_input_tokens}, 输出=${data.cumulative_output_tokens}, 总计=${data.cumulative_total_tokens}`);
                            
                            // 同时更新当前上下文Token（关键修复）
                            this.updateCurrentContextTokens();
                            
                            this.$forceUpdate();
                        }
                    });
                    
            this.socket.on('todo_updated', (data) => {
                console.log('收到todo更新事件:', data);
                if (data && data.conversation_id) {
                    this.currentConversationId = data.conversation_id;
                }
                this.todoList = data && data.todo_list ? data.todo_list : null;
            });
                    
                    // 系统就绪
                    this.socket.on('system_ready', (data) => {
                        this.projectPath = data.project_path || '';
                        this.agentVersion = data.version || this.agentVersion;
                        this.thinkingMode = !!data.thinking_mode;
                        console.log('系统就绪:', data);
                        
                        // 系统就绪后立即加载对话列表
                        this.loadConversationsList();
                    });

                    this.socket.on('tool_settings_updated', (data) => {
                        console.log('收到工具设置更新:', data);
                        if (data && Array.isArray(data.categories)) {
                            this.applyToolSettingsSnapshot(data.categories);
                        }
                    });
                    
                    // ==========================================
                    // 对话管理相关Socket事件
                    // ==========================================
                    
                    // 监听对话变更事件
                    this.socket.on('conversation_changed', (data) => {
                        console.log('对话已切换:', data);
                        this.currentConversationId = data.conversation_id;
                        this.currentConversationTitle = data.title || '';
                        this.promoteConversationToTop(data.conversation_id);
                        
                        if (data.cleared) {
                            // 对话被清空
                            this.messages = [];
                            this.currentConversationId = null;
                            this.currentConversationTitle = '';
                            // 重置Token统计
                            this.resetTokenStatistics();
                            history.replaceState({}, '', '/new');
                        }
                        
                        // 刷新对话列表
                        this.loadConversationsList();
                        this.fetchTodoList();
                        this.fetchSubAgents();
                    });

                    this.socket.on('conversation_resolved', (data) => {
                        if (!data || !data.conversation_id) {
                            return;
                        }
                        const convId = data.conversation_id;
                        this.currentConversationId = convId;
                        if (data.title) {
                            this.currentConversationTitle = data.title;
                        }
                        this.promoteConversationToTop(convId);
                        const pathFragment = this.stripConversationPrefix(convId);
                        const currentPath = window.location.pathname.replace(/^\/+/, '');
                        if (data.created) {
                            history.pushState({ conversationId: convId }, '', `/${pathFragment}`);
                        } else if (currentPath !== pathFragment) {
                            history.replaceState({ conversationId: convId }, '', `/${pathFragment}`);
                        }
                    });
                    
                    // 监听对话加载事件
                    this.socket.on('conversation_loaded', (data) => {
                        console.log('对话已加载:', data);
                        if (data.clear_ui) {
                            // 清理当前UI状态，准备显示历史内容
                            this.resetAllStates();
                        }
                        
                        // 延迟获取并显示历史对话内容
                        setTimeout(() => {
                            this.fetchAndDisplayHistory();
                        }, 300);
                        
                        // 延迟获取Token统计（累计+当前上下文）
                        setTimeout(() => {
                            this.fetchConversationTokenStatistics();
                            this.updateCurrentContextTokens();
                            this.fetchTodoList();
                        }, 500);
                    });
                    
                    // 监听对话列表更新事件
                    this.socket.on('conversation_list_update', (data) => {
                        console.log('对话列表已更新:', data);
                        // 刷新对话列表
                        this.loadConversationsList();
                    });
                    
                    // 监听状态更新事件
                    this.socket.on('status_update', (status) => {
                        this.applyStatusSnapshot(status);
                        if (status.conversation && status.conversation.current_id) {
                            this.currentConversationId = status.conversation.current_id;
                        }
                        if (typeof status.thinking_mode !== 'undefined') {
                            this.thinkingMode = !!status.thinking_mode;
                        }
                    });
                    
                    // AI消息开始
                    this.socket.on('ai_message_start', () => {
                        console.log('AI消息开始');
                        this.cleanupStaleToolActions();
                    const newMessage = {
                        role: 'assistant',
                        actions: [],
                        streamingThinking: '',
                        streamingText: '',
                        currentStreamingType: null,
                        activeThinkingId: null
                    };
                        this.messages.push(newMessage);
                        this.currentMessageIndex = this.messages.length - 1;
                        this.streamingMessage = true;
                        this.stopRequested = false;
                        this.autoScrollEnabled = true;
                        this.scrollToBottom();
                    });
                    
                    // 思考流开始
                    this.socket.on('thinking_start', () => {
                        console.log('思考开始');
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            msg.streamingThinking = '';
                            msg.currentStreamingType = 'thinking';
                            
                            const action = {
                                id: Date.now() + Math.random(),
                                type: 'thinking',
                                content: '',
                                streaming: true,
                                timestamp: Date.now()
                            };
                            msg.actions.push(action);
                            
                            const blockId = action.blockId || `thinking-${Date.now()}-${Math.random().toString(36).slice(2)}`;
                            action.blockId = blockId;
                            msg.activeThinkingId = action.id;
                            this.expandedBlocks.add(blockId);
                            // 开始思考时自动锁定滚动到底部
                            this.autoScrollEnabled = true;
                            this.userScrolling = false;
                            this.scrollToBottom();
                            this.thinkingScrollLocks.set(blockId, true);
                            this.$forceUpdate();
                        }
                    });
                    
                    // 思考内容块
                    this.socket.on('thinking_chunk', (data) => {
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            msg.streamingThinking += data.content;
                            
                            const thinkingAction = this.getActiveThinkingAction(msg);
                            if (thinkingAction) {
                                thinkingAction.content += data.content;
                            }
                            this.$forceUpdate();
                            this.$nextTick(() => {
                                if (thinkingAction && thinkingAction.blockId) {
                                    this.scrollThinkingToBottom(thinkingAction.blockId);
                                }
                                this.conditionalScrollToBottom();
                            });
                        }
                    });
                    
                    // 思考结束
                    this.socket.on('thinking_end', (data) => {
                        console.log('思考结束');
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            const thinkingAction = this.getActiveThinkingAction(msg);
                            if (thinkingAction) {
                                thinkingAction.streaming = false;
                                thinkingAction.content = data.full_content;
                                const blockId = thinkingAction.blockId || `thinking-${Date.now()}-${Math.random().toString(36).slice(2)}`;
                                if (!thinkingAction.blockId) {
                                    thinkingAction.blockId = blockId;
                                }
                                if (blockId) {
                                    setTimeout(() => {
                                        this.expandedBlocks.delete(blockId);
                                        this.thinkingScrollLocks.delete(blockId);
                                        this.$forceUpdate();
                                    }, 1000);
                                    this.$nextTick(() => this.scrollThinkingToBottom(blockId));
                                }
                            }
                            msg.streamingThinking = '';
                            msg.currentStreamingType = null;
                            msg.activeThinkingId = null;
                            this.$forceUpdate();
                        }
                    });
                    
                    // 文本流开始
                    this.socket.on('text_start', () => {
                        console.log('文本开始');
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            msg.streamingText = '';
                            msg.currentStreamingType = 'text';
                            
                            const action = {
                                id: Date.now() + Math.random(),
                                type: 'text',
                                content: '',
                                streaming: true,
                                timestamp: Date.now()
                            };
                            msg.actions.push(action);
                            this.$forceUpdate();
                        }
                    });
                    
                    // 文本内容块
                    this.socket.on('text_chunk', (data) => {
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            msg.streamingText += data.content;
                            
                            const lastAction = msg.actions[msg.actions.length - 1];
                            if (lastAction && lastAction.type === 'text') {
                                lastAction.content += data.content;
                            }
                            this.$forceUpdate();
                            this.conditionalScrollToBottom();
                            
                            // 实时渲染LaTeX
                            this.renderLatexInRealtime();
                        }
                    });
                    
                    // 文本结束
                    this.socket.on('text_end', (data) => {
                        console.log('文本结束');
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            
                            // 查找当前流式文本的action
                            for (let i = msg.actions.length - 1; i >= 0; i--) {
                                const action = msg.actions[i];
                                if (action.type === 'text' && action.streaming) {
                                    action.streaming = false;
                                    action.content = data.full_content;
                                    console.log('文本action已更新为完成状态');
                                    break;
                                }
                            }
                            
                            msg.streamingText = '';
                            msg.currentStreamingType = null;
                            this.$forceUpdate();
                        }
                    });
                    
                    // 工具提示事件（可选）
                    this.socket.on('tool_hint', (data) => {
                        console.log('工具提示:', data.name);
                        // 可以在这里添加提示UI
                    });
                    
                    // 工具准备中事件 - 实时显示
                    this.socket.on('tool_preparing', (data) => {
                        console.log('工具准备中:', data.name);
                        const msg = this.ensureAssistantMessage();
                        if (!msg) {
                            return;
                        }
                        const action = {
                            id: data.id,
                            type: 'tool',
                            tool: {
                                id: data.id,
                                name: data.name,
                                arguments: {},
                                argumentSnapshot: null,
                                argumentLabel: '',
                                status: 'preparing',
                                result: null,
                                message: data.message || `准备调用 ${data.name}...`
                            },
                            timestamp: Date.now()
                        };
                        msg.actions.push(action);
                        this.preparingTools.set(data.id, action);
                        this.registerToolAction(action, data.id);
                        this.trackToolAction(data.name, action);
                        this.$forceUpdate();
                        this.conditionalScrollToBottom();
                    });
                    
                    // 工具状态更新事件 - 实时显示详细状态
                    this.socket.on('tool_status', (data) => {
                        console.log('工具状态:', data);
                        const target = this.findToolAction(data.id, data.preparing_id, data.execution_id);
                        if (target) {
                            target.tool.statusDetail = data.detail;
                            target.tool.statusType = data.status;
                            this.$forceUpdate();
                            return;
                        }
                        const fallbackAction = this.getLatestActiveToolAction(data.tool);
                        if (fallbackAction) {
                            fallbackAction.tool.statusDetail = data.detail;
                            fallbackAction.tool.statusType = data.status;
                            this.registerToolAction(fallbackAction, data.execution_id || data.id || data.preparing_id);
                            this.$forceUpdate();
                        }
                    });
                    
                    // 工具开始（从准备转为执行）
                    this.socket.on('tool_start', (data) => {
                        console.log('工具开始执行:', data.name);
                        let action = null;
                        if (data.preparing_id && this.preparingTools.has(data.preparing_id)) {
                            action = this.preparingTools.get(data.preparing_id);
                            this.preparingTools.delete(data.preparing_id);
                        } else {
                            action = this.findToolAction(data.id, data.preparing_id, data.execution_id);
                        }
                        if (!action) {
                            const msg = this.ensureAssistantMessage();
                            if (!msg) {
                                return;
                            }
                            action = {
                                id: data.id,
                                type: 'tool',
                                    tool: {
                                        id: data.id,
                                        name: data.name,
                                        arguments: {},
                                        argumentSnapshot: null,
                                        argumentLabel: '',
                                        status: 'running',
                                        result: null
                                    },
                                    timestamp: Date.now()
                                };
                            msg.actions.push(action);
                        }
                        action.tool.status = 'running';
                        action.tool.arguments = data.arguments;
                        action.tool.argumentSnapshot = this.cloneToolArguments(data.arguments);
                        action.tool.argumentLabel = this.buildToolLabel(action.tool.argumentSnapshot);
                        action.tool.message = null;
                        action.tool.id = data.id;
                        action.tool.executionId = data.id;
                        this.registerToolAction(action, data.id);
                        this.trackToolAction(data.name, action);
                        this.$forceUpdate();
                        this.conditionalScrollToBottom();
                    });
                    
                    // 更新action（工具完成）
                    this.socket.on('update_action', (data) => {
                        console.log('更新action:', data.id, 'status:', data.status);
                        let targetAction = this.findToolAction(data.id, data.preparing_id, data.execution_id);
                        if (!targetAction && data.preparing_id && this.preparingTools.has(data.preparing_id)) {
                            targetAction = this.preparingTools.get(data.preparing_id);
                        }
                        if (!targetAction) {
                            outer: for (const message of this.messages) {
                                if (!message.actions) continue;
                                for (const action of message.actions) {
                                    if (action.type !== 'tool') continue;
                                    const matchByExecution = action.tool.executionId && action.tool.executionId === data.id;
                                    const matchByToolId = action.tool.id === data.id;
                                    const matchByPreparingId = action.id === data.preparing_id;
                                    if (matchByExecution || matchByToolId || matchByPreparingId) {
                                        targetAction = action;
                                        break outer;
                                    }
                                }
                            }
                        }
                        if (targetAction) {
                            if (data.status) {
                                targetAction.tool.status = data.status;
                            }
                            if (data.result !== undefined) {
                                targetAction.tool.result = data.result;
                            }
                            if (targetAction.tool && targetAction.tool.name === 'trigger_easter_egg' && data.result !== undefined) {
                                const eggPromise = this.handleEasterEggPayload(data.result);
                                if (eggPromise && typeof eggPromise.catch === 'function') {
                                    eggPromise.catch((error) => {
                                        console.warn('彩蛋处理异常:', error);
                                    });
                                }
                            }
                            if (data.message !== undefined) {
                                targetAction.tool.message = data.message;
                            }
                            if (data.awaiting_content) {
                                targetAction.tool.awaiting_content = true;
                            } else if (data.status === 'completed') {
                                targetAction.tool.awaiting_content = false;
                            }
                            if (!targetAction.tool.executionId && (data.execution_id || data.id)) {
                                targetAction.tool.executionId = data.execution_id || data.id;
                            }
                            if (data.arguments) {
                                targetAction.tool.arguments = data.arguments;
                                targetAction.tool.argumentSnapshot = this.cloneToolArguments(data.arguments);
                                targetAction.tool.argumentLabel = this.buildToolLabel(targetAction.tool.argumentSnapshot);
                            }
                            this.registerToolAction(targetAction, data.execution_id || data.id);
                            if (data.status && ["completed", "failed", "error"].includes(data.status) && !data.awaiting_content) {
                                this.unregisterToolAction(targetAction);
                                if (data.id) {
                                    this.preparingTools.delete(data.id);
                                }
                                if (data.preparing_id) {
                                    this.preparingTools.delete(data.preparing_id);
                                }
                            }
                            this.$forceUpdate();
                            this.conditionalScrollToBottom();
                        }
                        
                        // 关键修复：每个工具完成后都更新当前上下文Token
                    if (data.status === 'completed') {
                        setTimeout(() => {
                            this.updateCurrentContextTokens();
                        }, 500);
                    }
                });
                    
                    this.socket.on('append_payload', (data) => {
                        console.log('收到append_payload事件:', data);
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            const action = {
                                id: `append-payload-${Date.now()}-${Math.random()}`,
                                type: 'append_payload',
                                append: {
                                    path: data.path || '未知文件',
                                    forced: !!data.forced,
                                    success: data.success === undefined ? true : !!data.success,
                                    lines: data.lines ?? null,
                                    bytes: data.bytes ?? null
                                },
                                timestamp: Date.now()
                            };
                            msg.actions.push(action);
                            this.$forceUpdate();
                            this.conditionalScrollToBottom();
                        }
                    });
                    
                    this.socket.on('modify_payload', (data) => {
                        console.log('收到modify_payload事件:', data);
                        if (this.currentMessageIndex >= 0) {
                            const msg = this.messages[this.currentMessageIndex];
                            const action = {
                                id: `modify-payload-${Date.now()}-${Math.random()}`,
                                type: 'modify_payload',
                                modify: {
                                    path: data.path || '未知文件',
                                    total: data.total ?? null,
                                    completed: data.completed || [],
                                    failed: data.failed || [],
                                    forced: !!data.forced
                                },
                                timestamp: Date.now()
                            };
                            msg.actions.push(action);
                            this.$forceUpdate();
                            this.conditionalScrollToBottom();
                        }
                    });
                    
                    // 停止请求确认
                    this.socket.on('stop_requested', (data) => {
                        console.log('停止请求已接收:', data.message);
                        // 可以显示提示信息
                    });
                    
                    // 任务停止
                    this.socket.on('task_stopped', (data) => {
                        console.log('任务已停止:', data.message);
                        this.resetAllStates();
                    });
                    
                    // 任务完成（重点：更新Token统计）
                    this.socket.on('task_complete', (data) => {
                        console.log('任务完成', data);
                        this.resetAllStates();
                        
                        // 任务完成后立即更新Token统计（关键修复）
                        if (this.currentConversationId) {
                            this.updateCurrentContextTokens();
                            this.fetchConversationTokenStatistics(); 
                        }
                    });
                    
                    // 聚焦文件更新
                    this.socket.on('focused_files_update', (data) => {
                        this.focusedFiles = data || {};
                        // 聚焦文件变化时更新当前上下文Token（关键修复）
                        if (this.currentConversationId) {
                            setTimeout(() => {
                                this.updateCurrentContextTokens();
                            }, 500);
                        }
                    });
                    
                    // 文件树更新
                    this.socket.on('file_tree_update', (data) => {
                        this.updateFileTree(data);
                        // 文件树变化时也可能影响上下文
                        if (this.currentConversationId) {
                            setTimeout(() => {
                                this.updateCurrentContextTokens();
                            }, 500);
                        }
                    });
                    
                    // 系统消息
                    this.socket.on('system_message', (data) => {
                        if (!data || !data.content) {
                            return;
                        }
                        this.appendSystemAction(data.content);
                    });
                    
                    // 错误处理
                    this.socket.on('error', (data) => {
                        this.addSystemMessage(`错误: ${data.message}`);
                        // 仅标记当前流结束，避免状态错乱
                        this.streamingMessage = false;
                        this.stopRequested = false;
                    });
                    
                    // 命令结果
                    this.socket.on('command_result', (data) => {
                        if (data.command === 'clear' && data.success) {
                            this.messages = [];
                            this.currentMessageIndex = -1;
                            this.expandedBlocks.clear();
                            // 清除对话时重置Token统计
                            this.resetTokenStatistics();
                        } else if (data.command === 'status' && data.success) {
                            this.addSystemMessage(`系统状态:\n${JSON.stringify(data.data, null, 2)}`);
                        } else if (!data.success) {
                            this.addSystemMessage(`命令失败: ${data.message}`);
                        }
                    });
                    
                } catch (error) {
                    console.error('Socket初始化失败:', error);
                }
            },

            registerToolAction(action, executionId = null) {
                if (!action || action.type !== 'tool') {
                    return;
                }
                const keys = new Set();
                if (action.id) {
                    keys.add(action.id);
                }
                if (action.tool && action.tool.id) {
                    keys.add(action.tool.id);
                }
                if (executionId) {
                    keys.add(executionId);
                }
                if (action.tool && action.tool.executionId) {
                    keys.add(action.tool.executionId);
                }
                keys.forEach(key => {
                    if (!key) {
                        return;
                    }
                    this.toolActionIndex.set(key, action);
                });
            },

            unregisterToolAction(action) {
                if (!action || action.type !== 'tool') {
                    return;
                }
                const keysToRemove = [];
                for (const [key, stored] of this.toolActionIndex.entries()) {
                    if (stored === action) {
                        keysToRemove.push(key);
                    }
                }
                keysToRemove.forEach(key => this.toolActionIndex.delete(key));
                if (action.tool && action.tool.name) {
                    this.releaseToolAction(action.tool.name, action);
                }
            },

            findToolAction(id, preparingId, executionId) {
                if (!this.toolActionIndex) {
                    return null;
                }
                const candidates = [executionId, id, preparingId];
                for (const key of candidates) {
                    if (key && this.toolActionIndex.has(key)) {
                        return this.toolActionIndex.get(key);
                    }
                }
                return null;
            },

            trackToolAction(toolName, action) {
                if (!toolName || !action) {
                    return;
                }
                if (!this.toolStacks.has(toolName)) {
                    this.toolStacks.set(toolName, []);
                }
                const stack = this.toolStacks.get(toolName);
                if (!stack.includes(action)) {
                    stack.push(action);
                }
            },

            releaseToolAction(toolName, action) {
                if (!toolName || !this.toolStacks.has(toolName)) {
                    return;
                }
                const stack = this.toolStacks.get(toolName);
                const index = stack.indexOf(action);
                if (index !== -1) {
                    stack.splice(index, 1);
                }
                if (stack.length === 0) {
                    this.toolStacks.delete(toolName);
                }
            },

            getLatestActiveToolAction(toolName) {
                if (!toolName || !this.toolStacks.has(toolName)) {
                    return null;
                }
                const stack = this.toolStacks.get(toolName);
                for (let i = stack.length - 1; i >= 0; i--) {
                    const action = stack[i];
                    if (!action || action.type !== 'tool' || !action.tool) {
                        continue;
                    }
                    if (['preparing', 'running', 'stale'].includes(action.tool.status)) {
                        return action;
                    }
                }
                return stack[stack.length - 1] || null;
            },

            cleanupStaleToolActions() {
                this.messages.forEach(msg => {
                    if (!msg.actions) {
                        return;
                    }
                    msg.actions.forEach(action => {
                        if (action.type !== 'tool' || !action.tool) {
                            return;
                        }
                        if (['running', 'preparing'].includes(action.tool.status)) {
                            action.tool.status = 'stale';
                            action.tool.message = action.tool.message || '已被新的响应中断';
                            this.unregisterToolAction(action);
                        }
                    });
                });
                this.preparingTools.clear();
                this.toolActionIndex.clear();
            },

            ensureAssistantMessage() {
                if (this.currentMessageIndex >= 0) {
                    return this.messages[this.currentMessageIndex];
                }
                const message = {
                    role: 'assistant',
                    actions: [],
                    streamingThinking: '',
                    streamingText: '',
                    currentStreamingType: null,
                    activeThinkingId: null
                };
                this.messages.push(message);
                this.currentMessageIndex = this.messages.length - 1;
                return message;
            },
            
            // 完整重置所有状态
            resetAllStates() {
                console.log('重置所有前端状态');
                this.hideContextMenu();
                
                // 重置消息和流状态
                this.streamingMessage = false;
                this.currentMessageIndex = -1;
                this.stopRequested = false;
                
                // 清理工具状态
                this.preparingTools.clear();
                this.activeTools.clear();
                this.toolActionIndex.clear();
                
                // 新增：将所有未完成的工具标记为已完成
                this.messages.forEach(msg => {
                    if (msg.role === 'assistant' && msg.actions) {
                        msg.actions.forEach(action => {
                            if (action.type === 'tool' && 
                                (action.tool.status === 'preparing' || action.tool.status === 'running')) {
                                action.tool.status = 'completed';
                            }
                        });
                    }
                });
                
                // 重置滚动状态
                this.userScrolling = false;
                this.autoScrollEnabled = true;
                
                // 清理Markdown缓存
                if (this.markdownCache) {
                    this.markdownCache.clear();
                }
                this.thinkingScrollLocks.clear();
                
                // 强制更新视图
                this.$forceUpdate();
                
                this.settingsOpen = false;
                this.toolMenuOpen = false;
                this.quickMenuOpen = false;
                this.inputLineCount = 1;
                this.inputIsMultiline = false;
                this.toolSettingsLoading = false;
                this.toolSettings = [];
                
                console.log('前端状态重置完成');
            },
            
            // 重置Token统计

            openRealtimeTerminal() {
                const { protocol, hostname, port } = window.location;
                const target = `${protocol}//${hostname}${port ? ':' + port : ''}/terminal`;
                window.open(target, '_blank');
            },

            resetTokenStatistics() {
                this.currentContextTokens = 0;
                this.currentConversationTokens = {
                    cumulative_input_tokens: 0,
                    cumulative_output_tokens: 0,
                    cumulative_total_tokens: 0
                };
            },
            
            async loadInitialData() {
                try {
                    console.log('加载初始数据...');
                    
                    const filesResponse = await fetch('/api/files');
                    const filesData = await filesResponse.json();
                    this.updateFileTree(filesData);
                    
                    const focusedResponse = await fetch('/api/focused');
                    const focusedData = await focusedResponse.json();
                    this.focusedFiles = focusedData || {};
                    
                    await this.fetchTodoList();
                    
                    const statusResponse = await fetch('/api/status');
                    const statusData = await statusResponse.json();
                    this.projectPath = statusData.project_path || '';
                    this.agentVersion = statusData.version || this.agentVersion;
                    this.thinkingMode = !!statusData.thinking_mode;
                    this.applyStatusSnapshot(statusData);
                    await this.fetchUsageQuota();
                    
                    // 获取当前对话信息
                    const statusConversationId = statusData.conversation && statusData.conversation.current_id;
                    if (statusConversationId) {
                        if (!this.currentConversationId) {
                            this.currentConversationId = statusConversationId;
                        }
                        // 如果有当前对话，尝试获取标题和Token统计
                        try {
                            const convResponse = await fetch(`/api/conversations/current`);
                            const convData = await convResponse.json();
                            if (convData.success && convData.data) {
                                this.currentConversationTitle = convData.data.title;
                            }
                            await this.fetchAndDisplayHistory();
                            // 获取当前对话的Token统计
                            this.fetchConversationTokenStatistics();
                            this.updateCurrentContextTokens();
                        } catch (e) {
                            console.warn('获取当前对话标题失败:', e);
                        }
                    }
                    
                    await this.loadToolSettings(true);
                    
                    console.log('初始数据加载完成');
                } catch (error) {
                    console.error('加载初始数据失败:', error);
                }
            },

            async refreshFileTree() {
                try {
                    const response = await fetch('/api/files');
                    const data = await response.json();
                    this.updateFileTree(data);
                } catch (error) {
                    console.error('刷新文件树失败:', error);
                }
            },
            
            // ==========================================
            // Token统计相关方法（完全修复版）
            // ==========================================
            
            async updateCurrentContextTokens() {
                // 获取当前上下文Token数（动态计算，包含完整prompt构建）
                if (!this.currentConversationId) {
                    this.currentContextTokens = 0;
                    return;
                }
                
                try {
                    console.log(`正在更新当前上下文Token: ${this.currentConversationId}`);
                    
                    // 关键修复：使用正确的动态API，包含文件结构+记忆+聚焦文件+终端内容+工具定义
                    const response = await fetch(`/api/conversations/${this.currentConversationId}/tokens`);
                    const data = await response.json();
                    
                    if (data.success && data.data) {
                        this.currentContextTokens = data.data.total_tokens || 0;
                        console.log(`当前上下文Token更新: ${this.currentContextTokens}`);
                        this.$forceUpdate();
                    } else {
                        console.warn('获取当前上下文Token失败:', data.error);
                        this.currentContextTokens = 0;
                    }
                } catch (error) {
                    console.warn('获取当前上下文Token异常:', error);
                    this.currentContextTokens = 0;
                }
            },
            
            async fetchConversationTokenStatistics() {
                // 获取对话累计Token统计（加载对话时、任务完成后调用）
                if (!this.currentConversationId) {
                    this.resetTokenStatistics();
                    return;
                }
                
                try {
                    const response = await fetch(`/api/conversations/${this.currentConversationId}/token-statistics`);
                    const data = await response.json();
                    
                    if (data.success && data.data) {
                        // 更新累计统计
                        this.currentConversationTokens.cumulative_input_tokens = data.data.total_input_tokens || 0;
                        this.currentConversationTokens.cumulative_output_tokens = data.data.total_output_tokens || 0;
                        this.currentConversationTokens.cumulative_total_tokens = data.data.total_tokens || 0;
                        
                        console.log(`累计Token统计: 输入=${data.data.total_input_tokens}, 输出=${data.data.total_output_tokens}, 总计=${data.data.total_tokens}`);
                        this.$forceUpdate();
                    } else {
                        console.warn('获取Token统计失败:', data.error);
                        // 保持当前统计，不重置
                    }
                } catch (error) {
                    console.warn('获取Token统计异常:', error);
                    // 保持当前统计，不重置
                }
            },
            
            // Token面板折叠/展开切换
            toggleTokenPanel() {
                this.tokenPanelCollapsed = !this.tokenPanelCollapsed;
            },

            applyStatusSnapshot(status) {
                if (!status || typeof status !== 'object') {
                    return;
                }
                if (status.project) {
                    const project = status.project;
                    this.projectStorage.used_bytes = project.total_size || 0;
                    this.projectStorage.limit_bytes = project.limit_bytes ?? null;
                    this.projectStorage.limit_label = project.limit_label || (project.limit_bytes ? `${(project.limit_bytes / (1024 * 1024)).toFixed(0)} MB` : '未限制');
                    if (project.limit_bytes) {
                        const pct = typeof project.usage_percent === 'number'
                            ? project.usage_percent
                            : (project.total_size / project.limit_bytes * 100);
                        this.projectStorage.usage_percent = pct;
                    } else {
                        this.projectStorage.usage_percent = null;
                    }
                }
                if (Object.prototype.hasOwnProperty.call(status, 'container')) {
                    this.updateContainerStatus(status.container);
                }
            },

            updateContainerStatus(status) {
                if (!status || status.mode !== 'docker') {
                    this.containerStatus = status || null;
                    this.containerNetRate = { down_bps: null, up_bps: null };
                    this.lastContainerSample = null;
                    return;
                }
                const stats = status.stats;
                if (stats && typeof stats.timestamp === 'number') {
                    const currentSample = {
                        timestamp: stats.timestamp,
                        rx_bytes: stats.net_io && typeof stats.net_io.rx_bytes === 'number' ? stats.net_io.rx_bytes : null,
                        tx_bytes: stats.net_io && typeof stats.net_io.tx_bytes === 'number' ? stats.net_io.tx_bytes : null
                    };
                    const last = this.lastContainerSample;
                    if (
                        last &&
                        currentSample.rx_bytes !== null &&
                        currentSample.tx_bytes !== null &&
                        last.rx_bytes !== null &&
                        last.tx_bytes !== null
                    ) {
                        const deltaT = Math.max(0.001, currentSample.timestamp - last.timestamp);
                        const downRate = Math.max(0, (currentSample.rx_bytes - last.rx_bytes) / deltaT);
                        const upRate = Math.max(0, (currentSample.tx_bytes - last.tx_bytes) / deltaT);
                        this.containerNetRate = {
                            down_bps: downRate,
                            up_bps: upRate
                        };
                    } else {
                        this.containerNetRate = { down_bps: null, up_bps: null };
                    }
                    this.lastContainerSample = currentSample;
                } else {
                    this.containerNetRate = { down_bps: null, up_bps: null };
                    this.lastContainerSample = null;
                }
                this.containerStatus = status;
            },

            async pollContainerStats() {
                try {
                    const response = await fetch('/api/container-status');
                    if (!response.ok) {
                        return;
                    }
                    const data = await response.json();
                    if (data.success) {
                        this.updateContainerStatus(data.data || null);
                    }
                } catch (error) {
                    console.warn('获取容器状态异常:', error);
                }
            },

            startContainerStatsPolling() {
                if (this.containerStatsTimer) {
                    return;
                }
                this.pollContainerStats();
                this.containerStatsTimer = setInterval(() => {
                    this.pollContainerStats();
                }, 500);
            },

            stopContainerStatsPolling() {
                if (this.containerStatsTimer) {
                    clearInterval(this.containerStatsTimer);
                    this.containerStatsTimer = null;
                }
            },

            pollProjectStorage() {
                return fetch('/api/project-storage')
                    .then(resp => {
                        if (!resp.ok) {
                            throw new Error(resp.statusText || '请求失败');
                        }
                        return resp.json();
                    })
                    .then(data => {
                        if (data && data.success && data.data) {
                            this.projectStorage.used_bytes = data.data.used_bytes || 0;
                            this.projectStorage.limit_bytes = data.data.limit_bytes ?? null;
                            this.projectStorage.limit_label = data.data.limit_label || '';
                            this.projectStorage.usage_percent = data.data.usage_percent ?? null;
                        }
                    })
                    .catch(err => {
                        console.warn('获取存储信息失败:', err);
                    });
            },

            startProjectStoragePolling() {
                if (this.projectStorageTimer) {
                    return;
                }
                this.pollProjectStorage();
                this.projectStorageTimer = setInterval(() => {
                    this.pollProjectStorage();
                }, 5000);
            },

            stopProjectStoragePolling() {
                if (this.projectStorageTimer) {
                    clearInterval(this.projectStorageTimer);
                    this.projectStorageTimer = null;
                }
            },

            async fetchUsageQuota() {
                try {
                    const response = await fetch('/api/usage');
                    if (!response.ok) {
                        throw new Error(response.statusText || '请求失败');
                    }
                    const data = await response.json();
                    if (data && data.success && data.data) {
                        this.usageQuota = this.normalizeUsageQuota(data.data);
                    }
                } catch (error) {
                    console.warn('获取用量配额失败:', error);
                }
            },

            normalizeUsageQuota(raw) {
                const tpl = { count: 0, limit: 0, reset_at: null, window_start: null };
                const quotas = (raw && raw.quotas) || {};
                const role = quotas.role || (raw && raw.role) || 'user';
                return {
                    fast: { ...tpl, ...(quotas.fast || {}) },
                    thinking: { ...tpl, ...(quotas.thinking || {}) },
                    search: { ...tpl, ...(quotas.search || {}) },
                    role
                };
            },

            startUsageQuotaPolling() {
                if (this.usageQuotaTimer) {
                    return;
                }
                this.fetchUsageQuota();
                this.usageQuotaTimer = setInterval(() => {
                    this.fetchUsageQuota();
                }, 60000);
            },

            stopUsageQuotaPolling() {
                if (this.usageQuotaTimer) {
                    clearInterval(this.usageQuotaTimer);
                    this.usageQuotaTimer = null;
                }
            },
            
            // ==========================================
            // 对话管理核心功能
            // ==========================================
            
            async loadConversationsList() {
                this.conversationsLoading = true;
                try {
                    const response = await fetch(`/api/conversations?limit=${this.conversationsLimit}&offset=${this.conversationsOffset}`);
                    const data = await response.json();
                    
                    if (data.success) {
                        if (this.conversationsOffset === 0) {
                            this.conversations = data.data.conversations;
                        } else {
                            this.conversations.push(...data.data.conversations);
                        }
                        if (this.currentConversationId) {
                            this.promoteConversationToTop(this.currentConversationId);
                        }
                        this.hasMoreConversations = data.data.has_more;
                        console.log(`已加载 ${this.conversations.length} 个对话`);

                        if (this.conversationsOffset === 0 && !this.currentConversationId && this.conversations.length > 0) {
                            const latestConversation = this.conversations[0];
                            if (latestConversation && latestConversation.id) {
                                await this.loadConversation(latestConversation.id);
                            }
                        }
                    } else {
                        console.error('加载对话列表失败:', data.error);
                    }
                } catch (error) {
                    console.error('加载对话列表异常:', error);
                } finally {
                    this.conversationsLoading = false;
                }
            },
            
            async loadMoreConversations() {
                if (this.loadingMoreConversations || !this.hasMoreConversations) return;
                
                this.loadingMoreConversations = true;
                this.conversationsOffset += this.conversationsLimit;
                await this.loadConversationsList();
                this.loadingMoreConversations = false;
            },
            
            async loadConversation(conversationId) {
                console.log('加载对话:', conversationId);
                
                if (conversationId === this.currentConversationId) {
                    console.log('已是当前对话，跳过加载');
                    return;
                }
                
                try {
                    // 1. 调用加载API
                    const response = await fetch(`/api/conversations/${conversationId}/load`, {
                        method: 'PUT'
                    });
                    const result = await response.json();
                    
                    if (result.success) {
                        console.log('对话加载API成功:', result);
                        
                        // 2. 更新当前对话信息
                        this.currentConversationId = conversationId;
                        this.currentConversationTitle = result.title;
                        this.promoteConversationToTop(conversationId);
                        history.pushState({ conversationId }, '', `/${this.stripConversationPrefix(conversationId)}`);
                        
                        // 3. 重置UI状态
                        this.resetAllStates();
                        this.fetchSubAgents();
                        
                        // 4. 延迟获取并显示历史对话内容（关键功能）
                        setTimeout(() => {
                            this.fetchAndDisplayHistory();
                        }, 300);
                        
                        // 5. 获取Token统计（重点：加载历史累计统计+当前上下文）
                        setTimeout(() => {
                            this.fetchConversationTokenStatistics();
                            this.updateCurrentContextTokens();
                        }, 500);
                        
                    } else {
                        console.error('对话加载失败:', result.message);
                        this.pushToast({
                            title: '加载对话失败',
                            message: result.message || '服务器未返回成功状态',
                            type: 'error'
                        });
                    }
                } catch (error) {
                    console.error('加载对话异常:', error);
                    this.pushToast({
                        title: '加载对话异常',
                        message: error.message || String(error),
                        type: 'error'
                    });
                }
            },

            promoteConversationToTop(conversationId) {
                if (!Array.isArray(this.conversations) || !conversationId) {
                    return;
                }
                const index = this.conversations.findIndex(conv => conv && conv.id === conversationId);
                if (index > 0) {
                    const [selected] = this.conversations.splice(index, 1);
                    this.conversations.unshift(selected);
                }
            },
            
            // ==========================================
            // 关键功能：获取并显示历史对话内容
            // ==========================================
            async fetchAndDisplayHistory() {
                console.log('开始获取历史对话内容...');
                
                if (!this.currentConversationId || this.currentConversationId.startsWith('temp_')) {
                    console.log('没有当前对话ID，跳过历史加载');
                    return;
                }
                
                try {
                    // 使用专门的API获取对话消息历史
                    const messagesResponse = await fetch(`/api/conversations/${this.currentConversationId}/messages`);
                    
                    if (!messagesResponse.ok) {
                        console.warn('无法获取消息历史，尝试备用方法');
                        // 备用方案：通过状态API获取
                        const statusResponse = await fetch('/api/status');
                        const status = await statusResponse.json();
                        console.log('系统状态:', status);
                        this.applyStatusSnapshot(status);
                        
                        // 如果状态中有对话历史字段
                        if (status.conversation_history && Array.isArray(status.conversation_history)) {
                            this.renderHistoryMessages(status.conversation_history);
                            return;
                        }
                        
                        console.log('备用方案也无法获取历史消息');
                        return;
                    }
                    
                    const messagesData = await messagesResponse.json();
                    console.log('获取到消息数据:', messagesData);
                    
                    if (messagesData.success && messagesData.data && messagesData.data.messages) {
                        const messages = messagesData.data.messages;
                        console.log(`发现 ${messages.length} 条历史消息`);
                        
                        if (messages.length > 0) {
                            // 清空当前显示的消息
                            this.messages = [];
                            
                            // 渲染历史消息 - 这是关键功能
                            this.renderHistoryMessages(messages);
                            
                            // 滚动到底部
                            this.$nextTick(() => {
                                this.scrollToBottom();
                            });
                            
                            console.log('历史对话内容显示完成');
                        } else {
                            console.log('对话存在但没有历史消息');
                            this.messages = [];
                        }
                    } else {
                        console.log('消息数据格式不正确:', messagesData);
                        this.messages = [];
                    }
                    
                } catch (error) {
                    console.error('获取历史对话失败:', error);
                    console.log('尝试不显示错误弹窗，仅在控制台记录');
                    // 不显示alert，避免打断用户体验
                    this.messages = [];
                }
            },
            
            // ==========================================
            // 关键功能：渲染历史消息
            // ==========================================
            renderHistoryMessages(historyMessages) {
                console.log('开始渲染历史消息...', historyMessages);
                console.log('历史消息数量:', historyMessages.length);
                
                if (!Array.isArray(historyMessages)) {
                    console.error('历史消息不是数组格式');
                    return;
                }
                
                let currentAssistantMessage = null;
                
                historyMessages.forEach((message, index) => {
                    console.log(`处理消息 ${index + 1}/${historyMessages.length}:`, message.role, message);
                    
                    if (message.role === 'user') {
                        // 用户消息 - 先结束之前的assistant消息
                        if (currentAssistantMessage && currentAssistantMessage.actions.length > 0) {
                            this.messages.push(currentAssistantMessage);
                            currentAssistantMessage = null;
                        }
                        
                        this.messages.push({
                            role: 'user',
                            content: message.content || ''
                        });
                        console.log('添加用户消息:', message.content?.substring(0, 50) + '...');
                        
                    } else if (message.role === 'assistant') {
                        // AI消息 - 如果没有当前assistant消息，创建一个
                        if (!currentAssistantMessage) {
                            currentAssistantMessage = {
                                role: 'assistant',
                                actions: [],
                                streamingThinking: '',
                                streamingText: '',
                                currentStreamingType: null,
                                activeThinkingId: null
                            };
                        }
                        
                        const content = message.content || '';
                        let reasoningText = (message.reasoning_content || '').trim();
                        
                        if (!reasoningText) {
                            const thinkPatterns = [
                                /<think>([\s\S]*?)<\/think>/g,
                                /<thinking>([\s\S]*?)<\/thinking>/g
                            ];
                            
                            let extracted = '';
                            for (const pattern of thinkPatterns) {
                                let match;
                                while ((match = pattern.exec(content)) !== null) {
                                    extracted += (match[1] || '').trim() + '\n';
                                }
                            }
                            reasoningText = extracted.trim();
                        }
                        
                        if (reasoningText) {
                            const blockId = `history-thinking-${Date.now()}-${Math.random().toString(36).slice(2)}`;
                            currentAssistantMessage.actions.push({
                                id: `history-think-${Date.now()}-${Math.random()}`,
                                type: 'thinking',
                                content: reasoningText,
                                streaming: false,
                                timestamp: Date.now(),
                                blockId
                            });
                            console.log('添加思考内容:', reasoningText.substring(0, 50) + '...');
                        }
                        
                        // 处理普通文本内容（移除思考标签后的内容）
                        const metadata = message.metadata || {};
                        const appendPayloadMeta = metadata.append_payload;
                        const modifyPayloadMeta = metadata.modify_payload;
                        const isAppendMessage = message.name === 'append_to_file';
                        const isModifyMessage = message.name === 'modify_file';
                        const containsAppendMarkers = /<<<\s*(APPEND|MODIFY)/i.test(content || '') || /<<<END_\s*(APPEND|MODIFY)>>>/i.test(content || '');
                        
                        let textContent = content;
                        if (!message.reasoning_content) {
                            textContent = textContent
                                .replace(/<think>[\s\S]*?<\/think>/g, '')
                                .replace(/<thinking>[\s\S]*?<\/thinking>/g, '')
                                .trim();
                        } else {
                            textContent = textContent.trim();
                        }
                            
                        if (appendPayloadMeta) {
                            currentAssistantMessage.actions.push({
                                id: `history-append-payload-${Date.now()}-${Math.random()}`,
                                type: 'append_payload',
                                append: {
                                    path: appendPayloadMeta.path || '未知文件',
                                    forced: !!appendPayloadMeta.forced,
                                    success: appendPayloadMeta.success === undefined ? true : !!appendPayloadMeta.success,
                                    lines: appendPayloadMeta.lines ?? null,
                                    bytes: appendPayloadMeta.bytes ?? null
                                },
                                timestamp: Date.now()
                            });
                            console.log('添加append占位信息:', appendPayloadMeta.path);
                        } else if (modifyPayloadMeta) {
                            currentAssistantMessage.actions.push({
                                id: `history-modify-payload-${Date.now()}-${Math.random()}`,
                                type: 'modify_payload',
                                modify: {
                                    path: modifyPayloadMeta.path || '未知文件',
                                    total: modifyPayloadMeta.total_blocks ?? null,
                                    completed: modifyPayloadMeta.completed || [],
                                    failed: modifyPayloadMeta.failed || [],
                                    forced: !!modifyPayloadMeta.forced,
                                    details: modifyPayloadMeta.details || []
                                },
                                timestamp: Date.now()
                            });
                            console.log('添加modify占位信息:', modifyPayloadMeta.path);
                        }
                        
                        if (textContent && !appendPayloadMeta && !modifyPayloadMeta && !isAppendMessage && !isModifyMessage && !containsAppendMarkers) {
                            currentAssistantMessage.actions.push({
                                id: `history-text-${Date.now()}-${Math.random()}`,
                                type: 'text',
                                content: textContent,
                                streaming: false,
                                timestamp: Date.now()
                            });
                            console.log('添加文本内容:', textContent.substring(0, 50) + '...');
                        }
                        
                        // 处理工具调用
                        if (message.tool_calls && Array.isArray(message.tool_calls)) {
                            message.tool_calls.forEach((toolCall, tcIndex) => {
                                let arguments_obj = {};
                                try {
                                    arguments_obj = typeof toolCall.function.arguments === 'string' 
                                        ? JSON.parse(toolCall.function.arguments || '{}')
                                        : (toolCall.function.arguments || {});
                                } catch (e) {
                                    console.warn('解析工具参数失败:', e);
                                    arguments_obj = {};
                                }
                                
                                currentAssistantMessage.actions.push({
                                    id: `history-tool-${toolCall.id || Date.now()}-${tcIndex}`,
                                    type: 'tool',
                                    tool: {
                                        id: toolCall.id,
                                        name: toolCall.function.name,
                                        arguments: arguments_obj,
                                        status: 'preparing',
                                        result: null
                                    },
                                    timestamp: Date.now()
                                });
                                console.log('添加工具调用:', toolCall.function.name);
                            });
                        }
                        
                    } else if (message.role === 'tool') {
                        // 工具结果 - 更新当前assistant消息中对应的工具
                        if (currentAssistantMessage) {
                            // 查找对应的工具action - 使用更灵活的匹配
                            let toolAction = null;
                            
                            // 优先按tool_call_id匹配
                            if (message.tool_call_id) {
                                toolAction = currentAssistantMessage.actions.find(action => 
                                    action.type === 'tool' && 
                                    action.tool.id === message.tool_call_id
                                );
                            }
                            
                            // 如果找不到，按name匹配最后一个同名工具
                            if (!toolAction && message.name) {
                                const sameNameTools = currentAssistantMessage.actions.filter(action => 
                                    action.type === 'tool' && 
                                    action.tool.name === message.name
                                );
                                toolAction = sameNameTools[sameNameTools.length - 1]; // 取最后一个
                            }
                            
                            if (toolAction) {
                                // 解析工具结果
                                let result;
                                try {
                                    // 尝试解析为JSON
                                    result = JSON.parse(message.content);
                                } catch (e) {
                                    // 如果不是JSON，就作为纯文本
                                    result = { 
                                        output: message.content,
                                        success: true
                                    };
                                }
                                
                                toolAction.tool.status = 'completed';
                                toolAction.tool.result = result;
                                if (message.name === 'append_to_file' && result && result.message) {
                                    toolAction.tool.message = result.message;
                                }
                                console.log(`更新工具结果: ${message.name} -> ${message.content?.substring(0, 50)}...`);
                                
                                // append_to_file 的摘要在 append_payload 占位中呈现，此处无需重复
                            } else {
                                console.warn('找不到对应的工具调用:', message.name, message.tool_call_id);
                            }
                        }
                        
                    } else {
                        // 其他类型消息（如system）- 先结束当前assistant消息
                        if (currentAssistantMessage && currentAssistantMessage.actions.length > 0) {
                            this.messages.push(currentAssistantMessage);
                            currentAssistantMessage = null;
                        }
                        
                        console.log('处理其他类型消息:', message.role);
                        this.messages.push({
                            role: message.role,
                            content: message.content || ''
                        });
                    }
                });
                
                // 处理最后一个assistant消息
                if (currentAssistantMessage && currentAssistantMessage.actions.length > 0) {
                    this.messages.push(currentAssistantMessage);
                }
                
                console.log(`历史消息渲染完成，共 ${this.messages.length} 条消息`);
                
                // 强制更新视图
                this.$forceUpdate();
                
                // 确保滚动到底部
                this.$nextTick(() => {
                    this.scrollToBottom();
                });
            },
            
            async createNewConversation() {
                console.log('创建新对话...');
                
                try {
                    const response = await fetch('/api/conversations', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            thinking_mode: this.thinkingMode
                        })
                    });
                    
                    const result = await response.json();
                    
                    if (result.success) {
                        console.log('新对话创建成功:', result.conversation_id);
                        
                        // 清空当前消息
                        this.messages = [];
                        this.currentConversationId = result.conversation_id;
                        this.currentConversationTitle = '新对话';
                        history.pushState({ conversationId: this.currentConversationId }, '', `/${this.stripConversationPrefix(this.currentConversationId)}`);
                        
                        // 重置Token统计
                        this.resetTokenStatistics();
                        
                        // 重置状态
                        this.resetAllStates();
                        
                        // 刷新对话列表
                        this.conversationsOffset = 0;
                        await this.loadConversationsList();
                        
                    } else {
                        console.error('创建对话失败:', result.message);
                        this.pushToast({
                            title: '创建对话失败',
                            message: result.message || '服务器未返回成功状态',
                            type: 'error'
                        });
                    }
                } catch (error) {
                    console.error('创建对话异常:', error);
                    this.pushToast({
                        title: '创建对话异常',
                        message: error.message || String(error),
                        type: 'error'
                    });
                }
            },

            async deleteConversation(conversationId) {
                const confirmed = await this.confirmAction({
                    title: '删除对话',
                    message: '确定要删除这个对话吗？删除后无法恢复。',
                    confirmText: '删除',
                    cancelText: '取消'
                });
                if (!confirmed) {
                    return;
                }
                
                console.log('删除对话:', conversationId);
                
                try {
                    const response = await fetch(`/api/conversations/${conversationId}`, {
                        method: 'DELETE'
                    });
                    
                    const result = await response.json();
                    
                    if (result.success) {
                        console.log('对话删除成功');
                        
                        // 如果删除的是当前对话，清空界面
                        if (conversationId === this.currentConversationId) {
                            this.messages = [];
                            this.currentConversationId = null;
                            this.currentConversationTitle = '';
                            this.resetAllStates();
                            this.resetTokenStatistics();
                            history.replaceState({}, '', '/new');
                        }
                        
                        // 刷新对话列表
                        this.conversationsOffset = 0;
                        await this.loadConversationsList();
                        
                    } else {
                        console.error('删除对话失败:', result.message);
                        this.pushToast({
                            title: '删除对话失败',
                            message: result.message || '服务器未返回成功状态',
                            type: 'error'
                        });
                    }
                } catch (error) {
                    console.error('删除对话异常:', error);
                    this.pushToast({
                        title: '删除对话异常',
                        message: error.message || String(error),
                        type: 'error'
                    });
                }
            },

            async duplicateConversation(conversationId) {
                console.log('复制对话:', conversationId);
                try {
                    const response = await fetch(`/api/conversations/${conversationId}/duplicate`, {
                        method: 'POST'
                    });

                    const result = await response.json();

                    if (response.ok && result.success) {
                        const newId = result.duplicate_conversation_id;
                        if (newId) {
                            this.currentConversationId = newId;
                        }

                        this.conversationsOffset = 0;
                        await this.loadConversationsList();
                    } else {
                        const message = result.message || result.error || '复制失败';
                        this.pushToast({
                            title: '复制对话失败',
                            message,
                            type: 'error'
                        });
                    }
                } catch (error) {
                    console.error('复制对话异常:', error);
                    this.pushToast({
                        title: '复制对话异常',
                        message: error.message || String(error),
                        type: 'error'
                    });
                }
            },
            
            searchConversations() {
                // 简单的搜索功能，实际实现可以调用搜索API
                if (this.searchTimer) {
                    clearTimeout(this.searchTimer);
                }
                
                this.searchTimer = setTimeout(() => {
                    if (this.searchQuery.trim()) {
                        console.log('搜索对话:', this.searchQuery);
                        // TODO: 实现搜索API调用
                        // this.searchConversationsAPI(this.searchQuery);
                    } else {
                        // 清空搜索，重新加载全部对话
                        this.conversationsOffset = 0;
                        this.loadConversationsList();
                    }
                }, 300);
            },
            
            toggleSidebar() {
                this.sidebarCollapsed = !this.sidebarCollapsed;
            },

            async openPersonalPage() {
                this.personalPageVisible = true;
                if (!this.personalizationLoaded && !this.personalizationLoading) {
                    await this.fetchPersonalization();
                }
            },

            closePersonalPage() {
                this.personalPageVisible = false;
                this.draggedConsiderationIndex = null;
                this.overlayPressActive = false;
            },

            handleOverlayPressStart(event) {
                if (event && event.type === 'mousedown' && event.button !== 0) {
                    return;
                }
                this.overlayPressActive = true;
            },

            handleOverlayPressEnd() {
                if (!this.overlayPressActive) {
                    return;
                }
                this.overlayPressActive = false;
                this.closePersonalPage();
            },

            handleOverlayPressCancel() {
                this.overlayPressActive = false;
            },

            async handlePersonalizationToggle() {
                if (this.personalizationToggleUpdating) {
                    return;
                }
                const newValue = !!this.personalForm.enabled;
                const previousValue = !newValue;
                this.personalizationToggleUpdating = true;
                this.personalizationStatus = '';
                this.personalizationError = '';
                try {
                    const resp = await fetch('/api/personalization', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({ enabled: newValue })
                    });
                    const result = await resp.json();
                    if (!resp.ok || !result.success) {
                        throw new Error(result.error || '更新失败');
                    }
                    const statusLabel = newValue ? '已启用' : '已停用';
                    this.personalizationStatus = statusLabel;
                    setTimeout(() => {
                        if (this.personalizationStatus === statusLabel) {
                            this.personalizationStatus = '';
                        }
                    }, 2000);
                } catch (error) {
                    this.personalForm.enabled = previousValue;
                    this.personalizationError = error.message || '更新失败';
                } finally {
                    this.personalizationToggleUpdating = false;
                }
            },

            async handleLogout() {
                try {
                    const resp = await fetch('/logout', { method: 'POST' });
                    let result = {};
                    try {
                        result = await resp.json();
                    } catch (err) {
                        result = {};
                    }
                    if (!resp.ok || (result && result.success === false)) {
                        const message = (result && (result.error || result.message)) || '退出失败';
                        throw new Error(message);
                    }
                    window.location.href = '/login';
                } catch (error) {
                    console.error('退出登录失败:', error);
                    this.pushToast({
                        title: '退出登录失败',
                        message: error.message || '请稍后重试',
                        type: 'error'
                    });
                }
            },

            async fetchPersonalization() {
                this.personalizationLoading = true;
                this.personalizationError = '';
                try {
                    const resp = await fetch('/api/personalization');
                    const result = await resp.json();
                    if (!resp.ok || !result.success) {
                        throw new Error(result.error || '加载失败');
                    }
                    this.applyPersonalizationData(result.data || {});
                    this.personalizationLoaded = true;
                } catch (error) {
                    this.personalizationError = error.message || '加载失败';
                    this.pushToast({
                        title: '加载个性化配置失败',
                        message: this.personalizationError,
                        type: 'error'
                    });
                } finally {
                    this.personalizationLoading = false;
                }
            },

            applyPersonalizationData(data) {
                this.personalForm = {
                    enabled: !!data.enabled,
                    self_identify: data.self_identify || '',
                    user_name: data.user_name || '',
                    profession: data.profession || '',
                    tone: data.tone || '',
                    considerations: Array.isArray(data.considerations) ? [...data.considerations] : []
                };
                this.clearPersonalizationFeedback();
            },

            clearPersonalizationFeedback() {
                this.personalizationStatus = '';
                this.personalizationError = '';
            },

            async savePersonalization() {
                if (this.personalizationSaving) {
                    return;
                }
                this.personalizationSaving = true;
                this.personalizationStatus = '';
                this.personalizationError = '';
                try {
                    const resp = await fetch('/api/personalization', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify(this.personalForm)
                    });
                    const result = await resp.json();
                    if (!resp.ok || !result.success) {
                        throw new Error(result.error || '保存失败');
                    }
                    this.applyPersonalizationData(result.data || {});
                    this.personalizationStatus = '已保存';
                    setTimeout(() => {
                        this.personalizationStatus = '';
                    }, 3000);
                } catch (error) {
                    this.personalizationError = error.message || '保存失败';
                    this.pushToast({
                        title: '保存个性化配置失败',
                        message: this.personalizationError,
                        type: 'error'
                    });
                } finally {
                    this.personalizationSaving = false;
                }
            },

            addConsideration() {
                if (!this.newConsideration) {
                    return;
                }
                if (this.personalForm.considerations.length >= this.personalizationMaxConsiderations) {
                    this.pushToast({
                        title: '提示',
                        message: `最多添加 ${this.personalizationMaxConsiderations} 条信息`,
                        type: 'warning'
                    });
                    return;
                }
                this.personalForm.considerations = [
                    ...this.personalForm.considerations,
                    this.newConsideration
                ];
                this.newConsideration = '';
                this.clearPersonalizationFeedback();
            },

            removeConsideration(index) {
                const items = [...this.personalForm.considerations];
                items.splice(index, 1);
                this.personalForm.considerations = items;
                this.clearPersonalizationFeedback();
            },

            applyTonePreset(preset) {
                this.personalForm.tone = preset;
                this.clearPersonalizationFeedback();
            },

            handleConsiderationDragStart(index, event) {
                this.draggedConsiderationIndex = index;
                if (event && event.dataTransfer) {
                    event.dataTransfer.effectAllowed = 'move';
                }
            },

            handleConsiderationDragOver(index, event) {
                if (event) {
                    event.preventDefault();
                }
                if (this.draggedConsiderationIndex === null || this.draggedConsiderationIndex === index) {
                    return;
                }
                const items = [...this.personalForm.considerations];
                const [moved] = items.splice(this.draggedConsiderationIndex, 1);
                items.splice(index, 0, moved);
                this.personalForm.considerations = items;
                this.draggedConsiderationIndex = index;
                this.clearPersonalizationFeedback();
            },

            handleConsiderationDrop(index, event) {
                if (event) {
                    event.preventDefault();
                }
                this.draggedConsiderationIndex = null;
            },

            handleConsiderationDragEnd() {
                this.draggedConsiderationIndex = null;
            },

            async toggleThinkingMode() {
                try {
                    const resp = await fetch('/api/thinking-mode', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({ thinking_mode: !this.thinkingMode })
                    });
                    const data = await resp.json();
                    if (data.success) {
                        this.thinkingMode = !!data.data;
                    } else {
                        throw new Error(data.message || data.error || '切换失败');
                    }
                } catch (error) {
                    console.error('切换思考模式失败:', error);
                    this.pushToast({
                        title: '切换思考模式失败',
                        message: error.message || '请稍后重试',
                        type: 'error'
                    });
                }
            },
            
            formatTime(timeString) {
                if (!timeString) return '';
                
                const date = new Date(timeString);
                const now = new Date();
                const diffMs = now - date;
                const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
                const diffDays = Math.floor(diffHours / 24);
                
                if (diffHours < 1) {
                    return '刚刚';
                } else if (diffHours < 24) {
                    return `${diffHours}小时前`;
                } else if (diffDays < 7) {
                    return `${diffDays}天前`;
                } else {
                    return date.toLocaleDateString('zh-CN', { 
                        month: 'short', 
                        day: 'numeric' 
                    });
                }
            },
            
            // ==========================================
            // 原有功能保持不变
            // ==========================================
            
            updateFileTree(structure) {
                const treeDictionary = structure && structure.tree ? structure.tree : {};

                const buildNodes = (treeMap) => {
                    if (!treeMap) {
                        return [];
                    }

                    const entries = Object.keys(treeMap).map((name) => {
                        const node = treeMap[name] || {};
                        if (node.type === 'folder') {
                            return {
                                type: 'folder',
                                name,
                                path: node.path || name,
                                children: buildNodes(node.children)
                            };
                        }
                        return {
                            type: 'file',
                            name,
                            path: node.path || name,
                            annotation: node.annotation || ''
                        };
                    });

                    entries.sort((a, b) => {
                        if (a.type !== b.type) {
                            return a.type === 'folder' ? -1 : 1;
                        }
                        return a.name.localeCompare(b.name, 'zh-CN');
                    });

                    return entries;
                };

                const nodes = buildNodes(treeDictionary);

                const expanded = { ...this.expandedFolders };
                const validFolderPaths = new Set();

                const ensureExpansion = (list, depth = 0) => {
                    list.forEach((item) => {
                        if (item.type === 'folder') {
                            validFolderPaths.add(item.path);
                            if (expanded[item.path] === undefined) {
                                expanded[item.path] = false;
                            }
                            ensureExpansion(item.children || [], depth + 1);
                        }
                    });
                };

                ensureExpansion(nodes);

                Object.keys(expanded).forEach((path) => {
                    if (!validFolderPaths.has(path)) {
                        delete expanded[path];
                    }
                });

                this.expandedFolders = expanded;
                this.fileTree = nodes;
            },

            toggleFolder(path) {
                this.hideContextMenu();
                if (!path) {
                    return;
                }
                const current = !!this.expandedFolders[path];
                this.expandedFolders = {
                    ...this.expandedFolders,
                    [path]: !current
                };
            },

            cycleSidebarPanel() {
                const order = ['files', 'todo', 'subAgents'];
                const nextIndex = (order.indexOf(this.panelMode) + 1) % order.length;
                this.panelMode = order[nextIndex];
                if (this.panelMode === 'subAgents') {
                    this.fetchSubAgents();
                }
            },
            
            togglePanelMenu() {
                this.panelMenuOpen = !this.panelMenuOpen;
            },
            
            selectPanelMode(mode) {
                if (this.panelMode === mode) {
                    this.panelMenuOpen = false;
                    return;
                }
                this.panelMode = mode;
                this.panelMenuOpen = false;
                if (mode === 'todo') {
                    this.fetchTodoList();
                } else if (mode === 'subAgents') {
                    this.fetchSubAgents();
                }
            },

            async fetchSubAgents() {
                try {
                    const resp = await fetch('/api/sub_agents');
                    if (!resp.ok) {
                        throw new Error(await resp.text());
                    }
                    const data = await resp.json();
                    if (data.success) {
                        this.subAgents = Array.isArray(data.data) ? data.data : [];
                    }
                } catch (error) {
                    console.error('获取子智能体列表失败:', error);
                }
            },

            getSubAgentBaseUrl() {
                const override = window.SUB_AGENT_BASE_URL || window.__SUB_AGENT_BASE_URL__;
                if (override && typeof override === 'string') {
                    return override.replace(/\/$/, '');
                }
                const { protocol, hostname } = window.location;
                if (hostname && hostname.includes('agent.')) {
                    const mappedHost = hostname.replace('agent.', 'subagent.');
                    return `${protocol}//${mappedHost}`;
                }
                return `${protocol}//${hostname}:8092`;
            },

            openSubAgent(agent) {
                if (!agent || !agent.task_id) {
                    return;
                }
                const base = this.getSubAgentBaseUrl();
                const parentConv = agent.conversation_id || this.currentConversationId || '';
                const convSegment = this.stripConversationPrefix(parentConv);
                const agentLabel = agent.agent_id ? `sub_agent${agent.agent_id}` : agent.task_id;
                const pathSuffix = convSegment
                    ? `/${convSegment}+${agentLabel}`
                    : `/sub_agent/${agent.task_id}`;
                const url = `${base}${pathSuffix}`;
                window.open(url, '_blank');
            },

            async fetchTodoList() {
                try {
                    const response = await fetch('/api/todo-list');
                    const data = await response.json();
                    if (data.success) {
                        this.todoList = data.data || null;
                    }
                } catch (error) {
                    console.error('获取待办列表失败:', error);
                }
            },

            triggerFileUpload() {
                if (this.uploading) {
                    return;
                }
                const input = this.$refs.fileUploadInput;
                if (input) {
                    input.click();
                }
            },

            handleFileSelected(event) {
                const fileInput = event?.target;
                if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
                    return;
                }
                const [file] = fileInput.files;
                this.uploadSelectedFile(file);
            },

            resetFileInput() {
                const input = this.$refs.fileUploadInput;
                if (input) {
                    input.value = '';
                }
            },

            async uploadSelectedFile(file) {
                if (!file || this.uploading) {
                    this.resetFileInput();
                    return;
                }

                this.uploading = true;
                const toastId = this.pushToast({
                    title: '上传文件',
                    message: `正在上传 ${file.name}...`,
                    type: 'info',
                    duration: null
                });

                try {
                    const formData = new FormData();
                    formData.append('file', file);
                    formData.append('filename', file.name || '');

                    const response = await fetch('/api/upload', {
                        method: 'POST',
                        body: formData
                    });

                    if (toastId) {
                        this.updateToast(toastId, {
                            message: `文件已上传，正在执行安全扫描...`,
                            type: 'info'
                        });
                    }

                    let result = {};
                    try {
                        result = await response.json();
                    } catch (parseError) {
                        throw new Error('服务器响应无法解析');
                    }

                    if (!response.ok || !result.success) {
                        const message = result.error || result.message || '上传失败';
                        throw new Error(message);
                    }

                    await this.refreshFileTree();
                    if (toastId) {
                        this.updateToast(toastId, {
                            title: '上传完成',
                            message: `上传成功：${result.path || file.name}`,
                            type: 'success',
                            duration: 4500
                        });
                    } else {
                        this.pushToast({
                            title: '上传完成',
                            message: `上传成功：${result.path || file.name}`,
                            type: 'success'
                        });
                    }
                } catch (error) {
                    console.error('文件上传失败:', error);
                    const originalMessage = error && error.message ? String(error.message) : '';
                    const fileLabel = file && file.name ? file.name : '文件';
                    let displayMessage = originalMessage || '请稍后重试';
                    if (/安全审核未通过/.test(originalMessage) || /Eicar/i.test(originalMessage)) {
                        displayMessage = `${fileLabel} 安全审核未通过`;
                    } else if (/文件类型不在允许列表中/.test(originalMessage)) {
                        displayMessage = `${fileLabel} 文件类型不在允许列表中`;
                    } else if (displayMessage) {
                        displayMessage = `${fileLabel} 上传失败：${displayMessage}`;
                    } else {
                        displayMessage = `${fileLabel} 上传失败，请稍后重试`;
                    }
                    if (toastId) {
                        this.updateToast(toastId, {
                            title: '上传失败',
                            message: displayMessage,
                            type: 'error',
                            duration: 5000
                        });
                    } else {
                        this.pushToast({
                            title: '上传失败',
                            message: displayMessage,
                            type: 'error'
                        });
                    }
                } finally {
                    this.uploading = false;
                    this.resetFileInput();
                }
            },
            
            handleSendOrStop() {
                if (this.streamingMessage) {
                    this.stopTask();
                } else {
                    this.sendMessage();
                }
            },

            sendMessage() {
                if (this.streamingMessage || !this.isConnected) {
                    return;
                }

                if (!this.inputMessage.trim()) {
                    return;
                }

                const quotaType = this.thinkingMode ? 'thinking' : 'fast';
                if (this.isQuotaExceeded(quotaType)) {
                    this.showQuotaToast({ type: quotaType });
                    return;
                }
                
                const message = this.inputMessage;
                
                if (message.startsWith('/')) {
                    this.socket.emit('send_command', { command: message });
                    this.inputMessage = '';
                    this.autoResizeInput();
                    return;
                }
                
                this.messages.push({
                    role: 'user',
                    content: message
                });
                
                this.currentMessageIndex = -1;
                this.socket.emit('send_message', { message: message, conversation_id: this.currentConversationId });
                this.inputMessage = '';
                this.autoScrollEnabled = true;
                this.scrollToBottom();
                this.autoResizeInput();
                
                // 发送消息后延迟更新当前上下文Token（关键修复：恢复原逻辑）
                setTimeout(() => {
                    if (this.currentConversationId) {
                        this.updateCurrentContextTokens();
                    }
                }, 1000);
            },
            
            // 新增：停止任务方法
            stopTask() {
                if (this.streamingMessage && !this.stopRequested) {
                    this.socket.emit('stop_task');
                    this.stopRequested = true;
                    console.log('发送停止请求');
                }
            },
            
            async clearChat() {
                const confirmed = await this.confirmAction({
                    title: '清除对话',
                    message: '确定要清除所有对话记录吗？该操作不可撤销。',
                    confirmText: '清除',
                    cancelText: '取消'
                });
                if (confirmed) {
                    this.socket.emit('send_command', { command: '/clear' });
                }
            },

            async compressConversation() {
                if (!this.currentConversationId) {
                    this.pushToast({
                        title: '无法压缩',
                        message: '当前没有可压缩的对话。',
                        type: 'info'
                    });
                    return;
                }

                if (this.compressing) {
                    return;
                }

                const confirmed = await this.confirmAction({
                    title: '压缩对话',
                    message: '确定要压缩当前对话记录吗？压缩后会生成新的对话副本。',
                    confirmText: '压缩',
                    cancelText: '取消'
                });
                if (!confirmed) {
                    return;
                }

                this.compressing = true;

                try {
                    const response = await fetch(`/api/conversations/${this.currentConversationId}/compress`, {
                        method: 'POST'
                    });

                    const result = await response.json();

                    if (response.ok && result.success) {
                        const newId = result.compressed_conversation_id;
                        if (newId) {
                            this.currentConversationId = newId;
                        }
                        console.log('对话压缩完成:', result);
                    } else {
                        const message = result.message || result.error || '压缩失败';
                        this.pushToast({
                            title: '压缩失败',
                            message,
                            type: 'error'
                        });
                    }
                } catch (error) {
                    console.error('压缩对话异常:', error);
                    this.pushToast({
                        title: '压缩对话异常',
                        message: error.message || '请稍后重试',
                        type: 'error'
                    });
                } finally {
                    this.compressing = false;
                }
            },

            toggleToolMenu() {
                if (!this.isConnected) {
                    return;
                }
                const nextState = !this.toolMenuOpen;
                this.toolMenuOpen = nextState;
                if (nextState) {
                    this.settingsOpen = false;
                    if (!this.quickMenuOpen) {
                        this.quickMenuOpen = true;
                    }
                    this.loadToolSettings(true);
                }
            },

            toggleQuickMenu() {
                if (!this.isConnected) {
                    return;
                }
                const nextState = !this.quickMenuOpen;
                this.quickMenuOpen = nextState;
                if (!nextState) {
                    this.toolMenuOpen = false;
                    this.settingsOpen = false;
                }
            },

            closeQuickMenu() {
                this.quickMenuOpen = false;
                this.toolMenuOpen = false;
                this.settingsOpen = false;
            },

            handleQuickUpload() {
                if (this.uploading || !this.isConnected) {
                    return;
                }
                this.triggerFileUpload();
            },

            handleQuickModeToggle() {
                if (!this.isConnected || this.streamingMessage) {
                    return;
                }
                this.toggleThinkingMode();
            },

            handleInputChange() {
                this.autoResizeInput();
            },

            handleInputFocus() {
                this.inputIsFocused = true;
                this.closeQuickMenu();
            },

            handleInputBlur() {
                this.inputIsFocused = false;
            },

            handleRealtimeTerminalClick() {
                if (!this.isConnected) {
                    return;
                }
                this.openRealtimeTerminal();
            },

            handleFocusPanelToggleClick() {
                if (!this.isConnected) {
                    return;
                }
                this.toggleFocusPanel();
            },

            handleTokenPanelToggleClick() {
                if (!this.currentConversationId) {
                    return;
                }
                this.toggleTokenPanel();
            },

            handleCompressConversationClick() {
                if (this.compressing || this.streamingMessage || !this.isConnected) {
                    return;
                }
                this.compressConversation();
            },

            autoResizeInput() {
                this.$nextTick(() => {
                    const textarea = this.$refs.stadiumInput;
                    if (!textarea) {
                        return;
                    }
                    const previousHeight = textarea.offsetHeight;
                    textarea.style.height = 'auto';
                    const computedStyle = window.getComputedStyle(textarea);
                    const lineHeight = parseFloat(computedStyle.lineHeight || '20') || 20;
                    const maxHeight = lineHeight * 6;
                    const targetHeight = Math.min(textarea.scrollHeight, maxHeight);
                    this.inputLineCount = Math.max(1, Math.round(targetHeight / lineHeight));
                    this.inputIsMultiline = targetHeight > lineHeight * 1.4;
                    if (Math.abs(targetHeight - previousHeight) <= 0.5) {
                        textarea.style.height = `${targetHeight}px`;
                        return;
                    }
                    textarea.style.height = `${previousHeight}px`;
                    void textarea.offsetHeight;
                    requestAnimationFrame(() => {
                        textarea.style.height = `${targetHeight}px`;
                    });
                });
            },

            handleClickOutsideQuickMenu(event) {
                if (!this.quickMenuOpen) {
                    return;
                }
                const shell = this.$refs.stadiumShellOuter || this.$refs.compactInputShell;
                if (shell && shell.contains(event.target)) {
                    return;
                }
                this.closeQuickMenu();
            },
            
            handleClickOutsidePanelMenu(event) {
                if (!this.panelMenuOpen) {
                    return;
                }
                const wrapper = this.$refs.panelMenuWrapper;
                if (wrapper && wrapper.contains(event.target)) {
                    return;
                }
                this.panelMenuOpen = false;
            },

            applyToolSettingsSnapshot(categories) {
                if (!Array.isArray(categories)) {
                    return;
                }
                this.toolSettings = categories.map((item) => ({
                    id: item.id,
                    label: item.label || item.id,
                    enabled: !!item.enabled,
                    tools: Array.isArray(item.tools) ? item.tools : []
                }));
                this.toolSettingsLoading = false;
            },

            async loadToolSettings(force = false) {
                if (!this.isConnected) {
                    return;
                }
                if (this.toolSettingsLoading) {
                    return;
                }
                if (!force && this.toolSettings.length > 0) {
                    return;
                }
                this.toolSettingsLoading = true;
                try {
                    const response = await fetch('/api/tool-settings');
                    const data = await response.json();
                    if (response.ok && data.success && Array.isArray(data.categories)) {
                        this.applyToolSettingsSnapshot(data.categories);
                    } else {
                        console.warn('获取工具设置失败:', data);
                        this.toolSettingsLoading = false;
                    }
                } catch (error) {
                    console.error('获取工具设置异常:', error);
                    this.toolSettingsLoading = false;
                }
            },

            async updateToolCategory(categoryId, enabled) {
                if (!this.isConnected) {
                    return;
                }
                if (this.toolSettingsLoading) {
                    return;
                }
                const previousSnapshot = this.toolSettings.map((item) => ({ ...item }));
                this.toolSettings = this.toolSettings.map((item) => {
                    if (item.id === categoryId) {
                        return { ...item, enabled };
                    }
                    return item;
                });
                try {
                    const response = await fetch('/api/tool-settings', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            category: categoryId,
                            enabled
                        })
                    });
                    const data = await response.json();
                    if (response.ok && data.success && Array.isArray(data.categories)) {
                        this.applyToolSettingsSnapshot(data.categories);
                    } else {
                        console.warn('更新工具设置失败:', data);
                        this.toolSettings = previousSnapshot;
                    }
                } catch (error) {
                    console.error('更新工具设置异常:', error);
                    this.toolSettings = previousSnapshot;
                }
                this.toolSettingsLoading = false;
            },

            toggleSettings() {
                if (!this.isConnected) {
                    return;
                }
                const nextState = !this.settingsOpen;
                this.settingsOpen = nextState;
                if (nextState) {
                    this.toolMenuOpen = false;
                    if (!this.quickMenuOpen) {
                        this.quickMenuOpen = true;
                    }
                }
            },

            toggleFocusPanel() {
                this.rightCollapsed = !this.rightCollapsed;
                if (!this.rightCollapsed && this.rightWidth < this.minPanelWidth) {
                    this.rightWidth = this.minPanelWidth;
                }
            },

            addSystemMessage(content) {
                this.appendSystemAction(content);
            },

            appendSystemAction(content) {
                const msg = this.ensureAssistantMessage();
                msg.actions.push({
                    id: `system-${Date.now()}-${Math.random()}`,
                    type: 'system',
                    content: content,
                    timestamp: Date.now()
                });
                this.$forceUpdate();
                this.conditionalScrollToBottom();
            },
            
            toggleBlock(id) {
                if (this.expandedBlocks.has(id)) {
                    this.expandedBlocks.delete(id);
                } else {
                    this.expandedBlocks.add(id);
                }
                this.$forceUpdate();
            },

            getActiveThinkingAction(msg) {
                if (!msg || !Array.isArray(msg.actions)) {
                    return null;
                }
                if (msg.activeThinkingId) {
                    const found = msg.actions.find(action => action && action.id === msg.activeThinkingId && action.type === 'thinking');
                    if (found) {
                        return found;
                    }
                }
                for (let i = msg.actions.length - 1; i >= 0; i--) {
                    const action = msg.actions[i];
                    if (action && action.type === 'thinking' && action.streaming !== false) {
                        return action;
                    }
                }
                return null;
            },
            
            // 修复：工具相关方法 - 接收tool对象而不是name
            getToolIcon(tool) {
                const toolName = typeof tool === 'string' ? tool : (tool && tool.name);
                return TOOL_ICON_MAP[toolName] || 'settings';
            },
            
            getToolAnimationClass(tool) {
                // 根据工具状态返回不同的动画类
                if (tool.status === 'hinted') {
                    return 'hint-animation pulse-slow';
                } else if (tool.status === 'preparing') {
                    return 'preparing-animation';
                } else if (tool.status === 'running') {
                    const animations = {
                        'create_file': 'file-animation',
                        'read_file': 'read-animation',
                        'delete_file': 'file-animation',
                        'rename_file': 'file-animation',
                        'modify_file': 'file-animation',
                        'append_to_file': 'file-animation',
                        'create_folder': 'file-animation',
                        'focus_file': 'focus-animation',
                        'unfocus_file': 'focus-animation',
                        'web_search': 'search-animation',
                        'extract_webpage': 'search-animation',
                        'save_webpage': 'file-animation',
                        'run_python': 'code-animation',
                        'run_command': 'terminal-animation',
                        'update_memory': 'memory-animation',
                        'sleep': 'wait-animation',
                        'terminal_session': 'terminal-animation',
                        'terminal_input': 'terminal-animation',
                        'terminal_snapshot': 'terminal-animation',
                        'terminal_reset': 'terminal-animation',
                        'todo_create': 'file-animation',
                        'todo_update_task': 'file-animation',
                        'todo_finish': 'file-animation',
                        'todo_finish_confirm': 'file-animation',
                        'create_sub_agent': 'terminal-animation',
                        'wait_sub_agent': 'wait-animation'
                    };
                    return animations[tool.name] || 'default-animation';
                }
                return '';
            },
            
            // 修复：获取工具状态文本
            getToolStatusText(tool) {
                // 优先使用自定义消息
                if (tool.message) {
                    return tool.message;
                }
                
                if (tool.status === 'hinted') {
                    return `可能需要 ${tool.name}...`;
                } else if (tool.status === 'preparing') {
                    return `准备调用 ${tool.name}...`;
                } else if (tool.status === 'running') {
                    const texts = {
                        'create_file': '正在创建文件...',
                        'sleep': '正在等待...',
                        'delete_file': '正在删除文件...',
                        'rename_file': '正在重命名文件...',
                        'modify_file': '正在修改文件...',
                        'append_to_file': '正在追加文件...',
                        'create_folder': '正在创建文件夹...',
                        'focus_file': '正在聚焦文件...',
                        'unfocus_file': '正在取消聚焦...',
                        'web_search': '正在搜索网络...',
                        'extract_webpage': '正在提取网页...',
                        'save_webpage': '正在保存网页...',
                        'run_python': '正在执行Python代码...',
                        'run_command': '正在执行命令...',
                        'update_memory': '正在更新记忆...',
                        'terminal_session': '正在管理终端会话...',
                        'terminal_input': '正在发送终端输入...',
                        'terminal_snapshot': '正在获取终端快照...',
                        'terminal_reset': '正在重置终端...'
                    };
                    if (tool.name === 'read_file') {
                        const readType = ((tool.argumentSnapshot && tool.argumentSnapshot.type) ||
                            (tool.arguments && tool.arguments.type) || 'read').toLowerCase();
                        const runningMap = {
                            'read': '正在读取文件...',
                            'search': '正在执行搜索...',
                            'extract': '正在提取内容...'
                        };
                        return runningMap[readType] || '正在读取文件...';
                    }
                    return texts[tool.name] || '正在执行...';
                } else if (tool.status === 'completed') {
                    // 修复：完成状态的文本
                    const texts = {
                        'create_file': '文件创建成功',
                        'delete_file': '文件删除成功',
                        'sleep': '等待完成',
                        'rename_file': '文件重命名成功',
                        'modify_file': '文件修改成功',
                        'append_to_file': '文件追加完成',
                        'create_folder': '文件夹创建成功',
                        'focus_file': '文件聚焦成功',
                        'unfocus_file': '取消聚焦成功',
                        'web_search': '搜索完成',
                        'extract_webpage': '网页提取完成',
                        'save_webpage': '网页保存完成（纯文本）',
                        'run_python': '代码执行完成',
                        'run_command': '命令执行完成',
                        'update_memory': '记忆更新成功',
                        'terminal_session': '终端操作完成',
                        'terminal_input': '终端输入完成',
                        'terminal_snapshot': '终端快照已返回',
                        'terminal_reset': '终端已重置'
                    };
                    if (tool.name === 'read_file' && tool.result && typeof tool.result === 'object') {
                        const readType = (tool.result.type || 'read').toLowerCase();
                        if (readType === 'search') {
                            const query = tool.result.query ? `「${tool.result.query}」` : '';
                            const count = typeof tool.result.returned_matches === 'number'
                                ? tool.result.returned_matches
                                : (tool.result.actual_matches || 0);
                            return `搜索${query}，得到${count}个结果`;
                        } else if (readType === 'extract') {
                            const segments = Array.isArray(tool.result.segments) ? tool.result.segments : [];
                            const totalLines = segments.reduce((sum, seg) => {
                                const start = Number(seg.line_start) || 0;
                                const end = Number(seg.line_end) || 0;
                                if (!start || !end || end < start) return sum;
                                return sum + (end - start + 1);
                            }, 0);
                            const displayLines = totalLines || tool.result.char_count || 0;
                            return `提取了${displayLines}行`;
                        }
                        return '文件读取完成';
                    }
                    return texts[tool.name] || '执行完成';
                } else {
                    // 其他状态
                    return `${tool.name} - ${tool.status}`;
                }
            },
            
            getToolDescription(tool) {
                const args = tool.argumentSnapshot || tool.arguments;
                const argumentLabel = tool.argumentLabel || this.buildToolLabel(args);
                if (argumentLabel) {
                    return argumentLabel;
                }
                if (tool.statusDetail) {
                    return tool.statusDetail;
                }
                if (tool.result && typeof tool.result === 'object') {
                    if (tool.result.path) {
                        return tool.result.path.split('/').pop();
                    }
                }
                return '';
            },

            cloneToolArguments(args) {
                if (!args || typeof args !== 'object') {
                    return null;
                }
                try {
                    return JSON.parse(JSON.stringify(args));
                } catch (error) {
                    console.warn('无法克隆工具参数:', error);
                    return { ...args };
                }
            },

            buildToolLabel(args) {
                if (!args || typeof args !== 'object') {
                    return '';
                }
                if (args.command) {
                    return args.command;
                }
                if (args.path) {
                    return args.path.split('/').pop();
                }
                if (args.target_path) {
                    return args.target_path.split('/').pop();
                }
                if (args.query) {
                    return `"${args.query}"`;
                }
                if (args.seconds !== undefined) {
                    return `${args.seconds} 秒`;
                }
                if (args.name) {
                    return args.name;
                }
                return '';
            },

            formatSearchTopic(filters) {
                const mapping = {
                    'general': '通用',
                    'news': '新闻',
                    'finance': '金融'
                };
                const topic = (filters && filters.topic) ? String(filters.topic).toLowerCase() : 'general';
                return mapping[topic] || '通用';
            },

            formatSearchTime(filters) {
                if (!filters) {
                    return '未限定时间';
                }
                if (filters.time_range) {
                    const mapping = {
                        'day': '过去24小时',
                        'week': '过去7天',
                        'month': '过去30天',
                        'year': '过去365天'
                    };
                    return mapping[filters.time_range] || `相对范围：${filters.time_range}`;
                }
                if (typeof filters.days === 'number') {
                    return `过去${filters.days}天`;
                }
                if (filters.start_date && filters.end_date) {
                    return `${filters.start_date} 至 ${filters.end_date}`;
                }
                return '未限定时间';
            },
            
            renderMarkdown(text, isStreaming = false) {
                if (!text) return '';
                
                if (typeof marked === 'undefined') {
                    return text;
                }
                
                marked.setOptions({
                    breaks: true,
                    gfm: true,
                    sanitize: false
                });
                
                if (!isStreaming) {
                    if (!this.markdownCache) {
                        this.markdownCache = new Map();
                    }
                    
                    const cacheKey = `${text.length}_${text.substring(0, 100)}`;
                    
                    if (this.markdownCache.has(cacheKey)) {
                        return this.markdownCache.get(cacheKey);
                    }
                }
                
                let html = marked.parse(text);
                html = this.wrapCodeBlocks(html, isStreaming);
                
                if (!isStreaming && text.length < 10000) {
                    if (!this.markdownCache) {
                        this.markdownCache = new Map();
                    }
                    this.markdownCache.set(`${text.length}_${text.substring(0, 100)}`, html);
                    if (this.markdownCache.size > 20) {
                        const firstKey = this.markdownCache.keys().next().value;
                        this.markdownCache.delete(firstKey);
                    }
                }
                
                // 只在非流式状态处理（流式状态由renderLatexInRealtime处理）
                if (!isStreaming) {
                    setTimeout(() => {
                        // 代码高亮
                        if (typeof Prism !== 'undefined') {
                            const codeBlocks = document.querySelectorAll('.code-block-wrapper pre code:not([data-highlighted])');
                            codeBlocks.forEach(block => {
                                try {
                                    Prism.highlightElement(block);
                                    block.setAttribute('data-highlighted', 'true');
                                } catch (e) {
                                    console.warn('代码高亮失败:', e);
                                }
                            });
                        }
                        
                        // LaTeX最终渲染
                        if (typeof renderMathInElement !== 'undefined') {
                            const elements = document.querySelectorAll('.text-output .text-content:not(.streaming-text)');
                            elements.forEach(element => {
                                if (element.hasAttribute('data-math-rendered')) return;
                                
                                try {
                                    renderMathInElement(element, {
                                        delimiters: [
                                            {left: '$$', right: '$$', display: true},
                                            {left: '$', right: '$', display: false},
                                            {left: '\\[', right: '\\]', display: true},
                                            {left: '\\(', right: '\\)', display: false}
                                        ],
                                        throwOnError: false,
                                        trust: true
                                    });
                                    element.setAttribute('data-math-rendered', 'true');
                                } catch (e) {
                                    console.warn('LaTeX渲染失败:', e);
                                }
                            });
                        }
                    }, 100);
                }
                
                return html;
            },
            // 实时LaTeX渲染（用于流式输出）
            renderLatexInRealtime() {
                if (typeof renderMathInElement === 'undefined') {
                    return;
                }
                
                // 使用requestAnimationFrame优化性能
                if (this._latexRenderTimer) {
                    cancelAnimationFrame(this._latexRenderTimer);
                }
                
                this._latexRenderTimer = requestAnimationFrame(() => {
                    const elements = document.querySelectorAll('.text-output .streaming-text');
                    elements.forEach(element => {
                        try {
                            renderMathInElement(element, {
                                delimiters: [
                                    {left: '$$', right: '$$', display: true},
                                    {left: '$', right: '$', display: false},
                                    {left: '\\[', right: '\\]', display: true},
                                    {left: '\\(', right: '\\)', display: false}
                                ],
                                throwOnError: false,
                                trust: true
                            });
                        } catch (e) {
                            // 忽略错误，继续渲染
                        }
                    });
                });
            },
            // 用字符串替换包装代码块
            // 用字符串替换包装代码块 - 添加streaming参数
            wrapCodeBlocks(html, isStreaming = false) {
                // 如果是流式输出，不包装代码块，保持原样
                if (isStreaming) {
                    return html;
                }
                
                let counter = 0;
                
                // 匹配 <pre><code ...>...</code></pre>
                return html.replace(/<pre><code([^>]*)>([\s\S]*?)<\/code><\/pre>/g, (match, attributes, content) => {
                    // 提取语言
                    const langMatch = attributes.match(/class="[^"]*language-(\w+)/);
                    const language = langMatch ? langMatch[1] : 'text';
                    
                    // 生成唯一ID
                    const blockId = `code-${Date.now()}-${counter++}`;
                    
                    // 转义引号用于data属性
                    const escapedContent = content
                        .replace(/&/g, '&amp;')
                        .replace(/</g, '&lt;')
                        .replace(/>/g, '&gt;')
                        .replace(/"/g, '&quot;');
                    
                    // 构建新的HTML，保持code元素原样
                    return `
            <div class="code-block-wrapper">
                <div class="code-block-header">
                    <span class="code-language">${language}</span>
                    <button class="copy-code-btn" data-code="${blockId}" title="复制代码" aria-label="复制代码"></button>
                </div>
                <pre><code${attributes} data-code-id="${blockId}" data-original-code="${escapedContent}">${content}</code></pre>
            </div>`;
                });
            },
            
            getLanguageClass(path) {
                const ext = path.split('.').pop().toLowerCase();
                const langMap = {
                    'py': 'language-python',
                    'js': 'language-javascript',
                    'html': 'language-html',
                    'css': 'language-css',
                    'json': 'language-json',
                    'md': 'language-markdown',
                    'txt': 'language-plain'
                };
                return langMap[ext] || 'language-plain';
            },
            
            scrollToBottom() {
                setTimeout(() => {
                    const messagesArea = this.$refs.messagesArea;
                    if (messagesArea) {
                        // 标记为程序触发的滚动
                        if (this._setScrollingFlag) {
                            this._setScrollingFlag(true);
                        }
                        
                        messagesArea.scrollTop = messagesArea.scrollHeight;
                        
                        // 滚动完成后重置标记
                        setTimeout(() => {
                            if (this._setScrollingFlag) {
                                this._setScrollingFlag(false);
                            }
                        }, 100);
                    }
                }, 50);
            },
            
            conditionalScrollToBottom() {
                // 严格检查：只在明确允许时才滚动
                if (this.autoScrollEnabled === true && this.userScrolling === false) {
                    this.scrollToBottom();
                }
            },
            
            toggleScrollLock() {
                const currentlyLocked = this.autoScrollEnabled && !this.userScrolling;
                if (currentlyLocked) {
                    this.autoScrollEnabled = false;
                    this.userScrolling = true;
                } else {
                    this.autoScrollEnabled = true;
                    this.userScrolling = false;
                    this.scrollToBottom();
                }
            },

            scrollThinkingToBottom(blockId) {
                if (!this.thinkingScrollLocks.get(blockId)) return;
                const refName = `thinkingContent-${blockId}`;
                const elRef = this.$refs[refName];
                const el = Array.isArray(elRef) ? elRef[0] : elRef;
                if (el) {
                    el.scrollTop = el.scrollHeight;
                }
            },

            handleThinkingScroll(blockId, event) {
                const el = event.target;
                const threshold = 12;
                const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
                this.thinkingScrollLocks.set(blockId, atBottom);
            },
            
            // 面板调整方法
            startResize(panel, event) {
                this.isResizing = true;
                this.resizingPanel = panel;
                if (panel === 'right' && this.rightCollapsed) {
                    this.rightCollapsed = false;
                    if (this.rightWidth < this.minPanelWidth) {
                        this.rightWidth = this.minPanelWidth;
                    }
                }
                document.addEventListener('mousemove', this.handleResize);
                document.addEventListener('mouseup', this.stopResize);
                document.body.style.userSelect = 'none';
                document.body.style.cursor = 'col-resize';
                event.preventDefault();
            },
            
            handleResize(event) {
                if (!this.isResizing) return;
                
                const containerWidth = document.querySelector('.main-container').offsetWidth;
                
                if (this.resizingPanel === 'left') {
                    let newWidth = event.clientX - (this.sidebarCollapsed ? 60 : 300);
                    newWidth = Math.max(this.minPanelWidth, Math.min(newWidth, this.maxPanelWidth));
                    this.leftWidth = newWidth;
                } else if (this.resizingPanel === 'right') {
                    let newWidth = containerWidth - event.clientX;
                    newWidth = Math.max(this.minPanelWidth, Math.min(newWidth, this.maxPanelWidth));
                    this.rightWidth = newWidth;
                } else if (this.resizingPanel === 'conversation') {
                    // 对话侧边栏宽度调整
                    let newWidth = event.clientX;
                    newWidth = Math.max(200, Math.min(newWidth, 400));
                    // 这里可以动态调整对话侧边栏宽度，暂时不实现
                }
            },
            
            stopResize() {
                this.isResizing = false;
                this.resizingPanel = null;
                document.removeEventListener('mousemove', this.handleResize);
                document.removeEventListener('mouseup', this.stopResize);
                document.body.style.userSelect = '';
                document.body.style.cursor = '';
            },

            async handleEasterEggPayload(payload) {
                if (!payload) {
                    return;
                }
                let parsed = payload;
                if (typeof payload === 'string') {
                    try {
                        parsed = JSON.parse(payload);
                    } catch (error) {
                        console.warn('无法解析彩蛋结果:', payload);
                        return;
                    }
                }
                if (!parsed || typeof parsed !== 'object') {
                    return;
                }
                if (!parsed.success) {
                    if (parsed.error) {
                        console.warn('彩蛋触发失败:', parsed.error);
                    }
                    await this.destroyEasterEggEffect(true);
                    return;
                }
                const effectName = (parsed.effect || '').toLowerCase();
                if (!effectName) {
                    console.warn('彩蛋结果缺少 effect 字段');
                    return;
                }
                await this.startEasterEggEffect(effectName, parsed);
            },

            async startEasterEggEffect(effectName, payload = {}) {
                const registry = window.EasterEggRegistry;
                if (!registry) {
                    console.warn('EasterEggRegistry 尚未加载，无法播放彩蛋');
                    return;
                }
                if (!registry.has(effectName)) {
                    console.warn('未注册的彩蛋 effect:', effectName);
                    await this.destroyEasterEggEffect(true);
                    return;
                }
                const root = this.$refs.easterEggRoot;
                if (!root) {
                    console.warn('未找到彩蛋根节点');
                    return;
                }
                await this.destroyEasterEggEffect(true);
                this.easterEgg.active = true;
                this.easterEgg.effect = effectName;
                this.easterEgg.payload = payload;
                const instance = registry.start(effectName, {
                    root,
                    payload,
                    app: this
                });
                if (!instance) {
                    this.finishEasterEggCleanup();
                    return;
                }
                this.easterEgg.instance = instance;
                this.easterEgg.destroyPromise = null;
                this.easterEgg.destroying = false;
                if (this.easterEgg.cleanupTimer) {
                    clearTimeout(this.easterEgg.cleanupTimer);
                }
                const durationSeconds = Math.max(8, Number(payload.duration_seconds) || 45);
                this.easterEgg.cleanupTimer = setTimeout(() => {
                    const cleanup = this.destroyEasterEggEffect(false);
                    if (cleanup && typeof cleanup.catch === 'function') {
                        cleanup.catch(() => {});
                    }
                }, durationSeconds * 1000);
                if (payload.message) {
                    console.info(`[彩蛋] ${payload.display_name || effectName}: ${payload.message}`);
                }
            },

            destroyEasterEggEffect(forceImmediate = false) {
                if (this.easterEgg.cleanupTimer) {
                    clearTimeout(this.easterEgg.cleanupTimer);
                    this.easterEgg.cleanupTimer = null;
                }
                const instance = this.easterEgg.instance;
                if (!instance) {
                    this.finishEasterEggCleanup();
                    return Promise.resolve();
                }
                if (this.easterEgg.destroying) {
                    return this.easterEgg.destroyPromise || Promise.resolve();
                }
                this.easterEgg.destroying = true;
                let result;
                try {
                    result = instance.destroy({
                        immediate: forceImmediate,
                        payload: this.easterEgg.payload,
                        root: this.$refs.easterEggRoot || null
                    });
                } catch (error) {
                    console.warn('销毁彩蛋时发生错误:', error);
                    this.easterEgg.destroying = false;
                    this.finishEasterEggCleanup();
                    return Promise.resolve();
                }
                const finalize = () => {
                    this.easterEgg.destroyPromise = null;
                    this.easterEgg.destroying = false;
                    this.finishEasterEggCleanup();
                };
                if (result && typeof result.then === 'function') {
                    this.easterEgg.destroyPromise = result.then(() => {
                        finalize();
                    }).catch((error) => {
                        console.warn('彩蛋清理失败:', error);
                        finalize();
                    });
                    return this.easterEgg.destroyPromise;
                } else {
                    finalize();
                    return Promise.resolve();
                }
            },

            finishEasterEggCleanup() {
                if (this.easterEgg.cleanupTimer) {
                    clearTimeout(this.easterEgg.cleanupTimer);
                    this.easterEgg.cleanupTimer = null;
                }
                const root = this.$refs.easterEggRoot;
                if (root) {
                    root.innerHTML = '';
                }
                this.easterEgg.active = false;
                this.easterEgg.effect = null;
                this.easterEgg.payload = null;
                this.easterEgg.instance = null;
                this.easterEgg.destroyPromise = null;
                this.easterEgg.destroying = false;
            },

            // 格式化token显示（修复NaN问题）
            formatTokenCount(tokens) {
                // 确保tokens是数字，防止NaN
                const num = Number(tokens) || 0;
                if (num < 1000) {
                    return num.toString();
                } else if (num < 1000000) {
                    return (num / 1000).toFixed(1) + 'K';
                } else {
                    return (num / 1000000).toFixed(1) + 'M';
                }
            },

            formatBytes(bytes) {
                if (bytes === null || bytes === undefined) {
                    return '—';
                }
                const value = Number(bytes);
                if (!Number.isFinite(value)) {
                    return '—';
                }
                const units = ['B', 'KB', 'MB', 'GB', 'TB'];
                let display = value;
                let unitIndex = 0;
                while (display >= 1024 && unitIndex < units.length - 1) {
                    display /= 1024;
                    unitIndex++;
                }
                const decimals = display >= 10 || unitIndex === 0 ? 0 : 1;
                return `${display.toFixed(decimals)} ${units[unitIndex]}`;
            },

            formatPercentage(value) {
                if (typeof value !== 'number' || Number.isNaN(value)) {
                    return '—';
                }
                return `${value.toFixed(1)}%`;
            },

            formatRate(bytesPerSecond) {
                if (bytesPerSecond === null || bytesPerSecond === undefined) {
                    return '—';
                }
                const value = Number(bytesPerSecond);
                if (!Number.isFinite(value)) {
                    return '—';
                }
                const units = ['B/s', 'KB/s', 'MB/s', 'GB/s'];
                let display = value;
                let unitIndex = 0;
                while (display >= 1024 && unitIndex < units.length - 1) {
                    display /= 1024;
                    unitIndex++;
                }
                const decimals = display >= 10 || unitIndex === 0 ? 0 : 1;
                return `${display.toFixed(decimals)} ${units[unitIndex]}`;
            },

            hasContainerStats() {
                return !!(this.containerStatus && this.containerStatus.mode === 'docker' && this.containerStatus.stats);
            },

            quotaTypeLabel(type) {
                switch (type) {
                    case 'thinking':
                        return '思考模型';
                    case 'search':
                        return '搜索';
                    default:
                        return '常规模型';
                }
            },

            formatResetTime(iso) {
                if (!iso) {
                    return '--:--';
                }
                const date = new Date(iso);
                if (Number.isNaN(date.getTime())) {
                    return '--:--';
                }
                return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
            },

            formatQuotaValue(entry) {
                if (!entry) {
                    return '--';
                }
                const count = Number(entry.count) || 0;
                const limit = Number(entry.limit) || 0;
                if (!limit) {
                    return `${count}`;
                }
                return `${count} / ${limit}`;
            },

            quotaResetSummary() {
                const parts = [];
                ['fast', 'thinking', 'search'].forEach((type) => {
                    const entry = this.usageQuota && this.usageQuota[type];
                    if (entry && entry.reset_at && Number(entry.count || 0) > 0) {
                        parts.push(`${this.quotaTypeLabel(type)} ${this.formatResetTime(entry.reset_at)}`);
                    }
                });
                return parts.join(' · ');
            },

            isQuotaExceeded(type) {
                const entry = this.usageQuota && this.usageQuota[type];
                if (!entry) {
                    return false;
                }
                const limit = Number(entry.limit) || 0;
                if (!limit) {
                    return false;
                }
                return Number(entry.count || 0) >= limit;
            },

            showQuotaToast(payload) {
                if (!payload) {
                    return;
                }
                const type = payload.type || 'fast';
                const resetAt = payload.reset_at || (this.usageQuota[type] && this.usageQuota[type].reset_at);
                const label = this.quotaTypeLabel(type);
                const timeText = this.formatResetTime(resetAt);
                this.quotaToast = {
                    message: `${label} 配额已用完，将在 ${timeText} 重置`,
                    type
                };
                if (this.quotaToastTimer) {
                    clearTimeout(this.quotaToastTimer);
                }
                this.quotaToastTimer = setTimeout(() => {
                    this.quotaToast = null;
                    this.quotaToastTimer = null;
                }, 5000);
            },

            dismissQuotaToast() {
                if (this.quotaToastTimer) {
                    clearTimeout(this.quotaToastTimer);
                    this.quotaToastTimer = null;
                }
                this.quotaToast = null;
            },

            pushToast(options = {}) {
                const title = options.title || '';
                const message = options.message || '';
                if (!title && !message) {
                    return null;
                }
                const id = this.nextToastId++;
                const entry = {
                    id,
                    title,
                    message,
                    type: options.type || 'info',
                    closable: options.closable !== false,
                    timeoutId: null
                };
                const duration = Object.prototype.hasOwnProperty.call(options, 'duration') ? options.duration : 4000;
                if (duration !== null) {
                    entry.timeoutId = setTimeout(() => this.dismissToast(id), duration);
                }
                this.toastQueue.push(entry);
                return id;
            },

            updateToast(id, patch = {}) {
                const entry = this.toastQueue.find(item => item.id === id);
                if (!entry) {
                    return;
                }
                Object.assign(entry, patch);
                if (Object.prototype.hasOwnProperty.call(patch, 'duration')) {
                    if (entry.timeoutId) {
                        clearTimeout(entry.timeoutId);
                        entry.timeoutId = null;
                    }
                    if (patch.duration !== null) {
                        entry.timeoutId = setTimeout(() => this.dismissToast(id), patch.duration);
                    }
                }
            },

            dismissToast(id) {
                const index = this.toastQueue.findIndex(item => item.id === id);
                if (index === -1) {
                    return;
                }
                const [entry] = this.toastQueue.splice(index, 1);
                if (entry && entry.timeoutId) {
                    clearTimeout(entry.timeoutId);
                }
            },

            confirmAction(options = {}) {
                return new Promise((resolve) => {
                    if (this.pendingConfirmResolver) {
                        const previous = this.pendingConfirmResolver;
                        this.pendingConfirmResolver = null;
                        previous(false);
                    }
                    this.confirmDialog = {
                        visible: true,
                        title: options.title || '确认操作',
                        message: options.message || '请确认本次操作',
                        confirmText: options.confirmText || '确认',
                        cancelText: options.cancelText || '取消'
                    };
                    this.pendingConfirmResolver = resolve;
                });
            },

            handleConfirm(choice) {
                if (this.confirmDialog) {
                    this.confirmDialog.visible = false;
                }
                const resolver = this.pendingConfirmResolver;
                this.pendingConfirmResolver = null;
                this.confirmDialog = null;
                if (resolver) {
                    resolver(!!choice);
                }
            },

            containerStatusText() {
                if (!this.containerStatus) {
                    return '未知';
                }
                if (this.containerStatus.mode !== 'docker') {
                    return '宿主机模式';
                }
                const state = this.containerStatus.state;
                if (state && state.status) {
                    return state.status;
                }
                return (state && state.running === false) ? '已停止' : '运行中';
            },

            containerStatusClass() {
                if (!this.containerStatus) {
                    return {};
                }
                if (this.containerStatus.mode !== 'docker') {
                    return { 'status-pill--host': true };
                }
                const stopped = this.containerStatus.state && this.containerStatus.state.running === false;
                return {
                    'status-pill--running': !stopped,
                    'status-pill--stopped': stopped
                };
            }
        }
    });
    
    app.component('file-node', {
        name: 'FileNode',
        props: {
            node: {
                type: Object,
                required: true
            },
            level: {
                type: Number,
                default: 0
            },
            expandedFolders: {
                type: Object,
                required: true
            }
        },
        emits: ['toggle-folder', 'context-menu'],
        computed: {
            isExpanded() {
                if (this.node.type !== 'folder') {
                    return false;
                }
                const value = this.expandedFolders[this.node.path];
                return value === undefined ? true : value;
            },
            folderPadding() {
                return {
                    paddingLeft: `${12 + this.level * 16}px`
                };
            },
            filePadding() {
                return {
                    paddingLeft: `${40 + this.level * 16}px`
                };
            }
        },
        methods: {
            iconStyle(iconKey) {
                const iconPath = ICONS[iconKey];
                return iconPath ? { '--icon-src': `url(${iconPath})` } : {};
            },
            toggle() {
                if (this.node.type === 'folder') {
                    this.$emit('toggle-folder', this.node.path);
                }
            }
        },
        template: `
            <div class="file-node-wrapper" @contextmenu.stop.prevent="$emit('context-menu', { node, event: $event })">
                <div v-if="node.type === 'folder'" class="file-node folder-node">
                    <button class="folder-header" type="button" :style="folderPadding" @click="toggle">
                        <span class="folder-arrow">{{ isExpanded ? '▾' : '▸' }}</span>
                        <span class="icon icon-sm folder-icon"
                              :style="iconStyle(isExpanded ? 'folderOpen' : 'folder')"
                              aria-hidden="true"></span>
                        <span class="folder-name">{{ node.name }}</span>
                    </button>
                    <div v-show="isExpanded" class="folder-children">
                        <file-node
                            v-for="child in node.children"
                            :key="child.path"
                            :node="child"
                            :level="level + 1"
                            :expanded-folders="expandedFolders"
                            @toggle-folder="$emit('toggle-folder', $event)"
                            @context-menu="$emit('context-menu', $event)"
                        ></file-node>
                    </div>
                </div>
                <div v-else class="file-node file-leaf" :style="filePadding">
                    <span class="icon icon-sm file-icon"
                          :style="iconStyle('file')"
                          aria-hidden="true"></span>
                    <span class="file-name">{{ node.name }}</span>
                    <span v-if="node.annotation" class="annotation">{{ node.annotation }}</span>
                </div>
            </div>
        `
    });

    app.mount('#app');
    console.log('Vue应用初始化完成');
}

window.addEventListener('load', () => {
    bootstrapApp();
});
