Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d9ee5d6
Reorg phase 1: move all non-core files out of the addon root
Krathe82 Jul 31, 2026
1d51671
GUI.lua: hold mutable module state on one table
Krathe82 Jul 31, 2026
405a82b
GUI.lua: publish wide-reaching private helpers on GUI._priv
Krathe82 Jul 31, 2026
9a2f5ec
Split GUI.lua (13,343 lines) into five files
Krathe82 Jul 31, 2026
e39667d
AuraDesigner editor: hold mutable state on one table
Krathe82 Jul 31, 2026
8501daf
AuraDesigner editor: publish cross-cut private helpers on _priv
Krathe82 Jul 31, 2026
4bb27b0
Split the Aura Designer editor (9,093 lines) into five files
Krathe82 Jul 31, 2026
b31f4ad
Fix nil CreateElementBackdrop after the GUI.lua split
Krathe82 Jul 31, 2026
fcdff72
Fix nil TYPE_DEFAULTS after the Aura Designer split
Krathe82 Jul 31, 2026
d5ce61f
Split the settings pages (9,681 lines) into five files
Krathe82 Jul 31, 2026
122a5eb
Add a resident TestMode shim so test mode can become load-on-demand
Krathe82 Jul 31, 2026
fa8aee8
Move the Aura Designer migrations to a resident file
Krathe82 Jul 31, 2026
e01be03
Restructure the repo as a container for two addon folders
Krathe82 Jul 31, 2026
661edef
README: document the container layout and junction setup
Krathe82 Jul 31, 2026
d97ecfa
README: describe the layout that exists, not the planned one
Krathe82 Jul 31, 2026
ff3a023
Add the DandersFrames_Options load-on-demand companion
Krathe82 Jul 31, 2026
58a8c8b
Add the missing GUI/LoadOptions.lua TOC entry
Krathe82 Jul 31, 2026
b8e32f0
Fix a resident file caching a companion-owned table at load
Krathe82 Jul 31, 2026
55bf9bb
Fix the companion's addon table and its stranded login-time work
Krathe82 Jul 31, 2026
cff68e4
AutoProfiles: rebuild CONTENT_TYPES in place
Krathe82 Jul 31, 2026
17128b6
Split the Auto Profiles engine out to the resident addon
Krathe82 Jul 31, 2026
621cdd2
README: document the second junction and the resident-behaviour rule
Krathe82 Jul 31, 2026
fc31e7c
Move settings-only widgets out of the resident GUI toolkit
Krathe82 Jul 31, 2026
930ca09
Fix live tooltips breaking when the settings panel is unloaded
Krathe82 Jul 31, 2026
3340d78
Move settings search into the companion
Krathe82 Jul 31, 2026
b2a2372
Fix everything that stopped working when the settings panel was not l…
Krathe82 Jul 31, 2026
b080e81
Move four settings-only blocks to the companion (~290 KB resident)
Krathe82 Jul 31, 2026
cf79086
Perf hygiene from the audit sweep
Krathe82 Jul 31, 2026
4604894
.pkgmeta: strip TRANSLATING.md and .gitnexus from the zip
Krathe82 Jul 31, 2026
614e082
Drop third-party addon names from .pkgmeta
Krathe82 Jul 31, 2026
803c7e4
generate_changelog.sh: sync the companion TOC version too
Krathe82 Aug 1, 2026
1c8f3eb
Fix review findings from PR #222
Krathe82 Aug 1, 2026
78719df
gui-conventions: the menu registry straddles the addon boundary
Krathe82 Aug 1, 2026
e0586ad
Move GUI:CloseAllMenus back resident, with its registry
Krathe82 Aug 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
DOMINATED=true
for f in $(git diff --name-only HEAD~1..HEAD 2>/dev/null); do
case "$f" in
*.md|*.sh|.github/*|.gitignore|.pkgmeta|Changelog.lua) ;;
*.md|*.sh|.github/*|.gitignore|.pkgmeta|DandersFrames/Core/Changelog.lua|DandersFrames_Options/Changelog.lua) ;;
*) DOMINATED=false; break ;;
esac
done
Expand All @@ -57,8 +57,8 @@ jobs:
fi
./generate_changelog.sh

VERSION=$(grep '^## Version:' DandersFrames.toc | sed 's/^## Version: //')
CHANNEL=$(grep 'DF.RELEASE_CHANNEL' Changelog.lua | sed 's/.*= "\(.*\)"/\1/')
VERSION=$(grep '^## Version:' DandersFrames/DandersFrames.toc | sed 's/^## Version: //')
CHANNEL=$(grep 'DF.RELEASE_CHANNEL' DandersFrames/Core/Changelog.lua | sed 's/.*= "\(.*\)"/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT

Expand Down
22 changes: 22 additions & 0 deletions .pkgmeta
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
package-as: DandersFrames
enable-nolib-creation: no

# The repo root is a container: it holds the addon folders rather than being
# one. The packager copies the checkout into .release/<package-as>/, so both
# land a level too deep; move-folders lifts them back out to the zip root.
#
# move-folders keys are relative to the working directory and INCLUDE the
# package name as the root; values are relative to the package root. So
# "DandersFrames/DandersFrames: DandersFrames" reads as "the DandersFrames
# folder inside the package dir becomes the top-level DandersFrames" -- it
# looks like it collides with its own parent and does not. This is the
# documented shape for a container repo that ships more than one folder;
# verified against a working example before writing it, after talking myself
# out of it once on the assumption that it would collide.
#
# Everything at the repo root that is not an addon folder is stripped by the
# ignore list below, so the zip contains only the two addon folders.
move-folders:
DandersFrames/DandersFrames: DandersFrames
DandersFrames/DandersFrames_Options: DandersFrames_Options

ignore:
- CHANGELOG.md
- README.md
- TRANSLATING.md
- generate_changelog.sh
- Tools
- .github
- .gitignore
# Editor/tooling metadata (wow-api.json and friends). Nothing reads it at runtime.
- .gitnexus
# Contributor documentation. Tracked in git so it travels with pull requests,
# stripped here so it never reaches the packaged addon users download.
- docs
Expand Down
8,997 changes: 0 additions & 8,997 deletions AuraDesigner/Options.lua

This file was deleted.

1,498 changes: 0 additions & 1,498 deletions Changelog.lua

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
622 changes: 622 additions & 0 deletions DandersFrames/AuraDesigner/Migrations.lua

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3787,11 +3787,18 @@ SlashCmdList["DFCC"] = function(msg)
-- user was never shown. Fall through to "unknown" rather than
-- reporting "dev only": a hidden command must not advertise itself.
if e[1] == word and (not e[3] or dev) then
-- The handlers register when the companion loads. A deliberate
-- command -> load it, then retry the lookup. The old branch
-- errored with "click-casting module not loaded", which was
-- false (only the UI wasn't) and never self-healed.
local handler = SlashCmdList[e[2]]
if not handler and DF.EnsureOptionsLoaded and DF:EnsureOptionsLoaded() then
handler = SlashCmdList[e[2]]
end
if handler then
handler(rest or "")
else
DF:Err("click-casting module not loaded")
DF:Err("click-casting UI unavailable")
end
return
end
Expand Down
File renamed without changes.
File renamed without changes.
46 changes: 40 additions & 6 deletions ClickCasting/Events.lua → DandersFrames/ClickCasting/Events.lua
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function CC:RegisterEvents()
CC:ApplyBindings()
-- Also refresh UI in case talents changed
CC:DeferAfter("uiRefresh", 0.3, function()
CC:RefreshClickCastingUI()
CC:RefreshUIIfLoaded()
end)
end)
else
Expand All @@ -78,14 +78,16 @@ function CC:RegisterEvents()
if not InCombatLockdown() then
CC:ApplyBindings()
CC:DeferAfter("uiRefresh", 0.2, function()
CC:RefreshClickCastingUI()
CC:RefreshUIIfLoaded()
end)
else
CC:Defer("bindingRefresh")
end
elseif event == "PLAYER_EQUIPMENT_CHANGED" then
-- Equipment changed - refresh items tab if visible
if CC.activeTab == "items" then
-- Equipment changed - refresh items tab if visible. activeTab is only
-- ever set by the companion, but guard the method explicitly rather
-- than lean on that invariant.
if CC.activeTab == "items" and CC.RefreshSpellGrid then
CC:RefreshSpellGrid()
end
elseif event == "PLAYER_ENTERING_WORLD" then
Expand Down Expand Up @@ -360,7 +362,7 @@ function CC:DrainDeferred(onlyJob)
-- Refresh UI if needed (after a short delay for everything to settle)
if needsUIRefresh then
self:DeferAfter("uiRefresh", 0.2, function()
CC:RefreshClickCastingUI()
CC:RefreshUIIfLoaded()
end)
end
end
Expand All @@ -377,6 +379,18 @@ end
-- DeferAfter keys each timer: scheduling the same key again cancels the
-- pending one, so there is always at most one run in flight per key.

-- ☠ The click-casting UI lives in the load-on-demand companion; this file is
-- resident. Every "refresh the panel" reaction to a talent/spec/level event
-- must go through this guard: with the panel never opened the method is nil,
-- and DeferAfter runs its callback raw inside a C_Timer, so an unguarded call
-- is a Lua error on every talent swap. A refresh with no UI is correctly a
-- no-op -- the panel builds itself from current state when it loads.
function CC:RefreshUIIfLoaded()
if self.RefreshClickCastingUI then
self:RefreshClickCastingUI()
end
end

function CC:DeferAfter(key, delay, fn)
self.timers = self.timers or {}
local existing = self.timers[key]
Expand Down Expand Up @@ -412,7 +426,7 @@ function CC:OnSpecChanged()
self:ApplyBindings()
-- Refresh UI after a short delay to ensure spell data is ready
self:DeferAfter("uiRefresh", 0.3, function()
CC:RefreshClickCastingUI()
CC:RefreshUIIfLoaded()
end)
else
self:Defer("loadoutCheck")
Expand Down Expand Up @@ -629,6 +643,26 @@ function CC:GetNameplateClickableFrame(nameplate, unitToken)
return nil
end

-- ============================================================
-- BOOTSTRAP
-- ============================================================
-- ☠ This must stay resident. It used to live in ClickCasting/UI/BindingEditor,
-- which is now in the load-on-demand companion -- and the trigger below only
-- fires on the login/reload PLAYER_ENTERING_WORLD. A companion loaded on demand
-- has already missed it, and the zone-change firings are filtered out by the
-- flags, so click-casting would never have initialised at all.
--
-- CC:Initialize itself is in ClickCasting/Frames.lua and is resident too.
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
initFrame:SetScript("OnEvent", function(self, event, isInitialLogin, isReloadingUi)
-- Only initialize on first load or reload, not zone changes
if isInitialLogin or isReloadingUi then
CC:Initialize()
self:UnregisterEvent("PLAYER_ENTERING_WORLD")
end
end)

-- Register all currently visible nameplates
function CC:RegisterAllNameplates()
if not self.db or not self.db.enabled then return end
Expand Down
13 changes: 13 additions & 0 deletions ClickCasting/Frames.lua → DandersFrames/ClickCasting/Frames.lua
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,19 @@ function CC:InitializeSecureFrames()
-- Delay slightly to ensure UI is ready
C_Timer.After(1.5, function()
if CC.db.enabled and CC.hasConflictingAddons then
-- ☠ Read the opt-out FIRST. The popup lives in the companion and
-- this fires at login, so loading it here is the cost -- but
-- ShowClickCastConflictPopup's own first line is this same check
-- and returns immediately. Without this, a user who ticked "don't
-- warn me again" pulled the entire settings addon in at every
-- login to reach a function that does nothing. The flag lives in
-- DandersFramesClickCastingDB and is fully readable resident.
if CC.db.ignoreConflictWarning then return end
-- It is a modal the user must act on, so load rather than skip.
-- Only users with a conflicting addon installed ever pay.
if not CC.ShowClickCastConflictPopup then
if not (DF.EnsureOptionsLoaded and DF:EnsureOptionsLoaded()) then return end
end
CC:ShowClickCastConflictPopup(CC.conflictingAddons, CC.enableCb)
end
end)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,15 @@ function CC:SetActiveProfile(profileName)
if self.hasConflictingAddons and self.conflictingAddons then
C_Timer.After(0.1, function()
if CC.db.enabled and CC.hasConflictingAddons then
-- ☠ Opt-out first — the popup's own first line is this check,
-- so loading the companion to reach it would be pure waste.
if CC.db.ignoreConflictWarning then return end
-- Popup lives in the companion; profile switches are reachable
-- without the panel (slash, permanent movers, loadout
-- auto-switch). Modal warning -> load, don't skip.
if not CC.ShowClickCastConflictPopup then
if not (DF.EnsureOptionsLoaded and DF:EnsureOptionsLoaded()) then return end
end
CC:ShowClickCastConflictPopup(CC.conflictingAddons, CC.enableCb)
end
end)
Expand Down Expand Up @@ -536,7 +545,7 @@ function CC:CheckLoadoutProfileSwitch()
-- Profile is assigned (either to this specific loadout or as spec default) - switch to it
if self:SetActiveProfile(assignedProfile) then
self:ApplyBindings()
self:RefreshClickCastingUI()
self:RefreshUIIfLoaded()
local source = isSpecific and "loadout: " .. loadoutName or "spec default"
DF:Say("Switched to profile: " .. assignedProfile .. " (" .. source .. ")")
end
Expand Down Expand Up @@ -571,7 +580,7 @@ function CC:CheckLoadoutProfileSwitch()
-- Switch to it
if self:SetActiveProfile(newProfileName) then
self:ApplyBindings()
self:RefreshClickCastingUI()
self:RefreshUIIfLoaded()
end

-- Show notification
Expand Down
24 changes: 23 additions & 1 deletion Core.lua → DandersFrames/Core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4942,6 +4942,15 @@ DF._MainEventDispatcher = function(self, event, arg1)
-- "on"/"off" are the logging toggle, not commands named on/off.
if dbgWord and dbgWord:lower() ~= "on" and dbgWord:lower() ~= "off" then
local dbgKey = DF.DebugSlashBySub[dbgWord:lower()]
-- Several debug tools live in the companion and register their
-- slashes only when it loads. If the word is unknown and the
-- companion is not in yet, load it and retry once -- otherwise
-- the first use of /df debug memtest fell through to the final
-- else and opened the settings window instead of the tool.
if not dbgKey and not DF._optionsAddonLoaded
and DF.EnsureOptionsLoaded and DF:EnsureOptionsLoaded() then
dbgKey = DF.DebugSlashBySub[dbgWord:lower()]
end
if dbgKey and SlashCmdList[dbgKey] then
SlashCmdList[dbgKey](dbgRest or "")
else
Expand Down Expand Up @@ -5216,6 +5225,10 @@ DF._MainEventDispatcher = function(self, event, arg1)
-- is generated and cannot drift instead of duplicating a subset
-- of it by hand.
elseif msg == "test" then
-- The test panel lives in the load-on-demand companion.
-- Deliberate user command -> load it; the old nil-guard made
-- /df test a silent no-op until the settings panel was opened.
if DF.EnsureOptionsLoaded and not DF:EnsureOptionsLoaded() then return end
if DF.ToggleTestPanel then DF:ToggleTestPanel() end
elseif msg == "hide" then
if DF.HideTestFrames then DF:HideTestFrames() end
Expand Down Expand Up @@ -5487,7 +5500,11 @@ DF._MainEventDispatcher = function(self, event, arg1)
DF:Err("Header info not available")
end
elseif msg == "attached" then
-- List other addons anchored/parented to DF unit frames
-- List other addons anchored/parented to DF unit frames.
-- Scanner lives in the companion and registers no slash of its
-- own, so it never reaches the /df debug load-and-retry — this
-- branch has to load it, same as exportaudit two branches down.
if DF.EnsureOptionsLoaded and not DF:EnsureOptionsLoaded() then return end
if DF.ScanFrameAttachments then
DF:ScanFrameAttachments()
else
Expand All @@ -5503,6 +5520,8 @@ DF._MainEventDispatcher = function(self, event, arg1)
elseif msg == "exportaudit" then
-- Dev: verify every Config default is export-categorised or
-- declared local-only (guards against export-list drift).
-- Category tables live in the companion.
if DF.EnsureOptionsLoaded and not DF:EnsureOptionsLoaded() then return end
if DF.AuditExportCategories then
DF:AuditExportCategories()
end
Expand All @@ -5520,6 +5539,9 @@ DF._MainEventDispatcher = function(self, event, arg1)
-- can find the current ID (for re-hardcoding after reshuffles).
-- Results also land in the SavedVariables root (flushed by the
-- next /reload) so they can be read from disk — no chat copy.
-- DF.TestData lives in the companion; load it or bail.
if DF.EnsureOptionsLoaded and not DF:EnsureOptionsLoaded() then return end
if not DF.TestData then DF:Err("test data unavailable") return end
local dump = {}
local o = DF:Out("Test IDs", "spell-ID audit")
for _, poolName in ipairs({ "buffs", "debuffs" }) do
Expand Down
File renamed without changes.
Loading