// let $OutcomeMaterial = Java.loadClass("se.mickelus.tetra.module.schematic.OutcomeMaterial");
// let $ItemUtil = Java.loadClass("net.yiran.jeitetra.util.ItemUtil");
// let $DataManager = Java.loadClass("se.mickelus.tetra.data.DataManager").instance;

// let materialDatas = $DataManager.materialData.getData().entrySet();

// let Materials_Ftbid = [] // ftbid，shift+z用

// let questsArray = JsonIO.read('kubejs/client_scripts/yi/json/ftb.json') || [] // ftb文件

// let questsLinkArray = JsonIO.read('kubejs/client_scripts/yi/json/ftb_link.json') || [] // ftb链接任务文件

// let Materials$1 = JsonIO.read('kubejs/client_scripts/yi/json/material/material_category.json') || [] // 材料类别，注册tooltip用

// let Materials$2 = JsonIO.read('kubejs/client_scripts/yi/json/material/material_effect.json') || [] // 效果复用次数，注册sorter用

// let Materials = []; // 最终结果数组

// // let tooltip = JsonIO.read('kubejs/client_scripts/yi/json/material_items_schematic.json') || [] // 曾经用来注册卷轴tooltip

// let globalEffectCount = {}; // 用于全局统计效果频次
// let effectKeyToDisplayName = {}; // 缓存 key
// materialDatas.stream()
//     .filter(d => {
//         let materialData = d.getValue();
//         let predicate = materialData.material.getPredicate();
//         return predicate != null;
//     })
//     .forEach(d => {
//         let materialData = d.getValue();
//         let materialDataMaterial = materialData.material;
//         let materialDatacategory = materialData.category;
//         let materialDatacategory$1 = $I18n.get("tetra.variant_category." + materialDatacategory + ".label")

//         let resourceLocation = d.getKey();
//         // 提取中文名称和物品ID
//         if (!materialDataMaterial.displayNames || !materialDataMaterial.displayNames[0]) {
//             return; // 跳过无效材料
//         }

//         let materialNames = materialDataMaterial.displayNames[0].getString();
//         let itemId = materialDataMaterial.applicableItemStacks.length > 0
//             ? materialDataMaterial.applicableItemStacks[0].getId()
//             : "unknown";


//         // ====== 收集材料效果 并 累计计数 ======
//         let effects = [];
//         let materialDataEffect = materialData.effects;
//         let ItemEffect = materialDataEffect.values;

//         // ✅ 新增：累加到全局计数器
//         ItemEffect.forEach(effect => {
//             let effectKey = effect.key; // 原始 key，如 "reach"
//             let displayName = $I18n.get("tetra.stats." + effectKey);

//             // 翻译失败 fallback
//             if (!displayName || displayName.startsWith("tetra.stats.")) {
//                 displayName = effectKey; // 或设为 "[未翻译]"
//             }

//             // 累计次数（以 key 为准）
//             globalEffectCount[effectKey] = (globalEffectCount[effectKey] || 0) + 1;

//             // 缓存中文名（多次出现时会覆盖，但内容相同）
//             effectKeyToDisplayName[effectKey] = displayName;
//         });


//         ItemEffect.forEach(effect => {
//             let effectName = effect.key;
//             let level = materialDataEffect.getLevel(effect);
//             let efficiency = materialDataEffect.getEfficiency(effect);
//             let efficiencyRounded = parseFloat(efficiency.toFixed(2));

//             // 特殊处理：kubejs_reaching
//             let displayLevel = effectName === "kubejs_reaching"
//                 ? (level * 4.3944449154).toFixed(2)
//                 : level;

//             // 获取翻译文本

//             let effectName$1 = $I18n.get("tetra.stats." + effectName)
//             let tooltipKey = "tetra.stats." + effectName + ".tooltip";
//             let translation = $I18n.get(tooltipKey, displayLevel, efficiencyRounded);
//             let cleanTranslation = translation
//                 .replace("§", "&")
//                 .replace("Format error:", "")

//             // 推入效果列表
//             effects.push({
//                 "效果名": effectName$1,
//                 "效果等级": level,
//                 "效果效率": efficiencyRounded,
//                 "描述": cleanTranslation
//             });
//         });

//         let attributes = [];
//         let materialDataAttributes = materialData.attributes;

//         if (materialDataAttributes) {
//             let entries = materialDataAttributes.entries();
//             entries.forEach(entry => {
//                 let attributeName = $I18n.get(entry.key.getDescriptionId());
//                 let modifier = entry.value;
//                 let amountStr;
//                 let num = modifier.amount;

//                 if (modifier.operation == "ADDITION") {
//                     if (num > 0) {
//                         amountStr = `&a+${num.toFixed(2)}&r`;
//                     } else if (num < 0) {
//                         amountStr = `&c-${Math.abs(num).toFixed(2)}&r`;
//                     } else {
//                         amountStr = `&7${num.toFixed(2)}&r`; // 0 不加符号
//                     }
//                 } else {
//                     // 百分比类型
//                     let pct = num * 100;
//                     if (pct > 0) {
//                         amountStr = `&a+${pct.toFixed(2)}&r%`;
//                     } else if (pct < 0) {
//                         amountStr = `&c-${Math.abs(pct).toFixed(2)}&r%`;
//                     } else {
//                         amountStr = `&7${pct.toFixed(2)}&r%`;
//                     }
//                 }
//                 attributes.push({
//                     "属性名": attributeName,
//                     "属性数值": amountStr
//                 });
//             });
//         }

//         // ====== 构建最终材料对象 ======
//         Materials.push({
//             "材料名": materialNames,
//             "材料ID": itemId,
//             "材料类别": materialDatacategory$1,
//             "材料效果": effects,
//             "材料属性": attributes
//         });
//         Materials$1.push({
//             "materialID": itemId,
//             "category": materialDatacategory
//         });
//         // Materials$2：只包含使用次数 ≥2 的效果
//         Materials$2 = Object.entries(globalEffectCount)
//             .filter(([key, count]) => count > 5)
//             .map(([key, count]) => ({
//                 "names": effectKeyToDisplayName[key] || key,
//                 "key": key,
//                 "counts": count
//             }))
//             .sort((a, b) => b.counts - a.counts); // 按次数降序
//     });

// // console.log(Materials$2)

// // Materials$1.forEach(mat => {
// //     console.log("**********************");
// //     console.log(`材料ID: ${mat["materialID"]}`);
// // });


// // let MaterialsAsStrings = Materials.map(mat => {
// //     let effectStrings = [];

// //     // 添加所有效果：格式 "效果名: 描述"
// //     mat["材料效果"].forEach(effect => {
// //         effectStrings.push(`${effect["效果名"]}: ${effect["描述"]}`);
// //     });

// //     // 添加所有属性：格式 "属性名: 数值"
// //     mat["材料属性"].forEach(attr => {
// //         effectStrings.push(`${attr["属性名"]}: ${attr["属性数值"]}`);
// //     });

// //     return {
// //         "材料名": mat["材料名"],
// //         "材料ID": mat["材料ID"],
// //         "材料类别": mat["材料类别"],
// //         "材料效果": effectStrings
// //     };
// // });
// let MaterialsAsStrings = Materials.map(mat => {
//     let effectStrings = [];

//     // 处理“材料效果”
//     mat["材料效果"].forEach(effect => {
//         let effectName = effect["效果名"];
//         let rawDesc = effect["描述"].replace("&r", "&7"); // 例如："根据你所击杀的事物..."

//         // 构造新格式
//         let formatted = [
//             `&f  ️▶&r  ${effectName}: `,
//             ` &f· &7  ${rawDesc}`
//         ].join('\n'); // 注意：FTB Quests 支持 \n 换行

//         effectStrings.push(formatted);
//     });

//     // 处理“材料属性”（如果你也希望统一格式）
//     mat["材料属性"].forEach(attr => {
//         let attrName = attr["属性名"];
//         let attrValue = attr["属性数值"];

//         // 示例：如果原始是 "攻击伤害: 20.00%"，现在也格式化
//         let formattedAttr = [
//             `&f  ️▶&r  ${attrName}: `,
//             ` &f· &7  ${attrValue}`
//         ].join('\n');

//         effectStrings.push(formattedAttr);
//     });

//     return {
//         "材料名": mat["材料名"],
//         "材料ID": mat["材料ID"],
//         "材料类别": mat["材料类别"],
//         "材料效果": effectStrings // 已格式化
//     };
// });








// // ========== 构建全局材料效果按类别分组映射 ==========
// let materialEffectsByCategory = {};

// for (var i = 0; i < MaterialsAsStrings.length; i++) {
//     var entry = MaterialsAsStrings[i];
//     var matID = entry["材料ID"];
//     var rawCat = entry["材料类别"];
//     var cleanCat = String(rawCat)
//         .replace(/[\u200B-\u200D\uFEFF\u2028\u2029\u202C\u202A\u202B]/g, '')
//         .trim();
//     var effects = entry["材料效果"] || [];

//     if (!materialEffectsByCategory[matID]) {
//         materialEffectsByCategory[matID] = {};
//     }
//     // 合并同类别（理论上不会重复，但保险起见可覆盖或 concat）
//     materialEffectsByCategory[matID][cleanCat] = effects;
// }







// function formatMaterialTitle(name, effects, attributes, stageLabel) {
//     if (stageLabel === undefined || stageLabel === null) {
//         stageLabel = null;
//     }
//     // 工具：清理 § 和 &r
//     function clean(str) {
//         return str
//             .replace("§a", '')   // 移除 §x 正则不知道为什么不生效，枚举凑合用
//             .replace("§b", '')   // 移除 §x
//             .replace("§c", '')   // 移除 §x
//             .replace("§d", '')   // 移除 §x
//             .replace("§e", '')   // 移除 §x
//             .replace("§f", '')   // 移除 §x
//             .replace("§1", '')   // 移除 §x
//             .replace("§2", '')   // 移除 §x
//             .replace("§3", '')   // 移除 §x
//             .replace("§4", '')   // 移除 §x
//             .replace("§5", '')   // 移除 §x
//             .replace("§6", '')   // 移除 §x
//             .replace("§7", '')   // 移除 §x
//             .replace("§8", '')   // 移除 §x
//             .replace("§9", '')   // 移除 §x
//             .replace("&a", '')   // 移除 &x 正则不知道为什么不生效，枚举凑合用
//             .replace("&b", '')   // 移除 &x
//             .replace("&c", '')   // 移除 &x
//             .replace("&d", '')   // 移除 &x
//             .replace("&e", '')   // 移除 &x
//             .replace("&f", '')   // 移除 &x
//             .replace("&1", '')   // 移除 &x
//             .replace("&2", '')   // 移除 &x
//             .replace("&3", '')   // 移除 &x
//             .replace("&4", '')   // 移除 &x
//             .replace("&5", '')   // 移除 &x
//             .replace("&6", '')   // 移除 &x
//             .replace("&7", '')   // 移除 &x
//             .replace("&8", '')   // 移除 &x
//             .replace("&9", '')   // 移除 &x
//             .replace("&r", '')               // 移除 &r
//         // .replace(/&([0-9a-fk-o])/g, '&$1'); // 保留合法 &x
//     }

//     // 1. 清理材料名的颜色代码，然后添加 &f 前缀
//     let cleanName = clean(name);
//     let lines = [`&f[${cleanName}]`]; // 2 个元素 = 1 个换行

//     // ➕ 新增：如果提供了阶段标签，就加一行
//     if (stageLabel) {
//         lines.push(`${stageLabel}`);
//     }

//     // 2. 添加效果（每行：&f效果名:  &7描述）
//     for (let eff of effects) {
//         let effName = clean(eff["效果名"]);
//         let effDesc = clean(eff["描述"]);
//         lines.push(`&f${effName}:  &7${effDesc}`);
//     }

//     // 3. 添加属性（&f属性名:  &a/c数值）
//     for (let attr of attributes) {
//         let attrName = clean(attr["属性名"]);
//         let attrValue = attr["属性数值"]; // 已包含 &a/&c，但可能有 &r

//         // 从 attrValue 中提取数值部分（去掉颜色码，保留符号）
//         let cleanValue = attrValue
//             .replace(/&[0-9a-fk-or]/g, '') // 移除所有颜色码
//             .trim();

//         // 保留原始颜色代码
//         let colorCode = attrValue.match(/(&[ac])/); // 只匹配 &a 或 &c
//         let finalValue = colorCode ? `${colorCode[1]}${cleanValue}` : `&7${cleanValue}`;

//         lines.push(`&f${attrName}:  ${finalValue}`);
//     }

//     lines.push("");
//     return lines.join("\\n");
// }


// // ========== 任务ID生成器 ==========
// // === 全局计数器（重复任务用）===
// let ITEM_USAGE_COUNT = {};

// function generateTaskPair(materialId) {
//     if (materialId == null) materialId = "null";
//     materialId = (materialId + "");

//     // === 1. 生成 12 位哈希 ===
//     let CHAR_MAP = {
//         '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
//         'a': 10, 'b': 11, 'c': 12, 'd': 13, 'e': 14, 'f': 15, 'g': 16, 'h': 17, 'i': 18, 'j': 19,
//         'k': 20, 'l': 21, 'm': 22, 'n': 23, 'o': 24, 'p': 25, 'q': 26, 'r': 27, 's': 28, 't': 29,
//         'u': 30, 'v': 31, 'w': 32, 'x': 33, 'y': 34, 'z': 35,
//         'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15, 'G': 16, 'H': 17, 'I': 18, 'J': 19,
//         'K': 20, 'L': 21, 'M': 22, 'N': 23, 'O': 24, 'P': 25, 'Q': 26, 'R': 27, 'S': 28, 'T': 29,
//         'U': 30, 'V': 31, 'W': 32, 'X': 33, 'Y': 34, 'Z': 35,
//         '_': 36, ':': 37, '-': 38, '.': 39
//     };

//     let hash = 5381;
//     let MOD = 1000000000000; // 10^12 → 12位

//     for (let i = 0; i < materialId.length; i++) {
//         let c = materialId.charAt(i);
//         let val = CHAR_MAP[c] !== undefined ? CHAR_MAP[c] : 0;
//         hash = (hash * 33 + val) % MOD;
//     }

//     let hash12 = hash.toString();
//     while (hash12.length < 12) {
//         hash12 = '0' + hash12;
//     }
//     if (hash12.length > 12) {
//         hash12 = hash12.substring(0, 12);
//     }

//     // === 2. 获取实例索引（第15位）===
//     let count = ITEM_USAGE_COUNT[materialId] || 0;
//     if (count >= 10) {
//         console.warn("警告：物品 '" + materialId + "' 超过10个任务，索引将循环！");
//         count = count % 10;
//     }
//     ITEM_USAGE_COUNT[materialId] = count + 1;
//     let instanceIndex = String(count); // '0' to '9'

//     // === 3. 构造完整ID ===
//     let base15 = "10" + hash12 + instanceIndex; // 2+12+1 = 15位
//     let id1 = base15 + "0"; // 第16位 = 0
//     let id2 = base15 + "1"; // 第16位 = 1
//     let id3 = base15 + "2"; // 第16位 = 2
//     let id4 = base15 + "3"; // 第16位 = 3

//     return { id1: id1, id2: id2, id3: id3, id4: id4 };
// }

// // // ===== 测试用例（避免使用变量名 `id`）=====
// // let testCases = [
// //     "minecraft:diamond",
// //     "minecraft:emerald",
// //     "minecraft:end_rod",
// //     "cataclysm:ancient_metal_ingot",
// //     "art_of_forging:vobrivium_ingot",
// //     "jerotesvillage:villager_metal_block",
// //     "kubejs:test_item",
// //     "mod:very_long_item_name_that_exceeds_sixteen_chars",
// //     "simple",
// //     ""
// // ];

// // function safeLog(msg) {
// //     console.log(String(msg));
// // }

// // safeLog("=== 开始测试 generateTaskId ===");
// // let testInput = []
// // // ✅ 关键修复：变量名从 `id` 改为 `testInput`
// // for (let idx = 0; idx < testCases.length; idx++) {
// //     testInput = testCases[idx];
// //     try {
// //         let taskId = generateTaskId(testInput);
// //         safeLog('输入: "' + testInput + '" → 输出: ' + taskId);
// //     } catch (e) {
// //         safeLog('输入: "' + testInput + '" → 报错: ' + String(e));
// //     }
// // }

// // safeLog("=== 测试结束 ===");


// // ========== 1. 定义材料类别的显示顺序 ==========
// let categoryOrder = [
//     "金属",
//     "骨",
//     "镶嵌",
//     "宝石",
//     "机械",
//     "棍",
//     "石",
//     "木",
//     "血肉",
//     "食物",
//     "血肉药剂",
//     "糖裹",
//     "纤维",
//     "布料",
//     "鳞甲",
//     "皮革",
//     "头颅",
//     "宝珠",
//     "纹饰模板",
//     "纹饰原材料",
//     "锻造升级模板",
//     "强化附层材料",
//     "荣耀",
//     "融梦",
//     "灾变核心",
//     "杂项"
// ];

// // ========== 2. 辅助函数：获取材料效果描述 ==========
// function getEffectsById(ID) {
//     for (let i = 0; i < MaterialsAsStrings.length; i++) {
//         if (MaterialsAsStrings[i]["材料ID"] === ID) {
//             return MaterialsAsStrings[i]["材料效果"];
//         }
//     }
//     return [];
// }
// // ========== 3. 初始化 ==========

// let globalTaskIndex = 1

// let FTB_MATERIAL_DATA = [];
// JsonIO.read("kubejs/client_scripts/yi/json/ftb_ex_description.json", (p, f, json) => {
//     FTB_MATERIAL_DATA = json;
// });
// // 📌 作用：从json中获取材料的额外描述，比如获取方法等。

// let currentStageY = -0.5
// // 📌 作用：当前正在布局的“阶段”（stage1/stage2/stage3）在 Y 轴上的起始坐标。
// // 每个 stage 从这个 Y 值开始向下（或向上）排列任务，排完后更新为下一个 stage 的起始位置。
// // 初始值 -0.5 表示第一个任务大致在 (x, -0.5) 附近，通常是为了视觉居中或对齐。

// let startX = 25;// 默认 -2
// // 📌 作用：每一列任务在 X 轴上的起始位置。
// // 所有任务从这个 X 坐标开始向右排列（每列 +stepX），负值表示放在屏幕中心左侧。

// let stepX = 1;
// // 📌 作用：列与列之间的水平间距（单位：FTB Quests 网格单位）。
// // 每当一列排满（达到 maxPerColumn），就换到下一列：x = startX + 列号 * stepX。

// let stepY = 1;
// // 📌 作用：同一列内，相邻任务之间的垂直间距。
// // 例如：第一个任务 y = -0.5，第二个就是 -0.5 + 1.5 = 1.0，第三个是 2.5，依此类推。

// let maxPerColumn = 9;
// // 📌 作用：每列最多容纳的任务数量。
// // 达到这个数量后，自动换到下一列（X 增加，Y 重置为该列顶部）。
// // 控制界面不会拉得太长，保持紧凑。

// let categoryGapX = 1;
// // 🎯 控制不同类别块之间的 X 间隔（单位：网格）
// // 例如：1.0 = 默认一列宽，1.5 = 多空半列，2.0 = 空一整列

// let stageVerticalGap = 10.0;
// // 📌 作用：不同“阶段”（stage1 → stage2 → stage3）之间的垂直间距（绝对单位）。
// // 排完 stage1 后，stage2 的起始 Y = stage1 最后一个任务的 Y + stageVerticalGap。

// // ========== 4. 按 stage 分组材料 ==========
// let stageGroups = { stage1: [], stage2: [], stage3: [] };
// for (let i = 0; i < global.materials.length; i++) {
//     let m = global.materials[i];
//     if (m.tags.includes("dzzl:stage1")) stageGroups.stage1.push(m);
//     if (m.tags.includes("dzzl:stage2")) stageGroups.stage2.push(m);
//     if (m.tags.includes("dzzl:stage3")) stageGroups.stage3.push(m);
// }

// // ========== 5. 主布局循环 ==========
// let stages = ['stage1', 'stage2', 'stage3'];
// for (let s = 0; s < stages.length; s++) {
//     let stageName = stages[s];
//     let rawMaterials = stageGroups[stageName];
//     if (rawMaterials.length === 0) continue;

//     // ✅ 步骤1：提取当前 stage 的所有材料ID（去重）
//     let stageMaterialIDs = {};
//     for (var i = 0; i < rawMaterials.length; i++) {
//         stageMaterialIDs[rawMaterials[i].material] = true; // 用对象模拟 Set
//     }

//     // ✅ 步骤2：遍历 MaterialsAsStrings，只保留属于当前 stage 的条目
//     let materialsWithCategory = [];
//     for (var i = 0; i < MaterialsAsStrings.length; i++) {
//         var matEntry = MaterialsAsStrings[i];
//         var matID = matEntry["材料ID"];

//         // 🔍 只有当这个材料ID出现在当前 stage 中，才生成任务
//         if (stageMaterialIDs[matID]) {
//             var cleanCategory = String(matEntry["材料类别"])
//                 .replace(/[\u200B-\u200D\uFEFF\u2028\u2029\u202C\u202A\u202B]/g, '')
//                 .trim();

//             var newItem = {
//                 material: matID,
//                 tags: [], // 如果需要，可以从 rawMaterials 中取，但通常不需要
//                 category: cleanCategory,
//                 effects: matEntry["材料效果"]
//             };
//             materialsWithCategory.push(newItem);
//         }
//     }

//     // --- 按类别分组 ---
//     // 将材料按 "材料类别"（如"骨"、"纤维"）分组，存入 Map<类别, 材料数组>
//     let categoryMap = new Map();
//     for (let i = 0; i < materialsWithCategory.length; i++) {
//         let m = materialsWithCategory[i];
//         if (!categoryMap.has(m.category)) {
//             categoryMap.set(m.category, []); // 初始化空数组
//         }
//         categoryMap.get(m.category).push(m); // 把材料加入对应类别组
//     }

//     // --- 按预定义中文顺序拼接材料 --- 
//     // 按照 categoryOrder 的顺序，依次取出每类材料，保证显示顺序可控
//     let orderedMaterials = [];
//     for (let i = 0; i < categoryOrder.length; i++) {

//         // console.log("************************")

//         let cat = categoryOrder[i];
//         if (categoryMap.has(cat)) {

//             // console.log(cat)
//             let items = categoryMap.get(cat);
//             for (let j = 0; j < items.length; j++) {
//                 orderedMaterials.push(items[j]); // 按顺序收集材料
//             }
//         }
//     }

//     // --- 按类别分块布局 + 自定义 X 间隔 ---
//     let currentX = startX; // 当前类别块的起始 X

//     // 收集所有要处理的类别（保持顺序）
//     let categoriesToProcess = categoryOrder.filter(cat => categoryMap.has(cat));
//     // 添加未定义类别
//     for (let [cat, items] of categoryMap.entries()) {
//         if (!categoriesToProcess.includes(cat)) {
//             categoriesToProcess.push(cat);
//         }
//     }


//     for (let cat of categoriesToProcess) {
//         let items = categoryMap.get(cat);
//         if (items.length === 0) continue;

//         // 计算该类别需要多少列
//         let columnsNeeded = Math.ceil(items.length / maxPerColumn);

//         // 在 [currentX, currentX + columnsNeeded * stepX) 范围内布局
//         let localColumn = 0;
//         let localRow = 0;

//         for (let j = 0; j < items.length; j++) {
//             if (localRow >= maxPerColumn) {
//                 localColumn++;
//                 localRow = 0;
//             }

//             let x = currentX + localColumn * stepX;
//             let y = currentStageY + localRow * stepY;

//             let m = items[j];
//             // let title$1 = $I18n.get(Item.of(m.material).getDescriptionId());

//             let allCategoriesForThisMaterial = materialEffectsByCategory[m.material] || {};

//             // ========== 新：构建 title（材料名 + 效果/属性） ==========
//             // 需要从原始 Materials 中获取未格式化的数据
//             let originalMat = Materials.find(mat => mat["材料ID"] === m.material && mat["材料类别"] === m.category);
//             let title$1 = "";
//             if (originalMat) {
//                 // 根据当前 stageName 映射中文阶段名
//                 let stageLabelMap = {
//                     "stage1": "&f[阶段一]",
//                     "stage2": "&e[阶段二]",
//                     "stage3": "&6[阶段三]"
//                 };
//                 let currentStageLabel = stageLabelMap[stageName] || stageName;

//                 title$1 = formatMaterialTitle(
//                     originalMat["材料名"],
//                     originalMat["材料效果"],
//                     originalMat["材料属性"],
//                     currentStageLabel  // 👈 传入阶段标签
//                 );
//             } else {
//                 // 备选：只显示材料名
//                 title$1 = `&f[${$I18n.get(Item.of(m.material).getDescriptionId())}]`;
//             }

//             // ========== SUBTITLE: 简单类别名 ==========
//             let subtitle$1 = `&e[当前材料类型]&r: \n${m.category}`;

//             // ========== DESCRIPTION: 包含当前类别 + 其他类别 ==========
//             let description$1 = [];

//             // 首先添加当前类别（作为当前用途）
//             if (allCategoriesForThisMaterial[m.category]) {
//                 description$1.push("&e作为 " + m.category + "：");
//                 description$1.push(""); // 空行分隔
//                 description$1 = description$1.concat(allCategoriesForThisMaterial[m.category]);
//                 description$1.push(""); // 空行分隔
//                 description$1.push(""); // 空行分隔
//             }

//             let hasOther = false;

//             // 按 categoryOrder 顺序添加其他类别
//             for (var idx = 0; idx < categoryOrder.length; idx++) {
//                 var cat = categoryOrder[idx];
//                 if (cat !== m.category && allCategoriesForThisMaterial[cat]) {
//                     hasOther = true;
//                     description$1.push("&7作为 " + cat + "：");
//                     description$1 = description$1.concat(allCategoriesForThisMaterial[cat]);
//                     description$1.push(""); // 空行分隔
//                 }
//             }

//             // 处理未在 categoryOrder 中定义的类别（兜底）
//             for (var cat in allCategoriesForThisMaterial) {
//                 if (allCategoriesForThisMaterial.hasOwnProperty(cat) && cat !== m.category) {
//                     if (categoryOrder.indexOf(cat) === -1) {
//                         hasOther = true;
//                         description$1.push("&7作为 " + cat + "：");
//                         description$1 = description$1.concat(allCategoriesForThisMaterial[cat]);
//                         description$1.push("");
//                     }
//                 }
//             }

//             // 清理末尾空行
//             if (description$1.length > 0 && description$1[description$1.length - 1] === "") {
//                 description$1.pop();
//             }
//             // ========== ✅ 新增：从 ftb.json 注入额外描述 ==========
//             if (FTB_MATERIAL_DATA !== null) {
//                 // 使用 .get() 因为 FTB_MATERIAL_DATA 是 Java Map
//                 let extraData = FTB_MATERIAL_DATA.get(m.material);
//                 if (extraData !== null) {
//                     let extraDesc = extraData.get("description");
//                     if (extraDesc !== null) {

//                         // 如果已有描述，加一个分隔空行
//                         if (description$1.length > 0) {
//                             description$1.push("");
//                         }

//                         // 追加额外描述
//                         description$1.push("&7[额外描述]");
//                         description$1 = description$1.concat(extraDesc);
//                     }
//                 }
//             }

//             // 最终 description
//             let finalDescription = description$1.join("\\n");
//             let pair = generateTaskPair(m.material);
//             let id1 = pair.id1;
//             let id2 = pair.id2;
//             // let questId = globalTaskIndex.toString().padStart(16, '0');
//             // let questId = generateTaskId(m.material);
//             let questId = id1
//             // let taskId = ('1' + globalTaskIndex.toString()).padStart(16, '0').substring(0, 16);
//             let taskId = id2
//             let taskId$1 = pair.id3
//             let taskId$2 = pair.id4

//             questsArray.push({
//                 description: description$1,
//                 icon: {
//                     Count: 1,
//                     id: m.material

//                 },
//                 id: questId,
//                 subtitle: subtitle$1,
//                 tasks: [
//                     {
//                         id: taskId$1,
//                         type: "checkmark"
//                     },
//                     {
//                         id: taskId,
//                         item: m.material,
//                         type: "item"
//                     }
//                 ],
//                 title: title$1,
//                 x: x,
//                 y: y
//             });
//             questsLinkArray.push({
//                 id: taskId$2,
//                 linked_quest: questId,
//                 x: x,
//                 y: y
//             });
//             Materials_Ftbid.push({
//                 name: m.material,
//                 id: questId
//             });

//             globalTaskIndex++;
//             localRow++;
//         }
//         // 当前类别块结束，移动 X 到下一个块的起始位置
//         currentX += columnsNeeded * stepX + categoryGapX;
//     }
//     // 更新下一个 stage 的起始 y
//     let lastY = currentStageY;
//     if (questsArray.length > 0) {
//         lastY = questsArray[questsArray.length - 1].y;
//     }
//     currentStageY = lastY + stageVerticalGap;
// }
// let materials_ftbid_String = `global.materials_ftbid = ${JSON.stringify(Materials_Ftbid, null, '\t')};`;



// // let schematicData = $DataManager.schematicData.getData().entrySet();

// // for (let i = 0; i < schematicData.length; i++) {
// //     let entry = schematicData[i];
// //     let schematicKey = entry.getKey();
// //     let schematicValue = entry.getValue();
// //     let outcomes = schematicValue.outcomes;
// //     let slots = schematicValue.slots; //  ["blade", "handle"]

// //     for (let j = 0; j < outcomes.length; j++) {
// //         let material = outcomes[j].material;
// //         let itemStacks = material.getApplicableItemStacks();
// //         if (itemStacks.length === 0) continue;
// //         for (let k = 0; k < itemStacks.length; k++) {
// //             let stack = itemStacks[k];
// //             if (TetraJS$TetraJSUtils.stackIsTetraMaterial(stack)) {
// //                 continue;
// //             }
// //             let schematicPath = schematicKey.path; //  "zu_li_jian/water_walker/water_walker_you"
// //             let schematicName = $I18n.get("tetra/schematic/" + schematicPath + ".name");
// //             let slotNames = [];
// //             for (let s = 0; s < slots.length; s++) {
// //                 let slotKey = slots[s];
// //                 let localizedSlot = $I18n.get("tetra.slot." + slotKey);
// //                 slotNames.push(localizedSlot);
// //             }
// //             let combinedSlots = slotNames.join("/");
// //             tooltip.push({
// //                 "name": stack.id,
// //                 "slots": combinedSlots,
// //                 "schematic": schematicName
// //             });
// //         }
// //     }
// // }
// // JsonIO.jsjs$write("kubejs/client_scripts/yi/json/material_items_schematic.json",  tooltip);



// let replacementData = $DataManager.replacementData.getData().entrySet();
// let replacementItems = []
// let repInputItemid = []
// for (let i = 0; i < replacementData.length; i++) {
//     let entry = replacementData[i];
//     let replacementKey = entry.getKey();
//     let replacementDefinition = entry.getValue();
//     for (let j = 0; j < replacementDefinition.length; j++) {
//         let replacementItem = replacementDefinition[j].predicate.items;
//         let repOutputItemid = replacementDefinition[j].itemStack.id
//         let items = []
//         for (let k = 0; k < replacementItem.length; k++) {
//             let repInputItemid = replacementItem[k].id;
//             items.push({
//                 "id": repInputItemid
//             });
//         }
//         replacementItems.push({
//             "output":  repOutputItemid,
//             "input": items
//         });
//     }
// }
// let replacementItems_String = `global.replacementItems = ${JSON.stringify(replacementItems, null, '\t')};`;
// let material_category_String = `global.material_category = ${JSON.stringify(Materials$1, null, '\t')};`;

// // console.log(replacementItems)
// // JsonIO.jsjs$write("kubejs/client_scripts/yi/json/material_items_schematic.json",  tooltip);



// JsonIO.jsjs$write("kubejs/client_scripts/yi/json/ftb.json", questsArray);

// JsonIO.jsjs$write("kubejs/client_scripts/yi/json/ftb_link.json", questsLinkArray);

// JsonIO.jsjs$write("kubejs/client_scripts/yi/json/material/material_category.json", Materials$1);

// JsonIO.jsjs$write("kubejs/client_scripts/yi/json/material/material_effect.json", Materials$2);

// // JsonIO.jsjs$write("kubejs/client_scripts/yi/json/material/material_ftbid.json", Materials_Ftbid);

// JSIO.write("kubejs/startup_scripts/global/material_ftbid.js", materials_ftbid_String)

// JSIO.write("kubejs/startup_scripts/global/replacementItems.js", replacementItems_String)

// JSIO.write("kubejs/startup_scripts/global/material_category.js", material_category_String)


// // console.log(processedEffects)



// let $ScrollTooltipHandler = Java.loadClass("net.yiran.extraholopage.gui.ScrollTooltipHandler")
// // 