网页操作录制器

记录并回放鼠标点击操作

// ==UserScript==
// @name         网页操作录制器
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  记录并回放鼠标点击操作
// @author       You
// @match        *://*/*
// @grant        none
// @run-at       document-end
// @noframes
// ==/UserScript==

(function() {
    'use strict';

    // 全局变量
    let recording = false;
    let actions = [];
    let startTime;
    let isRunning = false;
    let stopRunning = false;
    let abortController = null;
    let editModeRecording = false;

    // 设置选项
    const settings = {
        opacity: parseFloat(localStorage.getItem('recorder_opacity')) || 0.8,
        useBlur: localStorage.getItem('recorder_use_blur') !== 'false',
        showClickAnimation: localStorage.getItem('recorder_show_click_animation') !== 'false',
        maxAnimations: parseInt(localStorage.getItem('recorder_max_animations')) || 10
    };

    const COLORS = {
        NORMAL: '#1890ff',
        NEW_ACTION: '#52c41a',
        ACTIVE: '#ff4d4f',
        TEXT: '#000' // 固定文字颜色
    };

    const CONSTANTS = {
        DRAG_DELAY: 200,
        Z_INDEX: {
            BASE: 2147483645,
            ACTIVE: 2147483646,
            EDIT_PANEL: 2147483647
        }
    };

    // 样式初始化
    function initStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .top-edit-panel {
                position: fixed;
                top: 20px;
                left: 25%;
                background: rgba(255, 255, 255, ${settings.opacity});
                padding: 10px;
                border-radius: 8px;
                box-shadow: 0 2px 12px rgba(0,0,0,0.08);
                z-index: ${CONSTANTS.Z_INDEX.EDIT_PANEL};
                display: flex;
                flex-wrap: wrap;
                gap: 8px;
                width: 50vw;
                max-height: 80vh;
                overflow-y: auto;
                cursor: move;
                resize: none;
                color: ${COLORS.TEXT}; // 固定文字颜色
                ${settings.useBlur ? 'backdrop-filter: blur(4px);' : ''}
            }

            .point-item {
                display: flex;
                align-items: center;
                padding: 8px 10px;
                background: rgba(245, 245, 245, 0.7);
                border-radius: 4px;
                cursor: pointer;
                width: 170px;
                color: ${COLORS.TEXT}; // 固定文字颜色
            }

            .point-item.active {
                background: rgba(23, 144, 255, 0.7);
            }

            .edit-point {
                width: 24px;
                height: 24px;
                min-width: 24px;
                background: ${COLORS.NORMAL};
                border-radius: 50%;
                margin-right: 10px;
                display: flex;
                align-items: center;
                justify-content: center;
                color: white;
                font-size: 12px;
                font-weight: bold;
            }

            .point-item.active .edit-point {
                background: ${COLORS.ACTIVE};
            }

            .point-data {
                display: flex;
                align-items: center;
                margin: 4px 0;
                cursor: pointer;
            }

            .point-input {
                width: 60px;
                padding: 4px 6px;
                margin: 0 4px;
                border: 1px solid #d9d9d9;
                border-radius: 2px;
                font-size: 12px;
                cursor: pointer;
            }

            .point-input:focus {
                outline: none;
            }

            .click-point {
                position: fixed;
                width: 20px;
                height: 20px;
                background: #1890ff;
                border: 1px solid #000;
                border-radius: 50%;
                transform: translate(-50%, -50%);
                z-index: ${CONSTANTS.Z_INDEX.BASE};
                display: flex;
                align-items: center;
                justify-content: center;
                color: white;
                font-size: 11px;
                font-weight: bold;
                cursor: move;
                user-select: none;
                opacity: 0.8;
            }

            .click-point.active {
                background: #ff4d4f;
                z-index: ${CONSTANTS.Z_INDEX.ACTIVE};
                opacity: 1;
                box-shadow: 0 0 12px rgba(255, 77, 79, 0.8);
            }

            .click-point:hover {
                opacity: 1;
            }

            .click-effect {
                position: fixed;
                pointer-events: none;
                width: 20px;
                height: 20px;
                background: rgba(255, 77, 79, 0.8);
                border: 2px solid rgba(255, 77, 79, 0.9);
                border-radius: 50%;
                z-index: ${CONSTANTS.Z_INDEX.BASE - 1};
                opacity: 0;
                transform: translate(-50%, -50%) scale(0.3);
                display: none;
                left: var(--x);
                top: var(--y);
            }

            .click-effect.active {
                display: block;
                animation: clickEffect 0.6s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
            }

            @keyframes clickEffect {
                0% {
                    opacity: 1;
                    transform: translate(-50%, -50%) scale(0.3);
                }
                100% {
                    opacity: 0;
                    transform: translate(-50%, -50%) scale(2);
                }
            }

            .toast-message {
                position: fixed;
                bottom: 20px;
                left: 50%;
                transform: translateX(-50%);
                background: rgba(0, 0, 0, 0.7);
                color: white;
                padding: 8px 16px;
                border-radius: 4px;
                font-size: 14px;
                z-index: ${CONSTANTS.Z_INDEX.EDIT_PANEL};
                pointer-events: none;
                animation: fadeOut 1.5s ease-in-out forwards;
            }

            .custom-checkbox {
                appearance: none;
                width: 14px;
                height: 14px;
                border: 2px solid ${COLORS.NORMAL};
                border-radius: 2px;
                margin: 0;
                cursor: pointer;
                position: relative;
                transition: background-color 0.2s;
                pointer-events: all;
            }

            .custom-checkbox:checked {
                background: ${COLORS.NORMAL};
            }

            .custom-checkbox:checked::after {
                content: '';
                position: absolute;
                left: 2px;
                top: 0px;
                width: 3px;
                height: 7px;
                border: solid white;
                border-width: 0 2px 2px 0;
                transform: rotate(45deg);
            }

            .checkbox-wrapper {
                width: 14px;
                height: 14px;
                position: relative;
                pointer-events: none;
            }

            .checkbox-wrapper input {
                pointer-events: all;
            }

            .checkbox-wrapper + span {
                pointer-events: none;
            }

            .click-point.dragging {
                cursor: grabbing;
                opacity: 0.8;
                box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.8), 0 0 0 4px rgba(255, 255, 255, 0.8), 0 0 16px rgba(0, 0, 0, 0.5);
            }

            .scheme-name {
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                font-size: 13px;
                cursor: pointer;
                padding: 2px 4px;
                border-radius: 2px;
                color: ${COLORS.TEXT}; // 固定文字颜色
            }

            .scheme-name:hover {
                background: rgba(0, 0, 0, 0.05);
            }

            .scheme-name-input {
                width: 100%;
                font-size: 13px;
                padding: 2px 4px;
                border: 1px solid #1890ff;
                border-radius: 2px;
                outline: none;
            }
        `;
        document.head.appendChild(style);
    }

    // 点击效果管理器
    const ClickEffectManager = {
        activeEffects: new Set(),
        effectPool: [],
        poolSize: 20, // 对象池大小

        init() {
            // 初始化对象池
            for (let i = 0; i < this.poolSize; i++) {
                const effect = this.createEffectElement();
                const innerEffect = this.createEffectElement(true);
                this.effectPool.push({
                    effect,
                    innerEffect,
                    inUse: false
                });
            }
        },

        createEffectElement(isInner = false) {
            const effect = document.createElement('div');
            effect.className = 'click-effect';
            if (isInner) {
                effect.style.animation = 'clickEffect 0.4s cubic-bezier(0.22, 0.61, 0.36, 1) forwards';
                effect.style.background = 'rgba(255, 77, 79, 0.4)';
                effect.style.border = '2px solid rgba(255, 77, 79, 0.6)';
            }
            effect.style.display = 'none';
            document.body.appendChild(effect);
            return effect;
        },

        getFromPool() {
            // 从池中获取可用的效果元素
            let poolItem = this.effectPool.find(item => !item.inUse);

            // 如果池中没有可用元素,创建新的
            if (!poolItem) {
                const effect = this.createEffectElement();
                const innerEffect = this.createEffectElement(true);
                poolItem = {
                    effect,
                    innerEffect,
                    inUse: false
                };
                this.effectPool.push(poolItem);
            }

            poolItem.inUse = true;
            return poolItem;
        },

        returnToPool(poolItem) {
            // 重置元素状态并返回池中
            const { effect, innerEffect } = poolItem;
            effect.style.display = 'none';
            effect.classList.remove('active');
            innerEffect.style.display = 'none';
            innerEffect.classList.remove('active');
            poolItem.inUse = false;
            this.activeEffects.delete(effect);
            this.activeEffects.delete(innerEffect);
        },

        show(x, y) {
            if (!settings.showClickAnimation) return;

            // 检查是否超过最大动画数量限制
            if (settings.maxAnimations > 0 && this.activeEffects.size >= settings.maxAnimations) {
                const oldestEffect = this.activeEffects.values().next().value;
                if (oldestEffect) {
                    const poolItem = this.effectPool.find(item =>
                        item.effect === oldestEffect || item.innerEffect === oldestEffect
                    );
                    if (poolItem) {
                        this.returnToPool(poolItem);
                    }
                }
            }

            // 从对象池获取效果元素
            const poolItem = this.getFromPool();
            const { effect, innerEffect } = poolItem;

            // 设置位置
            effect.style.setProperty('--x', `${x}px`);
            effect.style.setProperty('--y', `${y}px`);
            innerEffect.style.setProperty('--x', `${x}px`);
            innerEffect.style.setProperty('--y', `${y}px`);

            // 显示效果
            effect.style.display = 'block';
            innerEffect.style.display = 'block';
            this.activeEffects.add(effect);
            this.activeEffects.add(innerEffect);

            // 启动动画
            requestAnimationFrame(() => {
                effect.classList.add('active');
                innerEffect.classList.add('active');
            });

            // 动画结束后回收到对象池
            effect.addEventListener('animationend', () => {
                this.returnToPool(poolItem);
            }, { once: true });
        },

        cleanup() {
            // 清理所有效果元素
            this.effectPool.forEach(({ effect, innerEffect }) => {
                effect.remove();
                innerEffect.remove();
            });
            this.effectPool = [];
            this.activeEffects.clear();
        }
    };

    // 点击点管理器
    const ClickPointManager = {
        points: new Map(),
        activePoint: null,

        init() {
            // 初始化全局点击事件监听
            document.addEventListener('click', (e) => {
                if (!e.target.closest('.click-point')) {
                    this.clearActivePoint();
                }
            });
        },

        createPoints(actions) {
            this.cleanup();
            actions.forEach((action, index) => {
                const point = this.createSinglePoint(action, index);
                this.points.set(index, point);
                document.body.appendChild(point);
            });
        },

        createSinglePoint(action, index) {
            const point = document.createElement('div');
            point.className = 'click-point edit-mode';
            point.setAttribute('data-point-index', index);
            point.textContent = index + 1;
            point.style.left = action.x + 'px';
            point.style.top = action.y + 'px';

            this.initDragEvents(point);
            return point;
        },

        initDragEvents(point) {
            let offsetX, offsetY;

            const onMouseMove = (e) => {
                e.preventDefault();
                requestAnimationFrame(() => {
                    point.style.left = (e.clientX - offsetX) + 'px';
                    point.style.top = (e.clientY - offsetY) + 'px';
                });
            };

            const onMouseUp = () => {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
                this.handleDragEnd();
            };

            const onMouseDown = (e) => {
                if (e.button !== 0) return;
                e.preventDefault();
                e.stopPropagation();

                const rect = point.getBoundingClientRect();
                offsetX = e.clientX - rect.left - rect.width / 2;
                offsetY = e.clientY - rect.top - rect.height / 2;

                this.setActivePoint(point);
                document.addEventListener('mousemove', onMouseMove);
                document.addEventListener('mouseup', onMouseUp);
            };

            point.addEventListener('mousedown', onMouseDown);
            point.addEventListener('click', (e) => {
                e.stopPropagation();
                this.setActivePoint(point);
            });
        },

        handleDragEnd() {
            if (!this.activePoint) return;
            const index = parseInt(this.activePoint.getAttribute('data-point-index'));
            if (index >= 0 && index < actions.length) {
                actions[index].x = parseInt(this.activePoint.style.left);
                actions[index].y = parseInt(this.activePoint.style.top);
                EditManager.markAsChanged();
            }
        },

        setActivePoint(point) {
            this.clearActivePoint();
            this.activePoint = point;
            point.classList.add('active');

            // 高亮编辑界面对应的点
            const index = parseInt(point.getAttribute('data-point-index'));
            document.querySelectorAll('.point-item').forEach((item) => {
                const itemIndex = parseInt(item.getAttribute('data-point-index'));
                item.classList.toggle('active', itemIndex === index);
            });
        },

        clearActivePoint() {
            if (this.activePoint) {
                this.activePoint.classList.remove('active');
                this.activePoint = null;
            }
            document.querySelectorAll('.point-item').forEach(item => {
                item.classList.remove('active');
            });
        },

        cleanup() {
            this.points.forEach(point => point.remove());
            this.points.clear();
            this.activePoint = null;
        },

        addNewPoint(action, index) {
            const point = this.createSinglePoint(action, index);
            this.points.set(index, point);
            document.body.appendChild(point);
            return point;
        }
    };

    // 存储管理器
    const StorageManager = {
        STORAGE_KEY: 'recordSchemes',
        cache: null,
        saveTimeout: null,
        initialized: false,

        // 初始化缓存
        init() {
            if (this.initialized) return;
            this.cache = this.loadFromStorage();
            this.initialized = true;
        },

        // 从localStorage加载数据
        loadFromStorage() {
            try {
                return JSON.parse(localStorage.getItem(this.STORAGE_KEY) || '{}');
            } catch (error) {
                console.error('Failed to load schemes from storage:', error);
                return {};
            }
        },

        // 将缓存保存到localStorage
        saveToStorage() {
            try {
                localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.cache));
                return true;
            } catch (error) {
                console.error('Failed to save schemes to storage:', error);
                return false;
            }
        },

        // 延迟保存,防止频繁写入
        debounceSave() {
            if (this.saveTimeout) {
                clearTimeout(this.saveTimeout);
            }
            this.saveTimeout = setTimeout(() => {
                this.saveToStorage();
                this.saveTimeout = null;
            }, 300); // 300ms延迟
        },

        // 获取所有方案
        getAllSchemes() {
            this.init();
            return this.cache;
        },

        // 获取单个方案
        getScheme(name) {
            this.init();
            return this.cache[name];
        },

        // 保存单个方案
        saveScheme(name, scheme) {
            this.init();
            this.cache[name] = scheme;
            this.debounceSave();
        },

        // 批量保存方案
        saveSchemeBatch(schemes) {
            this.init();
            Object.assign(this.cache, schemes);
            this.debounceSave();
        },

        // 删除方案
        deleteScheme(name) {
            this.init();
            delete this.cache[name];
            this.debounceSave();
        },

        // 重命名方案
        renameScheme(oldName, newName) {
            this.init();
            if (this.cache[oldName]) {
                this.cache[newName] = this.cache[oldName];
                delete this.cache[oldName];
                this.debounceSave();
                return true;
            }
            return false;
        },

        // 更新方案的特定字段
        updateSchemeField(name, field, value) {
            this.init();
            const scheme = this.cache[name];
            if (scheme) {
                scheme[field] = value;
                this.debounceSave();
                return true;
            }
            return false;
        },

        // 创建新方案
        createScheme(name, data) {
            this.init();
            const scheme = {
                actions: data.actions || [],
                timestamp: Date.now(),
                site: window.location.hostname,
                infiniteLoop: data.infiniteLoop || false,
                loopCount: data.loopCount || 1,
                loopDelay: data.loopDelay || 0,
                ...data
            };
            this.cache[name] = scheme;
            this.debounceSave();
            return scheme;
        },

        // 强制立即保存
        forceSave() {
            if (this.saveTimeout) {
                clearTimeout(this.saveTimeout);
                this.saveTimeout = null;
            }
            return this.saveToStorage();
        },

        // 清除缓存
        clearCache() {
            this.cache = null;
            this.initialized = false;
        }
    };

    // 录制管理器
    const RecorderManager = {
        start() {
            actions = [];
            startTime = Date.now();
            recording = true;
            const recordToggleBtn = document.getElementById('recordToggle');
            recordToggleBtn.textContent = '停止录制';
            recordToggleBtn.style.background = '#ff4d4f';
            document.getElementById('record-status').style.background = '#ff0000';
        },

        stop() {
            recording = false;
            const recordToggleBtn = document.getElementById('recordToggle');
            recordToggleBtn.textContent = '开始录制';
            recordToggleBtn.style.background = '#52c41a';
            document.getElementById('record-status').style.background = '#ccc';
            if (actions.length > 0) {
                this.save();
                StorageManager.forceSave(); // 确保立即保存
            }
        },

        save() {
            const now = new Date();
            const defaultName = now.getFullYear() +
                        String(now.getMonth() + 1).padStart(2, '0') +
                        String(now.getDate()).padStart(2, '0') +
                        '_' +
                        String(now.getHours()).padStart(2, '0') +
                        ':' +
                        String(now.getMinutes()).padStart(2, '0') +
                        ':' +
                        String(now.getSeconds()).padStart(2, '0');

            StorageManager.createScheme(defaultName, { actions });
            updateSchemeList();
            showToast('保存成功');
        }
    };

    // 编辑管理器
    const EditManager = {
        currentPanelPosition: null,
        hasUnsavedChanges: false,
        initialState: null,

        enter(name, editBtn) {
            this.exit();
            this.hasUnsavedChanges = false;

            editBtn.textContent = '退出';
            editBtn.style.background = '#ff4d4f';
            editBtn.setAttribute('data-editing', 'true');

            const scheme = StorageManager.getScheme(name);
            actions = [...scheme.actions];

            this.initialState = {
                infiniteLoop: scheme.infiniteLoop,
                loopCount: scheme.loopCount,
                loopDelay: scheme.loopDelay,
                actions: JSON.stringify(actions)
            };

            this.createTopEditPanel(name, scheme);
            ClickPointManager.createPoints(actions);
        },

        markAsChanged() {
            this.hasUnsavedChanges = true;
        },

        exit() {
            const editBtn = document.querySelector('.edit-btn[data-editing="true"]');
            if (!editBtn) return;

            // 检查是否有实际改动
            const currentState = this.getCurrentState();
            const hasChanges = this.initialState && (
                currentState.infiniteLoop !== this.initialState.infiniteLoop ||
                currentState.loopCount !== this.initialState.loopCount ||
                currentState.loopDelay !== this.initialState.loopDelay ||
                currentState.actions !== this.initialState.actions
            );

            if (hasChanges) {
                this.saveEditState();
                StorageManager.forceSave(); // 确保立即保存
                showToast('已保存编辑');
            }

            // 保存当前面板位置
            const panel = document.querySelector('.top-edit-panel');
            if (panel) {
                this.currentPanelPosition = {
                    left: panel.style.left,
                    top: panel.style.top
                };
            }

            ClickPointManager.cleanup();
            document.querySelector('.top-edit-panel')?.remove();
            editBtn.textContent = '编辑';
            editBtn.style.background = '#52c41a';
            editBtn.removeAttribute('data-editing');
            editModeRecording = false;
            this.hasUnsavedChanges = false;
            this.initialState = null;
        },

        getCurrentState() {
            const infiniteLoop = document.querySelector('#infiniteLoop');
            const loopCount = document.querySelector('#loopCount');
            const loopDelay = document.querySelector('#loopDelay');

            return {
                infiniteLoop: infiniteLoop ? infiniteLoop.checked : false,
                loopCount: loopCount ? parseInt(loopCount.value) || 1 : 1,
                loopDelay: loopDelay ? parseInt(loopDelay.value) || 0 : 0,
                actions: JSON.stringify(actions)
            };
        },

        // 新增:更新点击点位置的通用方法
        updateClickPoints() {
            document.querySelectorAll('.click-point').forEach((point, index) => {
                if (index < actions.length) {
                    actions[index].x = parseInt(point.style.left);
                    actions[index].y = parseInt(point.style.top);
                }
            });
        },

        // 新增:保存方案的通用方法
        saveSchemeWithPoints(name, scheme) {
            this.updateClickPoints();
            scheme.actions = actions;
            StorageManager.saveScheme(name, scheme);
        },

        saveEditState() {
            const editBtn = document.querySelector('.edit-btn[data-editing="true"]');
            if (!editBtn) return;

            const name = editBtn.closest('[data-scheme]').getAttribute('data-scheme');
            const scheme = StorageManager.getScheme(name);

            // 更新循环设置
            const infiniteLoop = document.querySelector('#infiniteLoop');
            const loopCount = document.querySelector('#loopCount');
            const loopDelay = document.querySelector('#loopDelay');

            if (infiniteLoop && loopCount && loopDelay) {
                scheme.infiniteLoop = infiniteLoop.checked;
                scheme.loopCount = parseInt(loopCount.value) || 1;
                scheme.loopDelay = parseInt(loopDelay.value) || 0;
            }

            this.saveSchemeWithPoints(name, scheme);
        },

        createTopEditPanel(name, scheme) {
            const topPanel = document.createElement('div');
            topPanel.className = 'top-edit-panel';

            // 如果有保存的位置,应用它
            if (this.currentPanelPosition) {
                topPanel.style.left = this.currentPanelPosition.left;
                topPanel.style.top = this.currentPanelPosition.top;
            }

            // 应用全局设置的样式
            topPanel.style.background = `rgba(255, 255, 255, ${settings.opacity})`;
            topPanel.style.backdropFilter = settings.useBlur ? 'blur(4px)' : 'none';

            // 添加标题和按钮
            const headerDiv = document.createElement('div');
            headerDiv.style.cssText = `
                width: 100%;
                display: flex;
                justify-content: space-between;
                align-items: center;
                margin-bottom: 10px;
                padding-bottom: 10px;
                border-bottom: 1px solid #eee;
            `;

            const exitBtn = document.createElement('button');
            exitBtn.textContent = '退出编辑';
            exitBtn.style.cssText = `
                padding: 4px 12px;
                background: #ff4d4f;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 12px;
            `;
            exitBtn.onclick = this.exit.bind(this);

            const addActionBtn = document.createElement('button');
            addActionBtn.textContent = '添加操作';
            addActionBtn.style.cssText = `
                padding: 4px 12px;
                background: #1890ff;
                color: white;
                border: none;
                border-radius: 4px;
                cursor: pointer;
                font-size: 12px;
                margin-left: 8px;
            `;

            addActionBtn.onclick = (e) => {
                e.stopPropagation();
                if (!editModeRecording) {
                    editModeRecording = true;
                    startTime = Date.now();
                    addActionBtn.textContent = '停止添加';
                    addActionBtn.style.background = '#ff4d4f';
                } else {
                    editModeRecording = false;
                    addActionBtn.textContent = '添加操作';
                    addActionBtn.style.background = '#1890ff';
                    if (actions.length > 0) {
                        this.markAsChanged();
                        // 保存当前面板位置
                        const panel = document.querySelector('.top-edit-panel');
                        if (panel) {
                            this.currentPanelPosition = {
                                left: panel.style.left,
                                top: panel.style.top
                            };
                        }
                        this.mergeActionsIntoScheme(name);
                    }
                }
            };

            headerDiv.appendChild(exitBtn);
            headerDiv.appendChild(addActionBtn);
            topPanel.appendChild(headerDiv);

            // 添加循环设置面板
            const loopSettingsDiv = document.createElement('div');
            loopSettingsDiv.style.cssText = `
                width: 100%;
                padding: 10px;
                background: #f5f5f5;
                border-radius: 4px;
                margin-bottom: 10px;
            `;

            loopSettingsDiv.innerHTML = `
                <div style="font-size: 14px; margin-bottom: 8px;">循环设置</div>
                <div style="display: flex; gap: 10px; align-items: center;">
                    <label style="display: flex; align-items: center; gap: 4px; cursor: pointer;">
                        <input type="checkbox" id="infiniteLoop" class="custom-checkbox"
                            ${scheme.infiniteLoop ? 'checked' : ''}>
                        <span style="user-select: none;">无限循环</span>
                    </label>
                    <div style="display: flex; align-items: center; gap: 4px;">
                        <span>循环次数:</span>
                        <input type="number" id="loopCount" value="${scheme.loopCount || 1}" min="1"
                            style="width: 60px; padding: 2px 4px;" ${scheme.infiniteLoop ? 'disabled' : ''}>
                    </div>
                    <div style="display: flex; align-items: center; gap: 4px;">
                        <span>循环间隔:</span>
                        <input type="number" id="loopDelay" value="${scheme.loopDelay || 0}" min="0"
                            style="width: 60px; padding: 2px 4px;">
                        <span>ms</span>
                    </div>
                </div>
            `;

            topPanel.appendChild(loopSettingsDiv);

            // 添加点击点列表
            scheme.actions.forEach((action, index) => {
                const pointItem = this.createPointItem(action, index, name);
                topPanel.appendChild(pointItem);
            });

            document.body.appendChild(topPanel);
            this.addLoopSettingsListeners(name);
            makeElementDraggable(topPanel);
        },

        createPointItem(action, index, name) {
            const pointItem = document.createElement('div');
            pointItem.className = `point-item${action.isNewAction ? ' new-action' : ''}`;
            pointItem.setAttribute('data-point-index', index);
            pointItem.innerHTML = `
                <div class="edit-point">${index + 1}</div>
                <div style="display: flex; flex-direction: column; font-size: 12px; flex-grow: 1;">
                    <div class="point-data" data-point-index="${index}">
                        <span>次数: </span>
                        <input type="text" class="point-input" data-field="clickCount" value="${action.clickCount || 1}">
                    </div>
                    <div class="point-data" data-point-index="${index}">
                        <span>延迟: </span>
                        <input type="text" class="point-input" data-field="preDelay" value="${action.preDelay || 0}">
                        <span>ms</span>
                    </div>
                </div>
            `;

            // 添加输入框事件监听
            const inputs = pointItem.querySelectorAll('.point-input');
            inputs.forEach(input => {
                input.addEventListener('input', (e) => {
                    e.target.value = e.target.value.replace(/[^0-9]/g, '');
                });

                input.addEventListener('blur', (e) => {
                    if (e.target.value === '') {
                        e.target.value = '0';
                    }
                    const value = parseInt(e.target.value) || 0;
                    actions[index][input.dataset.field] = value;
                });
            });

            // 为每point-data添加点击事件
            const pointDatas = pointItem.querySelectorAll('.point-data');
            pointDatas.forEach(pointData => {
                pointData.addEventListener('click', handlePointClick);
                pointData.addEventListener('mousedown', handlePointClick);
            });

            // 为整point-item添加点击事件
            pointItem.addEventListener('click', handlePointClick);
            pointItem.addEventListener('mousedown', handlePointClick);

            function handlePointClick(e) {
                e.stopPropagation();
                const pointData = e.target.closest('.point-data') || e.target.closest('.point-item');
                if (!pointData) return;

                const index = parseInt(pointData.getAttribute('data-point-index'));
                const clickPoint = document.querySelector(`.click-point[data-point-index="${index}"]`);
                if (clickPoint) {
                    // 清除其他点位的高亮
                    document.querySelectorAll('.click-point').forEach(point => {
                        point.classList.remove('active');
                        point.style.zIndex = CONSTANTS.Z_INDEX.BASE;
                    });

                    // 高亮并置顶当前点位
                    clickPoint.classList.add('active');
                    clickPoint.style.zIndex = CONSTANTS.Z_INDEX.ACTIVE;

                    // 高亮编辑窗口中的点位
                    document.querySelectorAll('.point-item').forEach(item => {
                        const itemIndex = parseInt(item.getAttribute('data-point-index'));
                        item.classList.toggle('active', itemIndex === index);
                    });
                }
            }

            // 为输入框添加点击事件
            inputs.forEach(input => {
                input.addEventListener('click', (e) => {
                    e.stopPropagation();
                });

                input.addEventListener('focus', (e) => {
                    const pointData = e.target.closest('.point-data');
                    if (pointData) {
                        handlePointClick(e);
                    }
                });
            });

            return pointItem;
        },

        mergeActionsIntoScheme(name) {
            const scheme = StorageManager.getScheme(name);

            // 清除新添加标记
            actions.forEach(action => {
                delete action.isNewAction;
            });

            this.saveSchemeWithPoints(name, scheme);
            showToast('操作已保存');

            // 重新进入编辑模式刷新界面
            const editBtn = document.querySelector(`[data-scheme="${name}"] .edit-btn`);
            this.exit();
            this.enter(name, editBtn);
        },

        addLoopSettingsListeners(name) {
            const infiniteLoop = document.querySelector('#infiniteLoop');
            const loopCount = document.querySelector('#loopCount');
            const loopDelay = document.querySelector('#loopDelay');

            infiniteLoop.addEventListener('change', (e) => {
                loopCount.disabled = e.target.checked;
                StorageManager.updateSchemeField(name, 'infiniteLoop', e.target.checked);
            });

            [loopCount, loopDelay].forEach(input => {
                input.addEventListener('input', (e) => {
                    e.target.value = e.target.value.replace(/[^0-9]/g, '');
                });

                input.addEventListener('change', (e) => {
                    const value = parseInt(e.target.value) || 0;
                    e.target.value = value;
                    StorageManager.updateSchemeField(name, e.target.id, value);
                });
            });
        }
    };

    // 运行管理器
    const RunManager = {
        async run(name, runBtn) {
            if (isRunning) {
                await this.stop(runBtn);
                return;
            }

            await this.saveEditState(name);
            EditManager.exit();

            const scheme = await this.start(name, runBtn);
            if (!scheme) return;

            try {
                await this.execute(scheme);
            } catch (error) {
                if (error.message !== 'Delay aborted') {
                    console.error('运行出错:', error);
                }
            } finally {
                await this.resetState(runBtn);
            }
        },

        async stop(runBtn) {
            stopRunning = true;
            if (abortController) {
                abortController.abort();
                abortController = null;
            }
            isRunning = false;
            runBtn.textContent = '运行';
            runBtn.style.background = '#1890ff';
        },

        async start(name, runBtn) {
            const scheme = StorageManager.getScheme(name);
            if (!scheme) return null;

            isRunning = true;
            stopRunning = false;
            abortController = new AbortController();
            runBtn.textContent = '停止';
            runBtn.style.background = '#ff4d4f';

            return scheme;
        },

        async execute(scheme) {
            let loopCount = 0;
            while (!stopRunning && (scheme.infiniteLoop || loopCount < (scheme.loopCount || 1))) {
                if (loopCount > 0 && scheme.loopDelay) {
                    await this.interruptibleDelay(scheme.loopDelay);
                }

                await this.executeActions(scheme.actions);
                loopCount++;
            }
        },

        async executeActions(actions) {
            for (let action of actions) {
                if (stopRunning) break;
                await this.executeAction(action);
            }
        },

        async executeAction(action) {
            if (action.preDelay > 0) {
                await this.interruptibleDelay(action.preDelay);
            }

            const clickCount = action.clickCount || 1;
            for (let i = 0; i < clickCount; i++) {
                if (stopRunning) break;

                ClickEffectManager.show(action.x, action.y);
                const element = document.elementFromPoint(action.x, action.y);
                if (element) {
                    // 使用更底层的事件触发方法
                    const event = new MouseEvent('click', {
                        bubbles: true,
                        cancelable: true,
                        view: window
                    });
                    element.dispatchEvent(event);
                }

                if (i < clickCount - 1) {
                    await this.interruptibleDelay(100);
                }
            }
        },

        async saveEditState(name) {
            const editBtn = document.querySelector(`[data-scheme="${name}"] .edit-btn`);
            if (editBtn?.textContent !== '退出') return;

            const topPanel = document.querySelector('.top-edit-panel');
            if (!topPanel) return;

            const scheme = StorageManager.getScheme(name);
            scheme.infiniteLoop = topPanel.querySelector('#infiniteLoop').checked;
            scheme.loopCount = parseInt(topPanel.querySelector('#loopCount').value) || 1;
            scheme.loopDelay = parseInt(topPanel.querySelector('#loopDelay').value) || 0;

            // 更新点击点位置
            document.querySelectorAll('.click-point').forEach((point, index) => {
                if (index < actions.length) {
                    actions[index].x = parseInt(point.style.left);
                    actions[index].y = parseInt(point.style.top);
                }
            });

            scheme.actions = actions;
            StorageManager.saveScheme(name, scheme);
        },

        async resetState(runBtn) {
            isRunning = false;
            stopRunning = false;
            abortController = null;
            runBtn.textContent = '运行';
            runBtn.style.background = '#1890ff';
        },

        interruptibleDelay(ms) {
            if (!abortController) {
                abortController = new AbortController();
            }
            return new Promise((resolve, reject) => {
                const timeoutId = setTimeout(resolve, ms);
                abortController.signal.addEventListener('abort', () => {
                    clearTimeout(timeoutId);
                    reject(new Error('Delay aborted'));
                });
            });
        }
    };

    // 工具函数
    function showToast(message) {
        const toast = document.createElement('div');
        toast.className = 'toast-message';
        toast.textContent = message;
        document.body.appendChild(toast);
        setTimeout(() => toast.remove(), 1500);
    }

    // 创建录制界面
    function createRecorderPanel() {
        console.log('创建录制界面');
        // 先移除可能存在的旧面板
        const oldPanel = document.getElementById('recorder-panel');
        if (oldPanel) {
            oldPanel.remove();
        }

        const recorder = document.createElement('div');
        recorder.id = 'recorder-panel';

        // 根据设置应用样式
        function applyPanelStyles(panel) {
            // 保存当前位置
            const currentStyles = {
                left: panel.style.left,
                top: panel.style.top,
                right: panel.style.right
            };

            // 应用新样式时保持原有置
            const newStyles = `
                position: fixed;
                top: ${currentStyles.top || '20px'};
                ${currentStyles.left ? `left: ${currentStyles.left};` : `right: ${currentStyles.right || '20px'};`}
                background: rgba(255, 255, 255, ${settings.opacity});
                border: 1px solid rgba(204, 204, 204, 0.3);
                padding: 10px;
                border-radius: 5px;
                box-shadow: 0 0 10px rgba(0,0,0,0.08);
                z-index: ${CONSTANTS.Z_INDEX.EDIT_PANEL};
                width: 280px;
                cursor: move;
                color: ${COLORS.TEXT}; // 固定文字颜色
                ${settings.useBlur ? 'backdrop-filter: blur(4px);' : ''}
            `;
            panel.style.cssText = newStyles;
        }
        applyPanelStyles(recorder);

        recorder.innerHTML = `
            <div style="display: flex; align-items: center; margin-bottom: 10px;">
                <div id="record-status" style="
                    width: 10px;
                    height: 10px;
                    border-radius: 50%;
                    background: #ccc;
                    display: inline-block;
                    margin-right: 5px;
                "></div>
                <button id="recordToggle" style="
                    padding: 4px 12px;
                    background: #52c41a;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                ">开始录制</button>
                <div style="flex-grow: 1"></div>
                <button id="settingsToggle" style="
                    padding: 4px 8px;
                    background: #1890ff;
                    color: white;
                    border: none;
                    border-radius: 4px;
                    cursor: pointer;
                    font-size: 12px;
                    margin-left: 8px;
                ">设置</button>
            </div>
            <div id="settingsPanel" style="
                display: none;
                padding: 10px;
                background: rgba(245, 245, 245, 0.7);
                border-radius: 4px;
                margin-bottom: 10px;
            ">
                <div style="margin-bottom: 8px;">
                    <label style="display: flex; align-items: center; gap: 8px;">
                        <span>透明度:</span>
                        <div style="flex: 1; display: flex; align-items: center; gap: 8px;">
                            <input type="range" id="opacitySlider" min="0" max="100" value="${settings.opacity * 100}"
                                style="flex: 1; min-width: 0;">
                            <span id="opacityValue" style="min-width: 36px; text-align: right;">${Math.round(settings.opacity * 100)}%</span>
                        </div>
                    </label>
                </div>
                <div style="margin-bottom: 8px;">
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <label class="checkbox-wrapper" style="display: flex; align-items: center; cursor: pointer;">
                            <input type="checkbox" id="useBlurToggle" class="custom-checkbox" ${settings.useBlur ? 'checked' : ''}>
                        </label>
                        <span style="user-select: none;">启用毛玻璃效果</span>
                    </div>
                </div>
                <div style="margin-bottom: 8px;">
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <label class="checkbox-wrapper" style="display: flex; align-items: center; cursor: pointer;">
                            <input type="checkbox" id="showAnimationToggle" class="custom-checkbox" ${settings.showClickAnimation ? 'checked' : ''}>
                        </label>
                        <span style="user-select: none;">显示点击动画</span>
                    </div>
                </div>
                <div style="margin-bottom: 8px;">
                    <label style="display: flex; align-items: center; justify-content: space-between;">
                        <span>动画数量限制:</span>
                        <input type="number" id="maxAnimationsInput" value="${settings.maxAnimations}" min="0" max="50"
                            style="width: 60px; padding: 2px 4px; border: 1px solid #d9d9d9; border-radius: 2px;">
                        <span style="font-size: 12px; color: #666;">(0为不限制)</span>
                    </label>
                    <div style="font-size: 11px; color: #999; margin-top: 4px;">
                        点击频率过高时动画太多可能造成卡顿
                    </div>
                </div>
            </div>
            <div id="rightPanelSchemeList" style="
                max-height: 300px;
                overflow-y: auto;
            "></div>
        `;

        document.body.appendChild(recorder);
        console.log('录制界面已创建');

        // 初始化拖拽
        makeElementDraggable(recorder);

        // 初始化事件
        const recordToggleBtn = document.getElementById('recordToggle');
        const settingsToggleBtn = document.getElementById('settingsToggle');
        const settingsPanel = document.getElementById('settingsPanel');
        const opacitySlider = document.getElementById('opacitySlider');
        const opacityValue = document.getElementById('opacityValue');
        const useBlurToggle = document.getElementById('useBlurToggle');
        const showAnimationToggle = document.getElementById('showAnimationToggle');
        const maxAnimationsInput = document.getElementById('maxAnimationsInput');

        if (recordToggleBtn) {
            recordToggleBtn.addEventListener('click', (e) => {
                e.stopPropagation();
                const isEditing = editModeRecording || document.querySelector('.edit-btn[data-editing="true"]');
                if (isEditing) {
                    // 保存编辑状态
                    const editBtn = document.querySelector('.edit-btn[data-editing="true"]');
                    if (editBtn) {
                        const name = editBtn.closest('[data-scheme]').getAttribute('data-scheme');
                        const scheme = StorageManager.getScheme(name);

                        // 保存循环设置
                        const infiniteLoop = document.querySelector('#infiniteLoop');
                        const loopCount = document.querySelector('#loopCount');
                        const loopDelay = document.querySelector('#loopDelay');
                        if (infiniteLoop && loopCount && loopDelay) {
                            scheme.infiniteLoop = infiniteLoop.checked;
                            scheme.loopCount = parseInt(loopCount.value) || 1;
                            scheme.loopDelay = parseInt(loopDelay.value) || 0;
                        }

                        // 保存点击点的位置
                        document.querySelectorAll('.click-point').forEach((point, index) => {
                            if (index < actions.length) {
                                actions[index].x = parseInt(point.style.left);
                                actions[index].y = parseInt(point.style.top);
                            }
                        });

                        scheme.actions = actions;
                        StorageManager.saveScheme(name, scheme);
                    }

                    // 退出编辑模式
                    EditManager.exit();
                    editModeRecording = false;
                    recordToggleBtn.textContent = '开始录制';
                    recordToggleBtn.style.background = '#52c41a';
                    showToast('已保存编辑');
                } else if (recording) {
                    RecorderManager.stop();
                } else {
                    RecorderManager.start();
                }
            });
        }

        // 设置面板切换
        settingsToggleBtn.addEventListener('click', () => {
            const isHidden = settingsPanel.style.display === 'none';
            settingsPanel.style.display = isHidden ? 'block' : 'none';
            settingsToggleBtn.style.background = isHidden ? '#ff4d4f' : '#1890ff';
        });

        // 透明度滑块
        opacitySlider.addEventListener('input', (e) => {
            const value = e.target.value / 100;
            settings.opacity = value;
            opacityValue.textContent = `${Math.round(value * 100)}%`;
            localStorage.setItem('recorder_opacity', value);
            applyPanelStyles(recorder);
            updateEditPanelStyles();
        });

        // 毛玻璃效果切换
        useBlurToggle.addEventListener('change', (e) => {
            settings.useBlur = e.target.checked;
            localStorage.setItem('recorder_use_blur', e.target.checked);
            applyPanelStyles(recorder);
            updateEditPanelStyles();
        });

        // 点击动画开关
        showAnimationToggle.addEventListener('change', (e) => {
            settings.showClickAnimation = e.target.checked;
            localStorage.setItem('recorder_show_click_animation', e.target.checked);
        });

        // 动画数量限制
        maxAnimationsInput.addEventListener('input', (e) => {
            let value = parseInt(e.target.value) || 0;
            // 限制输入范围
            if (value < 0) value = 0;
            if (value > 50) value = 50;
            e.target.value = value;
            settings.maxAnimations = value;
            localStorage.setItem('recorder_max_animations', value);
        });

        // 更新方案列表
        updateSchemeList();
    }

    // 更新编辑面板样式
    function updateEditPanelStyles() {
        const editPanels = document.querySelectorAll('.top-edit-panel');
        editPanels.forEach(panel => {
            panel.style.background = `rgba(255, 255, 255, ${settings.opacity})`;
            panel.style.backdropFilter = settings.useBlur ? 'blur(4px)' : 'none';
        });
    }

    // 更新方案列表
    function updateSchemeList() {
        const savedSchemes = StorageManager.getAllSchemes();
        const rightPanelList = document.getElementById('rightPanelSchemeList');
        rightPanelList.innerHTML = '';

        if (Object.keys(savedSchemes).length === 0) {
            const emptyTip = document.createElement('div');
            emptyTip.style.cssText = `
                padding: 20px;
                text-align: center;
                color: #999;
                font-size: 13px;
            `;
            emptyTip.textContent = '无录制方案';
            rightPanelList.appendChild(emptyTip);
            return;
        }

        Object.entries(savedSchemes)
            .sort(([, a], [, b]) => b.timestamp - a.timestamp)
            .forEach(([name, scheme]) => {
                const schemeDiv = document.createElement('div');
                schemeDiv.setAttribute('data-scheme', name);
                schemeDiv.style.cssText = `
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    padding: 8px;
                    border-bottom: 1px solid #eee;
                    margin-bottom: 2px;
                    transition: all 0.2s;
                `;

                schemeDiv.innerHTML = `
                    <div style="display: flex; flex-direction: column; flex: 1; min-width: 0; margin-right: 8px;">
                        <span class="scheme-name" data-original-name="${name}">
                            ${name}
                        </span>
                        <span style="font-size: 11px; color: #666; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px;">
                            ${scheme.site || '未知网站'}
                        </span>
                    </div>
                    <div style="display: flex; gap: 4px;">
                        <button class="edit-btn" style="padding: 3px 8px; background: #52c41a; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 12px; transition: all 0.2s;">编辑</button>
                        <button class="run-btn" style="padding: 3px 8px; background: #1890ff; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 12px; transition: all 0.2s;">运行</button>
                        <button class="delete-btn" style="padding: 3px 8px; background: #ff4d4f; color: white; border: none; border-radius: 3px; cursor: pointer; font-size: 12px; transition: all 0.2s;">删除</button>
                    </div>`;

                // 添加双击重命名功能
                const schemeName = schemeDiv.querySelector('.scheme-name');
                schemeName.addEventListener('dblclick', (e) => {
                    e.stopPropagation();
                    const originalName = schemeName.getAttribute('data-original-name');
                    const input = document.createElement('input');
                    input.className = 'scheme-name-input';
                    input.value = originalName;
                    schemeName.replaceWith(input);
                    input.focus();
                    input.select();

                    const handleRename = () => {
                        const newName = input.value.trim();
                        if (newName && newName !== originalName) {
                            if (StorageManager.renameScheme(originalName, newName)) {
                                showToast('重命名成功');
                                updateSchemeList();
                            } else {
                                showToast('该名称已存在');
                                input.replaceWith(schemeName);
                            }
                        } else {
                            input.replaceWith(schemeName);
                        }
                    };

                    input.addEventListener('blur', handleRename);
                    input.addEventListener('keydown', (e) => {
                        if (e.key === 'Enter') {
                            e.preventDefault();
                            handleRename();
                        } else if (e.key === 'Escape') {
                            e.preventDefault();
                            input.replaceWith(schemeName);
                        }
                    });
                });

                // 添加按钮悬停效果
                const buttons = schemeDiv.querySelectorAll('button');
                buttons.forEach(btn => {
                    btn.addEventListener('mouseover', () => {
                        btn.style.opacity = '0.8';
                    });
                    btn.addEventListener('mouseout', () => {
                        btn.style.opacity = '1';
                    });
                });

                // 绑定按钮事件
                const editBtn = schemeDiv.querySelector('.edit-btn');
                const runBtn = schemeDiv.querySelector('.run-btn');
                const deleteBtn = schemeDiv.querySelector('.delete-btn');

                editBtn.onclick = (e) => {
                    e.stopPropagation();
                    if (editBtn.textContent === '退出') {
                        EditManager.exit();
                    } else {
                        EditManager.enter(name, editBtn);
                    }
                };
                runBtn.onclick = () => RunManager.run(name, runBtn);
                deleteBtn.onclick = () => handleDelete(name, deleteBtn);

                // 添加方案悬停效果
                schemeDiv.addEventListener('mouseover', () => {
                    schemeDiv.style.backgroundColor = '#f5f5f5';
                });
                schemeDiv.addEventListener('mouseout', () => {
                    schemeDiv.style.backgroundColor = 'transparent';
                });

                rightPanelList.appendChild(schemeDiv);
            });
    }

    // 删除功能
    function handleDelete(name, deleteBtn) {
        if (deleteBtn.textContent === '删除') {
            deleteBtn.textContent = '取消';
            deleteBtn.style.background = '#8c8c8c';

            const confirmBtn = document.createElement('button');
            confirmBtn.textContent = '确定';
            confirmBtn.style.cssText = `
                padding: 3px 8px;
                background: #ff4d4f;
                color: white;
                border: none;
                border-radius: 3px;
                cursor: pointer;
                font-size: 12px;
            `;

            confirmBtn.onclick = () => {
                StorageManager.deleteScheme(name);
                updateSchemeList();
                showToast('已删除');
            };

            deleteBtn.parentElement.insertBefore(confirmBtn, deleteBtn);

            deleteBtn.onclick = () => {
                confirmBtn.remove();
                deleteBtn.textContent = '删除';
                deleteBtn.style.background = '#ff4d4f';
                deleteBtn.onclick = () => handleDelete(name, deleteBtn);
            };
        }
    }

    // 初始化
    function init() {
        console.log('初始化开始');
        // 确保只初始化一次
        if (window.recorderInitialized) {
            return;
        }
        window.recorderInitialized = true;

        try {
            // 初始化各个组件
            console.log('初始化样式');
            initStyles();

            console.log('初始化点击效果');
            ClickEffectManager.init();

            console.log('初始化点击点管理器');
            ClickPointManager.init();

            // 创建录制界面
            console.log('创建录制界面');
            createRecorderPanel();

            // 初始化事件监听器
            console.log('初始化事件监听器');
            initializeEventListeners();

            // 确保录制界面存在
            setInterval(() => {
                const panel = document.getElementById('recorder-panel');
                if (!panel) {
                    console.log('重新创建录制界面');
                    createRecorderPanel();
                }
            }, 1000);

            console.log('初始化完成');
        } catch (error) {
            console.error('初始化出错:', error);
        }
    }

    // 初始化事件监听
    function initializeEventListeners() {
        // 记录点击事件
        document.addEventListener('click', (e) => {
            // 检查是否点击在面板或按钮上
            const isOnPanel = e.target.closest('#recorder-panel') ||
                            e.target.closest('.top-edit-panel') ||
                            e.target.closest('.click-point') ||
                            e.target.closest('button');

            if (isOnPanel) return;

            if (editModeRecording) {
                const currentTime = Date.now();
                const lastAction = actions[actions.length - 1];
                const newAction = {
                    type: 'click',
                    x: e.clientX,
                    y: e.clientY,
                    timestamp: currentTime - startTime,
                    clickCount: 1,
                    preDelay: actions.length === 0 ? 1000 : Math.max(0, currentTime - (lastAction ? lastAction.timestamp + startTime : startTime)),
                    isNewAction: true
                };
                actions.push(newAction);

                // 实时显示点击点
                ClickPointManager.addNewPoint(newAction, actions.length - 1);
                ClickEffectManager.show(e.clientX, e.clientY);
            } else if (recording) {
                const currentTime = Date.now() - startTime;
                actions.push({
                    type: 'click',
                    x: e.clientX,
                    y: e.clientY,
                    timestamp: currentTime,
                    clickCount: 1,
                    preDelay: actions.length === 0 ? 0 : currentTime - actions[actions.length - 1].timestamp
                });
                ClickEffectManager.show(e.clientX, e.clientY);
            }
        }, true); // 使用捕获阶段来确保能捕获到所有点击
    }

    // 可拖动元素功能
    function makeElementDraggable(element) {
        let isDragging = false;
        let startX, startY;
        let elementX, elementY;

        element.addEventListener('mousedown', (e) => {
            if (e.target.tagName === 'BUTTON' || e.target.tagName === 'INPUT') {
                return;
            }

            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            elementX = element.offsetLeft;
            elementY = element.offsetTop;

            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;

            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;

            element.style.left = `${elementX + deltaX}px`;
            element.style.top = `${elementY + deltaY}px`;
        });

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

    // 等待页面加载完成后再初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            console.log('DOMContentLoaded');
            setTimeout(init, 0);
        });
    } else {
        console.log('直接初始化');
        setTimeout(init, 0);
    }

    // 修改页面卸载事件,确保数据保存
    window.addEventListener('beforeunload', () => {
        StorageManager.forceSave();
    });

})();