Skip to content

v2.0

Choose a tag to compare

@github-actions github-actions released this 23 Jun 09:32
· 2 commits to main since this release

PUG Helper

v2.0 (2026-06-23)

Full Changelog Previous Releases

  • Add CurseForge packaging via BigWigs packager
    • Tag .toc with X-Curse-Project-ID 1585189
    • Add .pkgmeta (package-as PugHelper, ignore dev-only files)
    • Add release workflow that packages and uploads on tag push
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add per-line "drag to action bar" callout macros
    Each callout line gets a drag handle (normal mode) that turns the line
    into a self-contained WoW macro ("/pug send ") and puts it on
    the cursor to drop on an action bar -- a one-click broadcast button.
    The macro substitutes {TOKENS} and resolves the channel LIVE at click
    time (same Chat.SendLine path as the in-window click), and is a snapshot
    that survives /reload, relog, and updates with no saved-vars mapping.
    • Core/Macro.lua: PickupForLine/Fits/Explain; body-hash names (PH+base36,
      <=16 chars) reuse one macro per line. All macro API is api.has-guarded
      and CreateMacro is pcall'd (it errors when the macro list is full).
    • Core/Api.lua: api.InCombat shim; the gesture is refused in combat
      (CreateMacro/PickupMacro are #nocombat and bar drops are protected).
    • Core/Slash.lua: new "/pug send " subcommand.
    • UI/Window.lua: pooled per-row grip (normal mode only, hidden in edit
      mode), reserved label width, tooltip that warns in amber on unset
      {TOKEN}s (mirrors the in-window send guard), updated hint + once-ever tip.
    • Over-long callouts (>~245 chars) are refused, never truncated.
    • Config/Namespace/.toc/.luacheckrc/CLAUDE.md updated.
      Verified macro API signatures + #nocombat behavior against warcraft.wiki.gg.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Set Names: edit roles + drag-to-reorder, scoped per tab
    Add an Edit button (rename any role's label; change the {TOKEN} for custom
    roles only, since built-in tokens are referenced by callouts) and drag-to-
    reorder to the Set Names overlay, mirroring the callout section/line drag.
    Engine (Config/Content):
    • customRoles gains per-tab labels (token -> label override) and order
      (token list); merged into old saves and swept by PruneCustomization.
    • EffectiveRoles applies the per-tab label override + display order and
      exposes baseLabel; new EditRole/MoveRole/SetRoleLabel.
    • A custom token change migrates the saved name and follows the token through
      the order/label/hidden maps (renameTokenInMaps); deleting/reset clean up
      stale label/order entries.
      UI:
    • Role rows are draggable buttons with a per-row Edit button, a drop
      indicator, and a trailing end-zone for the last slot; a modal role-edit
      popup (label for all, token for customs). Row body is drag-only so it
      never fights the dropdown's click area.
    • Editor popup closes on tab switch and window hide.
      Editing/reordering are per-tab; Reset roles drops the tab's customs, hides,
      labels, and order. luacheck clean; CLAUDE.md updated.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Fix 14 issues from a comprehensive adversarial review
    All findings were low/nit (the WoW-API surface came back clean). Grouped:
    Display correctness (FontString '|' escaping): user-authored callouts,
    section titles, the instance note, custom-role labels, and the editor
    confirm/preview/send-confirm surfaces are now escaped via a shared
    util.escapePipes (display-only; what's broadcast to chat is untouched).
    Fork-on-edit: SetLine/SetSectionTitle/MoveSection/MoveLine now compare
    against a non-forking sourceSections() and bail on a no-op BEFORE calling
    materialize(), so re-saving identical text or a drop-in-place drag no
    longer falsely flags a tab "(customized)".
    Custom roles: RemoveCustomRole uses an explicit branch (no a and b or c
    fall-through to the global list); a global AddCustomRole now rejects a
    token already used per-instance (no silent shadowing); the add-role error
    is preserved across background roster-update rebuilds.
    Saved-vars lifecycle: Config.PruneCustomization + Content.PruneCustomization
    reap per-instance data for removed/renamed content (run from Boot), and
    PruneNames no longer lets orphaned roles pin saved names.
    UI lifecycle / perf: the window's OnHide hides the Set Names overlay so a
    reopen lands on the callouts; ToggleNames' hide path refreshes the pane;
    the roster/leader watcher skips work while the window is hidden (channel
    button catches up on reopen).
    Nits: UTF-8-safe util.truncate replaces byte-based :sub truncation in the
    delete-line/live/send previews.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add a UI design-token module for a cohesive, professional look
    Introduce UI/Theme.lua (ns.UI.Theme) as the single source of truth for
    the UI's colours, the cross-file sizes, and font roles, then migrate every
    UI module onto it. This is an internal token layer, not a user-facing
    theming system - nothing is configurable or saved.
    Collapses the value drift the UI had accumulated:
    • 4 near-identical dark panel fills -> one panelBg
    • 3 different border greys (+ the Set Names panel had none) -> one
      panelBorder, applied via a shared UI.PanelChrome helper
    • multiple blues/greens/oranges -> one accent / ok / unset token each
    • the unset-{TOKEN} cue was literally two different oranges (vertex vs
      inline text); both now derive from one token via Theme.hex, so they
      can't drift again
      Structure/polish:
    • UI.TitleBar gives the editor popup and Set Names overlay the same
      title-bar strip as the main window
    • a vertical divider between the tab list and the message pane
    • ROW_INSET 22 -> 18 so wrapped callout text fills the row (no dead strip)
    • distinct flash token keeps the send-confirmation pulse vivid
      Adds wrap-safe Theme.addLine so a coloured+wrapped GameTooltip line can't
      truncate the colour's returns against the trailing wrap flag. All texture-
      based, no :SetBackdrop, no new API; luacheck clean.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Third UX pass: unset-send guard, terminology, onboarding, feedback
    Driven by a multi-agent UX review (9 lenses, findings adversarially verified
    against the source and the project's constraints).
    Send-loop safety:
    • Gate sending a line that still has an unset {TOKEN} behind a "Send anyway"
      confirm showing the exact text + channel, so a click mid-pull can't broadcast
      a literal "{MT} tanks ..." to the raid. Resolved lines still send on one click.
    • Double-send guard: swallow a repeat click within ~0.4s (guarded C_Timer shim;
      never latches when no timer exists, so sends always work).
    • Flash "sent" only when a line actually went out (empty/whitespace sends none).
      Terminology & microcopy:
    • Set Names panel: 5-man Heroics are "tab", not "raid"; fix "Reset raid"
      references (the control is "Reset roles"); reset confirm reads "Reset callouts".
    • Slash: /pug show only shows; /pug edit forces edit ON (idempotent); /pug
      channel reports the resolved channel; /pug names states its scope; unknown
      commands warn; /pug reset clarified as window-position only.
    • Channel codes upper-cased in the tooltip; line-drag tooltip scoped
      "within section" (cross-section drops are a no-op).
      Onboarding & clarity:
    • Content hint adapts when a tab has no callouts yet (the framework model).
    • Edit-button tooltip sets the "tabs ship blank, author here" expectation.
    • Load message names the minimap button alongside /pug.
      Other:
    • "Reset callouts" reachable whenever a tab is customized, not only in edit mode,
      so the always-on "(customized)" badge has a remedy.
    • Stale-assignment cue backed with text, not color-only (accessibility).
    • Add-role Tab key cycles Name<->Token; brighter edit-mode tint; clear the
      left-list search filter on window reopen.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • UX pass: modal signalling, channel safety, feedback, and clarity
    From the third (UX-focused) adversarial review. Core one-click send keeps no
    added friction; shipped content stays a framework (empty-state messaging only).
    Modality (the headline gap):
    • Modal blockers now DIM the window behind them, so the lock is visible instead
      of clicks silently dying on a transparent scrim.
    • Confirm dialog is named + in UISpecialFrames, so Escape cancels the confirm
      instead of closing the whole window.
    • Edit mode tints the message pane amber, so "click edits, not sends" is obvious.
      Channel safety:
    • The channel button turns red when the live target is SAY/GUILD (public), and
      shows "(now: X)" instead of an unexplained ">".
    • Right-click the channel button to step back (one-click overshoot recovery).
    • RAID_WARNING downgrades to RAID when you're in a raid but not lead/assist
      (it was silently delivering nothing); new guarded api.CanRaidWarn() shim.
    • /pug channel (no arg) now reports the current channel + where it lands.
      Feedback / clarity:
    • Unfilled {TOKEN}s are tinted orange in the line text (not just a color-only
      bullet); a click flashes the row's bullet green to confirm a send.
    • Set Names: solo/pre-invite help text, "(not set)" placeholders, a reddish
      flag for a name no longer in the group, scope-aware delete-X tooltips, inline
      add-role error (was printed behind the overlay), a close-X, and a confirm on
      "Clear names" (was unconfirmed). Picking a name updates its cue immediately.
    • Editor box is single-line with Enter-to-save (it was multiline; newlines were
      stripped anyway). First-time Edit shows a one-off gesture tip in chat.
    • Clearer labels: "Reset tab"->"Reset callouts", "Reset raid"->"Reset roles",
      "Clear All"->"Clear names", "Scope:" prefix on the toggle; richer /pug help.
      luacheck: 0 warnings / 0 errors.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Fix 8 issues from the second adversarial review
    Modal blocker (UI.MakeModal) hardening:
    • Scope the blocker to the addon window (UI.frame) instead of the whole
      screen, so editing a callout no longer locks out action bars / the game UI.
    • Raise the blocker with its dialog, so a third-party DIALOG-strata frame
      can't sit above it and leak clicks through.
    • Eat the mouse wheel too (EnableMouseWheel + absorbing handler); EnableMouse
      alone let wheel events scroll panes behind the modal.
      Other:
    • Window OnHide now also closes the editor popup, clearing its shown flag so a
      reopen doesn't briefly re-show it (and dropping its modal blocker).
    • Config.CycleChannel routes its write through SetChannel, so the PugHelperDB
      nil-guard + validation live in one place like every sibling accessor.
    • Cap the edit-popup live preview at 160 chars so a long callout can't grow it
      down over the Save/Cancel buttons (the edit box keeps the full text).
    • Right-click on the "+ Add line"/"+ Add section" rows is now a no-op
      (left-click only), restructured so it can't fall through to a nil-index
      delete that table.remove would turn into "remove the section's last line".
    • Escape literal '|' in instance notes to '||' at display, or WoW's FontString
      parser eats the separator (e.g. "25-player | Phase 1").
    • Soften the Refresh drag-clear comment: the clear is a defensive guard, not a
      response to a resize-mid-drag refresh (the window isn't resizable).
      luacheck: 0 warnings / 0 errors.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Fix 10 issues found by adversarial review
    Engine / WoW API:
    • Minimap drag: math.atan2 is nil on the post-8.0 Classic-era client; use
      math.atan2 or math.atan so the drag math never calls a nil global.
    • Name tokens are now case-insensitive (Config.GetName/SetName uppercase),
      so a callout written with lowercase {mt} fills from a name set under MT
      instead of being sent to chat literally.
    • EffectiveRoles: scope the per-tab hidden suppression to built-ins, so a
      user can re-add their own role under a token whose built-in they hid
      (previously it passed the AddCustomRole duplicate check yet went invisible).
    • GUILD channel now downgrades when guildless (Config requires="guild",
      api.InGuild shim, Chat.ResolveChannel guard) instead of erroring per line.
    • Config.Channel self-heals a stale/invalid saved channel to AUTO.
    • util.wrap: hard-cut backs off UTF-8 continuation bytes (no split glyphs)
      and a leading-whitespace run can't emit an empty chunk.
      UI:
    • Edit popup and confirm dialog are now modal (UI.MakeModal): while editing,
      the rest of the window is inert, so an unrelated gesture can no longer
      trigger a refresh that silently discards an in-progress edit.
    • UI.Refresh takes preserveEditor; resize / name-set keep an open edit.
    • Confirm dialog is hidden on window OnHide so Escape can't strand it.
    • UI.Refresh clears any in-flight drag state + drop indicator, so a mid-drag
      refresh can't leave the pane stuck in phantom-drag mode.
      luacheck: 0 warnings / 0 errors (IsInGuild allowlisted).
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add minimap launcher, line drag/duplicate, search, unset-token cues
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Simplify /pug name parser: one regex pass, no redundant recheck
    Collapse the two-pass match (TOKEN+value, then bare TOKEN fallback) plus
    the duplicate if not token check into a single pattern that handles both
    forms. Bare name TOKEN still yields value "" (clears the name), so
    behavior is unchanged.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Simplify: own single-line invariant in Content; dedup PruneNames
    • Add util.oneLine (collapse embedded newlines/tabs + trim) and use it in
      the Content text mutators (SetLine/AddLine/SetSectionTitle/AddSection) so
      a saved callout is always one chat line regardless of caller. Drop the
      now-redundant local sanitize() from EditPanel (4 call sites pass raw text).
    • Collapse PruneNames' three identical role-token loops into one markLive().
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Simplify: O(N) row pool, shared instance-name helper, confirm Reset tab
    • Window: replace the O(N^2) per-render pool scan (linear search for a free
      frame on every row) with a forward cursor reset in ReleasePool.
    • Content.InstanceName: one home for the id -> display-name + fallback lookup
      the names panel re-rolled; reused by Reset tab.
    • Reset tab now routes through the shared UI.Confirm like every other
      destructive action instead of dropping a tab's edits unconfirmed.
    • Config.CustomRoles no-DB branch returns hidden too, matching the shape
      the accessor guarantees on its main path.
    • Inline the redundant EnableWheel alias; drop dead value nil-guards in /pug name.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Set Names: label-before-dropdown rows, confirm role deletes
    Put the "{TOKEN} Name" label first (right of the delete X) with the
    name dropdown in a reserved right column sized off the scroll width,
    so the token bracket no longer overlaps the dropdown and long names
    still wrap without truncating.
    Route the row X through UI.Confirm with a scope-aware prompt: custom
    roles confirm "Delete", built-ins confirm "Hide" (restorable via
    Reset raid), matching the confirm-on-destroy rule used elsewhere.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add user-defined roles (global + per-raid) with full customization
    Set Names can now create custom {TOKEN} roles, scoped Global (every
    tab) or This raid (current tab only) so the panel stays uncluttered.
    Names remain keyed by bare token (picked from the live roster), so
    substitution and the send path are unchanged.
    • Config: customRoles store { global, byInstance, hidden } + accessor.
    • Content: EffectiveRoles (built-ins + global + per-raid, minus hidden),
      AddCustomRole/RemoveCustomRole (token sanitized to {%w+}, collision-
      checked), HideRole, ResetRoles; PruneNames keeps custom tokens live.
    • NamesPanel: single-column, full-width word-wrapping rows so names are
      never truncated; every role deletable via a left-side X (custom ->
      removed, built-in -> hidden per raid); Reset raid / Reset global
      buttons; add-role inputs (name + token) with a scope toggle.
    • Helpers: shared UI.Confirm dialog and UI.EnableWheel.
    • EditPanel/Window: right-click delete of a callout line or section now
      asks for confirmation first.
    • Slash: /pug names lists effective (custom + raid) roles.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Simplify channel resolution and boot selection fallback
    Extract bestAvailable() in Chat.ResolveChannel for the AUTO and
    downgrade paths, and use Content.ResolveSelectedInstance() in Boot
    instead of re-deriving the keep-or-fallback policy inline.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Simplify: shared helpers, single-copy Effective, Config-owned name pruning
    • Add Config.PruneNames so Content.PruneNames no longer touches PugHelperDB
      directly (restores the "only Config owns saved vars" invariant).
    • Add UI.Background helper; replace three hand-rolled background-fill blocks.
    • Content.Effective: copy sections once instead of deep-copying defaults then
      discarding them on the customized path; fold the "is customized?" predicate
      into a shared customSections() used by Effective/materialize/HasCustom.
    • Drop redundant nm alias in NamesPanel; share drag-cancel via CancelDropTarget.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Fix edit-popup target staleness, drag cancel, and duplicate ids
    Adversarial review found three real interaction bugs:
    • Critical: the shared edit popup stayed open across tab switches and "Reset
      tab", so clicking Save then wrote to the wrong (or just-reset) instance.
      UI.Refresh now dismisses the popup (UI.CloseEditPopup), so it can never
      outlive the content it was opened against; the legitimate Save still applies
      first since the text is read before the refresh.
    • Major: a section drag released over dead space still applied the last hovered
      drop target. Row/header OnLeave now clears the drag target during a drag, so
      releasing off any target cancels the reorder.
    • Major: two instances whose names slug to the same id created a dead duplicate
      tab with unreachable content. Content.AddInstance now skips the duplicate with
      a data warning.
      luacheck clean.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Fix stale customization-model comments (Content.lua, Config.lua)
    A correctness review of the recent changes found no runtime bugs but flagged
    two comments still describing the old title-keyed override model
    (overrides/added/hidden). Update them to the implemented fork-on-edit model
    (PugHelperDB.custom[instanceId].sections). Comments only; no code change.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Simplify repeated patterns: UI builder helpers + string sections
    UI/Helpers.lua adds UI.Button, UI.Tooltip, and UI.AddBorder, replacing the
    duplicated Edge() border (defined in both Window.lua and EditPanel.lua), the
    repeated GameTooltip OnEnter/OnLeave wiring on static buttons, and the
    CreateFrame/SetSize/SetText/SetScript button boilerplate across the three UI
    files. Dynamic row/header tooltips (conditional on edit/drag mode) are left
    inline.
    Content.AddInstance now normalizes section entries, so a section can be authored
    as a bare title string ("Boss Name") instead of { title = "...", lines = {} }.
    All 20 content files use the string-list form, shrinking each and trimming the
    near-identical header comments.
    No behavior change. luacheck clean (33 files).
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add drag-and-drop section reordering in edit mode
    Section title headers are now draggable (RegisterForDrag) to reorder sections.
    While dragging, hovering a header/row marks the drop slot and an insertion line
    shows where the section will land; releasing calls Content.MoveSection and
    refreshes. A no-movement click still fires OnClick, so rename/delete coexist
    with drag. Dropping on the "+ Add section" row moves a section to the end.
    Adds Content.MoveSection (insert-before semantics, no-op when order unchanged)
    and the drag/indicator wiring in UI/Window.lua.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Make section titles editable; add/rename/delete sections in-game
    Move the customization layer to a fork-on-edit model: the first edit to an
    instance copies its whole section list into PugHelperDB.custom.sections, which
    then becomes authoritative (Content.Effective never mutates defaults; Reset tab
    drops the copy). Sections and lines are addressed by display index, recomputed
    each render, so section titles may repeat (e.g. multiple "Trash" sections).
    Editor (Edit mode):
    • Section headers are clickable: left-click renames, right-click deletes.
    • "+ Add section" row at the bottom; "+ Add line" per section unchanged.
    • Refresh now preserves scroll position across edits (resets to top only on
      tab switch).
      Updates Content.lua mutators (SetLine/AddLine/DeleteLine by index +
      SetSectionTitle/AddSection/DeleteSection), Window/EditPanel UI, and CLAUDE.md.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Refactor into modular architecture; add Phase 2 raid/heroic tabs
    Split the monolithic Core.lua/Data.lua into focused modules sharing one
    private namespace (ns): Core/ (Namespace, Util, Api, Config, Content, Chat,
    Slash, Boot) and UI/ (Window, NamesPanel, EditPanel). Content moves to a
    one-file-per-instance registry under Content/.
    • In-game callout editor with per-instance overrides persisted in
      PugHelperDB.custom, layered over registered defaults (Content.Effective);
      edit/add/delete lines + Reset tab.
    • Scrollable left tab list (UIPanelScrollFrameTemplate + mousewheel) so all
      tabs fit.
    • Tabs for Phase 2 raids (Karazhan, Gruul, Magtheridon, SSC, The Eye) and all
      15 launch heroics, plus Anzu and Yor; section titles only, empty lines.
    • Update .toc load order, .luacheckrc globals, and CLAUDE.md file roles.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Remove luacheck CI workflow
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Show full callout text in the message list, wrapped (no truncation)
    The list previously truncated each line to ~78 chars with an ellipsis, so the
    full callout was only reachable by hovering. Now each row label wraps to the
    pane width and the row grows to fit (height from GetStringHeight), so the whole
    substituted line is visible inline.
    • Remove the Truncate helper and LABEL_CHARS; add ROW_INSET/ROW_VPAD for the
      wrapped-row metrics. Bullet is top-aligned; label is top-aligned and wraps.
    • Trim the hover tooltip back to the "click to send to " hint now that
      the full text shows inline; drop the now-unused PREVIEW_CHARS.
    • Update the hint line accordingly.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Show full callout text in hover preview, never truncated
    The preview added the whole line as one auto-wrapped GameTooltip line, which
    sized the tooltip very wide; anchored to the right of a right-side row, long
    callouts ran off the screen edge and looked cut off. Now the preview wraps the
    text itself to a fixed width (PREVIEW_CHARS) and emits one tooltip line per
    chunk, so the tooltip grows downward and the full line is always visible.
    Factor the word-splitting into a shared WrapText() helper; SendLine reuses it at
    the 240-char chat limit instead of duplicating the loop.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Polish: centralize channel list, prune stale names, name layout constants
    • Channels are now defined once as a {name, requires} table; CHANNEL_NAMES and
      CHANNEL_REQUIRES are derived from it. ResolveChannel reads requires instead
      of hardcoding RAID/PARTY, the cycle/validation paths iterate the derived list,
      and the slash help text is built with table.concat -- the channel list no
      longer appears in four places.
    • PruneNames() drops saved names whose token is neither a current role key nor
      referenced by any callout line, so PugHelperDB.names stops accumulating
      leftovers when roles/lines change. Tokens still in use (including custom slash
      tokens) are kept; matching is case-insensitive.
    • Promote the window's defining numbers to named constants (FRAME_W/FRAME_H,
      BUTTON_H, TITLE_H) alongside the existing LEFT_W/CONTENT_X, so the frame's
      structural dimensions aren't inline literals while their siblings are.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Keep channel button in sync; drop dead Edge params
    • The toolbar channel button now updates after /pug channel X, not just when
      cycled by clicking. channelBtn is lifted to a module local and both input
      paths route through a shared UpdateChannelButton(), so the label can't go
      stale relative to PugHelperDB.channel.
    • Edge() no longer takes p1/p2/w/h: every caller invoked it with no args and
      anchored/sized the texture itself, so the parameters were dead and the
      signature misleading.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Tolerate malformed Data.lua instead of throwing
    Editing Data.lua is the intended workflow for non-coders, but the render path
    trusted it completely: a raid missing sections, a section missing lines, or
    a role missing key threw a cryptic Lua stack trace. Now:
    • asList() guards every ipairs over user content (raids, sections, lines, roles)
      so missing/garbage values degrade to empty instead of erroring.
    • Non-string lines and roles without a key are skipped; unnamed raids/sections
      render with placeholders rather than crashing on concatenation.
    • ValidateData() runs at load and prints friendly, actionable warnings
      ("raid 'Karazhan', section #3 has no lines") pointing at the offending entry.
    • The loader no longer indexes PugHelperRaids before confirming it's a table.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add CI workflow to run luacheck on push/PR
    Local environments can't always run the linter, so enforce it in CI instead:
    this is the durable fix for "we can't verify luacheck is green" and guards
    against .luacheckrc drifting from the code again. Uses Lua 5.1 to match WoW's
    Lua flavor and the std = "lua51" setting in .luacheckrc.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Harden channel/token handling; tidy constants and docs
    • ResolveChannel now downgrades a manual RAID/RAID_WARNING/PARTY override to a
      channel valid for the current group (Party or Say), so clicking a line while
      solo can no longer trigger "You are not in a raid/party group".
    • /pug name rejects non-alphanumeric tokens up front, since substitution only
      matches {%w+} — previously such a name was stored but could never fill in.
      Documented the same constraint next to PugHelperRoles in Data.lua.
    • Promote the 240-byte chat split to a named CHAT_LIMIT constant.
    • Use table.insert consistently (was a lone tinsert); anonymize the names panel
      frame (nothing referenced it by global name); comment the fixed 2-column
      names layout; set the .toc author.
      Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Align luacheck config with actual API usage
    read_globals had drifted from the code: it was missing globals the addon
    actually calls (UnitName, GetRaidRosterInfo, GetNumSubgroupMembers, the
    UIDropDownMenu_* family) — which luacheck flags as undefined-global access —
    while listing several never used (GetTime, InCombatLockdown, PlaySound,
    C_Timer, str*/wipe/format, tinsert/tremove). Rebuild the list to match what
    Core.lua references so luacheck . is actually clean, and add a note to keep
    it in sync.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Ignore Claude Code memory/ directory
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Harden CLAUDE.md WoW API guidance
    Expand the single API bullet into a dedicated "WoW API rules" section:
    verify-don't-assume, the modern-engine-reporting-2.0.5 reality, the
    mandatory type-guard compat-shim pattern (grounded in the existing
    RaidCount/InRaid helpers), and a list of specific landmines (C_* namespaces,
    backdrop system, CreateFrame templates, UIDropDownMenu, SendChatMessage
    channels, roster signatures, events).
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Add party/raid roster dropdowns to Set Names menu
    Replace the manual name EditBoxes with UIDropDownMenus populated live
    from the current group. Use modern group API (GetNumGroupMembers /
    GetNumSubgroupMembers) with the old API as fallback, since the
    Anniversary client runs on the modern engine where the TBC-era count
    functions are nil. Also raise the names panel frame level so its
    background covers the scroll rows.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Initial commit: PUG Helper addon for TBC Anniversary
    Raid callout/assignment helper with token substitution.
    Includes CLAUDE.md guidance and luacheck config.
    Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
  • Initial commit