| name | onewow-qol-modules |
| description | Use this skill when creating, scaffolding, or reviewing a OneWoW_QoL feature module — anything about the external module hub, module.lua/Define, the ModuleRegistry, Current() capture, per-module locale scopes, toggles, or the OnEnable/OnDisable/OnToggle lifecycle. Especially for "add a new QoL module" prompts where no module file is open yet. |
OneWoW QoL External Modules Skill
A OneWoW_QoL module is a self-contained folder under
OneWoW_QoL/Modules/external/<id>/. The hub handles registration, the Features
UI, toggles, saved state, and language switching — the folder declares metadata
and logic only.
Authoritative guide: OneWoW_QoL/DEVELOPERS.md — folder layout, full
module/toggle field tables, lifecycle callbacks, categories, SavedVariables,
common mistakes. Read it for anything beyond the scaffold below. A path-scoped
Cursor rule (OneWoW-QoL-Modules.mdc) auto-attaches the same rules when editing
files under Modules/external/; this skill covers the cold-start case where you
are creating a module and no file is open yet.
Minimal module scaffold
A working module needs module.lua (metadata, loads first), one code file, and
at least one locale file. autodelete is the reference module to copy.
local ADDON_NAME, ns = ...
ns.ModuleRegistry:Define(ADDON_NAME, {
id = "yourmodule",
title = "YOURMODULE_TITLE",
category = "UTILITY",
description = "YOURMODULE_DESC",
version = "1.0",
toggles = {
{ id = "myToggle", label = "YOURMODULE_TOGGLE", description = "YOURMODULE_TOGGLE_DESC", default = true },
},
defaultEnabled = true,
})
local _, ns = ...
local M, L = ns.ModuleRegistry:Current()
if not M then return end
function M:OnEnable() end
function M:OnDisable() end
function M:OnToggle(toggleId, value) end
local _, ns = ...
local M = ns.ModuleRegistry:Current()
OneWoW.Locale:Register(M._scope, "enUS", {
YOURMODULE_TITLE = "My Module",
YOURMODULE_DESC = "What it does, in plain language.",
YOURMODULE_TOGGLE = "My Toggle",
YOURMODULE_TOGGLE_DESC = "What the toggle does.",
})
Then add the files to the EXTERNAL MODULES block of OneWoW_QoL.toc, module.lua
first, then locales, then code files.
The rules that cause bugs when missed
module.lua loads first — it is the single home of the module id and the
thing every other file's Current() depends on. No data.lua; Define registers.
Current() is load-time only — local M, L = ns.ModuleRegistry:Current() at
the top of each file, captured into a local. Never call it at runtime. Use
local _, L = ... for a strings-only file; skip the call if a file needs neither.
- Cross-module access via
ns.ModuleRegistry:GetById("id") — never an
ns.YourModule global (those were removed). In-module, use the Current() local.
- Locale scope is
M._scope (ADDON_NAME .. "." .. id). Store locale KEYS in
title/description/toggle label; the Features UI resolves them from your scope.
- No
L[key] or "fallback" — a miss resolves to the key name (visible by design).
Genuinely optional text → OneWoW.Locale:GetOptional(scope, key).
(Suite Locale contract: OneWoW/Docs/ARCHITECTURE.md §6.)
Reading state at runtime
- Toggle value:
ns.ModuleRegistry:GetToggleValue("yourmodule", "myToggle")
- Enabled check (guard direct event handlers):
ns.ModuleRegistry:IsEnabled("yourmodule")
- Your saved data:
ns.db.global.modules["yourmodule"] — only after init
(inside OnEnable or later), never at file load.