File Details
v4.2.1
- R
- Apr 19, 2026
- 3.88 MB
- 2.1K
- 12.0.5+3
- Classic + 3
File Name
KeyUI-v4.2.1.zip
Supported Versions
- 12.0.5
- 5.5.4
- 2.5.5
- 1.15.8
KeyUI
v4.2.1 (2026-04-19)
Full Changelog Previous Releases
- fix: drag no longer casts – pin useOnKeyDown=false for release-only clicks
Dragging a key button cast the mapped spell before the drag threshold was
reached. Retail's secure handler keys off the ActionButtonUseKeyDown CVar
(default true), so only AnyUp registration silently dropped the click
entirely. Register AnyUp only, override the CVar per-button with
useOnKeyDown=false, and add the missing registrations on controller buttons.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - fix: guard OpenSettings and notify on deferred close in combat
Two related combat-lockdown polish fixes on top of 12ae722:- Settings.OpenToCategory dispatches to the protected C_SettingsUtil.
OpenSettingsPanel on Retail 12.x and — new since 5.5.4 — on MoP, where
it is blocked from insecure OnClick during combat. Right-clicking the
minimap or pressing an Options button during combat blew up with
ADDON_ACTION_BLOCKED. A central InCombatLockdown() guard in
addon:OpenSettings covers all four entry points (minimap + 3 frame
Options buttons). Anniversary and Classic Era still route through
the unprotected SettingsInbound path and are unaffected in practice;
the guard is harmless there. - Frames with SecureActionButtonTemplate children cannot be :Hide()'d
during combat lockdown, so SafeHideFrame queues the hide for
PLAYER_REGEN_ENABLED. From the user's perspective the close click
looked swallowed. Print a one-shot English hint ("Close will apply
after combat ends.") from SafeHideFrame on the first deferred hide
of each combat, and reset the flag together with the queue when
combat ends.
- Settings.OpenToCategory dispatches to the protected C_SettingsUtil.
- fix: block opening in combat on clients protecting secure-child parents
MoP 5.5.4 (and Retail 12.x) refuse Show() on frames containing Secure-
ActionButtonTemplate children during combat lockdown. The minimap OnClick
previously bypassed the combat guard when stay_open_in_combat=true, which
dispatched to show_frames and triggered ADDON_ACTION_BLOCKED on
keyui_keyboard_frame:Show().
stay_open_in_combat only governs auto-close on combat entry; opening during
combat was never intended. Remove the bypass from the minimap handler and
load(), use InCombatLockdown() authoritatively, and add a defensive guard
in show_frames. Closing still defers via SafeHideFrame as before.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - fix: MoP 5.5.4 – probe AutoCast template at runtime, bump TOC
MoP 5.5.4 dropped AutoCastShineTemplate in favor of AutoCastOverlayTemplate
(same move Anniversary 2.5.5 and Retail 12.x already made). The existing
isMoP heuristic picked the wrong template and raised a CreateFrame inherit
error in Keyboard/Mouse/Controller button factories.
Probe C_XMLUtil.GetTemplateInfo once (available in all supported clients),
cache, and share via addon.CreateAutoCastOverlay so the three surfaces don't
duplicate the selection logic. Build-range heuristics break across minor MoP
patches, a runtime probe doesn't.
TOC MoP interface raised 50503 → 50504 to clear the "outdated addon" warning
on the live 5.5.4 client.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - feat: add MoErgo Glove80 keyboard layout (#62)
Adds a new 'MoErgo' category with the Glove_80 split-ergo layout as
submitted in issue #62. Registers the layout in the dropdown selector
alongside existing ZSA/Logitech entries; frame auto-resize handles the
wider ~1308px extent.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - chore: bump retail TOC to 120005 and sync API snapshot refs
Retail TOC interface raised from 120000 to 120005 to match the current
12.0.5 client. COMPATIBILITY.md updated to reference the current API
dump builds (12.0.5.67088, 5.5.3.66509, 2.5.5.66765, 1.15.8.65888);
underlying compatibility claims were re-verified and still hold.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - fix: correct Classic click/drag handling and rename Cata→MoP
RegisterForClicks: on Classic flavors (MoP/Anniversary/Vanilla), register
only AnyUp. Keeping LeftButtonDown in the list caused the mapped spell
to fire before the drag threshold triggered, making buttons uncastable
when trying to drag. Retail keeps LeftButtonDown/RightButtonDown for
ActionButtonUseKeyDown CVar support.
Drop the ad-hoc OnMouseDown cursor-drop workaround (GetCursorInfo →
handle_action_drag → type=nil → C_Timer restore) – cursor placement is
now handled natively by OnReceiveDrag, matching Blizzard's ActionButton.
AutoCastShineTemplate (MoP Classic, Classic Era) requires the overlay to
be a named frame because its XML references children by parent name. Pass
an explicit name; Retail and Anniversary (AutoCastOverlayTemplate) stay
anonymous.
Rename isCata → isMoP everywhere: build 5.5.x is Mists-of-Pandaria
Classic, not Cataclysm. Updates VersionCompat.lua, the per-version
branches in the frames and ActionButton, and the compatibility doc.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - fix: defer frame hide during combat for secure buttons
Since switching KeyUI buttons to SecureActionButtonTemplate, hiding a
parent frame during combat is blocked by Blizzard's taint system. Add a
SafeHideFrame helper that either hides immediately or queues the frame
for PLAYER_REGEN_ENABLED, and route the keyboard/mouse/controller
close-button paths and hide_all_frames through it.
Also: always set type="action" on action-slot buttons (even empty ones)
so Blizzard's native OnReceiveDrag can place the cursor item, guard
handle_action_drag with InCombatLockdown(), and rebuild key_lookup lazily
when the keypress input frame is re-shown.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - feat: action bar mode – range fix, click fix, remove LibButtonGlow
Range indicator:- nil-freeze approach: nil keeps current color (no flicker during spell
rotation/transitions), false→red, true→white – no debounce needed - Add UpdateButtonRange call at end of set_key so reset_button_state
color reset is immediately corrected (fixes OOR flicker on key press) - Fix assist/One Button slots: correctly shows red when OOR using the
same IsActionInRange path as all other slots (Blizzard does not
special-case assist slots)
Click/drag: - Restore RegisterForClicks("AnyUp","LeftButtonDown","RightButtonDown")
so LeftButton cast-on-press works with RegisterForDrag("LeftButton") - Remove suppress-restore dance (SetAttribute nil + _restore_type);
spells now fire on press, matching Blizzard action bar behavior - Keep cursor-drop path in OnMouseDown (UseAction does not auto-place
cursor items; explicit PlaceAction required) - Add InCombatLockdown guards on frame drag and EnableKeyboard calls
Sync/equipped: - sync_dragged_action_slots: deferred refresh_equipped + restore type
- ACTIONBAR_SLOT_CHANGED: deferred refresh_equipped
- MODIFIER_STATE_CHANGED: immediate refresh_equipped
LibButtonGlow: - Remove LibButtonGlow-1.0 dependency (library, embeds.xml, .pkgmeta)
- Classic Era (1.15.x) has native ActionButton_ShowOverlayGlow /
ActionButton_HideOverlayGlow – use these directly
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
- nil-freeze approach: nil keeps current color (no flicker during spell
- feat: upgrade buttons to SecureActionButtonTemplate with full action bar feature parity
- Convert all key buttons (keyboard, mouse, controller) from Frame to CheckButton
with SecureActionButtonTemplate – enables secure click-to-cast in combat - Add RegisterForClicks/RegisterForDrag for combat-safe interaction
- Introduce named frame scheme (KeyUIButton_<device>_<index>) required by SecureActionButton
- Add Frames/ActionButton.lua: shared factory and update logic for all 3 surfaces
· CreateChargeCooldownFrame, CreateLoCCooldownFrame, CreateFlashTexture,
CreateEquippedBorder, SetButtonActionSlot
· UpdateButtonChargeCooldown, UpdateButtonLoCCooldown, UpdateButtonFlash,
UpdateButtonEquipped, HandleProcGlowShow/Hide (via LibButtonGlow-1.0),
UpdateButtonPetAutoCast, ToggleButtonPetAutoCast
· Flash OnUpdate ticker (0.4 s cycle) - Add LibButtonGlow-1.0 embed (taint-safe proc glow)
- Add ActionButtonSpellFXTemplate on Retail; client-switch for AutoCastShineTemplate
vs AutoCastOverlayTemplate across all 4 WoW client versions - Guard AutoCastShine_AutoCastStart/Stop (absent in Retail and Anniversary)
- Register 10 new events: LOSS_OF_CONTROL_, PLAYER__COMBAT, AUTOREPEAT_SPELL,
SPELL_ACTIVATION_OVERLAY_GLOW_, UNIT_INVENTORY_CHANGED, UPDATE_INVENTORY_ALERTS,
PET_BAR_UPDATE - Add settings: show_charge_cooldown, show_loc_cooldown, show_attack_flash,
show_proc_glow, show_equipped_border, show_pet_autocast
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
- Convert all key buttons (keyboard, mouse, controller) from Frame to CheckButton
- fix: harden input validation, fix SaveBindings binding set, fix PickupMacro index
- SaveBindings(2) → SaveBindings(GetCurrentBindingSet()) at 5 call sites;
fixes key/macro/unbind operations for players using account-wide bindings - PickupMacro(title) → PickupMacro(macro_index) at 2 call sites;
fixes macro placement via KeyUI (API expects numeric index, not name) - has_assisted_combat guard replaces has_modern_spellbook for Assisted Combat
menu entry to use the correct API_COMPAT flag - Deserializer hardened against pathological inputs: depth limit (128),
node limit (200 000), string-length limit, payload size limit (512 KB) - NormalizeLayoutName() centralises layout-name sanitisation (max 120 chars)
and is applied in save_keyboard/mouse/controller_layout, RenameLayout,
CopyLayout, SerializeLayoutPayload, ImportLayoutString and the copy dialog - Macro loop bounds replaced: hardcoded 36/54 → MAX_ACCOUNT_MACROS /
MAX_CHARACTER_MACROS WoW globals - Range-poll OnUpdate: early-return guard moved before refresh_range() call
- CI: add luac -p syntax check over all project Lua files before packaging
- COMPATIBILITY.md: update API dump build numbers and clarify AssistedCombat
availability notes
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
- SaveBindings(2) → SaveBindings(GetCurrentBindingSet()) at 5 call sites;

