File Details
v0.5.0-beta
- B
- Mar 4, 2026
- 129.44 KB
- 2
- 12.0.1
- Retail
File Name
GRIP-v0.5.0-beta.zip
Supported Versions
- 12.0.1
GRIP – Guild Recruitment Automation
v0.5.0-beta (2026-03-04)
Full Changelog Previous Releases
- Update GRIP.toc
- Migrate Blacklist panel from FauxScrollFrame to ScrollBox + DataProvider
Replace the deprecated FauxScrollFrame in the Blacklist panel (UI_Home_Blacklist.lua) with WowScrollBoxList + MinimalScrollBar + DataProvider, matching the pattern already proven in the Potential list migration. Eliminates manual row pool management (CreateFramePool, ResizeBlacklistRows, FauxScrollFrame offset tracking) in favor of ScrollBox's automatic row recycling and element initializer pattern. Updates the no-DB clearing path in UI_Home.lua to use empty DataProvider instead of iterating bl.rows. - Migrate Potential list from FauxScrollFrame to ScrollBox + DataProvider
Replaces the deprecated FauxScrollFrame + manual row pool pattern in the Home page Potential list with the modern ScrollBox + DataProvider API. Row recycling is now handled automatically by ScrollBox, eliminating ResizePotentialRows and ~100 lines of manual pool management. Column widths are stored centrally for the element initializer to read on each display pass. All existing behavior (class colors, tooltips, right-click menu, empty state, stripe alternation) is preserved. - Fix resize drag jump, dynamic max bounds, and debug message isolation
Replace native StartSizing with manual OnUpdate resize to eliminate the visual jump when clicking the resize grip. Max bounds now refresh dynamically on each drag start to match the current game window size, and SCREEN_MARGIN is removed so the frame can fill the full screen.
Debug and GateTrace messages no longer fall back to DEFAULT_CHAT_FRAME — they only appear in the dedicated Debug chat tab. /grip debug on now auto-creates the Debug chat window and enables ring buffer capture; /grip debug off disables capture. The unused _warnedMissingDebugWindow config key is removed. - Deduplicate local AddButtonAccent in UI_Home → shared W.AddButtonAccent
Remove the local AddButtonAccent helper from UI_Home.lua and replace all 4 call
sites with the identical shared W.AddButtonAccent already in UI_Widgets.lua.
Pure dedup — no behavior change, no other files modified. - Add visual polish to Settings and Ads pages (tooltips, separators, accents, fonts)
Brings Settings and Ads pages to visual parity with the already-polished Home page. Adds descriptive tooltips to all 39 interactive elements, inserts 7 section separators (1px, 8% white), applies gold/red button accent underlines to 10 action buttons, upgrades 5 section headers from GameFontNormalSmall to GameFontNormal, and applies destructive text color (1, 0.6, 0.6) to Clear Selections and Remove buttons. Promotes AddButtonAccent from UI_Home local to shared W.AddButtonAccent in UI_Widgets.lua. No functional behavior changes. - Add visual polish to Home page (class hover, status panel, tab underline, ghost border, gradients)
Visual-only polish pass on the Home page — 11 cosmetic items with zero functional changes. Adds class-colored row hover, bordered status panel with upgraded font, gold active tab underline, ghost strip backdrop with pulse animation, potential table header gradient, blacklist panel border, button accent underlines, empty state search icon, and content accent line. All frames use BackdropTemplate correctly for 12.0.1. Y-offset math updated to accommodate the new 38px status panel. - Resolve all remaining Deep Audit #2 findings (M1-M3, L1-L4)
Fix 7 remaining Deep Audit #2 findings:- M1: Move ghost auto-invite no-response timeout from queue time to execution time (Invite.lua)
- M2: Add 0.2s elapsed throttle to Ghost Mode updater OnUpdate (GhostMode.lua)
- M3: Rename shadowing local
radvariables and use upvalued trig functions (Minimap.lua) - L1: Add explicit nil guard on GRIPDB_CHAR.counters before daily cap check (Whisper.lua)
- L2: Use structured BuildGateCtx() for BL_ExecutionGate calls in Who.lua
- L3: Optimize debug log ring buffer trim from O(n*over) to O(n) bulk copy (Core.lua)
- L4: Document Phase 1 FlushOne/FlushAll as zero-caller public API (GhostMode.lua)
- Add combat guard to SetPropagateKeyboardInput in UI OnShow
Wraps the SetPropagateKeyboardInput(false) call in UI.lua's OnShow handler
with an InCombatLockdown() check to prevent taint errors when the UI frame
is shown during combat. Skipped calls are logged via GRIP:Debug. This fixes
beta blocker M4 and matches the defensive pattern already used in GhostMode.lua. - Summary Add right-click context menu to addon compartment button
Description
Add a right-click dropdown menu to the addon compartment button using MenuUtil.CreateContextMenu (same pattern as UI_Home_Menu.lua). The menu provides quick access to Toggle UI, Status, Build Scan Queue, Ghost Mode session toggle, and Whisper queue toggle. The compartment click handler now supports both string and table button arguments for WoW version compatibility. Tooltip updated to reflect the new right-click action. - Fix UI QA findings: stale row pool, HookScript, UISpecialFrames guard, TOC placeholders
Release row pool entries when scroll area collapses to zero height in both
Potential and Blacklist panels, preventing stale hidden rows. Switch
HookDirtyTracking from SetScript to HookScript so template-based edit box
handlers aren't overwritten. Add duplicate guard for UISpecialFrames
insertion. Add X-Curse/Wago/WoWI placeholder IDs to the TOC. - Update .gitignore
- Fix UI QA pre-beta P0/P1/P2 issues across 4 files
Fix P0 nil arithmetic crash on state.whoIndex in Home status bar. Fix P1 BuildInsertedTextAtCursor to return cursor byte position so token insertion places the cursor correctly. Add minimap button click-without-drag guard (_gripDragged flag) matching UI.lua's resize grip pattern. Wrap Ghost overlay gamepad API calls in pcall for clients without gamepad support. Clear _gripDirty on edit box focus lost so auto-refresh resumes after user clicks away. - Localize remaining bare globals in 3 UI files
Add upvalue locals for string.upper, string.sub, table.sort, and
InCombatLockdown in UI_Home.lua, UI_Home_Blacklist.lua, and
UI_Home_Menu.lua. Updates call sites to use the localized aliases,
matching the convention used across all other GRIP source files. - Summary Fix resize handle click-without-drag bug and add screen bounds clamping
Description
Fix resize grip clicking without dragging causing unintended frame resize by adding a 3-pixel drag threshold — clicks that don't exceed this revert to the original size. Add screen bounds clamping (20px margin) to SetResizeBounds, RestoreFrameGeometry, and SaveFrameGeometry so the frame can never exceed screen dimensions or get saved off-screen. Add /grip reset command to restore UI position and size to defaults as a recovery mechanism. - Add Home page button icons, Ghost strip tint, and class-color row borders
Phase 3 visual polish for the Home page. Adds 14×14 icons (magnifying glass, chat bubble, broadcast) to the left of Scan, Whisper+Invite, and Post Next button labels. The Ghost Mode status strip now shows a subtle green background during active sessions and orange during cooldown. Each Potential list row gets a 2px class-colored left border bar that's always visible, with header and row columns shifted 4px right to accommodate. - Add Home page visual polish — tooltips, class colors, status bar, separators
Phase 1+2 visual polish for the Home page. Adds rich tooltips on all buttons, column headers (W/I status legends), and potential/blacklist rows. Candidate names are now class-colored. Status bar uses color-coded ON/OFF indicators and pipe separators. Row stripes are more visible. Phase 2 adds section separators, contextual hint lines that adapt to current state, a red-tinted Clear button with confirmation popup (>10 candidates), and a restructured blacklist header with separated title and column labels. - Filter out instance/PvP/dungeon zones from dynamic zone discovery
Replace allDescendants=true with a controlled two-level C_Map walk in
GatherZonesGroupedByContinent(). Direct Zone children of each continent
(outdoor zones) plus their direct Zone sub-children (e.g. "Azj-Kahet -
Lower") are included; zones nested inside Dungeon-type parents are
structurally excluded. BuildExcludedZoneNames is now force-called at the
top so EJ/BG exclude names are ready before filtering. - Add dynamic zone discovery grouped by continent via C_Map
Settings zone filter checklist now dynamically enumerates zones at runtime via C_Map.GetMapChildrenInfo(), grouped by continent with expansion-friendly display names (newest first). This auto-discovers all zones including new Midnight areas without manual list maintenance. Static ZONES_BY_EXPANSION kept as fallback if C_Map fails. Cache clears on /grip zones reseed. - Split UI_Home.lua into focused modules for popups, blacklist panel, and context menu
Split UI_Home.lua (1518 lines) into 4 files:- UI_Home_Popups.lua: StaticPopup dialogs for blacklist add/remove
- UI_Home_Blacklist.lua: Permanent blacklist panel (shell, rows, layout, update)
- UI_Home_Menu.lua: Right-click context menu (MenuUtil)
- UI_Home.lua: Potential panel, buttons, Ghost strip, layout orchestration
Slash.lua (877 lines) stays as-is — it's a flat dispatcher with no natural split points.
Shared helpers promoted to GRIP table methods for cross-file access.
- Replace UIDropDownMenu with MenuUtil.CreateContextMenu in Home page
Replace the legacy UIDropDownMenu context menu on potential-list rows with
the modern MenuUtil.CreateContextMenu API. Removes EnsureRowMenu helper,
the "GRIP_PotentialRowMenu" global frame, and all UIDropDownMenu_* calls.
Same Blacklist and Invite to Guild actions with identical guard logic. - UTF-8-safe budget trim, slower UI ticker, Ghost queue dedup
Three P3 cleanup fixes in one pass:- Replace byte-at-a-time budget trim loops in UI_Settings.lua and UI_Ads.lua with a shared GRIP:TrimToBudget() helper (Utils.lua) that respects UTF-8 character boundaries via binary search, preventing split multi-byte characters in accented/non-Latin guild names.
- Reduce UI refresh ticker from 0.2s to 0.5s while the GRIP window is open (UpdateUI coalescing already prevents redundant work).
- Add actionType+target dedup safety net in Ghost Mode's QueueAction to prevent double-queued whispers/invites from race conditions.
- Replace fragile self parameter with explicit GRIP in Whisper.lua and Invite.lua local helpers
Remove the self parameter from local helper functions in Whisper.lua
(IsWhisperBlocked, WhisperBlacklistGate, PurgeBlacklistedFromPendingAndQueue)
and Invite.lua (IsInviteBlocked, InviteBlacklistGate, PurgeBlacklistedPending).
All internal self:Method() calls replaced with GRIP:Method() directly, and all
17 call sites updated to drop the now-unnecessary first argument. Completes the
pattern established in Post.lua (P2 #8) across all three Recruit/ pipeline files. - Replace fragile self parameter with explicit GRIP in Post.lua local functions
Remove the self parameter from three local helper functions in Post.lua
(IsPostBlocked, PostBlacklistGate, PurgeBlacklistedFromPostQueue) and
replace all internal self:Method() calls with GRIP:Method() directly.
Updated all 7 call sites to drop the now-unnecessary first argument.
Colon-syntax methods that call these helpers are unchanged. - Centralize duplicated utility functions into shared Utils.lua and UI_Widgets.lua
Move three duplicated utility patterns into Core/Utils.lua: BuildGateCtx (gate
context builder, was copied in Post/Whisper/Invite), CfgBool and CfgNum (typed
config accessors, were in GhostMode). All consumer files now use one-liner local
wrappers that delegate to the centralized versions, preserving identical call
signatures at all existing call sites. - Fix remaining Post.lua PurgeBlacklistedFromPostQueue(self) → GRIP in PostNext
Change the two remaining PurgeBlacklistedFromPostQueue(self) calls in
GRIP:PostNext() to pass GRIP explicitly, completing the self→GRIP safety
fix across all four call sites in Post.lua. - Fix Debug ring buffer O(n²) trim and Post.lua self→GRIP safety
Replace O(n²) table.remove loop in Debug.lua PersistAppend with a single-pass
batch shift for ring buffer trimming. Fix two PurgeBlacklistedFromPostQueue
calls in Post.lua that passedselfinstead ofGRIPexplicitly, making them
safe regardless of call syntax. - Centralize duplicated utility functions into shared Utils.lua and UI_Widgets.lua
Promotes ~180 lines of duplicated local helper functions from 10 consumer files into two shared locations: Core/Utils.lua (8 pipeline/data helpers on GRIP table) and UI/UI_Widgets.lua (6 UI widget helpers on GRIP.UIW table). All consumer call sites updated to use the shared versions. Eliminates maintenance drift risk from having identical logic copied across GhostMode, Slash, Events, Who, Whisper, Invite, Post, UI_Home, UI_Settings, and UI_Ads. - Fix zone data duplicates and make Current button use player's actual zone
Removes duplicate "Zuldazar" from the Midnight expansion group (it's a BfA zone only) and removes "Wintergrasp" from the WotLK zone list since it's already correctly excluded as a PvP zone in STATIC_ZONE_EXCLUDE_EXACT. Fixes the Settings page "Current" zone button to use GetRealZoneText() for the player's actual zone instead of incorrectly selecting the first expansion group. The button now additively adds the current zone to the filter, allowing multi-zone selection across sessions. - Pre-Beta-Realese-Update
- Fix stale comment in Utils.lua referencing GRIPDB instead of GRIPDB_CHAR
The whisper echo suppression comment on line 29 of Core/Utils.lua still
referenced GRIPDB.config, but config moved to GRIPDB_CHAR in the
SavedVariables split. Updated the comment to match. No logic changes. - Fix stale comment in Core.lua referencing GRIPDB instead of GRIPDB_CHAR
The Ghost Mode session comment on line 114 of Core/Core.lua still referenced
GRIPDB.config for ghostCooldownUntil, but config moved to GRIPDB_CHAR in the
SavedVariables split. Updated the comment to match. No logic changes. - Split GRIPDB into account-wide and per-character SavedVariables
Split the single GRIPDB SavedVariable into GRIPDB (account-wide) and GRIPDB_CHAR (per-character). Blacklists and no-response counters are now shared across all characters on the account, preventing alts from re-contacting blacklisted players. Per-character data (config, potential list, filters, zone lists, minimap position, debug log) remains isolated per character. Includes idempotent migration that moves existing data from the old single-table schema to the new split schema on first load. - Add ChatThrottleLib detect-and-use integration for WHISPER sends
Integrate ChatThrottleLib (CTL) for WHISPER sends only in SendChatMessageCompat.
When CTL is present (via Ace3 or standalone), whispers route through CTL at
NORMAL priority for CPS bandwidth fairness with other addons. CHANNEL sends
are never routed through CTL (hardware-event restriction). Without CTL loaded,
behavior is unchanged. Gate trace logs "SEND ROUTE: ChatThrottleLib" at
verbosity 3. - Add upvalue localizations for WoW API and Lua stdlib across all modules
Add file-top upvalue blocks to all 22 .lua source files, caching frequently-used
Lua standard library functions and WoW API references as locals. Each block is
grouped by category (-- Lua / -- WoW API) and placed immediately after the addon
table line. Data/Maps_Zones.lua excluded (pure data). No GRIPDB or CreateFrame
localized per design rules. Reduces _G hash lookups on hot paths (Ghost Mode
drain loop, whisper ticker, /who processing, UI refresh). - Add Ghost Mode UI surface (Settings checkbox + Home page session status)
Add the missing UI controls for Ghost Mode, which was previously slash-command only.
Settings page gets a new section with master enable checkbox, session max (5-120 min) and cooldown (1-60 min) edit boxes, and an Apply button. Home page gets a live status strip showing session state (Active with timer/queue/actions, Cooldown with remaining time, or Ready), plus a Start/Stop toggle button. Sub-controls grey out when Ghost Mode is disabled. Strip hides entirely for non-Ghost users. - Summary Add post auto-queue through Ghost overlay and campaign tracking (Phase 2e)
Description
When a Ghost Mode session is active, the post scheduler ticker now automatically drains queued Trade/General posts through the Ghost overlay frame instead of waiting for manual /grip post. AutoDrainPostQueueGhost loops all queue items, resolves channels, checks blacklist gates, and queues valid posts for hardware-event execution. Channel resolution failures leave items in queue for retry. Also adds RecordCampaignAction("post") to all three post send paths (ghost-auto, ghost via PostNext, direct send) for campaign cooldown consistency. - Summary Add auto-queue guild invite after whisper success in Ghost Mode (Phase 2d)
Description
When a Ghost Mode session is active and a whisper succeeds (OnWhisperInform), the candidate's guild invite is now automatically queued through the Ghost overlay frame instead of waiting for manual /grip invite. The new AutoQueueGhostInvite function validates all preconditions (blacklist gate, guild permissions, entry state), queues the invite action with a re-check at execution time, and starts the standard 70-second no-response timeout with escalation. This closes the whisper→invite gap during automated ghost sessions. - Summary Add whisper auto-start on Ghost Mode session (Phase 2c)
Description
Ghost Mode sessions now auto-start the whisper ticker, completing the scan→whisper
pipeline. WhisperTick becomes self-sustaining during ghost sessions: when its queue
empties, it rebuilds from Potential to pick up candidates added by ongoing /who scans.
The ticker starts directly (bypassing StartWhispers toggle behavior) and stops cleanly
when the ghost session ends. - Summary Add /who scan integration to Ghost Mode (Phase 2b)
Description
Ghost Mode sessions now automatically queue /who scans through the invisible overlay
frame instead of requiring manual keybind presses. SendNextWho() detects active ghost
sessions and routes through QueueAction("scan"), with auto-chain after processing
results, auto-rebuild of exhausted queues on session start, and proper cleanup of
ghost-queued state on session stop. Normal (non-ghost) scan path is unchanged. - Rewrite Ghost Mode with overlay frame, universal queue, and session management
Replaces Phase 1 CHANNEL-only Ghost Mode with full architecture: invisible overlay frame captures any hardware input (mouse, keyboard, gamepad) to drain a universal action queue one item per event. Adds session management (1-hour max with auto-timeout, 10-minute persistent cooldown across reload/relog), combat safety (overlay hidden during InCombatLockdown), and /grip ghost slash commands. Invite.lua and Post.lua now queue their hardware-event-restricted calls through Ghost Mode when a session is active, while preserving backward compatibility with Phase 1 API. - Add campaign cooldown reminder for session fatigue protection
Implement a campaign cooldown that warns after extended continuous recruiting (default 30 min) and optionally auto-pauses the whisper queue at 2x the threshold. Uses a sliding activity window with configurable gap reset. Controlled via /grip set cooldown <min>|on|off and visible in /grip status output. - Add frame pooling for dynamic potential/blacklist row counts on resize
Replace hardcoded 30 potential rows and 12 blacklist rows with CreateFramePool-based dynamic sizing. Row count now adapts to the visible scroll area on every resize — large monitors get more rows (no blank space), small frames get fewer rows (no wasted frames). Pools lazily create Button frames on first Acquire and reuse them on subsequent resize cycles. - Replace single 8s guild link retry with 15s×8 ticker + bulk request API
CLUB_FINDER_RECRUITMENT_POST_RETURNED takes ~95 seconds to fire after
RequestPostingInformationFromClubId — the single 8-second retry missed this
window entirely. Replaced with a C_Timer.NewTicker(15s, max 8 ticks = 2 min)
that re-requests posting data each tick and adds RequestSubscribedClubPostingIDs
(Blizzard's own CommunitiesFrame startup path). Ticker cancels on success or
when CLUB_FINDER_RECRUITMENT_POST_RETURNED fires. Also removed the one-shot
_gripGuildLinkRequested flag — no longer needed since the ticker manages retry. - Fix resolution with multi-path ClubFinder API chain + byte budget
Fix never resolving to a clickable link. Root cause:
ClubFinderGetCurrentClubListingInfo requires the Communities frame to
have been opened, even after LoadAddOn. Added C_ClubFinder.GetRecruitingClubInfoFromClubID
as primary path, SV cache for /reload survival, async RequestPostingInformation
warm-up, and delayed retry. Also fixes byte budget counter to reserve 120 bytes
for worst-case link length instead of using the short guild name fallback. - Beta-ready docs & defaults — README rewrite, CHANGELOG stamp, safer config values
Rewrites README.md with full feature coverage (daily cap, opt-out detection, templates, sound feedback), updated API names, complete slash command reference, and user-facing disclaimers about Silence penalties and Guild Finder requirements. Stamps CHANGELOG as 0.5.0-beta, removes phantom doc references, and updates the planned/future section. Makes defaults more conservative for beta safety (whisperDelay 3.0s, blacklistDays 14, postInterval 20min). Adds /grip set ghostmode on|off slash command and bumps TOC version to 0.5.0-beta. - Add Addon Compartment support — GRIP appears in minimap addon bag dropdown
GRIP now registers with WoW's Addon Compartment (10.1.0+), the dropdown from the minimap "addon bag" icon. Clicking the compartment entry toggles the GRIP window, same as the minimap button left-click. Hover tooltip shows basic usage hints. Existing minimap button behavior is unchanged — the compartment is a secondary access point. - Add /grip link command + EnsureDB whisperMessages cap
Adds /grip link slash command that prints the current guild name and Guild Finder link resolution for troubleshooting template tokens. EnsureDB now enforces a 10-template cap on whisperMessages to protect against manual WTF/SavedVariables edits. Help text updated with guild finder listing note. - Sound Feedback — optional audio cues for recruitment events
COWORK VERIFICATION — Task 4 (R20: Sound Feedback)
Files modified (7):- DB/DB_Init.lua — 5 sound boolean config keys in DEFAULT_DB + type-checks in EnsureDB
- Core/Utils.lua — GRIP:PlayAlertSound(soundKitID) helper
- Recruit/Whisper.lua — StopWhispers sound + cap warning sounds (80% + hard cap)
- Recruit/Invite.lua — OnInviteSystemSuccess sound
- Recruit/Who.lua — ProcessWhoResults sound (when added > 0)
- UI/UI_Settings.lua — Sound Feedback section with master + 4 sub-checkboxes
- Core/Slash.lua — /grip set sound on|off + help + status
Verify:
- All SOUNDKIT references use fallback IDs (SOUNDKIT and SOUNDKIT.X or ID)
- PlayAlertSound checks soundEnabled master toggle
- soundScanComplete defaults to false (truthy check in Who.lua)
- Other sound keys default to true (~= false check pattern)
- Sub-checkboxes grey out when master is disabled
- No stale references to sound config outside these 7 files
- UpdateScrollContentHeight considers all 6 sound elements (hdr + 5 checkboxes)
- Whisper Template Variety — multi-template rotation (R19)
Support multiple whisper message templates with automatic rotation.
Templates can be managed via the Settings page multi-template editor
(Prev/Next navigation, Add/Remove, Save All) or via /grip templates
slash commands. Sequential (round-robin) and random rotation modes
reduce spam filter detection and improve player perception. - Add opt-out response detection — auto-blacklist candidates who reply with refusal phrases
Detects incoming whisper replies from GRIP candidates containing opt-out phrases ("no thanks", "not interested", "stop", "already in a guild", etc.) and immediately permanent-blacklists them with reason "opt-out". Only acts on whispers from players in the Potential list or pending maps — random whispers are ignored. Enabled by default; toggle with /grip set optout on|off. - Add daily whisper cap — configurable limit with auto-reset, UI display, slash command
Adds a configurable daily whisper cap (default 500, 0 = unlimited) to prevent Silence penalties from excessive automated whispers. Counter persists in SavedVariables, auto-resets at calendar day change, and prints a soft warning at ~80% usage. Only GRIP's automated whispers count — manual player whispers are not tracked. Configure via /grip set dailycap <N>, view via /grip status or the Home page status bar. - UI/UI_Ads.lua — Complete rewrite addressing all 5 reported issues:
Byte budget enforcement — Each editor now has a remaining-bytes counter (bottom-right, matching Settings style), live enforcement on typing/paste that trims excess characters, and clamping so the counter never goes negative. Save is disabled when either editor is over budget.
Missing token buttons — Each editor now has its own "Insert " and "Insert " buttons that insert at cursor position (not append), with all-or-nothing budget checking (won't insert a partial token if it would exceed 255 bytes).
Preview buttons — Each editor now has its own "Preview" button that expands the template and prints the result, matching the whisper editor's behavior.
Button label consistency — Changed from "Append " to "Insert " to match Settings. All buttons now use "Insert" consistently.
Button ownership clarity — Buttons are now per-editor (each editor has its own Insert/Preview row directly below it). Save remains shared at the bottom since it saves both editors, alongside Queue Now and Post Next.
Bonus: Token ordering bug fix (3 files):
UI/UI_Ads.lua, UI/UI_Settings.lua, Core/Utils.lua — Fixed gsub running before , which mangled the longer token. Now is always replaced first. This was a latent bug that would have caused incorrect template expansion whenever both tokens were used in the same message. - Zone Overhaul — Part 2
UI/UI_Widgets.lua — Added CreateGroupedChecklist
New widget function appended after the existing CreateChecklist (which remains untouched for Races/Classes)
Renders expansion groups with gold header text (GameFontNormal, color 1/0.82/0/1)
Per-group All / None buttons inside the scrollable area (scroll with content)
Zone checkboxes indented 12px under group headers, separated by 6px gap between groups
Handles dynamic label width recalculation on resize via OnSizeChanged hook
Reuses existing W.EnsureCheckLabel and W.SetCheckLabelText helpers
UI/UI_Settings.lua — Four changes
Change A: Zone list now uses CreateGroupedChecklist at 250×200 (was CreateChecklist at 250×140)
Change B: Top-level All button rewired to use GetZonesGroupedForUI() for sync with displayed groups. Added Current button that selects only the first expansion group (Midnight). Layout: [Current] [All] [None]
Change C: Render() call passes GRIP:GetZonesGroupedForUI() instead of the flat GRIPDB.lists.zones
Change D: zoneCurrent button included in both the disable and enable blocks for DB-not-ready state - Zone system overhaul — Part 1
Maps_Zones.lua — Full rewrite
Replaced flat STATIC_ZONES (208 entries with tons of junk) with ZONES_BY_EXPANSION — 13 expansion groups (Midnight → Starting Zones) containing only zones where players realistically appear in /who
Flattened STATIC_ZONES is built automatically from ZONES_BY_EXPANSION for backward compat
Added SEASONAL_ZONES table with Darkmoon Island
Strengthened STATIC_ZONE_EXCLUDE_PATTERNS — added "BfA -", "Cataclysm -", "N'zoth Assault", changed " - Disabled" to just "Disabled"
Expanded STATIC_ZONE_EXCLUDE_EXACT — continents, 15 arenas, scenarios, world PvP, removed zones, micro sub-zones, seasonal entries
DB_Zones.lua — Seasonal detection + dungeon filtering
Added IsDarkmoonFaireActive() — calendar API primary, date-math fallback
Added IsSeasonalZoneActive(), GetActiveSeasonalZones(), RefreshSeasonalFromCalendar()
Added isDungeonMap() helper — checks Enum.UIMapType.Dungeon (value 4)
Updated all zone iteration loops in GatherAllZoneNames to skip dungeon maps
Updated GetBestZonesListForUI() to append active seasonal zones
Added GetZonesGroupedForUI() for the Part 2 UI grouped checklist
DB_Init.lua — SeedZones fix
Removed if #list > 10 then return end guard
Always wipes and rebuilds from best source (same pattern as races fix)
Appends active seasonal zones via GetActiveSeasonalZones()
Events.lua — Calendar event registration
Registered CALENDAR_UPDATE_EVENT_LIST event
Added handler that calls GRIP:RefreshSeasonalFromCalendar()
Added C_Calendar.OpenCalendar() call in PLAYER_LOGIN to request calendar data - Fix ESC opening Game Menu while GRIP UI is open
Remove SetPropagateKeyboardInput(true) from OnHide handler. Hide() fires
OnHide synchronously during OnKeyDown, which restored propagation before the handler
returned — causing ESC to leak to the keybind system and open the Game Menu. - Fixes
Fix 1 — DB_Init.lua: Added wipe(list) before the PLAYABLE_RACE_IDS loop so stale entries (Windborne Velocidrake, etc.) are cleared on every login. Orphaned filter selections are already handled by PruneFilterKeys in EnsureDB.
Fix 2 — Events.lua: Changed GUILD_ROSTER_UPDATE logging from level 2 (Debug) to level 3 (Trace), so the ~30s periodic cache-warm messages no longer flood the debug window at default verbosity.
Fix 3 — UI.lua: Added PRINTSCREEN early-return with SetPropagateKeyboardInput(true) in all three OnKeyDown handlers (main frame, editbox HookScript, editbox SetScript). PrintScreen now passes through to WoW/OS while ESC and WASD behavior is unchanged. - Update DB_Init.lua
- Filter SeedRaces to playable races only
The ID 1–100 iteration included NPC-only races (Ice Troll, Tuskarr, etc.).
Replaced with an explicit PLAYABLE_RACE_IDS table covering Classic, Allied
Races, Dracthyr, Vulpera, Mechagnome, Earthen, and Haranir. Duplicate names
(Pandaren×3, Dracthyr×2, Earthen×2) are already handled by SortUnique.
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com - Fix empty Races checklist — replace character-creation-only API in SeedRaces
SeedRaces() relied on C_CharacterCreation.GetAvailableRaces() which returns
an empty table during normal gameplay (only works on the character creation
screen). Replaced with direct iteration over C_CreatureInfo.GetRaceInfo(1..100),
which works in-game and covers all player races including Allied Races,
Dracthyr, Earthen, Haranir.
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com - Title: Repo tidy: .gitignore, README, Claude/ cleanup
Description:- .gitignore now excludes Claude/, CLAUDE.md, Docs/, and sync files from GitHub
- Removed tracked Claude/ and Docs/ files from git index (files remain on disk)
- Replaced placeholder README.md with full user-facing documentation
- Deleted 3 completed PROMPT files from Claude/
- Added superseded warning to Research_02
- Updated Research_Index with status tracking and completed work log
- Create PROMPT_Tidy_GitIgnore_README.md
- Fix and template replacement (Lua gsub count leak)
string.gsub() returns two values: the modified string and the replacement count. When used inline as the last argument to an outer gsub(), Lua expands both return values — the count becomes the n (max replacements) parameter, resulting in zero replacements. Wrapping the inner gsub() call in parentheses discards the count, passing only the escaped string.
Line 221: self:GetGuildName():gsub("%%", "%%%%") returned ("Temptation", 0) — the 0 became n=0 for the outer gsub, replacing nothing. Fixed by wrapping in (...).
Line 244: Same issue with link:gsub("%%", "%%%%") for the token.
/ were unaffected because they stored the gsub result in a local variable first, which naturally discards the second return value.
Files Modified
Core/Utils.lua — 2 lines changed (lines 221 and 244)
Verification
resolves to guild name in Preview
resolves to link or fallback in Preview
/ still work (unchanged code path)
No Lua errors (parentheses are valid Lua syntax for discarding extra return values) - Update CLAUDE.md
- GuildLink Fix
Changes Applied (4 files)
- Core/Utils.lua
GetGuildName(): Removed deadC_GuildInfo.GetGuildInfocode path (doesn't exist). Reordered logic: return cache first (survives early-login nil fromGetGuildInfo("player")), then tryGetGuildInfo("player")as the sole source.GetGuildFinderLink(): Added 5-minute cache (state._gripGuildLinkCache+state._gripGuildLinkCacheAt). Reduced trace spam to one-per-session viastate._gripGuildLinkTracedflag (resets on successful resolution). Removed all the per-step Trace calls that fired on every template render.
- Core/Events.lua
- Registered 3 new events:
PLAYER_GUILD_UPDATE,GUILD_ROSTER_UPDATE,INITIAL_CLUBS_LOADED - Added handlers that call
GetGuildName()/GetGuildFinderLink()to warm caches as soon as guild data becomes available during the login sequence.
- Registered 3 new events:
- Core/Debug.lua
- Added
GRIP:ShowDebugCopyFrame(n)— creates a movable DIALOG-strata frame with a scrollable multi-line EditBox, pre-populated with the lastnpersisted debug lines, fully selected for Ctrl+C. Frame is reused across invocations.
- Added
- Core/Slash.lua
- Added
/grip debug copy [n]subcommand - Updated help text and usage string to include
copy [n]
- Added
- Research Update
More info! - Codebase Cleanup — Complete.
Summary of all changes:
Task 1: Header blocks — All 24 .lua files now have standardized 2-line headers (-- GRIP: <Module> / -- <description>). Revision changelogs removed from every file.
Task 2: TOC file — Removed ## Rev 10 metadata and ## CHANGED block. Simplified 3-line ======== section separators to single # -- Section comment lines. Load order unchanged.
Task 3: Bindings.xml — Removed comment and multi-line NOTE block. All 4 binding elements preserved.
Task 4: Dead code removed:
GRIP.STATIC_ZONES_BY_GROUP scaffolding (Maps_Zones.lua) — unused, no references
U.AnySelected() (DB_Util.lua) — dead export; DB_Filters.lua has its own local copy
Task 5: Inline Rev N comments tidied (7 occurrences):
Core/GhostMode.lua — full header was still old (missed earlier); now cleaned
Recruit/Who.lua — (Rev 6/8) removed from section divider
Recruit/Invite.lua — 3 inline Rev 10:/Rev 11: prefixes stripped
UI/UI_Home.lua — (Rev 18 hardened) removed
DB/DB_Init.lua — (Rev 3) removed from comment
Zero functional code changes. All files parse as valid Lua/XML. - Research Update
Cleanup - GRIP 12.0.1 API Migration — Changes Applied
Files Modified
Core/Core.lua — Updated comment (GuildInvite → C_GuildInfo.Invite)
Core/Utils.lua — Added GRIP:SafeGuildInvite() compat wrapper
Recruit/Invite.lua — Replaced GuildInvite(name) callsite
Hooks/UnitPopupInvite.lua — Replaced GuildInvite(targetName) callsite + updated availability check
Recruit/Who.lua — Fixed scanMaxLevel fallback + added origin param to SendWho()
Data/Maps_Zones.lua — Added Harandar zone
Change Details- GuildInvite → C_GuildInfo.Invite compat wrapper
Added: GRIP:SafeGuildInvite(name) in Core/Utils.lua (line 406–417) — tries C_GuildInfo.Invite() first, falls back to GuildInvite(), prints error if neither exists
Replaced: GuildInvite(name) → self:SafeGuildInvite(name) in Recruit/Invite.lua (line 340)
Replaced: GuildInvite(targetName) → GRIP:SafeGuildInvite(targetName) in Hooks/UnitPopupInvite.lua (line 193)
Updated check: not GuildInvite → not (C_GuildInfo and C_GuildInfo.Invite) and not GuildInvite in UnitPopupInvite.lua (line 127)
Updated comment: Core/Core.lua line 7 — -- - GuildInvite() → -- - C_GuildInfo.Invite() (compat: GuildInvite deprecated 10.2.6) - scanMaxLevel fallback
File: Recruit/Who.lua line 352
Change: cfg.scanMaxLevel or 80 → cfg.scanMaxLevel or 90 - Midnight zones
File: Data/Maps_Zones.lua line 210–211
Added: "Harandar" (new Midnight zone)
Skipped: Eversong Woods, Ghostlands, Zul'Aman — already present in the list - SendWho origin parameter
File: Recruit/Who.lua line 434
Change: C_FriendList.SendWho(filter) → C_FriendList.SendWho(filter, Enum.SocialWhoOrigin and Enum.SocialWhoOrigin.Social or 1)
Nil-guarded for older clients that don't have Enum.SocialWhoOrigin
- GuildInvite → C_GuildInfo.Invite compat wrapper
- Research
- Bug Fix Report — GRIP Addon
Critical Fixes (4)- CHAT_MSG_WHISPER_INFORM wrong arg position (Core/Events.lua:232)
Target name was extracted from arg2 (always empty string) instead of arg5. This completely broke whisper confirmation tracking — pending whispers never cleared, whisperSuccess never set, downstream invite-after-whisper logic never triggered.
Changed: local msg, target = ... → local msg, _, _, _, target = ... - Ghost Mode parameter order mismatch (Core/Utils.lua:394)
SendChatMessageCompat called gm:Send(msg, chatType, ...) but Ghost:Send() expects (chatType, msg, ...). When Ghost Mode is enabled, CHANNEL sends would have message body and chat type swapped. Dormant since Ghost Mode defaults to off.
Changed: gm:Send(msg, chatType, ...) → gm:Send(chatType, msg, ...) - SeedRaces passes struct instead of numeric raceID (DB/DB_Init.lua:162)
C_CharacterCreation.GetAvailableRaces() returns structs with a .raceID field, not raw IDs. The code passed the whole struct to GetRaceInfo(), which returned nil. Race filter checklist never populated.
Changed: extracts raceData.raceID from the struct, falls back to raw value for forward compat. - EnsureRowMenu returns nil on 2nd+ call (UI/UI_Home.lua:565)
Early return if home._potMenu then return end returned nil instead of the cached menu. ShowRowMenu received nil and silently exited. Right-click context menu (Blacklist/Invite to Guild) only worked once.
Changed: return end → return home._potMenu end
Medium Fixes (6) - Table mutation during pairs() iteration (9 locations across 6 files)
Setting keys to nil inside pairs() is undefined behavior in Lua — may skip entries. Replaced all instances with collect-then-remove pattern.
Files: DB/DB_Blacklist.lua (4 loops), DB/DB_Init.lua (2 loops), DB/DB_Util.lua (1), Recruit/Who.lua (1), Recruit/Invite.lua (2), Recruit/Whisper.lua (1). - UISpecialFrames hash vs array (UI/UI.lua:366)
UISpecialFrames[name] = true used hash syntax; Blizzard iterates with ipairs. ESC-close fallback was dead code.
Changed: tinsert(UISpecialFrames, name) - ADDON_LOADED never unregistered (Core/Events.lua, Hooks/UnitPopupInvite.lua)
After GRIP processed its own load, the handler kept firing for every other addon. Added UnregisterEvent("ADDON_LOADED") after processing in both files. Also unregisters PLAYER_LOGIN in the hook file after it fires. - ApplyTemplate gsub replacement not %-escaped (Core/Utils.lua:259-284)
gsub replacement strings treat % as special. Player names, guild names, and guild finder links were used as raw replacements. WoW hyperlinks can contain %. All replacement values now escape % via :gsub("%%", "%%%%"). - /grip set hidewhispers doesn't sync config alias (Core/Slash.lua:588)
Set suppressWhisperEcho but not hideOutgoingWhispers. Alias pair drifted until next login. Now sets both.
Low Fix (1) - Whisper tick missing UpdateUI + dead code (Recruit/Whisper.lua:226-227, Recruit/Who.lua:241)
When whisper tick consumed a queue entry but found no matching entry or already-attempted, it returned without UpdateUI(). Added self:UpdateUI() to both early-return paths. Also removed dead #pot > 0 check on hash table in Who.lua (always evaluated to false).
- CHAT_MSG_WHISPER_INFORM wrong arg position (Core/Events.lua:232)
- Create .gitignore
- Add GRIP addon files
Initial upload of all GRIP World of Warcraft addon files - Initial commit