MediaWiki

Card.js:修订间差异

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献
无编辑摘要
标签已被回退
律Rhyme留言 | 贡献
无编辑摘要
标签已被回退
第11行: 第11行:
         cards: [],
         cards: [],
         currentCard: null,
         currentCard: null,
         defaultInfo: { order: '', ego: '' },
         defaultInfo: {
            order: '',
            ego: ''
        },
         editMode: 'base',
         editMode: 'base',
         currentGod: 'circen',
         currentGod: 'circen',
第23行: 第26行:
             name: '',
             name: '',
             base: {
             base: {
                 displayname: '', art: '', group: '', rarity: '', god: '',
                 displayname: '',
                 ap: '', type: '', dict: '', desc_global: '', sub: '',
                art: '',
                 isinspiration: 0, isgod_inspiration: 0
                group: '',
                rarity: '',
                god: '',
                 ap: '',
                type: '',
                dict: '',
                desc_global: '',
                sub: '',
                 isinspiration: 0,
                isgod_inspiration: 0
             },
             },
             var: {
             var: {
                 inspiration: [],
                 inspiration: [],
                 god_inspiration: {
                 god_inspiration: {
                     circen: [], diallos: [], nihilum: [], secred: [], vitor: []
                     circen: [],
                    diallos: [],
                    nihilum: [],
                    secred: [],
                    vitor: []
                 }
                 }
             }
             }
第41行: 第57行:
         if (className) el.className = className;
         if (className) el.className = className;
         Object.entries(attributes).forEach(([key, value]) => {
         Object.entries(attributes).forEach(([key, value]) => {
             if (key === 'textContent') el.textContent = value;
             if (key === 'textContent') {
             else if (key.startsWith('on')) el.addEventListener(key.substring(2).toLowerCase(), value);
                el.textContent = value;
             else el.setAttribute(key, value);
             } else if (key.startsWith('on')) {
                el.addEventListener(key.substring(2).toLowerCase(), value);
             } else {
                el.setAttribute(key, value);
            }
         });
         });
         return el;
         return el;
第71行: 第91行:
             dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
             dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none';
             const rect = select.getBoundingClientRect();
             const rect = select.getBoundingClientRect();
             Object.assign(dropdown.style, {
             dropdown.style.top = rect.bottom + 'px';
                top: rect.bottom + 'px',
            dropdown.style.left = rect.left + 'px';
                left: rect.left + 'px',
            dropdown.style.width = rect.width + 'px';
                width: rect.width + 'px'
            });
         });
         });
          
          
         document.addEventListener('click', () => dropdown.style.display = 'none');
         document.addEventListener('click', () => {
            dropdown.style.display = 'none';
        });
          
          
         const wrapper = createElement('div');
         const wrapper = createElement('div');
第129行: 第149行:
         const wrapper = createElement('div', 'checkbox-wrapper');
         const wrapper = createElement('div', 'checkbox-wrapper');
         const checkbox = createElement('div', checked ? 'checkbox checked' : 'checkbox');
         const checkbox = createElement('div', checked ? 'checkbox checked' : 'checkbox');
         const label = createElement('div', '', { textContent: '是' });
         const label = createElement('div', '');
        label.textContent = '是';
          
          
         wrapper.addEventListener('click', () => {
         wrapper.addEventListener('click', () => {
             const newChecked = !checkbox.classList.contains('checked');
             const newChecked = !checkbox.classList.contains('checked');
             checkbox.classList.toggle('checked');
             if (newChecked) {
                checkbox.classList.add('checked');
            } else {
                checkbox.classList.remove('checked');
            }
             if (onChange) onChange(newChecked ? 1 : 0);
             if (onChange) onChange(newChecked ? 1 : 0);
         });
         });
第145行: 第170行:
     function createFormGroup(label, control) {
     function createFormGroup(label, control) {
         const group = createElement('div', 'form-group');
         const group = createElement('div', 'form-group');
         group.appendChild(createElement('div', 'form-label', { textContent: label }));
         const labelEl = createElement('div', 'form-label');
        labelEl.textContent = label;
        group.appendChild(labelEl);
         group.appendChild(control);
         group.appendChild(control);
         return group;
         return group;
第152行: 第179行:
     // 创建按钮
     // 创建按钮
     function createButton(text, className, onClick) {
     function createButton(text, className, onClick) {
         return createElement('div', 'btn ' + className, { textContent: text, onclick: onClick });
         const btn = createElement('div', 'btn ' + className);
        btn.textContent = text;
        btn.addEventListener('click', onClick);
        return btn;
     }
     }
      
      
第166行: 第196行:
             if (selectedText) {
             if (selectedText) {
                 range.deleteContents();
                 range.deleteContents();
                 range.insertNode(document.createTextNode(text.replace('选择文字', selectedText)));
                 const textNode = document.createTextNode(text.replace('选择文字', selectedText));
                range.insertNode(textNode);
                // 触发input事件以更新数据
                element.dispatchEvent(new Event('input', { bubbles: true }));
                 return;
                 return;
             }
             }
第174行: 第207行:
     }
     }
      
      
     // 提取值辅助函数
     // 创建描述工具栏(统一函数)
     function extractValue(text, key, isString = false) {
     function createDescToolbar(descInput) {
         const pattern = isString
        const toolbar = createElement('div', 'button-group');
            ? new RegExp(key + '\\s*=\\s*"([^"]*)"', 'm')
       
            : new RegExp(key + '\\s*=\\s*([^,\\n]+)', 'm');
        const buttons = [
            { text: '蓝色文本', className: 'btn btn-blue', template: '{{文本|蓝|选择文字}}' },
            { text: '绿色文本', className: 'btn btn-green', template: '{{文本|绿|选择文字}}' },
            { text: '蓝色下划线', className: 'btn btn-blue', template: '{{文本|蓝|下划线|选择文字}}' },
            { text: '绿色下划线', className: 'btn btn-green', template: '{{文本|绿|下划线|选择文字}}' },
            { text: '绿色描边', className: 'btn btn-green', template: '{{描边|绿|选择文字}}' },
            { text: '词典', className: 'btn', template: '{{词典|选择文字}}' },
            { text: '换行', className: 'btn', template: '<br>' }
        ];
       
        buttons.forEach(btn => {
            const button = createButton(btn.text, btn.className, () => {
                insertTextAtCursor(descInput, btn.template, btn.text !== '换行');
            });
            toolbar.appendChild(button);
        });
       
        return toolbar;
    }
   
    // 辅助函数:提取字符串值
    function extractStringValue(text, key) {
         const pattern = new RegExp(key + '\\s*=\\s*"([^"]*)"', 'm');
        const match = text.match(pattern);
        return match ? match[1] : '';
    }
   
    // 辅助函数:提取数值
    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);
         const match = text.match(pattern);
         if (!match) return isString ? '' : 0;
         if (match) {
        return isString ? match[1] : (parseInt(match[1]) || match[1].trim().replace(/^["']|["']$/g, ''));
            let value = match[1].trim();
            value = value.replace(/^["']|["']$/g, '');
            return value;
        }
        return '';
     }
     }
      
      
第187行: 第260行:
     function parseInspirationList(content) {
     function parseInspirationList(content) {
         const inspirations = [];
         const inspirations = [];
         let depth = 0, currentBlock = '', inString = false;
       
         let depth = 0;
        let currentBlock = '';
        let inString = false;
          
          
         for (let i = 0; i < content.length; i++) {
         for (let i = 0; i < content.length; i++) {
第193行: 第269行:
             const prevChar = i > 0 ? content[i - 1] : '';
             const prevChar = i > 0 ? content[i - 1] : '';
              
              
             if (char === '"' && prevChar !== '\\') inString = !inString;
             if (char === '"' && prevChar !== '\\') {
                inString = !inString;
            }
              
              
             if (!inString) {
             if (!inString) {
                 if (char === '{') {
                 if (char === '{') {
                     if (depth > 0) currentBlock += char;
                     if (depth > 0) {
                        currentBlock += char;
                    }
                     depth++;
                     depth++;
                 } else if (char === '}') {
                 } else if (char === '}') {
第203行: 第283行:
                     if (depth === 0 && currentBlock) {
                     if (depth === 0 && currentBlock) {
                         const inspiration = {};
                         const inspiration = {};
                         ['ap', 'type', 'desc_global', 'dict'].forEach(key => {
                          
                            const val = extractValue(currentBlock, key, key !== 'ap');
                        const ap = extractValue(currentBlock, 'ap');
                            if (val) inspiration[key] = val;
                        if (ap) inspiration.ap = ap;
                         });
                       
                         if (Object.keys(inspiration).length > 0) inspirations.push(inspiration);
                        const type = extractStringValue(currentBlock, 'type');
                        if (type) inspiration.type = type;
                       
                        const desc = extractStringValue(currentBlock, 'desc_global');
                        if (desc) inspiration.desc_global = desc;
                       
                        const dict = extractStringValue(currentBlock, 'dict');
                        if (dict) inspiration.dict = dict;
                          
                         if (Object.keys(inspiration).length > 0) {
                            inspirations.push(inspiration);
                        }
                       
                         currentBlock = '';
                         currentBlock = '';
                     } else if (depth > 0) currentBlock += char;
                     } else if (depth > 0) {
                 } else if (depth > 0) currentBlock += char;
                        currentBlock += char;
             } else if (depth > 0) currentBlock += char;
                    }
                 } else if (depth > 0) {
                    currentBlock += char;
                }
             } else if (depth > 0) {
                currentBlock += char;
            }
         }
         }
          
          
第217行: 第315行:
     }
     }
      
      
     // 提取块内容
     // 辅助函数:使用括号计数提取块内容
     function extractBlock(content, startPattern) {
     function extractBlock(content, startPattern) {
         const match = content.match(startPattern);
         const match = content.match(startPattern);
第223行: 第321行:
          
          
         let startIdx = match.index + match[0].length;
         let startIdx = match.index + match[0].length;
         let depth = 1, endIdx = startIdx, inString = false;
         let depth = 1;
        let endIdx = startIdx;
        let inString = false;
          
          
         for (let i = startIdx; i < content.length && depth > 0; i++) {
         for (let i = startIdx; i < content.length && depth > 0; i++) {
             const char = content[i];
             const char = content[i];
             if (char === '"' && content[i - 1] !== '\\') inString = !inString;
            const prevChar = i > 0 ? content[i - 1] : '';
           
             if (char === '"' && prevChar !== '\\') {
                inString = !inString;
            }
              
              
             if (!inString) {
             if (!inString) {
                 if (char === '{') depth++;
                 if (char === '{') {
                 else if (char === '}' && --depth === 0) {
                    depth++;
                    endIdx = i;
                 } else if (char === '}') {
                    break;
                    depth--;
                    if (depth === 0) {
                        endIdx = i;
                        break;
                    }
                 }
                 }
             }
             }
第244行: 第352行:
     function parseLuaCode(luaText) {
     function parseLuaCode(luaText) {
         const cards = [];
         const cards = [];
         const defaultInfo = { order: '', ego: '' };
         const defaultInfo = {
            order: '',
            ego: ''
        };
          
          
         try {
         try {
             const orderMatch = luaText.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/);
             const orderMatch = luaText.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/);
             if (orderMatch) defaultInfo.order = orderMatch[1];
             if (orderMatch) {
                defaultInfo.order = orderMatch[1];
            }
              
              
             const egoMatch = luaText.match(/card\.info\s*=\s*\{[^}]*ego\s*=\s*"([^"]+)"/);
             const egoMatch = luaText.match(/card\.info\s*=\s*\{[^}]*ego\s*=\s*"([^"]+)"/);
             if (egoMatch) defaultInfo.ego = egoMatch[1];
             if (egoMatch) {
                defaultInfo.ego = egoMatch[1];
            }
              
              
             const cardPattern = /card\["([^"]+)"\]\s*=\s*\{/g;
             const cardPattern = /card\["([^"]+)"\]\s*=\s*\{/g;
第257行: 第372行:
              
              
             while ((cardMatch = cardPattern.exec(luaText)) !== null) {
             while ((cardMatch = cardPattern.exec(luaText)) !== null) {
                 const cardContent = extractBlock(luaText.substring(cardMatch.index), /card\["[^"]+"\]\s*=\s*\{/);
                 const cardName = cardMatch[1];
                if (!cardContent) continue;
                const cardStartIdx = cardMatch.index + cardMatch[0].length;
               
                let depth = 1;
                let cardEndIdx = cardStartIdx;
                let inString = false;
               
                for (let i = cardStartIdx; i < luaText.length && depth > 0; i++) {
                    const char = luaText[i];
                    const prevChar = i > 0 ? luaText[i - 1] : '';
                   
                    if (char === '"' && prevChar !== '\\') {
                        inString = !inString;
                    }
                   
                    if (!inString) {
                        if (char === '{') {
                            depth++;
                        } else if (char === '}') {
                            depth--;
                            if (depth === 0) {
                                cardEndIdx = i;
                                break;
                            }
                        }
                    }
                }
               
                const cardContent = luaText.substring(cardStartIdx, cardEndIdx);
                  
                  
                 const card = createEmptyCard();
                 const card = createEmptyCard();
                 card.name = cardMatch[1];
                 card.name = cardName;
                  
                  
                 const baseContent = extractBlock(cardContent, /base\s*=\s*\{/);
                 const baseContent = extractBlock(cardContent, /base\s*=\s*\{/);
                 if (baseContent) {
                 if (baseContent) {
                     ['displayname', 'art', 'group', 'rarity', 'god', 'type', 'dict', 'desc_global', 'sub'].forEach(key => {
                     card.base.displayname = extractStringValue(baseContent, 'displayname') || '';
                        card.base[key] = extractValue(baseContent, key, true);
                    card.base.art = extractStringValue(baseContent, 'art') || '';
                     });
                    card.base.group = extractStringValue(baseContent, 'group') || '';
                     card.base.ap = extractValue(baseContent, 'ap');
                    card.base.rarity = extractStringValue(baseContent, 'rarity') || '';
                     card.base.isinspiration = extractValue(baseContent, 'isinspiration') || 0;
                    card.base.god = extractStringValue(baseContent, 'god') || '';
                     card.base.isgod_inspiration = extractValue(baseContent, 'isgod_inspiration') || 0;
                    card.base.ap = extractValue(baseContent, 'ap') || '';
                    card.base.type = extractStringValue(baseContent, 'type') || '';
                    card.base.dict = extractStringValue(baseContent, 'dict') || '';
                     card.base.desc_global = extractStringValue(baseContent, 'desc_global') || '';
                     card.base.sub = extractStringValue(baseContent, 'sub') || '';
                     card.base.isinspiration = extractNumberValue(baseContent, 'isinspiration') || 0;
                     card.base.isgod_inspiration = extractNumberValue(baseContent, 'isgod_inspiration') || 0;
                 }
                 }
                  
                  
第276行: 第424行:
                 if (varContent) {
                 if (varContent) {
                     const inspirationContent = extractBlock(varContent, /inspiration\s*=\s*\{/);
                     const inspirationContent = extractBlock(varContent, /inspiration\s*=\s*\{/);
                     if (inspirationContent) card.var.inspiration = parseInspirationList(inspirationContent);
                     if (inspirationContent) {
                        card.var.inspiration = parseInspirationList(inspirationContent);
                    }
                      
                      
                     const godInspirationContent = extractBlock(varContent, /god_inspiration\s*=\s*\{/);
                     const godInspirationContent = extractBlock(varContent, /god_inspiration\s*=\s*\{/);
                     if (godInspirationContent) {
                     if (godInspirationContent) {
                         ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => {
                         ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => {
                             const godContent = extractBlock(godInspirationContent, new RegExp(god + '\\s*=\\s*\\{'));
                             const godPattern = new RegExp(god + '\\s*=\\s*\\{');
                             if (godContent) card.var.god_inspiration[god] = parseInspirationList(godContent);
                            const godContent = extractBlock(godInspirationContent, godPattern);
                             if (godContent) {
                                card.var.god_inspiration[god] = parseInspirationList(godContent);
                            }
                         });
                         });
                     }
                     }
第289行: 第442行:
                 cards.push(card);
                 cards.push(card);
             }
             }
           
         } catch (error) {
         } catch (error) {
             console.error('解析Lua代码失败:', error);
             console.error('解析Lua代码失败:', error);
第299行: 第453行:
     function escapeLuaString(str) {
     function escapeLuaString(str) {
         if (!str) return '';
         if (!str) return '';
         return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
         return str.replace(/\\/g, '\\\\')
                   .replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
                  .replace(/"/g, '\\"')
    }
                   .replace(/\n/g, '\\n')
   
                  .replace(/\r/g, '\\r')
    // 生成Lua属性行
                  .replace(/\t/g, '\\t');
    function generateLuaProperty(key, value, indent = '        ') {
        if (value === '' || value === undefined) return '';
        const isNumber = !isNaN(value) && key !== 'ap';
        return `${indent}${key} = ${isNumber ? value : `"${escapeLuaString(value)}"`},\n`;
    }
   
    // 生成灵光/神光代码
    function generateInspirationCode(inspList, indent = '            ') {
        let code = '';
        inspList.forEach(insp => {
            code += indent + '{\n';
            ['ap', 'type', 'desc_global', 'dict'].forEach(key => {
                if (insp[key] !== '' && insp[key] !== undefined) {
                    code += generateLuaProperty(key, insp[key], indent + '    ');
                }
            });
            code += indent + '},\n';
        });
        return code;
     }
     }
      
      
第329行: 第464行:
         let code = 'local card = {}\n\n';
         let code = 'local card = {}\n\n';
          
          
         if (state.defaultInfo.order)  
         if (state.defaultInfo.order) {
             code += `card.order = { "${escapeLuaString(state.defaultInfo.order)}" }\n\n`;
             code += `card.order = { "${escapeLuaString(state.defaultInfo.order)}" }\n\n`;
        }
          
          
         if (state.defaultInfo.ego)  
         if (state.defaultInfo.ego) {
             code += `card.info = {\n    ego = "${escapeLuaString(state.defaultInfo.ego)}",\n}\n\n`;
             code += 'card.info = {\n';
            code += `   ego = "${escapeLuaString(state.defaultInfo.ego)}",\n`;
            code += '}\n\n';
        }
          
          
         state.cards.forEach(card => {
         state.cards.forEach(card => {
             code += `card["${escapeLuaString(card.name)}"] = {\n    base = {\n`;
             code += `card["${escapeLuaString(card.name)}"] = {\n`;
            code += '   base = {\n';
              
              
             ['displayname', 'art', 'group', 'rarity', 'god', 'ap', 'type', 'dict', 'desc_global', 'sub', 'isinspiration', 'isgod_inspiration']
             const base = card.base;
                .forEach(key => code += generateLuaProperty(key, card.base[key]));
            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';
             code += '    },\n';
第351行: 第508行:
                 if (hasInspiration) {
                 if (hasInspiration) {
                     code += '        inspiration = {\n';
                     code += '        inspiration = {\n';
                     code += generateInspirationCode(card.var.inspiration);
                     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';
                     code += '        },\n';
                 }
                 }
第360行: 第530行:
                         if (inspList.length > 0) {
                         if (inspList.length > 0) {
                             code += `            ${god} = {\n`;
                             code += `            ${god} = {\n`;
                             code += generateInspirationCode(inspList, '                ');
                             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';
                         }
                         }
第373行: 第556行:
         });
         });
          
          
         return code + 'return card';
         code += 'return card';
    }
          
   
         return code;
    // API调用封装
    async function apiCall(params) {
         const api = new mw.Api();
         return await api.get(params);
     }
     }
      
      
第385行: 第564行:
     async function loadFighters() {
     async function loadFighters() {
         try {
         try {
             const result = await apiCall({
            const api = new mw.Api();
             const result = await api.get({
                 action: 'query',
                 action: 'query',
                 list: 'allpages',
                 list: 'allpages',
第393行: 第573行:
             });
             });
              
              
             if (result.query?.allpages) {
             if (result.query && result.query.allpages) {
                 state.fighters = result.query.allpages.map(page => page.title.replace('模块:卡牌/', ''));
                 state.fighters = result.query.allpages.map(page => {
                    return page.title.replace('模块:卡牌/', '');
                });
             }
             }
         } catch (error) {
         } catch (error) {
第406行: 第588行:
          
          
         try {
         try {
             const result = await apiCall({
            const api = new mw.Api();
           
             const result = await api.get({
                 action: 'query',
                 action: 'query',
                 prop: 'revisions',
                 prop: 'revisions',
第415行: 第599行:
             });
             });
              
              
             const page = result.query?.pages?.[0];
            if (!result.query || !result.query.pages || result.query.pages.length === 0) {
             if (!page || page.missing) {
                console.error('未找到页面');
                return [];
            }
           
             const page = result.query.pages[0];
           
             if (page.missing) {
                 console.error('页面不存在');
                 console.error('页面不存在');
                 return [];
                 return [];
第422行: 第612行:
              
              
             const content = page.revisions[0].slots.main.content;
             const content = page.revisions[0].slots.main.content;
           
             const parsed = parseLuaCode(content);
             const parsed = parseLuaCode(content);
           
             state.defaultInfo = parsed.defaultInfo;
             state.defaultInfo = parsed.defaultInfo;
           
             return parsed.cards;
             return parsed.cards;
              
              
第432行: 第625行:
     }
     }
      
      
     // 更新代码预览
     // 更新代码预览(不触发滚动)
     function updateCodePreview() {
     function updateCodePreview() {
         const codePreview = document.querySelector('.code-preview');
         const codePreview = document.querySelector('.code-preview');
         if (codePreview) codePreview.textContent = generateLuaCode();
         if (codePreview) {
    }
             codePreview.textContent = generateLuaCode();
   
         }
    // 创建描述工具栏
    function createDescToolbar(descInput) {
        const toolbar = createElement('div', 'button-group');
       
        const buttons = [
             { text: '蓝色文本', class: 'btn-blue', template: '{{文本|蓝|选择文字}}' },
            { text: '绿色文本', class: 'btn-green', template: '{{文本|绿|选择文字}}' },
            { text: '蓝色下划线', class: 'btn-blue', template: '{{文本|蓝|下划线|选择文字}}' },
            { text: '绿色下划线', class: 'btn-green', template: '{{文本|绿|下划线|选择文字}}' },
            { text: '绿色描边', class: 'btn-green', template: '{{描边|绿|选择文字}}' },
            { text: '换行', class: '', template: '<br>' }
        ];
       
        buttons.forEach(btn => {
            toolbar.appendChild(createButton(btn.text, btn.class, () => {
                insertTextAtCursor(descInput, btn.template, true);
            }));
        });
       
        toolbar.appendChild(createButton('词典', '', () => {
            const keyword = prompt('请输入词典关键词:');
            if (keyword) insertTextAtCursor(descInput, `{{词典|${keyword}}}`);
        }));
       
        return toolbar;
    }
   
    // 创建变体编辑表单
    function createVariantEditForm(insp, title, color, onBack, onDelete) {
        const section = createElement('div', 'variant-edit-section');
       
        const titleEl = createElement('div', 'section-title', { textContent: title });
        titleEl.style.borderColor = color;
        section.appendChild(titleEl);
       
        const backBtn = createButton('← 返回基础信息', 'btn', onBack);
        backBtn.style.cssText = 'margin-bottom:15px;width:100%;';
        section.appendChild(backBtn);
       
        // AP输入
        section.appendChild(createFormGroup('AP:',
            createInput('text', insp.ap, v => { insp.ap = v; updateCodePreview(); }, 'AP (数字、X、Ø)')));
       
        // 类型选择
        const typeSelect = createSelect(['', '攻击', '技能', '强化'], insp.type || '',
            v => { insp.type = v; updateCodePreview(); });
        section.appendChild(createFormGroup('类型:', typeSelect.wrapper));
       
        // 机制
        const dictSection = createElement('div', 'form-group');
        dictSection.appendChild(createElement('div', 'form-label', { textContent: '机制:' }));
       
        const dictToolbar = createElement('div', 'button-group');
        const dictInput = createInput('text', insp.dict, v => { insp.dict = v; updateCodePreview(); }, '多个机制使用、隔开');
        dictToolbar.appendChild(createButton('词典', 'btn', () => insertTextAtCursor(dictInput, '{{词典|选择文字}}', true)));
        dictSection.appendChild(dictToolbar);
        dictSection.appendChild(dictInput);
        section.appendChild(dictSection);
       
        // 描述
        const descSection = createElement('div', 'form-group');
         descSection.appendChild(createElement('div', 'form-label', { textContent: '描述:' }));
       
        const descInput = createInput('textarea', insp.desc_global, v => { insp.desc_global = v; updateCodePreview(); }, '变体描述');
        descInput.style.minHeight = '150px';
        descSection.appendChild(createDescToolbar(descInput));
        descSection.appendChild(descInput);
        section.appendChild(descSection);
       
        // 删除按钮
        const deleteBtn = createButton('删除此变体', 'btn-danger', onDelete);
        deleteBtn.style.cssText = 'margin-top:20px;width:100%;';
        section.appendChild(deleteBtn);
       
        return section;
     }
     }
      
      
     // 渲染变体编辑器
     // 渲染变体卡牌编辑表单
     function renderVariantEditor(container) {
     function renderVariantEditor(container) {
         if (!state.currentCard) return;
         if (!state.currentCard) return;
第523行: 第641行:
             if (!insp) return;
             if (!insp) return;
              
              
             container.appendChild(createVariantEditForm(
             const variantSection = createElement('div', 'variant-edit-section');
                insp,  
            const variantTitle = createElement('div', 'section-title');
                `灵光一闪 #${state.currentInspirationIndex + 1}`,
            variantTitle.textContent = `灵光一闪 #${state.currentInspirationIndex + 1}`;
                '#9c27b0',
            variantTitle.style.borderColor = '#9c27b0';
                () => { state.editMode = 'base'; state.currentInspirationIndex = null; renderWithoutScroll(); },
            variantSection.appendChild(variantTitle);
                 () => {
           
                     if (confirm('确定要删除这个灵光一闪吗?')) {
            const backBtn = createButton('← 返回基础信息', 'btn', () => {
                        state.currentCard.var.inspiration.splice(state.currentInspirationIndex, 1);
                state.editMode = 'base';
                        state.editMode = 'base';
                state.currentInspirationIndex = null;
                        state.currentInspirationIndex = null;
                renderWithoutScroll();
                        renderWithoutScroll();
            });
                    }
            backBtn.style.marginBottom = '15px';
            backBtn.style.width = '100%';
            variantSection.appendChild(backBtn);
           
            const apInput = createInput('text', insp.ap, (value) => {
                insp.ap = value;
                updateCodePreview();
            }, 'AP (数字、X、Ø)');
            variantSection.appendChild(createFormGroup('AP:', apInput));
           
            const typeSelect = createSelect(
                ['', '攻击', '技能', '强化'],
                insp.type || '',
                 (value) => {
                     insp.type = value;
                    updateCodePreview();
                }
            );
            variantSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
           
            const dictSection = createElement('div', 'form-group');
            const dictLabel = createElement('div', 'form-label');
            dictLabel.textContent = '机制:';
            dictSection.appendChild(dictLabel);
   
            const dictInput = createInput('text', insp.dict, (value) => {
                insp.dict = value;
                updateCodePreview();
            }, '多个机制使用、隔开');
           
            const dictToolbar = createElement('div', 'button-group');
            const dictBtn = createButton('词典', 'btn', () => {
                insertTextAtCursor(dictInput, '{{词典|选择文字}}', true);
            });
            dictToolbar.appendChild(dictBtn);
           
            dictSection.appendChild(dictToolbar);
            dictSection.appendChild(dictInput);
            variantSection.appendChild(dictSection);
           
            const descSection = createElement('div', 'form-group');
            const descLabel = createElement('div', 'form-label');
            descLabel.textContent = '描述:';
            descSection.appendChild(descLabel);
           
            const descInput = createInput('textarea', insp.desc_global, (value) => {
                insp.desc_global = value;
                updateCodePreview();
            }, '变体描述');
            descInput.style.minHeight = '150px';
           
            const toolbar = createDescToolbar(descInput);
            descSection.appendChild(toolbar);
            descSection.appendChild(descInput);
            variantSection.appendChild(descSection);
           
            const deleteBtn = createButton('删除此变体', 'btn-danger', () => {
                if (confirm('确定要删除这个灵光一闪吗?')) {
                    state.currentCard.var.inspiration.splice(state.currentInspirationIndex, 1);
                    state.editMode = 'base';
                    state.currentInspirationIndex = null;
                    renderWithoutScroll();
                 }
                 }
             ));
             });
            deleteBtn.style.marginTop = '20px';
            deleteBtn.style.width = '100%';
            variantSection.appendChild(deleteBtn);
           
            container.appendChild(variantSection);
              
              
         } else if (state.editMode === 'god_inspiration' && state.currentGodInspirationIndex !== null) {
         } else if (state.editMode === 'god_inspiration' && state.currentGodInspirationIndex !== null) {
第542行: 第726行:
             if (!insp) return;
             if (!insp) return;
              
              
             container.appendChild(createVariantEditForm(
             const variantSection = createElement('div', 'variant-edit-section');
                insp,
            const variantTitle = createElement('div', 'section-title');
                `${state.currentGod} 神光 #${state.currentGodInspirationIndex + 1}`,
            variantTitle.textContent = `${state.currentGod} 神光 #${state.currentGodInspirationIndex + 1}`;
                '#673ab7',
            variantTitle.style.borderColor = '#673ab7';
                () => { state.editMode = 'base'; state.currentGodInspirationIndex = null; renderWithoutScroll(); },
            variantSection.appendChild(variantTitle);
                 () => {
           
                     if (confirm('确定要删除这个神光一闪吗?')) {
            const backBtn = createButton('← 返回基础信息', 'btn', () => {
                        state.currentCard.var.god_inspiration[state.currentGod].splice(state.currentGodInspirationIndex, 1);
                state.editMode = 'base';
                        state.editMode = 'base';
                state.currentGodInspirationIndex = null;
                        state.currentGodInspirationIndex = null;
                renderWithoutScroll();
                        renderWithoutScroll();
            });
                    }
            backBtn.style.marginBottom = '15px';
            backBtn.style.width = '100%';
            variantSection.appendChild(backBtn);
           
            const apInput = createInput('text', insp.ap, (value) => {
                 insp.ap = value;
                updateCodePreview();
            }, 'AP (数字、X、Ø)');
            variantSection.appendChild(createFormGroup('AP:', apInput));
           
            const typeSelect = createSelect(
                ['', '攻击', '技能', '强化'],
                insp.type || '',
                (value) => {
                     insp.type = value;
                    updateCodePreview();
                }
            );
            variantSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
           
            const dictSection = createElement('div', 'form-group');
            const dictLabel = createElement('div', 'form-label');
            dictLabel.textContent = '机制:';
            dictSection.appendChild(dictLabel);
 
            const dictInput = createInput('text', insp.dict, (value) => {
                insp.dict = value;
                updateCodePreview();
            }, '多个机制使用、隔开');
           
            const dictToolbar = createElement('div', 'button-group');
            const dictBtn = createButton('词典', 'btn', () => {
                insertTextAtCursor(dictInput, '{{词典|选择文字}}', true);
            });
            dictToolbar.appendChild(dictBtn);
           
            dictSection.appendChild(dictToolbar);
            dictSection.appendChild(dictInput);
            variantSection.appendChild(dictSection);
           
            const descSection = createElement('div', 'form-group');
            const descLabel = createElement('div', 'form-label');
            descLabel.textContent = '描述:';
            descSection.appendChild(descLabel);
           
            const descInput = createInput('textarea', insp.desc_global, (value) => {
                insp.desc_global = value;
                updateCodePreview();
            }, '变体描述');
            descInput.style.minHeight = '150px';
           
            const toolbar = createDescToolbar(descInput);
            descSection.appendChild(toolbar);
            descSection.appendChild(descInput);
            variantSection.appendChild(descSection);
           
            const deleteBtn = createButton('删除此变体', 'btn-danger', () => {
                if (confirm('确定要删除这个神光一闪吗?')) {
                    state.currentCard.var.god_inspiration[state.currentGod].splice(state.currentGodInspirationIndex, 1);
                    state.editMode = 'base';
                    state.currentGodInspirationIndex = null;
                    renderWithoutScroll();
                 }
                 }
             ));
             });
            deleteBtn.style.marginTop = '20px';
            deleteBtn.style.width = '100%';
            variantSection.appendChild(deleteBtn);
           
            container.appendChild(variantSection);
         }
         }
     }
     }
第564行: 第814行:
         if (!container) return;
         if (!container) return;
          
          
         const scrollPositions = {};
         const scrollPositions = {
         ['input', 'list', 'preview'].forEach(type => {
            input: 0,
            const section = container.querySelector(`.card-${type}-section`);
            list: 0,
            if (section) scrollPositions[type] = section.scrollTop;
            preview: 0
         });
        };
          
        const inputSection = container.querySelector('.card-input-section');
        const listSection = container.querySelector('.card-list-section');
        const previewSection = container.querySelector('.card-preview-section');
       
        if (inputSection) scrollPositions.input = inputSection.scrollTop;
        if (listSection) scrollPositions.list = listSection.scrollTop;
         if (previewSection) scrollPositions.preview = previewSection.scrollTop;
          
          
         render();
         render();
          
          
         requestAnimationFrame(() => {
         requestAnimationFrame(() => {
             ['input', 'list', 'preview'].forEach(type => {
             const newInputSection = container.querySelector('.card-input-section');
                const section = container.querySelector(`.card-${type}-section`);
            const newListSection = container.querySelector('.card-list-section');
                if (section) section.scrollTop = scrollPositions[type];
            const newPreviewSection = container.querySelector('.card-preview-section');
             });
           
            if (newInputSection) newInputSection.scrollTop = scrollPositions.input;
            if (newListSection) newListSection.scrollTop = scrollPositions.list;
             if (newPreviewSection) newPreviewSection.scrollTop = scrollPositions.preview;
         });
         });
     }
     }
      
      
     // 主渲染函数(续)
     // 渲染函数
     function render() {
     function render() {
         const container = document.getElementById('card-manager-container');
         const container = document.getElementById('card-manager-container');
第586行: 第847行:
          
          
         container.innerHTML = '';
         container.innerHTML = '';
       
         const manager = createElement('div', 'card-manager');
         const manager = createElement('div', 'card-manager');
          
          
         // 左侧输入区
         // 左侧输入区
         const inputSection = createElement('div', 'card-input-section');
         const inputSection = createElement('div', 'card-input-section');
        inputSection.appendChild(createElement('div', 'section-title', { textContent: '卡牌管理器' }));
          
          
         // 战斗员选择
         const title = createElement('div', 'section-title');
        title.textContent = '卡牌管理器';
        inputSection.appendChild(title);
       
         const fighterSelect = createSelect(state.fighters, state.currentFighter, async (value) => {
         const fighterSelect = createSelect(state.fighters, state.currentFighter, async (value) => {
             state.currentFighter = value;
             state.currentFighter = value;
             const loading = createElement('div', 'loading-indicator', { textContent: '正在加载卡牌数据...' });
             const loading = createElement('div', 'loading-indicator');
            loading.textContent = '正在加载卡牌数据...';
             document.body.appendChild(loading);
             document.body.appendChild(loading);
              
              
             try {
             try {
                 state.cards = await loadFighterCards(value);
                 const cards = await loadFighterCards(value);
                 state.currentCard = state.cards[0] || null;
                state.cards = cards;
                 state.currentCard = cards.length > 0 ? cards[0] : null;
                 state.editMode = 'base';
                 state.editMode = 'base';
                 state.currentInspirationIndex = null;
                 state.currentInspirationIndex = null;
第608行: 第874行:
                 alert('加载失败:' + error.message);
                 alert('加载失败:' + error.message);
             } finally {
             } finally {
                 loading.remove();
                 if (loading.parentNode) {
                    document.body.removeChild(loading);
                }
             }
             }
         });
         });
         inputSection.appendChild(createFormGroup('选择战斗员:', fighterSelect.wrapper));
         inputSection.appendChild(createFormGroup('选择战斗员:', fighterSelect.wrapper));
          
          
        // 默认信息区
         if (state.currentFighter) {
         if (state.currentFighter) {
             const defaultSection = createElement('div', 'default-info-section');
             const defaultSection = createElement('div', 'default-info-section');
             const defaultTitle = createElement('div', 'section-title', { textContent: '默认信息' });
             const defaultTitle = createElement('div', 'section-title');
            defaultTitle.textContent = '默认信息';
             defaultTitle.style.borderColor = '#ff9800';
             defaultTitle.style.borderColor = '#ff9800';
             defaultSection.appendChild(defaultTitle);
             defaultSection.appendChild(defaultTitle);
              
              
             ['order', 'ego'].forEach(key => {
             const orderInput = createInput('text', state.defaultInfo.order, (value) => {
                 const label = key === 'order' ? '卡牌顺序:' : '属性:';
                 state.defaultInfo.order = value;
                 const placeholder = key === 'order' ? 'card.order' : 'card.info.ego';
                 updateCodePreview();
                defaultSection.appendChild(createFormGroup(label,  
            }, 'card.order');
                    createInput('text', state.defaultInfo[key], v => { state.defaultInfo[key] = v; updateCodePreview(); }, placeholder)));
            defaultSection.appendChild(createFormGroup('卡牌顺序:', orderInput));
             });
           
            const egoInput = createInput('text', state.defaultInfo.ego, (value) => {
                state.defaultInfo.ego = value;
                updateCodePreview();
            }, 'card.info.ego');
             defaultSection.appendChild(createFormGroup('属性:', egoInput));
              
              
             inputSection.appendChild(defaultSection);
             inputSection.appendChild(defaultSection);
         }
         }
          
          
        // 卡牌编辑区
         if (state.currentCard) {
         if (state.currentCard) {
             if (state.editMode === 'base') {
             if (state.editMode === 'base') {
                 const cardSection = createElement('div', '');
                 const cardSection = createElement('div', '');
                 cardSection.style.marginTop = '20px';
                 cardSection.style.marginTop = '20px';
                cardSection.appendChild(createElement('div', 'section-title', { textContent: '卡牌信息' }));
                  
                  
                // 基础属性
                 const cardTitle = createElement('div', 'section-title');
                 const fields = [
                cardTitle.textContent = '卡牌信息';
                    { key: 'name', label: '卡牌名称:', type: 'input', placeholder: '卡牌名称', obj: state.currentCard },
                cardSection.appendChild(cardTitle);
                     { key: 'displayname', label: '显示名称:', type: 'input', placeholder: '显示名称' },
               
                     { key: 'art', label: '图片:', type: 'input', placeholder: '图片文件名' },
                const nameInput = createInput('text', state.currentCard.name, (value) => {
                     { key: 'group', label: '分组:', type: 'input', placeholder: '分组' },
                    state.currentCard.name = value;
                     { key: 'rarity', label: '稀有度:', type: 'select', options: ['', '白', '蓝', '橙', '彩'] },
                    updateCodePreview();
                     { key: 'god', label: '神明:', type: 'select', options: ['', 'circen', 'diallos', 'nihilum', 'secred', 'vitor'] },
                    const cardItems = document.querySelectorAll('.card-item');
                     { key: 'ap', label: 'AP:', type: 'input', placeholder: 'AP (可以是数字、X、Ø等)' },
                     cardItems.forEach((item, idx) => {
                    { key: 'type', label: '类型:', type: 'select', options: ['', '攻击', '技能', '强化'] }
                        if (state.cards[idx] === state.currentCard) {
                ];
                            const nameEl = item.querySelector('.card-item-name');
                            if (nameEl) nameEl.textContent = value || '未命名卡牌';
                        }
                    });
                }, '卡牌名称');
                cardSection.appendChild(createFormGroup('卡牌名称:', nameInput));
               
                const displaynameInput = createInput('text', state.currentCard.base.displayname, (value) => {
                     state.currentCard.base.displayname = value;
                    updateCodePreview();
                }, '显示名称');
                cardSection.appendChild(createFormGroup('显示名称:', displaynameInput));
               
                const artInput = createInput('text', state.currentCard.base.art, (value) => {
                    state.currentCard.base.art = value;
                     updateCodePreview();
                }, '图片文件名');
                cardSection.appendChild(createFormGroup('图片:', artInput));
               
                const groupInput = createInput('text', state.currentCard.base.group, (value) => {
                    state.currentCard.base.group = value;
                     updateCodePreview();
                }, '分组');
                cardSection.appendChild(createFormGroup('分组:', groupInput));
               
                const raritySelect = createSelect(
                    ['', '白', '蓝', '橙', '彩'],
                     state.currentCard.base.rarity || '',
                    (value) => {
                        state.currentCard.base.rarity = value;
                        updateCodePreview();
                    }
                );
                cardSection.appendChild(createFormGroup('稀有度:', raritySelect.wrapper));
               
                const godSelect = createSelect(
                    ['', 'circen', 'diallos', 'nihilum', 'secred', 'vitor'],
                     state.currentCard.base.god || '',
                    (value) => {
                        state.currentCard.base.god = value;
                        updateCodePreview();
                    }
                );
                cardSection.appendChild(createFormGroup('神明:', godSelect.wrapper));
               
                const apInput = createInput('text', state.currentCard.base.ap, (value) => {
                    state.currentCard.base.ap = value;
                    updateCodePreview();
                }, 'AP (可以是数字、X、Ø等)');
                cardSection.appendChild(createFormGroup('AP:', apInput));
                  
                  
                 fields.forEach(field => {
                 const typeSelect = createSelect(
                    const obj = field.obj || state.currentCard.base;
                     ['', '攻击', '技能', '强化'],
                     if (field.type === 'select') {
                    state.currentCard.base.type || '',
                        const select = createSelect(field.options, obj[field.key] || '', v => { obj[field.key] = v; updateCodePreview(); });
                    (value) => {
                        cardSection.appendChild(createFormGroup(field.label, select.wrapper));
                        state.currentCard.base.type = value;
                    } else {
                         updateCodePreview();
                        const input = createInput('text', obj[field.key], v => {
                            obj[field.key] = v;
                            updateCodePreview();
                            if (field.key === 'name') {
                                const cardItems = document.querySelectorAll('.card-item');
                                cardItems.forEach((item, idx) => {
                                    if (state.cards[idx] === state.currentCard) {
                                        const nameEl = item.querySelector('.card-item-name');
                                        if (nameEl) nameEl.textContent = v || '未命名卡牌';
                                    }
                                });
                            }
                         }, field.placeholder);
                        cardSection.appendChild(createFormGroup(field.label, input));
                     }
                     }
                 });
                 );
                cardSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
                  
                  
                 // 机制
                 // 机制输入区域
                 const dictSection = createElement('div', 'form-group');
                 const dictSection = createElement('div', 'form-group');
                 dictSection.appendChild(createElement('div', 'form-label', { textContent: '机制:' }));
                 const dictLabel = createElement('div', 'form-label');
                dictLabel.textContent = '机制:';
                dictSection.appendChild(dictLabel);
   
                const dictInput = createInput('text', state.currentCard.base.dict, (value) => {
                    state.currentCard.base.dict = value;
                    updateCodePreview();
                }, '多个机制使用、隔开');
               
                 const dictToolbar = createElement('div', 'button-group');
                 const dictToolbar = createElement('div', 'button-group');
                 const dictInput = createInput('text', state.currentCard.base.dict, v => { state.currentCard.base.dict = v; updateCodePreview(); }, '多个机制使用、隔开');
                 const dictBtn = createButton('词典', 'btn', () => {
                dictToolbar.appendChild(createButton('词典', 'btn', () => insertTextAtCursor(dictInput, '{{词典|选择文字}}', true)));
                    insertTextAtCursor(dictInput, '{{词典|选择文字}}', true);
                });
                dictToolbar.appendChild(dictBtn);
               
                 dictSection.appendChild(dictToolbar);
                 dictSection.appendChild(dictToolbar);
                 dictSection.appendChild(dictInput);
                 dictSection.appendChild(dictInput);
                 cardSection.appendChild(dictSection);
                 cardSection.appendChild(dictSection);
                  
                  
                 // 描述
                 // 描述区域
                 const descSection = createElement('div', 'form-group');
                 const descSection = createElement('div', 'form-group');
                 descSection.appendChild(createElement('div', 'form-label', { textContent: '描述:' }));
                 const descLabel = createElement('div', 'form-label');
                 const descInput = createInput('textarea', state.currentCard.base.desc_global,  
                descLabel.textContent = '描述:';
                    v => { state.currentCard.base.desc_global = v; updateCodePreview(); }, '卡牌描述');
                descSection.appendChild(descLabel);
               
                 const descInput = createInput('textarea', state.currentCard.base.desc_global, (value) => {
                    state.currentCard.base.desc_global = value;
                    updateCodePreview();
                }, '卡牌描述');
                 descInput.style.minHeight = '150px';
                 descInput.style.minHeight = '150px';
                 descSection.appendChild(createDescToolbar(descInput));
               
                const toolbar = createDescToolbar(descInput);
                 descSection.appendChild(toolbar);
                 descSection.appendChild(descInput);
                 descSection.appendChild(descInput);
                 cardSection.appendChild(descSection);
                 cardSection.appendChild(descSection);
                  
                  
                // 衍生卡牌
                 const subInput = createInput('textarea', state.currentCard.base.sub, (value) => {
                 const subInput = createInput('textarea', state.currentCard.base.sub,  
                    state.currentCard.base.sub = value;
                    v => { state.currentCard.base.sub = v; updateCodePreview(); }, '衍生卡牌');
                    updateCodePreview();
                }, '衍生卡牌');
                 subInput.style.minHeight = '35px';
                 subInput.style.minHeight = '35px';
                 cardSection.appendChild(createFormGroup('衍生卡牌:', subInput));
                 cardSection.appendChild(createFormGroup('衍生卡牌:', subInput));
                  
                  
                // 是否存在灵光一闪
                 const inspirationCheckbox = createCheckbox(state.currentCard.base.isinspiration, (value) => {
                 const inspirationCheckbox = createCheckbox(state.currentCard.base.isinspiration, value => {
                     state.currentCard.base.isinspiration = value;
                     state.currentCard.base.isinspiration = value;
                     if (!value) state.currentCard.var.inspiration = [];
                     if (!value) {
                        state.currentCard.var.inspiration = [];
                    }
                     updateCodePreview();
                     updateCodePreview();
                     renderWithoutScroll();
                     renderWithoutScroll();
第707行: 第1,034行:
                 cardSection.appendChild(createFormGroup('是否存在灵光一闪:', inspirationCheckbox));
                 cardSection.appendChild(createFormGroup('是否存在灵光一闪:', inspirationCheckbox));
                  
                  
                // 是否存在神光一闪
                 const godInspirationCheckbox = createCheckbox(state.currentCard.base.isgod_inspiration, (value) => {
                 const godInspirationCheckbox = createCheckbox(state.currentCard.base.isgod_inspiration, value => {
                     state.currentCard.base.isgod_inspiration = value;
                     state.currentCard.base.isgod_inspiration = value;
                     if (!value) {
                     if (!value) {
                         state.currentCard.var.god_inspiration = {
                         state.currentCard.var.god_inspiration = {
                             circen: [], diallos: [], nihilum: [], secred: [], vitor: []
                             circen: [],
                            diallos: [],
                            nihilum: [],
                            secred: [],
                            vitor: []
                         };
                         };
                     }
                     }
第722行: 第1,052行:
                 inputSection.appendChild(cardSection);
                 inputSection.appendChild(cardSection);
                  
                  
                // 保存卡牌按钮
                 const saveCardBtn = createButton('保存卡牌', 'btn-primary', () => {
                 const saveCardBtn = createButton('保存卡牌', 'btn-primary', () => {
                     updateCodePreview();
                     updateCodePreview();
                     alert('卡牌已保存到代码预览!');
                     alert('卡牌已保存到代码预览!');
                 });
                 });
                 saveCardBtn.style.cssText = 'margin-top:20px;width:100%;';
                 saveCardBtn.style.marginTop = '20px';
                saveCardBtn.style.width = '100%';
                 inputSection.appendChild(saveCardBtn);
                 inputSection.appendChild(saveCardBtn);
                  
                  
第735行: 第1,065行:
         }
         }
          
          
        // 添加新卡牌按钮
         if (state.currentFighter && state.editMode === 'base') {
         if (state.currentFighter && state.editMode === 'base') {
             const addCardBtn = createButton('+ 新增卡牌', 'btn-success', () => {
             const addCardBtn = createButton('+ 新增卡牌', 'btn-success', () => {
第747行: 第1,076行:
                 renderWithoutScroll();
                 renderWithoutScroll();
             });
             });
             addCardBtn.style.cssText = 'margin-top:20px;width:100%;';
             addCardBtn.style.marginTop = '20px';
            addCardBtn.style.width = '100%';
             inputSection.appendChild(addCardBtn);
             inputSection.appendChild(addCardBtn);
         }
         }
第756行: 第1,086行:
         const listSection = createElement('div', 'card-list-section');
         const listSection = createElement('div', 'card-list-section');
          
          
        // 卡牌列表
         const cardListContainer = createElement('div', 'list-container');
         const cardListContainer = createElement('div', 'list-container');
         cardListContainer.style.minHeight = '250px';
         cardListContainer.style.minHeight = '250px';
         const cardListHeader = createElement('div', 'list-header');
         const cardListHeader = createElement('div', 'list-header');
         cardListHeader.appendChild(createElement('div', 'list-title', { textContent: '卡牌列表' }));
         const cardListTitle = createElement('div', 'list-title');
        cardListTitle.textContent = '卡牌列表';
        cardListHeader.appendChild(cardListTitle);
         cardListContainer.appendChild(cardListHeader);
         cardListContainer.appendChild(cardListHeader);
          
          
         if (state.cards.length === 0) {
         if (state.cards.length === 0) {
             const emptyState = createElement('div', 'empty-state');
             const emptyState = createElement('div', 'empty-state');
             emptyState.appendChild(createElement('div', 'empty-state-icon', { textContent: '📋' }));
             const emptyIcon = createElement('div', 'empty-state-icon');
             emptyState.appendChild(createElement('div', 'empty-state-text', { textContent: '暂无卡牌,点击"新增卡牌"开始创建' }));
            emptyIcon.textContent = '📋';
             const emptyText = createElement('div', 'empty-state-text');
            emptyText.textContent = '暂无卡牌,点击"新增卡牌"开始创建';
            emptyState.appendChild(emptyIcon);
            emptyState.appendChild(emptyText);
             cardListContainer.appendChild(emptyState);
             cardListContainer.appendChild(emptyState);
         } else {
         } else {
第772行: 第1,107行:
                 const cardItem = createElement('div', 'card-item' + (state.currentCard === card && state.editMode === 'base' ? ' active' : ''));
                 const cardItem = createElement('div', 'card-item' + (state.currentCard === card && state.editMode === 'base' ? ' active' : ''));
                  
                  
                 cardItem.appendChild(createElement('div', 'card-item-name', { textContent: card.name || '未命名卡牌' }));
                 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(' | ') || '暂无信息';
                  
                  
                 const infoText = [card.base.type, card.base.ap !== '' ? 'AP:' + card.base.ap : '', card.base.rarity]
                 cardItem.appendChild(cardName);
                    .filter(Boolean).join(' | ') || '暂无信息';
                 cardItem.appendChild(cardInfo);
                 cardItem.appendChild(createElement('div', 'card-item-info', { textContent: infoText }));
                  
                  
                 cardItem.addEventListener('click', () => {
                 cardItem.addEventListener('click', () => {
第786行: 第1,128行:
                 });
                 });
                  
                  
                 const deleteBtn = createButton('删除', 'btn-danger', e => {
                 const deleteBtn = createButton('删除', 'btn-danger', (e) => {
                     e.stopPropagation();
                     e.stopPropagation();
                     if (confirm(`确定要删除卡牌"${card.name}"吗?`)) {
                     if (confirm('确定要删除卡牌"' + card.name + '"吗?')) {
                         state.cards.splice(index, 1);
                         state.cards.splice(index, 1);
                         if (state.currentCard === card) {
                         if (state.currentCard === card) {
第808行: 第1,150行:
         listSection.appendChild(cardListContainer);
         listSection.appendChild(cardListContainer);
          
          
        // 灵光一闪列表
         if (state.currentCard && state.currentCard.base.isinspiration) {
         if (state.currentCard && state.currentCard.base.isinspiration) {
             const inspirationSection = createElement('div', 'inspiration-section');
             const inspirationSection = createElement('div', 'inspiration-section');
             inspirationSection.style.minHeight = '250px';
             inspirationSection.style.minHeight = '250px';
            const inspirationHeader = createElement('div', 'list-header');
            const inspirationTitle = createElement('div', 'list-title');
            inspirationTitle.textContent = '灵光一闪列表';
            inspirationHeader.appendChild(inspirationTitle);
              
              
             const inspirationHeader = createElement('div', 'list-header');
             const addInspirationBtn = createButton('+ 添加', 'btn-primary', () => {
            inspirationHeader.appendChild(createElement('div', 'list-title', { textContent: '灵光一闪列表' }));
            inspirationHeader.appendChild(createButton('+ 添加', 'btn-primary', () => {
                 const newIndex = state.currentCard.var.inspiration.length;
                 const newIndex = state.currentCard.var.inspiration.length;
                 state.currentCard.var.inspiration.push({ ap: '', type: '', desc_global: '', dict: '' });
                 state.currentCard.var.inspiration.push({
                    ap: '',
                    type: '',
                    desc_global: '',
                    dict: ''
                });
                 state.editMode = 'inspiration';
                 state.editMode = 'inspiration';
                 state.currentInspirationIndex = newIndex;
                 state.currentInspirationIndex = newIndex;
                 state.currentGodInspirationIndex = null;
                 state.currentGodInspirationIndex = null;
                 render();
                 render();
             }));
             });
            inspirationHeader.appendChild(addInspirationBtn);
             inspirationSection.appendChild(inspirationHeader);
             inspirationSection.appendChild(inspirationHeader);
              
              
第828行: 第1,177行:
                 const emptyState = createElement('div', 'empty-state');
                 const emptyState = createElement('div', 'empty-state');
                 emptyState.style.padding = '20px';
                 emptyState.style.padding = '20px';
                 emptyState.appendChild(createElement('div', 'empty-state-text', { textContent: '暂无灵光一闪,点击"添加"创建' }));
                 const emptyText = createElement('div', 'empty-state-text');
                emptyText.textContent = '暂无灵光一闪,点击"添加"创建';
                emptyState.appendChild(emptyText);
                 inspirationSection.appendChild(emptyState);
                 inspirationSection.appendChild(emptyState);
             } else {
             } else {
                 state.currentCard.var.inspiration.forEach((insp, idx) => {
                 state.currentCard.var.inspiration.forEach((insp, idx) => {
                     const inspItem = createElement('div', 'inspiration-item-simple' +  
                     const inspItem = createElement('div',  
                         (state.editMode === 'inspiration' && state.currentInspirationIndex === idx ? ' active' : ''));
                        'inspiration-item-simple' +  
                         (state.editMode === 'inspiration' && state.currentInspirationIndex === idx ? ' active' : '')
                    );
                   
                    const inspName = createElement('div', 'inspiration-item-name');
                    inspName.textContent = `灵光 #${idx + 1}`;
                      
                      
                     inspItem.appendChild(createElement('div', 'inspiration-item-name', { textContent: `灵光 #${idx + 1}` }));
                     const inspInfo = createElement('div', 'inspiration-item-info');
                    const infoText = [];
                    if (insp.type) infoText.push(insp.type);
                    if (insp.ap !== '') infoText.push('AP:' + insp.ap);
                    inspInfo.textContent = infoText.join(' | ') || '点击编辑';
                      
                      
                     const infoText = [insp.type, insp.ap !== '' ? 'AP:' + insp.ap : '']
                     inspItem.appendChild(inspName);
                        .filter(Boolean).join(' | ') || '点击编辑';
                     inspItem.appendChild(inspInfo);
                     inspItem.appendChild(createElement('div', 'inspiration-item-info', { textContent: infoText }));
                      
                      
                     inspItem.addEventListener('click', () => {
                     inspItem.addEventListener('click', () => {
第855行: 第1,214行:
         }
         }
          
          
        // 神光一闪列表
         if (state.currentCard && state.currentCard.base.isgod_inspiration) {
         if (state.currentCard && state.currentCard.base.isgod_inspiration) {
             const godInspirationSection = createElement('div', 'inspiration-section');
             const godInspirationSection = createElement('div', 'inspiration-section');
             godInspirationSection.style.minHeight = '250px';
             godInspirationSection.style.minHeight = '250px';
           
             const godInspirationHeader = createElement('div', 'list-header');
             const godInspirationHeader = createElement('div', 'list-header');
             godInspirationHeader.appendChild(createElement('div', 'list-title', { textContent: '神光一闪列表' }));
             const godInspirationTitle = createElement('div', 'list-title');
            godInspirationTitle.textContent = '神光一闪列表';
            godInspirationHeader.appendChild(godInspirationTitle);
             godInspirationSection.appendChild(godInspirationHeader);
             godInspirationSection.appendChild(godInspirationHeader);
              
              
            // 神明选择标签
             const godSelectGroup = createElement('div', 'god-select-group');
             const godSelectGroup = createElement('div', 'god-select-group');
             ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => {
             ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => {
                 const godTab = createElement('div', 'god-tab' + (state.currentGod === god ? ' active' : ''),
                 const godTab = createElement('div', 'god-tab' + (state.currentGod === god ? ' active' : ''));
                    { textContent: god, onclick: () => { state.currentGod = god; renderWithoutScroll(); } });
                godTab.textContent = god;
                godTab.addEventListener('click', () => {
                    state.currentGod = god;
                    renderWithoutScroll();
                });
                 godSelectGroup.appendChild(godTab);
                 godSelectGroup.appendChild(godTab);
             });
             });
             godInspirationSection.appendChild(godSelectGroup);
             godInspirationSection.appendChild(godSelectGroup);
              
              
            // 当前神明的神光一闪列表
             const currentGodInspirations = state.currentCard.var.god_inspiration[state.currentGod];
             const currentGodInspirations = state.currentCard.var.god_inspiration[state.currentGod];
              
              
             const addGodInspirationBtn = createButton(`+ 添加 ${state.currentGod} 神光`, 'btn-primary', () => {
             const addGodInspirationBtn = createButton('+ 添加 ' + state.currentGod + ' 神光', 'btn-primary', () => {
                 const newIndex = currentGodInspirations.length;
                 const newIndex = currentGodInspirations.length;
                 currentGodInspirations.push({ ap: '', type: '', desc_global: '', dict: '' });
                 currentGodInspirations.push({
                    ap: '',
                    type: '',
                    desc_global: '',
                    dict: ''
                });
                 state.editMode = 'god_inspiration';
                 state.editMode = 'god_inspiration';
                 state.currentGodInspirationIndex = newIndex;
                 state.currentGodInspirationIndex = newIndex;
第884行: 第1,250行:
                 render();
                 render();
             });
             });
             addGodInspirationBtn.style.cssText = 'margin-bottom:10px;width:100%;';
             addGodInspirationBtn.style.marginBottom = '10px';
            addGodInspirationBtn.style.width = '100%';
             godInspirationSection.appendChild(addGodInspirationBtn);
             godInspirationSection.appendChild(addGodInspirationBtn);
              
              
第890行: 第1,257行:
                 const emptyState = createElement('div', 'empty-state');
                 const emptyState = createElement('div', 'empty-state');
                 emptyState.style.padding = '20px';
                 emptyState.style.padding = '20px';
                 emptyState.appendChild(createElement('div', 'empty-state-text', { textContent: `暂无 ${state.currentGod} 神光一闪` }));
                 const emptyText = createElement('div', 'empty-state-text');
                emptyText.textContent = `暂无 ${state.currentGod} 神光一闪`;
                emptyState.appendChild(emptyText);
                 godInspirationSection.appendChild(emptyState);
                 godInspirationSection.appendChild(emptyState);
             } else {
             } else {
                 currentGodInspirations.forEach((insp, idx) => {
                 currentGodInspirations.forEach((insp, idx) => {
                     const inspItem = createElement('div', 'inspiration-item-simple' +  
                     const inspItem = createElement('div',  
                         (state.editMode === 'god_inspiration' && state.currentGodInspirationIndex === idx ? ' active' : ''));
                        'inspiration-item-simple' +  
                         (state.editMode === 'god_inspiration' &&  
                        state.currentGodInspirationIndex === idx ? ' active' : '')
                    );
                      
                      
                     inspItem.appendChild(createElement('div', 'inspiration-item-name', { textContent: `${state.currentGod} 神光 #${idx + 1}` }));
                     const inspName = createElement('div', 'inspiration-item-name');
                    inspName.textContent = `${state.currentGod} 神光 #${idx + 1}`;
                      
                      
                     const infoText = [insp.type, insp.ap !== '' ? 'AP:' + insp.ap : '']
                    const inspInfo = createElement('div', 'inspiration-item-info');
                        .filter(Boolean).join(' | ') || '点击编辑';
                     const infoText = [];
                     inspItem.appendChild(createElement('div', 'inspiration-item-info', { textContent: infoText }));
                    if (insp.type) infoText.push(insp.type);
                    if (insp.ap !== '') infoText.push('AP:' + insp.ap);
                    inspInfo.textContent = infoText.join(' | ') || '点击编辑';
                   
                     inspItem.appendChild(inspName);
                    inspItem.appendChild(inspInfo);
                      
                      
                     inspItem.addEventListener('click', () => {
                     inspItem.addEventListener('click', () => {
第921行: 第1,299行:
         // 右侧预览区
         // 右侧预览区
         const previewSection = createElement('div', 'card-preview-section');
         const previewSection = createElement('div', 'card-preview-section');
         previewSection.appendChild(createElement('div', 'section-title', { textContent: 'Lua 代码预览' }));
         const previewTitle = createElement('div', 'section-title');
        previewTitle.textContent = 'Lua 代码预览';
        previewSection.appendChild(previewTitle);
          
          
        // 复制按钮
         const copyBtn = createButton('复制代码', 'btn-primary', () => {
         const copyBtn = createButton('复制代码', 'btn-primary', () => {
             const code = generateLuaCode();
             const code = generateLuaCode();
第941行: 第1,320行:
             });
             });
         });
         });
         copyBtn.style.cssText = 'margin-bottom:10px;width:100%;';
         copyBtn.style.marginBottom = '10px';
        copyBtn.style.width = '100%';
         previewSection.appendChild(copyBtn);
         previewSection.appendChild(copyBtn);
          
          
        // 保存按钮
         const saveBtn = createButton('保存到Wiki', 'btn-success', async () => {
         const saveBtn = createButton('保存到Wiki', 'btn-success', async () => {
             if (!state.currentFighter) {
             if (!state.currentFighter) {
第952行: 第1,331行:
              
              
             const code = generateLuaCode();
             const code = generateLuaCode();
             const loading = createElement('div', 'loading-indicator', { textContent: '正在保存...' });
            const pageName = '模块:卡牌/' + state.currentFighter;
           
             const loading = createElement('div', 'loading-indicator');
            loading.textContent = '正在保存...';
             document.body.appendChild(loading);
             document.body.appendChild(loading);
              
              
第959行: 第1,341行:
                 await api.postWithToken('csrf', {
                 await api.postWithToken('csrf', {
                     action: 'edit',
                     action: 'edit',
                     title: '模块:卡牌/' + state.currentFighter,
                     title: pageName,
                     text: code,
                     text: code,
                     summary: '通过卡牌管理器更新卡牌数据',
                     summary: '通过卡牌管理器更新卡牌数据',
第969行: 第1,351行:
                 alert('保存失败:' + error);
                 alert('保存失败:' + error);
             } finally {
             } finally {
                 loading.remove();
                 if (loading.parentNode) {
                    document.body.removeChild(loading);
                }
             }
             }
         });
         });
         saveBtn.style.cssText = 'margin-bottom:10px;width:100%;';
         saveBtn.style.marginBottom = '10px';
        saveBtn.style.width = '100%';
         previewSection.appendChild(saveBtn);
         previewSection.appendChild(saveBtn);
          
          
        // 代码显示
         const codePreview = createElement('div', 'code-preview');
         const codePreview = createElement('div', 'code-preview', { textContent: generateLuaCode() });
        codePreview.textContent = generateLuaCode();
         previewSection.appendChild(codePreview);
         previewSection.appendChild(codePreview);
          
          
         manager.appendChild(previewSection);
         manager.appendChild(previewSection);
       
         container.appendChild(manager);
         container.appendChild(manager);
     }
     }
第989行: 第1,375行:
             container = createElement('div', '');
             container = createElement('div', '');
             container.id = 'card-manager-container';
             container.id = 'card-manager-container';
           
             const content = document.getElementById('mw-content-text');
             const content = document.getElementById('mw-content-text');
             if (content) content.insertBefore(container, content.firstChild);
             if (content) {
             else document.body.appendChild(container);
                content.insertBefore(container, content.firstChild);
             } else {
                document.body.appendChild(container);
            }
         }
         }
          
          
         const loading = createElement('div', 'loading-indicator', { textContent: '正在初始化...' });
         const loading = createElement('div', 'loading-indicator');
        loading.textContent = '正在初始化...';
         document.body.appendChild(loading);
         document.body.appendChild(loading);
          
          
第1,003行: 第1,394行:
                 state.currentFighter = state.fighters[0];
                 state.currentFighter = state.fighters[0];
                 state.cards = await loadFighterCards(state.currentFighter);
                 state.cards = await loadFighterCards(state.currentFighter);
                 if (state.cards.length > 0) state.currentCard = state.cards[0];
                 if (state.cards.length > 0) {
                    state.currentCard = state.cards[0];
                }
             }
             }
              
              
第1,011行: 第1,404行:
             alert('初始化失败:' + error.message);
             alert('初始化失败:' + error.message);
         } finally {
         } finally {
             loading.remove();
             if (loading.parentNode) {
                document.body.removeChild(loading);
            }
         }
         }
     }
     }
第1,022行: 第1,417行:
      
      
     window.CardManager = {
     window.CardManager = {
         init, render, state,
         init: init,
        render: render,
        state: state,
         createCard: createEmptyCard,
         createCard: createEmptyCard,
         generateCode: generateLuaCode
         generateCode: generateLuaCode

2025年10月24日 (五) 15:56的版本

(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',
        currentInspirationIndex: null,
        currentGodInspirationIndex: null
    };
    
    // 卡牌数据结构
    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);
                // 触发input事件以更新数据
                element.dispatchEvent(new Event('input', { bubbles: true }));
                return;
            }
        }
        
        document.execCommand('insertText', false, text);
    }
    
    // 创建描述工具栏(统一函数)
    function createDescToolbar(descInput) {
        const toolbar = createElement('div', 'button-group');
        
        const buttons = [
            { text: '蓝色文本', className: 'btn btn-blue', template: '{{文本|蓝|选择文字}}' },
            { text: '绿色文本', className: 'btn btn-green', template: '{{文本|绿|选择文字}}' },
            { text: '蓝色下划线', className: 'btn btn-blue', template: '{{文本|蓝|下划线|选择文字}}' },
            { text: '绿色下划线', className: 'btn btn-green', template: '{{文本|绿|下划线|选择文字}}' },
            { text: '绿色描边', className: 'btn btn-green', template: '{{描边|绿|选择文字}}' },
            { text: '词典', className: 'btn', template: '{{词典|选择文字}}' },
            { text: '换行', className: 'btn', template: '<br>' }
        ];
        
        buttons.forEach(btn => {
            const button = createButton(btn.text, btn.className, () => {
                insertTextAtCursor(descInput, btn.template, btn.text !== '换行');
            });
            toolbar.appendChild(button);
        });
        
        return toolbar;
    }
    
    // 辅助函数:提取字符串值
    function extractStringValue(text, key) {
        const pattern = new RegExp(key + '\\s*=\\s*"([^"]*)"', 'm');
        const match = text.match(pattern);
        return match ? match[1] : '';
    }
    
    // 辅助函数:提取数值
    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;
        
        for (let i = 0; i < content.length; i++) {
            const char = content[i];
            const prevChar = i > 0 ? content[i - 1] : '';
            
            if (char === '"' && prevChar !== '\\') {
                inString = !inString;
            }
            
            if (!inString) {
                if (char === '{') {
                    if (depth > 0) {
                        currentBlock += char;
                    }
                    depth++;
                } else if (char === '}') {
                    depth--;
                    if (depth === 0 && currentBlock) {
                        const inspiration = {};
                        
                        const ap = extractValue(currentBlock, 'ap');
                        if (ap) inspiration.ap = ap;
                        
                        const type = extractStringValue(currentBlock, 'type');
                        if (type) inspiration.type = type;
                        
                        const desc = extractStringValue(currentBlock, 'desc_global');
                        if (desc) inspiration.desc_global = desc;
                        
                        const dict = extractStringValue(currentBlock, 'dict');
                        if (dict) inspiration.dict = dict;
                        
                        if (Object.keys(inspiration).length > 0) {
                            inspirations.push(inspiration);
                        }
                        
                        currentBlock = '';
                    } else if (depth > 0) {
                        currentBlock += char;
                    }
                } else if (depth > 0) {
                    currentBlock += char;
                }
            } else if (depth > 0) {
                currentBlock += char;
            }
        }
        
        return inspirations;
    }
    
    // 辅助函数:使用括号计数提取块内容
    function extractBlock(content, startPattern) {
        const match = content.match(startPattern);
        if (!match) return null;
        
        let startIdx = match.index + match[0].length;
        let depth = 1;
        let endIdx = startIdx;
        let inString = false;
        
        for (let i = startIdx; i < content.length && depth > 0; i++) {
            const char = content[i];
            const prevChar = i > 0 ? content[i - 1] : '';
            
            if (char === '"' && prevChar !== '\\') {
                inString = !inString;
            }
            
            if (!inString) {
                if (char === '{') {
                    depth++;
                } else if (char === '}') {
                    depth--;
                    if (depth === 0) {
                        endIdx = i;
                        break;
                    }
                }
            }
        }
        
        return content.substring(startIdx, endIdx);
    }
    
    // 解析Lua代码
    function parseLuaCode(luaText) {
        const cards = [];
        const defaultInfo = {
            order: '',
            ego: ''
        };
        
        try {
            const orderMatch = luaText.match(/card\.order\s*=\s*\{\s*"([^"]+)"\s*\}/);
            if (orderMatch) {
                defaultInfo.order = orderMatch[1];
            }
            
            const egoMatch = luaText.match(/card\.info\s*=\s*\{[^}]*ego\s*=\s*"([^"]+)"/);
            if (egoMatch) {
                defaultInfo.ego = egoMatch[1];
            }
            
            const cardPattern = /card\["([^"]+)"\]\s*=\s*\{/g;
            let cardMatch;
            
            while ((cardMatch = cardPattern.exec(luaText)) !== null) {
                const cardName = cardMatch[1];
                const cardStartIdx = cardMatch.index + cardMatch[0].length;
                
                let depth = 1;
                let cardEndIdx = cardStartIdx;
                let inString = false;
                
                for (let i = cardStartIdx; i < luaText.length && depth > 0; i++) {
                    const char = luaText[i];
                    const prevChar = i > 0 ? luaText[i - 1] : '';
                    
                    if (char === '"' && prevChar !== '\\') {
                        inString = !inString;
                    }
                    
                    if (!inString) {
                        if (char === '{') {
                            depth++;
                        } else if (char === '}') {
                            depth--;
                            if (depth === 0) {
                                cardEndIdx = i;
                                break;
                            }
                        }
                    }
                }
                
                const cardContent = luaText.substring(cardStartIdx, cardEndIdx);
                
                const card = createEmptyCard();
                card.name = cardName;
                
                const baseContent = extractBlock(cardContent, /base\s*=\s*\{/);
                if (baseContent) {
                    card.base.displayname = extractStringValue(baseContent, 'displayname') || '';
                    card.base.art = extractStringValue(baseContent, 'art') || '';
                    card.base.group = extractStringValue(baseContent, 'group') || '';
                    card.base.rarity = extractStringValue(baseContent, 'rarity') || '';
                    card.base.god = extractStringValue(baseContent, 'god') || '';
                    card.base.ap = extractValue(baseContent, 'ap') || '';
                    card.base.type = extractStringValue(baseContent, 'type') || '';
                    card.base.dict = extractStringValue(baseContent, 'dict') || '';
                    card.base.desc_global = extractStringValue(baseContent, 'desc_global') || '';
                    card.base.sub = extractStringValue(baseContent, 'sub') || '';
                    card.base.isinspiration = extractNumberValue(baseContent, 'isinspiration') || 0;
                    card.base.isgod_inspiration = extractNumberValue(baseContent, 'isgod_inspiration') || 0;
                }
                
                const varContent = extractBlock(cardContent, /var\s*=\s*\{/);
                if (varContent) {
                    const inspirationContent = extractBlock(varContent, /inspiration\s*=\s*\{/);
                    if (inspirationContent) {
                        card.var.inspiration = parseInspirationList(inspirationContent);
                    }
                    
                    const godInspirationContent = extractBlock(varContent, /god_inspiration\s*=\s*\{/);
                    if (godInspirationContent) {
                        ['circen', 'diallos', 'nihilum', 'secred', 'vitor'].forEach(god => {
                            const godPattern = new RegExp(god + '\\s*=\\s*\\{');
                            const godContent = extractBlock(godInspirationContent, godPattern);
                            if (godContent) {
                                card.var.god_inspiration[god] = parseInspirationList(godContent);
                            }
                        });
                    }
                }
                
                cards.push(card);
            }
            
        } catch (error) {
            console.error('解析Lua代码失败:', error);
        }
        
        return { cards, defaultInfo };
    }
    
    // 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';
        
        if (state.defaultInfo.order) {
            code += `card.order = { "${escapeLuaString(state.defaultInfo.order)}" }\n\n`;
        }
        
        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';
            
            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,
                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;
            
            const parsed = parseLuaCode(content);
            
            state.defaultInfo = parsed.defaultInfo;
            
            return parsed.cards;
            
        } catch (error) {
            console.error('加载卡牌数据失败:', error);
            return [];
        }
    }
    
    // 更新代码预览(不触发滚动)
    function updateCodePreview() {
        const codePreview = document.querySelector('.code-preview');
        if (codePreview) {
            codePreview.textContent = generateLuaCode();
        }
    }
    
    // 渲染变体卡牌编辑表单
    function renderVariantEditor(container) {
        if (!state.currentCard) return;
        
        if (state.editMode === 'inspiration' && state.currentInspirationIndex !== null) {
            const insp = state.currentCard.var.inspiration[state.currentInspirationIndex];
            if (!insp) return;
            
            const variantSection = createElement('div', 'variant-edit-section');
            const variantTitle = createElement('div', 'section-title');
            variantTitle.textContent = `灵光一闪 #${state.currentInspirationIndex + 1}`;
            variantTitle.style.borderColor = '#9c27b0';
            variantSection.appendChild(variantTitle);
            
            const backBtn = createButton('← 返回基础信息', 'btn', () => {
                state.editMode = 'base';
                state.currentInspirationIndex = null;
                renderWithoutScroll();
            });
            backBtn.style.marginBottom = '15px';
            backBtn.style.width = '100%';
            variantSection.appendChild(backBtn);
            
            const apInput = createInput('text', insp.ap, (value) => {
                insp.ap = value;
                updateCodePreview();
            }, 'AP (数字、X、Ø)');
            variantSection.appendChild(createFormGroup('AP:', apInput));
            
            const typeSelect = createSelect(
                ['', '攻击', '技能', '强化'],
                insp.type || '',
                (value) => {
                    insp.type = value;
                    updateCodePreview();
                }
            );
            variantSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
            
            const dictSection = createElement('div', 'form-group');
            const dictLabel = createElement('div', 'form-label');
            dictLabel.textContent = '机制:';
            dictSection.appendChild(dictLabel);
    
            const dictInput = createInput('text', insp.dict, (value) => {
                insp.dict = value;
                updateCodePreview();
            }, '多个机制使用、隔开');
            
            const dictToolbar = createElement('div', 'button-group');
            const dictBtn = createButton('词典', 'btn', () => {
                insertTextAtCursor(dictInput, '{{词典|选择文字}}', true);
            });
            dictToolbar.appendChild(dictBtn);
            
            dictSection.appendChild(dictToolbar);
            dictSection.appendChild(dictInput);
            variantSection.appendChild(dictSection);
            
            const descSection = createElement('div', 'form-group');
            const descLabel = createElement('div', 'form-label');
            descLabel.textContent = '描述:';
            descSection.appendChild(descLabel);
            
            const descInput = createInput('textarea', insp.desc_global, (value) => {
                insp.desc_global = value;
                updateCodePreview();
            }, '变体描述');
            descInput.style.minHeight = '150px';
            
            const toolbar = createDescToolbar(descInput);
            descSection.appendChild(toolbar);
            descSection.appendChild(descInput);
            variantSection.appendChild(descSection);
            
            const deleteBtn = createButton('删除此变体', 'btn-danger', () => {
                if (confirm('确定要删除这个灵光一闪吗?')) {
                    state.currentCard.var.inspiration.splice(state.currentInspirationIndex, 1);
                    state.editMode = 'base';
                    state.currentInspirationIndex = null;
                    renderWithoutScroll();
                }
            });
            deleteBtn.style.marginTop = '20px';
            deleteBtn.style.width = '100%';
            variantSection.appendChild(deleteBtn);
            
            container.appendChild(variantSection);
            
        } else if (state.editMode === 'god_inspiration' && state.currentGodInspirationIndex !== null) {
            const insp = state.currentCard.var.god_inspiration[state.currentGod][state.currentGodInspirationIndex];
            if (!insp) return;
            
            const variantSection = createElement('div', 'variant-edit-section');
            const variantTitle = createElement('div', 'section-title');
            variantTitle.textContent = `${state.currentGod} 神光 #${state.currentGodInspirationIndex + 1}`;
            variantTitle.style.borderColor = '#673ab7';
            variantSection.appendChild(variantTitle);
            
            const backBtn = createButton('← 返回基础信息', 'btn', () => {
                state.editMode = 'base';
                state.currentGodInspirationIndex = null;
                renderWithoutScroll();
            });
            backBtn.style.marginBottom = '15px';
            backBtn.style.width = '100%';
            variantSection.appendChild(backBtn);
            
            const apInput = createInput('text', insp.ap, (value) => {
                insp.ap = value;
                updateCodePreview();
            }, 'AP (数字、X、Ø)');
            variantSection.appendChild(createFormGroup('AP:', apInput));
            
            const typeSelect = createSelect(
                ['', '攻击', '技能', '强化'],
                insp.type || '',
                (value) => {
                    insp.type = value;
                    updateCodePreview();
                }
            );
            variantSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
            
            const dictSection = createElement('div', 'form-group');
            const dictLabel = createElement('div', 'form-label');
            dictLabel.textContent = '机制:';
            dictSection.appendChild(dictLabel);

            const dictInput = createInput('text', insp.dict, (value) => {
                insp.dict = value;
                updateCodePreview();
            }, '多个机制使用、隔开');
            
            const dictToolbar = createElement('div', 'button-group');
            const dictBtn = createButton('词典', 'btn', () => {
                insertTextAtCursor(dictInput, '{{词典|选择文字}}', true);
            });
            dictToolbar.appendChild(dictBtn);
            
            dictSection.appendChild(dictToolbar);
            dictSection.appendChild(dictInput);
            variantSection.appendChild(dictSection);
            
            const descSection = createElement('div', 'form-group');
            const descLabel = createElement('div', 'form-label');
            descLabel.textContent = '描述:';
            descSection.appendChild(descLabel);
            
            const descInput = createInput('textarea', insp.desc_global, (value) => {
                insp.desc_global = value;
                updateCodePreview();
            }, '变体描述');
            descInput.style.minHeight = '150px';
            
            const toolbar = createDescToolbar(descInput);
            descSection.appendChild(toolbar);
            descSection.appendChild(descInput);
            variantSection.appendChild(descSection);
            
            const deleteBtn = createButton('删除此变体', 'btn-danger', () => {
                if (confirm('确定要删除这个神光一闪吗?')) {
                    state.currentCard.var.god_inspiration[state.currentGod].splice(state.currentGodInspirationIndex, 1);
                    state.editMode = 'base';
                    state.currentGodInspirationIndex = null;
                    renderWithoutScroll();
                }
            });
            deleteBtn.style.marginTop = '20px';
            deleteBtn.style.width = '100%';
            variantSection.appendChild(deleteBtn);
            
            container.appendChild(variantSection);
        }
    }
    
    // 不触发滚动的渲染函数
    function renderWithoutScroll() {
        const container = document.getElementById('card-manager-container');
        if (!container) return;
        
        const scrollPositions = {
            input: 0,
            list: 0,
            preview: 0
        };
        
        const inputSection = container.querySelector('.card-input-section');
        const listSection = container.querySelector('.card-list-section');
        const previewSection = container.querySelector('.card-preview-section');
        
        if (inputSection) scrollPositions.input = inputSection.scrollTop;
        if (listSection) scrollPositions.list = listSection.scrollTop;
        if (previewSection) scrollPositions.preview = previewSection.scrollTop;
        
        render();
        
        requestAnimationFrame(() => {
            const newInputSection = container.querySelector('.card-input-section');
            const newListSection = container.querySelector('.card-list-section');
            const newPreviewSection = container.querySelector('.card-preview-section');
            
            if (newInputSection) newInputSection.scrollTop = scrollPositions.input;
            if (newListSection) newListSection.scrollTop = scrollPositions.list;
            if (newPreviewSection) newPreviewSection.scrollTop = scrollPositions.preview;
        });
    }
    
    // 渲染函数
    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;
                state.editMode = 'base';
                state.currentInspirationIndex = null;
                state.currentGodInspirationIndex = 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;
                updateCodePreview();
            }, 'card.order');
            defaultSection.appendChild(createFormGroup('卡牌顺序:', orderInput));
            
            const egoInput = createInput('text', state.defaultInfo.ego, (value) => {
                state.defaultInfo.ego = value;
                updateCodePreview();
            }, 'card.info.ego');
            defaultSection.appendChild(createFormGroup('属性:', egoInput));
            
            inputSection.appendChild(defaultSection);
        }
        
        if (state.currentCard) {
            if (state.editMode === 'base') {
                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;
                    updateCodePreview();
                    const cardItems = document.querySelectorAll('.card-item');
                    cardItems.forEach((item, idx) => {
                        if (state.cards[idx] === state.currentCard) {
                            const nameEl = item.querySelector('.card-item-name');
                            if (nameEl) nameEl.textContent = value || '未命名卡牌';
                        }
                    });
                }, '卡牌名称');
                cardSection.appendChild(createFormGroup('卡牌名称:', nameInput));
                
                const displaynameInput = createInput('text', state.currentCard.base.displayname, (value) => {
                    state.currentCard.base.displayname = value;
                    updateCodePreview();
                }, '显示名称');
                cardSection.appendChild(createFormGroup('显示名称:', displaynameInput));
                
                const artInput = createInput('text', state.currentCard.base.art, (value) => {
                    state.currentCard.base.art = value;
                    updateCodePreview();
                }, '图片文件名');
                cardSection.appendChild(createFormGroup('图片:', artInput));
                
                const groupInput = createInput('text', state.currentCard.base.group, (value) => {
                    state.currentCard.base.group = value;
                    updateCodePreview();
                }, '分组');
                cardSection.appendChild(createFormGroup('分组:', groupInput));
                
                const raritySelect = createSelect(
                    ['', '白', '蓝', '橙', '彩'],
                    state.currentCard.base.rarity || '',
                    (value) => {
                        state.currentCard.base.rarity = value;
                        updateCodePreview();
                    }
                );
                cardSection.appendChild(createFormGroup('稀有度:', raritySelect.wrapper));
                
                const godSelect = createSelect(
                    ['', 'circen', 'diallos', 'nihilum', 'secred', 'vitor'],
                    state.currentCard.base.god || '',
                    (value) => {
                        state.currentCard.base.god = value;
                        updateCodePreview();
                    }
                );
                cardSection.appendChild(createFormGroup('神明:', godSelect.wrapper));
                
                const apInput = createInput('text', state.currentCard.base.ap, (value) => {
                    state.currentCard.base.ap = value;
                    updateCodePreview();
                }, 'AP (可以是数字、X、Ø等)');
                cardSection.appendChild(createFormGroup('AP:', apInput));
                
                const typeSelect = createSelect(
                    ['', '攻击', '技能', '强化'],
                    state.currentCard.base.type || '',
                    (value) => {
                        state.currentCard.base.type = value;
                        updateCodePreview();
                    }
                );
                cardSection.appendChild(createFormGroup('类型:', typeSelect.wrapper));
                
                // 机制输入区域
                const dictSection = createElement('div', 'form-group');
                const dictLabel = createElement('div', 'form-label');
                dictLabel.textContent = '机制:';
                dictSection.appendChild(dictLabel);
    
                const dictInput = createInput('text', state.currentCard.base.dict, (value) => {
                    state.currentCard.base.dict = value;
                    updateCodePreview();
                }, '多个机制使用、隔开');
                
                const dictToolbar = createElement('div', 'button-group');
                const dictBtn = createButton('词典', 'btn', () => {
                    insertTextAtCursor(dictInput, '{{词典|选择文字}}', true);
                });
                dictToolbar.appendChild(dictBtn);
                
                dictSection.appendChild(dictToolbar);
                dictSection.appendChild(dictInput);
                cardSection.appendChild(dictSection);
                
                // 描述区域
                const descSection = createElement('div', 'form-group');
                const descLabel = createElement('div', 'form-label');
                descLabel.textContent = '描述:';
                descSection.appendChild(descLabel);
                
                const descInput = createInput('textarea', state.currentCard.base.desc_global, (value) => {
                    state.currentCard.base.desc_global = value;
                    updateCodePreview();
                }, '卡牌描述');
                descInput.style.minHeight = '150px';
                
                const toolbar = createDescToolbar(descInput);
                descSection.appendChild(toolbar);
                descSection.appendChild(descInput);
                cardSection.appendChild(descSection);
                
                const subInput = createInput('textarea', state.currentCard.base.sub, (value) => {
                    state.currentCard.base.sub = value;
                    updateCodePreview();
                }, '衍生卡牌');
                subInput.style.minHeight = '35px';
                cardSection.appendChild(createFormGroup('衍生卡牌:', subInput));
                
                const inspirationCheckbox = createCheckbox(state.currentCard.base.isinspiration, (value) => {
                    state.currentCard.base.isinspiration = value;
                    if (!value) {
                        state.currentCard.var.inspiration = [];
                    }
                    updateCodePreview();
                    renderWithoutScroll();
                });
                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: []
                        };
                    }
                    updateCodePreview();
                    renderWithoutScroll();
                });
                cardSection.appendChild(createFormGroup('是否存在神光一闪:', godInspirationCheckbox));
                
                inputSection.appendChild(cardSection);
                
                const saveCardBtn = createButton('保存卡牌', 'btn-primary', () => {
                    updateCodePreview();
                    alert('卡牌已保存到代码预览!');
                });
                saveCardBtn.style.marginTop = '20px';
                saveCardBtn.style.width = '100%';
                inputSection.appendChild(saveCardBtn);
                
            } else {
                renderVariantEditor(inputSection);
            }
        }
        
        if (state.currentFighter && state.editMode === 'base') {
            const addCardBtn = createButton('+ 新增卡牌', 'btn-success', () => {
                const newCard = createEmptyCard();
                newCard.name = '新卡牌' + (state.cards.length + 1);
                state.cards.push(newCard);
                state.currentCard = newCard;
                state.editMode = 'base';
                state.currentInspirationIndex = null;
                state.currentGodInspirationIndex = null;
                renderWithoutScroll();
            });
            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');
        cardListContainer.style.minHeight = '250px';
        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 && state.editMode === 'base' ? ' 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;
                    state.editMode = 'base';
                    state.currentInspirationIndex = null;
                    state.currentGodInspirationIndex = null;
                    renderWithoutScroll();
                });
                
                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;
                            state.editMode = 'base';
                            state.currentInspirationIndex = null;
                            state.currentGodInspirationIndex = null;
                        }
                        renderWithoutScroll();
                    }
                });
                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');
            inspirationSection.style.minHeight = '250px';
            const inspirationHeader = createElement('div', 'list-header');
            const inspirationTitle = createElement('div', 'list-title');
            inspirationTitle.textContent = '灵光一闪列表';
            inspirationHeader.appendChild(inspirationTitle);
            
            const addInspirationBtn = createButton('+ 添加', 'btn-primary', () => {
                const newIndex = state.currentCard.var.inspiration.length;
                state.currentCard.var.inspiration.push({
                    ap: '',
                    type: '',
                    desc_global: '',
                    dict: ''
                });
                state.editMode = 'inspiration';
                state.currentInspirationIndex = newIndex;
                state.currentGodInspirationIndex = null;
                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-simple' + 
                        (state.editMode === 'inspiration' && state.currentInspirationIndex === idx ? ' active' : '')
                    );
                    
                    const inspName = createElement('div', 'inspiration-item-name');
                    inspName.textContent = `灵光 #${idx + 1}`;
                    
                    const inspInfo = createElement('div', 'inspiration-item-info');
                    const infoText = [];
                    if (insp.type) infoText.push(insp.type);
                    if (insp.ap !== '') infoText.push('AP:' + insp.ap);
                    inspInfo.textContent = infoText.join(' | ') || '点击编辑';
                    
                    inspItem.appendChild(inspName);
                    inspItem.appendChild(inspInfo);
                    
                    inspItem.addEventListener('click', () => {
                        state.editMode = 'inspiration';
                        state.currentInspirationIndex = idx;
                        state.currentGodInspirationIndex = null;
                        renderWithoutScroll();
                    });
                    
                    inspirationSection.appendChild(inspItem);
                });
            }
            
            listSection.appendChild(inspirationSection);
        }
        
        if (state.currentCard && state.currentCard.base.isgod_inspiration) {
            const godInspirationSection = createElement('div', 'inspiration-section');
            godInspirationSection.style.minHeight = '250px';
            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;
                    renderWithoutScroll();
                });
                godSelectGroup.appendChild(godTab);
            });
            godInspirationSection.appendChild(godSelectGroup);
            
            const currentGodInspirations = state.currentCard.var.god_inspiration[state.currentGod];
            
            const addGodInspirationBtn = createButton('+ 添加 ' + state.currentGod + ' 神光', 'btn-primary', () => {
                const newIndex = currentGodInspirations.length;
                currentGodInspirations.push({
                    ap: '',
                    type: '',
                    desc_global: '',
                    dict: ''
                });
                state.editMode = 'god_inspiration';
                state.currentGodInspirationIndex = newIndex;
                state.currentInspirationIndex = null;
                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-simple' + 
                        (state.editMode === 'god_inspiration' && 
                         state.currentGodInspirationIndex === idx ? ' active' : '')
                    );
                    
                    const inspName = createElement('div', 'inspiration-item-name');
                    inspName.textContent = `${state.currentGod} 神光 #${idx + 1}`;
                    
                    const inspInfo = createElement('div', 'inspiration-item-info');
                    const infoText = [];
                    if (insp.type) infoText.push(insp.type);
                    if (insp.ap !== '') infoText.push('AP:' + insp.ap);
                    inspInfo.textContent = infoText.join(' | ') || '点击编辑';
                    
                    inspItem.appendChild(inspName);
                    inspItem.appendChild(inspInfo);
                    
                    inspItem.addEventListener('click', () => {
                        state.editMode = 'god_inspiration';
                        state.currentGodInspirationIndex = idx;
                        state.currentInspirationIndex = null;
                        renderWithoutScroll();
                    });
                    
                    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);
            }
        }
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
    
    window.CardManager = {
        init: init,
        render: render,
        state: state,
        createCard: createEmptyCard,
        generateCode: generateLuaCode
    };
    
})();