[Extension] Save/Load/Delete Layer Visibility Settings

This is my first script, it’s simple and I welcome anyone to make it better.

This script needs to be saved as an extension and allows you to save layer settings persistently in a file. The only way I was to save data persistently was to make it an extension rather than a script. I work with a file with hundreds of layers for modular parts like hats, armor, etc. When I tediously turn on visibility for a certain character, like an orc wearing certain items, this allows me to save those layer settings with a name and load them very quickly later on, so I don’t have to turn on the layer visibility inside grouped folders for dozens of layers each time. It will set the layer visibility status based on the save point - so it turns certain layers hidden, and others visible. Works on group layers and child layers in a group.

The script should work recursively for layers inside groups. I recommend not making the layer setting names complex, use names like orc_warrior_1 not “Orc Warrior 1!”, to avoid likely bugs. This is primitive code.

Hidden feature: Saving layer settings with the same name as an existing layer setting should override it, this allows updating the layer setting file.

It works by adding three hotkey commands. By default they are not assigned because I didn’t know how to do that. I manually set up the hotkeys like so:

Save Layer Settings: hotkey F1
Load Layer Settings: hotkey ctrl+F1
Delete Layer Settings: hotkey shift+F1

Create a folder in the aseprite extensions folder called layermanager, inside that folder save these two files using the following file names and file extensions.

layermanager.lua

function init(plugin)
    print("Aseprite is initializing my plugin")

    -- Initialize preferences layer_table to store layer settings
    if not plugin.preferences.layer_settings then
        plugin.preferences.layer_settings = {}
    end

    -- Command to save the current layer visibility settings
    plugin:newCommand {
        id = "SaveLayers",
        title = "Save Layer Settings",
        group = "edit_menu",
        onclick = function()
            local dlg = Dialog({ title="Save Layer Settings" })
            dlg:entry { id = "user_value", label = "User Value:", text = "Default User" }
            dlg:button { id = "confirm", text = "Confirm" }
            dlg:button { id = "cancel", text = "Cancel" }
            dlg:show()
            local data = dlg.data
            if data.confirm then
                -- Save current layer visibility state
                local name = data.user_value
                local layer_table = {}
                local parent_layer = false
                for i, layer in ipairs(app.activeSprite.layers) do
                    layer_table = save_layer(layer_table, parent_layer, layer)
                end
                plugin.preferences.layer_settings[name] = layer_table
                app.alert("Layer settings saved as '" .. name .. "'")
            end
        end,
        onenabled = function()
            return app.activeSprite ~= nil
        end
    }

    -- Command to load saved layer visibility settings
    plugin:newCommand {
        id = "LoadLayers",
        title = "Load Layer Settings",
        group = "edit_menu",
        onclick = function()
            local dlg = Dialog({ title="Load Layer Settings" })
            for name, layer_table in pairs(plugin.preferences.layer_settings) do
                dlg:button { id = tostring(name), text = tostring(name), onclick = function()
                    if layer_table then
                        local indexed_layers = {}
                        for k, v in ipairs(layer_table) do
                            indexed_layers[v.layer_name] = v
                        end
                        local count = 0
                        for i, layer in ipairs(app.activeSprite.layers) do
                            if indexed_layers[layer.name] then
                                local parent_layer = indexed_layers[layer.name].parent_layer
                                local visibility = indexed_layers[layer.name].visibility
                                load_layer(indexed_layers, parent_layer, layer, visibility)
                                count = count +1
                            end
                        end
                        app.alert("Loaded")
                    end
                end
                }
            end

            dlg:button { id = "cancel", text = "Cancel" }

            dlg:show()
            local data = dlg.data
        end,
        onenabled = function()
            return app.activeSprite ~= nil and next(plugin.preferences.layer_settings) ~= nil
        end
    }

    -- Command to load saved layer visibility settings
    plugin:newCommand {
        id = "DeleteLayers",
        title = "Delete Layer Settings",
        group = "edit_menu",
        onclick = function()
            local dlg = Dialog({ title="Delete Layer Settings" })
            for name, layer_table in pairs(plugin.preferences.layer_settings) do
                dlg:button { id = tostring(name), text = tostring(name), onclick = function()
                    plugin.preferences.layer_settings[name] = nil
                    dlg:close()
                    app.alert("Deleted")
                end
                }
            end

            dlg:button { id = "cancel", text = "Cancel" }

            dlg:show()
            local data = dlg.data
        end,
        onenabled = function()
            return app.activeSprite ~= nil and next(plugin.preferences.layer_settings) ~= nil
        end
    }
end

function save_layer(layer_table, parent_layer, layer)
    table.insert(layer_table, { parent_layer = parent_layer, layer_name = layer.name, visibility = layer.isVisible })
    if layer.isGroup then
        for _, child_layer in ipairs(layer.layers) do
            save_layer(layer_table, layer, child_layer)
        end
    end
    return layer_table
end

function load_layer(indexed_layers, parent_layer, layer, visibility)
    layer.isVisible = indexed_layers[layer.name].visibility
    if layer.isGroup then
        for _, child_layer in ipairs(layer.layers) do
            local child_parent_layer = indexed_layers[child_layer.name].parent_layer
            local child_visibility = indexed_layers[child_layer.name].visibility
            load_layer(indexed_layers, child_parent_layer, child_layer, child_visibility)
        end
    end
end

function exit(plugin)
    print("Aseprite is closing my plugin")
end

package.json

{
    "name": "layermanager",
    "displayName": "Layer Manager",
    "description": "Save and restore layer visibility in a file",
    "version": "0.1",
    "author": { "name": "Chris Yoo",
                "email": "my@email.com",
                "url": "https://mywebsite.com/" },
    "contributors": [ ],
    "publisher": "myname",
    "license": "CC-BY-4.0",
    "categories": [ "Scripts" ],
    "contributes": {
      "scripts": [
          { "path": "./layermanager.lua" }
      ]
    }
}