/*

*/

const hYakusho = (function() {
    "use strict";

    // --- 第一层:State (唯一真理来源) ---
    const state = {
        activeMirror: null,
        allStats: [], // 汇总所有竞速结果的数组
        anchors: {},
        isMobile: window.innerWidth <= 768,
        abortController: null,
        libs: {},
        rawMD: "",
        rules: { volReg: "", pageReg: "", templates: [] },
        tree: [],
        winners: {},
    };

    // --- 第二层:Tools ---
    const Parser = {
    parseTags(str) {
        const match = str.match(/#\?<([^&\s]+)(.*)/);
        if (!match) return {};
        const tags = {};
        const parts = match[1].split('=');
        tags[parts[0]] = parts[1] || true;
        const rest = match[2].trim();
        if (rest) {
            const params = new URLSearchParams(rest.replace(/^&/, ''));
            params.forEach((v, k) => tags[k] = v);
        }
        return tags;
    },

    build(md) {
        const lines = md.split('\n');
        let mode = null; 
        const root = { title: "Root", children: [], indent: -1 };
        const stack = [root];
        
        state.libs = {}; 
        let currentLib = null, lastSet = null;

        for (let line of lines) {
            const trim = line.trim();
            const indent = line.search(/\S/);
            if (!trim) continue;

            // 1. 更加强壮的模式识别 (忽略前后空格)
            if (trim.startsWith('#')) {
                const headerText = trim.replace(/^#+\s*/, '');
                if (headerText.includes('galleryData')) { mode = 'gallery'; continue; }
                if (headerText.includes('yggdrasiLabs')) { mode = 'labs'; continue; }
                // 遇到其他一级标题则退出当前模式
                if (trim.startsWith('# ')) { mode = null; continue; }
            }

            if (!mode) continue;

            // 2. 线性解析 Labs (JS库)
            if (mode === 'labs' && trim.startsWith('- ')) {
                const content = trim.replace(/^- /, '');
                // 根据缩进深度判断层级:0级是库ID,2级或更多是Set,更深是URL
                if (indent === 0) {
                    const id = content.split(' #?<')[0].trim();
                    state.libs[id] = {}; 
                    currentLib = state.libs[id];
                } else if (content.startsWith('Set') && currentLib) {
                    currentLib[content] = []; 
                    lastSet = currentLib[content];
                } else if (content.startsWith('http') && lastSet) {
                    lastSet.push(content);
                }
            }

            // 3. 线性解析 Gallery (画廊)
            if (mode === 'gallery' && trim.startsWith('- ')) {
                const content = trim.replace(/^- /, '');
                const tags = this.parseTags(content);
                // 这里的 indent 是核心,决定了父子关系
                while (stack.length > 1 && stack[stack.length - 1].indent >= indent) stack.pop();
                const node = { title: content.split(' #?<')[0].trim(), indent, tags, children: [] };
                if (tags.anchor) state.anchors[tags.anchor] = node;
                stack[stack.length - 1].children.push(node);
                stack.push(node);
            }
        }
        state.tree = root.children;
        console.log(`[hLog] Parser.build 完成. 捕获库: ${Object.keys(state.libs).length} 个, 根节点: ${state.tree.length} 个`);
    },

    // 修复栈溢出的核心:activate
    // 基于 0.238 稳定版,注入 simpleAlias 支持
    activate(nodes, parentTags = {}, depth = 0) {
        if (depth > 10) return nodes;

        return nodes.map(node => {
            // 1. 继承父级标签(如 genSeqPics)
            let tags = { ...parentTags, ...node.tags };

            // 2. 捕获规则与镜像 (0.238 稳定逻辑)
            if (tags.regExp) {
                if (node.title === 'volume') state.rules.volReg = tags.regExp;
                if (node.title === 'page') state.rules.pageReg = tags.regExp;
            }
            if (node.title === 'mirrors' || tags.isMirrorNode === 'true') {
                state.rules.templates = node.children
                    .filter(c => c.title.startsWith('http'))
                    .map(c => c.title);
            }

            // --- 3. [核心增强] simpleAnchor & simpleAlias 处理 ---
            
            // A. 记录锚点:如果当前节点带有 simpleAnchor 标签,将其引用存入全局索引
            // 即使它没有子节点,我们也要记录它,以便后续查找
            if (node.tags.simpleAnchor) {
                state.anchors[node.tags.simpleAnchor] = node;
            }

            // B. 执行别名引用:追加数据数组
            if (node.tags.simpleAlias && state.anchors[node.tags.simpleAlias]) {
                const src = state.anchors[node.tags.simpleAlias];
                
                // 深度克隆源节点的子项,避免引用关联
                const inherited = JSON.parse(JSON.stringify(src.children));
                
                // 标记继承来的图片节点为需要溶解的数据(isImageData),防止它们出现在菜单里
                inherited.forEach(c => {
                    c.tags = { ...c.tags, isImageData: true }; 
                    c.children = [];
                });

                // 【关键点】追加到原有数组之后:[原有内容, ...克隆内容]
                node.children = [...node.children, ...inherited];
                
                // 消耗掉此标签,防止在后续可能的递归中重复触发
                delete node.tags.simpleAlias;
            }

            // --- 4. 溶解判定 (Bypass) ---
            let shouldBypass = node.title === 'default' || node.title === 'mirrors' || tags.isMenuNode === 'false';
            
            // 终端簇下的内容或通过 Alias 注入的内容,都不应该作为菜单项显示
            if (parentTags.endCluster === 'true' || parentTags.endCluster === true || node.tags.isImageData) {
                shouldBypass = true;
            }
            node.isBypass = shouldBypass;

            // --- 5. 递归处理 ---
            if (node.children && node.children.length > 0) {
                // 只有当自己不是 endCluster 时才递归处理子项的标签
                // 这保护了第二本漫画中作为子项的图片 URL 不会被误解析
                if (node.tags.endCluster !== 'true' && node.tags.endCluster !== true) {
                    node.children = this.activate(node.children, tags, depth + 1);
                }
            }

            return node;
        });
    },

    flatten(nodes) {
        let result = [];
        nodes.forEach(node => {
            const children = node.children ? this.flatten(node.children) : [];
            if (node.isBypass) {
                // 溶解自己,将其子项上浮
                result = result.concat(children);
            } else {
                node.children = children;
                result.push(node);
            }
        });
        return result;
    }
};

    /**
    * 修正后的 RaceEngine:返回所有参与者的成绩
    */
   const RaceEngine = {
    async run(urls, type = 'Default') {
        const validUrls = urls.filter(u => typeof u === 'string' && u.startsWith('http'));
        
        const tasks = validUrls.map(async (url) => {
            const start = performance.now();
            try {
                // 增加 AbortController 信号,应对页面关闭时的 fetch 报错
                await fetch(url, { method: 'HEAD', mode: 'no-cors' });
                return { 
                    type, 
                    name: new URL(url).hostname, // 统一获取 DNS
                    ms: parseFloat((performance.now() - start).toFixed(1)),
                    url: url,
                    status: ''
                };
            } catch (e) {
                // 如果是因为页面关闭导致的错误,不输出 log
                if (e.name !== 'AbortError') {
                    return { type, name: new URL(url).hostname, ms: 9999, url, status: '' };
                }
            }
        });

        const results = (await Promise.all(tasks)).filter(r => r);
        
        // 推入全量统计 (排除重复项)
        results.forEach(res => state.allStats.push(res));

        const winner = results.filter(r => r.status === '').sort((a, b) => a.ms - b.ms)[0];
        if (winner) winner.isWinner = true;
        return winner;
    }
};

    /**
    * Module B-4: URLFactory (带容错机制)
    */

const URLFactory = {
    generate(template, vol, page) {
        // 从 state 中实时获取最新的正则规则
        const { volReg, pageReg } = state.rules;
        let url = template;

        if (!url) return "";

        try {
            // 替换卷号
            if (volReg) {
                const vRegex = new RegExp(volReg);
                url = url.replace(vRegex, (match, p1) => match.replace(p1, vol));
            }
            // 替换页码
            if (pageReg) {
                const pRegex = new RegExp(pageReg);
                url = url.replace(pRegex, (match, p1) => match.replace(p1, page));
            }
        } catch (e) {
            console.warn("[hLog] URLFactory logic skip:", e.message);
        }
        return url;
    }
};

    /**
    * 修复 URLFactory 的调用链
    */
   const MirrorRacer = {
    // 1. 核心测速逻辑:直接测 MD 里的 mirrors 地址
    async report() {
        if (!state.rules.templates || state.rules.templates.length === 0) {
            console.warn("[hLog] 未发现镜像地址,请检查 Parser 是否成功捕获 mirrors 节点。");
            return;
        }

        console.log("[hLog] 开始图库镜像全量测速...");
        
        // 直接使用模板地址进行测速,不再生成具体的图片 URL
        // 这样既能测试连通性,又能获取最纯粹的延迟
        const winner = await RaceEngine.run(state.rules.templates, 'Gallery');
        
        if (winner) {
            state.activeMirror = winner.url;
            console.log(`[hLog] 最优镜像已锁定: ${winner.name}`);
        }

        // 测速完成后,如果监控面板已打开,则刷新它
        const activePanels = window.jsPanel?.activePanels;
        if (activePanels && typeof activePanels.get === 'function' && activePanels.get('stats-panel')) {
            this.showStatsPanel(); 
        }
    },

    // 2. 增强型显示面板
    showStatsPanel() {
        if (!window.jsPanel) return;

        // 定义三色外观
        const typeStyles = {
            'MdFile':  { bg: 'rgba(33, 150, 243, 0.15)', color: '#90caf9' },
            'Library': { bg: 'rgba(156, 39, 176, 0.15)', color: '#ce93d8' },
            'Gallery': { bg: 'rgba(255, 152, 0, 0.15)',  color: '#ffcc80' }
        };

        // 排序逻辑:先按类型(MdFile > Library > Gallery),再按延迟
        const typeOrder = { 'MdFile': 1, 'Library': 2, 'Gallery': 3 };
        const sortedStats = [...state.allStats].sort((a, b) => {
            if (a.type !== b.type) return typeOrder[a.type] - typeOrder[b.type];
            return a.ms - b.ms;
        });

        const rows = sortedStats.map(s => {
            const style = typeStyles[s.type] || { bg: 'transparent', color: '#eee' };
            const isWinner = s.isWinner ? 'border-left: 4px solid #4caf50; background: rgba(76, 175, 80, 0.1);' : '';
            const winnerIcon = s.isWinner ? '🏆 ' : '';

            return `
                <tr style="${isWinner} border-bottom: 1px solid #222;">
                    <td style="padding:8px; background:${style.bg}; color:${style.color}; font-size:11px;">${s.type}</td>
                    <td style="padding:8px; color:#eee;">${winnerIcon}${s.name}</td>
                    <td style="padding:8px; text-align:right; font-family:monospace; color:${s.ms < 500 ? '#4caf50' : '#888'}">
                        ${s.ms === 9999 ? '<span style="color:#ff5252">FAIL</span>' : s.ms + 'ms'}
                    </td>
                </tr>
            `;
        }).join('');

        // 创建或更新面板
        const panel = window.jsPanel?.activePanels?.get?.('stats-panel');
        if (panel) {
            panel.content.innerHTML = this.getPanelHTML(rows);
        } else {
            jsPanel.create({
                id: 'stats-panel',
                headerTitle: 'hYakusho 系统概览 (Alt + \\)',
                contentSize: '500 400',
                theme: 'dark',
                content: this.getPanelHTML(rows)
            });
        }
    },

    getPanelHTML(rows) {
        return `
            <div style="background:#111; height:100%; overflow:auto;">
                <table style="width:100%; border-collapse:collapse; font-size:13px;">
                    <thead style="background:#000; position:sticky; top:0; z-index:1;">
                        <tr>
                            <th style="padding:10px;text-align:left;color:#888;">类型</th>
                            <th style="padding:10px;text-align:left;color:#888;">主机节点</th>
                            <th style="padding:10px;text-align:right;color:#888;">响应</th>
                        </tr>
                    </thead>
                    <tbody>${rows}</tbody>
                </table>
            </div>
        `;
    }
};

    // --- 第三层:Executors ---
    /**
    * 修正后的 JSLoader 片段
    */

    const JSLoader = {
    globalMapping: {
        'fancyapps-ui': 'Fancybox',
        'imagesloaded': 'imagesLoaded',
        'jspanel': 'jsPanel'
    },

    // 1. CSS 加载器
    loadStyle(url) {
        return new Promise((resolve, reject) => {
            if (document.querySelector(`link[href="${url}"]`)) return resolve();
            const link = document.createElement('link');
            link.rel = 'stylesheet'; link.href = url;
            link.onload = () => { console.log(`[hLog] 🎨 CSS Loaded: ${url.split('/').pop()}`); resolve(); };
            link.onerror = reject;
            document.head.appendChild(link);
        });
    },

    // 2. JS 加载器 (含全局变量守卫)
    async loadScript(libId, url) {
        try {
            const mod = await import(url);
            const globalName = this.globalMapping[libId] || libId;
            window[globalName] = mod.default || mod[globalName] || mod;
            
            // 守卫轮询
            const start = Date.now();
            while (!window[globalName] && Date.now() - start < 3000) {
                await new Promise(r => requestAnimationFrame(r));
            }
            if (window[globalName]) {
                console.log(`[hLog] 📜 JS Executed: ${globalName}`);
            } else {
                console.warn(`[hLog] ⚠️ ${globalName} imported but window object missing.`);
            }
        } catch (e) {
            console.error(`[hLog] ❌ Script Load Fail: ${url}`, e);
            throw e; // 抛出异常以便外层捕获
        }
    },

    // 3. 智能分发器 (正则判断)
    async loadResource(libId, url) {
        // 匹配 .css 或 .css?v=...
        if (/\.css(\?.*)?$/i.test(url)) {
            await this.loadStyle(url);
        } else {
            await this.loadScript(libId, url);
        }
    },

    // 4. 核心注入逻辑 (Set 模式)
    async injectAll() {
        const tasks = Object.entries(state.libs).map(async ([libId, sets]) => {
            // A. 准备竞速候选者 (每个 Set 选一个探针)
            const candidates = Object.entries(sets).map(([setId, urls]) => {
                // 优先选 JS 作为探针,如果没有 JS 则选第一个
                const probeUrl = urls.find(u => !/\.css/i.test(u)) || urls[0];
                return { setId, probeUrl, allFiles: urls };
            });

            // B. 探针竞速
            const winnerResult = await RaceEngine.run(candidates.map(c => c.probeUrl), 'Library');

            // C. 胜者全量加载
            if (winnerResult) {
                // 找到赢家对应的完整 Set
                const winningSet = candidates.find(c => c.probeUrl === winnerResult.url);
                
                if (winningSet) {
                    // 记录胜利信息
                    state.winners[libId] = { dns: winnerResult.name, ms: winnerResult.ms, set: winningSet.setId };
                    
                    // 并行加载该 Set 内的所有文件 (CSS 和 JS 同时下载)
                    await Promise.all(winningSet.allFiles.map(url => this.loadResource(libId, url)));
                }
            }
        });

        await Promise.all(tasks);
    }
};

    // --- 事件监听补全 ---
const EventManager = {
    init() {
        const sb = document.querySelector('.md-sidebar');
        if (!sb) return;
        sb.onclick = (e) => {
            const target = e.target.closest('[data-action]');
            if (!target) return;

            const { action, index } = target.dataset;
            const idx = parseInt(index);

            switch (action) {
                case 'nav':  window.UI.navigateDown(idx); break;
                case 'back': window.UI.navigateUp(); break;
                case 'zoom': 
                    const node = window.UI.getCurrentNode();
                    window.UI.launchFancybox(node, idx); 
                    break;
            }
        };
    }
};
    // --- 1. 扩展全局状态 ---
    // --- AbortController 基础架构 ---
    // --- 整合后的图片加载控制器 ---
    const ImageLoader = {
        // 启动新任务并终止前序任务
        prepare() {
            // 1. 发射信号中断
            if (state.abortController) {
                console.log("[hLog] 发现并发,正在中止旧任务...");
                state.abortController.abort();
            }
            
            // 2. 物理层面的彻底清理
            // 必须在 signal 重置前清空 DOM 里的 src,否则浏览器会继续偷偷下载
            const sidebar = document.querySelector('.md-sidebar');
            if (sidebar) {
                const activeImgs = sidebar.querySelectorAll('.lazy-img');
                activeImgs.forEach(img => {
                    img.src = ''; // 强制断开连接
                    img.remove(); // 移除 DOM 节点
                });
            }

            // 3. 生成新的控制器
            state.abortController = new AbortController();
            return state.abortController.signal;
        }
    };

    const UI = {
        panel: null,
    
    injectStyles() {
        const css = `
            #h-orb { position: fixed; right: 20px; bottom: 20px; width: 48px; height: 48px; border-radius: 50%; background: rgba(80, 80, 80, 0.6); color: white; z-index: 1049; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 20px; backdrop-filter: blur(4px); }
            .md-sidebar { position: fixed; top: 0; right: 0; bottom: 0; width: 320px; background: #121212; z-index: 19; transform: translateX(100%); transition: transform 0.3s ease; overflow-y: auto; color: #eee; border-left: 1px solid #333; font-family: sans-serif; }
            .md-sidebar.active { transform: translateX(0); }
            .md-backdrop { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 18; opacity: 0; pointer-events: none; transition: opacity 0.3s; }
            .md-backdrop.active { opacity: 1; pointer-events: auto; }
            .u-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 15px; }
            .u-card { background: #1e1e1e; border: 1px solid #333; text-align: center; cursor: pointer; overflow: hidden; }
            .u-thumb { width: 100%; aspect-ratio: 3/4; background-size: cover; background-position: center; background-color: #222; }
            .u-label { font-size: 11px; padding: 6px; color: #bbb; }
            .u-btn { padding: 14px 18px; border-bottom: 1px solid #333; cursor: pointer; font-size: 14px; display: flex; justify-content: space-between; align-items: center; }
            .u-header { padding: 15px; background: #1a1a1a; font-weight: bold; border-bottom: 1px solid #333; color: #ea580c; }
        `;
        const style = document.createElement('style');
        style.innerHTML = css;
        document.head.appendChild(style);
    },
    currentPath: [], // [mIdx, vIdx]

    // --- 核心工具:获取当前节点 ---
    getCurrentNode() {
        let node = { title: "Root", children: state.tree, tags: {} };
        for (const index of this.currentPath) {
            if (!node.children || !node.children[index]) break;
            node = node.children[index];
        }
        return node;
    },

    // --- 核心工具:获取 Meta ---
    getMeta(node) {
        const t = node.tags || {};
        return {
            last: parseInt(t.lastPic) || 0,
            cover: String(t.coverPic || "1"),
            isMenu: t.isMenuNode !== 'false'
        };
    },

    // --- 渲染引擎 ---
    render() {
        const sidebar = document.querySelector('.md-sidebar');
        if (!sidebar) return;

        const node = this.getCurrentNode();
        const isRoot = this.currentPath.length === 0;
        const meta = this.getMeta(node);
        
        // 1. 头部与返回按钮
        let html = `<div class="u-header">${node.title}</div>`;
        if (!isRoot) {
            html += `<div class="u-btn" style="color:#2196F3" data-action="back">❮ 返回上级</div>`;
        } else {
            html += `<div class="u-btn" style="color:#ff9800" onclick="window.MirrorRacer.showStatsPanel()">⚙ 系统概览</div>`;
        }

        // --- 整合修正后的 isVolume ---
        // 1. 如果有 endCluster 标签,直接视为卷 (第二本漫画)
        // 2. 如果没有子菜单项(children为0) 且 有页数统计(meta.last>0),视为卷 (第一本漫画)
        // 注意:移除了对 node.tags.genSeqPics 的直接判断,防止误伤第一本漫画的目录层
        const isVolume = (node.tags.endCluster === 'true' || node.tags.endCluster === true) || 
                        (node.children.length === 0 && meta.last > 0);

        if (isVolume) {
            html += this._renderImageGrid(node, meta);
        } else {
            html += this._renderMenuGrid(node.children || []);
        }

        sidebar.innerHTML = html;
        sidebar.scrollTop = 0;
    },

    // --- 内部私有渲染:菜单 ---
    _renderMenuGrid(children) {
        let html = `<div class="u-grid">`;
        children.forEach((child, index) => {
            const childMeta = this.getMeta(child);
            if (!childMeta.isMenu) return;

            // 封面解析
            const coverUrl = this.resolveCover(child);

            html += `
                <div class="u-card" data-action="nav" data-index="${index}">
                    <div class="u-thumb" style="background-image: url('${coverUrl}')"></div>
                    <div class="u-label">${child.title}</div>
                </div>`;
        });
        html += `</div>`;
        return html;
    },

    // --- 内部私有渲染:图片流 ---
    _renderImageGrid(node, meta) {
        // 启动新控制器并获取信号
        // 调用物理中断
        // A. 开启新任务,获取本次渲染的唯一信号
        const signal = ImageLoader.prepare(); // 获取唯一信号
        
        // B. 清空旧视图 (物理层面的中断:移除旧 img 节点,停止它们的下载)
        const sidebar = document.querySelector('.md-sidebar');
        const oldImages = sidebar.querySelectorAll('.lazy-img');
        oldImages.forEach(img => {
            img.src = ''; 
            img.remove();
        });

        const urls = this.generateUrlArray(node);
        let html = `<div class="u-grid">`;
        
        urls.forEach((url, i) => {
            html += `
                <div class="u-card" data-action="zoom" data-index="${i}">
                    <img src="${url}" 
                        class="lazy-img"
                        style="width:100%; min-height:100px; display:block; background:#222;" 
                        loading="lazy">
                    <div class="u-label">P.${i+1}</div>
                </div>`;
        });
        html += `</div>`;

        // C. 检查信号:如果在生成 HTML 期间任务已被中止,则不进行 DOM 写入
        if (signal.aborted) return ''; 
        return html;
    },

    // --- 功能函数:生成图片数组 ---
    // 直接使用你刚才提供的 0.2381 版本
    generateUrlArray(node) {
        if (!node) return [];
        
        // console.log(`[Debug] 节点: ${node.title}`, "标签:", node.tags); // 调试可注释掉

        let urls = [];
        const nodeTags = node.tags || {};
        const meta = this.getMeta(node);

        // 逻辑A:endCluster (第二本漫画)
        if (nodeTags.endCluster === 'true' || nodeTags.endCluster === true) {
            if (node.children && node.children.length > 0) {
                urls = node.children.map(c => (typeof c === 'string' ? c : c.title).trim());
            }
        } 
        // 逻辑B:序列模式 (第一本漫画)
        else if (nodeTags.genSeqPics || state.rules.volReg) {
            const volNum = node.title.match(/\d+/)?.[0] || "01";
            if (meta.last > 0) {
                for (let i = 1; i <= meta.last; i++) {
                    urls.push(URLFactory.generate(state.activeMirror, volNum, i.toString(), state.rules.volReg, state.rules.pageReg));
                }
            }
        }

        return urls;
    },

    // --- 功能函数:解析封面 ---
    resolveCover(node) {
        if (!node) return "";
        
        // [关键修正] 如果是 Root 节点 (indent 为 -1),直接返回空,切断向下的递归
        if (node.indent === -1 || node.title.toLowerCase() === 'root') {
            return "";
        }

        const tags = node.tags || {};
        const meta = this.getMeta(node);

        // 1. 如果是书级(有子节点但不是直接存图的卷),向下追溯第一个有效子节点
        // 注意:这里仅对非 endCluster/genSeqPics 的中间目录递归
        if (node.children && node.children.length > 0 && !tags.endCluster && !tags.genSeqPics) {
            const firstChild = node.children[0];
            // 只有子节点是菜单时才递归
            if (this.getMeta(firstChild).isMenu) {
                return this.resolveCover(firstChild);
            }
        }

        // 2. 确定封面索引 (coverPic 标签,默认第1张)
        const coverIdx = parseInt(tags.coverPic || 1) - 1;

        // --- 情况 A: 显式地址模式 (第二本漫画 / endCluster) ---
        if (tags.endCluster === 'true' || tags.endCluster === true) {
            if (node.children && node.children[coverIdx]) {
                const imgNode = node.children[coverIdx];
                return (typeof imgNode === 'string' ? imgNode : imgNode.title).trim();
            }
        }

        // --- 情况 B: 序列生成模式 (第一本漫画 / genSeqPics) ---
        if (tags.genSeqPics || state.rules.volReg) {
            const volNum = node.title.match(/\d+/)?.[0] || "01";
            const pageNum = tags.coverPic || (meta.cover ? meta.cover : "1");
            return URLFactory.generate(state.activeMirror, volNum, pageNum, state.rules.volReg, state.rules.pageReg);
        }

        return "";
    },

    // --- 功能函数:启动 Fancybox (补全缺失函数) ---
    launchFancybox(node, startIndex) {
        const urls = this.generateUrlArray(node);
        if (window.Fancybox) {
            window.Fancybox.show(urls.map(src => ({ src, type: "image" })), {
                startIndex: startIndex,
                infinite: false,
                // 解决你提到的 SPA 刷新 Bug 的潜在补丁
                on: {
                    ready: () => {
                        console.log( "Fancybox ready" );
                    }
                }
            });
        } else {
            console.error("Fancybox 未加载");
        }
    },

    // --- 导航控制 API ---
    navigateTo(path) {
        this.currentPath = [...path];
        this.render();
    },

    navigateUp() {
        if (this.currentPath.length > 0) {
            this.currentPath.pop();
            this.render();
        }
    },

    navigateDown(index) {
        if (this.navController) this.navController.abort();
        this.navController = new AbortController();

        this.currentPath.push(index);
        this.render();
    },

    toggleDrawer(force) {
        const el = document.querySelector('.md-sidebar');
        const bk = document.querySelector('.md-backdrop');
        if (!el || !bk) return;
        const isOpen = force ?? !el.classList.contains('active');
        el.classList.toggle('active', isOpen);
        bk.classList.toggle('active', isOpen);
    },

    // --- 初始化 ---
    init() {
        window.UI = this;
        window.MirrorRacer = MirrorRacer;
        this.injectStyles();
        const bd = document.createElement('div'); bd.className = 'md-backdrop'; bd.onclick = () => this.toggleDrawer(false);
        const sb = document.createElement('div'); sb.className = 'md-sidebar';
        const orb = document.createElement('div'); orb.id = 'h-orb'; orb.innerHTML = ''; orb.onclick = () => this.toggleDrawer();
        document.body.append(bd, sb, orb);
        // 注意:这里需要确保 EventManager.init() 在此处之后执行
        EventManager.init(); // 绑定事件0.35

        // 2. 获取根节点元数据 (假设 state.tree[0] 是 Root)
            const rootNode = state.tree[0] || { tags: {} };

            // 3. 执行分流跳转
            // 情况 A: 显式指定跳转到第一本书 (L2)
            if (rootNode.tags.redirectToBook == 1) {
                console.log("[hLog] 触发指令:直接进入第一本漫画");
                this.navigateTo([0, 0]); 
            } 
            // 情况 B: 默认行为,进入书库 (L1)
            else {
                console.log("[hLog] 默认行为:进入书库列表");
                this.navigateTo([0]); 
            }
    }
};

    const Core = {
    async boot() {
        const mdMirrors = [
            `https://gcore.jsdelivr.net/gh/qqvvv/qqvvv.github.io/content/9/myriaDown/allInOne.markdown`,
            `https://testingcf.jsdelivr.net/gh/qqvvv/qqvvv.github.io/content/9/myriaDown/allInOne.markdown`,
            `https://cdn.jsdmirror.com/gh/qqvvv/qqvvv.github.io/content/9/myriaDown/allInOne.markdown`,
        ];

        try {
            // 第一场竞速:配置文件
            const winner = await RaceEngine.run(mdMirrors);
            state.allStats.push({ type: 'Config', name: winner.domain, ms: winner.ms });

            const res = await fetch(`${winner.url}?t=${Date.now()}`);
            const rawMD = await res.text();

            // 解析 MD
            Parser.build(rawMD);
            // 此时 Parser 会填充 state.libs
            
            // 激活与降维 (这里会捕获正则 rules)
            const activeTree = Parser.activate(state.tree);
            state.tree = Parser.flatten(activeTree);

            // 第二场竞速:JS 库注入
            if (Object.keys(state.libs).length > 0) {
                // 转换 winners 到 allStats
                Object.entries(state.winners).forEach(([id, info]) => {
                    state.allStats.push({ type: 'Library', name: id, ms: info.ms, dns: info.dns });
                });
            } else {
                console.warn("[hLog] Parser 未能提取到任何 JS 库地址。");
            }

            // 第三场竞速:图片镜像 (按需手动或在此处自动触发)
            // MirrorRacer.report(); 

            // 最终汇报
            console.group("--- hYakusho 系统概览 ---");
                console.table(state.allStats);
                console.log("画廊结构:", state.tree);
            console.groupEnd();

            // 如果 jsPanel 成功注入,自动弹窗
            // --- Core.boot 内部推荐的尾部逻辑 ---
            await JSLoader.injectAll(); // 等待所有 Set 加载完成

            // 此时 Library 的 stats 已经全了,开始跑 Gallery 测速
            // 注意:report 内部现在会自动调用 RaceEngine 并填充 Gallery stats
            await MirrorRacer.report(); 

            // 最终展现:确保 jsPanel 真的存在
            if (window.jsPanel) {
                console.log("[hLog] 所有竞速完成,弹出全量仪表盘。");
            } else {
                // 如果 jsPanel 没出来,至少在控制台给你看结果
                console.table(state.allStats);
            }

            UI.init();
            UI.toggleDrawer ();

        } catch (err) {
            if (err.message.includes('shutting down')) return; // 忽略静默错误
            console.error("[hLog] 启动过程中断:", err);
        }
    }
};

    return { start: Core.boot, debug: () => state };
})();

hYakusho.start();

/*

*/