// code lists
// code:0001 -> Imports
// code:0002 -> Configurations (Hooks & Conditions)
// code:0003 -> Command Registrations (Menu, Help, Loops, Console, Variable, Varlist, Broadcast, Send)
// code:0004 -> Data Management Helpers
// code:0005 -> Iteration Helpers
// code:0006 -> Variable Storage Helpers
// code:0007 -> Text Parsing (Replace Variables)
// code:0008 -> Expression Evaluation
// code:0009 -> Variable Conditions Check & Action
// code:0010 -> Built-in Conditions Check
// code:0011 -> Input Matching
// code:0012 -> Action Execution (Commands, Messages, Variables) with Error Logging
// code:0013 -> Custom UI Handling
// code:0014 -> Execution Pipeline
// code:0015 -> World Event Subscriptions (ItemUse, BlockBreak, BlockPlace, ChatSend, EntityDie...)
// code:0016 -> Main Menu & Help Menus
// code:0017 -> Folder Management (CRUD)
// code:0018 -> Function Management (List, Create, Delete, Editor)
// code:0019 -> Condition & Input Editors
// code:0020 -> Messages & Commands Editors
// code:0021 -> Variables Editor
// code:0022 -> Custom UI Editor
// code:0023 -> Variable Conditions Editor
// code:0024 -> Loops Management (Data, Tick Handler, Actions, Menus)
// code:0025 -> Error Console Menu
// code:0026 -> Player Spawn Initialization

/* ------------------------------
   code:0001 [Imports]
   ------------------------------ */
import { CommandPermissionLevel, CustomCommandStatus, CustomCommandParamType, system, Player, world, BlockPermutation, EquipmentSlot } from "@minecraft/server";
import { ActionFormData, ModalFormData } from "@minecraft/server-ui";
const startTime = Date.now();

/* ------------------------------
   code:0002 [Configurations (Hooks & Conditions)]
   ------------------------------ */
const HOOKS = [
    { id: "itemUse", label: "Item Use", inputs: [{ id: "items", label: "Items (comma-separated)", placeholder: "stone, iron_sword, diamond_axe" }], events: ["before", "after"] },
    { id: "playerBreakBlock", label: "Player Break Block", inputs: [{ id: "blocks", label: "Blocks (comma-separated)", placeholder: "stone, dirt, diamond_block" }], events: ["before", "after"] },
    { id: "playerPlaceBlock", label: "Player Place Block", inputs: [{ id: "blocks", label: "Blocks (comma-separated)", placeholder: "stone, dirt, diamond_block" }], events: ["before", "after"] },
    { id: "entityDie", label: "Entity Die", inputs: [{ id: "entities", label: "Entities (comma-separated)", placeholder: "minecraft:zombie, minecraft:cow, any" }], events: ["after"] },
    { id: "onChat", label: "On Chat", inputs: [{ id: "message", label: "Message (comma-separated or \"any\")", placeholder: "hello, hi there, any" }], events: ["before", "after"] },
    { id: "entityHurt", label: "Entity Hurt", inputs: [{ id: "entities", label: "Entities (comma-separated)", placeholder: "minecraft:zombie, minecraft:cow, any" }], events: ["after"] },
    { id: "playerInteractWithBlock", label: "Player Interact With Block", inputs: [{ id: "blocks", label: "Blocks (comma-separated)", placeholder: "stone, dirt, diamond_block, any" }], events: ["before", "after"] },
    { id: "onHold", label: "On Hold (Item in Hand)", inputs: [{ id: "items", label: "Items (comma-separated or 'any')", placeholder: "diamond_sword, bow, any" }], events: ["after"] },
    { id: "itemStartUse", label: "Item Start Use", inputs: [{ id: "items", label: "Items", placeholder: "bow, crossbow, shield" }], events: ["after"] },
    { id: "itemStopUse", label: "Item Stop Use", inputs: [{ id: "items", label: "Items", placeholder: "bow, crossbow, shield" }], events: ["after"] },
    { id: "itemStartUseOn", label: "Item Start Use On", inputs: [{ id: "items", label: "Items", placeholder: "flint_and_steel, bone_meal" }], events: ["after"] },
    { id: "itemStopUseOn", label: "Item Stop Use On", inputs: [{ id: "items", label: "Items", placeholder: "flint_and_steel, bone_meal" }], events: ["after"] },
    { id: "itemCompleteUse", label: "Item Complete Use", inputs: [{ id: "items", label: "Items", placeholder: "chorus_fruit, potion" }], events: ["after"] },
    { id: "itemReleaseUse", label: "Item Release Use", inputs: [{ id: "items", label: "Items", placeholder: "bow, trident" }], events: ["after"] },
    { id: "entityHitBlock", label: "Entity Hit Block", inputs: [{ id: "entities", label: "Entities", placeholder: "minecraft:arrow, any" }], events: ["after"] },
    { id: "entitySpawn", label: "Entity Spawn", inputs: [{ id: "entities", label: "Entities", placeholder: "minecraft:zombie, any" }], events: ["after"] },
    { id: "entityRemove", label: "Entity Remove", inputs: [{ id: "entities", label: "Entities", placeholder: "any" }], events: ["before", "after"] },
    { id: "playerJoin", label: "Player Join", inputs: [], events: ["after"] },
    { id: "playerLeave", label: "Player Leave", inputs: [], events: ["after"] },
    { id: "playerSpawn", label: "Player Spawn", inputs: [], events: ["after"] },
    { id: "playerInteractWithEntity", label: "Player Interact With Entity", inputs: [{ id: "entities", label: "Entities", placeholder: "minecraft:villager, any" }], events: ["before", "after"] },
];

const HOOK_VARIABLES = {
    itemUse: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}"
    ],
    playerBreakBlock: [
        "{player}", "{player health}", "{player maxhealth}",
        "{broken block}", "{block_name}", "{raw block}",
        "{x}", "{y}", "{z}"
    ],
    playerPlaceBlock: [
        "{player}", "{player health}", "{player maxhealth}",
        "{placed block}", "{block_name}", "{raw block}",
        "{x}", "{y}", "{z}"
    ],
    entityDie: [
        "{dead entity}", "{entity_name}", "{raw victim}",
        "{victim}", "{victim health}", "{victim maxhealth}", "{victim coords}",
        "{killer}", "{attacker}", "{raw attacker}", "{attacker health}", "{attacker maxhealth}", "{attacker coords}"
    ],
    entityHurt: [
        "{dead entity}", "{entity_name}", "{raw victim}",
        "{victim}", "{victim health}", "{victim maxhealth}", "{victim coords}",
        "{attacker}", "{raw attacker}", "{attacker health}", "{attacker maxhealth}", "{attacker coords}",
        "{damage cause}", "{damage}", "{killer}"
    ],
    playerInteractWithBlock: [
        "{player}", "{player health}", "{player maxhealth}",
        "{interacted block}", "{block_name}", "{raw block}",
        "{x}", "{y}", "{z}"
    ],
    onChat: [
        "{player}", "{player health}", "{player maxhealth}",
        "{message}"
    ],
    onHold: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}"
    ],
    itemStartUse: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}", "{use_duration}"
    ],
    itemStopUse: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}", "{use_duration}"
    ],
    itemStartUseOn: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}",
        "{block}", "{block_name}", "{x}", "{y}", "{z}"
    ],
    itemStopUseOn: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}",
        "{block}", "{block_name}", "{x}", "{y}", "{z}"
    ],
    itemCompleteUse: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}"
    ],
    itemReleaseUse: [
        "{player}", "{player health}", "{player maxhealth}",
        "{item}", "{item_name}", "{item_lore}", "{raw item}", "{charge_time}"
    ],
    entityHitBlock: [
        "{block}", "{block_name}", "{raw block}", "{x}", "{y}", "{z}"
    ],
    entitySpawn: [
        "{entity}", "{entity_name}", "{raw entity}", "{x}", "{y}", "{z}"
    ],
    entityRemove: [
        "{entity}", "{entity_name}", "{raw entity}"
    ],
    playerJoin: [
        "{player}", "{player health}", "{player maxhealth}"
    ],
    playerLeave: [
        "{player}"
    ],
    playerSpawn: [
        "{player}", "{player health}", "{player maxhealth}", "{initial_spawn}"
    ],
    playerInteractWithEntity: [
        "{player}", "{entity}", "{entity_name}", "{raw entity}"
    ]
};

// Able to use toggle/param in "type"
const CONDITIONS = [
    { id: "isClimbing", label: "isClimbing", type: "toggle" },
    { id: "isEmoting", label: "isEmoting", type: "toggle" },
    { id: "isFalling", label: "isFalling", type: "toggle" },
    { id: "isFlying", label: "isFlying", type: "toggle" },
    { id: "isGliding", label: "isGliding", type: "toggle" },
    { id: "isInWater", label: "isInWater", type: "toggle" },
    { id: "isJumping", label: "isJumping", type: "toggle" },
    { id: "isOnGround", label: "isOnGround", type: "toggle" },
    { id: "isSleeping", label: "isSleeping", type: "toggle" },
    { id: "isSneaking", label: "isSneaking", type: "toggle" },
    { id: "isSprinting", label: "isSprinting", type: "toggle" },
    { id: "isSwimming", label: "isSwimming", type: "toggle" },
    { id: "KilledByPlayer", label: "Killed By Player", type: "toggle" },
    { id: "KilledByEntity", label: "Killed By Entity", type: "toggle" },
    { id: "KilledByEnvironment", label: "Killed By Environment", type: "toggle" },
    { id: "CauseLava", label: "Cause: Lava", type: "toggle" },
    { id: "CauseFall", label: "Cause: Fall", type: "toggle" },
    { id: "CauseFire", label: "Cause: Fire", type: "toggle" },
    { id: "CauseExplosion", label: "Cause: Explosion", type: "toggle" },
    { id: "HurtByPlayer", label: "Hurt By Player", type: "toggle" },
    { id: "HurtByEntity", label: "Hurt By Entity", type: "toggle" },
    { id: "HurtByEnvironment", label: "Hurt By Environment", type: "toggle" },
    { id: "region", label: "In Region", type: "param", example: "myspawn" },
    { id: "atLocation", label: "At Location", type: "param", example: "10 54 -3" }
];

/* ------------------------------
   code:0003 [Command Registrations (Menu, Help, Loops, Console, Variable, Varlist, Broadcast, Send)]
   ------------------------------ */
system.beforeEvents.startup.subscribe(({ customCommandRegistry }) => {
    customCommandRegistry.registerCommand(
        {
            name: "sync:menu",
            description: "Open the Sync main menu.",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false
        },
        (origin) => {
            const source = origin.initiator ?? origin.sourceEntity;
            if (!source.hasTag("sync")) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: "You must have the 'sync' tag to use this command.\n§7Run: §a/tag @s add sync"
                };
            }
            if (!(source instanceof Player)) {
                return { status: CustomCommandStatus.Failure, message: "Only players can open the menu." };
            }
            system.run(() => openMainMenu(source));
            return { status: CustomCommandStatus.Success };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:synchelp",
            description: "Open the Sync help menu to learn how to use the addon.",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false
        },
        (origin) => {
            const source = origin.initiator ?? origin.sourceEntity;
            if (!source.hasTag("sync")) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: "You must have the 'sync' tag to use this command.\n§7Run: §a/tag @s add sync"
                };
            }
            if (!(source instanceof Player)) {
                return { status: CustomCommandStatus.Failure, message: "Only players can open the help menu." };
            }
            system.run(() => openHelpMainMenu(source));
            return { status: CustomCommandStatus.Success };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:loops",
            description: "Manage loops in Sync.",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false
        },
        (origin) => {
            const source = origin.initiator ?? origin.sourceEntity;
            if (!(source instanceof Player)) {
                return { status: CustomCommandStatus.Failure, message: "Only players can manage loops." };
            }
            if (!source.hasTag("sync")) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: "You must have the 'sync' tag to use this command.\n§7Run: §a/tag @s add sync"
                };
            }
            system.run(() => openLoopsMainMenu(source));
            return { status: CustomCommandStatus.Success };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:console",
            description: "Open the Sync error console (last 20 errors).",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false
        },
        (origin) => {
            const source = origin.initiator ?? origin.sourceEntity;
            if (!source.hasTag("sync")) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: "You must have the 'sync' tag to use this command.\n§7Run: §a/tag @s add sync"
                };
            }
            if (!(source instanceof Player)) {
                return { status: CustomCommandStatus.Failure, message: "Only players can open the console." };
            }
            system.run(() => openErrorConsoleMenu(source));
            return { status: CustomCommandStatus.Success };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:variable",
            description: "Modify Sync global/local variables directly.",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false,
            mandatoryParameters: [
                { name: "action", type: CustomCommandParamType.String },
                { name: "scope", type: CustomCommandParamType.String },
                { name: "folder", type: CustomCommandParamType.String },
                { name: "varname", type: CustomCommandParamType.String }
            ],
            optionalParameters: [
                { name: "player", type: CustomCommandParamType.String },
                { name: "amount", type: CustomCommandParamType.Integer }
            ]
        },
        (origin, action, scope, folderName, varName, playerName, amount) => {
            const source = origin.initiator ?? origin.sourceEntity;
            const sender = source instanceof Player ? source : null;
            if (!source.hasTag("sync")) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: "You must have the 'sync' tag to use this command.\n§7Run: §a/tag @s add sync"
                };
            }
            action = action?.toLowerCase();
            if (!["add", "set", "remove", "reset"].includes(action)) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        "§cInvalid action.\n" +
                        "§7Valid actions: §fadd, set, remove, reset\n" +
                        "§7Example: §f/sync:variable add local stats diamondbreak Mace 1"
                };
            }
            scope = scope?.toLowerCase();
            if (!["local", "global"].includes(scope)) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        "§cInvalid scope.\n" +
                        "§7Valid scopes: §flocal, global\n" +
                        "§7Example: §f/sync:variable set global stats diamondbreak 100"
                };
            }
            if (!folderName || !varName) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        "§cFolder and variable name cannot be empty.\n" +
                        "§7Usage: §f/sync:variable <action> <scope> <folder> <varname> [player] [amount]\n" +
                        "§7Example: §f/sync:variable add local stats diamondbreak Mace 1"
                };
            }
            let targetPlayer = sender;
            if (scope === "local" && playerName) {
                targetPlayer = world.getPlayers().find(p => p.name === playerName) ?? null;
                if (!targetPlayer) {
                    return {
                        status: CustomCommandStatus.Failure,
                        message:
                            `§cPlayer '${playerName}' not found.\n` +
                            "§7Make sure you typed the exact player name.\n" +
                            "§7Example: §f/sync:variable add local stats diamondbreak Steve 1"
                    };
                }
            }
            if (scope === "local" && !targetPlayer) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        "§cLocal variables require a target player.\n" +
                        "§7Usage: §f/sync:variable add local <folder> <varname> <player> <amount>"
                };
            }
            if (action === "reset") {
                amount = 0;
            } else {
                amount = amount ?? 1;
                if (isNaN(amount)) {
                    return {
                        status: CustomCommandStatus.Failure,
                        message:
                            "§cAmount must be a number.\n" +
                            "§7Example: §f/sync:variable add local stats diamondbreak Steve 5"
                    };
                }
            }
            let current = 0;
            try {
                if (scope === "global") {
                    current = getVarValue("global", folderName, null, varName) ?? 0;
                } else {
                    const folder = folderByName(folderName);
                    if (!folder) {
                        return {
                            status: CustomCommandStatus.Failure,
                            message:
                                `§cFolder '${folderName}' not found.\n` +
                                "§7Check your folder names in your Sync system."
                        };
                    }
                    current = getVarValue("local", folder, targetPlayer, varName) ?? 0;
                }
            } catch (err) {
                return { status: CustomCommandStatus.Failure, message: `§cError reading variable '${varName}': ${err}` };
            }
            let newValue = current;
            switch (action) {
                case "add": newValue = current + amount; break;
                case "remove": newValue = current - amount; break;
                case "set": newValue = amount; break;
                case "reset": newValue = 0; break;
            }
            try {
                if (scope === "global") {
                    setGlobalValue(varName, newValue);
                } else {
                    const folder = folderByName(folderName);
                    setLocalValue(folder, targetPlayer, varName, newValue);
                }
            } catch (err) {
                return { status: CustomCommandStatus.Failure, message: `§cError setting variable '${varName}': ${err}` };
            }
            const scopeLabel = scope === "global" ? `global (${folderName})` : `local (${folderName})`;
            const playerLabel = scope === "local" ? ` for §e${targetPlayer.name}` : "";
            return {
                status: CustomCommandStatus.Success,
                message:
                    `§aVariable updated successfully!\n` +
                    `§7Scope: §f${scopeLabel}\n` +
                    `§7Variable: §f${varName}\n` +
                    `§7Previous Value: §f${current}\n` +
                    `§7New Value: §a${newValue}${playerLabel}\n` +
                    "§7Tip: Use /sync:varlist to see all variables in a folder."
            };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:varlist",
            description: "List Sync variables for a folder (local or global).",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false,
            mandatoryParameters: [
                { name: "scope", type: CustomCommandParamType.String },
                { name: "folder", type: CustomCommandParamType.String }
            ],
            optionalParameters: [
                { name: "player", type: CustomCommandParamType.String }
            ]
        },
        (origin, scope, folderName, playerName) => {
            const source = origin.initiator ?? origin.sourceEntity;
            const sender = source instanceof Player ? source : null;
            if (!source.hasTag("sync")) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: "You must have the 'sync' tag to use this command.\n§7Run: §a/tag @s add sync"
                };
            }
            scope = scope?.toLowerCase();
            if (!["local", "global"].includes(scope)) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        "§cInvalid scope.\n" +
                        "§7Valid options: §flocal, global\n" +
                        "§7Usage: §f/sync:varlist <scope> <folder> [player]\n" +
                        "§7Examples:\n" +
                        "§7/sync:varlist global stats\n" +
                        "§7/sync:varlist local stats Steve"
                };
            }
            if (!folderName) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        "§cFolder name cannot be empty.\n" +
                        "§7Usage: §f/sync:varlist <scope> <folder> [player]\n" +
                        "§7Example: §f/sync:varlist global stats"
                };
            }
            let targetPlayer = null;
            if (scope === "local") {
                if (playerName) {
                    targetPlayer = world.getPlayers().find(p => p.name === playerName) ?? null;
                    if (!targetPlayer) {
                        return {
                            status: CustomCommandStatus.Failure,
                            message:
                                `§cPlayer '${playerName}' not found.\n` +
                                "§7Make sure you typed the exact player name.\n" +
                                "§7Example: §f/sync:varlist local stats Steve"
                        };
                    }
                } else {
                    targetPlayer = sender;
                }
                if (!targetPlayer) {
                    return {
                        status: CustomCommandStatus.Failure,
                        message:
                            "§cLocal variables require a player.\n" +
                            "§7Usage: §f/sync:varlist local <folder> <player>\n" +
                            "§7Example: §f/sync:varlist local stats Steve"
                    };
                }
            }
            let varsObj = {};
            try {
                if (scope === "global") {
                    varsObj = getGlobalStore();
                } else {
                    const folder = folderByName(folderName);
                    if (!folder) {
                        return {
                            status: CustomCommandStatus.Failure,
                            message:
                                `§cFolder '${folderName}' not found.\n` +
                                "§7Check your Sync folder names.\n" +
                                "§7Example: §f/sync:varlist local stats Steve"
                        };
                    }
                    varsObj = getLocalStore(folder, targetPlayer);
                }
            } catch (err) {
                return {
                    status: CustomCommandStatus.Failure,
                    message:
                        `§cError accessing variables: ${err}\n` +
                        "§7Tip: Make sure your Sync system is initialized correctly."
                };
            }
            const varNames = Object.keys(varsObj);
            if (varNames.length === 0) {
                return {
                    status: CustomCommandStatus.Success,
                    message:
                        `§7No variables found in ${scope} folder '${folderName}'.\n` +
                        "§7Tip: Use /sync:variable to create or modify variables."
                };
            }
            let output =
                `§aVariables in ${scope} folder '${folderName}':\n` +
                "§7(Use /sync:variable to change values)\n";
            for (const v of varNames) {
                const value = varsObj[v];
                output += `§7- §e${v}§f: §a${value}\n`;
            }
            return {
                status: CustomCommandStatus.Success,
                message: output.trim()
            };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:broadcast",
            description: "Send a message to all players.",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false,
            mandatoryParameters: [
                { name: "message", type: CustomCommandParamType.String }
            ]
        },
        (origin, message) => {
            world.getPlayers().forEach(p => p.sendMessage(`§f${message}`));
            return {
                status: CustomCommandStatus.Success
            };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:send",
            description: "Send a private message to a specific player.",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false,
            mandatoryParameters: [
                { name: "player", type: CustomCommandParamType.String },
                { name: "message", type: CustomCommandParamType.String }
            ]
        },
        (origin, playerName, message) => {
            const source = origin.initiator ?? origin.sourceEntity;
            const sender = source instanceof Player ? source : null;
            const target = world.getPlayers().find(p => p.name === playerName);
            if (!target) {
                return {
                    status: CustomCommandStatus.Failure,
                    message: `§cPlayer '${playerName}' not found.`
                };
            }
            target.sendMessage(`§f${message}`);
            return {
                status: CustomCommandStatus.Success
            };
        }
    );
    customCommandRegistry.registerCommand(
        {
            name: "sync:region",
            description: "Manage protection regions",
            permissionLevel: CommandPermissionLevel.Any,
            cheatsRequired: false,
            mandatoryParameters: [
                { name: "action", type: CustomCommandParamType.String }
            ],
            optionalParameters: [
                { name: "regionName", type: CustomCommandParamType.String },
                { name: "arg2", type: CustomCommandParamType.String }
            ]
        },
        (origin, action, regionName, arg2) => {
            const player = origin.sourceEntity instanceof Player ? origin.sourceEntity : null;
            if (!player?.hasTag("sync")) {
                return { status: CustomCommandStatus.Failure, message: "§cYou need the 'sync' tag." };
            }
            action = (action ?? "").toLowerCase();

            if (!player.getDynamicProperty("region_pos1")) player.setDynamicProperty("region_pos1", null);
            if (!player.getDynamicProperty("region_pos2")) player.setDynamicProperty("region_pos2", null);

            if (action === "selection" || action === "sel") {
                player.setDynamicProperty("region_pos1", null);
                player.setDynamicProperty("region_pos2", null);
                clearOutline(player);
                player.sendMessage("§aRegion selection cleared. §eLeft-click (punch/break attempt) a block to set Position 1, right-click (interact) a block to set Position 2. §7(Selection won't break blocks.)");
                system.run(() => {
                    player.playSound("random.pop");
                });
                return { status: CustomCommandStatus.Success };
            }

            if (action === "expand") {
                const pos1 = player.getDynamicProperty("region_pos1");
                const pos2 = player.getDynamicProperty("region_pos2");
                if (!pos1 || !pos2) {
                    return { status: CustomCommandStatus.Failure, message: "§cNo selection. Use /sync:region selection first." };
                }
                player.setDynamicProperty("region_pos1", { ...pos1, y: -64 });
                player.setDynamicProperty("region_pos2", { ...pos2, y: 319 });
                player.sendMessage("§aRegion expanded vertically (-64 to 319).");
                return { status: CustomCommandStatus.Success };
            }

            if (action === "list") {
                const regions = getRegions();
                if (regions.length === 0) {
                    player.sendMessage("§eNo regions defined.");
                } else {
                    player.sendMessage("§aRegions:");
                    regions.forEach(r => {
                        const min = `${Math.min(r.pos1.x, r.pos2.x)},${Math.min(r.pos1.y, r.pos2.y)},${Math.min(r.pos1.z, r.pos2.z)}`;
                        const max = `${Math.max(r.pos1.x, r.pos2.x)},${Math.max(r.pos1.y, r.pos2.y)},${Math.max(r.pos1.z, r.pos2.z)}`;
                        player.sendMessage(`§7- §e${r.name} §8(${min} → ${max})`);
                    });
                }
                return { status: CustomCommandStatus.Success };
            }
            if (action === "reveal") {
                if (!regionName) return { status: CustomCommandStatus.Failure, message: "§cUsage: /sync:region reveal <name>" };
                const region = getRegion(regionName);
                if (!region) return { status: CustomCommandStatus.Failure, message: "§cRegion not found." };
                outlineRegion(player, region);
                player.sendMessage(`§aRevealing region '${regionName}' with particles.`);
                return { status: CustomCommandStatus.Success };
            }
            if (action === "create") {
                if (!regionName) return { status: CustomCommandStatus.Failure, message: "§cUsage: /sync:region create <name>" };
                const pos1 = player.getDynamicProperty("region_pos1");
                const pos2 = player.getDynamicProperty("region_pos2");
                if (!pos1 || !pos2) return { status: CustomCommandStatus.Failure, message: "§cSelect two positions first." };
                if (getRegion(regionName)) return { status: CustomCommandStatus.Failure, message: "§cRegion already exists." };

                const regions = getRegions();
                regions.push({
                    name: regionName,
                    pos1: { x: Math.floor(pos1.x), y: Math.floor(pos1.y), z: Math.floor(pos1.z) },
                    pos2: { x: Math.floor(pos2.x), y: Math.floor(pos2.y), z: Math.floor(pos2.z) }
                });
                clearOutline(player);
                saveRegions(regions);
                player.sendMessage(`§aRegion '${regionName}' created.`);
                return { status: CustomCommandStatus.Success };
            }

            if (action === "delete" || action === "remove") {
                if (!regionName) return { status: CustomCommandStatus.Failure, message: "§cUsage: /sync:region delete <name>" };
                const regions = getRegions().filter(r => r.name.toLowerCase() !== regionName.toLowerCase());
                saveRegions(regions);
                player.sendMessage(`§aRegion '${regionName}' deleted (or didn't exist).`);
                return { status: CustomCommandStatus.Success };
            }

            return { status: CustomCommandStatus.Failure, message: "§cInvalid action. Use: selection, expand, list, reveal <name>, create <name>, delete <name>" };
        }
    );
});

/* ------------------------------
   code:0004 [Data Management Helpers]
   ------------------------------ */
function ensureData() {
    let raw = world.getDynamicProperty("sync:data");
    if (!raw) raw = JSON.stringify({ folders: [], globals: {} });
    return JSON.parse(raw);
}
function getData() {
    return ensureData();
}
function setData(data) {
    world.setDynamicProperty("sync:data", JSON.stringify(data));
}
function getFolders() {
    return getData().folders.map(f => f.name);
}
function getAllFolders() {
    return getData().folders;
}
function ensureFolder(name) {
    const data = getData();
    let folder = data.folders.find(f => f.name === name);
    if (!folder) {
        folder = { name, functions: [], variables: { local: {}, variableDefaults: {} }, randomVars: {}, enabled: true };
        data.folders.push(folder);
        setData(data);
    }
    if (folder.enabled === undefined) folder.enabled = true;
    if (!folder.randomVars) folder.randomVars = {};
    return folder;
}
function folderByName(name) {
    const data = getData();
    return data.folders.find(f => f.name === name);
}
function saveFolder(updatedFolder) {
    const data = getData();
    const idx = data.folders.findIndex(f => f.name === updatedFolder.name);
    if (idx >= 0) data.folders[idx] = updatedFolder;
    setData(data);
}

// Regions

function getRegions() {
    const data = getData();
    if (!data.regions) data.regions = [];
    return data.regions;
}
function saveRegions(regions) {
    const data = getData();
    data.regions = regions;
    setData(data);
}
function getRegion(name) {
    return getRegions().find(r => r.name.toLowerCase() === name.toLowerCase());
}

let outlineTasks = new Map();

function clearOutline(player) {
    const taskId = outlineTasks.get(player);
    if (taskId !== undefined) {
        system.clearRun(taskId);
        outlineTasks.delete(player);
    }
}

function startOutline(player) {
    clearOutline(player);

    const task = system.runInterval(() => {
        const pos1 = player.getDynamicProperty("region_pos1");
        const pos2 = player.getDynamicProperty("region_pos2");
        if (!pos1 || !pos2) {
            clearOutline(player);
            return;
        }

        const dim = player.dimension;
        const p1 = { x: pos1.x + 0.5, y: pos1.y + 0.5, z: pos1.z + 0.5 };
        const p2 = { x: pos2.x + 0.5, y: pos2.y + 0.5, z: pos2.z + 0.5 };

        const minX = Math.min(p1.x, p2.x), maxX = Math.max(p1.x, p2.x);
        const minY = Math.min(p1.y, p2.y), maxY = Math.max(p1.y, p2.y);
        const minZ = Math.min(p1.z, p2.z), maxZ = Math.max(p1.z, p2.z);

        const edges = [
            [{ x: minX, y: minY, z: minZ }, { x: maxX, y: minY, z: minZ }],
            [{ x: maxX, y: minY, z: minZ }, { x: maxX, y: minY, z: maxZ }],
            [{ x: maxX, y: minY, z: maxZ }, { x: minX, y: minY, z: maxZ }],
            [{ x: minX, y: minY, z: maxZ }, { x: minX, y: minY, z: minZ }],
            [{ x: minX, y: maxY, z: minZ }, { x: maxX, y: maxY, z: minZ }],
            [{ x: maxX, y: maxY, z: minZ }, { x: maxX, y: maxY, z: maxZ }],
            [{ x: maxX, y: maxY, z: maxZ }, { x: minX, y: maxY, z: maxZ }],
            [{ x: minX, y: maxY, z: maxZ }, { x: minX, y: maxY, z: minZ }],
            [{ x: minX, y: minY, z: minZ }, { x: minX, y: maxY, z: minZ }],
            [{ x: maxX, y: minY, z: minZ }, { x: maxX, y: maxY, z: minZ }],
            [{ x: maxX, y: minY, z: maxZ }, { x: maxX, y: maxY, z: maxZ }],
            [{ x: minX, y: minY, z: maxZ }, { x: minX, y: maxY, z: maxZ }]
        ];

        edges.forEach(edge => drawLineInstant(dim, edge[0], edge[1]));
    }, 40);

    outlineTasks.set(player, task);
}

function drawLineInstant(dim, start, end) {
    const dx = end.x - start.x;
    const dy = end.y - start.y;
    const dz = end.z - start.z;
    const steps = Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz), 16);
    if (steps === 0) return;

    const xInc = dx / steps;
    const yInc = dy / steps;
    const zInc = dz / steps;

    let current = { ...start };
    for (let i = 0; i <= steps; i++) {
        dim.spawnParticle("minecraft:endrod", current);
        current.x += xInc;
        current.y += yInc;
        current.z += zInc;
    }
}
/* ------------------------------
   code:0005 [Iteration Helpers]
   ------------------------------ */
function allFunctions() {
    const data = ensureData();
    const out = [];
    for (const folder of data.folders) {
        if (folder.enabled === false) continue;
        for (const fn of folder.functions || []) {
            if (fn.enabled === false) continue;
            out.push({ folder, fn });
        }
    }
    return out;
}

/* ------------------------------
   code:0006 [Variable Storage Helpers]
   ------------------------------ */
function getLocalStore(folder, player) {
    if (!folder.variables) folder.variables = { local: {} };
    const key = player.name;
    if (!folder.variables.local[key]) folder.variables.local[key] = {};
    return folder.variables.local[key];
}
function getGlobalStore() {
    const data = ensureData();
    if (!data.globals) data.globals = {};
    return data.globals;
}
function setLocalValue(folder, player, varName, value) {
    const store = getLocalStore(folder, player);
    store[varName] = value;
    const data = ensureData();
    data.folders = data.folders.map(f => f.name === folder.name ? folder : f);
    setData(data);
}
function setGlobalValue(varName, value) {
    const data = ensureData();
    if (!data.globals) data.globals = {};
    data.globals[varName] = value;
    setData(data);
}
function getVarValue(scope, folder, player, varName) {
    if (scope === "global") {
        const globals = getGlobalStore();
        return Number(globals[varName] ?? 0);
    } else {
        const store = getLocalStore(folder, player);
        return Number(store[varName] ?? 0);
    }
}

/* ------------------------------
   code:0007 [Text Parsing (Replace Variables)]
   ------------------------------ */
function replaceReservedVars(text, ev, actor) {
    let t = String(text);
    const player = actor instanceof Player ? actor : null;

    let victim = null;
    let attacker = null;
    let victimHealth = 0;
    let victimMaxHealth = 20;
    let attackerHealth = 0;
    let attackerMaxHealth = 20;
    let victimCoords = { x: "<none>", y: "<none>", z: "<none>" };
    let attackerCoords = { x: "<none>", y: "<none>", z: "<none>" };

    if (ev.deadEntity || ev.hurtEntity) {
        victim = ev.deadEntity || ev.hurtEntity;
        if (victim) {
            try {
                const healthComp = victim.getComponent("minecraft:health");
                if (healthComp) {
                    victimHealth = Math.round(healthComp.currentValue ?? 0);
                    victimMaxHealth = Math.round(healthComp.effectiveMax ?? 20);
                }
            } catch { }
            const loc = victim.location;
            victimCoords = { x: Math.floor(loc.x), y: Math.floor(loc.y), z: Math.floor(loc.z) };
        }

        const dmgSrc = ev.damageSource;
        if (dmgSrc?.damagingEntity) {
            attacker = dmgSrc.damagingEntity;
            if (attacker instanceof Player || attacker?.getComponent?.("minecraft:health")) {
                try {
                    const healthComp = attacker.getComponent("minecraft:health");
                    if (healthComp) {
                        attackerHealth = Math.round(healthComp.currentValue ?? 0);
                        attackerMaxHealth = Math.round(healthComp.effectiveMax ?? 20);
                    }
                } catch { }
                const loc = attacker.location;
                attackerCoords = { x: Math.floor(loc.x), y: Math.floor(loc.y), z: Math.floor(loc.z) };
            }
        }
    }
    if (ev.hitBlock !== undefined) {
        const block = ev.hitBlock;

        const entityId = ev.entityId || "<unknown>";
        const entityName = entityId.split(":").pop();

        const blockId = block?.typeId || "<none>";
        const blockName = blockId.split(":").pop();
        const loc = block.location;

        t = t.replaceAll("{entity}", entityName);
        t = t.replaceAll("{entity_name}", entityName);
        t = t.replaceAll("{raw entity}", entityId);

        t = t.replaceAll("{block}", blockName);
        t = t.replaceAll("{block_name}", blockName);
        t = t.replaceAll("{raw block}", blockId);

        t = t.replaceAll("{x}", Math.floor(loc.x));
        t = t.replaceAll("{y}", Math.floor(loc.y));
        t = t.replaceAll("{z}", Math.floor(loc.z));
    }
    if (ev.entity) {
        const entity = ev.entity;

        if (!entity.isValid) {
            t = t.replaceAll("{entity}", "<removed>");
            t = t.replaceAll("{entity_name}", "<removed>");
            t = t.replaceAll("{raw entity}", "<removed>");
            t = t.replaceAll("{x}", "<none>");
            t = t.replaceAll("{y}", "<none>");
            t = t.replaceAll("{z}", "<none>");
        } else {
            let entityId = "<unknown>";
            let entityName = "<unknown>";
            let loc = { x: 0, y: 0, z: 0 };

            try {
                entityId = entity.typeId || "<unknown>";
                entityName = entity.nameTag || entityId.split(":").pop();
                loc = entity.location;
            } catch (e) {
                entityId = "<error>";
                entityName = "<error>";
            }

            t = t.replaceAll("{entity}", entityName);
            t = t.replaceAll("{entity_name}", entityName);
            t = t.replaceAll("{raw entity}", entityId);
            t = t.replaceAll("{x}", Math.floor(loc.x));
            t = t.replaceAll("{y}", Math.floor(loc.y));
            t = t.replaceAll("{z}", Math.floor(loc.z));
        }
    }
    if (ev.entity || ev.removedEntity) {
        const entity = ev.entity || ev.removedEntity;

        let entityId = "<removed>";
        let entityName = "<removed>";

        if (entity && entity.isValid && entity.isValid) {
            try {
                entityId = entity.typeId || "<unknown>";
                entityName = entity.typeId.split(":").pop();
            } catch (e) {
                entityId = "<error>";
                entityName = "<error>";
            }
        } else {
            if (ev.entityId) {
                entityId = ev.entityId;
                entityName = ev.entityId.split(":").pop();
            } else {
                entityId = "<removed>";
                entityName = "<removed>";
            }
        }

        t = t.replaceAll("{entity}", entityName);
        t = t.replaceAll("{entity_name}", entityName);
        t = t.replaceAll("{raw entity}", entityId);
    }
    if (ev.target !== undefined || ev.interactedEntity !== undefined) {
        const player = ev.player || actor;
        const entity = ev.target || ev.interactedEntity;

        let entityName = "<unknown>";
        let entityId = "<unknown>";

        if (entity && entity.isValid && entity.isValid) {
            try {
                entityId = entity.typeId || "<unknown>";
                entityName = entity.nameTag || entityId.split(":").pop();
            } catch { /* ignore */ }
        } else if (ev.entityId) {
            entityId = ev.entityId;
            entityName = ev.entityId.split(":").pop();
        }

        t = t.replaceAll("{player}", player?.name || "<unknown>");
        t = t.replaceAll("{entity}", entityName);
        t = t.replaceAll("{entity_name}", entityName);
        t = t.replaceAll("{raw entity}", entityId);
    }

    const item = ev.itemStack;
    const block = ev.interactedBlock || ev.block || ev.placedBlock || null;
    const entity = ev.deadEntity || ev.hurtEntity;

    const itemId = item?.typeId ?? "<none>";
    const blockId = ev.block?.typeId ?? "<none>";
    const placedId = ev.block?.typeId ?? "<none>";
    const deadEntityId = ev.deadEntity?.typeId ?? ev.hurtEntity?.typeId ?? "<none>";
    const damageCause = ev.damageSource?.cause ?? "<none>";
    const loc = ev.blockLocation || block?.location || ev.location || null;
    const x = loc?.x ?? "<none>";
    const y = loc?.y ?? "<none>";
    const z = loc?.z ?? "<none>";
    let killer = "<none>";
    if (ev.damageSource?.damagingEntity) {
        if (ev.damageSource.damagingEntity instanceof Player) {
            killer = ev.damageSource.damagingEntity.name ?? "<none>";
        } else {
            killer = ev.damageSource.damagingEntity.typeId ?? "<none>";
        }
    }
    const damage = ev.damage ?? "<none>";

    const itemName = item ? (item.nameTag || item.typeId?.split(":")[1] || "<none>") : "<none>";
    const itemLore = item?.getLore()?.join(" ") || "<none>";
    const blockName = block ? (block.permutation.getState("minecraft:display_name") || block.typeId.split(":")[1] || "<none>") : "<none>";
    const entityName = entity?.nameTag || "<none>";

    const itemRaw = item?.typeId ?? "<none>";
    const blockRaw = block?.typeId ?? "<none>";
    const victimRaw = entity?.typeId ?? "<none>";
    const attackerRaw = ev.damageSource?.damagingEntity?.typeId ?? "<none>";

    const replacements = {
        "{player}": player?.name ?? "<none>",
        "{item}": itemId,
        "{broken block}": blockId,
        "{placed block}": placedId,
        "{dead entity}": deadEntityId,
        "{damage cause}": damageCause,
        "{killer}": killer,
        "{x}": String(x),
        "{y}": String(y),
        "{z}": String(z),
        "{damage}": String(damage),
        "{item_name}": itemName,
        "{item_lore}": itemLore,
        "{block_name}": blockName,
        "{interacted block}": blockId,
        "{entity_name}": entityName ?? victim?.nameTag ?? victim?.typeId ?? victimRaw ?? "<none>",
        "{raw item}": itemRaw,
        "{raw block}": blockRaw,
        "{raw victim}": victimRaw,
        "{raw attacker}": attackerRaw,
        "{victim}": victim?.nameTag ?? victim?.typeId ?? victimRaw ?? "<none>",
        "{victim health}": String(victimHealth ?? "<none>"),
        "{victim maxhealth}": String(victimMaxHealth ?? "<none>"),
        "{victim coords}": `${victimCoords.x} ${victimCoords.y} ${victimCoords.z}`,
        "{attacker}": attacker?.nameTag ?? attacker?.typeId ?? "<none>",
        "{attacker health}": String(attackerHealth ?? "<none>"),
        "{attacker maxhealth}": String(attackerMaxHealth ?? "<none>"),
        "{attacker coords}": `${attackerCoords.x} ${attackerCoords.y} ${attackerCoords.z}`,
    };

    for (const [token, value] of Object.entries(replacements)) {
        t = t.replaceAll(token, value);
    }

    try {
        const pHealth = player?.getComponent("minecraft:health");
        t = t.replaceAll("{player health}", pHealth ? Math.round(pHealth.currentValue) : "<none>");
        t = t.replaceAll("{player maxhealth}", pHealth ? Math.round(pHealth.effectiveMax ?? 20) : "<none>");
    } catch {
        t = t.replaceAll("{player health}", "<none>").replaceAll("{player maxhealth}", "<none>");
    }

    return t;
}
function replaceCustomVars(text, folder, actor) {
    let t = String(text);
    const globals = getGlobalStore();
    const locals = actor instanceof Player ? getLocalStore(folder, actor) : {};
    const randomVars = folder.randomVars || {};
    const matches = t.match(/\{([^}]+)\}/g) || [];
    matches.forEach(m => {
        let key = m.slice(1, -1).trim();
        key = replaceCustomVars(key, folder, actor);
        let val;
        if (randomVars[key]) {
            let def = randomVars[key];
            def = replaceCustomVars(def, folder, actor);
            val = parseRandom(def);
        } else if (/^[a-zA-Z0-9_]+$/.test(key)) {
            val = (locals[key] ?? globals[key] ?? 0).toString();
        } else {
            val = parseRandom(key);
        }
        t = t.replaceAll(m, val);
    });
    return t;
}
function parseRandom(s) {
    s = s.trim();
    if (s.includes('%')) {
        const parts = s.split(',').map(p => p.trim());
        let items = [];
        for (let i = 0; i < parts.length; i += 2) {
            const val = parts[i];
            const wStr = parts[i + 1]?.replace('%', '') ?? '';
            const weight = parseFloat(wStr);
            if (!isNaN(weight) && weight > 0) {
                items.push({ val, weight });
            }
        }
        let total = items.reduce((a, b) => a + b.weight, 0);
        if (total === 0) return '';
        let r = Math.random() * total;
        for (let it of items) {
            if (r < it.weight) return it.val;
            r -= it.weight;
        }
        return '';
    } else if (s.includes('-')) {
        const [minS, maxS] = s.split('-').map(p => p.trim());
        const min = parseInt(minS, 10);
        const max = parseInt(maxS, 10);
        if (!isNaN(min) && !isNaN(max) && min <= max) {
            return (Math.floor(Math.random() * (max - min + 1)) + min).toString();
        }
        return '';
    } else {
        const parts = s.split(',').map(p => p.trim()).filter(Boolean);
        if (parts.length === 0) return '';
        const idx = Math.floor(Math.random() * parts.length);
        return parts[idx];
    }
}
function parseVars(text, ev, folder, actor) {
    const r = replaceReservedVars(text, ev, actor);
    return replaceCustomVars(r, folder, actor);
}

/* ------------------------------
   code:0008 [Expression Evaluation]
   ------------------------------ */
function evaluateExpression(exprRaw, folder, actor, ev) {
    let expr = String(exprRaw);
    const replaced = parseVars(expr, ev, folder, actor).trim();
    if (replaced === "") return true;
    if (replaced.includes(" contains ")) {
        const [left, right] = replaced.split(" contains ");
        const leftVal = left.trim();
        const rightVal = right.trim().slice(1, -1);
        return leftVal.toLowerCase().includes(rightVal.toLowerCase());
    }
    const operatorMatch = replaced.match(/(<=|>=|==|!=|<|>)/);
    if (!operatorMatch) {
        const asNum = Number(replaced);
        if (!Number.isNaN(asNum)) return asNum !== 0;
        return replaced.length > 0;
    }
    const op = operatorMatch[0];
    const parts = replaced.split(op).map(s => s.trim());
    if (parts.length !== 2) return false;
    const leftRaw = parts[0];
    const rightRaw = parts[1].replace(/^[\"\']|[\"\']$/g, "");
    const leftNum = Number(leftRaw);
    const rightNum = Number(rightRaw);
    const bothNumeric = !Number.isNaN(leftNum) && !Number.isNaN(rightNum);
    if (bothNumeric) {
        switch (op) {
            case "<": return leftNum < rightNum;
            case "<=": return leftNum <= rightNum;
            case ">": return leftNum > rightNum;
            case ">=": return leftNum >= rightNum;
            case "==": return leftNum === rightNum;
            case "!=": return leftNum !== rightNum;
        }
    } else {
        if (op === "==") return leftRaw === rightRaw;
        if (op === "!=") return leftRaw !== rightRaw;
    }
    return false;
}

/* ------------------------------
   code:0009 [Variable Conditions Check & Action]
   ------------------------------ */
function checkVarConditionsAndAct(folder, fn, ev, actor) {
    if (!fn.varConditions || !fn.varConditions.length) return { ok: true, cancelEvent: false };
    let shouldCancel = false;
    for (const vc of fn.varConditions) {
        const ok = evaluateExpression(vc.expr, folder, actor, ev);
        if (vc.mode === "require") {
            if (!ok) {
                return { ok: false, cancelEvent: false };
            }
        } else if (vc.mode === "cancel") {
            if (ok) {
                shouldCancel = true;
            }
        }
    }
    if (shouldCancel) {
        try { ev.cancel = true; } catch (e) { }
    }
    return { ok: true, cancelEvent: shouldCancel };
}

/* ------------------------------
   code:0010 [Built-in Conditions Check]
   ------------------------------ */
function checkConditions(actor, conditions, ev = null) {
    const conds = conditions || [];
    for (const c of conds) {
        if (["isSneaking", "isSprinting", "isSwimming", "isFlying", "isGliding", "isJumping", "isFalling", "isOnGround", "isInWater", "isSleeping", "isClimbing", "isEmoting"].includes(c)) {
            if (!(actor instanceof Player)) return false;
            if (c === "isSneaking" && !actor.isSneaking) return false;
            if (c === "isSprinting" && !actor.isSprinting) return false;
            if (c === "isSwimming" && !actor.isSwimming) return false;
            if (c === "isFlying" && !actor.isFlying) return false;
            if (c === "isGliding" && !actor.isGliding) return false;
            if (c === "isJumping" && !actor.isJumping) return false;
            if (c === "isFalling" && !actor.isFalling) return false;
            if (c === "isOnGround" && !actor.isOnGround) return false;
            if (c === "isInWater" && !actor.isInWater) return false;
            if (c === "isSleeping" && !actor.isSleeping) return false;
            if (c === "isClimbing" && !actor.isClimbing) return false;
            if (c === "isEmoting" && !actor.isEmoting) return false;
        }
        if (c.startsWith("HandItem ")) {
            if (!(actor instanceof Player)) return false;
            const expected = c.slice("HandItem ".length).toLowerCase();
            const eq = actor.getComponent("minecraft:equipment_inventory");
            if (!eq) return false;
            const handItem = eq.getEquipment("mainhand");
            if (!handItem) return false;
            const id = handItem.typeId.toLowerCase().replace("minecraft:", "");
            if (id === expected) return true;
            const name = (handItem.nameTag ?? "").toLowerCase();
            const lore = (handItem.getLore()?.join(" ").toLowerCase()) ?? "";
            if (name.includes(expected) || lore.includes(expected)) return true;
            return false;
        }
        if (ev && ev.deadEntity) {
            const src = ev.damageSource;
            if (c === "KilledByPlayer") {
                if (!(src?.damagingEntity instanceof Player)) return false;
            }
            if (c === "KilledByEntity") {
                if (!src?.damagingEntity || src.damagingEntity instanceof Player) return false;
            }
            if (c === "KilledByEnvironment") {
                if (src?.damagingEntity) return false;
            }
            if (c === "CauseLava" && src?.cause !== "lava") return false;
            if (c === "CauseFall" && src?.cause !== "fall") return false;
            if (c === "CauseFire" && src?.cause !== "fire") return false;
            if (c === "CauseExplosion" && src?.cause !== "explosion") return false;
        }
        if (ev && ev.hurtEntity) {
            const src = ev.damageSource;
            if (c === "HurtByPlayer") {
                if (!(src?.damagingEntity instanceof Player)) return false;
            }
            if (c === "HurtByEntity") {
                if (!src?.damagingEntity || src.damagingEntity instanceof Player) return false;
            }
            if (c === "HurtByEnvironment") {
                if (src?.damagingEntity) return false;
            }
            if (c === "CauseLava" && src?.cause !== "lava") return false;
            if (c === "CauseFall" && src?.cause !== "fall") return false;
            if (c === "CauseFire" && src?.cause !== "fire") return false;
            if (c === "CauseExplosion" && src?.cause !== "explosion") return false;
        }
        if (c.startsWith("DistanceToPoint ")) {
            if (!(actor instanceof Player)) return false;
            const parts = c.slice("DistanceToPoint ".length).split(" ").map(s => s.trim());
            if (parts.length !== 4) return false;
            const [maxDistStr, xStr, yStr, zStr] = parts;
            const maxDist = Number(maxDistStr);
            const x = Number(xStr);
            const y = Number(yStr);
            const z = Number(zStr);
            if (isNaN(maxDist) || maxDist <= 0 || isNaN(x) || isNaN(y) || isNaN(z)) return false;
            const dist = Math.sqrt(
                Math.pow(actor.location.x - x, 2) +
                Math.pow(actor.location.y - y, 2) +
                Math.pow(actor.location.z - z, 2)
            );
            if (dist >= maxDist) return false;
        }
        if (c.startsWith("DistanceToPlayer ")) {
            if (!(actor instanceof Player)) return false;
            const parts = c.slice("DistanceToPlayer ".length).split(" ");
            if (parts.length < 2) return false;
            const maxDist = Number(parts[0]);
            const playerName = parts.slice(1).join(" ");
            if (isNaN(maxDist) || maxDist <= 0 || !playerName) return false;
            const target = world.getPlayers().find(p => p.name === playerName || p.nameTag === playerName);
            if (!target) return false;
            const dist = Math.hypot(
                actor.location.x - target.location.x,
                actor.location.y - target.location.y,
                actor.location.z - target.location.z
            );
            if (dist >= maxDist) return false;
        }
        if (c.startsWith("region ")) {
            const regionName = c.slice("region ".length).trim();
            const region = getRegion(regionName);
            if (!region) return true;

            let posToCheck = null;

            if (ev?.block?.location) {
                posToCheck = ev.block.location;
            }
            else if (ev?.hurtEntity?.location) {
                posToCheck = ev.hurtEntity.location;
            }
            else if (ev?.deadEntity?.location) {
                posToCheck = ev.deadEntity.location;
            }

            if (!posToCheck) return true;

            const { x, y, z } = posToCheck;
            const minX = Math.min(region.pos1.x, region.pos2.x);
            const maxX = Math.max(region.pos1.x, region.pos2.x);
            const minY = Math.min(region.pos1.y, region.pos2.y);
            const maxY = Math.max(region.pos1.y, region.pos2.y);
            const minZ = Math.min(region.pos1.z, region.pos2.z);
            const maxZ = Math.max(region.pos1.z, region.pos2.z);

            return x >= minX && x <= maxX && y >= minY && y <= maxY && z >= minZ && z <= maxZ;
        }
        if (c.startsWith("atLocation ")) {
            const coordsStr = c.slice("atLocation ".length).trim();
            const parts = coordsStr.split(" ").map(n => parseInt(n));
            if (parts.length !== 3 || parts.some(n => isNaN(n))) return true;

            const [targetX, targetY, targetZ] = parts;

            // Priority order: custom location → interacted block → other block → player position
            let loc = null;
            if (ev?.blockLocation) {
                loc = ev.blockLocation;
            } else if (ev?.interactedBlock?.location) {
                loc = ev.interactedBlock.location;
            } else if (ev?.block?.location) {
                loc = ev.block.location;
            } else if (actor?.location) {
                loc = actor.location;
            }

            if (!loc) return false; // no location = condition fails (safe)

            const x = Math.floor(loc.x);
            const y = Math.floor(loc.y);
            const z = Math.floor(loc.z);

            return x === targetX && y === targetY && z === targetZ;
        }
    }
    return true;
}

/* ------------------------------
   code:0011 [Input Matching]
   ------------------------------ */
function matchInputs(hookId, fn, ev) {
    function matchesConfig(targetTypeId, targetNames, configs) {
        for (const cfg of configs) {
            if (cfg === "any") return true;
            const andParts = cfg.split("+").map(s => s.trim()).filter(Boolean);
            let allMatch = true;
            for (const part of andParts) {
                const cleanPart = part.startsWith("minecraft:") ? part : "minecraft:" + part;
                let matched = false;
                if (targetTypeId === cleanPart) matched = true;
                if (targetNames.some(n => n.toLowerCase().includes(part.toLowerCase()))) matched = true;
                if (!matched) {
                    allMatch = false;
                    break;
                }
            }
            if (allMatch) return true;
        }
        return false;
    }
    if (hookId === "itemUse") {
        const item = ev.itemStack;
        if (!item) return true;
        const configRaw = (fn.inputs?.items ?? "").trim();
        if (!configRaw || configRaw === "any") return true;
        const configs = configRaw.split(",").map(s => s.trim()).filter(Boolean);
        const lore = item.getLore() ?? [];
        const targetNames = [item.nameTag || "", ...lore];
        return matchesConfig(item.typeId, targetNames, configs);
    }
    if (hookId === "onChat") {
        const message = ev.message ?? "";
        const configRaw = (fn.inputs?.message ?? "").trim();
        if (!configRaw || configRaw === "any") return true;
        const configs = configRaw.split(",").map(s => s.trim().toLowerCase()).filter(Boolean);
        for (const cfg of configs) {
            const andParts = cfg.split("+").map(s => s.trim()).filter(Boolean);
            if (andParts.every(part => message.toLowerCase().includes(part))) return true;
        }
        return false;
    }
    if (hookId === "playerBreakBlock" || hookId === "playerPlaceBlock") {
        const block = ev.block;
        if (!block) return true;
        const configRaw = (fn.inputs?.blocks ?? "").trim();
        if (!configRaw || configRaw === "any") return true;
        const configs = configRaw.split(",").map(s => s.trim()).filter(Boolean);
        const state = block.permutation.getState("minecraft:display_name");
        const targetNames = [state ? String(state) : ""];
        return matchesConfig(block.typeId, targetNames, configs);
    }
    if (hookId === "entityDie" || hookId === "entityHurt") {
        const entity = ev.deadEntity || ev.hurtEntity;
        if (!entity) return true;
        let typeId = "";
        let nameTag = "";
        try {
            typeId = entity?.typeId ?? "";
            nameTag = entity?.nameTag ?? "";
        } catch (e) {
            typeId = "";
            nameTag = "";
        }
        const configRaw = (fn.inputs?.entities ?? "").trim();
        if (!configRaw || configRaw === "any") return true;
        const configs = configRaw.split(",").map(s => s.trim()).filter(Boolean);
        return matchesConfig(typeId, [nameTag], configs);
    }
    if (hookId === "onHold") {
        const item = ev.itemStack;
        if (!item) return false;
        const configRaw = (fn.inputs?.items ?? "").trim().toLowerCase();
        if (!configRaw || configRaw === "any") return true;

        const configs = configRaw.split(",").map(s => s.trim()).filter(Boolean);
        const lore = item.getLore()?.map(l => l.toLowerCase()) || [];
        const nameTag = (item.nameTag || "").toLowerCase();
        const typeId = item.typeId.toLowerCase();

        return configs.some(cfg => {
            const andParts = cfg.split("+").map(p => p.trim());
            return andParts.every(part => {
                if (typeId.includes(part)) return true;
                if (nameTag.includes(part)) return true;
                if (lore.some(l => l.includes(part))) return true;
                return false;
            });
        });
    }
    return true;
}

/* ------------------------------
   code:0012 [Action Execution (Commands, Messages, Variables) with Error Logging]
   ------------------------------ */
const recentErrors = [];
function logError(err, context) {
    const entry = {
        message: err?.message ?? String(err),
        command: context?.command ?? "unknown",
        time: Date.now()
    };
    recentErrors.unshift(entry);
    if (recentErrors.length > 20) recentErrors.pop();
}
function runMessages(messages, ev, folder, actor) {
    (messages || []).forEach(m => {
        const text = (m.parts || [m]).join(""); // compat old
        const parsed = parseVars(text, ev, folder, actor);
        if (actor instanceof Player) {
            actor.sendMessage(parsed);
        } else {
            for (const p of world.getPlayers()) p.sendMessage(parsed);
        }
    });
}

function runCommands(commands, ev, folder, actor) {
    const dimension = actor?.dimension ?? world.getDimension("overworld");
    (commands || []).forEach(c => {
        const text = (c.parts || [c.command || ""]).join(""); // compat old
        const parsed = parseVars(text, ev, folder, actor);
        try {
            if (c.mode === "player") {
                if (actor instanceof Player) {
                    system.run(() => {
                        try { actor.runCommand(parsed); }
                        catch (err) { logError(err, c); }
                    });
                } else {
                    system.run(() => {
                        try { dimension.runCommand(parsed); }
                        catch (err) { logError(err, c); }
                    });
                }
            } else {
                system.run(() => {
                    try { dimension.runCommand(parsed); }
                    catch (err) { logError(err, c); }
                });
            }
        } catch (err) {
            logError(err, c);
        }
    });
}
function applyVariableActions(folder, fn, actor) {
    (fn.variableActions || []).forEach(act => {
        if (act.scope === "global") {
            const current = getVarValue("global", folder, actor, act.name);
            if (act.mode === "add") setGlobalValue(act.name, current + Number(act.value ?? 0));
            else if (act.mode === "remove") setGlobalValue(act.name, current - Number(act.value ?? 0));
            else if (act.mode === "set") setGlobalValue(act.name, Number(act.value ?? 0));
            else if (act.mode === "reset") setGlobalValue(act.name, 0);
        } else {
            if (!(actor instanceof Player)) return;
            const current = getVarValue("local", folder, actor, act.name);
            if (act.mode === "add") setLocalValue(folder, actor, act.name, current + Number(act.value ?? 0));
            else if (act.mode === "remove") setLocalValue(folder, actor, act.name, current - Number(act.value ?? 0));
            else if (act.mode === "set") setLocalValue(folder, actor, act.name, Number(act.value ?? 0));
            else if (act.mode === "reset") setLocalValue(folder, actor, act.name, 0);
        }
    });
}

/* ------------------------------
   code:0013 [Custom UI Handling]
   ------------------------------ */
function handleUIAction(action, folder, fn, actor, ev) {
    if (!action || !action.type) return;
    if (action.type === "command") {
        const parsed = parseVars(action.value ?? "", ev, folder, actor);
        if (actor instanceof Player) system.run(() => actor.runCommand(parsed));
        else system.run(() => world.getDimension("overworld").runCommand(parsed));
    } else if (action.type === "message") {
        const parsed = parseVars(action.value ?? "", ev, folder, actor);
        if (actor instanceof Player) actor.sendMessage(parsed);
        else for (const p of world.getPlayers()) p.sendMessage(parsed);
    } else if (action.type === "setvar") {
        const val = (action.value ?? "").split(":").map(s => s.trim());
        if (val.length >= 3) {
            const scope = val[0];
            const name = val[1];
            const v = Number(val.slice(2).join(":"));
            if (scope === "global") setGlobalValue(name, v);
            else if (scope === "local" && actor instanceof Player) setLocalValue(folder, actor, name, v);
        }
    } else if (action.type === "cancel") {
        try { ev.cancel = true; } catch (e) { }
    }
}
function runUIsForFunction(fn, folder, actor, ev) {
    if (!actor || !(actor instanceof Player)) return;
    if (!fn.uis || !fn.uis.length) return;
    const runNextUI = (index) => {
        if (index >= fn.uis.length) return;
        const ui = fn.uis[index];
        if (ui.type === "action") {
            const rawBody = ui.bodyLines ? ui.bodyLines.join("\n") : (ui.body ?? "");
            const parsedBody = parseVars(rawBody, ev, folder, actor);
            const form = new ActionFormData()
                .title(ui.title ?? "Menu")
                .body(parsedBody);
            (ui.buttons || []).forEach(b => form.button(b.label ?? "button"));
            system.run(() => {
                form.show(actor).then(res => {
                    if (res.canceled) return;
                    const btn = (ui.buttons || [])[res.selection];
                    if (!btn) return;
                    handleUIAction(btn.action || { type: "none" }, folder, fn, actor, ev);
                    runNextUI(index + 1);
                });
            });
        } else if (ui.type === "modal") {
            const modal = new ModalFormData().title(ui.title ?? "Input");
            (ui.modalFields || []).forEach(f =>
                modal.textField(f.label ?? "Field", f.placeholder ?? "")
            );
            system.run(() => {
                modal.show(actor).then(res => {
                    if (res.canceled) return;
                    (ui.modalFields || []).forEach((f, i) => {
                        const val = String(res.formValues[i] ?? "");
                        if (f.onSubmitAction) {
                            const actionCopy = JSON.parse(JSON.stringify(f.onSubmitAction));
                            actionCopy.value = (actionCopy.value ?? "")
                                .replaceAll("{value}", val)
                                .replaceAll("{field" + i + "}", val);
                            handleUIAction(actionCopy, folder, fn, actor, ev);
                        }
                    });
                    runNextUI(index + 1);
                });
            });
        } else {
            runNextUI(index + 1);
        }
    };
    runNextUI(0);
}
function parseUIActionString(raw) {
    const r = (raw ?? "").trim();
    if (r.length === 0) return { type: "none", value: "" };
    if (r.toLowerCase() === "cancel") return { type: "cancel", value: "" };
    const idx = r.indexOf(":");
    if (idx === -1) {
        return { type: "command", value: r };
    }
    const t = r.slice(0, idx).toLowerCase();
    const v = r.slice(idx + 1);
    if (t === "command") return { type: "command", value: v };
    if (t === "message") return { type: "message", value: v };
    if (t === "setvar") return { type: "setvar", value: v };
    return { type: "command", value: r };
}

/* ------------------------------
   code:0014 [Execution Pipeline]
   ------------------------------ */
function eventMatchesPhase(fn, phase) {
    if (!fn.event) return phase === null || phase === undefined;
    return fn.event === phase;
}
function executeHook(hookId, ev, phase = null) {
    const actor = ev.sender instanceof Player ? ev.sender :
        ev.source instanceof Player ? ev.source :
            ev.player instanceof Player ? ev.player :
                ev.damageSource?.damagingEntity instanceof Player ? ev.damageSource.damagingEntity :
                    null;
    const functions = allFunctions().filter(x => x.fn.hook === hookId && eventMatchesPhase(x.fn, phase));
    functions.forEach(({ folder, fn }) => {
        if (!matchInputs(hookId, fn, ev)) return;
        if (!checkConditions(actor, fn.conditions, ev)) return;
        const varCheck = checkVarConditionsAndAct(folder, fn, ev, actor);
        if (!varCheck.ok) return;
        applyVariableActions(folder, fn, actor);
        if (fn.uis && fn.uis.length > 0) {
            runUIsForFunction(fn, folder, actor, ev);
        } else {
            runMessages(fn.messages, ev, folder, actor);
            runCommands(fn.commands, ev, folder, actor);
        }
    });
}

/* ------------------------------
   code:0015 [World Event Subscriptions]
   ------------------------------ */
world.beforeEvents.itemUse.subscribe(ev => {
    executeHook("itemUse", ev, "before");
});
world.afterEvents.itemUse.subscribe(ev => {
    executeHook("itemUse", ev, "after");
});
world.beforeEvents.playerBreakBlock.subscribe(ev => {
    executeHook("playerBreakBlock", ev, "before");
});
world.afterEvents.playerBreakBlock.subscribe(ev => {
    executeHook("playerBreakBlock", ev, "after");
});
world.beforeEvents.playerPlaceBlock.subscribe(ev => {
    executeHook("playerPlaceBlock", ev, "before");
});
world.afterEvents.playerPlaceBlock.subscribe(ev => {
    executeHook("playerPlaceBlock", ev, "after");
});
world.afterEvents.entityDie.subscribe(ev => {
    executeHook("entityDie", ev, "after");
});
world.beforeEvents.chatSend.subscribe(ev => {
    executeHook("onChat", ev, "before");
});
world.afterEvents.chatSend.subscribe(ev => {
    executeHook("onChat", ev, "after");
});
world.afterEvents.entityHurt.subscribe(ev => {
    executeHook("entityHurt", ev, "after");
});
world.beforeEvents.playerInteractWithBlock.subscribe(ev => {
    if (ev.target) {
        ev.interactedBlock = ev.target;
        ev.blockLocation = ev.target.location;
    } else {
        ev.interactedBlock = null;
        ev.blockLocation = null;
    }
    executeHook("playerInteractWithBlock", ev, "before");
});

world.afterEvents.playerInteractWithBlock.subscribe(ev => {
    if (ev.target) {
        ev.interactedBlock = ev.target;
        ev.blockLocation = ev.target.location;
    } else {
        ev.interactedBlock = null;
        ev.blockLocation = null;
    }
    executeHook("playerInteractWithBlock", ev, "after");
});
world.afterEvents.itemStartUse.subscribe(ev => {
    executeHook("itemStartUse", ev, "after");
});

world.afterEvents.itemStopUse.subscribe(ev => {
    executeHook("itemStopUse", ev, "after");
});

world.afterEvents.itemStartUseOn.subscribe(ev => {
    executeHook("itemStartUseOn", ev, "after");
});

world.afterEvents.itemStopUseOn.subscribe(ev => {
    executeHook("itemStopUseOn", ev, "after");
});

world.afterEvents.itemCompleteUse.subscribe(ev => {
    executeHook("itemCompleteUse", ev, "after");
});

world.afterEvents.itemReleaseUse.subscribe(ev => {
    executeHook("itemReleaseUse", ev, "after");
});

world.afterEvents.entityHitBlock.subscribe(ev => {
    executeHook("entityHitBlock", ev, "after");
});

world.afterEvents.entitySpawn.subscribe(ev => {
    executeHook("entitySpawn", ev, "after");
});

world.afterEvents.entityRemove.subscribe(ev => {
    executeHook("entityRemove", ev, "after");
});
world.beforeEvents.entityRemove.subscribe(ev => {
    executeHook("entityRemove", ev, "before");
});

world.afterEvents.playerJoin.subscribe(ev => {
    executeHook("playerJoin", ev, "after");
});

world.afterEvents.playerLeave.subscribe(ev => {
    executeHook("playerLeave", ev, "after");
});

world.afterEvents.playerSpawn.subscribe(ev => {
    executeHook("playerSpawn", ev, "after");
});

world.beforeEvents.playerInteractWithEntity.subscribe(ev => {
    executeHook("playerInteractWithEntity", ev, "before");
});
world.afterEvents.playerInteractWithEntity.subscribe(ev => {
    executeHook("playerInteractWithEntity", ev, "after");
});
world.beforeEvents.playerBreakBlock.subscribe(ev => {
    const player = ev.player;
    if (!player.hasTag("sync")) return;

    const pos1 = player.getDynamicProperty("region_pos1");
    const pos2 = player.getDynamicProperty("region_pos2");

    if (pos1 === undefined || pos1 === null) {
        const loc = { x: ev.block.location.x, y: ev.block.location.y, z: ev.block.location.z };
        player.setDynamicProperty("region_pos1", loc);
        player.sendMessage(`§aPosition 1 set §7(${loc.x}, ${loc.y}, ${loc.z})`);
        system.run(() => {
            player.playSound("random.orb", { pitch: 0.8 });
        });
        startOutline(player);
        ev.cancel = true;
        return;
    }
});

world.beforeEvents.playerInteractWithBlock.subscribe(ev => {
    const player = ev.player;
    if (!player.hasTag("sync")) return;

    const pos1 = player.getDynamicProperty("region_pos1");
    const pos2 = player.getDynamicProperty("region_pos2");

    if (pos1 !== undefined && pos1 !== null && (pos2 === undefined || pos2 === null)) {
        const loc = { x: ev.block.location.x, y: ev.block.location.y, z: ev.block.location.z };
        player.setDynamicProperty("region_pos2", loc);
        player.sendMessage(`§aPosition 2 set §7(${loc.x}, ${loc.y}, ${loc.z})`);
        player.sendMessage(`§eSelection complete! Use §b/sync:region create <name> §eor §b/sync:region expand`);
        system.run(() => {
            player.playSound("random.levelup", { pitch: 1.2 });
        });
        startOutline(player);
        ev.cancel = true;
        return;
    }
});

/* ------------------------------
   code:0016 [Main Menu & Help Menus]
   ------------------------------ */
function openMainMenu(player) {
    const form = new ActionFormData()
        .title("§l§bSync Menu")
        .body("§bChoose an option:")
        .button(" §6§lFolders")
        .button(":switch_start: §eCreate Folder")
        .button(" §eDelete Folder")
        .button(":xbox_select: §eRename Folder")
        .button(":ps4_face_button_up: §4§lClose Menu");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openFolderList(player); break;
            case 1: openCreateFolder(player); break;
            case 2: openDeleteFolder(player); break;
            case 3: openRenameFolder(player); break;
            case 4: return;
        }
    });
}

function openHelpMainMenu(player) {
    const form = new ActionFormData()
        .title("§l§bSync Help Menu")
        .body("§bWelcome to Sync Help! This is a guide for beginners. Sync lets you create custom events and actions in Minecraft without writing code. Think of it like building automation rules.\n\nChoose a topic to learn more. Everything is explained step-by-step like you're new to coding.\n\n§eThis addon is fully developed and maintained by §d§lExterLeo\n\n§r§bDocs: §7https://leowascoding.github.io/sync/")
        .button("§3§lOverview of Sync")
        .button("§8Folders")
        .button("§8Functions (How to Create Them)")
        .button("§8Hooks (Triggers)")
        .button("§8Events (Before/After)")
        .button("§8Conditions (Player States & More)")
        .button("§8Messages")
        .button("§8Commands")
        .button("§8Variables (Counters & Storage)")
        .button("§8Custom UI (Interactive Forms)")
        .button("§8Variable Conditions (Smart Logic)")
        .button("§8Event Cancellation & Protection")
        .button("§8Inputs (Items, Blocks, Entities, Chat)")
        .button("§8Placeholders {Dynamic Text}")
        .button("§8Execution Order")
        .button("§8Global vs Local Variables")
        .button("§8Loops (Repeating Actions)")
        .button("§8Error Console (/console)")
        .button("§8/variable Command")
        .button("§8/varlist Command")
        .button("§8/broadcast Command")
        .button("§8/send Command")
        .button("§8/region - Protection Regions")
        .button("§8Random Variables")
        .button("§8Tips, Examples & Best Practices")
        .button("§4§lClose");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openHelpOverview(player); break;
            case 1: openHelpFolders(player); break;
            case 2: openHelpFunctions(player); break;
            case 3: openHelpHooks(player); break;
            case 4: openHelpEvents(player); break;
            case 5: openHelpConditions(player); break;
            case 6: openHelpMessages(player); break;
            case 7: openHelpCommands(player); break;
            case 8: openHelpVariables(player); break;
            case 9: openHelpCustomUI(player); break;
            case 10: openHelpVarConditions(player); break;
            case 11: openHelpCancellation(player); break;
            case 12: openHelpInputs(player); break;
            case 13: openHelpPlaceholders(player); break;
            case 14: openHelpExecution(player); break;
            case 15: openHelpGlobalsLocals(player); break;
            case 16: openHelpLoops(player); break;
            case 17: openHelpConsole(player); break;
            case 18: openHelpVariableCommands(player); break;
            case 19: openHelpVarlist(player); break;
            case 20: openHelpBroadcast(player); break;
            case 21: openHelpSend(player); break;
            case 22: openHelpRegions(player); break;
            case 23: openHelpRandomVariables(player); break;
            case 24: openHelpTipsExamples(player); break;
            case 25: return;
        }
    });
}

function openHelpVariableCommands(player) {
    const form = new ActionFormData()
        .title("§aVariable Commands")
        .body("§bWhat is /variable?\n" +
            "This command lets you directly modify variables stored in Sync. Variables are numeric values you can increase, decrease, set, or reset.\n\n" +
            "§eSyntax:\n" +
            "/variable <add|set|remove|reset> <local|global> <folder> <varname> [player] [amount]\n\n" +
            "§eExamples:\n" +
            "- /variable set global stats diamondbreak 100\n" +
            "- /variable add local stats diamondbreak Steve 1\n\n" +
            "§eHow It Works:\n" +
            "- §flocal scope: variable belongs to a specific player inside a folder.\n" +
            "- §fglobal scope: variable is shared across the whole world.\n" +
            "- §famount: defaults to 1 if not given (for add/remove).\n" +
            "- §freset: ignores amount and sets value to 0.\n\n" +
            "§eTips:\n" +
            "- Use local variables for per-player counters (kills, blocks broken).\n" +
            "- Use global variables for shared stats (total diamonds mined).\n" +
            "- Combine with variable conditions to build rules like 'if {kills} > 10 then give reward'.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpRandomVariables(player) {
    const form = new ActionFormData()
        .title("§eRandom Variables")
        .body("§bWhat are Random Variables?\nThey let you define dynamic random values using {myRandom} in messages, commands, or variable conditions.\n\n§eHow to Create:\nFolder Editor → Create Random Var → Name + Definition\n\n§6Supported Formats:\n• §f1-100 §7→ random number 1 to 100\n• §fstone,dirt,oak_log §7→ picks one\n• §f10%,50%,40% §7→ weighted chance (adds to 100%)\n• §fhello world, hi, hey §7→ random text\n\n§eExamples:\n{rarity} = common,70%,rare,20%,epic,9%,legendary,1%\n{damage} = 5-12\n{greeting} = Hello,Hi there,Hey!\n\n§eTip: Use in messages, commands, or var conditions for loot tables, random events, RPG systems!")
        .button("§aBack to Help Menu");
    form.show(player).then(r => { if (!r.canceled) openHelpMainMenu(player); });
}

function openHelpVarlist(player) {
    const form = new ActionFormData()
        .title("§aVarlist Command")
        .body("§bWhat is /varlist?\n" +
            "This command shows variables inside a folder, either global or local.\n\n" +
            "§eSyntax:\n" +
            "/varlist <local|global> <folder> [player]\n\n" +
            "§eExamples:\n" +
            "- /varlist global stats\n" +
            "- /varlist local stats Steve\n\n" +
            "§eHow It Works:\n" +
            "- §flocal scope: requires a player (executor or specified).\n" +
            "- §fglobal scope: shows shared variables for the folder.\n" +
            "- Output lists variable names and their current values.\n\n" +
            "§eTips:\n" +
            "- Use this to debug your variable changes.\n" +
            "- Run after /variable to confirm updates.\n" +
            "- Helpful for checking counters, scores, or storage values.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpBroadcast(player) {
    const form = new ActionFormData()
        .title("§aBroadcast Command")
        .body(
            "§bWhat is /broadcast?\n" +
            "This command sends a message to all players in the world.\n\n" +
            "§eSyntax:\n" +
            "/broadcast [message]\n\n" +
            "§eExample:\n" +
            "- /broadcast \"Welcome to the server!\"\n\n" +
            "§eTips:\n" +
            "- Use for announcements or global notifications.\n" +
            "- Type everything inside a \"\".\n" +
            "- All players online will see the message with a broadcast tag if configured."
        )
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpSend(player) {
    const form = new ActionFormData()
        .title("§aSend Command")
        .body(
            "§bWhat is /send?\n" +
            "This command sends a private message to a specific player.\n\n" +
            "§eSyntax:\n" +
            "/send <player> [message]\n\n" +
            "§eExample:\n" +
            "- /send Steve \"Hey, want to team up?\"\n\n" +
            "§eHow It Works:\n" +
            "- Only the target player sees the message.\n" +
            "- The sender gets confirmation that the message was delivered if implemented.\n\n" +
            "§eTips:\n" +
            "- Great for private whispers.\n" +
            "- Type everything inside a \"\".\n" +
            "- Make sure you type the exact player name."
        )
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpConsole(player) {
    const form = new ActionFormData()
        .title("§cSync Error Console Help")
        .body("§bWhat is the Error Console?\n" +
            "The Error Console is a Sync menu that shows recent errors that happened when running commands through Sync. Whenever a command fails (for example, because of a typo, missing target, or invalid syntax), Sync logs the error here.\n\n" +
            "§eWhy Use the Console?\n" +
            "- Quickly see what went wrong without guessing.\n" +
            "- Each entry shows the command, the time it failed, and the error message.\n" +
            "- Helps debug functions, hooks, and loops.\n\n" +
            "§eHow to Open It:\n" +
            "- Use /console (or open via the Help Menu /synchelp).\n\n" +
            "§eHow to Read It:\n" +
            "- Each button is one error entry.\n" +
            "- The label shows the time and command.\n" +
            "- Clicking shows the full error message in chat.\n\n" +
            "§eTips:\n" +
            "- If you see repeated errors, check your function or variable setup.\n" +
            "- Use the console often while testing new rules.\n" +
            "- Only the most recent errors are stored (configurable in code).\n\n" +
            "§dThink of the Error Console like a debugger for Sync — it helps you fix mistakes fast.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpOverview(player) {
    const form = new ActionFormData()
        .title("§6Sync Overview")
        .body("§bWhat is Sync?\nSync is a Bedrock addon that lets you create custom 'rules' or 'automations' in your world without writing code. Use menus to set up what happens when events occur, like using an item or breaking a block.\n\nKey Ideas:\n- §eFolders: Organize your rules.\n- §eFunctions: The rules themselves. Each function is triggered by a hook (event).\n- §eHooks: Triggers like 'Item Use', 'Entity Hurt' or 'Entity Die'.\n- §eConditions: Checks if something is true, like 'player is sneaking'.\n- §eVariable Conditions: Advanced checks using expressions like '{kills} > 5' or '{item_name} contains \"magic\"'.\n- §eActions: What happens — messages, commands, variable changes, or showing custom UIs.\n- §eVariables: Numeric storage used across functions and loops.\n- §eCustom UI: Forms with buttons or inputs to interact with players.\n- §eCancellation: Prevent events in 'before' hooks.\n- §eLoops: Periodic actions that run every N ticks.\n\nTo start: Use /menu to open the main menu, /synchelp for this help, or /loops for managing loops. Create a folder, then add functions or loops.\n\nSync saves data in world storage, and supports global and per-player (local) variables. Think of Sync as If-Then: IF (hook occurs & conditions pass) THEN (do actions).")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpFolders(player) {
    const form = new ActionFormData()
        .title("§eFolders Explained")
        .body("§bWhat are Folders?\nFolders are containers to organize functions, variables, and loops. For example, 'Farming' for crop rules and 'Combat' for mob rules.\n\nWhy Use Folders?\n- Keeps large setups tidy.\n- Local variables are stored per-folder and per-player.\n- Loops can be linked to folders for per-player behavior.\n- Toggle enabled/disabled to turn off all functions and variables inside.\n\nHow to Manage Folders:\n1. Open /menu.\n2. Choose 'Folders'.\n3. Create Folder: Enter a unique name.\n4. Delete Folder: Confirm (removes functions/variables inside!).\n5. Rename Folder: Enter new name.\n\nInside a Folder: Add functions, create/delete variables, reset storage, toggle enabled.\n\nTip: Start with a folder 'Test' to experiment before building complex systems.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpLoops(player) {
    const form = new ActionFormData()
        .title("§eLoops (Periodic Actions)")
        .body("§bWhat are Loops?\nLoops run repeatedly every N ticks (1 tick ≈ 0.05s, 20 ticks = 1s). Useful for timers, counters, or repeated effects.\nToggle enabled/disabled to pause loops.\n\nHow to Manage Loops:\n1. Use /loops for the Loops menu.\n2. Create Loop: name (unique), interval (ticks).\n3. Choose Folder: Optional; required for per-player scope.\n4. Choose Scope:\n - Global: Runs once per interval for the world.\n - Per-Player: Runs for each online player and uses folder locals.\n\nEditing a Loop: Rename, change interval, change scope/folder, edit messages/commands/variables, toggle enabled.\n\nNotes:\n- Loops do not support function conditions, UI, or cancellation — they simply run actions (vars, messages, commands).\n- Per-player loops allow commands to run as player.\n\nExamples:\n- Global day counter: interval 24000, variable add 1 to global 'days'.\n- Per-player playtime: folder 'Stats', interval 20, add 1 to local 'playtime'.\n\nTips: Avoid very small intervals (may lag). Test in a safe world.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpFunctions(player) {
    const form = new ActionFormData()
        .title("§eFunctions (How to Create Them)")
        .body("§bWhat is a Function?\nA function is a user-created rule: it listens to a hook, checks conditions, and performs actions (messages, commands, variable changes, UIs).\nToggle enabled/disabled to pause individual functions.\n\nCreate a Function:\n1. /menu > Folders > select/Create folder > Create Function.\n2. Name it (unique).\n3. Select Hook (trigger).\n4. Configure Inputs (items/blocks/entities).\n5. Select Event (before/after if available).\n6. Add built-in conditions and variable conditions.\n7. Add actions: messages, commands, var changes, or custom UI.\n\nEdit/Delete: From folder > function list.\n\nExample: 'HealOnApple' — hook: Item Use (apple), event: after, actions: message + give effect.\n\nTip: Treat functions like recipes: hook = trigger, conditions = filters, actions = outcome.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpHooks(player) {
    const form = new ActionFormData()
        .title("§eHooks (Triggers)")
        .body(
            "§bAvailable Hooks:\n\n" +

            "§6◆ Item & Interaction Hooks §r\n" +
            "• §eItem Use §7– right-click air with an item (§fbefore§7/§fafter§7)\n" +
            "• §eItem Start Use §7– begin charging bow, shield, etc. (§fafter§7 only)\n" +
            "• §eItem Stop Use §7– stop charging (§fafter§7 only)\n" +
            "• §eItem Start Use On §7– start using on block (bone meal, flint & steel) (§fafter§7 only)\n" +
            "• §eItem Stop Use On §7– stop using on block (§fafter§7 only)\n" +
            "• §eItem Complete Use §7– finish eating/drinking (§fafter§7 only)\n" +
            "• §eItem Release Use §7– release bow/trident after charge (§fafter§7 only)\n" +
            "• §eOn Hold §7– holding an item in hand every tick (§fafter§7 only)\n\n" +

            "§6◆ Block Hooks §r\n" +
            "• §ePlayer Break Block §7– break a block (§fbefore§7/§fafter§7)\n" +
            "• §ePlayer Place Block §7– place a block (§fbefore§7/§fafter§7)\n" +
            "• §ePlayer Interact With Block §7– right-click a block (§fbefore§7/§fafter§7)\n\n" +

            "§6◆ Entity Hooks §r\n" +
            "• §eEntity Die §7– any entity dies (§fafter§7 only)\n" +
            "• §eEntity Hurt §7– any entity takes damage (§fafter§7 only)\n" +
            "• §eEntity Spawn §7– entity appears in world (§fafter§7 only)\n" +
            "• §eEntity Remove §7– entity is deleted (§fbefore§7/§fafter§7)\n" +
            "• §eEntity Hit Block §7– arrow, snowball, egg, trident hits a block (§fafter§7 only)\n" +
            "• §ePlayer Interact With Entity §7– right-click villager, armor stand, etc. (§fbefore§7/§fafter§7)\n\n" +

            "§6◆ Player & Chat Hooks §r\n" +
            "• §eOn Chat §7– player sends a message (§fbefore§7/§fafter§7)\n" +
            "• §ePlayer Join §7– player joins the world (§fafter§7 only)\n" +
            "• §ePlayer Leave §7– player leaves the world (§fafter§7 only)\n" +
            "• §ePlayer Spawn §7– player spawns/resawns (§fafter§7 only)\n\n" +

            "§eSpecial Built-in Condition:\n" +
            "• §fregion <name> §7– true when the event happens inside a protected region\n" +
            "   created with §f/region§7.\n\n" +

            "§eTip: §fBefore§r = prevent/cancel actions • §fAfter§r = rewards, logging, effects"
        )
        .button("§aBack to Help Menu");

    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpEvents(player) {
    const form = new ActionFormData()
        .title("§eEvents (Before/After)")
        .body("§bWhat are Events?\nSome hooks have phases: 'before' (right before the action completes) and 'after' (after it completes).\n\nWhich Hooks Have Them?\n- Item Use: Before/After\n- Player Break Block: Before/After\n- Player Place Block: Before/After\n- On Chat: Before/After\n- Player Interact With Block: Before/After\n- Entity Hurt: After only (damage is already applied)\n- Entity Die: After only\n\nWhy Choose Before or After?\n- §eBefore: Run checks or cancel the action. Good for protection and prevention.\n- §eAfter: Run follow-up actions like rewards or logging.\n\nNewbie Note: If unsure, choose 'after' — it's safer.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpConditions(player) {
    const form = new ActionFormData()
        .title("§eConditions (Checks)")
        .body("§bBuilt-in Conditions (You can create your own too!):\n\n" +
            "§ePlayer States:\n" +
            "isSneaking • isSprinting • isSwimming • isFlying • isGliding • isJumping • isFalling • isOnGround • isInWater • isSleeping • isClimbing • isEmoting\n\n" +
            "§eDeath / Damage Causes:\n" +
            "KilledByPlayer • KilledByEntity • KilledByEnvironment\n" +
            "CauseLava • CauseFall • CauseFire • CauseExplosion\n" +
            "HurtByPlayer • HurtByEntity • HurtByEnvironment\n\n" +
            "§eSpecial:\n" +
            "• §fDistanceToPoint <max> <x> <y> <z> §7– player within max distance of point\n" +
            "• §fDistanceToPlayer <max> <name> §7– player within max distance of another player\n" +
            "• §fregion <name> §7– event happens inside a protected region\n" +
            "• §fAt Location <x> <y> <z> §7– event at exact coordinates\n\n" +
            "§eTip: Combine with Variable Conditions for insane flexibility!")
        .button("§aBack to Help Menu");
    form.show(player).then(res => { if (!res.canceled) openHelpMainMenu(player); });
}

function openHelpMessages(player) {
    const form = new ActionFormData()
        .title("§eMessages")
        .body("§bWhat are Messages?\nMessages are chat text sent to players when a function or loop runs. Use placeholders to include dynamic data.\n\nHow to Add/Edit:\n- Function/Loop editor > Messages > Add Message: build from multiple parts for unlimited length (no character limits).\n- Multiple messages send in order.\n\nExample: 'You broke a {broken block}! Score: {score}'\n\nTip: Use § color codes for styled text. Messages are useful for debugging and feedback.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpCommands(player) {
    const form = new ActionFormData()
        .title("§eCommands")
        .body("§bWhat are Commands?\nCommands are Minecraft commands run when a function or loop triggers. Enter them without a leading '/'.\n\nHow to Add/Edit:\n- Editor > Commands > Add Command: type the command (no leading /) and choose executor (console or player), build from multiple parts for long commands.\n- Console: runs as server. Player: runs as the triggering player (@s).\n- In loops, 'player' executor only works in per-player scope.\n\nExample: 'effect {player} speed 10 1'\n\nTip: Test commands in chat first. Use console for world changes, player for personal effects.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpVariables(player) {
    const form = new ActionFormData()
        .title("§eVariables (Numbers & Storage)")
        .body("§bWhat are Variables?\nVariables store numeric values. You can add, remove, set, or reset them. Use {varname} in messages/commands/conditions.\n\nTypes: Global (world-wide) vs Local (per-player, per-folder).\n\nHow to Create/Register: Folder editor > Create Variable (name, scope).\n\nIn Editors: Add/modify variable actions: add/remove/set/reset a numeric value.\n\nDelete/Reset: Delete variable or reset folder storage to 0.\n\nTip: Variables default to 0 when missing. In placeholders, Sync prefers a local variable if present, otherwise falls back to global.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpCustomUI(player) {
    const form = new ActionFormData()
        .title("§eCustom UI (Forms)")
        .body("§bWhat is Custom UI?\nCustom UIs are interactive forms shown instead of direct actions. They are not available in loops.\n\nTypes:\n- ActionForm: a buttons menu where each button can run actions (message, command, variable action, cancel, open another UI).\n- ModalForm: input fields; on submit you can run actions that use {value} placeholders.\n\nHow to Use: Function editor > Custom UI > Add UI > Configure buttons/fields and their actions.\n\nExample: ActionForm 'Shop' with buttons that run commands and modify variables.\n\nTip: UIs let players choose outcomes interactively; they replace direct actions for that function.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpVarConditions(player) {
    const form = new ActionFormData()
        .title("§eVariable Conditions")
        .body("§bWhat are Variable Conditions?\nVariable Conditions are expressions that evaluate to true/false and let you gate or cancel functions. They support numeric comparisons and string checks.\n\nSupported operators: <, <=, >, >=, ==, != and the string operator 'contains' (case-insensitive).\n\nExamples:\n- '{kills} > 5'\n- '10 == {level}'\n- '{item_name} contains \"magic\"'\n- '{flag}' (truthy if non-zero/non-empty)\n\nModes:\n- Require: expression must be true to continue.\n- Cancel: expression true will cancel the event (only valid in 'before' hooks).\n\nNotes: Use placeholders like {item_name}, {item_lore}, {block_name}, {entity_name}, and {player}, and any variables you created.\n\nTip: Combine variable conditions with built-in conditions for precise behavior.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpCancellation(player) {
    const form = new ActionFormData()
        .title("§eEvent Cancellation")
        .body("§bWhat is Event Cancellation?\nIn 'before' event phases you can prevent the default action (stop place/break/use). Cancellation can be done via UI actions ('cancel') or variable conditions in 'cancel' mode.\n\nWorks for: Item Use (before), Player Break (before), Player Place (before), On Chat (before).\nNot available for after-phase hooks, Entity Hurt (after) or Entity Die (after).\n\nExample: Before Place Block, var condition cancel if '{protected} == 1'.\n\nTip: Use cancellation to implement regions, permission checks, or item restrictions.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpInputs(player) {
    const form = new ActionFormData()
        .title("§eInputs (Items / Blocks / Entities / Chat)")
        .body("§bWhat Are Inputs?\n" +
            "Inputs filter exactly which item/block/entity/message triggers the function.\n\n" +
            "§eSyntax Rules (works the same for items, blocks, entities, chat):\n" +
            "• Comma (,) = OR (any one can match)\n" +
            "• Plus (+) = AND (ALL parts must match)\n" +
            "• You can use short ids (§fstone§7) or full (§fminecraft:stone§7)\n" +
            "• Item/Block/Entity names and lore are also checked!\n" +
            "• Use §fany §7to match everything\n\n" +
            "§6Examples:\n" +
            "• §fstone,dirt,oak_log §7→ any of these\n" +
            "• §fdiamond_sword + legendary §7→ must be diamond sword AND name/lore contains “legendary”\n" +
            "• §fexcalibur + sharp + epic, wand_of_healing §7→ either a sword that has all three words OR a healing wand\n" +
            "• §fzombie + boss, minecraft:skeleton §7→ boss-tagged zombie OR any skeleton\n" +
            "• §fhello+world, hi there §7→ chat must contain both “hello” and “world” OR the exact phrase “hi there”\n\n" +
            "§eTip: Start with §fany§7, test, then narrow it down. This is the most powerful filtering system in Sync!")
        .button("§aBack to Help Menu");
    form.show(player).then(res => { if (!res.canceled) openHelpMainMenu(player); });
}

function openHelpPlaceholders(player) {
    const form = new ActionFormData()
        .title("§ePlaceholders {Like This}")
        .body("§bWhat are Placeholders?\nUse {word} in messages/commands/expressions to insert runtime values.\n\nCommon placeholders (generated from events):\n- {player} — player name.\n- {player health}, {player maxhealth} — player's health values.\n- {item}, {item_name}, {item_lore}, {raw item} — item identifiers and display info.\n- {broken block}, {placed block}, {block_name}, {raw block} — block data.\n- {interacted block}, {block_name}, {raw block} — for interact hooks.\n- {dead entity}, {entity_name}, {raw victim} — victim info on death/hurt hooks.\n- {victim}, {victim health}, {victim maxhealth}, {victim coords} — victim details.\n- {attacker}, {raw attacker}, {attacker health}, {attacker maxhealth}, {attacker coords} — attacker details when available.\n- {damage cause}, {damage}, {killer} — damage info and killer.\n- {x}, {y}, {z} — event or block coordinates.\n- {message} — chat message content.\n\nCustom placeholders: {varname} for variables (local or global). In modal UI actions use {value} for user input.\n\nTip: Use placeholders to make messages and commands dynamic and context-aware.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpExecution(player) {
    const form = new ActionFormData()
        .title("§eExecution Flow (What Happens When)")
        .body("§bHow Does Sync Process an Event?\n1. Minecraft event fires (item use, block break, chat, etc.).\n2. Sync finds matching functions for the hook and selected event phase.\n3. Inputs are checked (IDs, names, lore, chat text).\n4. Built-in conditions run (player states, death causes).\n5. Variable conditions evaluated (require/cancel).\n6. If UIs exist, they are presented and their actions execute when players interact.\n7. Otherwise, variable changes, messages, and commands are applied in order.\n\nNotes:\n- Cancellation can happen during variable conditions (in 'before' hooks) or via UI actions.\n- Multiple functions can match and will run in sequence.\n- Loops execute independently on their interval (no conditions/UI/cancellation).")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpGlobalsLocals(player) {
    const form = new ActionFormData()
        .title("§eGlobals vs Locals")
        .body("§bGlobals:\n- Shared across the whole world, same value for everyone.\n- Good for totals (e.g., total zombies killed).\n- Stored in world data.\n\nLocals:\n- Per-player and per-folder. Each player has their own value for that variable inside a folder.\n- Good for personal scores (player kills, playtime).\n\nBehavior:\n- When a placeholder {varname} is expanded, Sync prefers a local value (if present) and falls back to the global value.\n\nExample: Global 'total_kills' increments for everyone; local 'my_kills' increments per player.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}

function openHelpRegions(player) {
    const form = new ActionFormData()
        .title("§aProtection Regions (/region)")
        .body("§bWhat are Regions?\nSync now supports simple cuboid protection regions. You can block placing/breaking blocks inside defined areas using the built-in 'region <name>' condition.\n\n§eHow to Create a Region:\n1. Run §f/region selection §e→ clears current selection\n2. Left-click (attempt to break) a block → sets Pos1\n3. Right-click (interact) a block → sets Pos2\n4. Optional: §f/region expand §e→ expands vertically from -64 to 319\n5. §f/region create <name> §e→ saves the region\n\n§eOther Commands:\n• §f/region list §e→ shows all regions\n• §f/region reveal <name> §e→ shows outline with particles\n• §f/region delete <name>\n\n§eHow to Use in Functions:\nAdd condition §fregion myspawn §ein any before-phase hook (break/place) to prevent actions inside that region.\n\n§eExample: Spawn Protection\nHook: Player Place Block (before)\nCondition: region spawn\n→ Player cannot place blocks in 'spawn' region.\n\n§eTip: Combine with variable conditions like 'region adminzone && {rank} != admin' for ranked zones!")
        .button("§aBack to Help Menu");
    form.show(player).then(res => { if (!res.canceled) openHelpMainMenu(player); });
}

function openHelpTipsExamples(player) {
    const form = new ActionFormData()
        .title("§eTips, Examples & Best Practices")
        .body("§bPro Tips:\n" +
            "• Always test with messages first before adding commands.\n" +
            "• Use §f/console §eto debug failed commands instantly.\n" +
            "• Name folders logically: §eCombat, Farming, Economy, Admin\n" +
            "• Use §flocal variables §efor per-player stats, §fglobal §efor server totals.\n" +
            "• Short tick intervals (<5) can cause lag — use wisely.\n" +
            "• 'any' in inputs = match everything (great for testing).\n" +
            "• Use §f/region reveal <name> §eto visually confirm region borders.\n\n" +
            "§6Real-World Examples:\n" +
            "1. §eAuto-Farm Reward§f\n→ Hook: Player Break Block (after), input: wheat, add 1 to local 'wheatbroken', message 'Wheat broken: {wheatbroken}'\n\n" +
            "2. §eVIP Only Zone§f\n→ Hook: Player Place Block (before), condition: region vipzone, var condition: cancel if {vip} != 1\n\n" +
            "3. §eKill Counter + Title§f\n→ Hook: Entity Die (after), input: zombie, condition: KilledByPlayer, add 1 to local 'zombiekills', message 'Zombies killed: {zombiekills}'\n\n" +
            "4. §eDaily Login Reward§f\n→ Loop (global, 24000 ticks), command: give @a diamond 1, broadcast 'Daily reward claimed!'\n\n" +
            "5. §eCustom Shop§f\n→ Hook: Item Use (after), input: emerald, Custom UI with buttons → 'Buy Sword (10 emeralds)' → command:give @s diamond_sword, setvar:local:emeralds:-10\n\n" +
            "6. §eAnti-Grief Logging§f\n→ Hook: Player Break Block (after), message '§c{player} broke {block_name} at {x} {y} {z}'\n\n" +
            "§dStart small → test → expand. You’ve got this!")
        .button("§aBack to Help Menu");
    form.show(player).then(res => { if (!res.canceled) openHelpMainMenu(player); });
}

// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// IGNORE (Important schema (useful for updating help menu)
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################

// Latest Version Of the help menu schema:

/*
function openHelpMainMenu(player) {
    const form = new ActionFormData()
        .title("§l§bSync Help Menu")
        .body("§bWelcome to Sync Help! This is a guide for beginners. Sync lets you create custom events and actions in Minecraft without writing code. Think of it like building automation rules.\n\nChoose a topic to learn more. Everything is explained step-by-step like you're new to coding.\n\n§eThis addon is fully developed and maintained by §d§lExterLeo")
        .button("§3§lOverview of Sync")
        .button("§8Folders")
        .button("§8Functions (How to Create Them)")
        .button("§8Hooks (Triggers)")
        .button("§8Events (Before/After)")
        .button("§8Conditions (Checks)")
        .button("§8Messages")
        .button("§8Commands")
        .button("§8Variables (Numbers & Storage)")
        .button("§8Custom UI (Forms)")
        .button("§8Variable Conditions")
        .button("§8Event Cancellation")
        .button("§8Inputs (Items/Blocks/Entities)")
        .button("§8Placeholders {Like This}")
        .button("§8Execution Flow (What Happens When)")
        .button("§8Globals vs Locals")
        .button("§8Tips & Examples")
        .button("§8Loops (Periodic Actions)")
        .button("§4§lClose");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openHelpOverview(player); break;
            case 1: openHelpFolders(player); break;
            case 2: openHelpFunctions(player); break;
            case 3: openHelpHooks(player); break;
            case 4: openHelpEvents(player); break;
            case 5: openHelpConditions(player); break;
            case 6: openHelpMessages(player); break;
            case 7: openHelpCommands(player); break;
            case 8: openHelpVariables(player); break;
            case 9: openHelpCustomUI(player); break;
            case 10: openHelpVarConditions(player); break;
            case 11: openHelpCancellation(player); break;
            case 12: openHelpInputs(player); break;
            case 13: openHelpPlaceholders(player); break;
            case 14: openHelpExecution(player); break;
            case 15: openHelpGlobalsLocals(player); break;
            case 16: openHelpTipsExamples(player); break;
            case 17: openHelpLoops(player); break;
            case 18: return;
        }
    });
}
function openHelpOverview(player) {
    const form = new ActionFormData()
        .title("§6Sync Overview")
        .body("§bWhat is Sync?\nSync is an addon for Minecraft Bedrock that lets you create custom 'rules' or 'automations' in your world. Instead of writing code, you use menus to set up what happens when certain events occur, like using an item or breaking a block.\n\nKey Ideas:\n- §eFolders: Like folders on your computer, they organize your rules.\n- §eFunctions: The rules themselves. Each function is triggered by a 'hook' (event).\n- §eHooks: Triggers like 'Item Use' or 'Entity Die'.\n- §eConditions: Checks if something is true, like 'player is sneaking'.\n- §eVariable Conditions: Advanced checks using variables, like '{kills} > 5' or '{item_name} contains \"magic\"'.\n- §eActions: What happens, like sending messages, running commands, changing variables, or showing custom UIs.\n- §eVariables: Numbers you can store and change, like scores or counters.\n- §eCustom UI: Forms with buttons or inputs to interact with players.\n- §eCancellation: Prevent events in 'before' hooks.\n- §eLoops: Periodic actions that run every N ticks, like timers or counters.\n\nTo start: Use /sync:menu to open the main menu, /sync:synchelp for this help, or /sync:loops for managing loops. Create a folder, then add functions or loops.\n\nSync saves everything in the world, so it works for all players, but some things (like local variables) are per-player.\n\nThink of Sync like If-Then statements in coding: IF (hook happens and conditions are true), THEN (do messages, commands, variable changes, or show UI). Loops are like scheduled tasks.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpFolders(player) {
    const form = new ActionFormData()
        .title("§eFolders Explained")
        .body("§bWhat are Folders?\nFolders are like containers to organize your functions, variables, and can be associated with loops. For example, you might have a folder called 'Farming' for rules about breaking crops, and another called 'Combat' for entity deaths.\n\nWhy Use Folders?\n- Keeps things tidy if you have many rules.\n- Local variables (explained later) are stored per folder, so they don't mix up.\n- Loops can be linked to folders for per-player local variables.\n\nHow to Manage Folders:\n1. Open /sync:menu.\n2. Choose 'Folders' to see your list.\n3. 'Create Folder': Enter a name (e.g., 'MyRules'). It must be unique and at least 1 character.\n4. 'Delete Folder': Select one and confirm. This deletes all functions and variables inside!\n5. 'Rename Folder': Select one, enter new name.\n\nInside a Folder:\n- You can add functions, create/delete variables, reset variable storage.\n- Variables in folders can be global (shared world-wide) or local (per-player in that folder).\n\nTip for Newbies: Start with one folder called 'Test' to experiment. If you mess up, delete it and start over.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpLoops(player) {
    const form = new ActionFormData()
        .title("§eLoops (Periodic Actions)")
        .body("§bWhat are Loops?\nLoops are automations that run repeatedly every N ticks (1 tick ≈ 0.05 seconds, so 20 ticks = 1 second). They are great for timers, counters, or ongoing effects without needing a trigger event.\n\nHow to Manage Loops:\n1. Use /sync:loops to open the Loops menu.\n2. Create Loop: Enter name (unique, min 1 char), interval (positive number in ticks, e.g., 20 for every second).\n3. Choose Folder: Optional; select a folder for local variables. Required for per-player scope.\n4. Choose Scope:\n - Global: Runs once per interval for the world. No specific player; messages sent to all players, commands run in overworld dimension (as server for both modes), variables limited to globals.\n - Per-Player: Runs once per interval for each online player. Uses the selected folder's local variables (per-player), {player} placeholder = player's name, messages sent to the player, commands can run as player (if mode 'player') or server.\n\nEditing a Loop:\n- Rename: Change the name.\n- Edit Interval: Update the tick delay.\n- Change Scope/Folder: Switch between global/per-player (folder required for per-player).\n- Messages: Add/edit/delete texts sent when loop runs. Supports {varname} for variables and {player} (player's name in per-player, empty in global).\n- Commands: Add/edit/delete Minecraft commands. Choose executor ('console' for server, 'player' for as player in per-player scope). Supports {varname} and {player}.\n- Variables: Modify globals (always) or locals (if folder selected). Add/remove/set/reset values; in per-player, locals update per player.\n\nDeleting: Select and confirm; removes the loop.\n\nExecution:\n- Loops run automatically if any exist.\n- For per-player: Actions executed individually for each online player, allowing personalized effects like incrementing playtime.\n- Placeholders: {player} inserts name; variables replaced based on scope (locals per-player in per-player loops).\n\nExamples:\n1. Global Day Counter: Interval 24000 (1 Minecraft day), no folder, global scope, variable add 1 to global 'days', message 'Day {days} has begun!' (to all).\n2. Per-Player Playtime: Folder 'Stats', per-player scope, interval 20 (1 second), variable add 1 to local 'playtime', message '/title {player} actionbar Playtime: {playtime} seconds' (command as console).\n3. Server Announcement: Global, interval 1200 (1 minute), message 'Remember to vote!' (to all).\n\nTips for Newbies:\n- Start with global for simple timers.\n- For per-player (e.g., playtime, hunger drain), use a folder with local vars.\n- Small intervals (e.g., 1 tick) may lag; use larger for efficiency.\n- Test in a test world; delete loop if issues.\n- Combine with functions: Loops can update vars used in function conditions.\n- No conditions/UI/cancellation in loops; pure actions.\n\nLike cron jobs in programming: Schedule repeated tasks.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpFunctions(player) {
    const form = new ActionFormData()
        .title("§eFunctions (How to Create Them)")
        .body("§bWhat is a Function?\nIn Sync, a function is like a custom script or rule. It's the core of what you build. Each function listens for a specific hook (trigger), checks conditions, and then does actions like messages, commands, variable changes, or shows custom UIs.\n\nHow to Create a Function Step-by-Step:\n1. Open /sync:menu > Folders > Select or create a folder > Create Function.\n2. Enter a name (e.g., 'OnDiamondBreak'). Unique per folder, at least 1 char.\n3. Select Hook: Choose what triggers it, like 'Item Use'.\n4. Configure Inputs: If the hook needs it, enter items/blocks/entities (comma-separated, or 'any').\n5. Select Event: If available, choose 'before' or 'after' the action.\n6. Conditions: Toggle checks like 'isSneaking'. The function only runs if ALL are true.\n7. Now edit the function: Add messages (text to send), commands (Minecraft commands to run), variables (change numbers), custom UIs, variable conditions.\n\nEditing a Function:\n- From folder > Functions List > Select function.\n- Rename, change hook/event, edit conditions/inputs, add/edit messages/commands/variables/UI/var conditions.\n\nDeleting: Folder > Delete Function > Select and confirm.\n\nExample: Create function 'HealOnApple' in folder 'Health'. Hook: Item Use, Inputs: apple, Event: after, Conditions: none, Messages: 'You feel better!', Commands: effect @s regeneration 10 1, Variables: add 1 to a 'healthCounter' variable.\n\nAs a newbie: Functions are like recipes. Hook is the ingredient, conditions are the checks, actions are the outcome.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpHooks(player) {
    const form = new ActionFormData()
        .title("§eHooks (Triggers)")
        .body("§bWhat are Hooks?\nHooks are the starting points – they 'hook' into Minecraft events. When the event happens, Sync checks if any functions are listening to that hook.\n\nAvailable Hooks:\n- §eItem Use: Triggers when a player uses an item (right-click).\n- §ePlayer Break Block: When a player breaks a block.\n- §ePlayer Place Block: When a player places a block.\n- §eEntity Die: When any entity (mob, player, etc.) dies.\n\nHow They Work:\nEach hook can have 'events' (before/after) and 'inputs' (specific items/blocks/entities).\n\nIn Function Creation: You pick one hook per function. You can change it later.\n\nTip: 'Before' hooks can cancel actions using 'cancel' in UI or var conditions. 'After' runs post-event.\n\nFor Newbies: Think of hooks as 'when this happens'. Like, 'when I use an item, do something'. Start with simple ones like Item Use.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpEvents(player) {
    const form = new ActionFormData()
        .title("§eEvents (Before/After)")
        .body("§bWhat are Events?\nSome hooks have phases: 'before' (right before the action completes) or 'after' (right after).\n\nWhich Hooks Have Them?\n- Item Use: Before/After\n- Player Break Block: Before/After\n- Player Place Block: Before/After\n- Entity Die: Only After (death can't be 'before')\n\nWhy Choose Before or After?\n- §eBefore: Good for checks or modifications before the game processes the action. Can cancel the event using 'cancel' action or var condition.\n- §eAfter: Runs after the action, like rewarding after breaking a block.\n\nIn Function: During creation, pick one if available. Change later in editor.\n\nExample: For Break Block, 'before' could check and cancel if not allowed, 'after' could give drops.\n\nNewbie Note: If unsure, pick 'after' – it's safer and more common. Use 'before' for prevention.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpConditions(player) {
    const form = new ActionFormData()
        .title("§eConditions (Checks)")
        .body("§bWhat are Conditions?\nConditions are yes/no checks that must ALL be true for the function to run. They filter when the hook triggers.\n\nAvailable Conditions:\nPlayer States (for player actors):\n- isClimbing: Player is on a ladder/vine.\n- isEmoting: Doing an emote.\n- isFalling: Falling down.\n- isFlying: Creative flying.\n- isGliding: Using elytra.\n- isInWater: In water.\n- isJumping: Jumping up.\n- isOnGround: Touching ground.\n- isSleeping: In bed.\n- isSneaking: Crouching.\n- isSprinting: Running fast.\n- isSwimming: Swimming motion.\n\nFor Entity Die Hook:\n- KilledByPlayer: Killer is a player.\n- KilledByEntity: Killer is a mob/animal.\n- KilledByEnvironment: No killer (fall, lava, etc.).\n- CauseLava/Fall/Fire/Explosion: Specific death causes.\n\nHow to Use: In function creation/editor, toggle them on/off. Bold means selected.\n\nExample: Hook 'Item Use', condition 'isSneaking': Only runs if sneaking while using item.\n\nNewbie Tip: Conditions prevent unwanted triggers. Start without, add if too broad. For advanced checks, use Variable Conditions. Like if-else in code.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpMessages(player) {
    const form = new ActionFormData()
        .title("§eMessages")
        .body("§bWhat are Messages?\nMessages are text sent to the player (or all if no player) when the function runs. Like chat notifications. Also available in loops.\n\nHow to Add/Edit:\nIn function/loop editor > Messages > Add Message: Enter text. Use placeholders like {player} (explained later).\n- Edit: Select, choose Edit, update text.\n- Delete: Select, choose Delete.\n\nMultiple Messages: Add as many as you want; they all send in order.\n\nExample: 'You broke a {broken block}! Score: {counter}' – Shows block type and a variable.\n\nNewbie Note: Messages are great for feedback. Use § for colors, like §a for green. Think of them as print statements in code. If using Custom UI, messages can be actions in buttons/fields.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpCommands(player) {
    const form = new ActionFormData()
        .title("§eCommands")
        .body("§bWhat are Commands?\nCommands are Minecraft commands run when the function or loop triggers. Like /say or /effect.\n\nHow to Add/Edit:\nIn function/loop editor > Commands > Add Command: Enter command without /, choose executor (console or player).\n- Console: Runs as server (powerful, no @s).\n- Player: Runs as the player who triggered (uses @s). In loops, 'player' only for per-player scope.\nUse placeholders {player}, etc.\n- Edit/Delete: Similar to messages.\n\nMultiple: Run in order.\n\nExample: 'effect {player} speed 10 1' – Gives speed to the player.\n\nNewbie Tip: Test commands in chat first. Console for world changes, player for personal effects. Like running functions in code. If using Custom UI, commands can be actions in buttons/fields.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpVariables(player) {
    const form = new ActionFormData()
        .title("§eVariables (Numbers & Storage)")
        .body("§bWhat are Variables?\nVariables are like boxes to store numbers. You can add, remove, set, or reset them. Use in messages/commands as {varname}, or in variable conditions. Available in functions and loops.\n\nTypes: Global (world-wide) vs Local (per-player, per-folder). More in Globals vs Locals.\n\nHow to Create:\nFolder editor > Create Variable: Name (e.g., 'kills'), scope (global/local).\nStarts at 0.\n\nIn Functions/Loops: Editor > Variables > Add/Modify: Select var, choose add/remove/set/reset, enter value (number).\n\nDelete: Folder > Delete Variable.\nReset All: Folder > Reset Variable Storage (sets all to 0).\n\nExample: On entity die, add 1 to 'kills'. Message: 'Kills: {kills}'.\n\nNewbie: Variables are like variables in programming. Use for counters, scores. Only numbers for now. Can be used in expressions for conditions.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpCustomUI(player) {
    const form = new ActionFormData()
        .title("§eCustom UI (Forms)")
        .body("§bWhat is Custom UI?\nCustom UIs are forms shown to the player when the function triggers, instead of direct actions. Good for interactive choices or inputs. Not available in loops.\n\nTypes:\n- ActionForm: Menu with buttons. Each button has a label and action (command, message, setvar:scope:name:value, cancel).\n- ModalForm: Input fields. Each field has label/placeholder, and onSubmit action (same as button actions, use {value} for input).\n\nHow to Add/Edit:\nFunction editor > Custom UI > Add UI > Choose type > Set title/body/fields.\nFor Action: Add buttons with labels and actions.\nFor Modal: Set fields, then edit onSubmit actions.\nMultiple UIs run in sequence.\n\nNote: If UIs are defined, they replace direct messages/commands/variables. Use actions for those.\n\nExample: Item Use on book: Show ActionForm 'Choose Spell' with buttons 'Fire (command:summon fireball)', 'Heal (setvar:local:health:10)'.\n\nNewbie: Like dialogs in games. Makes functions interactive.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpVarConditions(player) {
    const form = new ActionFormData()
        .title("§eVariable Conditions")
        .body("§bWhat are Variable Conditions?\nAdvanced checks using variables/expressions. Must be true/false based on numbers/strings. Not available in loops.\n\nExpressions: Like '{kills} > 5' or '10 == {level}' or '{flag}' (truthy if non-zero/non-empty), or '{item_name} contains \"magic\"' for string matching.\nSupports < > <= >= == != and 'contains' (case-insensitive). Use placeholders {varname}, {item_name}, {item_lore}, {block_name}, {entity_name}.\n\nModes:\n- Require: Expression must be true to run function.\n- Cancel: If true, cancel event (if before) and stop function.\n\nHow to Add/Edit:\nFunction editor > Var Conditions > Add: Enter expression, choose mode.\nMultiple checked in order, all requires must pass, any cancel stops.\n\nExample: Before Place Block, require '{build_permission} == 1' to allow building. Or cancel if '{block_name} contains \"protected\"'.\n\nNewbie: Like if statements with variables. Combines with built-in conditions for powerful filters.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpCancellation(player) {
    const form = new ActionFormData()
        .title("§eEvent Cancellation")
        .body("§bWhat is Event Cancellation?\nFor 'before' events, you can prevent the action from happening (e.g., stop block place, item use).\n\nHow:\n- In Custom UI actions: Use 'cancel' as action type.\n- In Variable Conditions: Use 'cancel' mode – if expression true, cancels.\n\nWorks for: Item Use Before, Break/Place Block Before.\nNot for After or Entity Die.\n\nExample: Before Break Block, var condition cancel if '{region_protected} == 1'.\n\nNewbie: Like vetoing an event. Use to protect areas or restrict items.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpInputs(player) {
    const form = new ActionFormData()
        .title("§eInputs (Items/Blocks/Entities)")
        .body("§bWhat are Inputs?\nFor some hooks, specify which items/blocks/entities trigger the function.\n\nHow: During hook config or editor > Edit Items/Blocks: Comma-separated IDs/names (e.g., 'diamond_sword, iron_axe') or 'any'.\nMatching: Checks ID (minecraft:stone), nameTag, lore (for items), display_name state (for blocks).\n\nHooks That Use:\n- Item Use: Items\n- Break/Place Block: Blocks\n- Entity Die: Entities (minecraft:zombie, etc.)\n\nIf empty or 'any', matches all.\n\nExample: Item Use with 'apple': Only triggers on apples. Or 'magic sword' to match lore/name.\n\nNewbie: Inputs narrow triggers. Use full IDs like 'minecraft:stone' or descriptive names. Test with 'any' first.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpPlaceholders(player) {
    const form = new ActionFormData()
        .title("§ePlaceholders {Like This}")
        .body("§bWhat are Placeholders?\nIn messages/commands/expressions/actions, use {word} to insert values dynamically.\n\nReserved (Auto from Event):\n- {player}: Player's name.\n- {item}: Item ID used.\n- {broken block}: Broken block ID.\n- {placed block}: Placed block ID.\n- {dead entity}: Dead entity ID.\n- {damage cause}: Death cause (fall, lava, etc.).\n- {killer}: Killer's name or ID.\n- {x}, {y}, {z}: Location coordinates (block or event location).\n- {item_name}: Item's nameTag or type name.\n- {item_lore}: Item's lore joined.\n- {block_name}: Block's display_name or type name.\n- {entity_name}: Entity's nameTag.\n\nCustom: {varname} for variables (e.g., {kills}).\nIn Modal UI actions: {value} for field input.\n\nHow: Just type in text. Sync replaces at runtime.\n\nExample: Message ' {player} killed {dead entity} with {item}! Score: {kills} at {x},{y},{z}'\n\nNewbie: Like string interpolation in code. Makes things personalized.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpExecution(player) {
    const form = new ActionFormData()
        .title("§eExecution Flow (What Happens When)")
        .body("§bHow Does Sync Work When an Event Happens?\n1. Minecraft event occurs (e.g., item use).\n2. Sync finds all functions with matching hook and event phase (before/after).\n3. For each: Check inputs match (items/blocks/etc., including names/lore).\n4. Check all built-in conditions true.\n5. Check variable conditions (require/cancel). May cancel here.\n6. If ok: If Custom UIs defined, show them sequentially (actions in UI). Else: Apply variable actions, send messages, run commands.\n\nOrder: Conditions first, then UIs or actions. Multiple functions can run if they match.\n\nWhere: Runs in the dimension of the actor.\n\nFor Loops: No events/conditions/UI/cancellation. Just run actions (variables, messages, commands) every interval, globally or per-player.\n\nNewbie: Like event listeners in code. Hook = onEvent, conditions = if, actions = then. UIs for interaction, cancellation for prevention. Loops for timers.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpGlobalsLocals(player) {
    const form = new ActionFormData()
        .title("§eGlobals vs Locals")
        .body("§bGlobals:\n- Shared across the entire world and all folders.\n- Same value for everyone.\n- Good for world totals, like total mobs killed.\n- Stored in world data.\n\nLocals:\n- Per-player and per-folder.\n- Each player has their own value in that folder.\n- Good for personal scores, like player's own kills.\n- Stored per folder.\n\nWhen Creating: Choose scope.\nIn Functions/Loops: Modify either.\nIn Placeholders: {varname} works for both (uses local if exists, else global).\n\nExample: Global 'total kills' adds 1 on any death. Local 'my kills' adds 1 per player.\n\nNewbie: Globals = static/shared, locals = instance/per-user. Like global vs local vars in functions.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpTipsExamples(player) {
    const form = new ActionFormData()
        .title("§eTips & Examples")
        .body("§bTips for Newbies:\n- Start small: One folder, one function or loop.\n- Test in creative: Use /sync:menu or /sync:loops, trigger events.\n- Use messages to debug: Add 'Test triggered!' to see if it runs.\n- Variables start at 0; reset if needed.\n- IDs: Use full like 'minecraft:dirt', no spaces. Names/lore for flexible matching.\n- Errors: If name exists or empty, menu tells you.\n- Multiplayer: Works for all, locals per-player.\n- For cancellation, use 'before' hooks.\n- UIs replace direct actions; use for choices.\n- Loops: Avoid small intervals to prevent lag.\n\nExamples:\n1. Auto-Heal: Hook Item Use (after), Inputs: golden_apple, Condition: isInWater, Message: 'Healed in water!', Command: effect @s instant_health 1 1.\n2. Break Counter: Hook Player Break Block (after), Inputs: any, Variable: add 1 to local 'breaks', Message: 'Breaks: {breaks}'.\n3. Mob Farm: Hook Entity Die (after), Inputs: minecraft:zombie, Condition: KilledByPlayer, Variable: add 1 to global 'zombie kills', Command: give {player} rotten_flesh 1.\n4. Protected Area: Hook Player Place Block (before), Var Condition: cancel if '{protected} == 1'.\n5. Shop UI: Hook Item Use (after), Inputs: emerald, Custom UI: ActionForm 'Buy' with buttons 'Sword (setvar:local:coins:-10; command:give @s iron_sword)', 'Armor (setvar:local:coins:-20; command:give @s iron_chestplate)'.\n6. Magic Item Check: Hook Item Use (before), Inputs: sword, Var Condition: require '{item_lore} contains \"enchanted\"', Message: 'Magic activated!'.\n7. Location Logger: Hook Player Break Block (after), Message: 'Broke {broken block} at {x},{y},{z}'.\n\nExperiment! If stuck, delete and retry.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
*/

// THIS IS THE SYNC HELP MENU SCHEMA (Follow this for updating stuff) (outdated), use the one above

/*
function openHelpMainMenu(player) {
    const form = new ActionFormData()
        .title("§l§bSync Help Menu")
        .body("§bWelcome to Sync Help! This is a guide for beginners. Sync lets you create custom events and actions in Minecraft without writing code. Think of it like building automation rules.\n\nChoose a topic to learn more. Everything is explained step-by-step like you're new to coding.\n\n§eThis addon is fully developed and maintained by §d§lExterLeo")
        .button("§3§lOverview of Sync")
        .button("§8Folders")
        .button("§8Functions (How to Create Them)")
        .button("§8Hooks (Triggers)")
        .button("§8Events (Before/After)")
        .button("§8Conditions (Checks)")
        .button("§8Messages")
        .button("§8Commands")
        .button("§8Variables (Numbers & Storage)")
        .button("§8Custom UI (Forms)")
        .button("§8Variable Conditions")
        .button("§8Event Cancellation")
        .button("§8Inputs (Items/Blocks/Entities)")
        .button("§8Placeholders {Like This}")
        .button("§8Execution Flow (What Happens When)")
        .button("§8Globals vs Locals")
        .button("§8Tips & Examples")
        .button("§8Loops (Periodic Actions)")
        .button("§4§lClose");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openHelpOverview(player); break;
            case 1: openHelpFolders(player); break;
            case 2: openHelpFunctions(player); break;
            case 3: openHelpHooks(player); break;
            case 4: openHelpEvents(player); break;
            case 5: openHelpConditions(player); break;
            case 6: openHelpMessages(player); break;
            case 7: openHelpCommands(player); break;
            case 8: openHelpVariables(player); break;
            case 9: openHelpCustomUI(player); break;
            case 10: openHelpVarConditions(player); break;
            case 11: openHelpCancellation(player); break;
            case 12: openHelpInputs(player); break;
            case 13: openHelpPlaceholders(player); break;
            case 14: openHelpExecution(player); break;
            case 15: openHelpGlobalsLocals(player); break;
            case 16: openHelpTipsExamples(player); break;
            case 17: openHelpLoops(player); break;
            case 18: return;
        }
    });
}
function openHelpOverview(player) {
    const form = new ActionFormData()
        .title("§6Sync Overview")
        .body("§bWhat is Sync?\nSync is an addon for Minecraft Bedrock that lets you create custom 'rules' or 'automations' in your world. Instead of writing code, you use menus to set up what happens when certain events occur, like using an item or breaking a block.\n\nKey Ideas:\n- §eFolders: Like folders on your computer, they organize your rules.\n- §eFunctions: The rules themselves. Each function is triggered by a 'hook' (event).\n- §eHooks: Triggers like 'Item Use' or 'Entity Die'.\n- §eConditions: Checks if something is true, like 'player is sneaking'.\n- §eVariable Conditions: Advanced checks using variables, like '{kills} > 5'.\n- §eActions: What happens, like sending messages, running commands, changing variables, or showing custom UIs.\n- §eVariables: Numbers you can store and change, like scores or counters.\n- §eCustom UI: Forms with buttons or inputs to interact with players.\n- §eCancellation: Prevent events in 'before' hooks.\n\nTo start: Use /sync:menu to open the main menu. Create a folder, then add functions inside it.\n\nSync saves everything in the world, so it works for all players, but some things (like local variables) are per-player.\n\nThink of Sync like If-Then statements in coding: IF (hook happens and conditions are true), THEN (do messages, commands, variable changes, or show UI).")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpFolders(player) {
    const form = new ActionFormData()
        .title("§eFolders Explained")
        .body("§bWhat are Folders?\nFolders are like containers to organize your functions and variables. For example, you might have a folder called 'Farming' for rules about breaking crops, and another called 'Combat' for entity deaths.\n\nWhy Use Folders?\n- Keeps things tidy if you have many rules.\n- Local variables (explained later) are stored per folder, so they don't mix up.\n\nHow to Manage Folders:\n1. Open /sync:menu.\n2. Choose 'Folders' to see your list.\n3. 'Create Folder': Enter a name (e.g., 'MyRules'). It must be unique and at least 1 character.\n4. 'Delete Folder': Select one and confirm. This deletes all functions and variables inside!\n5. 'Rename Folder': Select one, enter new name.\n\nInside a Folder:\n- You can add functions, create/delete variables, reset variable storage.\n- Variables in folders can be global (shared world-wide) or local (per-player in that folder).\n\nTip for Newbies: Start with one folder called 'Test' to experiment. If you mess up, delete it and start over.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpLoops(player) {
    const form = new ActionFormData()
        .title("§eLoops (Periodic Actions)")
        .body("§bWhat are Loops?\nLoops are automations that run repeatedly every N ticks (1 tick ≈ 0.05 seconds, so 20 ticks = 1 second). They are great for timers, counters, or ongoing effects without needing a trigger event.\n\nHow to Manage Loops:\n1. Use /sync:loops to open the Loops menu.\n2. Create Loop: Enter name (unique, min 1 char), interval (positive number in ticks, e.g., 20 for every second).\n3. Choose Folder: Optional; select a folder for local variables. Required for per-player scope.\n4. Choose Scope:\n   - Global: Runs once per interval for the world. No specific player; messages sent to all players, commands run in overworld dimension (as server for both modes), variables limited to globals.\n   - Per-Player: Runs once per interval for each online player. Uses the selected folder's local variables (per-player), {player} placeholder = player's name, messages sent to the player, commands can run as player (if mode 'player') or server.\n\nEditing a Loop:\n- Rename: Change the name.\n- Edit Interval: Update the tick delay.\n- Change Scope/Folder: Switch between global/per-player (folder required for per-player).\n- Messages: Add/edit/delete texts sent when loop runs. Supports {varname} for variables and {player} (player's name in per-player, empty in global).\n- Commands: Add/edit/delete Minecraft commands. Choose executor ('console' for server, 'player' for as player in per-player scope). Supports {varname} and {player}.\n- Variables: Modify globals (always) or locals (if folder selected). Add/remove/set/reset values; in per-player, locals update per player.\n\nDeleting: Select and confirm; removes the loop.\n\nExecution:\n- Loops run automatically if any exist.\n- For per-player: Actions executed individually for each online player, allowing personalized effects like incrementing playtime.\n- Placeholders: {player} inserts name; variables replaced based on scope (locals per-player in per-player loops).\n\nExamples:\n1. Global Day Counter: Interval 24000 (1 Minecraft day), no folder, global scope, variable add 1 to global 'days', message 'Day {days} has begun!' (to all).\n2. Per-Player Playtime: Folder 'Stats', per-player scope, interval 20 (1 second), variable add 1 to local 'playtime', message '/title {player} actionbar Playtime: {playtime} seconds' (command as console).\n3. Server Announcement: Global, interval 1200 (1 minute), message 'Remember to vote!' (to all).\n\nTips for Newbies:\n- Start with global for simple timers.\n- For per-player (e.g., playtime, hunger drain), use a folder with local vars.\n- Small intervals (e.g., 1 tick) may lag; use larger for efficiency.\n- Test in a test world; delete loop if issues.\n- Combine with functions: Loops can update vars used in function conditions.\n- No conditions/UI/cancellation in loops; pure actions.\n\nLike cron jobs in programming: Schedule repeated tasks.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpFunctions(player) {
    const form = new ActionFormData()
        .title("§eFunctions (How to Create Them)")
        .body("§bWhat is a Function?\nIn Sync, a function is like a custom script or rule. It's the core of what you build. Each function listens for a specific hook (trigger), checks conditions, and then does actions like messages, commands, variable changes, or shows custom UIs.\n\nHow to Create a Function Step-by-Step:\n1. Open /sync:menu > Folders > Select or create a folder > Create Function.\n2. Enter a name (e.g., 'OnDiamondBreak'). Unique per folder, at least 1 char.\n3. Select Hook: Choose what triggers it, like 'Item Use'.\n4. Configure Inputs: If the hook needs it, enter items/blocks/entities (comma-separated, or 'any').\n5. Select Event: If available, choose 'before' or 'after' the action.\n6. Conditions: Toggle checks like 'isSneaking'. The function only runs if ALL are true.\n7. Now edit the function: Add messages (text to send), commands (Minecraft commands to run), variables (change numbers), custom UIs, variable conditions.\n\nEditing a Function:\n- From folder > Functions List > Select function.\n- Rename, change hook/event, edit conditions/inputs, add/edit messages/commands/variables/UI/var conditions.\n\nDeleting: Folder > Delete Function > Select and confirm.\n\nExample: Create function 'HealOnApple' in folder 'Health'. Hook: Item Use, Inputs: apple, Event: after, Conditions: none, Messages: 'You feel better!', Commands: effect @s regeneration 10 1, Variables: add 1 to a 'healthCounter' variable.\n\nAs a newbie: Functions are like recipes. Hook is the ingredient, conditions are the checks, actions are the outcome.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpHooks(player) {
    const form = new ActionFormData()
        .title("§eHooks (Triggers)")
        .body("§bWhat are Hooks?\nHooks are the starting points – they 'hook' into Minecraft events. When the event happens, Sync checks if any functions are listening to that hook.\n\nAvailable Hooks:\n- §eItem Use: Triggers when a player uses an item (right-click).\n- §ePlayer Break Block: When a player breaks a block.\n- §ePlayer Place Block: When a player places a block.\n- §eEntity Die: When any entity (mob, player, etc.) dies.\n\nHow They Work:\nEach hook can have 'events' (before/after) and 'inputs' (specific items/blocks/entities).\n\nIn Function Creation: You pick one hook per function. You can change it later.\n\nTip: 'Before' hooks can cancel actions using 'cancel' in UI or var conditions. 'After' runs post-event.\n\nFor Newbies: Think of hooks as 'when this happens'. Like, 'when I use an item, do something'. Start with simple ones like Item Use.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpEvents(player) {
    const form = new ActionFormData()
        .title("§eEvents (Before/After)")
        .body("§bWhat are Events?\nSome hooks have phases: 'before' (right before the action completes) or 'after' (right after).\n\nWhich Hooks Have Them?\n- Item Use: Before/After\n- Player Break Block: Before/After\n- Player Place Block: Before/After\n- Entity Die: Only After (death can't be 'before')\n\nWhy Choose Before or After?\n- §eBefore: Good for checks or modifications before the game procect events the action. Can cancel the event using 'cancel' action or var condition.\n- §eAfter: Runs after the action, like rewarding after breaking a block.\n\nIn Function: During creation, pick one if available. Change later in editor.\n\nExample: For Break Block, 'before' could check and cancel if not allowed, 'after' could give drops.\n\nNewbie Note: If unsure, pick 'after' – it's safer and more common. Use 'before' for prevention.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpConditions(player) {
    const form = new ActionFormData()
        .title("§eConditions (Checks)")
        .body("§bWhat are Conditions?\nConditions are yes/no checks that must ALL be true for the function to run. They filter when the hook triggers.\n\nAvailable Conditions:\nPlayer States (for player actors):\n- isClimbing: Player is on a ladder/vine.\n- isEmoting: Doing an emote.\n- isFalling: Falling down.\n- isFlying: Creative flying.\n- isGliding: Using elytra.\n- isInWater: In water.\n- isJumping: Jumping up.\n- isOnGround: Touching ground.\n- isSleeping: In bed.\n- isSneaking: Crouching.\n- isSprinting: Running fast.\n- isSwimming: Swimming motion.\n\nFor Entity Die Hook:\n- KilledByPlayer: Killer is a player.\n- KilledByEntity: Killer is a mob/animal.\n- KilledByEnvironment: No killer (fall, lava, etc.).\n- CauseLava/Fall/Fire/Explosion: Specific death causes.\n\nHow to Use: In function creation/editor, toggle them on/off. Bold means selected.\n\nExample: Hook 'Item Use', condition 'isSneaking': Only runs if sneaking while using item.\n\nNewbie Tip: Conditions prevent unwanted triggers. Start without, add if too broad. For advanced checks, use Variable Conditions. Like if-else in code.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpMessages(player) {
    const form = new ActionFormData()
        .title("§eMessages")
        .body("§bWhat are Messages?\nMessages are text sent to the player (or all if no player) when the function runs. Like chat notifications.\n\nHow to Add/Edit:\nIn function editor > Messages > Add Message: Enter text. Use placeholders like {player} (explained later).\n- Edit: Select, choose Edit, update text.\n- Delete: Select, choose Delete.\n\nMultiple Messages: Add as many as you want; they all send in order.\n\nExample: 'You broke a {broken block}! Score: {counter}' – Shows block type and a variable.\n\nNewbie Note: Messages are great for feedback. Use § for colors, like §a for green. Think of them as print statements in code. If using Custom UI, messages can be actions in buttons/fields.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpCommands(player) {
    const form = new ActionFormData()
        .title("§eCommands")
        .body("§bWhat are Commands?\nCommands are Minecraft commands run when the function triggers. Like /say or /effect.\n\nHow to Add/Edit:\nIn function editor > Commands > Add Command: Enter command without /, choose executor (console or player).\n- Console: Runs as server (powerful, no @s).\n- Player: Runs as the player who triggered (uses @s).\nUse placeholders {player}, etc.\n- Edit/Delete: Similar to messages.\n\nMultiple: Run in order.\n\nExample: 'effect {player} speed 10 1' – Gives speed to the player.\n\nNewbie Tip: Test commands in chat first. Console for world changes, player for personal effects. Like running functions in code. If using Custom UI, commands can be actions in buttons/fields.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpVariables(player) {
    const form = new ActionFormData()
        .title("§eVariables (Numbers & Storage)")
        .body("§bWhat are Variables?\nVariables are like boxes to store numbers. You can add, remove, set, or reset them. Use in messages/commands as {varname}, or in variable conditions.\n\nTypes: Global (world-wide) vs Local (per-player, per-folder). More in Globals vs Locals.\n\nHow to Create:\nFolder editor > Create Variable: Name (e.g., 'kills'), scope (global/local).\nStarts at 0.\n\nIn Functions: Editor > Variables > Add/Modify: Select var, choose add/remove/set/reset, enter value (number).\n\nDelete: Folder > Delete Variable.\nReset All: Folder > Reset Variable Storage (sets all to 0).\n\nExample: On entity die, add 1 to 'kills'. Message: 'Kills: {kills}'.\n\nNewbie: Variables are like variables in programming. Use for counters, scores. Only numbers for now. Can be used in expressions for conditions.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpCustomUI(player) {
    const form = new ActionFormData()
        .title("§eCustom UI (Forms)")
        .body("§bWhat is Custom UI?\nCustom UIs are forms shown to the player when the function triggers, instead of direct actions. Good for interactive choices or inputs.\n\nTypes:\n- ActionForm: Menu with buttons. Each button has a label and action (command, message, setvar:scope:name:value, cancel).\n- ModalForm: Input fields. Each field has label/placeholder, and onSubmit action (same as button actions, use {value} for input).\n\nHow to Add/Edit:\nFunction editor > Custom UI > Add UI > Choose type > Set title/body/fields.\nFor Action: Add buttons with labels and actions.\nFor Modal: Set fields, then edit onSubmit actions.\nMultiple UIs run in sequence.\n\nNote: If UIs are defined, they replace direct messages/commands/variables. Use actions for those.\n\nExample: Item Use on book: Show ActionForm 'Choose Spell' with buttons 'Fire (command:summon fireball)', 'Heal (setvar:local:health:10)'.\n\nNewbie: Like dialogs in games. Makes functions interactive.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpVarConditions(player) {
    const form = new ActionFormData()
        .title("§eVariable Conditions")
        .body("§bWhat are Variable Conditions?\nAdvanced checks using variables/expressions. Must be true/false based on numbers/strings.\n\nExpressions: Like '{kills} > 5' or '10 == {level}' or '{flag}' (truthy if non-zero/non-empty).\nSupports < > <= >= == != . Use placeholders {varname}.\n\nModes:\n- Require: Expression must be true to run function.\n- Cancel: If true, cancel event (if before) and stop function.\n\nHow to Add/Edit:\nFunction editor > Var Conditions > Add: Enter expression, choose mode.\nMultiple checked in order, all requires must pass, any cancel stops.\n\nExample: Before Place Block, require '{build_permission} == 1' to allow building.\n\nNewbie: Like if statements with variables. Combines with built-in conditions for powerful filters.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpCancellation(player) {
    const form = new ActionFormData()
        .title("§eEvent Cancellation")
        .body("§bWhat is Event Cancellation?\nFor 'before' events, you can prevent the action from happening (e.g., stop block place, item use).\n\nHow:\n- In Custom UI actions: Use 'cancel' as action type.\n- In Variable Conditions: Use 'cancel' mode – if expression true, cancels.\n\nWorks for: Item Use Before, Break/Place Block Before.\nNot for After or Entity Die.\n\nExample: Before Break Block, var condition cancel if '{region_protected} == 1'.\n\nNewbie: Like vetoing an event. Use to protect areas or restrict items.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpInputs(player) {
    const form = new ActionFormData()
        .title("§eInputs (Items/Blocks/Entities)")
        .body("§bWhat are Inputs?\nFor some hooks, specify which items/blocks/entities trigger the function.\n\nHow: During hook config or editor > Edit Items/Blocks: Comma-separated IDs (e.g., 'diamond_sword, iron_axe') or 'any'.\n\nHooks That Use:\n- Item Use: Items\n- Break/Place Block: Blocks\n- Entity Die: Entities (minecraft:zombie, etc.)\n\nIf empty or 'any', matches all.\n\nExample: Item Use with 'apple': Only triggers on apples.\n\nNewbie: Inputs narrow triggers. Use full IDs like 'minecraft:stone'. Test with 'any' first.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpPlaceholders(player) {
    const form = new ActionFormData()
        .title("§ePlaceholders {Like This}")
        .body("§bWhat are Placeholders?\nIn messages/commands/expressions/actions, use {word} to insert values dynamically.\n\nReserved (Auto from Event):\n- {player}: Player's name.\n- {item}: Item ID used.\n- {broken block}: Broken block ID.\n- {placed block}: Placed block ID.\n- {dead entity}: Dead entity ID.\n- {damage cause}: Death cause (fall, lava, etc.).\n- {killer}: Killer's name or ID.\n\nCustom: {varname} for variables (e.g., {kills}).\nIn Modal UI actions: {value} for field input.\n\nHow: Just type in text. Sync replaces at runtime.\n\nExample: Message ' {player} killed {dead entity} with {item}! Score: {kills}'\n\nNewbie: Like string interpolation in code. Makes things personalized.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpExecution(player) {
    const form = new ActionFormData()
        .title("§eExecution Flow (What Happens When)")
        .body("§bHow Does Sync Work When an Event Happens?\n1. Minecraft event occurs (e.g., item use).\n2. Sync finds all functions with matching hook and event phase (before/after).\n3. For each: Check inputs match (items/blocks/etc.).\n4. Check all built-in conditions true.\n5. Check variable conditions (require/cancel). May cancel here.\n6. If ok: If Custom UIs defined, show them sequentially (actions in UI). Else: Apply variable actions, send messages, run commands.\n\nOrder: Conditions first, then UIs or actions. Multiple functions can run if they match.\n\nWhere: Runs in the dimension of the actor.\n\nNewbie: Like event listeners in code. Hook = onEvent, conditions = if, actions = then. UIs for interaction, cancellation for prevention.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpGlobalsLocals(player) {
    const form = new ActionFormData()
        .title("§eGlobals vs Locals")
        .body("§bGlobals:\n- Shared across the entire world and all folders.\n- Same value for everyone.\n- Good for world totals, like total mobs killed.\n- Stored in world data.\n\nLocals:\n- Per-player and per-folder.\n- Each player has their own value in that folder.\n- Good for personal scores, like player's own kills.\n- Stored per folder.\n\nWhen Creating: Choose scope.\nIn Functions: Modify either.\nIn Placeholders: {varname} works for both (uses local if exists, else global).\n\nExample: Global 'total kills' adds 1 on any death. Local 'my kills' adds 1 per player.\n\nNewbie: Globals = static/shared, locals = instance/per-user. Like global vs local vars in functions.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
function openHelpTipsExamples(player) {
    const form = new ActionFormData()
        .title("§eTips & Examples")
        .body("§bTips for Newbies:\n- Start small: One folder, one function.\n- Test in creative: Use /sync:menu, trigger events.\n- Use messages to debug: Add 'Test triggered!' to see if it runs.\n- Variables start at 0; reset if needed.\n- IDs: Use full like 'minecraft:dirt', no spaces.\n- Errors: If name exists or empty, menu tells you.\n- Multiplayer: Works for all, locals per-player.\n- For cancellation, use 'before' hooks.\n- UIs replace direct actions; use for choices.\n\nExamples:\n1. Auto-Heal: Hook Item Use (after), Inputs: golden_apple, Condition: isInWater, Message: 'Healed in water!', Command: effect @s instant_health 1 1.\n2. Break Counter: Hook Player Break Block (after), Inputs: any, Variable: add 1 to local 'breaks', Message: 'Breaks: {breaks}'.\n3. Mob Farm: Hook Entity Die (after), Inputs: minecraft:zombie, Condition: KilledByPlayer, Variable: add 1 to global 'zombie kills', Command: give {player} rotten_flesh 1.\n4. Protected Area: Hook Player Place Block (before), Var Condition: cancel if '{protected} == 1'.\n5. Shop UI: Hook Item Use (after), Inputs: emerald, Custom UI: ActionForm 'Buy' with buttons 'Sword (setvar:local:coins:-10; command:give @s iron_sword)', 'Armor (setvar:local:coins:-20; command:give @s iron_chestplate)'.\n\nExperiment! If stuck, delete and retry.")
        .button("§aBack to Help Menu");
    form.show(player).then(res => {
        if (!res.canceled) openHelpMainMenu(player);
    });
}
*/

// BACKUP OF SYNChelp 1

/*
        .body("§bWelcome to Sync Help! This is a guide for beginners. Sync lets you create custom events and actions in Minecraft without writing code. Think of it like building automation rules.\n\nChoose a topic to learn more. Everything is explained step-by-step like you're new to coding.\n\n§eThis addon is fully developed and maintained by §d§lExterLeo")
        .body("§bWhat is Sync?\nSync is an addon for Minecraft Bedrock that lets you create custom 'rules' or 'automations' in your world. Instead of writing code, you use menus to set up what happens when certain events occur, like using an item or breaking a block.\n\nKey Ideas:\n- §eFolders: Like folders on your computer, they organize your rules.\n- §eFunctions: The rules themselves. Each function is triggered by a 'hook' (event).\n- §eHooks: Triggers like 'Item Use' or 'Entity Die'.\n- §eConditions: Checks if something is true, like 'player is sneaking'.\n- §eVariable Conditions: Advanced checks using variables, like '{kills} > 5'.\n- §eActions: What happens, like sending messages, running commands, changing variables, or showing custom UIs.\n- §eVariables: Numbers you can store and change, like scores or counters.\n- §eCustom UI: Forms with buttons or inputs to interact with players.\n- §eCancellation: Prevent events in 'before' hooks.\n\nTo start: Use /sync:menu to open the main menu. Create a folder, then add functions inside it.\n\nSync saves everything in the world, so it works for all players, but some things (like local variables) are per-player.\n\nThink of Sync like If-Then statements in coding: IF (hook happens and conditions are true), THEN (do messages, commands, variable changes, or show UI).")
        .body("§bWhat are Folders?\nFolders are like containers to organize your functions and variables. For example, you might have a folder called 'Farming' for rules about breaking crops, and another called 'Combat' for entity deaths.\n\nWhy Use Folders?\n- Keeps things tidy if you have many rules.\n- Local variables (explained later) are stored per folder, so they don't mix up.\n\nHow to Manage Folders:\n1. Open /sync:menu.\n2. Choose 'Folders' to see your list.\n3. 'Create Folder': Enter a name (e.g., 'MyRules'). It must be unique and at least 1 character.\n4. 'Delete Folder': Select one and confirm. This deletes all functions and variables inside!\n5. 'Rename Folder': Select one, enter new name.\n\nInside a Folder:\n- You can add functions, create/delete variables, reset variable storage.\n- Variables in folders can be global (shared world-wide) or local (per-player in that folder).\n\nTip for Newbies: Start with one folder called 'Test' to experiment. If you mess up, delete it and start over.")
        .body("§bWhat are Loops?\nLoops are automations that run repeatedly every N ticks (1 tick ≈ 0.05 seconds, so 20 ticks = 1 second). They are great for timers, counters, or ongoing effects without needing a trigger event.\n\nHow to Manage Loops:\n1. Use /sync:loops to open the Loops menu.\n2. Create Loop: Enter name (unique, min 1 char), interval (positive number in ticks, e.g., 20 for every second).\n3. Choose Folder: Optional; select a folder for local variables. Required for per-player scope.\n4. Choose Scope:\n   - Global: Runs once per interval for the world. No specific player; messages sent to all players, commands run in overworld dimension (as server for both modes), variables limited to globals.\n   - Per-Player: Runs once per interval for each online player. Uses the selected folder's local variables (per-player), {player} placeholder = player's name, messages sent to the player, commands can run as player (if mode 'player') or server.\n\nEditing a Loop:\n- Rename: Change the name.\n- Edit Interval: Update the tick delay.\n- Change Scope/Folder: Switch between global/per-player (folder required for per-player).\n- Messages: Add/edit/delete texts sent when loop runs. Supports {varname} for variables and {player} (player's name in per-player, empty in global).\n- Commands: Add/edit/delete Minecraft commands. Choose executor ('console' for server, 'player' for as player in per-player scope). Supports {varname} and {player}.\n- Variables: Modify globals (always) or locals (if folder selected). Add/remove/set/reset values; in per-player, locals update per player.\n\nDeleting: Select and confirm; removes the loop.\n\nExecution:\n- Loops run automatically if any exist.\n- For per-player: Actions executed individually for each online player, allowing personalized effects like incrementing playtime.\n- Placeholders: {player} inserts name; variables replaced based on scope (locals per-player in per-player loops).\n\nExamples:\n1. Global Day Counter: Interval 24000 (1 Minecraft day), no folder, global scope, variable add 1 to global 'days', message 'Day {days} has begun!' (to all).\n2. Per-Player Playtime: Folder 'Stats', per-player scope, interval 20 (1 second), variable add 1 to local 'playtime', message '/title {player} actionbar Playtime: {playtime} seconds' (command as console).\n3. Server Announcement: Global, interval 1200 (1 minute), message 'Remember to vote!' (to all).\n\nTips for Newbies:\n- Start with global for simple timers.\n- For per-player (e.g., playtime, hunger drain), use a folder with local vars.\n- Small intervals (e.g., 1 tick) may lag; use larger for efficiency.\n- Test in a test world; delete loop if issues.\n- Combine with functions: Loops can update vars used in function conditions.\n- No conditions/UI/cancellation in loops; pure actions.\n\nLike cron jobs in programming: Schedule repeated tasks.")
        .body("§bWhat is a Function?\nIn Sync, a function is like a custom script or rule. It's the core of what you build. Each function listens for a specific hook (trigger), checks conditions, and then does actions like messages, commands, variable changes, or shows custom UIs.\n\nHow to Create a Function Step-by-Step:\n1. Open /sync:menu > Folders > Select or create a folder > Create Function.\n2. Enter a name (e.g., 'OnDiamondBreak'). Unique per folder, at least 1 char.\n3. Select Hook: Choose what triggers it, like 'Item Use'.\n4. Configure Inputs: If the hook needs it, enter items/blocks/entities (comma-separated, or 'any').\n5. Select Event: If available, choose 'before' or 'after' the action.\n6. Conditions: Toggle checks like 'isSneaking'. The function only runs if ALL are true.\n7. Now edit the function: Add messages (text to send), commands (Minecraft commands to run), variables (change numbers), custom UIs, variable conditions.\n\nEditing a Function:\n- From folder > Functions List > Select function.\n- Rename, change hook/event, edit conditions/inputs, add/edit messages/commands/variables/UI/var conditions.\n\nDeleting: Folder > Delete Function > Select and confirm.\n\nExample: Create function 'HealOnApple' in folder 'Health'. Hook: Item Use, Inputs: apple, Event: after, Conditions: none, Messages: 'You feel better!', Commands: effect @s regeneration 10 1, Variables: add 1 to a 'healthCounter' variable.\n\nAs a newbie: Functions are like recipes. Hook is the ingredient, conditions are the checks, actions are the outcome.")
        .body("§bWhat are Hooks?\nHooks are the starting points – they 'hook' into Minecraft events. When the event happens, Sync checks if any functions are listening to that hook.\n\nAvailable Hooks:\n- §eItem Use: Triggers when a player uses an item (right-click).\n- §ePlayer Break Block: When a player breaks a block.\n- §ePlayer Place Block: When a player places a block.\n- §eEntity Die: When any entity (mob, player, etc.) dies.\n\nHow They Work:\nEach hook can have 'events' (before/after) and 'inputs' (specific items/blocks/entities).\n\nIn Function Creation: You pick one hook per function. You can change it later.\n\nTip: 'Before' hooks can cancel actions using 'cancel' in UI or var conditions. 'After' runs post-event.\n\nFor Newbies: Think of hooks as 'when this happens'. Like, 'when I use an item, do something'. Start with simple ones like Item Use.")
        .body("§bWhat are Events?\nSome hooks have phases: 'before' (right before the action completes) or 'after' (right after).\n\nWhich Hooks Have Them?\n- Item Use: Before/After\n- Player Break Block: Before/After\n- Player Place Block: Before/After\n- Entity Die: Only After (death can't be 'before')\n\nWhy Choose Before or After?\n- §eBefore: Good for checks or modifications before the game procect events the action. Can cancel the event using 'cancel' action or var condition.\n- §eAfter: Runs after the action, like rewarding after breaking a block.\n\nIn Function: During creation, pick one if available. Change later in editor.\n\nExample: For Break Block, 'before' could check and cancel if not allowed, 'after' could give drops.\n\nNewbie Note: If unsure, pick 'after' – it's safer and more common. Use 'before' for prevention.")
        .body("§bWhat are Conditions?\nConditions are yes/no checks that must ALL be true for the function to run. They filter when the hook triggers.\n\nAvailable Conditions:\nPlayer States (for player actors):\n- isClimbing: Player is on a ladder/vine.\n- isEmoting: Doing an emote.\n- isFalling: Falling down.\n- isFlying: Creative flying.\n- isGliding: Using elytra.\n- isInWater: In water.\n- isJumping: Jumping up.\n- isOnGround: Touching ground.\n- isSleeping: In bed.\n- isSneaking: Crouching.\n- isSprinting: Running fast.\n- isSwimming: Swimming motion.\n\nFor Entity Die Hook:\n- KilledByPlayer: Killer is a player.\n- KilledByEntity: Killer is a mob/animal.\n- KilledByEnvironment: No killer (fall, lava, etc.).\n- CauseLava/Fall/Fire/Explosion: Specific death causes.\n\nHow to Use: In function creation/editor, toggle them on/off. Bold means selected.\n\nExample: Hook 'Item Use', condition 'isSneaking': Only runs if sneaking while using item.\n\nNewbie Tip: Conditions prevent unwanted triggers. Start without, add if too broad. For advanced checks, use Variable Conditions. Like if-else in code.")
        .body("§bWhat are Messages?\nMessages are text sent to the player (or all if no player) when the function runs. Like chat notifications.\n\nHow to Add/Edit:\nIn function editor > Messages > Add Message: Enter text. Use placeholders like {player} (explained later).\n- Edit: Select, choose Edit, update text.\n- Delete: Select, choose Delete.\n\nMultiple Messages: Add as many as you want; they all send in order.\n\nExample: 'You broke a {broken block}! Score: {counter}' – Shows block type and a variable.\n\nNewbie Note: Messages are great for feedback. Use § for colors, like §a for green. Think of them as print statements in code. If using Custom UI, messages can be actions in buttons/fields.")
        .body("§bWhat are Commands?\nCommands are Minecraft commands run when the function triggers. Like /say or /effect.\n\nHow to Add/Edit:\nIn function editor > Commands > Add Command: Enter command without /, choose executor (console or player).\n- Console: Runs as server (powerful, no @s).\n- Player: Runs as the player who triggered (uses @s).\nUse placeholders {player}, etc.\n- Edit/Delete: Similar to messages.\n\nMultiple: Run in order.\n\nExample: 'effect {player} speed 10 1' – Gives speed to the player.\n\nNewbie Tip: Test commands in chat first. Console for world changes, player for personal effects. Like running functions in code. If using Custom UI, commands can be actions in buttons/fields.")
        .body("§bWhat are Variables?\nVariables are like boxes to store numbers. You can add, remove, set, or reset them. Use in messages/commands as {varname}, or in variable conditions.\n\nTypes: Global (world-wide) vs Local (per-player, per-folder). More in Globals vs Locals.\n\nHow to Create:\nFolder editor > Create Variable: Name (e.g., 'kills'), scope (global/local).\nStarts at 0.\n\nIn Functions: Editor > Variables > Add/Modify: Select var, choose add/remove/set/reset, enter value (number).\n\nDelete: Folder > Delete Variable.\nReset All: Folder > Reset Variable Storage (sets all to 0).\n\nExample: On entity die, add 1 to 'kills'. Message: 'Kills: {kills}'.\n\nNewbie: Variables are like variables in programming. Use for counters, scores. Only numbers for now. Can be used in expressions for conditions.")
        .body("§bWhat is Custom UI?\nCustom UIs are forms shown to the player when the function triggers, instead of direct actions. Good for interactive choices or inputs.\n\nTypes:\n- ActionForm: Menu with buttons. Each button has a label and action (command, message, setvar:scope:name:value, cancel).\n- ModalForm: Input fields. Each field has label/placeholder, and onSubmit action (same as button actions, use {value} for input).\n\nHow to Add/Edit:\nFunction editor > Custom UI > Add UI > Choose type > Set title/body/fields.\nFor Action: Add buttons with labels and actions.\nFor Modal: Set fields, then edit onSubmit actions.\nMultiple UIs run in sequence.\n\nNote: If UIs are defined, they replace direct messages/commands/variables. Use actions for those.\n\nExample: Item Use on book: Show ActionForm 'Choose Spell' with buttons 'Fire (command:summon fireball)', 'Heal (setvar:local:health:10)'.\n\nNewbie: Like dialogs in games. Makes functions interactive.")
        .body("§bWhat are Variable Conditions?\nAdvanced checks using variables/expressions. Must be true/false based on numbers/strings.\n\nExpressions: Like '{kills} > 5' or '10 == {level}' or '{flag}' (truthy if non-zero/non-empty).\nSupports < > <= >= == != . Use placeholders {varname}.\n\nModes:\n- Require: Expression must be true to run function.\n- Cancel: If true, cancel event (if before) and stop function.\n\nHow to Add/Edit:\nFunction editor > Var Conditions > Add: Enter expression, choose mode.\nMultiple checked in order, all requires must pass, any cancel stops.\n\nExample: Before Place Block, require '{build_permission} == 1' to allow building.\n\nNewbie: Like if statements with variables. Combines with built-in conditions for powerful filters.")
        .body("§bWhat is Event Cancellation?\nFor 'before' events, you can prevent the action from happening (e.g., stop block place, item use).\n\nHow:\n- In Custom UI actions: Use 'cancel' as action type.\n- In Variable Conditions: Use 'cancel' mode – if expression true, cancels.\n\nWorks for: Item Use Before, Break/Place Block Before.\nNot for After or Entity Die.\n\nExample: Before Break Block, var condition cancel if '{region_protected} == 1'.\n\nNewbie: Like vetoing an event. Use to protect areas or restrict items.")
        .body("§bWhat are Inputs?\nFor some hooks, specify which items/blocks/entities trigger the function.\n\nHow: During hook config or editor > Edit Items/Blocks: Comma-separated IDs (e.g., 'diamond_sword, iron_axe') or 'any'.\n\nHooks That Use:\n- Item Use: Items\n- Break/Place Block: Blocks\n- Entity Die: Entities (minecraft:zombie, etc.)\n\nIf empty or 'any', matches all.\n\nExample: Item Use with 'apple': Only triggers on apples.\n\nNewbie: Inputs narrow triggers. Use full IDs like 'minecraft:stone'. Test with 'any' first.")
        .body("§bWhat are Placeholders?\nIn messages/commands/expressions/actions, use {word} to insert values dynamically.\n\nReserved (Auto from Event):\n- {player}: Player's name.\n- {item}: Item ID used.\n- {broken block}: Broken block ID.\n- {placed block}: Placed block ID.\n- {dead entity}: Dead entity ID.\n- {damage cause}: Death cause (fall, lava, etc.).\n- {killer}: Killer's name or ID.\n\nCustom: {varname} for variables (e.g., {kills}).\nIn Modal UI actions: {value} for field input.\n\nHow: Just type in text. Sync replaces at runtime.\n\nExample: Message ' {player} killed {dead entity} with {item}! Score: {kills}'\n\nNewbie: Like string interpolation in code. Makes things personalized.")
        .body("§bHow Does Sync Work When an Event Happens?\n1. Minecraft event occurs (e.g., item use).\n2. Sync finds all functions with matching hook and event phase (before/after).\n3. For each: Check inputs match (items/blocks/etc.).\n4. Check all built-in conditions true.\n5. Check variable conditions (require/cancel). May cancel here.\n6. If ok: If Custom UIs defined, show them sequentially (actions in UI). Else: Apply variable actions, send messages, run commands.\n\nOrder: Conditions first, then UIs or actions. Multiple functions can run if they match.\n\nWhere: Runs in the dimension of the actor.\n\nNewbie: Like event listeners in code. Hook = onEvent, conditions = if, actions = then. UIs for interaction, cancellation for prevention.")
        .body("§bGlobals:\n- Shared across the entire world and all folders.\n- Same value for everyone.\n- Good for world totals, like total mobs killed.\n- Stored in world data.\n\nLocals:\n- Per-player and per-folder.\n- Each player has their own value in that folder.\n- Good for personal scores, like player's own kills.\n- Stored per folder.\n\nWhen Creating: Choose scope.\nIn Functions: Modify either.\nIn Placeholders: {varname} works for both (uses local if exists, else global).\n\nExample: Global 'total kills' adds 1 on any death. Local 'my kills' adds 1 per player.\n\nNewbie: Globals = static/shared, locals = instance/per-user. Like global vs local vars in functions.")
        .body("§bTips for Newbies:\n- Start small: One folder, one function.\n- Test in creative: Use /sync:menu, trigger events.\n- Use messages to debug: Add 'Test triggered!' to see if it runs.\n- Variables start at 0; reset if needed.\n- IDs: Use full like 'minecraft:dirt', no spaces.\n- Errors: If name exists or empty, menu tells you.\n- Multiplayer: Works for all, locals per-player.\n- For cancellation, use 'before' hooks.\n- UIs replace direct actions; use for choices.\n\nExamples:\n1. Auto-Heal: Hook Item Use (after), Inputs: golden_apple, Condition: isInWater, Message: 'Healed in water!', Command: effect @s instant_health 1 1.\n2. Break Counter: Hook Player Break Block (after), Inputs: any, Variable: add 1 to local 'breaks', Message: 'Breaks: {breaks}'.\n3. Mob Farm: Hook Entity Die (after), Inputs: minecraft:zombie, Condition: KilledByPlayer, Variable: add 1 to global 'zombie kills', Command: give {player} rotten_flesh 1.\n4. Protected Area: Hook Player Place Block (before), Var Condition: cancel if '{protected} == 1'.\n5. Shop UI: Hook Item Use (after), Inputs: emerald, Custom UI: ActionForm 'Buy' with buttons 'Sword (setvar:local:coins:-10; command:give @s iron_sword)', 'Armor (setvar:local:coins:-20; command:give @s iron_chestplate)'.\n\nExperiment! If stuck, delete and retry.")
*/
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// IGNORE
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################
// #################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################


/* ------------------------------
   code:0017 [Folder Management (CRUD)]
   ------------------------------ */
function openFolderList(player) {
    const folders = getAllFolders();
    const form = new ActionFormData().title("§aYour Folders");
    if (folders.length === 0) {
        form.body("§cYou have no folders.").button("§eCreate One");
        form.show(player).then(res => { if (res.selection === 0) openCreateFolder(player); });
        return;
    }
    folders.forEach(folder => form.button(`${folder.name} ${folder.enabled !== false ? "§a[On]" : "§c[Off]"}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === folders.length) { openMainMenu(player); return; }
        const selectedFolder = folders[res.selection];
        openFolderEditor(player, selectedFolder.name);
    });
}
function openCreateFolder(player) {
    const form = new ModalFormData()
        .title("§eCreate Folder")
        .textField("Enter folder name:", "Folder name");
    form.show(player).then(res => {
        if (res.canceled) { openMainMenu(player); return; }
        const name = (res.formValues[0] ?? "").trim();
        let names = getFolders();
        if (name.length < 1) { player.sendMessage("§cFolder name must be at least 1 character."); openCreateFolder(player); return; }
        if (names.includes(name)) { player.sendMessage("§cA folder with that name already exists."); openCreateFolder(player); return; }
        const data = getData();
        data.folders.push({ name, functions: [], variables: { local: {}, }, randomVars: {} });
        setData(data);
        player.sendMessage(`§aFolder '${name}' created successfully.`);
        openMainMenu(player);
    });
}
function openDeleteFolder(player) {
    const arr = getFolders();
    const form = new ActionFormData().title("§cDelete Folder");
    if (arr.length === 0) {
        form.body("§cYou have no folders to delete.").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => { if (res.selection === 0) openMainMenu(player); });
        return;
    }
    arr.forEach(f => form.button(f));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === arr.length) { openMainMenu(player); return; }
        const folder = arr[res.selection];
        openDeleteConfirm(player, folder);
    });
}
function openDeleteConfirm(player, folder) {
    const form = new ActionFormData()
        .title("Confirm Delete")
        .body(`§eDo you really want to delete '${folder}'?`)
        .button("§aYes")
        .button("§cNo");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const data = getData();
            data.folders = data.folders.filter(f => f.name !== folder);
            setData(data);
            player.sendMessage(`§aFolder '${folder}' deleted successfully.`);
            openMainMenu(player);
        } else {
            openDeleteFolder(player);
        }
    });
}
function openRenameFolder(player) {
    const arr = getFolders();
    const form = new ActionFormData().title("Rename Folder");
    if (arr.length === 0) {
        form.body("§eYou have no folders to rename.").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => { if (res.selection === 0) openMainMenu(player); });
        return;
    }
    arr.forEach(f => form.button(f));
    form.button("⬅ Back");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === arr.length) { openMainMenu(player); return; }
        const folder = arr[res.selection];
        const form2 = new ModalFormData().title("Rename Folder").textField(`Current name: ${folder}\nEnter new name:`, "New name");
        form2.show(player).then(r => {
            if (r.canceled) { openMainMenu(player); return; }
            const newName = (r.formValues[0] ?? "").trim();
            const data = getData();
            if (newName.length < 1) { player.sendMessage("§cFolder name must be at least 1 character."); openRenameFolder(player); return; }
            if (data.folders.some(f => f.name === newName)) { player.sendMessage("§cA folder with that name already exists."); openRenameFolder(player); return; }
            const f = data.folders.find(x => x.name === folder);
            if (!f) { openMainMenu(player); return; }
            f.name = newName;
            setData(data);
            player.sendMessage(`§aFolder '${folder}' renamed to '${newName}'.`);
            openMainMenu(player);
        });
    });
}

/* ------------------------------
   code:0018 [Folder Editor and Functions List]
   ------------------------------ */
function openFolderEditor(player, folderName) {
    const folder = folderByName(folderName);
    folder.enabled = folder.enabled ?? true;
    const form = new ActionFormData()
        .title(`§bEditing: §e${folderName}`)
        .button(":switch_dpad_down: §6§lFunctions List")
        .button(":switch_start: §eCreate Function")
        .button(" §eDelete Function")
        .button(":switch_start: §eCreate Variable")
        .button(" §eDelete Variable")
        .button(":switch_start: §eCreate Random Var")
        .button(" §eDelete Random Var")
        .button(":ps4_start: §eEdit Random Var")
        .button(":switch_stick_right: §eReset Variable Storage")
        .button(folder.enabled ? " §cDisable Folder" : " §aEnable Folder")
        .button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openFunctionsList(player, folderName); break;
            case 1: openCreateFunctionName(player, folderName); break;
            case 2: openDeleteFunction(player, folderName); break;
            case 3: openAddVariable(player, folderName); break;
            case 4: openDeleteVariable(player, folderName); break;
            case 5: openCreateRandomVar(player, folderName); break;
            case 6: openDeleteRandomVar(player, folderName); break;
            case 7: openEditRandomVar(player, folderName); break;   // ← NEW CASE
            case 8: openResetVariableStorage(player, folderName); break;
            case 9: folder.enabled = !folder.enabled; saveFolder(folder); openFolderEditor(player, folderName); break;
            case 10: openFolderList(player); break;
        }
    });
}
function openEditRandomVar(player, folderName) {
    const folder = folderByName(folderName);
    const randoms = Object.keys(folder?.randomVars || {});
    const form = new ActionFormData().title("§eEdit Random Variable");
    if (randoms.length === 0) {
        form.body("§7No random variables to edit.").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => { if (res.selection === 0) openFolderEditor(player, folderName); });
        return;
    }
    randoms.forEach(r => form.button(`${r}\n§7${folder.randomVars[r]}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === randoms.length) { openFolderEditor(player, folderName); return; }
        const name = randoms[res.selection];
        const currentDef = folder.randomVars[name];

        const modal = new ModalFormData()
            .title(`§eEdit Random Var: ${name}`)
            .textField("Definition (e.g. 1-100, stone,dirt, 10%,50%,40%):", currentDef);
        modal.show(player).then(r => {
            if (r.canceled) { openEditRandomVar(player, folderName); return; }
            const newDef = (r.formValues[0] ?? "").trim();
            if (newDef.length === 0) {
                player.sendMessage("§cDefinition cannot be empty.");
                openEditRandomVar(player, folderName);
                return;
            }
            folder.randomVars[name] = newDef;
            saveFolder(folder);
            player.sendMessage(`§aRandom variable '§e${name}§a' updated!\n§7New definition: §f${newDef}`);
            openFolderEditor(player, folderName);
        });
    });
}
function openCreateRandomVar(player, folderName) {
    const form = new ModalFormData()
        .title("§eCreate Random Variable")
        .textField("Variable name:", "myRandom")
        .textField("Definition (e.g., 1-100, dirt,cobblestone, 1,10%,40,2%):", "1-100");
    form.show(player).then(res => {
        if (res.canceled) { openFolderEditor(player, folderName); return; }
        const name = (res.formValues[0] ?? "").trim();
        const def = (res.formValues[1] ?? "").trim();
        if (name.length < 1) { player.sendMessage("§cName must be at least 1 character."); openCreateRandomVar(player, folderName); return; }
        if (def.length < 1) { player.sendMessage("§cDefinition required."); openCreateRandomVar(player, folderName); return; }
        const folder = ensureFolder(folderName);
        folder.randomVars ??= {};
        if (folder.randomVars[name]) { player.sendMessage("§cName already exists."); openCreateRandomVar(player, folderName); return; }
        folder.randomVars[name] = def;
        saveFolder(folder);
        player.sendMessage(`§aRandom variable '${name}' created with definition '${def}'.`);
        openFolderEditor(player, folderName);
    });
}
function openDeleteRandomVar(player, folderName) {
    const folder = folderByName(folderName);
    const randoms = Object.keys(folder?.randomVars || {});
    const form = new ActionFormData().title("§cDelete Random Var");
    if (randoms.length === 0) {
        form.body("§eNo random vars to delete.").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => { if (res.selection === 0) openFolderEditor(player, folderName); });
        return;
    }
    randoms.forEach(r => form.button(r));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === randoms.length) { openFolderEditor(player, folderName); return; }
        const name = randoms[res.selection];
        const confirm = new ActionFormData().title("Confirm Delete").body(`Delete '${name}'?`).button("§aYes").button("§cNo");
        confirm.show(player).then(r => {
            if (r.canceled) return;
            if (r.selection === 0) {
                delete folder.randomVars[name];
                saveFolder(folder);
                player.sendMessage(`§aRandom var '${name}' deleted.`);
                openDeleteRandomVar(player, folderName);
            } else {
                openDeleteRandomVar(player, folderName);
            }
        });
    });
}
function openFunctionsList(player, folderName) {
    const folder = folderByName(folderName);
    const form = new ActionFormData().title(`§6Functions: §e${folderName}`);
    if (!folder || folder.functions.length === 0) {
        form.body("§eNo functions yet.").button("§aCreate Function").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => {
            if (res.canceled) return;
            if (res.selection === 0) openCreateFunctionName(player, folderName);
            if (res.selection === 1) openFolderEditor(player, folderName);
        });
        return;
    }
    folder.functions.forEach(fn => form.button(`${fn.name} ${fn.enabled !== false ? "§a[On]" : "§c[Off]"}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === folder.functions.length) { openFolderEditor(player, folderName); return; }
        const fn = folder.functions[res.selection];
        openFunctionEditor(player, folderName, fn.name);
    });
}
function openCreateFunctionName(player, folderName) {
    const form = new ModalFormData().title("§aCreate Function").textField("Function name:", "myFunction");
    form.show(player).then(res => {
        if (res.canceled) { openFolderEditor(player, folderName); return; }
        const name = (res.formValues[0] ?? "").trim();
        const folder = folderByName(folderName);
        if (!folder) { openFolderList(player); return; }
        if (name.length < 1) { player.sendMessage("§cFunction name must be at least 1 character."); openCreateFunctionName(player, folderName); return; }
        if (folder.functions.some(f => f.name === name)) { player.sendMessage("§cFunction name already exists."); openCreateFunctionName(player, folderName); return; }
        openSelectHook(player, folderName, name);
    });
}
function openSelectHook(player, folderName, functionName) {
    const form = new ActionFormData().title("§eSelect Hook");
    HOOKS.forEach(h => form.button(h.label));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === HOOKS.length) { openFolderEditor(player, folderName); return; }
        const hook = HOOKS[res.selection];
        openHookInputs(player, folderName, functionName, hook);
    });
}
function openHookInputs(player, folderName, functionName, hook) {
    if (hook.id === "onHold") {
        const form = new ModalFormData()
            .title("§eConfigure: On Hold")
            .textField("§fItems§r (comma-separated or §bany§r)", "diamond_sword, bow, any")
            .textField("§fRun every X ticks§r ticks §7(20 = 1 second)", "20");
        form.show(player).then(res => {
            if (res.canceled) {
                openSelectHook(player, folderName, functionName);
                return;
            }
            const items = String(res.formValues[0] ?? "any").trim();
            const intervalStr = String(res.formValues[1] ?? "20").trim();
            const interval = parseInt(intervalStr);
            if (isNaN(interval) || interval < 1) {
                player.sendMessage("§cInterval must be a positive number (ticks).");
                openHookInputs(player, folderName, functionName, hook);
                return;
            }
            const inputs = {
                items: items,
                interval: intervalStr
            };
            openConditionSelection(player, folderName, functionName, hook, inputs, "after", []);
        });
        return;
    }
    if (!hook.inputs || hook.inputs.length === 0) {
        openEventSelection(player, folderName, functionName, hook, {});
        return;
    }
    const form = new ModalFormData().title(`§eConfigure: ${hook.label}`);
    hook.inputs.forEach(inp => form.textField(`${inp.label}`, inp.placeholder));
    form.show(player).then(res => {
        if (res.canceled) {
            openSelectHook(player, folderName, functionName);
            return;
        }
        const values = {};
        hook.inputs.forEach((inp, i) => {
            values[inp.id] = String(res.formValues[i] ?? "").trim();
        });
        openEventSelection(player, folderName, functionName, hook, values);
    });
}
function openEventSelection(player, folderName, functionName, hook, inputs) {
    // If the hook has no events defined → go straight to conditions
    if (!hook.events || hook.events.length === 0) {
        openConditionSelection(player, folderName, functionName, hook, inputs, null);
        return;
    }

    // If the hook only supports ONE event → apply it automatically (no menu needed)
    if (hook.events.length === 1) {
        const onlyEvent = hook.events[0] === "before" ? "before" : "after";
        openConditionSelection(player, folderName, functionName, hook, inputs, onlyEvent);
        return;
    }

    // Otherwise: show Before / After selection menu
    const form = new ActionFormData()
        .title("Select Event Phase")
        .button("Before")
        .button("After")
        .button("Back");

    form.show(player).then(res => {
        if (res.canceled || res.selection === 2) {
            openHookInputs(player, folderName, functionName, hook);
            return;
        }

        const eventSel = res.selection === 0 ? "before" : "after";
        openConditionSelection(player, folderName, functionName, hook, inputs, eventSel);
    });
}
function openDeleteFunction(player, folderName) {
    const folder = folderByName(folderName);
    const form = new ActionFormData().title("§cDelete Function");
    if (!folder || folder.functions.length === 0) {
        form.body("§eNo functions to delete.").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => { if (res.selection === 0) openFolderEditor(player, folderName); });
        return;
    }
    folder.functions.forEach(fn => form.button(fn.name));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === folder.functions.length) { openFolderEditor(player, folderName); return; }
        const fn = folder.functions[res.selection];
        const confirm = new ActionFormData().title("Confirm Delete").body(`Delete function '${fn.name}'?`).button("§aYes").button("§cNo");
        confirm.show(player).then(r => {
            if (r.canceled) return;
            if (r.selection === 0) {
                folder.functions = folder.functions.filter(f => f.name !== fn.name);
                saveFolder(folder);
                player.sendMessage(`§aFunction '${fn.name}' deleted.`);
                openFolderEditor(player, folderName);
            } else {
                openDeleteFunction(player, folderName);
            }
        });
    });
}
function openFunctionEditor(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    fn.enabled = fn.enabled ?? true;
    const hookLabel = HOOKS.find(h => h.id === fn.hook)?.label ?? fn.hook;
    const eventLabel = fn.event ? ` (${fn.event})` : "";
    const form = new ActionFormData()
        .title(`§6Function: §e${fn.name}`)
        .body(`§7Type: ${hookLabel}${eventLabel}`)
        .button(":tip_touch_inventory: §eRename")
        .button(":ps4_select: §eFunction Type")
        .button(" §eEvents")
        .button(" §eConditions")
        .button(":touch_chat: §eMessages")
        .button(" §eCommands")
        .button(":furnace: §eVariables")
        .button(":ps4_start: §eEdit Items/Blocks/Entity")
        .button(":xbox_start: §aCustom UI")
        .button(" §aVar Conditions")
        .button(fn.enabled ? " §cDisable Function" : " §aEnable Function")
        .button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openRenameFunction(player, folderName, fn.name); break;
            case 1: openSwitchHook(player, folderName, fn.name); break;
            case 2: openSwitchEvent(player, folderName, fn.name); break;
            case 3: openEditConditions(player, folderName, fn.name); break;
            case 4: openMessagesMenu(player, folderName, fn.name); break;
            case 5: openCommandsMenu(player, folderName, fn.name); break;
            case 6: openVariablesMenu(player, folderName, fn.name); break;
            case 7: openEditInputs(player, folderName, fn.name); break;
            case 8: openUIsMenu(player, folderName, fn.name); break;
            case 9: openVarConditionsMenu(player, folderName, fn.name); break;
            case 10: fn.enabled = !fn.enabled; saveFolder(folder); openFunctionEditor(player, folderName, fn.name); break;
            case 11: openFunctionsList(player, folderName); break;
        }
    });
}

/* ------------------------------
   code:0019 [Condition & Input Editors]
   ------------------------------ */
function openConditionSelection(player, folderName, functionName, hook, inputs, eventSel, existing = []) {
    const form = new ActionFormData().title("§eConditions");
    CONDITIONS.forEach((cond) => {
        let isSelected = false;
        let displayLabel = cond.label;
        if (cond.type === "toggle") {
            isSelected = existing.includes(cond.id);
        } else {
            const found = existing.find(s => s.startsWith(cond.id + " "));
            if (found) {
                isSelected = true;
                const param = found.slice((cond.id + " ").length);
                displayLabel += ` ${param}`;
            } else {
                displayLabel += " [set value]";
            }
        }
        form.button(isSelected ? `§e§l${displayLabel}` : displayLabel);
    });
    form.button("§aConfirm").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        const count = CONDITIONS.length;
        if (res.selection === count) {
            const folder = folderByName(folderName);
            if (!folder) {
                openFolderList(player);
                return;
            }
            folder.functions.push({
                name: functionName,
                hook: hook.id,
                inputs,
                event: eventSel,
                conditions: existing,
                messages: [],
                commands: [],
                variableActions: fnSafeEmptyArray(),
                uis: [],
                varConditions: [],
                enabled: true
            });
            const createdFn = folder.functions[folder.functions.length - 1];
            createdFn.variableActions ??= [];
            createdFn.messages ??= [];
            createdFn.commands ??= [];
            createdFn.uis ??= [];
            createdFn.varConditions ??= [];
            saveFolder(folder);
            player.sendMessage(`§aFunction '${functionName}' created.`);
            openFunctionsList(player, folderName);
            return;
        }
        if (res.selection === count + 1) {
            openEventSelection(player, folderName, functionName, hook, inputs);
            return;
        }
        const cond = CONDITIONS[res.selection];
        if (cond.type === "toggle") {
            const idx = existing.indexOf(cond.id);
            if (idx >= 0) existing.splice(idx, 1);
            else existing.push(cond.id);
            openConditionSelection(player, folderName, functionName, hook, inputs, eventSel, existing);
            return;
        }
        const foundIdx = existing.findIndex(s => s.startsWith(cond.id + " "));
        let currentParam = cond.example || "";
        if (foundIdx >= 0) {
            currentParam = existing[foundIdx].slice((cond.id + " ").length);
        }
        const modal = new ModalFormData()
            .title(`Set ${cond.label}`)
            .textField("Enter value:", currentParam);
        modal.show(player).then(mr => {
            if (mr.canceled) {
                openConditionSelection(player, folderName, functionName, hook, inputs, eventSel, existing);
                return;
            }
            const newParam = String(mr.formValues[0] ?? "").trim();
            if (newParam.length === 0) {
                if (foundIdx >= 0) existing.splice(foundIdx, 1);
            } else {
                const full = `${cond.id} ${newParam}`;
                if (foundIdx >= 0) existing[foundIdx] = full;
                else existing.push(full);
            }
            openConditionSelection(player, folderName, functionName, hook, inputs, eventSel, existing);
        });
    });
}
function fnSafeEmptyArray() { return []; }
function openEditInputs(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    let current = "";
    if (fn.hook === "itemUse") current = fn.inputs?.items ?? "any";
    if (fn.hook === "playerBreakBlock" || fn.hook === "playerPlaceBlock") current = fn.inputs?.blocks ?? "any";
    const form = new ModalFormData()
        .title("Edit Items/Blocks/Entity")
        .textField("Enter comma-separated list (or 'any'):", current);
    form.show(player).then(res => {
        if (res.canceled) { openFunctionEditor(player, folderName, functionName); return; }
        const newVal = String(res.formValues[0] ?? "").trim();
        if (fn.hook === "itemUse") fn.inputs.items = newVal;
        if (fn.hook === "playerBreakBlock" || fn.hook === "playerPlaceBlock") fn.inputs.blocks = newVal;
        saveFolder(folder);
        player.sendMessage(`§aInputs updated to: ${newVal}`);
        openFunctionEditor(player, folderName, functionName);
    });
}
function openRenameFunction(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    const form = new ModalFormData().title("Rename Function").textField(`Current: ${fn.name}\nEnter new name:`, "New name");
    form.show(player).then(res => {
        if (res.canceled) { openFunctionEditor(player, folderName, functionName); return; }
        const newName = (res.formValues[0] ?? "").trim();
        if (newName.length < 1) { player.sendMessage("§cFunction name must be at least 1 character."); openRenameFunction(player, folderName, functionName); return; }
        if (folder.functions.some(f => f.name === newName)) { player.sendMessage("§cFunction name already exists."); openRenameFunction(player, folderName, functionName); return; }
        fn.name = newName;
        saveFolder(folder);
        player.sendMessage(`§aFunction renamed to '${newName}'.`);
        openFunctionEditor(player, folderName, newName);
    });
}
function openSwitchHook(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    const form = new ActionFormData().title("Switch Hook");
    HOOKS.forEach(h => form.button(h.label));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === HOOKS.length) { openFunctionEditor(player, folderName, functionName); return; }
        const hook = HOOKS[res.selection];
        fn.hook = hook.id;
        fn.inputs = {};
        fn.event = hook.events && hook.events.length ? hook.events[0] : null;
        fn.conditions = [];
        saveFolder(folder);
        player.sendMessage(`§aHook switched to '${hook.label}'.`);
        openFunctionEditor(player, folderName, functionName);
    });
}
function openSwitchEvent(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    const hook = HOOKS.find(h => h.id === fn.hook);
    if (!hook || !hook.events || hook.events.length === 0) { openFunctionEditor(player, folderName, functionName); return; }
    const form = new ActionFormData().title("Switch Event");
    hook.events.forEach(e => form.button(e));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === hook.events.length) { openFunctionEditor(player, folderName, functionName); return; }
        fn.event = hook.events[res.selection];
        saveFolder(folder);
        player.sendMessage(`§aEvent set to '${fn.event}'.`);
        openFunctionEditor(player, folderName, functionName);
    });
}
function openEditConditions(player, folderName, functionName, current = null) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    const selected = current ?? [...(fn.conditions || [])];
    const form = new ActionFormData().title("Conditions");
    CONDITIONS.forEach((cond, i) => {
        let isSelected = false;
        let displayLabel = cond.label;
        if (cond.type === "toggle") {
            isSelected = selected.includes(cond.id);
        } else {
            const found = selected.find(s => s.startsWith(cond.id + " "));
            if (found) {
                isSelected = true;
                const param = found.slice((cond.id + " ").length);
                displayLabel += ` ${param}`;
            } else {
                displayLabel += ` [set value]`;
            }
        }
        form.button(isSelected ? `§e§l${displayLabel}` : displayLabel);
    });
    form.button("§aConfirm").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        const count = CONDITIONS.length;
        if (res.selection === count) {
            fn.conditions = selected;
            saveFolder(folder);
            player.sendMessage("§aConditions updated.");
            openFunctionEditor(player, folderName, functionName);
            return;
        }
        if (res.selection === count + 1) { openFunctionEditor(player, folderName, functionName); return; }
        const cond = CONDITIONS[res.selection];
        if (cond.type === "toggle") {
            const idx = selected.indexOf(cond.id);
            if (idx >= 0) selected.splice(idx, 1); else selected.push(cond.id);
            openEditConditions(player, folderName, functionName, selected);
            return;
        }
        const foundIdx = selected.findIndex(s => s.startsWith(cond.id + " "));
        let currentParam = cond.example;
        if (foundIdx >= 0) {
            currentParam = selected[foundIdx].slice((cond.id + " ").length);
        }
        const modal = new ModalFormData().title(`Set ${cond.label}`).textField("Enter value:", currentParam);
        modal.show(player).then(mr => {
            if (mr.canceled) { openEditConditions(player, folderName, functionName, selected); return; }
            const newParam = String(mr.formValues[0] ?? "").trim();
            if (newParam.length === 0) {
                if (foundIdx >= 0) selected.splice(foundIdx, 1);
            } else {
                const full = `${cond.id} ${newParam}`;
                if (foundIdx >= 0) selected[foundIdx] = full;
                else selected.push(full);
            }
            openEditConditions(player, folderName, functionName, selected);
        });
    });
}

/* ------------------------------
   code:0020 [Messages & Commands Editors]
   ------------------------------ */
function ensureMessageFormat(msg) {
    if (typeof msg === "string") return { parts: [msg] };
    if (!msg.parts) msg.parts = [];
    return msg;
}
function ensureCommandFormat(cmd) {
    if (typeof cmd === "string") return { mode: "console", parts: [cmd] };
    if (typeof cmd.command === "string") {
        cmd.parts = [cmd.command];
        delete cmd.command;
    }
    if (!cmd.parts) cmd.parts = [];
    return cmd;
}

function buildVarHintForHook(hookId, prefix) {
    const vars = HOOK_VARIABLES[hookId] || [];
    const list = [...vars, "plus any custom {variables} you define"];
    return `${prefix}\n• ${list.join("\n• ")}`;
}

function openMessagesMenu(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    if (!fn.messages) fn.messages = [];
    fn.messages = fn.messages.map(ensureMessageFormat);
    const form = new ActionFormData().title("Messages").button("§aAdd Message");
    if (fn.messages.length === 0) form.body("§7No messages defined.");
    fn.messages.forEach((m, i) => {
        const preview = m.parts.join("").slice(0, 20);
        form.button(`Msg ${i + 1}: ${preview}...`);
    });
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            fn.messages.push({ parts: [] });
            saveFolder(folder);
            openEditMessageParts(player, folderName, functionName, fn.messages.length - 1);
            return;
        }
        const idx = res.selection - 1;
        if (idx >= 0 && idx < fn.messages.length) {
            openEditMessageParts(player, folderName, functionName, idx);
        } else {
            openFunctionEditor(player, folderName, functionName);
        }
    });
}
function openEditMessageParts(player, folderName, functionName, msgIdx) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openMessagesMenu(player, folderName, functionName); return; }
    const msg = fn.messages[msgIdx];
    if (!msg) { openMessagesMenu(player, folderName, functionName); return; }
    if (!msg.parts) msg.parts = [];
    const form = new ActionFormData().title("Edit Message Parts").button("§aAdd Part");
    if (msg.parts.length === 0) form.body("§7No parts yet. Add to build the message.");
    msg.parts.forEach((part, i) => form.button(`Part ${i + 1}: ${part.slice(0, 20)}...`));
    form.button("§cDelete Message").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const add = new ModalFormData()
                .title("Add Part")
                .textField(
                    buildVarHintForHook(fn.hook, "Message part (will be concatenated):"),
                    "Continue text here"
                );
            add.show(player).then(r => {
                if (r.canceled) { openEditMessageParts(player, folderName, functionName, msgIdx); return; }
                const part = String(r.formValues[0] ?? "").trim();
                if (part.length === 0) { player.sendMessage("§cPart cannot be empty."); openEditMessageParts(player, folderName, functionName, msgIdx); return; }
                msg.parts.push(part);
                saveFolder(folder);
                openEditMessageParts(player, folderName, functionName, msgIdx);
            });
            return;
        }
        const partIdx = res.selection - 1;
        if (partIdx >= 0 && partIdx < msg.parts.length) {
            const current = msg.parts[partIdx];
            const edit = new ActionFormData().title("Edit Part").body(current).button("§eEdit").button("§cDelete").button(":switch_dpad_left: §cBack");
            edit.show(player).then(er => {
                if (er.canceled) return;
                if (er.selection === 0) {
                    const mod = new ModalFormData().title("Edit Part").textField("Update:", current);
                    mod.show(player).then(mr => {
                        if (mr.canceled) { openEditMessageParts(player, folderName, functionName, msgIdx); return; }
                        const newPart = String(mr.formValues[0] ?? "").trim();
                        if (newPart.length === 0) { player.sendMessage("§cPart cannot be empty."); openEditMessageParts(player, folderName, functionName, msgIdx); return; }
                        msg.parts[partIdx] = newPart;
                        saveFolder(folder);
                        openEditMessageParts(player, folderName, functionName, msgIdx);
                    });
                } else if (er.selection === 1) {
                    msg.parts.splice(partIdx, 1);
                    saveFolder(folder);
                    openEditMessageParts(player, folderName, functionName, msgIdx);
                } else {
                    openEditMessageParts(player, folderName, functionName, msgIdx);
                }
            });
            return;
        }
        if (res.selection === msg.parts.length + 1) {
            const confirm = new ActionFormData().title("Delete Message").body("Delete this entire message?").button("§aYes").button("§cNo");
            confirm.show(player).then(cr => {
                if (cr.canceled || cr.selection === 1) { openEditMessageParts(player, folderName, functionName, msgIdx); return; }
                fn.messages.splice(msgIdx, 1);
                saveFolder(folder);
                openMessagesMenu(player, folderName, functionName);
            });
            return;
        }
        openMessagesMenu(player, folderName, functionName);
    });
}

function openCommandsMenu(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    if (!fn.commands) fn.commands = [];
    // Migrate old format
    fn.commands = fn.commands.map(ensureCommandFormat);
    const form = new ActionFormData().title("Commands").button("§aAdd Command");
    if (fn.commands.length === 0) form.body("§7No commands defined.");
    fn.commands.forEach((c, i) => {
        const preview = c.parts.join("").slice(0, 20);
        form.button(`Cmd ${i + 1} [${c.mode}]: ${preview}...`);
    });
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const addMode = new ModalFormData().title("Add Command").dropdown("Executor", ["console", "player"]);
            addMode.show(player).then(rm => {
                if (rm.canceled) { openCommandsMenu(player, folderName, functionName); return; }
                const mode = ["console", "player"][rm.formValues[0]] ?? "console";
                fn.commands.push({ mode, parts: [] });
                saveFolder(folder);
                openEditCommandParts(player, folderName, functionName, fn.commands.length - 1);
            });
            return;
        }
        const idx = res.selection - 1;
        if (idx >= 0 && idx < fn.commands.length) {
            openEditCommandParts(player, folderName, functionName, idx);
        } else {
            openFunctionEditor(player, folderName, functionName);
        }
    });
}
function openEditCommandParts(player, folderName, functionName, cmdIdx) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openCommandsMenu(player, folderName, functionName); return; }
    const cmd = fn.commands[cmdIdx];
    if (!cmd) { openCommandsMenu(player, folderName, functionName); return; }
    if (!cmd.parts) cmd.parts = [];
    let bodyText = `Executor: ${cmd.mode}`;
    const form = new ActionFormData().title("Edit Command Parts").body(bodyText).button("§aAdd Part").button("§eChange Executor");
    if (cmd.parts.length === 0) {
        bodyText += "\n§7No parts yet. Add to build the command.";
        form.body(bodyText);
    }
    cmd.parts.forEach((part, i) => form.button(`Part ${i + 1}: ${part.slice(0, 20)}...`));
    form.button("§cDelete Command").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const add = new ModalFormData()
                .title("Add Part")
                .textField(
                    buildVarHintForHook(fn.hook, "Command part (will be concatenated):"),
                    "continue command"
                );
            add.show(player).then(r => {
                if (r.canceled) { openEditCommandParts(player, folderName, functionName, cmdIdx); return; }
                const part = String(r.formValues[0] ?? "").trim();
                if (part.length === 0) { player.sendMessage("§cPart cannot be empty."); openEditCommandParts(player, folderName, functionName, cmdIdx); return; }
                cmd.parts.push(part);
                saveFolder(folder);
                openEditCommandParts(player, folderName, functionName, cmdIdx);
            });
            return;
        }
        if (res.selection === 1) {
            const changeMode = new ModalFormData().title("Change Executor").dropdown("Executor", ["console", "player"], cmd.mode === "console" ? 0 : 1);
            changeMode.show(player).then(rm => {
                if (rm.canceled) { openEditCommandParts(player, folderName, functionName, cmdIdx); return; }
                cmd.mode = ["console", "player"][rm.formValues[0]] ?? "console";
                saveFolder(folder);
                openEditCommandParts(player, folderName, functionName, cmdIdx);
            });
            return;
        }
        const partIdx = res.selection - 2;
        if (partIdx >= 0 && partIdx < cmd.parts.length) {
            const current = cmd.parts[partIdx];
            const edit = new ActionFormData().title("Edit Part").body(current).button("§eEdit").button("§cDelete").button(":switch_dpad_left: §cBack");
            edit.show(player).then(er => {
                if (er.canceled) return;
                if (er.selection === 0) {
                    const mod = new ModalFormData().title("Edit Part").textField("Update:", current);
                    mod.show(player).then(mr => {
                        if (mr.canceled) { openEditCommandParts(player, folderName, functionName, cmdIdx); return; }
                        const newPart = String(mr.formValues[0] ?? "").trim();
                        if (newPart.length === 0) { player.sendMessage("§cPart cannot be empty."); openEditCommandParts(player, folderName, functionName, cmdIdx); return; }
                        cmd.parts[partIdx] = newPart;
                        saveFolder(folder);
                        openEditCommandParts(player, folderName, functionName, cmdIdx);
                    });
                } else if (er.selection === 1) {
                    cmd.parts.splice(partIdx, 1);
                    saveFolder(folder);
                    openEditCommandParts(player, folderName, functionName, cmdIdx);
                } else {
                    openEditCommandParts(player, folderName, functionName, cmdIdx);
                }
            });
            return;
        }
        if (res.selection === cmd.parts.length + 2) {
            const confirm = new ActionFormData().title("Delete Command").body("Delete this entire command?").button("§aYes").button("§cNo");
            confirm.show(player).then(cr => {
                if (cr.canceled || cr.selection === 1) { openEditCommandParts(player, folderName, functionName, cmdIdx); return; }
                fn.commands.splice(cmdIdx, 1);
                saveFolder(folder);
                openCommandsMenu(player, folderName, functionName);
            });
            return;
        }
        openCommandsMenu(player, folderName, functionName);
    });
}

/* ------------------------------
   code:0021 [Variables Editor]
   ------------------------------ */
function openVariablesMenu(player, folderName, functionName) {
    const data = getData();
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    const globals = Object.keys(data.globals || {});
    const locals = Object.keys((folder.variables && folder.variables.local) || {});
    const form = new ActionFormData().title("Variables").button("§aAdd Variable").button("§eModify Variable");
    form.body(`§6Globals: §f${globals.length ? globals.join(", ") : "None"}\n§bLocals: §f${locals.length ? locals.join(", ") : "None"}`);
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) { openAddVariable(player, folderName, functionName); return; }
        if (res.selection === 1) { openModifyVariableSelect(player, folderName, functionName); return; }
        openFunctionEditor(player, folderName, functionName);
    });
}
function openAddVariable(player, folderName, functionName = null) {
    const scopeOptions = ["global", "local"];
    const form = new ModalFormData()
        .title("Add Variable")
        .textField("Variable name:", "counter")
        .dropdown("Scope", scopeOptions);
    form.show(player).then(res => {
        if (res.canceled) {
            functionName ? openVariablesMenu(player, folderName, functionName) : openFolderEditor(player, folderName);
            return;
        }
        const name = String(res.formValues[0] ?? "").trim();
        const scopeIndex = res.formValues[1] ?? 0;
        const scope = scopeOptions[scopeIndex] ?? "global";
        if (name.length < 1) {
            player.sendMessage("§cVariable name must be at least 1 character.");
            openAddVariable(player, folderName, functionName);
            return;
        }
        const data = getData();
        const folder = folderByName(folderName);
        if (scope === "global") {
            if (!data.globals) data.globals = {};
            if (data.globals[name] === undefined) data.globals[name] = 0;
            setData(data);
        } else {
            if (!folder.variables) folder.variables = { local: {} };
            if (!folder.variables.local[name]) folder.variables.local[name] = {};
            setData({ ...getData(), folders: getData().folders.map(f => f.name === folder.name ? folder : f) });
        }
        player.sendMessage(`§aVariable '${name}' (${scope}) added.`);
        functionName ? openVariablesMenu(player, folderName, functionName) : openFolderEditor(player, folderName);
    });
}
function openDeleteVariable(player, folderName) {
    const data = getData();
    const folder = folderByName(folderName);
    const globals = Object.keys(data.globals || {});
    const locals = Object.keys((folder.variables && folder.variables.local) || {});
    const form = new ActionFormData().title("§cDelete Variable");
    globals.forEach(g => form.button(`§6[Global] ${g}`));
    locals.forEach(l => form.button(`§b[Local] ${l}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        const total = globals.length + locals.length;
        if (res.selection === total) { openFolderEditor(player, folderName); return; }
        if (res.selection < globals.length) {
            const name = globals[res.selection];
            const confirm = new ActionFormData().title("Confirm").body(`Delete global '${name}'?`).button("§aYes").button("§cNo");
            confirm.show(player).then(r => {
                if (r.canceled) return;
                if (r.selection === 0) {
                    delete data.globals[name];
                    setData(data);
                    player.sendMessage("§aGlobal deleted.");
                    openDeleteVariable(player, folderName);
                } else openDeleteVariable(player, folderName);
            });
        } else {
            const idx = res.selection - globals.length;
            const name = locals[idx];
            const confirm = new ActionFormData().title("Confirm").body(`Delete local '${name}'?`).button("§aYes").button("§cNo");
            confirm.show(player).then(r => {
                if (r.canceled) return;
                if (r.selection === 0) {
                    delete folder.variables.local[name];
                    saveFolder(folder);
                    player.sendMessage("§aLocal deleted.");
                    openDeleteVariable(player, folderName);
                } else openDeleteVariable(player, folderName);
            });
        }
    });
}
function openResetVariableStorage(player, folderName) {
    const confirm = new ActionFormData().title("Reset Variable Storage").body("Reset globals and locals to 0?").button("§aYes").button("§cNo").button(":switch_dpad_left: §cBack");
    confirm.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const data = getData();
            Object.keys(data.globals || {}).forEach(k => data.globals[k] = 0);
            const folder = folderByName(folderName);
            Object.keys((folder.variables && folder.variables.local) || {}).forEach(k => folder.variables.local[k] = {});
            saveFolder(folder);
            setData(data);
            player.sendMessage("§aVariable storage reset.");
            openFolderEditor(player, folderName);
        } else {
            openFolderEditor(player, folderName);
        }
    });
}
function openModifyVariableSelect(player, folderName, functionName) {
    const data = getData();
    const folder = folderByName(folderName);
    const globals = Object.keys(data.globals || {});
    const locals = Object.keys((folder.variables && folder.variables.local) || {});
    const form = new ActionFormData().title("Modify Variable");
    globals.forEach(g => form.button(`§6[Global] ${g}`));
    locals.forEach(l => form.button(`§b[Local] ${l}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        const total = globals.length + locals.length;
        if (res.selection === total) { openVariablesMenu(player, folderName, functionName); return; }
        let scope = "global"; let name = "";
        if (res.selection < globals.length) { scope = "global"; name = globals[res.selection]; }
        else { scope = "local"; name = locals[res.selection - globals.length]; }
        openModifyVariableAction(player, folderName, functionName, scope, name);
    });
}
function openModifyVariableAction(player, folderName, functionName, scope, varName) {
    const form = new ActionFormData().title(`Modify '${varName}' (${scope})`).button("§aAdd Value").button("§cRemove Value").button("§eSet Exact Value").button("§eReset to 0").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) openModifyVariableValue(player, folderName, functionName, scope, varName, "add");
        else if (res.selection === 1) openModifyVariableValue(player, folderName, functionName, scope, varName, "remove");
        else if (res.selection === 2) openModifyVariableValue(player, folderName, functionName, scope, varName, "set");
        else if (res.selection === 3) saveVariableAction(player, folderName, functionName, scope, varName, "reset", 0);
        else openVariablesMenu(player, folderName, functionName);
    });
}
function openModifyVariableValue(player, folderName, functionName, scope, varName, mode) {
    const form = new ModalFormData().title(`${mode === "add" ? "Add" : mode === "remove" ? "Remove" : "Set"} Value`).textField("Value (number):", "1");
    form.show(player).then(res => {
        if (res.canceled) { openModifyVariableAction(player, folderName, functionName, scope, varName); return; }
        const val = Number(String(res.formValues[0] ?? "0").trim());
        if (Number.isNaN(val)) { player.sendMessage("§cValue must be a number."); openModifyVariableValue(player, folderName, functionName, scope, varName, mode); return; }
        saveVariableAction(player, folderName, functionName, scope, varName, mode, val);
    });
}
function saveVariableAction(player, folderName, functionName, scope, varName, mode, value) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    if (!fn.variableActions) fn.variableActions = [];
    const existingIdx = fn.variableActions.findIndex(v => v.scope === scope && v.name === varName);
    const action = { scope, name: varName, mode, value };
    if (existingIdx >= 0) fn.variableActions[existingIdx] = action; else fn.variableActions.push(action);
    saveFolder(folder);
    player.sendMessage("§aVariable control updated.");
    openVariablesMenu(player, folderName, functionName);
}

/* ------------------------------
   code:0022 [Custom UI Editor]
   ------------------------------ */
function openUIsMenu(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    if (!fn.uis) fn.uis = [];
    const form = new ActionFormData().title("Custom UI").button("§aAdd UI");
    if (fn.uis.length === 0) form.body("§7No UIs defined.");
    fn.uis.forEach((u, i) => form.button(`${i + 1}. ${u.type} - ${u.title ?? "Untitled"}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            openCreateUIType(player, folderName, functionName);
            return;
        }
        const idx = res.selection - 1;
        if (idx >= 0 && idx < fn.uis.length) {
            openEditUI(player, folderName, functionName, idx);
            return;
        }
        openFunctionEditor(player, folderName, functionName);
    });
}
function openCreateUIType(player, folderName, functionName) {
    const form = new ActionFormData().title("Choose UI Type").button("ActionForm (buttons)").button("ModalForm (text fields)").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 2) { openUIsMenu(player, folderName, functionName); return; }
        const type = res.selection === 0 ? "action" : "modal";
        openCreateUIBase(player, folderName, functionName, type);
    });
}
function openCreateUIBase(player, folderName, functionName, type) {
    const form = new ModalFormData().title(`Create ${type} UI`).textField("Title:", "My UI").textField(type === "action" ? "Body:" : "Field placeholder (comma-separated for multiple):", type === "action" ? "Choose an option" : "Enter name,Enter message");
    form.show(player).then(res => {
        if (res.canceled) { openUIsMenu(player, folderName, functionName); return; }
        const title = String(res.formValues[0] ?? "").trim();
        const second = String(res.formValues[1] ?? "").trim();
        const folder = folderByName(folderName);
        const fn = folder?.functions.find(f => f.name === functionName);
        if (!fn) { openUIsMenu(player, folderName, functionName); return; }
        if (!fn.uis) fn.uis = [];
        if (type === "action") {
            const ui = {
                type: "action",
                title,
                bodyLines: second.split(",").map(l => l.trim()).filter(Boolean),
                buttons: []
            };
            fn.uis.push(ui);
            saveFolder(folder);
            openEditActionButtons(player, folderName, functionName, fn.uis.length - 1);
        } else {
            const fields = second.length ? second.split(",").map(s => s.trim()).filter(Boolean) : ["value"];
            const modalFields = fields.map(f => ({ label: f, placeholder: f, onSubmitAction: null }));
            const ui = { type: "modal", title, modalFields };
            fn.uis.push(ui);
            saveFolder(folder);
            openEditModalFieldActions(player, folderName, functionName, fn.uis.length - 1);
        }
    });
}
function openEditActionButtons(player, folderName, functionName, uiIndex) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openUIsMenu(player, folderName, functionName); return; }
    const ui = fn.uis[uiIndex];
    if (!ui) { openUIsMenu(player, folderName, functionName); return; }
    if (!ui.buttons || ui.buttons.length === 0) {
        const form = new ModalFormData().title("Buttons (comma-separated)").textField("Button labels:", "Option 1, Option 2");
        form.show(player).then(res => {
            if (res.canceled) { openUIsMenu(player, folderName, functionName); return; }
            const labels = String(res.formValues[0] ?? "").split(",").map(s => s.trim()).filter(Boolean);
            ui.buttons = labels.map(l => ({ label: l, action: { type: "none", value: "" } }));
            saveFolder(folder);
            openEditActionButtons(player, folderName, functionName, uiIndex);
        });
        return;
    }
    const form = new ActionFormData().title(`Edit Buttons - ${ui.title}`);
    ui.buttons.forEach(b => form.button(b.label + (b.action?.type && b.action.type !== "none" ? ` · [${b.action.type}]` : "")));
    form.button("§aAdd Button").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        const sel = res.selection;
        if (sel === ui.buttons.length) {
            const modal = new ModalFormData().title("New Button").textField("Label:", "New").textField("Action (examples):\n- command:say hi\n- message:Hello\n- setvar:global:coins:10\n- cancel:", "command:say hi");
            modal.show(player).then(r => {
                if (r.canceled) { openEditActionButtons(player, folderName, functionName, uiIndex); return; }
                const label = String(r.formValues[0] ?? "").trim();
                const actionRaw = String(r.formValues[1] ?? "").trim();
                const action = parseUIActionString(actionRaw);
                ui.buttons.push({ label, action });
                saveFolder(folder);
                openEditActionButtons(player, folderName, functionName, uiIndex);
            });
            return;
        }
        if (sel === ui.buttons.length + 1) { openUIsMenu(player, folderName, functionName); return; }
        const btn = ui.buttons[sel];
        const edit = new ActionFormData().title("Edit Button").body(`Label: ${btn.label}\nAction: ${btn.action?.type ?? "none"} ${btn.action?.value ?? ""}`).button("§eEdit").button("§cDelete").button(":switch_dpad_left: §cBack");
        edit.show(player).then(er => {
            if (er.canceled) return;
            if (er.selection === 0) {
                const modal = new ModalFormData().title("Edit Button").textField("Label:", btn.label).textField("Action:", `${btn.action?.type ?? "none"}:${btn.action?.value ?? ""}`);
                modal.show(player).then(mr => {
                    if (mr.canceled) { openEditActionButtons(player, folderName, functionName, uiIndex); return; }
                    btn.label = String(mr.formValues[0] ?? "").trim();
                    btn.action = parseUIActionString(String(mr.formValues[1] ?? "").trim());
                    saveFolder(folder);
                    openEditActionButtons(player, folderName, functionName, uiIndex);
                });
            } else if (er.selection === 1) {
                ui.buttons.splice(sel, 1);
                saveFolder(folder);
                openEditActionButtons(player, folderName, functionName, uiIndex);
            } else {
                openEditActionButtons(player, folderName, functionName, uiIndex);
            }
        });
    });
}
function openEditModalFieldActions(player, folderName, functionName, uiIndex) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openUIsMenu(player, folderName, functionName); return; }
    const ui = fn.uis[uiIndex];
    if (!ui || ui.type !== "modal") { openUIsMenu(player, folderName, functionName); return; }
    const form = new ActionFormData().title(`Modal Fields - ${ui.title}`);
    ui.modalFields.forEach(f => form.button(`${f.label} · ${f.onSubmitAction?.type ?? "none"}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === ui.modalFields.length) { openUIsMenu(player, folderName, functionName); return; }
        const idx = res.selection;
        const field = ui.modalFields[idx];
        const modal = new ModalFormData().title("Field Action").textField("Action (format same as button):", field.onSubmitAction ? `${field.onSubmitAction.type}:${field.onSubmitAction.value}` : "message:{value}");
        modal.show(player).then(r => {
            if (r.canceled) { openEditModalFieldActions(player, folderName, functionName, uiIndex); return; }
            field.onSubmitAction = parseUIActionString(String(r.formValues[0] ?? "").trim());
            saveFolder(folder);
            openEditModalFieldActions(player, folderName, functionName, uiIndex);
        });
    });
}
function openEditUI(player, folderName, functionName, uiIndex) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openUIsMenu(player, folderName, functionName); return; }
    const ui = fn.uis[uiIndex];
    if (!ui) { openUIsMenu(player, folderName, functionName); return; }
    const form = new ActionFormData()
        .title(`Edit UI: ${ui.title ?? "Untitled"}`)
        .button(":tip_touch_inventory: §eRename")
        .button(":ps4_select: §eEdit Content/Actions")
        .button(ui.type === "action" ? ":ps4_start: §eEdit Body" : ":ps4_start: §8Edit Body (Action UI only)")
        .button(" §cDelete")
        .button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const modal = new ModalFormData().title("Rename UI").textField("Title:", ui.title ?? "");
            modal.show(player).then(r => {
                if (r.canceled) { openEditUI(player, folderName, functionName, uiIndex); return; }
                ui.title = String(r.formValues[0] ?? "").trim();
                saveFolder(folder);
                openEditUI(player, folderName, functionName, uiIndex);
            });
            return;
        }
        if (res.selection === 1) {
            if (ui.type === "action") openEditActionButtons(player, folderName, functionName, uiIndex);
            else openEditModalFieldActions(player, folderName, functionName, uiIndex);
            return;
        }
        if (res.selection === 2) {
            if (ui.type !== "action") { openEditUI(player, folderName, functionName, uiIndex); return; }
            const current = ui.bodyLines && ui.bodyLines.length ? ui.bodyLines.join(", ") : (ui.body ?? "");
            const modal = new ModalFormData()
                .title("Edit UI Body")
                .textField("Body (comma = newline):", current);
            modal.show(player).then(r => {
                if (r.canceled) { openEditUI(player, folderName, functionName, uiIndex); return; }
                const raw = String(r.formValues[0] ?? "").trim();
                const lines = raw.split(",").map(s => s.trim()).filter(Boolean);
                ui.bodyLines = lines;
                delete ui.body;
                saveFolder(folder);
                openEditUI(player, folderName, functionName, uiIndex);
            });
            return;
        }
        if (res.selection === 3) {
            const confirm = new ActionFormData().title("Confirm Delete").body("Delete this UI?").button("§aYes").button("§cNo");
            confirm.show(player).then(r => {
                if (r.canceled) return;
                if (r.selection === 0) {
                    fn.uis.splice(uiIndex, 1);
                    saveFolder(folder);
                    openUIsMenu(player, folderName, functionName);
                } else openEditUI(player, folderName, functionName, uiIndex);
            });
            return;
        }
        openUIsMenu(player, folderName, functionName);
    });
}

/* ------------------------------
   code:0023 [Variable Conditions Editor]
   ------------------------------ */
function openVarConditionsMenu(player, folderName, functionName) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openFunctionsList(player, folderName); return; }
    if (!fn.varConditions) fn.varConditions = [];
    const form = new ActionFormData().title("Variable Conditions").button("§aAdd Condition");
    if (fn.varConditions.length === 0) form.body("§7No variable conditions defined.");
    fn.varConditions.forEach((v, i) => form.button(`${i + 1}. ${v.mode} ⇒ ${v.expr}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            openAddVarCondition(player, folderName, functionName);
            return;
        }
        const idx = res.selection - 1;
        if (idx >= 0 && idx < fn.varConditions.length) {
            openEditVarCondition(player, folderName, functionName, idx);
            return;
        }
        openFunctionEditor(player, folderName, functionName);
    });
}
function openAddVarCondition(player, folderName, functionName) {
    const form = new ModalFormData().title("Add Condition").textField("Expression (e.g. {player kills} < 100):", "{player kills} < 100").dropdown("Mode", ["Require True (skip if false)", "Cancel If True"]);
    form.show(player).then(res => {
        if (res.canceled) { openVarConditionsMenu(player, folderName, functionName); return; }
        const expr = String(res.formValues[0] ?? "").trim();
        const mode = res.formValues[1] === 0 ? "require" : "cancel";
        const folder = folderByName(folderName);
        const fn = folder?.functions.find(f => f.name === functionName);
        if (!fn) { openVarConditionsMenu(player, folderName, functionName); return; }
        if (!fn.varConditions) fn.varConditions = [];
        fn.varConditions.push({ expr, mode });
        saveFolder(folder);
        player.sendMessage("§aCondition added.");
        openVarConditionsMenu(player, folderName, functionName);
    });
}
function openEditVarCondition(player, folderName, functionName, idx) {
    const folder = folderByName(folderName);
    const fn = folder?.functions.find(f => f.name === functionName);
    if (!fn) { openVarConditionsMenu(player, folderName, functionName); return; }
    const v = fn.varConditions[idx];
    const form = new ActionFormData().title("Edit Condition").body(`${v.mode} ⇒ ${v.expr}`).button("§eEdit").button("§cDelete").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const modal = new ModalFormData().title("Edit Condition").textField("Expression:", v.expr).dropdown("Mode", ["Require True (skip if false)", "Cancel If True"]);
            modal.show(player).then(r => {
                if (r.canceled) { openEditVarCondition(player, folderName, functionName, idx); return; }
                v.expr = String(r.formValues[0] ?? "").trim();
                v.mode = r.formValues[1] === 0 ? "require" : "cancel";
                saveFolder(folder);
                openVarConditionsMenu(player, folderName, functionName);
            });
            return;
        }
        if (res.selection === 1) {
            const confirm = new ActionFormData().title("Confirm Delete").body("Delete this condition?").button("§aYes").button("§cNo");
            confirm.show(player).then(r => {
                if (r.canceled) return;
                if (r.selection === 0) {
                    fn.varConditions.splice(idx, 1);
                    saveFolder(folder);
                    openVarConditionsMenu(player, folderName, functionName);
                } else openEditVarCondition(player, folderName, functionName, idx);
            });
            return;
        }
        openVarConditionsMenu(player, folderName, functionName);
    });
}

/* ------------------------------
   code:0024 [Loops Management (Data, Tick Handler, Actions, Menus)]
   ------------------------------ */
function getLoops() {
    const data = getData();
    if (!data.loops) data.loops = [];
    data.loops.forEach(loop => {
        if (loop.enabled === undefined) loop.enabled = true;
    });
    return data.loops;
}
function saveLoops(updatedLoops) {
    const data = getData();
    data.loops = updatedLoops;
    setData(data);
}
function loopByName(name) {
    return getLoops().find(l => l.name === name);
}
function saveLoop(updatedLoop) {
    const loops = getLoops();
    const idx = loops.findIndex(l => l.name === updatedLoop.name);
    if (idx >= 0) loops[idx] = updatedLoop;
    else loops.push(updatedLoop);
    saveLoops(loops);
}
let tickHandler = null;
function ensureTickHandler() {
    if (tickHandler) return;
    tickHandler = system.runInterval(() => {
        const data = getData();
        if (!data.loops || data.loops.length === 0) {
            system.clearRun(tickHandler);
            tickHandler = null;
            return;
        }
        const currentTick = system.currentTick;
        let modified = false;
        data.loops.forEach(loop => {
            if (loop.enabled === false) return;

            if (!loop.lastRun) {
                loop.lastRun = currentTick;
                modified = true;
            }
            if (currentTick - loop.lastRun >= loop.interval) {
                const folder = loop.folderName ? folderByName(loop.folderName) : null;
                if (loop.scope === "per-player") {
                    if (!folder) return;
                    for (const player of world.getPlayers()) {
                        runLoopActions(loop, folder, player);
                    }
                } else {
                    runLoopActions(loop, folder, null);
                }
                loop.lastRun = currentTick;
                modified = true;
            }
        });
        if (modified) setData(data);
    }, 1);
}
system.afterEvents.scriptEventReceive.subscribe(() => {
    if (getLoops().length > 0) ensureTickHandler();
}, { namespaces: ["minecraft"] });
function runLoopActions(loop, folder, actor) {
    runMessages(loop.messages || [], {}, folder, actor);
    runCommands(loop.commands || [], {}, folder, actor);
    applyVariableActions(folder, loop, actor);
}
function openLoopsMainMenu(player) {
    const form = new ActionFormData()
        .title("§l§bSync Loops Menu")
        .body("§bManage periodic loops.")
        .button(":switch_start: §eCreate Loop")
        .button(":switch_dpad_down: §6List Loops")
        .button(":switch_select: §eDelete Loop")
        .button(" §4§lClose");
    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openCreateLoop(player); break;
            case 1: openListLoops(player); break;
            case 2: openDeleteLoop(player); break;
            case 3: return;
        }
    });
}
function openCreateLoop(player) {
    const folders = getFolders();
    const form = new ModalFormData()
        .title("§eCreate Loop")
        .textField("Enter loop name:", "loop name")
        .textField("Interval (ticks):", "20")
        .dropdown("Folder", [...folders, "None"])
        .dropdown("Scope", ["Global", "Per-Player"]);
    form.show(player).then(res => {
        if (res.canceled) { openLoopsMainMenu(player); return; }
        const name = (res.formValues[0] ?? "").trim();
        const intervalStr = (res.formValues[1] ?? "").trim();
        const interval = Number(intervalStr);
        const folderIdx = res.formValues[2] ?? folders.length;
        const folderName = folderIdx < folders.length ? folders[folderIdx] : null;
        const scopeIdx = res.formValues[3] ?? 0;
        const scope = scopeIdx === 0 ? "global" : "per-player";
        if (name.length < 1) { player.sendMessage("§cName must be at least 1 character."); openCreateLoop(player); return; }
        if (isNaN(interval) || interval < 1) { player.sendMessage("§cInterval must be a positive number."); openCreateLoop(player); return; }
        if (scope === "per-player" && !folderName) { player.sendMessage("§cPer-player loops require a folder."); openCreateLoop(player); return; }
        const loops = getLoops();
        if (loops.some(l => l.name === name)) { player.sendMessage("§cLoop with that name exists."); openCreateLoop(player); return; }
        loops.push({
            name, interval, folderName, scope,
            messages: [], commands: [], variableActions: [],
            enabled: true   // ← new
        });
        saveLoops(loops);
        ensureTickHandler();
        player.sendMessage(`§aLoop '${name}' created.`);
        openLoopEditor(player, name);
    });
}
system.run(() => ensureTickHandler());
function openListLoops(player) {
    const loops = getLoops();
    const form = new ActionFormData().title("§6Your Loops");
    if (loops.length === 0) {
        form.body("§cNo loops.").button("§eCreate One");
        form.show(player).then(res => { if (!res.canceled && res.selection === 0) openCreateLoop(player); });
        return;
    }
    loops.forEach(l =>
        form.button(`${l.name} ${l.enabled !== false ? "§a[On]" : "§c[Off]"}`)
    );
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === loops.length) { openLoopsMainMenu(player); return; }
        const loopName = loops[res.selection].name;
        openLoopEditor(player, loopName);
    });
}
function openDeleteLoop(player) {
    const loops = getLoops();
    const form = new ActionFormData().title("§cDelete Loop");
    if (loops.length === 0) {
        form.body("§cNo loops to delete.").button(":switch_dpad_left: §cBack");
        form.show(player).then(res => { if (!res.canceled) openLoopsMainMenu(player); });
        return;
    }
    loops.forEach(l => form.button(l.name));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === loops.length) { openLoopsMainMenu(player); return; }
        const loopName = loops[res.selection].name;
        const confirm = new ActionFormData()
            .title("Confirm Delete")
            .body(`§eDelete '${loopName}'?`)
            .button(":ps4_face_button_down: §aYes")
            .button(":ps4_face_button_right: §cNo");
        confirm.show(player).then(r => {
            if (r.canceled) return;
            if (r.selection === 0) {
                const newLoops = loops.filter(l => l.name !== loopName);
                saveLoops(newLoops);
                player.sendMessage(`§aLoop '${loopName}' deleted.`);
                openLoopsMainMenu(player);
            } else {
                openDeleteLoop(player);
            }
        });
    });
}
function openLoopEditor(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openListLoops(player); return; }
    loop.enabled = loop.enabled ?? true;

    const form = new ActionFormData()
        .title(`§bEditing Loop: §e${loopName}`)
        .body(`Interval: ${loop.interval} ticks\nScope: ${loop.scope}\nFolder: ${loop.folderName ?? "None"}\nStatus: ${loop.enabled ? "§aOn" : "§cOff"}`)
        .button(":xbox_select: §eRename")
        .button(":xbox_start: §eEdit Interval")
        .button(" §eChange Scope")
        .button(" §eChange Folder")
        .button(":touch_chat: §eMessages")
        .button(" §eCommands")
        .button(":furnace: §eVariables")
        .button(loop.enabled ? " §cDisable Loop" : " §aEnable Loop")
        .button(":switch_dpad_left: §cBack");

    form.show(player).then(res => {
        if (res.canceled) return;
        switch (res.selection) {
            case 0: openRenameLoop(player, loopName); break;
            case 1: openEditInterval(player, loopName); break;
            case 2: openChangeScope(player, loopName); break;
            case 3: openChangeFolder(player, loopName); break;
            case 4: openLoopMessagesMenu(player, loopName); break;
            case 5: openLoopCommandsMenu(player, loopName); break;
            case 6: openLoopVariablesMenu(player, loopName); break;
            case 7:
                loop.enabled = !loop.enabled;
                saveLoop(loop);
                player.sendMessage(loop.enabled ? "§aLoop enabled." : "§cLoop disabled.");
                openLoopEditor(player, loopName);
                break;
            case 8: openListLoops(player); break;
        }
    });
}
function openRenameLoop(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openListLoops(player); return; }
    const form = new ModalFormData().title("Rename Loop").textField(`Current: ${loop.name}\nNew name:`, "New name");
    form.show(player).then(res => {
        if (res.canceled) { openLoopEditor(player, loopName); return; }
        const newName = (res.formValues[0] ?? "").trim();
        if (newName.length < 1) { player.sendMessage("§cName must be at least 1 character."); openRenameLoop(player, loopName); return; }
        const loops = getLoops();
        if (loops.some(l => l.name === newName)) { player.sendMessage("§cName exists."); openRenameLoop(player, loopName); return; }
        loop.name = newName;
        saveLoop(loop);
        player.sendMessage(`§aRenamed to '${newName}'.`);
        openLoopEditor(player, newName);
    });
}
function openEditInterval(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openListLoops(player); return; }
    const form = new ModalFormData().title("Edit Interval").textField("New interval (ticks):", loop.interval.toString());
    form.show(player).then(res => {
        if (res.canceled) { openLoopEditor(player, loopName); return; }
        const intervalStr = (res.formValues[0] ?? "").trim();
        const interval = Number(intervalStr);
        if (isNaN(interval) || interval < 1) { player.sendMessage("§cInvalid number."); openEditInterval(player, loopName); return; }
        loop.interval = interval;
        saveLoop(loop);
        player.sendMessage("§aInterval updated.");
        openLoopEditor(player, loopName);
    });
}
function openChangeScope(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openListLoops(player); return; }
    const form = new ModalFormData().title("Change Scope").dropdown("Scope", ["Global", "Per-Player"]);
    form.show(player).then(res => {
        if (res.canceled) { openLoopEditor(player, loopName); return; }
        const scopeIdx = res.formValues[0] ?? 0;
        const newScope = scopeIdx === 0 ? "global" : "per-player";
        if (newScope === "per-player" && !loop.folderName) { player.sendMessage("§cPer-player requires a folder."); openChangeScope(player, loopName); return; }
        loop.scope = newScope;
        saveLoop(loop);
        player.sendMessage("§aScope updated.");
        openLoopEditor(player, loopName);
    });
}
function openChangeFolder(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openListLoops(player); return; }
    const folders = getFolders();
    const currentIdx = loop.folderName ? folders.indexOf(loop.folderName) : folders.length;
    const form = new ModalFormData().title("Change Folder").dropdown("Folder", [...folders, "None"]);
    form.show(player).then(res => {
        if (res.canceled) { openLoopEditor(player, loopName); return; }
        const folderIdx = res.formValues[0] ?? folders.length;
        const newFolderName = folderIdx < folders.length ? folders[folderIdx] : null;
        if (loop.scope === "per-player" && !newFolderName) { player.sendMessage("§cPer-player requires a folder."); openChangeFolder(player, loopName); return; }
        loop.folderName = newFolderName;
        saveLoop(loop);
        player.sendMessage("§aFolder updated.");
        openLoopEditor(player, loopName);
    });
}
function openLoopMessagesMenu(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openLoopEditor(player, loopName); return; }
    if (!loop.messages) loop.messages = [];
    loop.messages = loop.messages.map(ensureMessageFormat);
    const form = new ActionFormData().title("Messages").button("§aAdd Message");
    if (loop.messages.length === 0) form.body("§7No messages defined.");
    loop.messages.forEach((m, i) => {
        const preview = m.parts.join("").slice(0, 20);
        form.button(`Msg ${i + 1}: ${preview}...`);
    });
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            loop.messages.push({ parts: [] });
            saveLoop(loop);
            openLoopEditMessageParts(player, loopName, loop.messages.length - 1);
            return;
        }
        const idx = res.selection - 1;
        if (idx >= 0 && idx < loop.messages.length) {
            openLoopEditMessageParts(player, loopName, idx);
        } else {
            openLoopEditor(player, loopName);
        }
    });
}
function openLoopEditMessageParts(player, loopName, msgIdx) {
    const loop = loopByName(loopName);
    if (!loop) { openLoopMessagesMenu(player, loopName); return; }
    const msg = loop.messages[msgIdx];
    if (!msg) { openLoopMessagesMenu(player, loopName); return; }
    if (!msg.parts) msg.parts = [];
    const form = new ActionFormData().title("Edit Message Parts").button("§aAdd Part");
    if (msg.parts.length === 0) form.body("§7No parts yet. Add to build the message.");
    msg.parts.forEach((part, i) => form.button(`Part ${i + 1}: ${part.slice(0, 20)}...`));
    form.button("§cDelete Message").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const add = new ModalFormData().title("Add Part").textField("Message part (concatenated):", "Continue text here");
            add.show(player).then(r => {
                if (r.canceled) { openLoopEditMessageParts(player, loopName, msgIdx); return; }
                const part = String(r.formValues[0] ?? "").trim();
                if (part.length === 0) { player.sendMessage("§cPart cannot be empty."); openLoopEditMessageParts(player, loopName, msgIdx); return; }
                msg.parts.push(part);
                saveLoop(loop);
                openLoopEditMessageParts(player, loopName, msgIdx);
            });
            return;
        }
        const partIdx = res.selection - 1;
        if (partIdx >= 0 && partIdx < msg.parts.length) {
            const current = msg.parts[partIdx];
            const edit = new ActionFormData().title("Edit Part").body(current).button("§eEdit").button("§cDelete").button(":switch_dpad_left: §cBack");
            edit.show(player).then(er => {
                if (er.canceled) return;
                if (er.selection === 0) {
                    const mod = new ModalFormData().title("Edit Part").textField("Update:", current);
                    mod.show(player).then(mr => {
                        if (mr.canceled) { openLoopEditMessageParts(player, loopName, msgIdx); return; }
                        const newPart = String(mr.formValues[0] ?? "").trim();
                        if (newPart.length === 0) { player.sendMessage("§cPart cannot be empty."); openLoopEditMessageParts(player, loopName, msgIdx); return; }
                        msg.parts[partIdx] = newPart;
                        saveLoop(loop);
                        openLoopEditMessageParts(player, loopName, msgIdx);
                    });
                } else if (er.selection === 1) {
                    msg.parts.splice(partIdx, 1);
                    saveLoop(loop);
                    openLoopEditMessageParts(player, loopName, msgIdx);
                } else {
                    openLoopEditMessageParts(player, loopName, msgIdx);
                }
            });
            return;
        }
        if (res.selection === msg.parts.length + 1) {
            const confirm = new ActionFormData().title("Delete Message").body("Delete this entire message?").button("§aYes").button("§cNo");
            confirm.show(player).then(cr => {
                if (cr.canceled || cr.selection === 1) { openLoopEditMessageParts(player, loopName, msgIdx); return; }
                loop.messages.splice(msgIdx, 1);
                saveLoop(loop);
                openLoopMessagesMenu(player, loopName);
            });
            return;
        }
        openLoopMessagesMenu(player, loopName);
    });
}
function openLoopCommandsMenu(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openLoopEditor(player, loopName); return; }
    if (!loop.commands) loop.commands = [];
    loop.commands = loop.commands.map(ensureCommandFormat);
    const form = new ActionFormData().title("Commands").button("§aAdd Command");
    if (loop.commands.length === 0) form.body("§7No commands defined.");
    loop.commands.forEach((c, i) => {
        const preview = c.parts.join("").slice(0, 20);
        form.button(`Cmd ${i + 1} [${c.mode}]: ${preview}...`);
    });
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const addMode = new ModalFormData().title("Add Command").dropdown("Executor", ["console", "player"]);
            addMode.show(player).then(rm => {
                if (rm.canceled) { openLoopCommandsMenu(player, loopName); return; }
                const mode = ["console", "player"][rm.formValues[0]] ?? "console";
                loop.commands.push({ mode, parts: [] });
                saveLoop(loop);
                openLoopEditCommandParts(player, loopName, loop.commands.length - 1);
            });
            return;
        }
        const idx = res.selection - 1;
        if (idx >= 0 && idx < loop.commands.length) {
            openLoopEditCommandParts(player, loopName, idx);
        } else {
            openLoopEditor(player, loopName);
        }
    });
}
function openLoopEditCommandParts(player, loopName, cmdIdx) {
    const loop = loopByName(loopName);
    if (!loop) { openLoopCommandsMenu(player, loopName); return; }
    const cmd = loop.commands[cmdIdx];
    if (!cmd) { openLoopCommandsMenu(player, loopName); return; }
    if (!cmd.parts) cmd.parts = [];
    let bodyText = `Executor: ${cmd.mode}`;
    const form = new ActionFormData().title("Edit Command Parts").body(bodyText).button("§aAdd Part").button("§eChange Executor");
    if (cmd.parts.length === 0) {
        bodyText += "\n§7No parts yet. Add to build the command.";
        form.body(bodyText);
    }
    cmd.parts.forEach((part, i) => form.button(`Part ${i + 1}: ${part.slice(0, 20)}...`));
    form.button("§cDelete Command").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) {
            const add = new ModalFormData().title("Add Part").textField("Command part (concatenated):", "continue command");
            add.show(player).then(r => {
                if (r.canceled) { openLoopEditCommandParts(player, loopName, cmdIdx); return; }
                const part = String(r.formValues[0] ?? "").trim();
                if (part.length === 0) { player.sendMessage("§cPart cannot be empty."); openLoopEditCommandParts(player, loopName, cmdIdx); return; }
                cmd.parts.push(part);
                saveLoop(loop);
                openLoopEditCommandParts(player, loopName, cmdIdx);
            });
            return;
        }
        if (res.selection === 1) {
            const changeMode = new ModalFormData().title("Change Executor").dropdown("Executor", ["console", "player"]);
            changeMode.show(player).then(rm => {
                if (rm.canceled) { openLoopEditCommandParts(player, loopName, cmdIdx); return; }
                cmd.mode = ["console", "player"][rm.formValues[0]] ?? "console";
                saveLoop(loop);
                openLoopEditCommandParts(player, loopName, cmdIdx);
            });
            return;
        }
        const partIdx = res.selection - 2;
        if (partIdx >= 0 && partIdx < cmd.parts.length) {
            const current = cmd.parts[partIdx];
            const edit = new ActionFormData().title("Edit Part").body(current).button("§eEdit").button("§cDelete").button(":switch_dpad_left: §cBack");
            edit.show(player).then(er => {
                if (er.canceled) return;
                if (er.selection === 0) {
                    const mod = new ModalFormData().title("Edit Part").textField("Update:", current);
                    mod.show(player).then(mr => {
                        if (mr.canceled) { openLoopEditCommandParts(player, loopName, cmdIdx); return; }
                        const newPart = String(mr.formValues[0] ?? "").trim();
                        if (newPart.length === 0) { player.sendMessage("§cPart cannot be empty."); openLoopEditCommandParts(player, loopName, cmdIdx); return; }
                        cmd.parts[partIdx] = newPart;
                        saveLoop(loop);
                        openLoopEditCommandParts(player, loopName, cmdIdx);
                    });
                } else if (er.selection === 1) {
                    cmd.parts.splice(partIdx, 1);
                    saveLoop(loop);
                    openLoopEditCommandParts(player, loopName, cmdIdx);
                } else {
                    openLoopEditCommandParts(player, loopName, cmdIdx);
                }
            });
            return;
        }
        if (res.selection === cmd.parts.length + 2) {
            const confirm = new ActionFormData().title("Delete Command").body("Delete this entire command?").button("§aYes").button("§cNo");
            confirm.show(player).then(cr => {
                if (cr.canceled || cr.selection === 1) { openLoopEditCommandParts(player, loopName, cmdIdx); return; }
                loop.commands.splice(cmdIdx, 1);
                saveLoop(loop);
                openLoopCommandsMenu(player, loopName);
            });
            return;
        }
        openLoopCommandsMenu(player, loopName);
    });
}
function openLoopVariablesMenu(player, loopName) {
    const loop = loopByName(loopName);
    if (!loop) { openLoopEditor(player, loopName); return; }
    if (!loop.variableActions) loop.variableActions = [];
    const folder = loop.folderName ? folderByName(loop.folderName) : null;
    const globals = Object.keys(getGlobalStore());
    const locals = folder ? Object.keys((folder.variables && folder.variables.local) || {}) : [];
    const form = new ActionFormData().title("Variables").button("§eModify Variable §c(Not Supported)");
    form.body(`§6Globals: §f${globals.length ? globals.join(", ") : "None"}\n§bLocals: §f${locals.length ? locals.join(", ") : "None"}`);
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) { openLoopModifyVariableSelect(player, loopName); return; }
        openLoopEditor(player, loopName);
    });
}
function openLoopModifyVariableSelect(player, loopName) {
    const loop = loopByName(loopName);
    const folder = loop.folderName ? folderByName(loop.folderName) : null;
    const globals = Object.keys(getGlobalStore());
    const locals = folder ? Object.keys((folder.variables && folder.variables.local) || {}) : [];
    const form = new ActionFormData().title("Select Variable");
    globals.forEach(g => form.button(`§6[Global] ${g}`));
    locals.forEach(l => form.button(`§b[Local] ${l}`));
    form.button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        const total = globals.length + locals.length;
        if (res.selection === total) { openLoopVariablesMenu(player, loopName); return; }
        let scope = "global"; let name = "";
        if (res.selection < globals.length) { scope = "global"; name = globals[res.selection]; }
        else { scope = "local"; name = locals[res.selection - globals.length]; }
        openLoopModifyVariableAction(player, loopName, scope, name);
    });
}
function openLoopModifyVariableAction(player, loopName, scope, varName) {
    const form = new ActionFormData().title(`Modify '${varName}' (${scope})`).button("§aAdd Value").button("§cRemove Value").button("§eSet Exact Value").button("§eReset to 0").button(":switch_dpad_left: §cBack");
    form.show(player).then(res => {
        if (res.canceled) return;
        if (res.selection === 0) openLoopModifyVariableValue(player, loopName, scope, varName, "add");
        else if (res.selection === 1) openLoopModifyVariableValue(player, loopName, scope, varName, "remove");
        else if (res.selection === 2) openLoopModifyVariableValue(player, loopName, scope, varName, "set");
        else if (res.selection === 3) saveLoopVariableAction(player, loopName, scope, varName, "reset", 0);
        else openLoopVariablesMenu(player, loopName);
    });
}
function openLoopModifyVariableValue(player, loopName, scope, varName, mode) {
    const form = new ModalFormData().title(`${mode === "add" ? "Add" : mode === "remove" ? "Remove" : "Set"} Value`).textField("Value (number):", "1");
    form.show(player).then(res => {
        if (res.canceled) { openLoopModifyVariableAction(player, loopName, scope, varName); return; }
        const val = Number(String(res.formValues[0] ?? "0").trim());
        if (Number.isNaN(val)) { player.sendMessage("§cValue must be a number."); openLoopModifyVariableValue(player, loopName, scope, varName, mode); return; }
        saveLoopVariableAction(player, loopName, scope, varName, mode, val);
    });
}
function saveLoopVariableAction(player, loopName, scope, varName, mode, value) {
    const loop = loopByName(loopName);
    if (!loop) { openLoopVariablesMenu(player, loopName); return; }
    if (!loop.variableActions) loop.variableActions = [];
    loop.variableActions.push({ scope, name: varName, mode, value });
    saveLoop(loop);
    player.sendMessage("§aVariable control updated.");
    openLoopVariablesMenu(player, loopName);
}


const onHoldFunctions = new Map();
let onHoldTickHandler = null;

function ensureOnHoldHandler() {
    if (onHoldTickHandler) return;
    onHoldTickHandler = system.runInterval(() => {
        for (const player of world.getPlayers()) {
            if (!player || !player.hasTag("sync")) continue;

            const equippable = player.getComponent("equippable");
            const heldItem = equippable?.getEquipment(EquipmentSlot.Mainhand);
            if (!heldItem) {
                onHoldFunctions.delete(player.name);
                continue;
            }

            const matchingFns = allFunctions().filter(({ folder, fn }) =>
                fn.hook === "onHold" &&
                fn.enabled !== false &&
                folder.enabled !== false &&
                matchInputs("onHold", fn, { itemStack: heldItem })
            );

            if (matchingFns.length === 0) {
                onHoldFunctions.delete(player.name);
                continue;
            }

            for (const { folder, fn } of matchingFns) {
                const interval = Number(fn?.inputs?.interval ?? 20);
                if (isNaN(interval) || interval < 1) continue;

                const key = `${player.name}_${fn.name}`;
                const data = onHoldFunctions.get(key) || { lastRun: 0 };
                const now = system.currentTick;

                if (now - data.lastRun >= interval) {
                    const fakeEv = { itemStack: heldItem, player };
                    const varCheck = checkVarConditionsAndAct(folder, fn, fakeEv, player);
                    if (!varCheck.ok) continue;

                    if (!checkConditions(player, fn.conditions, fakeEv)) continue;

                    applyVariableActions(folder, fn, player);

                    if (fn.uis?.length > 0) {
                        runUIsForFunction(fn, folder, player, fakeEv);
                    } else {
                        runMessages(fn.messages, fakeEv, folder, player);
                        runCommands(fn.commands, fakeEv, folder, player);
                    }

                    data.lastRun = now;
                    onHoldFunctions.set(key, data);
                }
            }
        }
    }, 1);
}

system.run(() => ensureOnHoldHandler());

/* ------------------------------
   code:0025 [Error Console Menu]
   ------------------------------ */
function openErrorConsoleMenu(player) {
    const form = new ActionFormData()
        .title("§cSync Error Console")
        .body(recentErrors.length ? "Last 20 errors:" : "No errors logged.");
    if (recentErrors.length) {
        recentErrors.forEach((e, i) => {
            const label = `#${i + 1} ${new Date(e.time).toLocaleTimeString()} | ${e.command}`;
            form.button(label, undefined);
        });
    }
    form.show(player).then(response => {
        if (response.canceled) return;
        const selected = recentErrors[response.selection];
        if (selected) {
            player.sendMessage(`§c[Error] ${selected.command}: ${selected.message}`);
        }
    });
}

/* ------------------------------
   code:0026 [Player Spawn Initialization]
   ------------------------------ */
world.afterEvents.playerSpawn.subscribe(ev => {
    const player = ev.player;
    if (!player) return;

    if (ev.initialSpawn) {
        player.sendMessage("§l§d━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        player.sendMessage("§b§l✨ Welcome to §dSync ✨§r");
        player.sendMessage("§l§d━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        player.sendMessage("§e➤ §a/menu §f– Create folders & functions");
        player.sendMessage("§e➤ §a/loops §f– Build powerful repeating actions");
        player.sendMessage("§e➤ §a/console §f– View the last 20 errors");
        player.sendMessage("§e➤ §a/synchelp §f– Explore every feature in detail");
        player.sendMessage("§e➤ §a/variable §f– Modify local & global variables");
        player.sendMessage("§e➤ §a/varlist §f– List variables in a folder");
        player.sendMessage("§l§d━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        player.sendMessage("§c⚠ You must have the 'sync' tag to use these commands.");
        player.sendMessage("§7Run: §a/tag @s add sync");
        player.sendMessage("§l§d━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
        player.sendMessage("§bTutorials & Guides: §ahttps://www.youtube.com/@exterleo");
        player.sendMessage("§bDocs: §ahttps://leowascoding.github.io/sync/");
        player.sendMessage("§l§d━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    }
});

system.run(() => {
    const elapsed = Date.now() - startTime;

    const message = [
        "§8§m-----------------------------",
        "§b§l⚡ §aSYNC §fhas ignited §b⚡",
        `§7» §dSuccessfully loaded in §f${elapsed}ms §7«`,
        "§8§m-----------------------------"
    ].join("\n");

    world.sendMessage(message);

    console.log(
        "" +
        `Successfully loaded in ${elapsed}ms\n` +
        ""
    );
});