MediaWiki

MediaWiki:Card.js

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献2025年10月23日 (四) 17:31的版本

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
(function() {
    'use strict';
    
    // 加载CSS
    mw.loader.load('/index.php?title=Mediawiki:Card.css&action=raw&ctype=text/css', 'text/css');
    
    // 状态管理
    const state = {
        currentFighter: '',
        fighters: [],
        cards: [],
        currentCard: null,
        defaultInfo: {
            order: '',
            ego: ''
        },
        editMode: 'base',
        currentGod: 'circen'
    };
    
    // 卡牌数据结构
    function createEmptyCard() {
        return {
            name: '',
            base: {
                displayname: '',
                art: '',
                group: '',
                rarity: '',
                god: '',
                ap: '',
                type: '',
                dict: '',
                desc_global: '',
                sub: '',
                isinspiration: 0,
                isgod_inspiration: 0
            },
            var: {
                inspiration: [],
                god_inspiration: {
                    circen: [],
                    diallos: [],
                    nihilum: [],
                    secred: [],
                    vitor: []
                }
            }
        };
    }
    
    // 创建元素辅助函数
    function createElement(tag, className, attributes = {}) {
        const el = document.createElement(tag);
        if (className) el.className = className;
        Object.entries(attributes).forEach(([key, value]) => {
            if (key === 'textContent') {
                el.textContent = value;
            } else if (key.startsWith('on')) {
                el.addEventListener(key.substring(2).toLowerCase(), value);
            } else {
                el.setAttribute(key, value);
            }
        });
        return el;
    }
    
    // 创建自定义下拉选择器
    function createSelect(options, selectedValue, onChange) {
        const select = createElement('div', 'form-select');
        select.textContent = selectedValue || options[0] || '请选择';
        select.setAttribute('tabindex', '0');
        
        const dropdown = createElement('div', 'dropdown-menu');
        
        options.forEach(option => {
            const item = createElement('div', 'dropdown-item');
            item.textContent = option;
            item.addEventListener('click', () => {
                select.textContent = option;
                dropdown.style.display = 'none';
                if (onChange) onChange(option);
            });
            dropdown.appendChild(item);
        });
        
        select.addEventListener('click', (e) => {
            e.stopPropagation();
            dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
            const rect = select.getBoundingClientRect();
            dropdown.style.top = rect.bottom + 'px';
            dropdown.style.left = rect.left + 'px';
            dropdown.style.width = rect.width + 'px';
        });
        
        document.addEventListener('click', () => {
            dropdown.style.display = 'none';
        });
        
        const wrapper = createElement('div');
        wrapper.style.position = 'relative';
        wrapper.appendChild(select);
        document.body.appendChild(dropdown);
        
        return { wrapper, select, dropdown };
    }
    
    // 创建自定义输入框
    function createInput(type, value, onChange, placeholder = '') {
        const input = createElement('div', 'form-input');
        input.setAttribute('contenteditable', 'true');
        input.textContent = value || '';
        input.setAttribute('data-placeholder', placeholder);
        input.style.minHeight = type === 'textarea' ? '100px' : 'auto';
        
        if (!value) {
            input.style.color = '#999';
            input.textContent = placeholder;
        }
        
        input.addEventListener('focus', () => {
            if (input.textContent === placeholder) {
                input.textContent = '';
                input.style.color = '#333';
            }
        });
        
        input.addEventListener('blur', () => {
            if (!input.textContent.trim()) {
                input.textContent = placeholder;
                input.style.color = '#999';
            }
            if (onChange) onChange(input.textContent === placeholder ? '' : input.textContent);
        });
        
        input.addEventListener('input', () => {
            if (onChange && input.textContent !== placeholder) {
                onChange(input.textContent);
            }
        });
        
        return input;
    }
    
    // 创建复选框
    function createCheckbox(checked, onChange) {
        const wrapper = createElement('div', 'checkbox-wrapper');
        const checkbox = createElement('div', checked ? 'checkbox checked' : 'checkbox');
        const label = createElement('div', '');
        label.textContent = '是';
        
        wrapper.addEventListener('click', () => {
            const newChecked = !checkbox.classList.contains('checked');
            if (newChecked) {
                checkbox.classList.add('checked');
            } else {
                checkbox.classList.remove('checked');
            }
            if (onChange) onChange(newChecked ? 1 : 0);
        });
        
        wrapper.appendChild(checkbox);
        wrapper.appendChild(label);
        return wrapper;
    }
    
    // 创建表单组
    function createFormGroup(label, control) {
        const group = createElement('div', 'form-group');
        const labelEl = createElement('div', 'form-label');
        labelEl.textContent = label;
        group.appendChild(labelEl);
        group.appendChild(control);
        return group;
    }
    
    // 创建按钮
    function createButton(text, className, onClick) {
        const btn = createElement('div', 'btn ' + className);
        btn.textContent = text;
        btn.addEventListener('click', onClick);
        return btn;
    }
    
    // 文本插入辅助函数
    function insertTextAtCursor(element, text, wrap = false) {
        element.focus();
        const selection = window.getSelection();
        
        if (wrap && selection.rangeCount > 0) {
            const range = selection.getRangeAt(0);
            const selectedText = range.toString();
            
            if (selectedText) {
                range.deleteContents();
                const textNode = document.createTextNode(text.replace('选择文字', selectedText));
                range.insertNode(textNode);
                return;
            }
        }
        
        document.execCommand('insertText', false, text);
    }
    
    
    // 辅助函数:提取数值
    function extractNumberValue(text, key) {
        const pattern = new RegExp(key + '\\s*=\\s*(\\d+)', 'm');
        const match = text.match(pattern);
        return match ? parseInt(match[1]) : 0;
    }
    
    // 辅助函数:提取任意值(包括X、Ø等)
    function extractValue(text, key) {
        const pattern = new RegExp(key + '\\s*=\\s*([^,\\n]+)', 'm');
        const match = text.match(pattern);
        if (match) {
            let value = match[1].trim();
            // 移除引号
            value = value.replace(/^["']|["']$/g, '');
            return value;
        }
        return '';
    }
    
        // 解析灵光一闪/神光一闪列表(修复版)
    function parseInspirationList(content) {
        const inspirations = [];
        
        // 使用更精确的解析方法
        let depth = 0;
        let currentBlock = '';
        let inString = false;
        let escapeNext = false;
        let startNewBlock = false;
        
        for (let i = 0; i < content.length; i++) {
            const char = content[i];
            const prevChar = i > 0 ? content[i - 1] : '';
            
            // 处理转义字符
            if (escapeNext) {
                if (depth > 0) currentBlock += char;
                escapeNext = false;
                continue;
            }
            
            if (char === '\\') {
                escapeNext = true;
                if (depth > 0) currentBlock += char;
                continue;
            }
            
            // 处理字符串
            if (char === '"' && !escapeNext) {
                inString = !inString;
                if (depth > 0) currentBlock += char;
                continue;
            }
            
            if (inString) {
                if (depth > 0) currentBlock += char;
                continue;
            }
            
            // 处理括号
            if (char === '{') {
                if (depth === 0) {
                    startNewBlock = true;
                } else {
                    currentBlock += char;
                }
                depth++;
            } else if (char === '}') {
                depth--;
                if (depth === 0 && startNewBlock) {
                    // 解析完整的块
                    if (currentBlock.trim()) {
                        const inspiration = parseInspirationBlock(currentBlock);
                        if (Object.keys(inspiration).length > 0) {
                            inspirations.push(inspiration);
                        }
                    }
                    currentBlock = '';
                    startNewBlock = false;
                } else if (depth > 0) {
                    currentBlock += char;
                }
            } else if (depth > 0) {
                currentBlock += char;
            }
        }
        
        return inspirations;
    }
    
    // 解析单个灵光块
    function parseInspirationBlock(blockContent) {
        const inspiration = {};
        
        // 解析 ap
        const apPattern = /ap\s*=\s*(?:"([^"]*)"|'([^']*)'|(\d+)|([^,\n}\s]+))/;
        const apMatch = blockContent.match(apPattern);
        if (apMatch) {
            const apValue = apMatch[1] || apMatch[2] || apMatch[3] || apMatch[4];
            if (apValue !== undefined && apValue !== '') {
                inspiration.ap = apValue.trim();
            }
        }
        
        // 解析 type
        const typePattern = /type\s*=\s*"([^"]*)"/;
        const typeMatch = blockContent.match(typePattern);
        if (typeMatch && typeMatch[1]) {
            inspiration.type = typeMatch[1];
        }
        
        // 解析 desc_global
        const descPattern = /desc_global\s*=\s*"((?:[^"\\]|\\.)*)"/;
        const descMatch = blockContent.match(descPattern);
        if (descMatch && descMatch[1]) {
            inspiration.desc_global = descMatch[1].replace(/\\"/g, '"').replace(/\\n/g, '\n');
        }
        
        // 解析 dict
        const dictPattern = /dict\s*=\s*"((?:[^"\\]|\\.)*)"/;
        const dictMatch = blockContent.match(dictPattern);
        if (dictMatch && dictMatch[1]) {
            inspiration.dict = dictMatch[1].replace(/\\"/g, '"');
        }
        
        return inspiration;
    }
    
    // 解析Lua代码(修复版)
    function parseLuaCode(luaText) {
        const cards = [];
        const defaultInfo = {
            order: '',
            ego: ''
        };
        
        try {
            // 解析 card.order
            const orderMatch = luaText.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/);
            if (orderMatch) {
                defaultInfo.order = orderMatch[1];
            }
            
            // 解析 card.info.ego
            const egoMatch = luaText.match(/card\.info\s*=\s*\{[^}]*ego\s*=\s*"([^"]+)"/);
            if (egoMatch) {
                defaultInfo.ego = egoMatch[1];
            }
            
            // 使用更精确的方法解析每张卡牌
            const lines = luaText.split('\n');
            let currentCard = null;
            let inCard = false;
            let inBase = false;
            let inVar = false;
            let inInspiration = false;
            let inGodInspiration = false;
            let currentGod = null;
            let braceDepth = 0;
            let baseContent = '';
            let inspirationContent = '';
            let godInspirationContent = '';
            let godContent = '';
            
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i];
                const trimmed = line.trim();
                
                // 检测新卡牌开始
                const cardStartMatch = line.match(/^card\["([^"]+)"\]\s*=\s*\{/);
                if (cardStartMatch) {
                    inCard = true;
                    currentCard = createEmptyCard();
                    currentCard.name = cardStartMatch[1];
                    braceDepth = 1;
                    continue;
                }
                
                if (!inCard) continue;
                
                // 计算括号深度
                for (let char of line) {
                    if (char === '{') braceDepth++;
                    if (char === '}') braceDepth--;
                }
                
                // 检测 base 开始
                if (trimmed.startsWith('base = {')) {
                    inBase = true;
                    baseContent = '';
                    continue;
                }
                
                // 收集 base 内容
                if (inBase) {
                    if (trimmed === '},') {
                        inBase = false;
                        // 解析 base 内容
                        parseBaseContent(currentCard, baseContent);
                    } else {
                        baseContent += line + '\n';
                    }
                    continue;
                }
                
                // 检测 var 开始
                if (trimmed.startsWith('var = {')) {
                    inVar = true;
                    continue;
                }
                
                // 在 var 内部检测 inspiration
                if (inVar && trimmed.startsWith('inspiration = {')) {
                    inInspiration = true;
                    inspirationContent = '';
                    continue;
                }
                
                // 收集 inspiration 内容
                if (inInspiration) {
                    if (trimmed === '},') {
                        inInspiration = false;
                        currentCard.var.inspiration = parseInspirationList(inspirationContent);
                    } else {
                        inspirationContent += line + '\n';
                    }
                    continue;
                }
                
                // 检测 god_inspiration 开始
                if (inVar && trimmed.startsWith('god_inspiration = {')) {
                    inGodInspiration = true;
                    godInspirationContent = '';
                    continue;
                }
                
                // 在 god_inspiration 内部
                if (inGodInspiration) {
                    if (trimmed === '},') {
                        inGodInspiration = false;
                        inVar = false;
                    } else {
                        // 检测单个神明
                        const godMatch = trimmed.match(/^(circen|diallos|nihilum|secred|vitor)\s*=\s*\{/);
                        if (godMatch) {
                            if (currentGod && godContent) {
                                // 保存前一个神明的数据
                                currentCard.var.god_inspiration[currentGod] = parseInspirationList(godContent);
                            }
                            currentGod = godMatch[1];
                            godContent = '';
                        } else if (trimmed === '},') {
                            // 当前神明结束
                            if (currentGod && godContent) {
                                currentCard.var.god_inspiration[currentGod] = parseInspirationList(godContent);
                                currentGod = null;
                                godContent = '';
                            }
                        } else if (currentGod) {
                            godContent += line + '\n';
                        }
                    }
                    continue;
                }
                
                // 检测卡牌结束
                if (braceDepth === 0 && inCard) {
                    cards.push(currentCard);
                    currentCard = null;
                    inCard = false;
                    inBase = false;
                    inVar = false;
                    inInspiration = false;
                    inGodInspiration = false;
                    baseContent = '';
                    inspirationContent = '';
                    godContent = '';
                    currentGod = null;
                }
            }
            
            // 处理最后一张卡牌
            if (currentCard) {
                cards.push(currentCard);
            }
            
        } catch (error) {
            console.error('解析Lua代码失败:', error);
            console.error('错误堆栈:', error.stack);
        }
        
        return { cards, defaultInfo };
    }
    
    // 解析 base 内容
    function parseBaseContent(card, content) {
        card.base.displayname = extractStringValue(content, 'displayname') || '';
        card.base.art = extractStringValue(content, 'art') || '';
        card.base.group = extractStringValue(content, 'group') || '';
        card.base.rarity = extractStringValue(content, 'rarity') || '';
        card.base.god = extractStringValue(content, 'god') || '';
        card.base.type = extractStringValue(content, 'type') || '';
        card.base.dict = extractStringValue(content, 'dict') || '';
        card.base.desc_global = extractStringValue(content, 'desc_global') || '';
        card.base.sub = extractStringValue(content, 'sub') || '';
        
        // 解析 ap(可能是数字、字符串或特殊值)
        const apMatch = content.match(/ap\s*=\s*(?:"([^"]*)"|(\d+)|([^,\n]+))/);
        if (apMatch) {
            card.base.ap = apMatch[1] || apMatch[2] || (apMatch[3] ? apMatch[3].trim() : '');
        }
        
        card.base.isinspiration = extractNumberValue(content, 'isinspiration') || 0;
        card.base.isgod_inspiration = extractNumberValue(content, 'isgod_inspiration') || 0;
    }
    
    // 辅助函数:提取字符串值(支持转义)
    function extractStringValue(text, key) {
        const pattern = new RegExp(key + '\\s*=\\s*"((?:[^"\\\\]|\\\\.)*)"', 'm');
        const match = text.match(pattern);
        if (match && match[1]) {
            // 处理转义字符
            return match[1]
                .replace(/\\"/g, '"')
                .replace(/\\n/g, '\n')
                .replace(/\\t/g, '\t')
                .replace(/\\\\/g, '\\');
        }
        return '';
    }

    
    // Lua字符串转义
    function escapeLuaString(str) {
        if (!str) return '';
        return str.replace(/\\/g, '\\\\')
                  .replace(/"/g, '\\"')
                  .replace(/\n/g, '\\n')
                  .replace(/\r/g, '\\r')
                  .replace(/\t/g, '\\t');
    }
    
    // 生成Lua代码
    function generateLuaCode() {
        let code = 'local card = {}\n\n';
        
        // 生成 card.order
        if (state.defaultInfo.order) {
            code += `card.order = { "${escapeLuaString(state.defaultInfo.order)}" }\n\n`;
        }
        
        // 生成 card.info
        if (state.defaultInfo.ego) {
            code += 'card.info = {\n';
            code += `    ego = "${escapeLuaString(state.defaultInfo.ego)}",\n`;
            code += '}\n\n';
        }
        
        // 生成每张卡牌
        state.cards.forEach(card => {
            code += `card["${escapeLuaString(card.name)}"] = {\n`;
            code += '    base = {\n';
            
            const base = card.base;
            if (base.displayname) code += `        displayname = "${escapeLuaString(base.displayname)}",\n`;
            if (base.art) code += `        art = "${escapeLuaString(base.art)}",\n`;
            if (base.group) code += `        group = "${escapeLuaString(base.group)}",\n`;
            if (base.rarity) code += `        rarity = "${escapeLuaString(base.rarity)}",\n`;
            if (base.god) code += `        god = "${escapeLuaString(base.god)}",\n`;
            if (base.ap !== '') {
                if (isNaN(base.ap)) {
                    code += `        ap = "${escapeLuaString(base.ap)}",\n`;
                } else {
                    code += `        ap = ${base.ap},\n`;
                }
            }
            if (base.type) code += `        type = "${escapeLuaString(base.type)}",\n`;
            if (base.dict) code += `        dict = "${escapeLuaString(base.dict)}",\n`;
            if (base.desc_global) code += `        desc_global = "${escapeLuaString(base.desc_global)}",\n`;
            if (base.sub) code += `        sub = "${escapeLuaString(base.sub)}",\n`;
            if (base.isinspiration) code += `        isinspiration = ${base.isinspiration},\n`;
            if (base.isgod_inspiration) code += `        isgod_inspiration = ${base.isgod_inspiration},\n`;
            
            code += '    },\n';
            
            // 生成 var 部分
            const hasInspiration = card.var.inspiration.length > 0;
            const hasGodInspiration = Object.values(card.var.god_inspiration).some(arr => arr.length > 0);
            
            if (hasInspiration || hasGodInspiration) {
                code += '    var = {\n';
                
                // 灵光一闪
                if (hasInspiration) {
                    code += '        inspiration = {\n';
                    card.var.inspiration.forEach(insp => {
                        code += '            {\n';
                        if (insp.ap !== '' && insp.ap !== undefined) {
                            if (isNaN(insp.ap)) {
                                code += `                ap = "${escapeLuaString(insp.ap)}",\n`;
                            } else {
                                code += `                ap = ${insp.ap},\n`;
                            }
                        }
                        if (insp.type) code += `                type = "${escapeLuaString(insp.type)}",\n`;
                        if (insp.desc_global) code += `                desc_global = "${escapeLuaString(insp.desc_global)}",\n`;
                        if (insp.dict) code += `                dict = "${escapeLuaString(insp.dict)}",\n`;
                        code += '            },\n';
                    });
                    code += '        },\n';
                }
                
                // 神光一闪
                if (hasGodInspiration) {
                    code += '        god_inspiration = {\n';
                    Object.entries(card.var.god_inspiration).forEach(([god, inspList]) => {
                        if (inspList.length > 0) {
                            code += `            ${god} = {\n`;
                            inspList.forEach(insp => {
                                code += '                {\n';
                                if (insp.ap !== '' && insp.ap !== undefined) {
                                    if (isNaN(insp.ap)) {
                                        code += `                    ap = "${escapeLuaString(insp.ap)}",\n`;
                                    } else {
                                        code += `                    ap = ${insp.ap},\n`;
                                    }
                                }
                                if (insp.type) code += `                    type = "${escapeLuaString(insp.type)}",\n`;
                                if (insp.desc_global) code += `                    desc_global = "${escapeLuaString(insp.desc_global)}",\n`;
                                if (insp.dict) code += `                    dict = "${escapeLuaString(insp.dict)}",\n`;
                                code += '                },\n';
                            });
                            code += '            },\n';
                        }
                    });
                    code += '        },\n';
                }
                
                code += '    },\n';
            }
            
            code += '}\n\n';
        });
        
        code += 'return card';
        
        return code;
    }
    
    // 加载战斗员列表
    async function loadFighters() {
        try {
            const api = new mw.Api();
            const result = await api.get({
                action: 'query',
                list: 'allpages',
                apprefix: '卡牌/',
                apnamespace: 828, // Module namespace
                aplimit: 500
            });
            
            if (result.query && result.query.allpages) {
                state.fighters = result.query.allpages.map(page => {
                    return page.title.replace('模块:卡牌/', '');
                });
            }
        } catch (error) {
            console.error('加载战斗员列表失败:', error);
        }
    }
    
    // 加载战斗员卡牌数据
    async function loadFighterCards(fighter) {
        if (!fighter) return [];
        
        try {
            const api = new mw.Api();
            
            // 获取页面内容
            const result = await api.get({
                action: 'query',
                prop: 'revisions',
                titles: '模块:卡牌/' + fighter,
                rvprop: 'content',
                rvslots: 'main',
                formatversion: 2
            });
            
            if (!result.query || !result.query.pages || result.query.pages.length === 0) {
                console.error('未找到页面');
                return [];
            }
            
            const page = result.query.pages[0];
            
            if (page.missing) {
                console.error('页面不存在');
                return [];
            }
            
            const content = page.revisions[0].slots.main.content;
            
            // 解析Lua代码
            const parsed = parseLuaCode(content);
            
            // 更新默认信息
            state.defaultInfo = parsed.defaultInfo;
            
            return parsed.cards;
            
        } catch (error) {
            console.error('加载卡牌数据失败:', error);
            return [];
        }
    }
    
    // 渲染函数
    function render() {
        const container = document.getElementById('card-manager-container');
        if (!container) return;
        
        container.innerHTML = '';
        
        const manager = createElement('div', 'card-manager');
        
        // 左侧输入区
        const inputSection = createElement('div', 'card-input-section');
        
        // 战斗员选择
        const title = createElement('div', 'section-title');
        title.textContent = '卡牌管理器';
        inputSection.appendChild(title);
        
        const fighterSelect = createSelect(state.fighters, state.currentFighter, async (value) => {
            state.currentFighter = value;
            // 显示加载提示
            const loading = createElement('div', 'loading-indicator');
            loading.textContent = '正在加载卡牌数据...';
            document.body.appendChild(loading);
            
            try {
                const cards = await loadFighterCards(value);
                state.cards = cards;
                state.currentCard = cards.length > 0 ? cards[0] : null;
                render();
            } catch (error) {
                alert('加载失败:' + error.message);
            } finally {
                if (loading.parentNode) {
                    document.body.removeChild(loading);
                }
            }
        });
        inputSection.appendChild(createFormGroup('选择战斗员:', fighterSelect.wrapper));
        
        // 默认信息区
        if (state.currentFighter) {
            const defaultSection = createElement('div', 'default-info-section');
            const defaultTitle = createElement('div', 'section-title');
            defaultTitle.textContent = '默认信息';
            defaultTitle.style.borderColor = '#ff9800';
            defaultSection.appendChild(defaultTitle);
            
            const orderInput = createInput('text', state.defaultInfo.order, (value) => {
                state.defaultInfo.order = value;
                render();
            }, 'card.order');
            defaultSection.appendChild(createFormGroup('卡牌顺序:', orderInput));
            
            const egoInput = createInput('text', state.defaultInfo.ego, (value) => {
                state.defaultInfo.ego = value;
                render();
            }, 'card.info.ego');
            defaultSection.appendChild(createFormGroup('EGO:', egoInput));
            
            inputSection.appendChild(defaultSection);
        }
        
        // 卡牌编辑区
        if (state.currentCard) {
            const cardSection = createElement('div', '');
            cardSection.style.marginTop = '20px';
            
            const cardTitle = createElement('div', 'section-title');
            cardTitle.textContent = '卡牌信息';
            cardSection.appendChild(cardTitle);
            
            // 卡牌名称
            const nameInput = createInput('text', state.currentCard.name, (value) => {
                state.currentCard.name = value;
                render();
            }, '卡牌名称');
            cardSection.appendChild(createFormGroup('卡牌名称:', nameInput));
            
            // 显示名称
            const displaynameInput = createInput('text', state.currentCard.base.displayname, (value) => {
                state.currentCard.base.displayname = value;
                render();
            }, '显示名称');
            cardSection.appendChild(createFormGroup('显示名称:', displaynameInput));
            
            // 图片
            const artInput = createInput('text', state.currentCard.base.art, (value) => {
                state.currentCard.base.art = value;
                render();
            }, '图片文件名');
            cardSection.appendChild(createFormGroup('图片:', artInput));
            
            // 分组
            const groupInput = createInput('text', state.currentCard.base.group, (value) => {
                state.currentCard.base.group = value;
                render();
            }, '分组');
            cardSection.appendChild(createFormGroup('分组:', groupInput));
            
            // 稀有度
            const raritySelect = createSelect(
                ['', 'ZAYIN', 'TETH', 'HE', 'WAW', 'ALEPH'],
                state.currentCard.base.rarity || '',
                (value) => {
                    state.currentCard.base.rarity = value;
                    render();
                }
            );
            cardSection.appendChild(createFormGroup('稀有度:', raritySelect.wrapper));
            
            // 神明
            const godSelect = createSelect(
                ['', 'circen', 'diallos', 'nihilum', 'secred', 'vitor'],
                state.currentCard.base.god || '',
                (value) => {
                    state.currentCard.base.god = value;
                    render();
                }
            );
            cardSection.appendChild(createFormGroup('神明:', godSelect.wrapper));
            
            // AP
            const apInput = createInput('text', state.currentCard.base.ap, (value) => {
                state.currentCard.base.ap = value;
                render();
            }, 'AP (可以是数字、X、Ø等)');
            cardSection.appendChild(createFormGroup('AP:', apInput));
            
            // 类型
            const typeSelect = createSelect(
                ['', '攻击', '技能', '强化'],
                state.currentCard.base.type || '',
                (value) => {
                    state.currentCard.base.type = value;
                    render();
                }
            );
            cardSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
            
            // 词典
            const dictInput = createInput('text', state.currentCard.base.dict, (value) => {
                state.currentCard.base.dict = value;
                render();
            }, '词典关键词');
            cardSection.appendChild(createFormGroup('词典:', dictInput));
            
            // 描述
            const descSection = createElement('div', 'form-group');
            const descLabel = createElement('div', 'form-label');
            descLabel.textContent = '描述:';
            descSection.appendChild(descLabel);
            
            // 描述工具栏
            const toolbar = createElement('div', 'button-group');
            
            const blueTextBtn = createButton('蓝色文本', 'btn btn-blue', () => {
                insertTextAtCursor(descInput, '{blue|选择文字|}', true);
            });
            toolbar.appendChild(blueTextBtn);
            
            const greenTextBtn = createButton('绿色文本', 'btn btn-green', () => {
                insertTextAtCursor(descInput, '{green|选择文字|}', true);
            });
            toolbar.appendChild(greenTextBtn);
            
            const strokeTextBtn = createButton('绿色描边', 'btn btn-green', () => {
                insertTextAtCursor(descInput, '{stroke|选择文字|}', true);
            });
            toolbar.appendChild(strokeTextBtn);
            
            const dictBtn = createButton('词典', 'btn', () => {
                const keyword = prompt('请输入词典关键词:');
                if (keyword) {
                    insertTextAtCursor(descInput, `{dict|${keyword}|}`);
                }
            });
            toolbar.appendChild(dictBtn);
            
            const wrapBtn = createButton('换行', 'btn', () => {
                insertTextAtCursor(descInput, '{wrap|}');
            });
            toolbar.appendChild(wrapBtn);
            
            descSection.appendChild(toolbar);
            
            const descInput = createInput('textarea', state.currentCard.base.desc_global, (value) => {
                state.currentCard.base.desc_global = value;
                render();
            }, '卡牌描述');
            descInput.style.minHeight = '150px';
            descSection.appendChild(descInput);
            
            cardSection.appendChild(descSection);
            
            // 副描述
            const subInput = createInput('textarea', state.currentCard.base.sub, (value) => {
                state.currentCard.base.sub = value;
                render();
            }, '副描述');
            subInput.style.minHeight = '80px';
            cardSection.appendChild(createFormGroup('副描述:', subInput));
            
            // 是否存在灵光一闪
            const inspirationCheckbox = createCheckbox(state.currentCard.base.isinspiration, (value) => {
                state.currentCard.base.isinspiration = value;
                if (!value) {
                    state.currentCard.var.inspiration = [];
                }
                render();
            });
            cardSection.appendChild(createFormGroup('是否存在灵光一闪:', inspirationCheckbox));
            
            // 是否存在神光一闪
            const godInspirationCheckbox = createCheckbox(state.currentCard.base.isgod_inspiration, (value) => {
                state.currentCard.base.isgod_inspiration = value;
                if (!value) {
                    state.currentCard.var.god_inspiration = {
                        circen: [],
                        diallos: [],
                        nihilum: [],
                        secred: [],
                        vitor: []
                    };
                }
                render();
            });
            cardSection.appendChild(createFormGroup('是否存在神光一闪:', godInspirationCheckbox));
            
            inputSection.appendChild(cardSection);
        }
        
        // 添加新卡牌按钮
        const addCardBtn = createButton('+ 新增卡牌', 'btn-success', () => {
            const newCard = createEmptyCard();
            newCard.name = '新卡牌' + (state.cards.length + 1);
            state.cards.push(newCard);
            state.currentCard = newCard;
            render();
        });
        addCardBtn.style.marginTop = '20px';
        addCardBtn.style.width = '100%';
        inputSection.appendChild(addCardBtn);
        
        manager.appendChild(inputSection);
        
        // 中间列表区
        const listSection = createElement('div', 'card-list-section');
        
        // 卡牌列表
        const cardListContainer = createElement('div', 'list-container');
        const cardListHeader = createElement('div', 'list-header');
        const cardListTitle = createElement('div', 'list-title');
        cardListTitle.textContent = '卡牌列表';
        cardListHeader.appendChild(cardListTitle);
        cardListContainer.appendChild(cardListHeader);
        
        if (state.cards.length === 0) {
            const emptyState = createElement('div', 'empty-state');
            const emptyIcon = createElement('div', 'empty-state-icon');
            emptyIcon.textContent = '📋';
            const emptyText = createElement('div', 'empty-state-text');
            emptyText.textContent = '暂无卡牌,点击"新增卡牌"开始创建';
            emptyState.appendChild(emptyIcon);
            emptyState.appendChild(emptyText);
            cardListContainer.appendChild(emptyState);
        } else {
            state.cards.forEach((card, index) => {
                const cardItem = createElement('div', 'card-item' + (state.currentCard === card ? ' active' : ''));
                
                const cardName = createElement('div', 'card-item-name');
                cardName.textContent = card.name || '未命名卡牌';
                
                const cardInfo = createElement('div', 'card-item-info');
                const infoText = [];
                if (card.base.type) infoText.push(card.base.type);
                if (card.base.ap !== '') infoText.push('AP:' + card.base.ap);
                if (card.base.rarity) infoText.push(card.base.rarity);
                cardInfo.textContent = infoText.join(' | ') || '暂无信息';
                
                cardItem.appendChild(cardName);
                cardItem.appendChild(cardInfo);
                
                cardItem.addEventListener('click', () => {
                    state.currentCard = card;
                    render();
                });
                
                // 删除按钮
                const deleteBtn = createButton('删除', 'btn-danger', (e) => {
                    e.stopPropagation();
                    if (confirm('确定要删除卡牌"' + card.name + '"吗?')) {
                        state.cards.splice(index, 1);
                        if (state.currentCard === card) {
                            state.currentCard = state.cards[0] || null;
                        }
                        render();
                    }
                });
                deleteBtn.style.cssText = 'margin-top:8px;width:100%;';
                cardItem.appendChild(deleteBtn);
                
                cardListContainer.appendChild(cardItem);
            });
        }
        
        listSection.appendChild(cardListContainer);
        
        // 灵光一闪列表
        if (state.currentCard && state.currentCard.base.isinspiration) {
            const inspirationSection = createElement('div', 'inspiration-section');
            const inspirationHeader = createElement('div', 'list-header');
            const inspirationTitle = createElement('div', 'list-title');
            inspirationTitle.textContent = '灵光一闪列表';
            inspirationHeader.appendChild(inspirationTitle);
            
            const addInspirationBtn = createButton('+ 添加', 'btn-primary', () => {
                state.currentCard.var.inspiration.push({
                    ap: '',
                    type: '',
                    desc_global: ''
                });
                render();
            });
            inspirationHeader.appendChild(addInspirationBtn);
            inspirationSection.appendChild(inspirationHeader);
            
            if (state.currentCard.var.inspiration.length === 0) {
                const emptyState = createElement('div', 'empty-state');
                emptyState.style.padding = '20px';
                const emptyText = createElement('div', 'empty-state-text');
                emptyText.textContent = '暂无灵光一闪,点击"添加"创建';
                emptyState.appendChild(emptyText);
                inspirationSection.appendChild(emptyState);
            } else {
                state.currentCard.var.inspiration.forEach((insp, idx) => {
                    const inspItem = createElement('div', 'inspiration-item');
                    
                    const inspHeader = createElement('div', 'inspiration-item-header');
                    const inspTitle = createElement('div', 'inspiration-item-title');
                    inspTitle.textContent = `灵光 #${idx + 1}`;
                    inspHeader.appendChild(inspTitle);
                    
                    const deleteInspBtn = createButton('删除', 'btn-danger', () => {
                        if (confirm('确定要删除这个灵光一闪吗?')) {
                            state.currentCard.var.inspiration.splice(idx, 1);
                            render();
                        }
                    });
                    deleteInspBtn.style.padding = '4px 8px';
                    deleteInspBtn.style.fontSize = '12px';
                    inspHeader.appendChild(deleteInspBtn);
                    inspItem.appendChild(inspHeader);
                    
                    // AP输入
                    const inspApInput = createInput('text', insp.ap, (value) => {
                        insp.ap = value;
                    }, 'AP');
                    inspApInput.style.marginBottom = '8px';
                    inspApInput.style.fontSize = '13px';
                    inspItem.appendChild(inspApInput);
                    
                    // 类型选择
                    const inspTypeSelect = createSelect(
                        ['', '攻击', '技能', '强化'],
                        insp.type || '',
                        (value) => {
                            insp.type = value;
                        }
                    );
                    inspTypeSelect.wrapper.style.marginBottom = '8px';
                    inspItem.appendChild(inspTypeSelect.wrapper);
                    
                    // 描述输入
                    const inspDescInput = createInput('textarea', insp.desc_global, (value) => {
                        insp.desc_global = value;
                    }, '描述');
                    inspDescInput.style.minHeight = '60px';
                    inspDescInput.style.fontSize = '13px';
                    inspItem.appendChild(inspDescInput);
                    
                    inspirationSection.appendChild(inspItem);
                });
            }
            
            listSection.appendChild(inspirationSection);
        }
        
        // 神光一闪列表
        if (state.currentCard && state.currentCard.base.isgod_inspiration) {
            const godInspirationSection = createElement('div', 'inspiration-section');
            const godInspirationHeader = createElement('div', 'list-header');
            const godInspirationTitle = createElement('div', 'list-title');
            godInspirationTitle.textContent = '神光一闪列表';
            godInspirationHeader.appendChild(godInspirationTitle);
            godInspirationSection.appendChild(godInspirationHeader);
            
            // 神明选择标签
            const godSelectGroup = createElement('div', 'god-select-group');
            ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => {
                const godTab = createElement('div', 'god-tab' + (state.currentGod === god ? ' active' : ''));
                godTab.textContent = god;
                godTab.addEventListener('click', () => {
                    state.currentGod = god;
                    render();
                });
                godSelectGroup.appendChild(godTab);
            });
            godInspirationSection.appendChild(godSelectGroup);
            
            // 当前神明的神光一闪列表
            const currentGodInspirations = state.currentCard.var.god_inspiration[state.currentGod];
            
            const addGodInspirationBtn = createButton('+ 添加 ' + state.currentGod + ' 神光', 'btn-primary', () => {
                currentGodInspirations.push({
                    ap: '',
                    type: '',
                    desc_global: ''
                });
                render();
            });
            addGodInspirationBtn.style.marginBottom = '10px';
            addGodInspirationBtn.style.width = '100%';
            godInspirationSection.appendChild(addGodInspirationBtn);
            
            if (currentGodInspirations.length === 0) {
                const emptyState = createElement('div', 'empty-state');
                emptyState.style.padding = '20px';
                const emptyText = createElement('div', 'empty-state-text');
                emptyText.textContent = `暂无 ${state.currentGod} 神光一闪`;
                emptyState.appendChild(emptyText);
                godInspirationSection.appendChild(emptyState);
            } else {
                currentGodInspirations.forEach((insp, idx) => {
                    const inspItem = createElement('div', 'inspiration-item');
                    
                    const inspHeader = createElement('div', 'inspiration-item-header');
                    const inspTitle = createElement('div', 'inspiration-item-title');
                    inspTitle.textContent = `${state.currentGod} 神光 #${idx + 1}`;
                    inspHeader.appendChild(inspTitle);
                    
                    const deleteInspBtn = createButton('删除', 'btn-danger', () => {
                        if (confirm('确定要删除这个神光一闪吗?')) {
                            currentGodInspirations.splice(idx, 1);
                            render();
                        }
                    });
                    deleteInspBtn.style.padding = '4px 8px';
                    deleteInspBtn.style.fontSize = '12px';
                    inspHeader.appendChild(deleteInspBtn);
                    inspItem.appendChild(inspHeader);
                    
                    // AP输入
                    const inspApInput = createInput('text', insp.ap, (value) => {
                        insp.ap = value;
                    }, 'AP');
                    inspApInput.style.marginBottom = '8px';
                    inspApInput.style.fontSize = '13px';
                    inspItem.appendChild(inspApInput);
                    
                    // 类型选择
                    const inspTypeSelect = createSelect(
                        ['', '攻击', '技能', '强化'],
                        insp.type || '',
                        (value) => {
                            insp.type = value;
                        }
                    );
                    inspTypeSelect.wrapper.style.marginBottom = '8px';
                    inspItem.appendChild(inspTypeSelect.wrapper);
                    
                    // 描述输入
                    const inspDescInput = createInput('textarea', insp.desc_global, (value) => {
                        insp.desc_global = value;
                    }, '描述');
                    inspDescInput.style.minHeight = '60px';
                    inspDescInput.style.fontSize = '13px';
                    inspItem.appendChild(inspDescInput);
                    
                    godInspirationSection.appendChild(inspItem);
                });
            }
            
            listSection.appendChild(godInspirationSection);
        }
        
        manager.appendChild(listSection);
        
        // 右侧预览区
        const previewSection = createElement('div', 'card-preview-section');
        const previewTitle = createElement('div', 'section-title');
        previewTitle.textContent = 'Lua 代码预览';
        previewSection.appendChild(previewTitle);
        
        // 复制按钮
        const copyBtn = createButton('复制代码', 'btn-primary', () => {
            const code = generateLuaCode();
            navigator.clipboard.writeText(code).then(() => {
                alert('代码已复制到剪贴板!');
            }).catch(err => {
                console.error('复制失败:', err);
                // 降级方案
                const textarea = document.createElement('textarea');
                textarea.value = code;
                textarea.style.position = 'fixed';
                textarea.style.opacity = '0';
                document.body.appendChild(textarea);
                textarea.select();
                document.execCommand('copy');
                document.body.removeChild(textarea);
                alert('代码已复制到剪贴板!');
            });
        });
        copyBtn.style.marginBottom = '10px';
        copyBtn.style.width = '100%';
        previewSection.appendChild(copyBtn);
        
        // 保存按钮
        const saveBtn = createButton('保存到Wiki', 'btn-success', async () => {
            if (!state.currentFighter) {
                alert('请先选择战斗员!');
                return;
            }
            
            const code = generateLuaCode();
            const pageName = '模块:卡牌/' + state.currentFighter;
            
            const loading = createElement('div', 'loading-indicator');
            loading.textContent = '正在保存...';
            document.body.appendChild(loading);
            
            try {
                const api = new mw.Api();
                await api.postWithToken('csrf', {
                    action: 'edit',
                    title: pageName,
                    text: code,
                    summary: '通过卡牌管理器更新卡牌数据',
                    contentmodel: 'Scribunto'
                });
                alert('保存成功!');
            } catch (error) {
                console.error('保存失败:', error);
                alert('保存失败:' + error);
            } finally {
                if (loading.parentNode) {
                    document.body.removeChild(loading);
                }
            }
        });
        saveBtn.style.marginBottom = '10px';
        saveBtn.style.width = '100%';
        previewSection.appendChild(saveBtn);
        
        // 代码显示
        const codePreview = createElement('div', 'code-preview');
        codePreview.textContent = generateLuaCode();
        previewSection.appendChild(codePreview);
        
        manager.appendChild(previewSection);
        
        container.appendChild(manager);
    }
    
    // 初始化
    async function init() {
        // 创建容器
        let container = document.getElementById('card-manager-container');
        if (!container) {
            container = createElement('div', '');
            container.id = 'card-manager-container';
            
            // 查找插入位置
            const content = document.getElementById('mw-content-text');
            if (content) {
                content.insertBefore(container, content.firstChild);
            } else {
                document.body.appendChild(container);
            }
        }
        
        // 显示加载提示
        const loading = createElement('div', 'loading-indicator');
        loading.textContent = '正在初始化...';
        document.body.appendChild(loading);
        
        try {
            // 加载战斗员列表
            await loadFighters();
            
            // 如果有战斗员,默认选择第一个
            if (state.fighters.length > 0) {
                state.currentFighter = state.fighters[0];
                state.cards = await loadFighterCards(state.currentFighter);
                if (state.cards.length > 0) {
                    state.currentCard = state.cards[0];
                }
            }
            
            // 渲染界面
            render();
        } catch (error) {
            console.error('初始化失败:', error);
            alert('初始化失败:' + error.message);
        } finally {
            if (loading.parentNode) {
                document.body.removeChild(loading);
            }
        }
    }
    
    // 等待DOM加载完成后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
    
    // 导出API供外部使用
    window.CardManager = {
        init: init,
        render: render,
        state: state,
        createCard: createEmptyCard,
        generateCode: generateLuaCode
    };
    
})();