Understanding How Plugins Work
This page is the practical mental model for writing Acode plugins: what Acode does, what your plugin must do, and what happens during load/unload.
The Plugin Contract
From Acode's perspective, your plugin is:
- A folder in
PLUGIN_DIR - A
plugin.json - An entry script (usually
main.js)
From your perspective, your script should register:
acode.setPluginInit(pluginId, initFn)acode.setPluginUnmount(pluginId, unmountFn)(strongly recommended)
If you skip setPluginInit, your script may load, but your plugin logic will not run through Acode's lifecycle.
Lifecycle In One View
- Acode discovers plugin folders.
- It decides which plugins to load (enabled, not broken, not already loaded).
- It loads your entry script.
- It calls your registered
initwith runtime context. - Later, on disable/uninstall/reload, it calls your registered
unmount.
What You Get In init
The init callback registered with setPluginInit receives three arguments:
baseUrl: internal base URL to your plugin files (normalize it with a trailing slash, see below)$page: a plugin page object for UI screensoptions: object with:cacheFileUrlcacheFilefirstInitctxfileIcons— plugin-bound File Icons API (versionCode1012+)
Use firstInit for one-time setup or migration.
ctx is your plugin's native-backed context: encrypted secret storage and permission checks. See Plugin Context (ctx).
Recommended main.js Shape
The official templates structure your plugin as an AcodePlugin class with init() and destroy():
import plugin from "../plugin.json";
class AcodePlugin {
baseUrl = "";
async init(_page, _cacheFile, _cacheFileUrl, _firstInit, _ctx, _fileIcons) {
// plugin code
}
async destroy() {
// plugin clean up
}
}
if (window.acode) {
const acodePlugin = new AcodePlugin();
acode.setPluginInit(plugin.id, async (baseUrl, $page, { cacheFileUrl, cacheFile, firstInit, ctx, fileIcons }) => {
acodePlugin.baseUrl = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
await acodePlugin.init($page, cacheFile, cacheFileUrl, firstInit, ctx, fileIcons);
});
acode.setPluginUnmount(plugin.id, () => {
acodePlugin.destroy();
});
}Breaking that down:
window.acodeis only present once Acode's API is ready, so registration is wrapped in a guard.plugin.idcomes from yourplugin.json, so the registration always matches the installed id.- The
initcallback receives(baseUrl, $page, options), whereoptionsis{ cacheFileUrl, cacheFile, firstInit, ctx, fileIcons }. Those are forwarded to your class'sinit.fileIconsis also available asacode.require("fileIcons")if you prefer to capture it in the main script — see File Icons. baseUrlis stored with a guaranteed trailing slash so you can build file paths withUrl.joinor string concatenation.destroy()is wired tosetPluginUnmountso it runs on disable/reload/uninstall.initis awaited, so heavy setup can be done inside it.
What Happens On Disable / Enable / Uninstall
- Disable:
- Acode calls
acode.unmountPlugin(id)which triggers your registered unmount (your class'sdestroy()). - Plugin runtime state is cleared (including plugin cache file).
- Acode calls
- Enable:
- Acode loads the plugin again and runs init again.
- Uninstall:
- Plugin files are removed.
- Acode runs unmount cleanup for loaded resources.
Treat init as repeatable and destroy as mandatory cleanup.
Failure Behavior You Should Know
If your plugin throws during load/init:
- it is marked as broken for the session flow,
- Acode skips loading it again until user/action retries it.
For programmatic recovery:
acode.clearBrokenPluginMark("com.example.plugin");Author Guidelines
- Keep
initfast; do heavy work lazily. - Register commands through
acode.require("commands"). - Always remove listeners, commands, intervals, and UI hooks in
destroy. - Avoid storing important state only in memory; use cache/settings when needed.
