PersonalShopper

An addon to post purchase requests to the community.
Back to Files

PersonalShopper-v0.7.0

File namePersonalShopper-PersonalShopper-v0.7.0.zip
Uploader
PmptastyPmptasty
Uploaded
Oct 8, 2026
Downloads
41
Size
298.7 KB
Flavors
ClassicRetailMoP ClassicTitan Reforged ClassicClassic TBCForever
File ID
9101282
Type
R
Release
Supported game versions
  • 12.1.0
  • 12.0.1
  • 12.0.0
  • 11.2.7
  • 5.5.4
  • 5.5.3
  • 5.5.2
  • 4.4.2
  • 3.80.2
  • 3.80.0
  • 3.4.5
  • 2.5.6
  • 2.5.5
  • 1.60.1
  • 1.15.9
  • 1.15.7

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 ClearFrame built once per session and never released, a compact LAGW-Strip toolbar across each tab (view chips, the library's search box, one-word dropdowns, strip buttons), a zebra W.RowList filling 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 a UI.Dialog (one ClearFrame per key, never released, Escape closes it), docked outside the main window's top-right edge with W:DockWindow so it follows the window, and only one is open at a time. Window size and position persist in profile.window through W:PersistWindow; a size saved by the six-tab window is dropped once and its position kept. Every entry point other files call (SelectTab with 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.lua reads LibStub("LibItemDB-1.0") once DB: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 from DB:Iterate(), because DB:Search caps its hits before sorting. The Shopping List gains a Best AH Price column from DB:GetPrice(itemId, "best"), whose tooltip names the source, statistic and age. ItemDB (CurseForge libitemdb) 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:IsVisible shows 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 on OnSisterConfigChanged and PLAYER_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 guildOnly and ownerGuild, 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; and OnRequestReceived whispered 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); MergeCampaign keeps a campaign guild-only once it is; and the whisper reply is gated by GoldFundMe: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 in Tests/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 paths SetScript'd the subject box's OnTextChanged, replacing Blizzard's SendMailFrame_CanSend (Classic Era MailFrame.xml), and ClearSubjectHook then set it to nil. MailHandler:GuardSubject now HookScripts the box once beside Blizzard's handler and ClearSubjectHook only clears the guarded subject. The guard also stopped too late: it was cleared in our MAIL_SEND_SUCCESS handler, but Blizzard's own handler resets the box (SendMailFrame_Reset -> SetText("")), and a guard still armed wrote PS: / 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 the SendMail hook, once the subject has been handed over (peer-review thread 23302f28). 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. PrepareGFMMail printed 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_InsertLink is only an alias of ChatFrameUtil.InsertLink on current clients (Blizzard_DeprecatedChatInfo/Deprecated_ChatFrame.lua in the Classic Era tree). UI.InsertLink prefers the namespaced function and falls back to the global; every item click uses it. Location: GUI/UI.lua, the three tab files.

  • addon.isForever was false on a real WoW Forever client. v0.6.6 assumed Forever reports WOW_PROJECT_MAINLINE and told it apart by interface number; a live Forever client reports WOW_PROJECT_ID 18 (observed in-client by FastGuildInvite on 2026-10-01, build 1.60.1.70124; the harness's Forever shape now reports the same as of 53dad60). So on Forever the flag was false and the item-class taxonomy in GUI/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 now WOW_PROJECT_ID == 18, the same test as FastGuildInvite's gv.isForever; MainFrame.lua needed no change. The only other reads of WOW_PROJECT_ID in the shipped code are these two (searched; the Compat/ shims key on API presence). Still not observed on a Forever client here. Pinned by Tests/forever_spec.lua. Location: init.lua.

  • Replica installs could lose a live file on a save-by-replace. Sync-File in the dev sync script removed every replica copy on a Deleted without checking whether the source still had the file. This copy decides by snapshot diff and only reports Deleted once Test-Path says 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-File now turns a Deleted for a file that is back on disk into a Changed and re-copies it (the fix TOGProfessionMaster found and shipped, peer-review thread 65b10702). Not reproduced deliberately. Location: wow-version-replication.ps1.

  • Packaging checker clean. The harness's pkgmeta.lua reported nine FAILs for locale files that Locale/Locale.xml only named inside XML comments -- nothing loaded them, but the dead lines read as shipped loads -- and two ignore: entries (*.txt, *.bat) that matched no file. The dead comment lines and the two entries are gone; lua Tests/wowapi/pkgmeta.lua now reports no FAIL, and confirms the upload carries libdbicon-1-0 as a required dependency (neither embedded library carries the @curseforge-project-slug marker 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.0 addon (CurseForge libdbicon-1-0, whose own embeds.xml brings LibDataBroker-1.1) updates for every player on its own. PersonalShopper.toc lists it in ## Dependencies and no longer loads Libs/; .pkgmeta lists libdbicon-1-0 under required-dependencies. The standalone's TOC does not declare Interface 16001, so Forever gets its own PersonalShopper_Camelot.toc, which keeps loading the embedded Libs/LibDataBroker-1.1 and Libs/LibDBIcon-1.0 (both stay in the repo for it) and takes them as OptionalDeps; 16001 is gone from the base TOC's Interface list. No Lua changed. Decided by the operator 2026-09-25 (contract from TOGTools, thread 1a805417). KNOWN COST: a player installing by hand must also install LibDBIcon-1.0, and required-dependencies is project-wide, so a Forever player is asked to install it too even though Forever never loads it. Forever picking up the _Camelot TOC is not verified -- no Forever client is installed here. Location: PersonalShopper.toc, PersonalShopper_Camelot.toc, .pkgmeta.
  • Brand colour on the addon title. Both TOCs' ## Title is now |cffFF8000Personal Shopper|r, the Forever TOC with a grey |cff999999Forever|r tag (fleet contract from DeltaSync, thread c5eece0f). Not a library, so no ## Category line. Not yet seen in a game client's AddOns list. Location: PersonalShopper.toc, PersonalShopper_Camelot.toc.
  • Test suite on harness 0de2e0a (was 0b51701). 0de2e0a carries 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's LibDBIcon-1.0 module rather than the vendored copy, as the base TOC now does. Tests/toc_spec.lua asserts the loader list against the base TOC with no Libs/ 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 loading Libs/, and that both titles are orange with every colour code closed; Tests/forever_spec.lua checks that Forever's 16001 lives only in the _Camelot TOC. Seven assertions writ flagged as weak (is_nil on a literal key, which passes if the key is misspelled) now pin a positive value on the same key first (peer-review thread 0e294aca). 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 are UI.Field, UI.Text, UI.Button and UI.DockDialog in GUI/UI.lua now, and the stage-2 stand-ins UI.LegacyHeaderRow / UI.LegacyViewSwitch are gone. UI.Dialog resets the reused frame's OnClose itself: the dlg:SetCallback("OnClose", nil) lines in the tabs did nothing, because AceGUI's SetCallback ignores nil. 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 and UI.dialogs rather than the stage-2 placeholder; specs for the shared helpers, the subject guard and the GF codec. Tests/dbfiles_spec.lua and Tests/ahcompat_spec.lua went with the code they covered. Tests/toc_spec.lua pins LibAceGUIWidgets, ItemDB and GuildRoster in both TOCs, .pkgmeta and 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/wowapi is the harness as a git submodule (pinned at 0b51701); lua Tests/wowapi/run.lua from 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_TradeSkillUI Era 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.lua loads every file the TOC lists, in TOC order, on the harness's widget layer with the installed Ace3, DeltaSync, AceCommQueue and VersionCheck, and app.init() runs the real OnInitialize/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 by Tests/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, and Tests in .pkgmeta's ignore list so none of it ships. The tests/ line in .gitignore was 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, GetItemCount being absent, and GetInboxHeaderInfo answering 0 rather than the client's nil for 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 interface 16001. Every Compat/ 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: SetSendMailCOD is in the live tree's GlobalAPI.lua and Blizzard_MailFrame calls 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 in GUI/MainFrame.lua that adds TBC's Gem class and trade-goods subclasses on any non-Classic project, now asks addon.isForever first (init.lua: mainline project ID and build below 100000, the same test as FastGuildInvite's gv.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 by Tests/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 compared playerName to the bare UnitName("player") for ownership in six places, so a peer's gossiped copy of my order (stored as Me-Realm) failed the check in HandleOrderResponse and landed in PS_KnownOrders as a foreign order, and a GFM_RESPONSE echoing my own campaign replaced my record and cost me isOwner and RecordMailDonation. Fixed with Communication.CanonicalName, which folds a suffix naming our own realm (compared with spaces stripped, since the library spells it GetNormalizedRealmName() and the addon stores GetRealmName()) 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 by Tests/names_spec.lua. Location: Core/Communication.lua, Core/GoldFundMe.lua.
  • GoldFundMe: a peer's copy could overwrite the owner's own campaign. OnRequestReceived whispers back every active campaign including the requester's own, and MergeCampaign accepted 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. MergeCampaign now 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.md since 2026-01-16). OnEnable guarded registration on if VersionCheckAddOn then, a global the VersionCheck-1.0 library has never defined, so the branch never ran. The library is LibStub("VersionCheck-1.0") and registration is RegisterCheck(name, rawTocVersion); init.lua now does that. Pinned by a spec that runs the real OnEnable against the installed library. Location: init.lua.
  • Released zip carried tools/, docs/ and a 2.1 MB Questie extraction. .pkgmeta's ignore: 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.lua and Tests added; dot entries dropped (the packager prunes them regardless). Pinned by Tests/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) and 11507 but not 11509, 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 by Tests/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:Import raised on well-formed data with no items table instead of reporting it; it now returns false, "Import data carries no item list" (Core/ShoppingList.lua). (3) MailHandler:ScanInbox tested the attachment count for truthiness; the client returns nil for no attachments so it worked in game, but a 0 from any other source read as "has items" -- it now tests the number, and the unreachable "single large stack" branch in CalculateFulfillmentPlan (Phase 3 always handles that shape first) is removed (Modules/MailHandler.lua).
  • Price proposals could not be answered. [Propose Price] sent a PRICE_PROPOSAL whisper and the buyer's HandlePriceProposal stored it and printed four chat lines -- and that was the whole feature: nothing read addon.priceProposals, nothing called SendPriceResponse, and the button's tooltip promised "if they accept, the order's price will be updated" for an accept that did not exist. Now Communication:RespondToPriceProposal(orderId, accepted) is the buyer's answer: accepting sets the order's pricePerItem, bumps version/updatedAt/orderHash, saves, broadcasts the same UPDATE_ORDER the mail path sends (with pricePerItem, which HandleOrderUpdate already 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.lua shows it as a StaticPopup (PS_PRICE_PROPOSAL, multiple so two proposals do not collide) through the onPriceProposal callback, 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, orderFulfilled and priceProposals were written by GUI/Options.lua and 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 by orderFulfilled; the price-proposal chat line is gated by priceProposals (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. SendReturnMail has 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_AddMessageEventFilter is an alias behind loadDeprecationFallbacks on every flavour (Blizzard_DeprecatedChatInfo/Deprecated_ChatFrame.lua:51 in the Classic Era tree); the real function is ChatFrameUtil.AddMessageEventFilter. SetupChannelFiltering now prefers the namespaced one with the global as fallback. Location: Core/Communication.lua.

Improvements

  • watch-addon.ps1 retired; wow-version-replication.ps1 is 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's ignore: 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 registers FileSystemWatcher events without an action block and blocks on Wait-Event in the main runspace, with the same snapshot diff deciding what changed -- an event, not a timer. -DryRun was 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.md also 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.md and docs/Curseforge_Description.html was 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 _; the function(self) focus handlers that shadowed a method's self are function(box); whitespace-only lines are stripped from six files; two empty if branches are plain conditions; a duplicated priority-colour computation, an unused PositionDialogTopRightOfMain width parameter, an unused GetLocale() read in enUS.lua and a handful of unused locals are removed. Two config fixes behind most of it: .luacheckrc declares string.trim/split/join (the client extends string, per GlobalAPI.lua), and the .code-workspace no longer disables the basic/string/table/math builtins for the language server -- LuaLS declares the primitive types (string, number, table, ...) inside basic, so with it off every @param x string annotation read as an undefined type. io/os/package/debug/utf8 stay disabled, because the client really lacks them. Also: docs/BUILD_GUIDE.md linked 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.md Tooling section rewritten for the test suite, the luacheck config and the new sync script, and its overview carries the Forever rule; docs/BUGS.md records the four fixes above; .luarc.json gains ChatFrameUtil, GetNormalizedRealmName and the test-DSL names, drops the never-defined VersionCheckAddOn, and ignores Tests/ 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 standalone DeltaSync addon (CurseForge slug: deltasync). End users installing PersonalShopper from CurseForge get DeltaSync (and its transitive deps Ace3 + 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 — added DeltaSync to ## Dependencies:; dropped the three Libs\DeltaSync\*.lua load lines.
    • .pkgmeta — added deltasync to required-dependencies so CurseForge resolves it on install; added CLAUDE.md to ignore so dev-only documentation doesn't ship in the release zip.
    • init.lua — added LibStub("AceCommQueue-1.0"):Embed(addon.lib) immediately after NewAddon. The external DeltaSync routes every send through addon.lib:SendCommMessage, and AceCommQueue must wrap that path before DeltaSync: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.lua and docs/DESIGN.md — updated stale "embedded DeltaSync" references to describe the library as external. The matching reference in docs/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 into PersonalShopper.toc's load list and had no LibStub("LibGuildRoster-1.0") callers. The external DeltaSync addon already provides equivalent guild-roster tracking via its embedded GuildCache-1.0 companion lib. Deleting dead code rather than carrying it forward.

  • Stripped trailing whitespace from init.lua (10 whitespace-only lines flagged by lua-language-server). Pre-existing lint hints; cleared while we were here.

Bug Fixes

  • watch-addon.ps1 no longer mirrors the source dir onto itself. The script's $wowFolders list 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 a Deleted event on the original; the handler called Remove-Item -Force on $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 stale powershell.exe PIDs whose command line contains watch-addon.ps1 after 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.lua on TBC Anniversary — The monolithic ItemDatabase_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.lua through _4.lua, ~1.6–2 MB each), each containing one self-contained IIFE chunk. All four files are listed in PersonalShopper.toc. Updated tools/generate-retail-item-db.ps1 to produce four files going forward; added tools/split-retail-item-db.ps1 as 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_TradeSkillUI namespace to some Classic Era builds, with some 1.x Anniversary / Season-of-Discovery patches removing GetTradeSkillLine and GetCraftLine entirely. This caused all C_TradeSkillUI-presence checks to misclassify those clients as retail; the retail path's C_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:
    1. PS_TradeSkill.IsRetail() now requires GetCraftLine == nil in addition to GetTradeSkillLine == nil. (Compat/TradeSkillCompat.lua)
    2. PS_TradeSkill.GetAdapters() retail guard updated to match. (Compat/TradeSkillCompat.lua)
    3. The CRAFT_SHOW/CRAFT_UPDATE event handler replaced bare if C_TradeSkillUI then with if PS_TradeSkill.IsRetail() then so the known-recipe scan fires correctly on all Classic clients. (GUI/ShoppingListTab.lua)
    4. ShowMissingRecipesDialog no longer relies on IsRetail() to pick a code path. It now always tries GetNumSkillLines()-based profession enumeration first — on any Classic Era build this yields the player's professions and runs the Classic recipe DB + IsSpellKnown path without needing the window open. On true retail, GetNumSkillLines() returns 0 and the function falls through to _ShowMissingRecipesRetail as 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 — _ShowMissingRecipesForProfession previously guarded on addon.knownRecipes[profID] being non-nil, which is only populated when the profession window fires CRAFT_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, iterates recipeDB[profID] and calls IsSpellKnown(spellID, false) for each entry to build the known-recipes set in place. The result is cached in addon.knownRecipes[profID]; if the player later opens their profession window, CRAFT_SHOW/TRADE_SKILL_SHOW overwrites 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 = true and ownerGuild fields in the payload; GetActive() hides campaigns whose ownerGuild does 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); respects addon.db.profile.announce.greenwall = false opt-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; reads addon.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 — .toc updated to declare compatibility with Classic Era, TBC, WotLK, Cata, MoP, and Retail (The War Within); three new Compat/ 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 retail C_TradeSkillUI behind a single iterator interface; ScanOpenProfessionWindow() in ShoppingListTab.lua migrated to use it
  • MailCODCompat.lua — PS_MailCOD.SetCOD(copper) handles Classic Era frame manipulation, Cata+ SetSendMailCOD(), and retail (COD removed — documented no-op with chat warning); MailHandler.lua COD block migrated
  • AHCompat.lua — PS_AHCompat unifies 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.lua and AHScannerFrame.lua migrated

Bug Fixes

  • Lint: AddTooltip undefined in ShoppingListTab.lua — added local definition mirroring MainFrame.lua and GoldFundMeTab.lua
  • Lint: resultsLabel forward reference in ShoppingListTab.lua — declaration hoisted above hover-to-fill closure, assignment left at original site
  • Lint: @return annotation in MailCODCompat.lua — split boolean, string|nil into two separate @return lines to satisfy the language server
  • Lint: sorts missing from AHCompat.lua SendBrowseQuery — added sorts = {} to satisfy the required AuctionHouseBrowseQuery fields
  • Lint: five missing globals in .luarc.json — added PersonalShopperDB_Items_Retail, GetAuctionItemLink, GreenWallAPI, gw, GW_MTYPE_CHAT
  • C_AddOns.GetAddOnMetadata crash on Classic Era — guarded with (C_AddOns and C_AddOns.GetAddOnMetadata) or GetAddOnMetadata in init.lua
  • C_Container.* crash on Classic Era — MailHandler.lua uses 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, and OnGuildMessageReceived all removed (Core/Communication.lua, Core/GoldFundMe.lua)
  • .pkgmeta cleaned up — removed YAML # comment lines that were triggering markdownlint; added .markdownlint.json, .markdownlintignore, and .vscode/settings.json to the ignore list
  • .markdownlint.json updated — added MD007/MD022/MD041 disable rules to silence false positives from non-markdown config files
  • .vscode/settings.json added — associates .pkgmeta with 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 — customChannel settings block and the three never-called Communication functions (JoinCustomChannel, LeaveCustomChannel, IsInCustomChannel) removed from init.lua, GUI/Options.lua, and Core/Communication.lua
  • goldFundMeAnnounce DB key renamed to announce — generic key shared by all tabs; existing SavedVariables will reset to defaults on first load after upgrade
  • PS_OrderTombstones added to .luarc.json globals; SendChatMessage channel 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 /reload via framePositions.donorList SavedVariable (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_REQUEST back to the sender whenever an incoming campaign has a newer version, triggering a full GFM_RESPONSE whisper (no size limit) that includes all pledges
  • Available orders leaking into GFM tab — stale availableOrdersScroll AceGUI widget reference was reused after switching away from the Available tab; fixed by niling both scroll refs on tab leave and adding a currentTab ~= "available" early-exit guard in RefreshOrderList
  • Donor dialog double-release error — AceGUI Frame fires OnClose on Hide(), causing a double-release when the dialog was also explicitly released; fixed by niling the OnClose callback before every programmatic Release() call
  • Donor dialog always centering on screen — AceGUI OnAcquire auto-shows the frame at CENTER and ApplyStatus re-centers whenever status.left/top are nil; fixed by hiding before snapping, positioning relative to the main window, then writing resolved pixel coords back into the AceGUI status table so ApplyStatus preserves the position
  • GFM donation recipient double-realm — playerName received over a cross-realm channel already contained a -Realm suffix; the name is now stripped to base before appending the campaign's playerRealm

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.lua and Modules/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 in ShoppingList:GetAll
  • AHScanner sort crash on nil priority — table.sort crashed when item.priority was nil; guarded with or 99 fallback
  • nil priority on item add — PS_ShoppingListSettings.defaultPriority absent in old saves; triple fallback or 2 added in ShoppingList: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 dumpsl diagnostic slash command: dumps all PS_ShoppingList entries 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 via PS_ShoppingList SavedVariable; 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.professionSpec table tracks the player's active sub-specialization per profession

Bug Fixes

  • Crash on "Add to Shopping List" � attempt to compare table with number at ShoppingList.lua:83; caller was passing one table argument instead of a numeric itemId followed by an options table
  • Lint errors in GUI/ShoppingListTab.lua � undefined WoW globals, protected AceGUI field access, IsSpellKnown wrong arg count
  • Lint errors in Modules/AHScanner.lua � undefined GetNumAuctionItems and GetAuctionItemInfo
  • Lint errors in GUI/AHScannerFrame.lua � undefined AH globals and protected AceGUI .frame field 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 RequestOrders throttle 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 SendChatMessage block error � Blizzard blocks SendChatMessage calls 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; MailHandler scans 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, and CANCELLED states 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 calls SetChecked(false) on SendMailMoneyButton before 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
  • SendCommMessage channel index � must pass an integer channel index for CHANNEL distribution; was incorrectly passing a string channel name
  • AceComm-3.0 registration � RegisterComm/SendCommMessage must be called on the host addon's mixin, not the standalone library; fixed by using addon:RegisterComm()
  • DeltaSync %z gsub pattern � Lua 5.1 treats a literal \0 as end-of-string inside character classes; replaced with %z to fix "malformed pattern (missing ']')" on every CHAT_MSG_CHANNEL event
  • DeltaSync debug buffer unification � lib.debugMessageBuffer and addon.debugMessageBuffer were separate tables; RedrawDebugMessages wiped lib messages on tab switch; unified to a single shared reference
  • DeltaSync.debugEnabled live sync � /ps debug now also sets DeltaSync.debugEnabled so COMMS.RECEIVE logs appear immediately without /reload

[v0.0.3-alpha] (2026-01-21) - Embedded Libraries

Internal

  • Embed external libraries � LibDataBroker-1.1 and LibDBIcon-1.0 moved into Libs/ folder and loaded directly; removed from .pkgmeta externals
  • Switch externals to GitHub � .pkgmeta external 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.lua defines the order schema (itemId, quantity, maxPrice, requester, status, timestamp)
  • Communication system � AceComm-3.0 based message bus; guild and whisper distribution; server-wide hidden PersonalShopper channel for cross-guild reach; custom user-configurable announcement channel
  • Mail system integration � MailHandler.lua detects 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.0 minimap icon; LibDataBroker-1.1 data 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 � enUS base locale loaded via Locale.xml; all user-facing strings keyed through L[...]

Bug Fixes

  • Addon load failure � LibDataBroker-1.1 and LibDBIcon-1.0 were declared as .pkgmeta externals but not embedded; addon failed to load; resolved by embedding both libs
  • LibDBIcon lib.xml � LibDBIcon-1.0.lua must be loaded directly; loading via lib.xml caused a nil error on init
  • AddToBlizzardOptions deprecated � removed deprecated call from Options:Init(); options panel now opened via AceConfigDialog:Open()
  • TitanPanel nil dataObj � MinimapButton and TitanPanel each created a separate LibDataBroker object; second creation returned nil; fixed by sharing one object
  • RegisterComm method not found � RegisterComm must be called in Communication:Init(), not at file load time before the addon is initialized
  • tabGroup nil on SelectTab � self.tabGroup must be assigned before calling SelectTab; moved assignment earlier in MainFrame:Open()
  • Duplicate button code � duplicate widget block referenced undefined content variable; 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 SetStatusTable for 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_Timer delay with PLAYER_ENTERING_WORLD event to ensure the channel exists before attempting to join

This mod has no additional files