PersonalShopper-v0.7.0
What's new
PersonalShopper Changelog
[v0.7.0] (2026-10-08) - New Window, ItemDB Prices, Sister Guilds, LibDBIcon as a Dependency
New Features
- The window is rebuilt on LibAceGUIWidgets, three tabs instead of six. The look is Grouper's: a
ClearFramebuilt once per session and never released, a compactLAGW-Striptoolbar across each tab (view chips, the library's search box, one-word dropdowns, strip buttons), a zebraW.RowListfilling the rest with sortable headers, hover tooltips and the row actions as 14px icons, the counts in the frame's status bar, and the frame's own info, settings-gear and Close buttons. Orders carries My Orders / Available / History as chips; Shopping List carries the list and its History; GoldFundMe has Mine / Others chips. Every popup is aUI.Dialog(one ClearFrame per key, never released, Escape closes it), docked outside the main window's top-right edge withW:DockWindowso it follows the window, and only one is open at a time. Window size and position persist inprofile.windowthroughW:PersistWindow; a size saved by the six-tab window is dropped once and its position kept. Every entry point other files call (SelectTabwith the old tab names,RefreshOrders,ShowCreateOrderDialog,RebuildDonorListDialog, ...) is kept and mapped onto the new tabs. Decided by the operator 2026-10-07, including the density rule (one strip row, no title or spacer labels, counts in the status bar). Seen in a game client only as the stage-2 placeholder; the finished tabs have not been looked at in game. Location:GUI/UI.lua,GUI/MainFrame.lua,GUI/OrdersTab.lua,GUI/ShoppingListTab.lua,GUI/GoldFundMeTab.lua. - Item lookup and prices come from ItemDB. The bundled databases (
DB/ItemDatabase*.lua, ~7 MB, English only, loaded on every flavour) and the AH scanner (Modules/AHScanner.lua,GUI/AHScannerFrame.lua,Compat/AHCompat.lua) are removed.Core/ItemSearch.luareadsLibStub("LibItemDB-1.0")onceDB:IsReady(): per-flavour, localised data that works for items the client has not cached. Its public API is unchanged; autocomplete stays prefix-first on a sorted index built lazily fromDB:Iterate(), becauseDB:Searchcaps its hits before sorting. The Shopping List gains a Best AH Price column fromDB:GetPrice(itemId, "best"), whose tooltip names the source, statistic and age. ItemDB (CurseForgelibitemdb) and LibAceGUIWidgets (libaceguiwidgets) are hard dependencies in both TOCs and.pkgmeta. KNOWN COST: on clients whose Interface number ItemDB's TOCs do not list (38000/38002 Titan Reforged and older patches), ItemDB -- and so PersonalShopper -- needs "load out of date addons"; not verified in a client. Location:Core/ItemSearch.lua,GUI/ShoppingListTab.lua, both TOCs,.pkgmeta. - Guild-only GoldFundMe campaigns reach the owner's sister guilds.
GoldFundMe:IsVisibleshows a guild-only campaign to its owner, to the owner's guild, and to any guild on the VIEWER's LibGuildRoster sister list (GR:IsSisterGuildKey(faction .. "-" .. ownerGuild)), the gate TOGProfessionMaster uses; the owner's own list does not fit the 255-byte channel message. A guild-only campaign naming no guild is hidden. The tab redraws onOnSisterConfigChangedandPLAYER_GUILD_UPDATE, and the checkbox, its tooltip and the confirmation line say "your guild and its sister guilds", naming the sister guilds when there are any. GuildRoster (libguildroster) is a hard dependency of both TOCs; it was installed before only as DeltaSync's dependency. Location:Core/GoldFundMe.lua,GUI/GoldFundMeTab.lua, both TOCs,.pkgmeta.
Bug Fixes
Guild-only GoldFundMe campaigns leaked to the whole server. Three separate holes, all mine: the channel codec dropped
guildOnlyandownerGuild, so every receiver stored a guild-only campaign as open; a same-version re-broadcast (the owner's Sync button) then overwrote a receiver's copy with that open one; andOnRequestReceivedwhispered every active campaign, guild-only included, to anyone who asked. The GF message now carries both as fields 13-14, after everything an older client parses (an older sender's 12-field message reads as "not said", never as open);MergeCampaignkeeps a campaign guild-only once it is; and the whisper reply is gated byGoldFundMe:MaySendTo-- a guild-only campaign goes only to a requester LibGuildRoster places in our guild or a sister roster, and to nobody when the library is absent. The owner's faction is now stamped on a new campaign so the sister key can be built. KNOWN COST: clients older than this release drop fields 13-14 and still show guild-only campaigns to everyone. Pinned by twelve specs inTests/goldfundme_spec.lua, the 255-byte limit included. Location:Core/Communication.lua,Core/GoldFundMe.lua.The mailbox Send button stopped tracking the subject for the rest of the session. To keep the
PS:/GFM:subject from being overwritten, both mail pathsSetScript'd the subject box'sOnTextChanged, replacing Blizzard'sSendMailFrame_CanSend(Classic EraMailFrame.xml), andClearSubjectHookthen set it tonil.MailHandler:GuardSubjectnowHookScripts the box once beside Blizzard's handler andClearSubjectHookonly clears the guarded subject. The guard also stopped too late: it was cleared in ourMAIL_SEND_SUCCESShandler, but Blizzard's own handler resets the box (SendMailFrame_Reset->SetText("")), and a guard still armed wrotePS:/GFM:back into it, so the next ordinary mail could go out under an order's subject and be counted against it. It is now cleared in theSendMailhook, once the subject has been handed over (peer-review thread23302f28). The GoldFundMe guild name on the wire is no longer cut to 30 bytes, which could split a multibyte character and stop the owner's own guild matching it. Location:Modules/MailHandler.lua,Core/Communication.lua.Donating away from a mailbox printed two chat lines.
PrepareGFMMailprinted its own line and returned a message the Donate dialog printed too; it now only returns it. Location:Modules/MailHandler.lua.Chat links went through a deprecation alias.
ChatEdit_InsertLinkis only an alias ofChatFrameUtil.InsertLinkon current clients (Blizzard_DeprecatedChatInfo/Deprecated_ChatFrame.luain the Classic Era tree).UI.InsertLinkprefers the namespaced function and falls back to the global; every item click uses it. Location:GUI/UI.lua, the three tab files.addon.isForeverwas false on a real WoW Forever client. v0.6.6 assumed Forever reportsWOW_PROJECT_MAINLINEand told it apart by interface number; a live Forever client reportsWOW_PROJECT_ID18 (observed in-client by FastGuildInvite on 2026-10-01, build 1.60.1.70124; the harness's Forever shape now reports the same as of53dad60). So on Forever the flag was false and the item-class taxonomy inGUI/MainFrame.lua--WOW_PROJECT_ID ~= WOW_PROJECT_CLASSIC and not addon.isForever-- added TBC's Gem class and trade-goods subclasses to a game that has neither. The flag is nowWOW_PROJECT_ID == 18, the same test as FastGuildInvite'sgv.isForever;MainFrame.luaneeded no change. The only other reads ofWOW_PROJECT_IDin the shipped code are these two (searched; theCompat/shims key on API presence). Still not observed on a Forever client here. Pinned byTests/forever_spec.lua. Location:init.lua.Replica installs could lose a live file on a save-by-replace.
Sync-Filein the dev sync script removed every replica copy on aDeletedwithout checking whether the source still had the file. This copy decides by snapshot diff and only reportsDeletedonceTest-Pathsays the file is gone, so the window was narrow, but a save that replaces the file between the scan and the delete would still wipe it from_classic_,_anniversary_and_retail_until the next launch.Sync-Filenow turns aDeletedfor a file that is back on disk into aChangedand re-copies it (the fix TOGProfessionMaster found and shipped, peer-review thread65b10702). Not reproduced deliberately. Location:wow-version-replication.ps1.Packaging checker clean. The harness's
pkgmeta.luareported nine FAILs for locale files thatLocale/Locale.xmlonly named inside XML comments -- nothing loaded them, but the dead lines read as shipped loads -- and twoignore:entries (*.txt,*.bat) that matched no file. The dead comment lines and the two entries are gone;lua Tests/wowapi/pkgmeta.luanow reports no FAIL, and confirms the upload carrieslibdbicon-1-0as a required dependency (neither embedded library carries the@curseforge-project-slugmarker that would turn it into an embed). Location:Locale/Locale.xml,.pkgmeta.CurseForge description linked to GitHub. Player-facing docs point at Discord only; the GitHub issues link is removed. Location:
docs/Curseforge_Description.html.
Improvements
- LibDBIcon-1.0 is a required dependency instead of an embedded copy, on every flavour except WoW Forever. An embedded copy only updates when PersonalShopper releases; the standalone
LibDBIcon-1.0addon (CurseForgelibdbicon-1-0, whose ownembeds.xmlbrings LibDataBroker-1.1) updates for every player on its own.PersonalShopper.toclists it in## Dependenciesand no longer loadsLibs/;.pkgmetalistslibdbicon-1-0underrequired-dependencies. The standalone's TOC does not declare Interface16001, so Forever gets its ownPersonalShopper_Camelot.toc, which keeps loading the embeddedLibs/LibDataBroker-1.1andLibs/LibDBIcon-1.0(both stay in the repo for it) and takes them asOptionalDeps;16001is gone from the base TOC's Interface list. No Lua changed. Decided by the operator 2026-09-25 (contract from TOGTools, thread1a805417). KNOWN COST: a player installing by hand must also install LibDBIcon-1.0, andrequired-dependenciesis project-wide, so a Forever player is asked to install it too even though Forever never loads it. Forever picking up the_CamelotTOC is not verified -- no Forever client is installed here. Location:PersonalShopper.toc,PersonalShopper_Camelot.toc,.pkgmeta. - Brand colour on the addon title. Both TOCs'
## Titleis now|cffFF8000Personal Shopper|r, the Forever TOC with a grey|cff999999Forever|rtag (fleet contract from DeltaSync, threadc5eece0f). Not a library, so no## Categoryline. Not yet seen in a game client's AddOns list. Location:PersonalShopper.toc,PersonalShopper_Camelot.toc. - Test suite on harness
0de2e0a(was0b51701).0de2e0acarries the harness's fix for this addon's report that its packaging checker counted<Script>tags inside XML comments (675962f). The loader takes LibDBIcon from the installed standalone through the harness'sLibDBIcon-1.0module rather than the vendored copy, as the base TOC now does.Tests/toc_spec.luaasserts the loader list against the base TOC with noLibs/lines, that the base TOC hard-depends on LibDBIcon-1.0, that the Forever TOC lists the same files after its two embedded lines and is the only one loadingLibs/, and that both titles are orange with every colour code closed;Tests/forever_spec.luachecks that Forever's16001lives only in the_CamelotTOC. Seven assertions writ flagged as weak (is_nilon a literal key, which passes if the key is misspelled) now pin a positive value on the same key first (peer-review thread0e294aca). Location:Tests/. - One copy of each dialog helper. The Orders and GoldFundMe tabs each carried
Field/Text/Button, and the Orders and Shopping List tabs each carried a dock function; they areUI.Field,UI.Text,UI.ButtonandUI.DockDialoginGUI/UI.luanow, and the stage-2 stand-insUI.LegacyHeaderRow/UI.LegacyViewSwitchare gone.UI.Dialogresets the reused frame'sOnCloseitself: thedlg:SetCallback("OnClose", nil)lines in the tabs did nothing, because AceGUI'sSetCallbackignoresnil. Location:GUI/. - Test suite for the new window, on the same harness pin (
0de2e0a). New specs for each tab (Tests/orderstab_spec.lua,Tests/shoppinglisttab_spec.lua,Tests/goldfundmetab_spec.lua) and the shell (Tests/mainwindow_spec.lua), which now asserts the chips, the per-view lists, the status text andUI.dialogsrather than the stage-2 placeholder; specs for the shared helpers, the subject guard and the GF codec.Tests/dbfiles_spec.luaandTests/ahcompat_spec.luawent with the code they covered.Tests/toc_spec.luapins LibAceGUIWidgets, ItemDB and GuildRoster in both TOCs,.pkgmetaand the loader. 492 specs, all passing; the packaging checker reports no FAIL. Location:Tests/.
[v0.6.6] (2026-09-20) - Offline Test Suite, DeltaSync 4.3 Names, Packaging Fixes
New Features
Offline unit-test suite on the shared WoWAPITesting harness.
Tests/wowapiis the harness as a git submodule (pinned at0b51701);lua Tests/wowapi/run.luafrom the addon root runs 260 specs in milliseconds with no game client. Measured coverage (lua Tests/wowapi/coverage.lua): Core is 91-100% per file (BuyOrder 99%, ShoppingList 99%, Communication 93%, GoldFundMe 91%, ItemSearch 91%), the three Compat shims 97-100% -- every one driven under each client shape it exists for, including the partial-C_TradeSkillUIEra build behind the v0.6.3 bug and the retail two-phase AH flow -- and MailHandler's fulfilment plan, inbox matching and order update at 50%. The GUI files are essentially untested (2-10%), so the addon-wide figure is 37.9%; that is the honest number and the GUI is the next step.Tests/support/addon.lualoads every file the TOC lists, in TOC order, on the harness's widget layer with the installed Ace3, DeltaSync, AceCommQueue and VersionCheck, andapp.init()runs the realOnInitialize/OnEnable. The four retail item-database parts are on the loader's skip list (generated data, ~7 MB) and are instead checked byte-for-byte byTests/dbfiles_spec.lua. Also new:.busted(shim to the harness's config),.luacheckrc(the WoW-globals allowlist luacheck needs, mirrored from.luarc.json),.writ-suites.json, andTestsin.pkgmeta's ignore list so none of it ships. Thetests/line in.gitignorewas hiding the suite from git and is gone. Five harness gaps found while writing the specs (HideUIPanel,ChatFrame_AddMessageEventFilter, the XML loader reading commented-out<Script>tags,GetItemCountbeing absent, andGetInboxHeaderInfoanswering0rather than the client'snilfor a mail with no attachments) were raised on the harness's inbox and delivered the same day, so no stand-ins remain. Location:Tests/.World of Warcraft: Forever declared, with a content flag. Forever is the retail client (
WOW_PROJECT_ID == WOW_PROJECT_MAINLINE, no project constant of its own) serving Classic Era content at interface16001. EveryCompat/shim keys on API presence and takes the retail path there, which is correct for a retail client -- and the shim header that said retail has no COD was wrong:SetSendMailCODis in the live tree'sGlobalAPI.luaandBlizzard_MailFramecalls it, so COD fulfilment works on retail and Forever through the Cata+ path the detection already preferred (comment corrected). The one CONTENT branch, the item-class taxonomy inGUI/MainFrame.luathat adds TBC's Gem class and trade-goods subclasses on any non-Classic project, now asksaddon.isForeverfirst (init.lua: mainline project ID and build below 100000, the same test as FastGuildInvite'sgv.isForever) so Forever gets the Classic Era taxonomy its content actually has. The Classic item database is searched before the retail one on every flavour, so Forever's items resolve from the right table without a branch. Declared and loading; not observed on a Forever client -- none is installed on this machine. Pinned byTests/forever_spec.lua. Location:PersonalShopper.toc,init.lua,GUI/MainFrame.lua,Compat/MailCODCompat.lua.
Bug Fixes
- Own orders and campaigns came back as somebody else's after DeltaSync v4.3.0 (MINOR 21). DeltaSync now hands every callback sender as canonical
Name-Realm; PersonalShopper comparedplayerNameto the bareUnitName("player")for ownership in six places, so a peer's gossiped copy of my order (stored asMe-Realm) failed the check inHandleOrderResponseand landed inPS_KnownOrdersas a foreign order, and aGFM_RESPONSEechoing my own campaign replaced my record and cost meisOwnerandRecordMailDonation. Fixed withCommunication.CanonicalName, which folds a suffix naming our own realm (compared with spaces stripped, since the library spells itGetNormalizedRealmName()and the addon storesGetRealmName()) back to the bare name at the three ingress points --UnpackChannel,HandleOrderResponse,GoldFundMe:MergeCampaign-- and keeps a foreign realm's suffix so cross-realm mail addressing is unchanged. Idempotent on a bare name, so correct on MINOR 20 too. Pinned byTests/names_spec.lua. Location:Core/Communication.lua,Core/GoldFundMe.lua. - GoldFundMe: a peer's copy could overwrite the owner's own campaign.
OnRequestReceivedwhispers back every active campaign including the requester's own, andMergeCampaignaccepted any copy at an equal-or-newer version -- so an equal-version echo replaced pledges the owner had recorded from mail but not yet synced.MergeCampaignnow refuses to overwrite a campaign we own, the same authority rule the order handlers apply. Location:Core/GoldFundMe.lua. - VersionCheck integration never worked (open in
docs/BUGS.mdsince 2026-01-16).OnEnableguarded registration onif VersionCheckAddOn then, a global the VersionCheck-1.0 library has never defined, so the branch never ran. The library isLibStub("VersionCheck-1.0")and registration isRegisterCheck(name, rawTocVersion);init.luanow does that. Pinned by a spec that runs the realOnEnableagainst the installed library. Location:init.lua. - Released zip carried
tools/,docs/and a 2.1 MB Questie extraction..pkgmeta'signore:entries were written with trailing slashes and**/globs, both of which the BigWigs packager matches against nothing, so roughly 2.3 MB of dev files shipped in every release while the synced dev installs (filtered by a separate hand-written list) looked correct. Rewritten as bare folder names and single-star globs;questie_items_raw.luaandTestsadded; dot entries dropped (the packager prunes them regardless). Pinned byTests/toc_spec.lua, which refuses every one of those spellings. Location:.pkgmeta. - TOC did not declare live Classic Era. The Interface list carried
11508(Vanilla Test) and11507but not11509, and was a patch behind on TBC Anniversary (20506), Titan Reforged (38002) and Mists (50504). Replaced with the fleet's current sixteen-entry list, which also declares World of Warcraft: Forever (16001) -- see the Forever bullet under New Features. Pinned byTests/toc_spec.lua. Location:PersonalShopper.toc. - Three defects the new specs found, all fixed. (1) The channel codec's decoder split on
[^:]+, which drops empty fields, so a GoldFundMe campaign with a blank title -- or any payload with an empty realm -- shifted every later field one slot and parsed as garbage; the split now keeps empty fields in place (Core/Communication.lua,UnpackChannel). (2)ShoppingList:Importraised on well-formed data with noitemstable instead of reporting it; it now returnsfalse, "Import data carries no item list"(Core/ShoppingList.lua). (3)MailHandler:ScanInboxtested the attachment count for truthiness; the client returns nil for no attachments so it worked in game, but a0from any other source read as "has items" -- it now tests the number, and the unreachable "single large stack" branch inCalculateFulfillmentPlan(Phase 3 always handles that shape first) is removed (Modules/MailHandler.lua). - Price proposals could not be answered.
[Propose Price]sent aPRICE_PROPOSALwhisper and the buyer'sHandlePriceProposalstored it and printed four chat lines -- and that was the whole feature: nothing readaddon.priceProposals, nothing calledSendPriceResponse, and the button's tooltip promised "if they accept, the order's price will be updated" for an accept that did not exist. NowCommunication:RespondToPriceProposal(orderId, accepted)is the buyer's answer: accepting sets the order'spricePerItem, bumpsversion/updatedAt/orderHash, saves, broadcasts the sameUPDATE_ORDERthe mail path sends (withpricePerItem, whichHandleOrderUpdatealready applied) and whispers the verdict; declining only whispers. A proposal for an order that is no longer active is declined on the buyer's behalf.GUI/MainFrame.luashows it as aStaticPopup(PS_PRICE_PROPOSAL,multipleso two proposals do not collide) through theonPriceProposalcallback, which now receives the stored record (seller, old and new price) rather than the bare wire data. The seller sees the accept or decline in chat. Pinned by three specs. Location:Core/Communication.lua,GUI/MainFrame.lua. - The three Notifications toggles in Settings did nothing.
notifications.newOrders,orderFulfilledandpriceProposalswere written byGUI/Options.luaand read by no code. Now: a NEW_ORDER for an order not previously seen prints one line (a newer copy of a known order stays quiet); the "Order complete!" / "Partial fulfillment" lines on the buyer's side are gated byorderFulfilled; the price-proposal chat line is gated bypriceProposals(the popup is not -- it is the only way to answer). All three default on, as before. Pinned by three specs. Location:Core/Communication.lua,Modules/MailHandler.lua. - Over-fulfilment told the buyer to "check your mail for a return" that was never sent.
SendReturnMailhas no caller (mail cannot be sent programmatically), so the message promised something the addon does not do. It now says how many items were counted against the order and that the extra were not. Location:Modules/MailHandler.lua. - Collector tooltip described a feature that does not exist. It said "paste a list of item names or links"; the dialog offers Missing Recipes only. Reworded. Location:
GUI/ShoppingListTab.lua. - Chat-frame filter registration used a deprecation alias.
ChatFrame_AddMessageEventFilteris an alias behindloadDeprecationFallbackson every flavour (Blizzard_DeprecatedChatInfo/Deprecated_ChatFrame.lua:51in the Classic Era tree); the real function isChatFrameUtil.AddMessageEventFilter.SetupChannelFilteringnow prefers the namespaced one with the global as fallback. Location:Core/Communication.lua.
Improvements
watch-addon.ps1retired;wow-version-replication.ps1is the dev sync script. The old watcher kept a hand-written exclusion list beside.pkgmeta, and the two had diverged: the synced installs looked right while the zip did not (the packaging bug above went unnoticed for 44 days because every copy anyone looked at was correct). The replacement is the fleet script adapted for PersonalShopper: it reads.pkgmeta'signore:block so the installs and the zip cannot disagree, skips every dotted path component and itself, prunes files the rules exclude from the targets on launch, takes a per-repo mutex (skipped under-DryRun) so two copies cannot race, and copies into every installed flavour including_retail_. One deliberate departure from the fleet copy: instead of a 2-second polling loop it registersFileSystemWatcherevents without an action block and blocks onWait-Eventin the main runspace, with the same snapshot diff deciding what changed -- an event, not a timer.-DryRunwas run and read; the watch loop itself has not been run live yet. The VS Code folder-open task that launches the watcher (.vscode/tasks.json, git-ignored) was re-pointed. Location:wow-version-replication.ps1.- Docs say what the addon runs on.
README.md(which still said "Classic Era WoW" and "all dependencies are embedded", and whose dashes had been saved as mojibake),docs/DESIGN.md(whose Overview and Compatibility sections said "Classic Era only", and which carried a UTF-8 BOM and a repository link under the wrong GitHub account) and the CurseForge headline now list every supported flavour including Forever, the real dependency story (Ace3, DeltaSync, VersionCheck-1.0, auto-installed from CurseForge) and the Forever content-vs-API rule.DESIGN.mdalso now describes the player-name fold, the GoldFundMe ownership guard, the VersionCheck registration and the AH compat layer as they are, not as they were. - Release docs verified against the code, feature by feature. Every claim in
README.mdanddocs/Curseforge_Description.htmlwas checked against the source before the v0.6.6 notes were finalised. Corrected: the tab list (AH Scan was missing), the Titan Panel text (it shows the available-order count), the recipe filter (it drops trainer-only, seasonal and other-spec recipes; "BoP quest rewards" was never a filter), the missing-recipes flow (Collector -> Missing Recipes -> pick a profession, not "open a window and click Add Missing Recipes"), the Ace3 list (AceTimer is not used; AceEvent/AceSerializer/AceConfig are), the sync description (a hidden chat channel plus DeltaSync, not "AceComm over a channel"), and the full slash-command list. Added, because the code has them and the docs did not: price proposals, GoldFundMe as a feature section (campaigns, guild-only, pledges, mail donations, donor list), Announcements, the scan modes and filters, auto-remove-when-obtained, the auto-open and notification options, the LibDataBroker tooltip, and the TOGBankClassic bank-request button. The item-database counts (14,889 Classic, 164,799 retail) were re-measured and are exact. Location:README.md,docs/Curseforge_Description.html. - Lint-clean across the addon: 219 editor problems to zero. No behaviour change. The unused half of
local addonName, addon = ...is_in every file (and the luacheck ignore that hid it is gone, so the next unused one is reported); unused AceGUI callback parameters are_; thefunction(self)focus handlers that shadowed a method'sselfarefunction(box); whitespace-only lines are stripped from six files; two emptyifbranches are plain conditions; a duplicated priority-colour computation, an unusedPositionDialogTopRightOfMainwidth parameter, an unusedGetLocale()read inenUS.luaand a handful of unused locals are removed. Two config fixes behind most of it:.luacheckrcdeclaresstring.trim/split/join(the client extendsstring, perGlobalAPI.lua), and the.code-workspaceno longer disables thebasic/string/table/mathbuiltins for the language server -- LuaLS declares the primitive types (string,number,table, ...) insidebasic, so with it off every@param x stringannotation read as an undefined type.io/os/package/debug/utf8stay disabled, because the client really lacks them. Also:docs/BUILD_GUIDE.mdlinked the repository under the wrong GitHub account (corrected to Pimptasty) and had two bare URLs. Location:.luacheckrc,PersonalShopper.code-workspace,Core/,GUI/,Modules/,Locale/enUS.lua,docs/. CLAUDE.mdTooling section rewritten for the test suite, the luacheck config and the new sync script, and its overview carries the Forever rule;docs/BUGS.mdrecords the four fixes above;.luarc.jsongainsChatFrameUtil,GetNormalizedRealmNameand the test-DSL names, drops the never-definedVersionCheckAddOn, and ignoresTests/for workspace analysis.- Peer review moved to writ's inbox.
docs/AUDIT.md(four findings, all fixed in this version) was imported into the peer-review inbox and deleted; the addon is a writ workspace and a WoWAPITesting consumer from this version on.
[v0.6.5] (2026-04-27) - DeltaSync Externalized
Improvements
DeltaSync-1.0 is now a hard external dependency instead of an embedded library. Removed the three-file embedded copy at
Libs/DeltaSync/and switched to the standaloneDeltaSyncaddon (CurseForge slug:deltasync). End users installing PersonalShopper from CurseForge get DeltaSync (and its transitive depsAce3+AceCommQueue-1.0) pulled in automatically; sibling addons that also embed DeltaSync (TOGProfessionMaster, etc.) cooperate cleanly via LibStub MINOR resolution. This drops ~3 files / ~1500 LOC from the package and ensures every consumer of the library shares one canonical revision instead of drifting per-addon.PersonalShopper.toc— addedDeltaSyncto## Dependencies:; dropped the threeLibs\DeltaSync\*.luaload lines..pkgmeta— addeddeltasynctorequired-dependenciesso CurseForge resolves it on install; addedCLAUDE.mdtoignoreso dev-only documentation doesn't ship in the release zip.init.lua— addedLibStub("AceCommQueue-1.0"):Embed(addon.lib)immediately afterNewAddon. The external DeltaSync routes every send throughaddon.lib:SendCommMessage, and AceCommQueue must wrap that path beforeDeltaSync:Initialize()runs — otherwise high-traffic broadcasts produce CTL chunk-interleave CRC errors.Libs/DeltaSync/— deleted entirely.CLAUDE.md— updated tech-stack line, file-load-order, the DeltaSync architecture section, and the External Dependencies list to reflect the new shape.Core/Communication.luaanddocs/DESIGN.md— updated stale "embedded DeltaSync" references to describe the library as external. The matching reference indocs/BUGS.md(a 3/17/2026 incident report) is intentionally left as-is — historical bug-log entries should describe state at the time, not be retroactively rewritten.
Removed orphan
Libs/LibGuildRoster-1.0/. The library was never wired intoPersonalShopper.toc's load list and had noLibStub("LibGuildRoster-1.0")callers. The external DeltaSync addon already provides equivalent guild-roster tracking via its embeddedGuildCache-1.0companion lib. Deleting dead code rather than carrying it forward.Stripped trailing whitespace from
init.lua(10 whitespace-only lines flagged bylua-language-server). Pre-existing lint hints; cleared while we were here.
Bug Fixes
watch-addon.ps1no longer mirrors the source dir onto itself. The script's$wowFolderslist always included_classic_era_, which on this machine is the source. The unfiltered self-mirror caused a file-deletion race: every atomic save (write-temp + replace-original) fires aDeletedevent on the original; the handler calledRemove-Item -Forceon$destination, and since destination resolved to the same path as source, the freshly-renamed file was nuked in place. Fixed by computing a normalized source path with[System.IO.Path]::GetFullPath()and skipping any target whose normalized path matches it. Related operational note: a pre-patch instance of this watcher loaded into PowerShell at OS startup will keep running the buggy code in memory even after the script file is updated on disk — kill stalepowershell.exePIDs whose command line containswatch-addon.ps1after editing the script. Location:watch-addon.ps1.
[v0.6.4] (2026-04-15) - Retail Item DB Split
Bug Fixes
LUA_WARNING: Error loading DB/ItemDatabase_Retail.luaon TBC Anniversary — The monolithicItemDatabase_Retail.lua(~6.2 MB, ~165k lines) exceeded the Lua per-file size limit on older clients (TBC Anniversary and similar), causing a compile-time warning on every login. The file did load successfully but the warning was unavoidable given the file size. Fixed by splitting into four numbered files (DB/ItemDatabase_Retail_1.luathrough_4.lua, ~1.6–2 MB each), each containing one self-contained IIFE chunk. All four files are listed inPersonalShopper.toc. Updatedtools/generate-retail-item-db.ps1to produce four files going forward; addedtools/split-retail-item-db.ps1as a one-time migration utility. (PersonalShopper.toc,DB/ItemDatabase_Retail_*.lua,tools/)
[v0.6.3] (2026-04-15) - Enchanting Classic Era Fix
Bug Fixes
- Collector → Missing Recipes now works for Enchanting on Classic Era — Clicking Collector → Missing Recipes → Enchanting printed "Please open a profession window first" even with the Enchanting window open. Root cause: Blizzard has backported a partial
C_TradeSkillUInamespace to some Classic Era builds, with some 1.x Anniversary / Season-of-Discovery patches removingGetTradeSkillLineandGetCraftLineentirely. This caused allC_TradeSkillUI-presence checks to misclassify those clients as retail; the retail path'sC_TradeSkillUI.GetTradeSkillLine()returns 0 for Enchanting (which uses the Craft window, not the TradeSkill API) → "Please open a profession window first." Fixed across all affected code paths:PS_TradeSkill.IsRetail()now requiresGetCraftLine == nilin addition toGetTradeSkillLine == nil. (Compat/TradeSkillCompat.lua)PS_TradeSkill.GetAdapters()retail guard updated to match. (Compat/TradeSkillCompat.lua)- The
CRAFT_SHOW/CRAFT_UPDATEevent handler replaced bareif C_TradeSkillUI thenwithif PS_TradeSkill.IsRetail() thenso the known-recipe scan fires correctly on all Classic clients. (GUI/ShoppingListTab.lua) ShowMissingRecipesDialogno longer relies onIsRetail()to pick a code path. It now always triesGetNumSkillLines()-based profession enumeration first — on any Classic Era build this yields the player's professions and runs the Classic recipe DB +IsSpellKnownpath without needing the window open. On true retail,GetNumSkillLines()returns 0 and the function falls through to_ShowMissingRecipesRetailas before. (GUI/ShoppingListTab.lua)
[v0.6.2] (2026-04-14) - Collector Bug Fix
Bug Fixes
- Collector → Missing Recipes works without opening the profession window first —
_ShowMissingRecipesForProfessionpreviously guarded onaddon.knownRecipes[profID]being non-nil, which is only populated when the profession window firesCRAFT_SHOW/TRADE_SKILL_SHOW. For Enchanting (Classic Craft window API) this meant the feature was inaccessible unless the player manually opened their Enchanting window before clicking Collector. The hard guard has been replaced with an on-demand fallback: if the window was never opened this session, iteratesrecipeDB[profID]and callsIsSpellKnown(spellID, false)for each entry to build the known-recipes set in place. The result is cached inaddon.knownRecipes[profID]; if the player later opens their profession window,CRAFT_SHOW/TRADE_SKILL_SHOWoverwrites the cache with the more precise window-scanned data. Affects all professions on all game versions, but was most visible for Enchanting on Classic Era. (GUI/ShoppingListTab.lua)
[v0.6.1] (2026-04-14) - GFM Guild-Only, GreenWall, Announce Buttons & Multi-Version WoW Support
New Features
- GoldFundMe guild-only campaigns — new "Guild Only" checkbox in the Create Campaign dialog (disabled when not in a guild; label shows guild name); guild-only campaigns are broadcast over the shared PersonalShopper channel with
guildOnly = trueandownerGuildfields in the payload;GetActive()hides campaigns whoseownerGuilddoes not match the viewer's guild; campaign rows show a[g]guild badge for guild-only entries (Core/GoldFundMe.lua,GUI/GoldFundMeTab.lua) - GreenWall confederation forwarding —
Communication:AnnounceToChannels(msg)now forwards announce messages through GreenWall's confederation channel (gw.config.channel.guild:send(GW_MTYPE_CHAT, msg)) when GreenWall is installed and connected; enabled by default, capped at 240 chars (GW wire limit); respectsaddon.db.profile.announce.greenwall = falseopt-out (Core/Communication.lua) - [Announce] buttons — gold
[Announce]button added to GoldFundMe campaigns, Shopping List items, and My Orders rows; sends a lore-flavoured message to configured chat channels with one click - Shared announce framework —
Communication:AnnounceToChannels(msg)centralises all channel send logic; readsaddon.db.profile.announce(guild toggle + per-channel multiselect); channel choices persist in SavedVariables via AceDB-3.0 - Announcements settings page — unified ESC → Interface → AddOns → Personal Shopper → Announcements page replaces the old GoldFundMe-only channel settings; covers all three tabs
- Channel spam filter — addon-owned and merge channels (
personalshopper,grouper,greenwall,guildmerge) are always hidden from the channel multiselect - Multi-version WoW support —
.tocupdated to declare compatibility with Classic Era, TBC, WotLK, Cata, MoP, and Retail (The War Within); three newCompat/shims abstract the diverging APIs for each game version
Compatibility Shims (Compat/)
TradeSkillCompat.lua—PS_TradeSkill.GetAdapters()unifies the Classic dual-window API (GetTradeSkillLine+GetCraftLine) and retailC_TradeSkillUIbehind a single iterator interface;ScanOpenProfessionWindow()inShoppingListTab.luamigrated to use itMailCODCompat.lua—PS_MailCOD.SetCOD(copper)handles Classic Era frame manipulation, Cata+SetSendMailCOD(), and retail (COD removed — documented no-op with chat warning);MailHandler.luaCOD block migratedAHCompat.lua—PS_AHCompatunifies Classic (QueryAuctionItems/AUCTION_ITEM_LIST_UPDATE/GetAuctionItemInfo) and retail two-phase flow (SendBrowseQuery→SendCommoditySearchQuery/SendSearchQuery, with commodity vs. equipment branching); callback-based API (RegisterQueryHandler/UnregisterQueryHandler) replaces raw event wiring;AHScanner.luaandAHScannerFrame.luamigrated
Bug Fixes
- Lint:
AddTooltipundefined inShoppingListTab.lua— added local definition mirroringMainFrame.luaandGoldFundMeTab.lua - Lint:
resultsLabelforward reference inShoppingListTab.lua— declaration hoisted above hover-to-fill closure, assignment left at original site - Lint:
@returnannotation inMailCODCompat.lua— splitboolean, string|nilinto two separate@returnlines to satisfy the language server - Lint:
sortsmissing fromAHCompat.luaSendBrowseQuery— addedsorts = {}to satisfy the requiredAuctionHouseBrowseQueryfields - Lint: five missing globals in
.luarc.json— addedPersonalShopperDB_Items_Retail,GetAuctionItemLink,GreenWallAPI,gw,GW_MTYPE_CHAT C_AddOns.GetAddOnMetadatacrash on Classic Era — guarded with(C_AddOns and C_AddOns.GetAddOnMetadata) or GetAddOnMetadataininit.luaC_Container.*crash on Classic Era —MailHandler.luauses a local compat table that falls back to Classic globals (GetContainerNumSlots,GetContainerItemInfo, etc.)
Changes
- GFM guild-only routing simplified to single channel — initial implementation used a second AceComm prefix (
PSGfmG) and GUILD distribution in parallel with the PersonalShopper channel; refactored so guild-only campaigns travel over the same channel with payload flags;MSG_GFM_GCAMPAIGN/GCANCEL/GREQUEST,GFM_GUILD_PREFIX,SendGuildMessage, andOnGuildMessageReceivedall removed (Core/Communication.lua,Core/GoldFundMe.lua) .pkgmetacleaned up — removed YAML#comment lines that were triggering markdownlint; added.markdownlint.json,.markdownlintignore, and.vscode/settings.jsonto the ignore list.markdownlint.jsonupdated — added MD007/MD022/MD041 disable rules to silence false positives from non-markdown config files.vscode/settings.jsonadded — associates.pkgmetawith YAML language mode- Settings tree simplified — Notifications page removed; all notification toggles merged into the General page under a "Notifications" header
- Dead Custom Channel feature removed —
customChannelsettings block and the three never-called Communication functions (JoinCustomChannel,LeaveCustomChannel,IsInCustomChannel) removed frominit.lua,GUI/Options.lua, andCore/Communication.lua goldFundMeAnnounceDB key renamed toannounce— generic key shared by all tabs; existing SavedVariables will reset to defaults on first load after upgradePS_OrderTombstonesadded to.luarc.jsonglobals;SendChatMessagechannel arg corrected from numeric id to channel name string
[v0.6.0] (2026-04-13) - GoldFundMe Crowdfunding
New Features
- GoldFundMe tab — create community gold-goal campaigns visible server-wide to all Personal Shopper users; set a title, gold goal, and optional linked item; progress bar shows current savings + pledges vs. goal with donor count
- Donor List dialog — opens to the right of the main window and lists each backer's contribution with a mail-confirmed badge; owner gets a "Sync" button to push authoritative campaign data to all online peers; dialog size persists across redraws and
/reloadviaframePositions.donorListSavedVariable (default 380 × 400) - Automatic mail donation detection — when a campaign owner clicks "Take Money" on a
GFM:<campaignId>mail the addon records the donation and updates the donor list instantly with no manual input - "Donate via Mail" shortcut — non-owners can pre-fill the mail compose window (recipient, subject, optional gold amount) with one click from any campaign row or the Donor List dialog
Bug Fixes
- GFM sync not propagating pledges — WoW channel messages are capped at 255 bytes so pledges were always stripped; fixed by whispering a
GFM_REQUESTback to the sender whenever an incoming campaign has a newer version, triggering a fullGFM_RESPONSEwhisper (no size limit) that includes all pledges - Available orders leaking into GFM tab — stale
availableOrdersScrollAceGUI widget reference was reused after switching away from the Available tab; fixed by niling both scroll refs on tab leave and adding acurrentTab ~= "available"early-exit guard inRefreshOrderList - Donor dialog double-release error — AceGUI
FramefiresOnCloseonHide(), causing a double-release when the dialog was also explicitly released; fixed by niling theOnClosecallback before every programmaticRelease()call - Donor dialog always centering on screen — AceGUI
OnAcquireauto-shows the frame atCENTERandApplyStatusre-centers wheneverstatus.left/topare nil; fixed by hiding before snapping, positioning relative to the main window, then writing resolved pixel coords back into the AceGUI status table soApplyStatuspreserves the position - GFM donation recipient double-realm —
playerNamereceived over a cross-realm channel already contained a-Realmsuffix; the name is now stripped to base before appending the campaign'splayerRealm
Changes
- DESIGN.md consolidated: full GoldFundMe section added; full Shopping List section added; four obsolete pre-implementation planning docs deleted (
SHOPPING_LIST_FEATURE.md,SHOPPING_LIST_ARCHITECTURE.md,SHOPPING_LIST_SUMMARY.md,PHASE1_COMPLETE.md) - Lint fixes in
GUI/GoldFundMeTab.luaandModules/MailHandler.lua
[v0.5.0] (2026-04-04) - Settings Panel & Auto-Cancel on Obtain
New Features
- Settings in ESC > Interface > AddOns — Personal Shopper options are now embedded in the Blizzard Interface Options panel with an expandable tree (General, Notifications, Custom Channel, Profiles, Debugging); the old floating AceConfigDialog window is gone
- Auto-cancel buy orders on obtain — the "Auto-Remove When Obtained" feature now also cancels active buy orders when your bags contain enough of the item; previously only Shopping List entries were removed
- Right-click minimap button toggles settings — right-clicking now opens or closes the options panel; previously it always opened
Changes
- "Auto-Remove When Obtained" tooltip updated to describe both Shopping List removal and buy order cancellation
[v0.4.1] (2026-04-04) - Shopping List Fixes & Inline Scan Options
Bug Fixes
- Shopping List last row clipped — final item in the list was always invisible, cut off by the scroll frame edge; fixed with a trailing spacer after the render loop
- Priority filter type mismatch — AceGUI dropdown returns string keys but priority is stored as a number;
tonumber()applied to both sides inShoppingList:GetAll - AHScanner sort crash on nil priority —
table.sortcrashed whenitem.prioritywas nil; guarded withor 99fallback - nil priority on item add —
PS_ShoppingListSettings.defaultPriorityabsent in old saves; triple fallbackor 2added inShoppingList:Add - Stale category filter hiding items — filter reset to ALL when saved category no longer exists in the list
- New item hidden by active filter — both filters reset to ALL on successful
ShoppingList:Add - Empty itemLink produces invisible row —
itemLink = ""now treated same as nil - Row crash silently suppressed — pcall errors now print to chat instead of only to debug log
- Shopping List title truncated — title label changed to full width so
(N filtered)indicator is always visible
Changes
- Scan Options moved into AH Scan tab — removed the Shopping List group from the Settings/Options panel; scan settings (Scan Mode, Min Priority, Delay, Hide over budget, Skip fully stocked) now render inline in the AH Scan tab via
AddScanOptionsPanel - Clear Filters link added to Shopping List filter bar; shown in red when a filter is active
/ps dumpsldiagnostic slash command: dumps allPS_ShoppingListentries to chat
[v0.4.0] (2026-04-03) - AH Scanner, Shopping List & Missing Recipe Tracker
New Features
- Shopping List (
Core/ShoppingList.lua,GUI/ShoppingListTab.lua) � new "Shopping List" tab in the main window; add/edit/remove items with quantity, max price, priority (High/Medium/Low), category, and notes; color-coded priority rows; per-account persistence viaPS_ShoppingListSavedVariable; BoP and quest item validation - AH Scanner (
Modules/AHScanner.lua,GUI/AHScannerFrame.lua) � one-click scan at the Auction House for every item on your shopping list; results frame shows current buyout, quantity available, and green/red budget indicators; configurable scan delay and skip-fully-stocked option; enabled/disabled automatically when AH opens/closes - Missing Recipe Tracker � "Add Missing Recipes" button scans open profession windows and adds all missing tradeable recipe scrolls to the shopping list in one click
- Specialization filtering � recipes locked to a different sub-spec (Dragonscale/Elemental/Tribal Leatherworking, Gnomish/Goblin Engineering, Armorsmith/Weaponsmith Blacksmithing) are hidden when the player's spec doesn't match; detected via
IsSpellKnown()when the tradeskill window opens addon.professionSpectable tracks the player's active sub-specialization per profession
Bug Fixes
- Crash on "Add to Shopping List" �
attempt to compare table with numberatShoppingList.lua:83; caller was passing one table argument instead of a numericitemIdfollowed by an options table - Lint errors in
GUI/ShoppingListTab.lua� undefined WoW globals, protected AceGUI field access,IsSpellKnownwrong arg count - Lint errors in
Modules/AHScanner.lua� undefinedGetNumAuctionItemsandGetAuctionItemInfo - Lint errors in
GUI/AHScannerFrame.lua� undefined AH globals and protected AceGUI.framefield access
[v0.3.0] (2026-03-24) - Item Filters, Tooltips & History Filters
New Features
- Item type/subtype filters � filter the Available Orders list by item type and subtype to narrow results when browsing orders
- Item tooltips � hover any item name in Available Orders or History to see the full WoW item tooltip
- History filters � filter Order History by date range, item name, or fulfillment status
[v0.2.0] (2026-03-20) - Session 3 Bug Fixes
Bug Fixes
- Session 3 bug fixes and improvements � resolved multiple issues found during session 3 testing including order propagation, MailHandler syntax errors, mailbox tab key navigation, and
RequestOrdersthrottle bypass
[v0.1.2] (2026-03-18) - BoP & Quest Item Blocking
New Features
- Block Bind on Pickup and quest item orders � orders for BoP or quest items are now rejected at creation time with an error message; prevents unfulfillable orders from being broadcast to the server
[v0.1.1] (2026-03-18) - Fulfillment Tracking Fix
Bug Fixes
- Correct fulfillment tracking � orders were not being marked fulfilled correctly after the mailbox attach sequence completed; now tracked reliably via order state
- Suppress
SendChatMessageblock error � Blizzard blocksSendChatMessagecalls during certain protected frames; error is now caught and suppressed rather than surfacing to the user
[v0.1.0] (2026-03-18) - COD, DeltaSync & Mailbox Fulfillment
New Features
- Auto-attach items and COD on fulfill � ported TOGBank's greedy algorithm;
MailHandlerscans bags, calculates a fulfillment plan (full stacks largest-first, small-stack skip, split candidate fallback), then auto-attaches items and sets COD without manual interaction - DeltaSyncChannel (
DeltaSyncChannel.lua) � optional CHANNEL transport module; routes order broadcasts through a hidden addon-owned channel instead of GUILD/WHISPER for server-wide reach - Order state tracking � orders now track
PENDING,FULFILLED, andCANCELLEDstates persisted in SavedVariables; mailbox option added to open the mail tab directly from the fulfill dialog
Bug Fixes
- Classic Era COD API �
SetSendMailCOD()does not exist in Classic Era 1.15; replaced with direct frame manipulation (SendMailCODButton:Click()+MoneyInputFrame_SetCopper) - Uncheck send-money button before COD � previous
Click()approach could leave both radio buttons active; now callsSetChecked(false)onSendMailMoneyButtonbefore activating COD - COD nil guard, recipient realm dedup, 7-day order TTL � guards against nil COD amount; deduplicates realm suffix on recipient name; orders older than 7 days are pruned automatically
- Available orders overlap and history stats layout � fixed row overlap in the Available Orders list and misaligned stat labels in the Order History statistics panel
SendCommMessagechannel index � must pass an integer channel index for CHANNEL distribution; was incorrectly passing a string channel name- AceComm-3.0 registration �
RegisterComm/SendCommMessagemust be called on the host addon's mixin, not the standalone library; fixed by usingaddon:RegisterComm() - DeltaSync
%zgsub pattern � Lua 5.1 treats a literal\0as end-of-string inside character classes; replaced with%zto fix "malformed pattern (missing ']')" on everyCHAT_MSG_CHANNELevent - DeltaSync debug buffer unification �
lib.debugMessageBufferandaddon.debugMessageBufferwere separate tables;RedrawDebugMessageswiped lib messages on tab switch; unified to a single shared reference DeltaSync.debugEnabledlive sync �/ps debugnow also setsDeltaSync.debugEnabledso COMMS.RECEIVE logs appear immediately without/reload
[v0.0.3-alpha] (2026-01-21) - Embedded Libraries
Internal
- Embed external libraries �
LibDataBroker-1.1andLibDBIcon-1.0moved intoLibs/folder and loaded directly; removed from.pkgmetaexternals - Switch externals to GitHub �
.pkgmetaexternal references updated from WoWAce SVN to GitHub repository URLs
[v0.0.2-alpha] (2026-01-21) - TOC & MainFrame Fixes
Bug Fixes
- TOC version bump and MainFrame bug fixes � corrected interface version in
PersonalShopper.toc; fixed several nil-error crashes in the main window on first open
[v0.0.1-alpha] (2026-01-16) - Initial Alpha Release
New Features
- Core addon framework � AceAddon/AceDB/AceGUI skeleton; namespace initialization; multi-version TOC support; VersionCheck system; debug logging system
- BuyOrder data model �
BuyOrder.luadefines the order schema (itemId, quantity, maxPrice, requester, status, timestamp) - Communication system � AceComm-3.0 based message bus; guild and whisper distribution; server-wide hidden
PersonalShopperchannel for cross-guild reach; custom user-configurable announcement channel - Mail system integration �
MailHandler.luadetects inbox messages, parses order CODs, and surfaces fulfill prompts when the mailbox is open - Main window GUI � tabbed AceGUI window (Available Orders, Order History, Mail); sortable orders list with search bar; order statistics panel
- Create Order dialog � item name auto-complete, quantity/max-price inputs, validation; popup positions to the left of the main window
- Minimap button & Titan Panel �
LibDBIcon-1.0minimap icon;LibDataBroker-1.1data source for Titan Panel; single shared dataobj between both - Options panel � AceConfig options: debug toggle, minimap show/hide, notifications, custom channel name, profile management
- Localization �
enUSbase locale loaded viaLocale.xml; all user-facing strings keyed throughL[...]
Bug Fixes
- Addon load failure �
LibDataBroker-1.1andLibDBIcon-1.0were declared as.pkgmetaexternals but not embedded; addon failed to load; resolved by embedding both libs - LibDBIcon
lib.xml�LibDBIcon-1.0.luamust be loaded directly; loading vialib.xmlcaused a nil error on init AddToBlizzardOptionsdeprecated � removed deprecated call fromOptions:Init(); options panel now opened viaAceConfigDialog:Open()- TitanPanel nil
dataObj�MinimapButtonandTitanPaneleach created a separateLibDataBrokerobject; second creation returned nil; fixed by sharing one object RegisterCommmethod not found �RegisterCommmust be called inCommunication:Init(), not at file load time before the addon is initializedtabGroupnil onSelectTab�self.tabGroupmust be assigned before callingSelectTab; moved assignment earlier inMainFrame:Open()- Duplicate button code � duplicate widget block referenced undefined
contentvariable; removed - Keybindings firing in edit boxes � main window keybindings now check
GetCurrentKeyBoardFocus()and no-op when an edit box is focused - Window position not saving � switched to
AceGUI SetStatusTablefor automatic position/size persistence across sessions - Window not centered on first open � when no saved position exists, main window now centers on screen instead of spawning at 0,0
- Custom channel auto-join timing � replaced
C_Timerdelay withPLAYER_ENTERING_WORLDevent to ensure the channel exists before attempting to join
This mod has no additional files