File Details
TOGTools-v0.3.5
- R
- May 23, 2026
- 1.93 MB
- 0
- 12.0.1+7
- Retail + 3
File Name
TOGTools-TOGTools-v0.3.5.zip
Supported Versions
- 12.0.1
- 12.0.0
- 11.2.7
- 5.5.3
- 4.4.2
- 3.4.5
- 2.5.5
- 1.15.8
Changelog
[v0.3.5] (2026-05-23) - NamePrefix Chat-History Recall Duplicate Fix
Bug Fixes
- NamePrefix doubled the prefix when ElvUI's Up-arrow chat history recalled a previously-sent message — ElvUI's chat editbox enhancement lets the user press Up in the chat editbox to re-populate it with a previously-sent message; pressing Enter then re-sends it. The recalled text already contains the prefix that
ApplyPrefixadded on the original send (e.g.(Vishiswaz): 123), so when our hook fired again on the recall it prepended the prefix a second time, producing(Vishiswaz): (Vishiswaz): 123. Name2Chat does not double-prefix in the same scenario because its hook point inspects the outgoing message differently; ours just unconditionally prepended. Fix: inApplyPrefix, after building theprefixstring fromcfg.formatandcfg.nickname, early-exit whenstring.sub(msg, 1, #prefix) == prefix. Plainsubcomparison (not a Lua pattern) so format strings containing magic characters are safe. Edge case where the user changes their nickname between sends is acceptable: the old prefix in the recalled message won't match the new one, so the message ships as the user originally typed it rather than gaining a second prefix. Hook entry points (RetailEventRegistryand ClassicOnKeyDown) are unchanged. Location:Modules/NamePrefix/NamePrefix.lua.
[v0.3.4] (2026-05-22) - TBC /camp /exit /logout Taint Fix
Bug Fixes
/camp,/exit,/logouttyped in chat trippedADDON_ACTION_FORBIDDENon TBC / Anniversary 1.15.x with NamePrefix active — v0.3.3 replaced the per-editboxOnEnterPressedscript slot with an addon-Lua closure that invoked the captured original script viasecurecall(origScript, editBox, ...). Thesecurecallboundary clears taint at its call site, but on TBC / Anniversary the slash-dispatch chain (ChatFrameEditBoxMixin:OnEnterPressed→SendText→ParseText→ slash-command dispatcher → protectedLogout()) still failed the secure-execution check forLogout()— confirmed in the field after v0.3.3 shipped, with the failing trace[TOGTools/Modules/NamePrefix/NamePrefix.lua]:161 → [C]: securecall → ChatFrameEditBox.lua:370 → SendText:252 → ParseText:207 → SlashCommands.lua:748 → Logout(). The OnEnterPressed slot itself becomes addon-owned onceSetScriptruns on it, so the C-level taint check sees addon ownership on the dispatch path regardless of thesecurecallclear at the boundary. Switched the Classic / older-Retail hook fromSetScript("OnEnterPressed", ...) + securecall(origScript)toHookScript("OnKeyDown", ...)keyed onkey == "ENTER"or"NUMPADENTER".OnKeyDownfires in its own C dispatch frame, ahead ofOnEnterPressed, which is dispatched as a separate C-level call — our hook applies the prefix viaSetTextand returns to C beforeOnEnterPressedbegins, so addon Lua is never on the call stack whenSendText/ParseText/Logout()run. TheOnEnterPressedscript slot is no longer touched at all and stays bound to the original Blizzard handler, so the entire slash-dispatch chain executes in a fully secure context.HookScriptchains alongside any existingOnKeyDownhandler instead of claiming the slot; chat editboxes have no defaultOnKeyDownbinding, so our hook is the only one on the editbox. The retail 12.0+EventRegistry "ChatFrame.OnEditBoxPreSendText"path is unchanged. TheApplyPrefixleading-/early-exit is unchanged and continues to ensure noSetTextis issued for slash commands even though the OnKeyDown hook still runs. Updated the file-header comment block to document the rejectedSetScript + securecallapproach alongside the previously-rejectedSendChatMessage/ChatEdit_SendText/ mixin-method-replace wraps, and updated theModifyMessagedoc comment to reference the new OnKeyDown entry point. Removed the v0.3.3 temporaryOnKeyDowndiagnostic since its purpose (verifyOnKeyDownfires on TBC ahead ofOnEnterPressed) is now load-bearing in production code. Location:Modules/NamePrefix/NamePrefix.lua.
[v0.3.3] (2026-05-18) - NamePrefix /say Channel + TBC /logout Taint Fix
Bug Fixes
- NamePrefix did not fire on TBC / Anniversary 1.15.x in the v0.3.2 working tree — During pre-release work toward this version the Classic hook was experimentally rewritten twice — first to wrap the global
SendChatMessage, then to wrap the globalChatEdit_SendText(Name2Chat's pattern). Both wraps installed cleanly (verified with a temporary install-timeprint) but neither fire-time path was ever entered for user-typed chat on TBC / Anniversary 1.15.x — confirmed empirically with a temporary fire-timeprintinsideModifyMessage. On those clientsChatFrameEditBoxMixin:OnEnterPresseddispatches viaself:SendText()using a captured local reference, so addon-level global reassignment never intercepts user chat. On Classic Era 1.15.x the globalChatEdit_SendTextpath is still active and did fire during testing, which initially masked the TBC/Anniversary regression. Restored v0.3.2's per-editboxSetScript("OnEnterPressed", ...)wrap that walksChatFrame1EditBox..ChatFrameN.EditBoxviaNUM_CHAT_WINDOWS— this is the only entry point that reliably fires on every Classic flavor. Verified empirically on TBC (Anniversary,Wowhead Looterinterface 20505) and Classic Era (interface 11508). Location:Modules/NamePrefix/NamePrefix.lua. - Per-editbox
OnEnterPressedwrap trippedADDON_ACTION_FORBIDDENon/logout—SetScript("OnEnterPressed", addonFn)leaves an addon-Lua handler on the C call stack whenever Enter is pressed in a chat editbox. When the user types/logout, the originalOnEnterPressedwe invoke from inside our handler runs throughChatEdit_SendText→ChatEdit_ParseText→ slash-command dispatcher → protectedLogout(). Because addon Lua is still on the stack, taint propagates the whole way and the secure-execution layer blocksLogout()on TBC and Anniversary. Fix: invoke the captured original script viasecurecall(origScript, editBox, ...)instead of a direct call.securecallis the canonical taint-clearing dispatcher (see WoWWiki "Secure Execution and Tainting") — it saves and clears the current taint flag around the call soParseText→ slash-handler →Logout()runs untainted regardless of what sits on the C stack above. Belt-and-suspenders:ApplyPrefixalready early-exits on a leading/, so the editbox text is never modified for slash commands and itsGetTextresult stays untainted on the secure dispatch path. Location:Modules/NamePrefix/NamePrefix.lua.
New Features
- NamePrefix
Say (/s)channel toggle — New checkbox at the top of the NamePrefix tab's Active Channels list. When enabled, the configured nickname is prepended to outgoing/saymessages alongside the existing guild / officer / party / raid / instance toggles.SAYcase added to theApplyPrefixchatType dispatch table;say = falseadded toDB_DEFAULTS.global.namePrefix. Location:Modules/NamePrefix/NamePrefix.lua,GUI/NamePrefixTab.lua,TOGTools.lua. - Account-wide debug flag +
addon:Debug()helper — NewDebugsection in the Settings panel (ESC → Options → Addons → TOG Tools, or right-click the minimap button) with aVerbose debug outputtoggle. Stored atdb.global.debug, defaultfalse.addon:Debug(msg)prints|cffFF8000TOGTools|r <msg>only when the flag is true; modules call it for hook-install confirmations and fire-time diagnostics that should be silent in normal play. NamePrefix uses it for two diagnostics: the install confirmationNamePrefix: hook installed (OnEnterPressed xN)(one-shot at addon load — requires/reloadto re-fire after toggling the flag) and the per-send fire lineNamePrefix fire: chatType=X prefixed=true/false(no message echo, takes effect on the next chat send without/reload). Location:TOGTools.lua,GUI/Settings.lua,Modules/NamePrefix/NamePrefix.lua.
Improvements
- NamePrefix channel defaults are now all
false—say,guild,officer,party,raid,instanceall default tofalseinDB_DEFAULTS.global.namePrefix. Previouslyguildandofficerdefaulted totrue. Fresh installs now opt-in per channel; existing saved settings are unaffected because AceDB merges defaults without overwriting saved keys. Location:TOGTools.lua. .luarc.json/TOGTools.code-workspace— Addedsecurecall,rawget,rawsettodiagnostics.globalsfor Lua LSP coverage of the new hook code and the v0.3.2 account-wide migration block.
[v0.3.2] (2026-05-17) - NamePrefix BCC / Anniversary Fix
Bug Fixes
- NamePrefix did nothing on BCC and Anniversary (Classic Era 1.15.x) — v0.3.0 relocated the Classic hook from
ChatEdit_SendTextto the globalChatEdit_OnEnterPressedto avoid tainting OPie'ssecurecall(ChatEdit_SendText, ...)macrotext dispatch. On modern Classic builds (BCC, Anniversary 1.15.x) the chat editbox'sOnEnterPressedscript is theChatFrameEditBoxMixin:OnEnterPressedmethod, NOT the global — so the wrapped global never fired and outgoing messages went un-prefixed. Replaced the global-wrap with a per-editboxSetScript("OnEnterPressed", ...)wrap that walksChatFrame1EditBox..ChatFrameN EditBox(NUM_CHAT_WINDOWS) and prependsModifyMessageto each editbox's existing script. This catches both the legacyChatEdit_OnEnterPressedscript binding and the modern mixin-method binding without touching any global function reference. Verified safe for OPie: the wrap only touches user-visibleChatFrameNeditboxes, not OPie's synthetic Rewire editboxes, and never replacesChatEdit_SendTextorChatFrameEditBoxMixin.SendText. Confirmed against Name2Chat's own analysis thatEventRegistry "ChatFrame.OnEditBoxPreSendText"is not fired on Classic 1.15.x despiteEventRegistrybeing backported — the existinggv.isRetail120Plusgate stays correct. Location:Modules/NamePrefix/NamePrefix.lua. .luarc.json— AddedNUM_CHAT_WINDOWStodiagnostics.globals.
Improvements
- MainWindow remembers last-selected tab — Opening the main window (minimap button,
/togt, orToggle()with no arg) now restores the tab that was active the last time the user closed the window. Stored asdb.char.lastTab; per-character so different alts can have different defaults. Resolution order inMainWindow:Open(tabKey)is: explicit caller arg →db.char.lastTab(if the module still exists) → alphabetically first tab. Slash commands that pass an explicit tab (/togt np,/togt mb, etc.) still win, and clicking a different tab updates the saved value viaOnGroupSelected. Location:GUI/MainWindow.lua,TOGTools.lua. - NamePrefix is now account-wide — Moved
namePrefixfromDB_DEFAULTS.chartoDB_DEFAULTS.globalso the nickname, format string, per-channel toggles, andhideIfCharNameare configured once and shared across every alt. Toons that shouldn't self-prefix rely onhideIfCharName(now defaulting totrue) to suppress the prefix when the nickname matches the character's own name. One-shot migration inAce:OnInitializecopies any pre-upgradedb.char.namePrefixwith a non-empty nickname intodb.global.namePrefixthe first time an upgraded character logs in (only if the global table is still at defaults); subsequent alts inherit the now-global config. Leftover per-char entries are left in place since AceDB ignores keys not in the defaults. Location:TOGTools.lua,Modules/NamePrefix/NamePrefix.lua,GUI/NamePrefixTab.lua. - NamePrefix default —
hideIfCharNamenow defaults totrueinDB_DEFAULTS.global.namePrefix. New accounts no longer self-prefix on the character whose name matches the configured nickname (the common case where the nickname IS the main's name). Existing saved-variables are unaffected. Location:TOGTools.lua.
[v0.3.1] (2026-05-06) - TBC Compatibility Fix
Bug Fixes
- Addon Load tab crashed on TBC (and Wrath/Cata/Mists/Retail) —
GetNumAddOns,GetAddOnInfo,IsAddOnLoaded,GetAddOnMemoryUsage, andUpdateAddOnMemoryUsagewere called as bare globals, but TBC+ moved them all intoC_AddOns.*. Added five compat locals at the top of the module that preferC_AddOns.*when available and fall back to the bare globals for Classic Era, matching the pattern already used elsewhere in the addon. Location:Modules/AddonLoad/AddonLoad.lua.
[v0.3.0] (2026-05-04) - Mailbox Stale Watcher
New Features
- Mailbox Stale Watcher — New tab (
/togt mail/mb/mailbox) tracking mail expiration across every alt on the account so mail never gets auto-deleted at the 30-day mark for an unvisited character. Captures a per-alt inbox snapshot on eachMAIL_INBOX_UPDATEevent, stamping absoluteexpiresAt = GetServerTime() + (daysLeft * 86400)per item so the time-remaining math stays accurate even when the alt does not re-visit a mailbox for several days (same content-derived-timestamp pattern TOGPM uses). Login chat alert prints once when any alt has mail expiring within the configured threshold (default 2 days, slider-configurable 1–7 in the tab). Tab lists each alt with a row-per-mail breakdown of expiring items (sender, subject, time remaining), snapshot age display, and a- staleflag for snapshots older than 7 days. Live-refresh onMAIL_INBOX_UPDATEwhile the tab is active, so opening a mailbox in-game while the Mailbox tab is showing updates the rows in real time without forcing a manual tab-switch (addon.MainWindow:Refresh()is called from the engine's event handler whenaddon.MainWindow.activeTab == "mailbox"). Account-wide storage indb.global.mailbox.snapshots[Name-Realm]so every alt's data is visible from any character, fullName-Realmkeys throughout to prevent collisions across connected-realm clusters. Location:Modules/Mailbox/Mailbox.lua,GUI/MailboxTab.lua.
Improvements
- AceDB schema — Added
globalnamespace toDB_DEFAULTSwithmailbox = { thresholdDays, snapshots }. Account-wide scope so cross-alt mail data persists across every physical realm in a connected-realm cluster (per-db.realmwould silo each physical realm of the cluster into a separate notes table — wrong for our use case). Location:TOGTools.lua. - Slash commands —
/togt mb//togt mail//togt mailboxopen the Mailbox tab directly; help output updated. Location:SlashCommands.lua. .luarc.json— AddedGetInboxNumItems,GetInboxHeaderInfo, andChatEdit_OnEnterPressedtodiagnostics.globalsso the new mail API calls and the relocated NamePrefix hook target resolve cleanly under the Lua LSP.- TOC files — All six TOC variants (Vanilla, BCC, Wrath, Cata, Mists, Mainline) updated to load
Modules\Mailbox\Mailbox.luaandGUI\MailboxTab.luaafter the AddonLoad pair.
Bug Fixes
- NamePrefix broke OPie macrotext ring entries (custom mounts,
/castmacros) — On Classic clients, NamePrefix wrapped the globalChatEdit_SendTextto enable pre-send text modification. OPie'sLibs/ActionBook/Rewire.luadispatches each line of macro ring entries viasecurecall(ChatEdit_SendText, box, false). Replacing the global with a wrapper created in our (insecure) addon loading context tainted that function reference, so OPie's secure dispatch path picked up taint when calling it — failing for any ring slot delivered as macrotext (custom mount macros,/castslots), while direct spell-cast slots that bypass macrotext (most tradeskills) kept working. Fix: relocated the Classic hook target fromChatEdit_SendTexttoChatEdit_OnEnterPressed, which is upstream ofChatEdit_SendTextin the user-typed Enter-key flow but is not on OPie's Rewire dispatch path. The user-facing prefixing behavior is unchanged. The replaced-global pattern itself stays (hooksecurefuncfires post-call, so it can't be used to modify text before send); we just no longer taint the function OPie depends on. Location:Modules/NamePrefix/NamePrefix.lua.
[v0.2.0] (2026-05-04) - Addon Load Monitor
New Features
- Addon Load Monitor — New tab (
/togt al) showing every installed addon's memory usage, load status, and load timing. Sortable by name, memory, load time, and status (failures-first when descending). Summary bar shows totals (loaded / failed / disabled / on-demand) and total memory. Requires the!TOGTcompanion addon for full timing coverage across all addons. Location:Modules/AddonLoad/AddonLoad.lua,GUI/AddonLoadTab.lua. - !TOGT companion integration —
!TOGT(companion addon) added as a required dependency. Loads before all other addons at the very start of the loading screen (the!prefix sorts before all letters) and records load order and per-addon offset into theTOGToolsEarlyDataglobal. The Addon Load Monitor reads it at display time and shows a status-bar indicator (!TOGT active/!TOGT missing). - Shared
addon.RowListcomponent — New fileGUI/RowList.lua, ported and trimmed from FastGuildInvite'sGUI/RowList.lua. Reusable sortable-row list for any tab that needs a tabular data view: brand-colored clickable header bar, click-toggle ASC/DESC sort with per-columnsortKey/sortDescDefault/sortable/gapBeforeopts and tie-break onentry.name, alternating row banding, virtual-scroll pool that grows with the parent's height, custom Blizzard-textured scrollbar, mouse-wheel scroll. Cells useSetWordWrap(false)+SetMaxLines(1)and anchorLEFT/RIGHTto the parent so column layouts compact elastically when the user shrinks the window — no row-wrap. Public API::New(parent, opts),:SetData(arr),:SetSort(key, desc),:Refresh(),:Detach(). Registered to all six TOC files afterCompat.lua, beforeMainWindow.lua. Compat.luashared GUI helpers —addon.Tooltip.Owner/AnchorFrame(smart anchor flipping based on screen half),addon.AceGUIFrameScripts(leak-safe raw frame scripts that restore prior handlers on pooled-widget release),addon.GUI.AttachTooltip,addon.GUI.MakeColumnHeader,addon.GUI.ApplyMinResize,addon.GUI.DetachPool. Used by every tab.
Bug Fixes
- Load times reporting
0.000s—GetTime()is frame-locked, so everyADDON_LOADEDevent fired in the same loading-screen frame returned the same timestamp. Per-addon deltas collapsed to zero. Switched the timing capture in!TOGT.luaand the fallback capture inModules/AddonLoad/AddonLoad.luatodebugprofilestop(), which has sub-millisecond precision. Offsets are stored in seconds (ms / 1000) so downstream consumers are unchanged. - Load Time column sort produced random-looking order — The sort comparator in
GUI/AddonLoadTab.luawas readingrow.offset(cumulative seconds since the first event) instead ofrow.loadTime(per-addon delta). Becauseoffsetis monotonic with load order, clicking the header effectively sorted by load order regardless of direction. Comparator now readsrow.loadTime. - Addon Load tab wrapped to multiple lines on horizontal shrink — The tab used AceGUI Flow with
widget:SetWidth(...)per cell, so when the user shrunk the window narrower than the column-width sum AceGUI moved cells to a new line and the layout fell apart. Replaced the row-rendering path withaddon.RowList, whose anchor-based cells truncate instead of wrapping. The window'sminWidthwas tightened from 640 to 520 now that compaction is graceful. - Tab status bar leaked between tabs —
MainWindow:DrawTabnow resets the status text to the version string before delegating to the tab module, so a tab that overrides the status (e.g. Addon Load showing!TOGT active/missing) doesn't bleed that text into the next tab the user opens. - Tab content didn't reflow on tab switch —
MainWindow:ApplyTabSizenow callsf:DoLayout()after sizing, so when switching between tabs whoseWINDOW_SIZEdiffers, the new tab's content reflows immediately instead of inheriting stale layout.
Improvements
- Addon Load tab column widths tightened — Status reduced from 175 → 85 px (fits "Wrong Version"); Memory reduced from 110 → 65 px (fits "999.9 MB"); Load Time reduced from 110 → 60 px. The auto-width Addon Name column reclaims the freed space.
RowList'sSCROLLBAR_GUTTERreduced fromSCROLLBAR_WIDTH + 6toSCROLLBAR_WIDTH + 2so the rightmost column sits ~5 px from the scrollbar. Status column getsgapBefore = 5to break the visual collision between right-justified Load Time text and left-justified Status text. - Sort indicator removed from active column header —
RowList:_updateHeaderTextno longer appends a glyph (v/^) to the active sort column; WoW's default font doesn't render unicode triangles cleanly and ASCII letters look like typos. Click affordance is communicated via the column tooltip. - AceGUI pool-bleed protection —
RowList:Detach()orphans the row pool, header, and scrollbar toUIParenton host-widget release; a_detachedguard on:Refresh()makes sure a recycled host doesn't trigger a pool grow that would re-attach rows into the next addon's widget. Wired viabody:SetCallback("OnRelease", ...)inGUI/AddonLoadTab.lua. .luarc.jsonupdated — Addeddebugprofilestoptodiagnostics.globalsin bothTOGTools/.luarc.jsonand!TOGT/.luarc.jsonso the new timer call resolves cleanly under the Lua LSP.
[v0.1.0] (2026-05-04) - Initial Release
New Features
- Project scaffolding — TOC files for all WoW versions (Vanilla 11508, TBC 20505, Wrath 30405, Cata 40402, Mists 50503, Retail 110207/120001/120000),
.pkgmetawith BigWigs packager config,.luarc.json(Lua 5.1 LSP),.markdownlint.json, and.github/workflows/release.ymlfor tag-triggered CurseForge releases. - Core addon —
TOGTools.lua: AceAddon-3.0 instance with AceConsole-3.0, AceEvent-3.0, AceHook-3.0 mixins; AceDB-3.0 schema (charscope); version detection flags (gv.isVanilla,gv.isTBC,gv.isWrath,gv.isCata,gv.isMists,gv.isClassic,gv.isRetail,gv.isRetail120Plus). - Slash commands —
SlashCommands.lua: all/togtcommand registration and handling in a dedicated file, separate from core addon logic. Subcommands:/togt(toggle window),/togt np(Name Prefix tab),/togt settings(open settings),/togt vc(version check). - Tabbed main window —
GUI/MainWindow.lua: dynamic AceGUI Frame + TabGroup; tabs auto-populate fromaddon.modulessorted alphabetically bylabel; per-tab locked/unlocked window sizing viaWINDOW_SIZE; ESC-to-close proxy; position persistence via AceDB. - Name Prefix module —
Modules/NamePrefix/NamePrefix.lua+GUI/NamePrefixTab.lua: wrapsChatEdit_SendText(Classic) or hooksEventRegistry(Retail 12.0+) to prepend a configurable nickname to outgoing chat messages. Settings: enable/disable, nickname, format string with live preview, per-channel toggles (Guild, Officer, Party, Raid, Instance, custom channel), skip-exclamation option, suppress-if-char-name option. Per-character DB scope. Skips messages beginning with/(slash commands/macros) unconditionally. - Minimap button —
GUI/MinimapButton.lua: LibDataBroker-1.1 launcher + LibDBIcon-1.0 registration. Left-click toggles the main window; right-click opens the native addon settings panel. Icon:textures/ToGTools_PH_MMB.tga. Button show/hide state and position persisted inDB.char. - Addon settings panel —
GUI/Settings.lua: AceConfig-3.0 options table registered under ESC → Interface → Addons → TOG Tools (native Blizzard panel).addon:OpenSettings()usesSettings.OpenToCategory(Classic Era 1.15+ / Retail 10+),InterfaceOptionsFrame_OpenToCategory(older builds), orInterfaceOptionsFrame:Show()as a final fallback — detected by API presence, not version flag. - VersionCheck-1.0 integration —
VC:Enable(Ace)called on all non-Retail versions inOnInitialize;/togt vcbroadcasts a guild-wide version check and prints responses after 21 seconds. - Embedded libs —
libs/LibDataBroker-1.1.lua,libs/LibDBIcon-1.0.lua(single-file embeds, copied from TOGProfessionMaster). - MIT License —
LICENSEfile added; referenced in.pkgmetavialicense-output. - CurseForge project — Project ID
1533830set in.pkgmetaand all TOC files. - Governance files —
.github/copilot-instructions.mdandCLAUDE.mdwith full project rules (module pattern, commit/tag process, changelog format, HTML doc rules, no-tag rule).
Bug Fixes
- Name Prefix not reaching other players —
hooksecurefuncfires afterSendChatMessagehas already been called. Fixed by wrappingChatEdit_SendTextdirectly (local orig = ...; ChatEdit_SendText = function(...) ... return orig(...) end) so the prefix is applied before the message is sent. Location:Modules/NamePrefix/NamePrefix.lua. - Macros/slash commands being prefixed — Added an unconditional early-return in
ModifyMessagewhen the message starts with/. Location:Modules/NamePrefix/NamePrefix.lua.

