Repository navigation
Contributing
Thank you for your interest in contributing to the Advanced Hotkey Overlay System! Contributions of all kinds are welcome — bug reports, feature requests, code, documentation, and localization.
- Code of Conduct
- Reporting Bugs
- Requesting Features
- Setting Up a Development Environment
- Running the Tests
- How the Modules Work Together
- Pull Requests
- Adding Support for a New Action Bar Addon (Provider)
- Localization
- Project Structure
- Coding Conventions
Be respectful and constructive. This is a community project maintained in the contributor's free time. Issues and PRs that are dismissive or demanding will be closed without response.
A good bug report is the single most valuable contribution you can make. Before opening a new issue:
- Search existing issues — the bug may already be reported.
- Check the Troubleshooting page — the issue may have a known fix.
- Update AHOS — confirm the bug exists in the latest release.
Open a GitHub Issue with:
| Field | Details |
|---|---|
| AHOS version | Found in the options panel title or the .toc file |
| WoW version | e.g., Retail 11.0.7, Classic Era 1.15.3 |
| Action bar addon | Dominos 10.x, Bartender4, Blizzard, etc. |
| Other UI addons | Especially ElvUI, DynamicCam, any UI replacements |
| Steps to reproduce | Numbered, specific steps |
| Expected behavior | What should happen |
| Actual behavior | What actually happens |
| Debug export | Output of /ahos debugexport — paste the full string |
| Lua errors | If you see a Lua error popup, include the full text |
Install BugSack or !BugGrabber to capture Lua errors reliably. Paste the full error including the stack trace.
Feature requests are welcome. Before submitting:
- Check that the feature isn't already implemented (see Configuration for the full option list)
- Check open issues to avoid duplicates
## Feature Request: <Short Title>
**What problem does this solve?**
<Describe the gap or limitation>
**Proposed solution**
<What you'd like to see, as specifically as possible>
**Alternatives considered**
<Other approaches you thought of>
**WoW versions affected**
<Retail / MoP Classic / Classic Era / All>
AHOS is a pure Lua WoW addon — no build system or compilation is needed.
- World of Warcraft installed (any supported version)
- A text editor (VS Code with the WoW Lua extension is recommended)
- Git
-
Fork the repository on GitHub.
-
Clone your fork into your WoW AddOns directory:
cd "World of Warcraft/_retail_/Interface/AddOns/" git clone https://github.com/<your-username>/AdvancedHotkeyOverlaySystem.git
-
Create a branch for your change:
git checkout -b feature/my-feature-name
-
Make your changes in the Lua/TOC files.
-
Test in-game:
- Log in to WoW
- After each change, type
/reloadin-game to reload the UI and pick up your edits
-
Enable Debug Mode (
/ahos debug) to see verbose output while testing.
After editing a .lua file, type /reload in WoW to pick up changes immediately. You do not need to restart the game client.
For TOC changes or new file additions, you must fully restart WoW (or at minimum, disable and re-enable the addon from the character select screen).
AHOS has a test harness that loads the addon against a mock WoW client, so most changes can be checked without starting the game. You need Lua 5.1 (lua5.1 on Linux, LuaBinaries or Lua for Windows).
From the addon folder:
lua Tools/RunTests.lua # every suite, against every client TOC
lua Tools/RunTests.lua TestCore # one suite
lua Tools/TestCore.lua AdvancedHotkeyOverlaySystem_Vanilla.toc # one suite, one client| File | What it does |
|---|---|
Tools/Mock.lua |
The mock client. Loads every file a TOC lists, in order, with the real pure-Lua Ace3 libraries and stand-ins for the ones that draw. Frames record their text, colours, points and scripts, so a test can read what was drawn. game.MakeButton(name, { action = n }) makes an action button; game.bindings, game.actions, game.combat, game.shift, game.cursor and game.lockdown set the client's state; game.fire(event, ...) fires an event and game.flush() runs pending timers. |
Tools/TestPanel.lua |
The options window: every page, every row bound, the preview, the changed-from-default marks, search, the landing page under ESC > Options > AddOns, the multi-line field. |
Tools/TestCore.lua |
The engine: key detection on Blizzard, Dominos, Bartender4, LUI and Neuron buttons, abbreviations and mouse icons, overlays, per-bar placement, glow and plate, ignored buttons, the visibility rules, combat style, press animations, the editor, presets, snapshots, profile switching, the LibKeyBound hook, the slash commands, and a timing of a full update over 300+ buttons. |
Tools/TestLocales.lua |
Every locale loads; every string the modules ask for is in enUS; every translation keeps its %s and %d. |
Tools/RunTests.lua |
Runs each suite in its own process for each client TOC and exits non-zero on a failure. |
CI runs lua5.1 Tools/RunTests.lua on every push and pull request (.github/actions/verify), after the Lua syntax, TOC and version checks.
Translations. Module strings are named constants (L.SOME_KEY or "English"). New ones go in Tools/translations_roadmap.py (English plus de, es, fr, pt, ru, ko, zhCN, zhTW); the options window's own strings are keyed by their English text in Tools/translations.py. Then run python Tools/MakeLocales.py <file with the options/*.lua text> to write them into locales/. TestLocales fails if a new string has no entry.
Art. python Tools/MakePanelArt.py draws the options window's art and python Tools/MakeGlyphs.py the mouse icons (both need Pillow).
Every module is an AceAddon module created in AdvancedHotkeyOverlaySystem.lua and filled in by its file under modules/:
| Module | Job |
|---|---|
| Config | The default profile (GetDefaultProfile) and the font list |
| Core | Events: bindings, paging, vehicles, combat. Batches refreshes into one series of passes (ScheduleRefreshPasses) and hooks LibKeyBound |
| Performance | The update queue: QueueFullUpdate and QueueButtonUpdate, processed together 0.1 s later |
| Bars | Finds the buttons (GetAllButtons), names their bars (GetBarInfo), and keeps per-bar and per-button settings |
| Keybinds | The key on a button (GetFullBindingText), its abbreviation (GetBinding), glyphs and icons |
| Display | Draws the overlays or rewrites the native labels, and gives the labels back. Four files: DisplayShared.lua (state and helpers, shared as privateScope.DisplayShared), Display.lua (the update flow), DisplayNative.lua (the buttons' own labels), DisplayStyle.lua (font, place, Masque, glow, plate, scale) |
| Visibility | Fades the overlays by the visibility rules, the combat style, press animations |
| Editor | /ahos edit |
| Profiles | Presets, snapshots, switching profiles |
| Options | The option table, in AceConfig's format, which the options window (options/) reads and draws. Options.lua puts it together from one file per page (OptionsStatus, OptionsDisplay, OptionsText, OptionsBars, OptionsProfiles), which share Options.Helpers
|
A change flows: an event → Core:FullUpdate() → Performance:QueueFullUpdate() → Display:UpdateAllOverlays() → for each button Keybinds:GetBinding() and Display:StyleOverlay() → Visibility:Track().
Types. types/AHOS.lua describes the profile, the providers, overlays, snapshots and every module for the Lua language server (it is not loaded by the game or packaged). Each module file declares its class where it takes its module (---@class AHOSDisplay above local Display = addon.Display), so methods defined there are known everywhere. Check the workspace with the language server's --check mode.
SafeCall. Until the player is in the world, addon:SafeCall(moduleName, method, ...) queues the call (the same call without arguments only once) and runs the queue when addon:SetReady() is called on PLAYER_LOGIN. After that it calls straight through. Use it for any cross-module call that can happen during loading.
Bar addons are data, not code: each is an entry in addon.ProviderRegistry in the core file (see Adding Support for a New Action Bar Addon).
- Run
lua Tools/RunTests.luaand make sure every run passes - Test your change in-game on at least one WoW version (Retail preferred)
- If your change affects multiple providers (Blizzard, Dominos, etc.), test each
- Ensure you haven't introduced any Lua errors (
/reloadwith BugSack installed is a good check) - Keep PRs focused — one logical change per PR
- Describe what the PR does and why
- Reference any related issues (
Closes #123) - Tested in-game without Lua errors
- Does not break existing behavior
- Follows existing code style (see Coding Conventions)
- Updated relevant wiki pages if behavior changed
[Feature] Add support for XYZ addon
[Fix] Overlays not appearing on ExtraActionButton after vehicle exit
[Chore] Clean up deprecated API calls
[Docs] Update Troubleshooting page
Adding a new action bar provider is the most impactful type of contribution. Here's what's involved:
Use /fstack (WoW frame inspector) or a UI debugging addon to find the frame names of the action buttons created by the addon. You need:
- The frame name prefix (e.g.,
DominosActionButton) - The naming convention (e.g.,
Prefix1throughPrefixN, or a nested structure) - Any special parent frames
Determine how the addon reports keybinds:
- Does it use the standard
GetBindingKey("ACTIONBUTTON1")etc.? - Does it have its own keybind table or API (like Dominos)?
- Is LibKeyBound-1.0 involved?
A provider is an entry in addon.ProviderRegistry in AdvancedHotkeyOverlaySystem.lua, plus its key in PROVIDER_ORDER. The fields:
| Field | What it says |
|---|---|
addon / addons / globals
|
How to tell the addon is loaded |
button_patterns |
Lua patterns that match its button names |
button_scanners.globals |
{ prefix, max }: buttons named prefix1..prefixN (scanning goes on past max while names continue) |
button_scanners.containers |
{ prefix, count, matcher }: bar frames whose children are the buttons |
binding_rules |
{ matcher, suffixes }: the key is found on CLICK <button><suffix>
|
bound_keys_path |
For addons that bind with override bindings: where on the button its own key list is (Neuron: { "keys", "hotKeys" }) |
hotkey_fields |
The button's label field when it is not HotKey (Neuron: Hotkey) |
hotkey_update_method(s) |
The method that redraws the label, hooked so AHOS rewrites after it |
Then add the bar names to BAR_PATTERNS in modules/Bars.lua, so the Bars page names its bars.
Make a few of its buttons in Tools/TestCore.lua with game.MakeButton, give them bindings in the bindings table at the top, and check what Keybinds:GetBinding and Bars:GetBarInfo answer (the LUI and Neuron checks are the pattern).
- Test with at least the provider's most common configuration
- Test adding/removing bars mid-session
- Test
/ahos reloadand/ahos detectui - Test with debug mode on (
/ahos debug) to verify events fire correctly
Include in the PR description:
- The addon name and CurseForge link
- WoW versions you tested on
- Any known limitations
AHOS uses locale files in the locales/ directory. Each locale file corresponds to a language code (e.g., enUS.lua, deDE.lua, frFR.lua).
- Find or create the locale file for your language in
locales/. - Add or update the string entries following the existing format.
- Test in-game with that locale set (change your WoW client language in Battle.net settings).
- Submit a PR with your changes.
-- locales/deDE.lua
local L = LibStub("AceLocale-3.0"):NewLocale("AdvancedHotkeyOverlaySystem", "deDE")
if not L then return end
L["Enable Addon"] = "Addon aktivieren"
L["Font Size"] = "Schriftgröße"
-- etc.Strings that have not been translated fall back to the enUS (English) base locale automatically.
The following languages are most commonly used by the WoW playerbase and are high priority for localization:
enUS, deDE, frFR, esES, esMX, ruRU, zhCN, zhTW, koKR, ptBR
AdvancedHotkeyOverlaySystem/
├── AdvancedHotkeyOverlaySystem.lua ← Modules, provider registry, SafeCall, slash commands
├── AdvancedHotkeyOverlaySystem.toc ← Development TOC (not packaged)
├── AdvancedHotkeyOverlaySystem_Mainline.toc ← Retail
├── AdvancedHotkeyOverlaySystem_Camelot.toc ← WoW Forever (keep its file list in sync with Mainline)
├── AdvancedHotkeyOverlaySystem_Mists.toc ← MoP Classic
├── AdvancedHotkeyOverlaySystem_Vanilla.toc ← Classic Era
├── Libs/ ← Bundled libraries (do not edit)
├── locales/ ← Strings per language (generated blocks from Tools/)
├── media/ ← Logo, options window art (panel/), mouse icons (glyphs/)
├── modules/ ← The engine (see How the Modules Work Together)
├── options/ ← The options window
├── Tools/ ← Test harness, locale and art generators (not packaged)
└── wiki/ ← This wiki (published on push)
AHOS follows standard WoW Lua conventions:
- Indentation: Tabs (not spaces)
-
Naming:
- Local variables and functions:
camelCase - Module-level or global references:
PascalCase - Constants:
UPPER_SNAKE_CASE
- Local variables and functions:
- Nil checks: Always guard against nil before calling methods on potentially-nil values
-
API compatibility: Use version-safe API calls where Retail and Classic APIs differ. Use
C_AddOnswhere available, with fallback toIsAddOnLoadedfor older versions. -
No global pollution: All AHOS code should be namespaced under the
AHOStable or within local scope - Comments: Add comments for non-obvious logic, especially around WoW API quirks
When using APIs that differ between Retail and Classic:
-- Good practice: version-safe addon detection
local isLoaded = (C_AddOns and C_AddOns.IsAddOnLoaded or IsAddOnLoaded)("SomeAddon")Check the WoWpedia API reference for API availability by version.