promotional bannermobile promotional banner

Epic Damage Meter

A lightweight, non-bloated World of Warcraft damage meter designed for accurate real-time tracking and minimal to non existent performance impact.

File Details

EpicDamageMeter v.2.4.15

  • R
  • May 23, 2026
  • 431.84 KB
  • 560
  • 12.0.7+14
  • Classic TBC + 4

File Name

EpicDamageMeter.zip

Supported Versions

  • 12.0.7
  • 12.0.5
  • 12.0.1
  • 5.5.4
  • 5.5.3
  • 4.4.2
  • 4.4.1
  • 3.80.1
  • 3.80.0
  • 3.4.5
  • 3.4.4
  • 2.5.5
  • 2.5.4
  • 1.15.8
  • 1.15.7

EpicDamageMeter - Changelog
============================

Version 2.4.15
--------------
Light-Mode Fix + ESC-Menu Button + Arena Theme

LIGHT MODE
- Fixed: section headers were unreadable in light mode — the dark
  accent-tinted gradient overlay I added in 2.4.13 dominated the
  card colour and inverted the page (light bg + dark headers with
  dark text). Now adapts per theme:
    light → very subtle accent-tint that fades to zero
    dark  → keeps the moody accent depth gradient
- Text outline + shadow disabled in light mode (was creating a
  black halo around dark text on light bg).

ESC MENU BUTTON
- The in-game ESC menu now has an "Epic DM" entry, the same way
  SUI and Arena Core surface their settings. Tries the modern
  Menu.ModifyMenu API first (Midnight 12.0 dropdown menu) and
  falls back to a GameMenuButtonTemplate button on older clients.
  Clicking it opens the EDM settings panel and dismisses the
  game menu.

ARENA THEME (NEW)
- New "arena" theme variant, inspired by Arena Core's deep purple
  aesthetic. Cycle with the sun/moon button: dark → light → arena
  → dark. While in arena mode the icon tints purple to signal the
  third state.
- Palette: near-black panels, saturated purple accent, yellow
  hover on nav items (matches Arena Core's yellow glow on active
  category).
- Section headers in arena mode get a thicker 2px purple border,
  heavier wash gradient, and BOLD UPPERCASE titles in accent
  purple instead of the standard text colour — the "section card"
  feel from the reference.

Version 2.4.14
--------------
Tracker / Details Polish + Display Bug Fixes

TRACKER
- Title bar no longer duplicates the mode name. Previously the
  title showed "Epic DM  Verursachter Schaden" while the toolbar
  button directly below also said "Verursachter Schaden" — visual
  redundancy that pushed the brand off-screen on narrow windows.
  Title is now just the brand (+ enemy indicator when active);
  mode lives exclusively in the toolbar button.

DETAIL WINDOW
- Fixed: header pills still showed "0 DPS / 0 HPS" while spell rows
  rendered correct per-second values. Root cause: the previous
  fallback divided by the DB segment duration which is clamped to
  1s after a segment is reset — producing absurd values OR being
  short-circuited by a SecretValue rate that resolved to a tiny
  non-zero number. Now reads `session.durationSeconds` from
  C_DamageMeter (the duration the meter actually measured) as the
  primary divisor, with a session-level fallback when the per-source
  data doesn't carry it, and treats any rate <= 0 as "missing" so
  the fallback always kicks in for the bad cases.
- Fixed: spell rows below 1000 DPS displayed raw IEEE-754 floats
  ("777.76580810547 DPS"). The Abbr helper short-circuited around
  Utils.ResolveNumber for SecretValues and routed them through
  Blizzard's AbbreviateNumbers, which doesn't round low values.
  Both Abbr helpers in DetailWindow now go through ResolveNumber
  first, then Utils.FormatNumber — guaranteed clean integers.
- Spell-row depth: the gloss / shadow overlays added in 2.4.10
  weren't actually visible because they sat on the bar parent
  while the StatusBar child rendered above them. Moved both to
  bar.statusBar at OVERLAY layer so they paint on top of the
  colored fill. Added a 2px right-edge highlight texture (hidden
  by default, ready for show* code to position).

Version 2.4.13
--------------
Pill Look Goes System-Wide

TOOLBAR SPACING
- Fixed: in the meter toolbar, the mode label ("Verursachter Schaden"),
  the Team/Enemy toggle ("Team"), and the segment label ("Aktuell")
  visually fused into each other on long localised strings. Root
  cause: mode button was a fixed 90px wide and the German label
  overflows beyond that width, bleeding into the next button.
- Mode button now auto-fits its label width (clamped 60-220px) via
  a new `Instance:ResizeModeBtn()` method that's called on init
  and on every SetMode. Gaps between mode/enemy/divider/segment
  bumped from 3-4px to 7-8px so labels stay visually separate.

PILL DESIGN — REUSABLE WIDGETS
- Extracted the Detail Window header pill into
  EDM.Widgets:CreateStatPill(parent, opts) so the same dark-card +
  top thin / bottom thick / left vertical accent stripes + accent-
  tinted depth gradient style can be reused across the addon.
  Returns a frame with :SetValue(value, sub, isZero) for live updates.
- Added EDM.Widgets:CreateSectionBar(parent, opts) — a wider
  horizontal sibling for section headers (single-line title + meta).
  Same accent palette, same lit-from-top feel.

SETTINGS PANEL
- Settings section headers (Config:CreateSection) adopt the pill
  look: dark backdrop, horizontal accent-tinted depth gradient,
  top + bottom + left-vertical accent stripes in the theme's
  ACCENT colour. Section title outlined for better legibility on
  the new gradient bg, chevron is now accent-coloured.
- Hover state intensifies all three accent stripes plus the border
  rather than just dimming the bg — clear visual feedback that the
  section is interactive/collapsible.

Version 2.4.12
--------------
Detail Window Pass 2 — Pills, Targets, DPS

PILLS
- Split the confusing "INT / DSP" combo pill into two separate pills:
  "INTERRUPTS" and "DISPELS". User feedback was that "INT / DSP"
  read like a DPS abbreviation. Each pill now has its own label,
  accent colour, and dim-when-zero state.
- Added a second (thicker) bottom accent stripe and a subtle
  accent-tinted depth gradient on each pill. Active pills feel
  more "lit" while zero-valued pills clearly recede.
- Default window grew from 460x560 to 520x580 so the new 6-pill
  row doesn't truncate the "INTERRUPTS" label.

HEADER DPS / HPS FALLBACK
- Fixed: header pills showed "0 DPS" and "0 HPS" while the spell
  rows showed correct DPS values (e.g. "Göttlicher Hammer 7.1K
  DPS"). C_DamageMeter's amountPerSecond is sometimes nil / 0 /
  SecretValue even when totalAmount is populated — happens on
  Overall sessions, post-combat snapshots and expired sessions.
- Header now computes rate = total / duration as a fallback when
  the session-provided rate is missing, so DPS/HPS stays in sync
  with the spell rows.

TARGETS TAB
- Fixed: "Ziele" tab was empty on Retail because we removed CLEU
  registration in 2.4.8 (12.0 moved combat events to EventRegistry
  and forbade CLEU for addons). actor.targets had no data.
- Now inverts C_DamageMeter's EnemyDamageTaken view: each enemy
  session has combatSources listing the players who damaged it.
  Walking those and matching the actor's GUID gives "what targets
  did THIS player damage, and for how much". SecretValue-safe
  matching via pcall'd equality.
- Target rows now show "Damage  DPS · X.X%" in the same compact
  layout the spell rows use, plus the gold/silver/bronze rank
  badge on the top 3 targets.

Version 2.4.11
--------------
Bug Fixes + Debug Console + Detail Window Polish

BAR TOOLTIP — STALE SPELL CACHE
- Fixed: bar tooltip on hover showed spells from the wrong player.
  Repro: hovering Toilettegodx (Monk) in the Healing window showed
  Warlock spells like Drain Soul / Dark Pact / Healthstone — those
  were Méuw's (a Warlock who occupied that bar slot earlier).
- Root cause: bars are pooled and recycled across actors / modes /
  segments. `bar._cachedSpells` (populated by UpdateBars for the
  tooltip) had no invalidation key, so a bar that previously showed
  Player A and was later reassigned to Player B kept serving A's
  spells. The bug also surfaced when switching segments or modes on
  the same window.
- Fix: every cache write now tags the bar with a composite key
  (`actor.guid | mode | segmentIndex`). Both the cache-write paths
  (UpdateBars and GetTooltipSpells lazy fetch) set the key, and
  every cache read checks it. Mismatch → drop and re-fetch.
- Also fixed a second SecretValue arithmetic site at MainFrame.lua:
  1938 (cache build during UpdateBars) — same `tonumber(secret)`
  taint leak we fixed at line 2918 last release.


CRASH / TAINT
- Fixed: 14x "attempt to perform arithmetic on local 'amt' (a secret
  number value)" at MainFrame.lua:2916 (GetTooltipSpells). The bar
  tooltip's spell loop called `tonumber(sp.totalAmount)` which on a
  SecretValue returns the still-secret value, then `spellTotal + amt`
  blew up. Now uses Utils.ResolveNumber which handles SecretValues
  via the byte-rebuild slow path.
- Fixed: Utils.ResolveNumber fast-path silently passed SecretValues
  through. `tonumber(secret) > 0` doesn't throw and the comparison
  reports ok, so the wrapper assumed the value was clean and returned
  it. Subsequent arithmetic / format calls then leaked taint or
  produced raw float strings. Added an explicit issecretvalue() check
  before the fast path so SecretValues always take the byte-rebuild
  route.
- Fixed: 6x "attempt to call a nil value" at DebugConsole.lua:671
  when running `/mem`. Midnight 12.0 moved GetNumAddOns, GetAddOnInfo,
  IsAddOnLoaded, GetAddOnMemoryUsage, UpdateAddOnMemoryUsage under
  C_AddOns. The command now resolves through C_AddOns first and
  falls back to the legacy globals where they still exist.

DETAIL WINDOW
- Fixed: spell row DPS showed raw floats like "687.46350097656 DPS"
  for values below 1000. Root cause was the same ResolveNumber leak
  above — perSecond is a SecretValue on Retail and slipped past
  the rounding. The Utils.ResolveNumber fix resolves this generically,
  and the row layout was rewritten:
    * Value (top-right, big) : amount only, e.g. "41K"
    * Detail (bottom-right)  : "687 DPS · 26.1%" (rate + share)
    * Subtext (bottom-left)  : "5 hits · 2 crits (40%)"
  Eye lands on the big number, then drops to the rate+share line.
- Header pills: zero-valued pills now dim themselves (value muted,
  border faded, accent strip dimmed) so the eye skips empty stats.
- Removed redundant "I / D" sub-label under Int/Dsp pill — the
  pill's main label already says "INT / DSP".

DEBUG CONSOLE
- Fixed: errors captured by the in-addon console showed up as
  "Runtime: table: 0x..." with no actual message. BugGrabber-format
  error objects use the field name `.msg` (not `.message` which the
  old extractor expected). The extractor now probes multiple field
  names — msg, message, error, errorMessage, text, what, desc,
  reason — and falls back to serialising primitive table fields so
  you always see something concrete instead of an opaque table id.
- Stack trace shown bumped from 3 to 6 lines (3 was too short to
  see where the error actually came from).
- NEW console command: `/edm console` -> `last` (also `error last`)
  dumps the full last captured error: message, full stack trace,
  and the first 12 lines of locals. No more BugSack required for
  basic debugging — though BugSack still works on top if installed.
--------------
Detail Window Redesign
- Reworked the Player Detail window UI top-to-bottom while keeping all
  existing functionality, signatures and data flow intact.

  HEADER (was: cramped text rows separated by pipes)
    * Default window grew from 420×520 to 460×560 for breathing room.
    * Larger class icon (54px, was 48), framed with class-tinted border.
    * Big soft class-color halo glow behind the icon.
    * Class-color top accent band runs across the entire header.
    * Player name now in 16px outlined class color (was 13).
    * Class subtitle line under the name (small, muted).
    * "Dmg / DPS | Heal / HPS | Deaths | Interrupts | Dispels | Absorbs"
      one-line text block REPLACED by a row of five stat PILLS:
        DAMAGE   - big total + small "X DPS" sub
        HEALING  - big total + small "X HPS" sub
        ABSORBS  - big total
        DEATHS   - big number (greys out when zero)
        INT/DSP  - combined interrupts/dispels as "12 / 3"
      Each pill has its own colored top accent (red/green/gold/purple/blue).
    * Pills flex-resize on window resize.

  TABS (was: text-only with thin underline)
    * Now a segmented control: 30px tall, gradient bg, top accent strip,
      bottom indicator. Each tab carries its own type color (Damage red,
      Healing green, Targets orange, Damage Taken crimson).
    * Hover state subtly tints the bg toward the tab's accent.
    * Active tab gets a stronger tinted gradient + full-color top accent
      bar + bottom indicator line.

  ABILITY BARS (was: flat red statusbar, tiny icon)
    * Layered rendering: dark base + vertical depth gradient + status bar
      (skin-aware texture) + top gloss highlight + bottom shadow.
    * Slightly larger icon with 1px dark border (separates from bg).
    * Spell name jumped from 10px to 11px outlined; value text bolder.
    * Hits/crits sub-line now in its own field (was crammed in tiny text).
    * Top 3 entries get a gold/silver/bronze rank badge on the icon.
    * Subtle row divider for cleaner list rhythm.

  All show* functions (ShowDamageAbilities, ShowHealingAbilities,
  ShowTargets, ShowDamageTaken, ShowSpellList, ShowSpellTooltip,
  ShowSpellDetail) and the live C_DamageMeter integration are unchanged.
  Backward-compat shims keep self.header.stats1/stats2 callable as
  no-ops in case any code path still touches them.

Version 2.4.9
-------------
Bug Fix
- Fixed: hovering a player bar during combat on Retail showed 0/0/0
  in the tooltip; the right values only appeared after combat ended.
  Root cause: UpdateBarsFromDamageMeter writes a zero-initialised
  actor stub into bar.actorData (the bars get their width directly
  from C_DamageMeter session values, the stub is for spell-cache
  lazy loading). The tooltip read those zeros instead of asking
  C_DamageMeter itself. After combat, OnCombatEnd imports session
  totals back into the database actor, which is why post-combat
  worked.
- Tooltip:ShowActorTooltip now queries C_DamageMeter live for the
  hovered source's damage / healing / absorbs / damage-taken /
  interrupts / dispels across multiple session enums, picking the
  freshest available value and falling back to the database actor
  only when live data is missing. Duration is taken from the
  session itself so it matches what the bars are rendering.
- SecretValue-safe throughout: GUID/name comparisons go through a
  pcalled equality wrapper, formatting routes secret numbers through
  AbbreviateNumbers, division skipped entirely for secret damage.

Theme Pack + Animation Framework
- NEW: 8 new skins, expanding the catalogue from 23 -> 31. Each
  one targets a stylistic gap the existing themes don't cover:
    * Terminal — retro CRT phosphor green on near-black, monospace
      font, scanline overlay, sharp edges. For DOS nostalgia.
    * Sakura — soft rose pastel on dark wine background, gentle
      gradients, cherry-blossom palette. Daytime / lighter mood.
    * Bloodforge — Death Knight runic aesthetic, crimson on iron,
      gothic Morpheus serif, runic accent borders.
    * Vaporwave — Y2K sunset: magenta/cyan/violet on twilight grid.
      Dreamy gradients, neon-with-soft-edges (NOT Cyberpunk's harsh
      industrial; this is Lisa Frank / Macintosh Plus).
    * Newsprint — anti-color skin. Cream paper, black ink, serif.
      Class colors intentionally muted so typography does the
      talking. For anyone tired of the class-color rainbow.
    * Tactical — military HUD: amber on dark olive, monospace,
      crosshair-style accents. Reads like a cockpit display.
    * Brutalist — architecture-inspired heavy minimalism. Raw
      concrete grays, oversized typography, 3px black borders,
      yellow alert accents. Nothing soft, nothing decorated.
    * Glitch — broken-LCD aesthetic with chromatic aberration.
      Red shadow on cyan text, RGB-tinted edges. Pixel statusbar
      texture. For when you want the UI to feel slightly wrong.
- NEW: Procedural statusbar palettes (Skins:CreateProceduralBar) —
  Lua-generated gradients registered as LSM statusbars, no .tga
  artwork needed. Ships with Sunset, Ocean, Toxic, Ember, Aurora
  ready-made palettes for quick custom-theming.
- NEW: UI/Animations.lua module with reusable effects:
    * A:Pulse(frame, opts)         — breathing opacity loop
    * A:Glow(frame, opts)          — one-shot glow burst with scale
    * A:CountUp(fs, from, to, opts) — number-tween FontString with
                                      ease-out / linear / ease-in
    * A:Shimmer(frame, opts)       — light-band slides across (good
                                      for #1 rank highlight)
    * A:RecordFlash(frame, opts)   — celebratory flash for new bests
    * A:Shake(frame, opts)         — damped oscillation for emphasis
    * A:CancelAll(frame)           — cleanup all anims on a frame
    * A.FormatBig(v)               — formats 1234567 -> "1.23M"
  All self-cleaning: starting a new anim on the same frame cancels
  the previous one to avoid stacking.
- NEW: Animations wired into Bars.lua. Each bar now reacts to its
  data live:
    * CountUp on the value FontString when DPS changes by ≥100
      (deltas below that snap to avoid micro-tween churn)
    * Glow burst in class color when the bar climbs a rank
    * Continuous Shimmer band sliding across the #1 bar
    * Gold RecordFlash the first time a bar takes #1 in the
      current (actor, mode) — celebrates breaking through
  Per-bar state tracking (peak value, last rank, shimmer flag,
  ever-was-first flag) makes the triggers idempotent — they
  don't refire on every refresh during a single rank streak.
  Anim state is reset on bar recycling (ReleaseBar) and on theme
  changes (ApplySettings) so nothing leaks between segments.
- NEW: Settings hooks bars.useAnimations (master toggle) and
  bars.animOptOut = { CountUp = true, Glow = true, ... } for
  per-anim opt-out. Default: all on. Defaults can be wired into
  Config.lua's Bars tab in a follow-up.

Version 2.4.8
-------------
Retail (Midnight 12.0) — ADDON_ACTION_FORBIDDEN fix
- Root cause: Midnight 12.0 migrated combat / unit / roster
  events from the legacy Frame:RegisterEvent API to the new
  global EventRegistry (CallbackRegistryMixin). The legacy path
  now fires ADDON_ACTION_FORBIDDEN for these events; only
  lifecycle events (ADDON_LOADED, PLAYER_LOGIN) still work,
  which is why AceAddon kept loading fine while combat tracking
  stayed silently broken.
- Solution: register every restricted event through
  EventRegistry:RegisterFrameEventAndCallback(event, fn, owner).
  Callback signature is (ownerID, ...eventArgs) — no event-name
  arg, which differs from the OnEvent script style. DAMAGE_METER_*
  events are custom (not real frame events) so they use
  EventRegistry:RegisterCallback instead.
- COMBAT_LOG_EVENT_UNFILTERED is NO LONGER registered on Retail —
  Blizzard removed addon CLEU access entirely in 12.0. Damage
  data flows through C_DamageMeter exclusively (the server-combat
  ticker in OnEnable polls C_DamageMeter.GetCombatSessionFromType
  each tick and UpdateFromDamageMeter renders from it).
- ENCOUNTER_STATE_CHANGED dropped — it is a custom callback on
  EncounterJournal, not a real frame event. ENCOUNTER_START /
  ENCOUNTER_END (real events, registered via EventRegistry) cover
  the encounter-tracking need.
- TOC: bumped Interface 120001 -> 120005 and Version 2.4.3 -> 2.4.8.
- Applied to Core_Retail.lua (10 frame events + 3 C_DamageMeter
  callbacks) and Parser_Retail.lua (6 frame events).
- OnDisable now unregisters via EventRegistry:UnregisterFrameEventAndCallback
  and UnregisterCallback so a /reload doesn't double-register.
- Core:RegisterEvents() and Parser:RegisterEvents() are now
  no-op stubs (kept for API compatibility).

Known follow-up: BroadcasterTools.lua and ArenaSummary.lua still
use legacy RegisterEvent for UNIT_SPELLCAST_*, COMBAT_LOG_EVENT_*,
PVP_MATCH_COMPLETE. Those features (Action Tracker, Event Tracker,
Arena Summary) will hit the same restriction when activated.
Migration to EventRegistry planned for next release.

Note: earlier 2.4.8 attempts (pcall wrap, dedicated frame,
C_Timer.After defer, file-load registration, Interface bump,
Frame:RegisterEventCallback) all failed because they fixed the
wrong layer. RegisterEventCallback is a CallbackRegistryMixin
method whose event set is closed and mixin-local — it rejects
raw frame events like PLAYER_REGEN_DISABLED. EventRegistry is
the right entry point.

Version 2.4.7
-------------
Broadcaster Tools (Retail only)
- NEW: Action Tracker overlay — shows your spell casts in a scrolling bar
  list. Cast bars fill as you cast; instant casts show a 1.2s decay. Tracks
  UNIT_SPELLCAST_* events for full cast state (start, success, interrupt,
  channel). Enable/Disable from Broadcaster Tools settings tab.
- NEW: Event Tracker overlay — shows nearby combat events: defensive
  cooldowns, offensive cooldowns (90s+), crowd control, and spell interrupts.
  Events appear as color-coded rows with a 1-second fill animation.
  Built-in curated spell database covering all classes.
- NEW: Arena DPS Bar — split bar comparing your team's damage vs the enemy
  team in arena. 5-second rolling window with 100ms sampling. Activates
  automatically when entering an arena zone.
- Broadcaster Tools tab is now Retail-only (hidden on Classic/TBC/Wrath/MoP).
- Config UI: Enable/Disable + Options buttons for each tracker (like Details).
- "Disable Mythic+ Stuff" toggle now finds the Mythic Dungeon button
  dynamically instead of hardcoding index [4].

Easter Egg
- NEW: Snake minigame in Settings sidebar! Click the Snake tab,
  click the grid, and use Arrow keys or WASD to play. Space to
  pause, R to restart. Speed increases as you eat. High score
  saved per profile. A little something for raid downtime.
- Fixed Snake game freezing / becoming unresponsive after a while.
  Root cause: EditBox keyboard capture lost focus (ESC, clicking
  elsewhere, combat lockdown) with no recovery path. Now auto-pauses
  on focus loss with a clear "Click to resume" overlay. Click or
  press R to restart after Game Over without needing keyboard focus.
- Fixed Snake grid clipped on left side. Grid was offset by
  -SIDEBAR_WIDTH/2 from the scroll child center; now centred at 0.
- Fixed Click to Play — clicking the grid now immediately starts
  the game (snake moves right). Previously only keyboard worked.
- Fixed Snake 180-degree turn death: direction checks now use the
  queued direction instead of the current one, preventing instant
  self-collision from fast opposite-key presses.
- Snake visual overhaul:
  - Animated effects: death flash (red overlay fade-out), screen
    shake on death, floating "+10" score popups that rise and fade,
    golden level-up flash when ranking up.
  - Rank system: Worm → Snake → Serpent → Viper → Python → Wyrm
    (WoW item-quality colours: green/blue/purple/orange/red/gold).
  - Speed bar in header showing current game speed percentage.
  - Animated score counter that smoothly counts up on eat.
  - Persistent stats: games played, total food eaten, longest snake.
  - Golden shimmering food with dual-frequency pulse.
  - Brighter snake head, distinct neck, smoother body gradient.
  - Dimmed overlay backgrounds for all states with 3 text lines.
  - Red-tinted grid on death, darker grid during play.
  - Outer glow border on grid, icon in title.
  - Footer: high score (gold), stats, controls hint.
  - Proper cleanup (OnUpdate + EditBox) when switching tabs.

Debug Console & Debug Parity
- NEW: In-game Debug Console — CMD-like window showing debug output,
  errors, and system messages in real-time. Color-coded by category
  (CORE=green, COMBAT=orange, ERROR=red, DATA=cyan, PVP=magenta,
  PERF=teal, EVENT=blue, WARN=orange). 500-line ring buffer,
  copy-pasteable text. Toggle with /edm console. All clients.
- Console toolbar: one-click buttons for Clear, Diag, Errors, Mem, GC.
- Console filter tabs: click category tabs (CORE, COMBAT, DATA, ERROR,
  PVP, UI, PARSER, EVENT, SYSTEM) to show/hide specific log categories.
  "All" button resets filters.
- Console Tab-completion: press Tab to autocomplete commands.
- Console commands: help, clear, diag, errors, log [n] [cat], debug
  on|off, info, events, mem, gc, segments, search <text>, fps,
  profile, api, lua <code>, reset, reload.
- Console "lua" command: execute arbitrary Lua in a sandboxed env with
  EDM namespace access. Useful for live addon inspection.
- Console "api" command: checks availability of all relevant WoW APIs.
- Console "segments" command: shows current + stored segment details.
- Console "search" command: find matching lines in console history.
- Console "fps" command: shows framerate and network latency.
- Console "profile" command: shows active profile and available list.
- NEW: Dedicated "Debug" tab in Settings sidebar — full diagnostic
  panel with inline pass/fail results, error log viewer (last 15
  errors with timestamps), system info panel (addon version, TOC,
  client, memory, FPS, latency, segments, profile), WoW API
  availability checker (13 APIs), and quick-action buttons (Open
  Console, Run Diagnostic, Show Errors).
- Debug/Record Timeline toggles moved from Advanced section into the
  new Debug tab's General section.
- Debug Console now embedded directly in the Settings Debug tab:
  scrollable output area, toolbar buttons (Clear, Diag, Errors, Mem,
  Info), command input with > prompt, Tab-completion, and command
  history (Up/Down arrows). Matches the addon's own design language.
  Live-refresh: output updates automatically as new log entries arrive.
  /edm console now opens Settings on the Debug tab instead of a popup.
  Standalone BugSack-style window still available via DC:Toggle().
- Fixed Unicode square characters in diagnostic output and settings.
  Replaced checkmark (U+2713) and cross (U+2717) with ASCII "OK"/"X"
  — WoW's game fonts lack these glyphs, causing visible squares.
- Hardcoded SelectTab(6) for Profiles tab replaced with dynamic
  FindTabIndex("BuildProfilesTab") lookup — tab indices no longer
  break when new tabs are added.
- Classic debug parity: /edm diag, /edm errors, /edm log [n] [cat],
  and /edm console now available on Classic Era, TBC Anniversary,
  MoP Classic, and Wrath Titan Forged (previously Retail-only).

Arena Summary (Retail only)
- Fixed Arena Summary showing wrong/missing data compared to WoW's
  default scoreboard. Enemy team was empty, friendly team showed tiny
  numbers (e.g. 45K instead of 915K). Root cause: CLEU combat log data
  is restricted in WoW 12.x arenas. Switched primary data source to
  C_PvP.GetScoreInfo() — the same API WoW's own scoreboard uses.
  Falls back to CLEU segment data on older clients.
- NEW: Arena match history — matches are saved persistently (up to 50)
  in your profile. Browse past matches with Prev/Next navigation
  buttons after the arena has ended. Each saved match stores team
  rosters, damage, healing, deaths, kills, rating, and rating change.
- NEW: Rating change display — shows +/- rating in green/red next to
  player names when available from the scoreboard API.
- NEW: Data source indicator — label shows whether data comes from
  "Scoreboard" (C_PvP API), "Combat Log" (CLEU fallback), or
  "No data" so you know the source at a glance.
- NEW: Date/time shown in subtitle for saved matches when browsing
  history.

Spell Casts Tracking
- NEW: "Casts" column in spell tooltip — hover any player bar (Damage,
  Healing, DPS, etc.) to see how many times each spell was cast. Shown
  alongside the existing Total / DPS / % columns in a light blue color.
  Cast data comes from SPELL_CAST_SUCCESS events via CLEU.
- NEW: "Casts" display mode — dedicated bar view ranking players by total
  spell casts. Cycles alongside existing modes in the menu and rotation.
  Tooltip shows per-spell cast breakdown with count and percentage.
- Pet casts are attributed to owners via GetEffectiveSource mapping.
- Works on all clients (Retail, Classic, TBC, Wrath, MoP).

Bug Fixes
- Fixed Action Tracker entries disappearing immediately after cast
  completes. Entries now stay visible in their final state (green for
  success, red for interrupted) with a gentle fade, and are only
  removed when pushed off by newer casts. Channels and instants also
  persist instead of vanishing after 1.2-2s.
- Fixed "cannot be indexed with secret keys" errors in arena/PvP.
  UnitGUID() returns tainted secret values for arena/nameplate units
  in WoW 12.x. Added SafeGUID/SafeName wrappers with issecretvalue()
  guards in Parser_Retail (NAME_PLATE_UNIT_ADDED, AddPetForUnit,
  UpdatePetMapping) and ArenaSummary (BuildRosterMaps).
- Debug Console now captures real Lua runtime errors (not just
  manual LogError calls). Hooks into BugGrabber if installed,
  otherwise wraps the global error handler via seterrorhandler().
  Only shows errors from EpicDamageMeter files. Errors include
  stack traces (first 3 frames) and occurrence count.
- Fixed DPS/HPS colored bars invisible during combat. Bar width used
  amountPerSecond (rate, e.g. 8.9K) against topValue=session.maxAmount
  (total, e.g. 890K), producing ~1% fill. Now always uses totalAmount
  for bar width — proportions are identical since all actors share the
  same combat duration.
- Fixed 5 diagnostic FAIL results (Constants, MainFrame, Config UI,
  Graph, DetailWindow). RunDiagnostic was checking wrong namespace
  paths (EDM.C, EDM.UI.MainFrame, EDM.UI.Config, EDM.UI.Graph,
  EDM.UI.DetailWindow). Corrected to actual paths (EDM.Constants,
  EDM.UI, EDM.Config, EDM.Graph, EDM.DetailWindow).

Version 2.4.6
-------------
Full Compatibility Audit — Retail + Classic/TBC
- Fixed ADDON_ACTION_FORBIDDEN error spam (6x) on TBC Anniversary Edition.
  AceEvent frame was named, causing Blizzard taint tracking to block
  RegisterEvent during combat. Changed to unnamed frame. Also defers event
  registration if the addon loads during combat (InCombatLockdown guard).
- Fixed ADDON_ACTION_BLOCKED on mode menu open: SetPropagateKeyboardInput
  is a protected function on Retail/TBC Anniversary. Replaced with
  UISpecialFrames for ESCAPE-to-close (standard non-protected pattern).
- Fixed Overhealing and Threat modes showing no data on TBC Anniversary.
  Root cause: ADDON_ACTION_FORBIDDEN blocked CLEU event registration →
  parser never received combat log events → database stayed empty.
- Fixed DPS/HPS bar display to match Damage Done/Healing Done format.
  Now shows "52.3K (476 DPS) 43.4%" instead of "476/s 43.4%".
- Fixed "table index is nil" crash at Parser_Classic.lua:506 when taking
  fall damage (ENVIRONMENTAL_DAMAGE). sourceGUID is nil for environmental
  events — can't be used as a table key. Now uses "Environment" fallback.
- Fixed SetGradient API crash on Classic and TBC Anniversary. The wrapper
  now auto-probes at first call: tries new 2-arg CreateColor form, if it
  errors falls back to old 7-arg form. Previous detection via GetBuildInfo
  TOC version was unreliable on TBC Anniversary (Retail client, but
  reported Classic-like version). All 60+ SetGradient calls across
  MainFrame, Config, DetailWindow, Graph, Skins, Widgets, ArenaSummary,
  DeathRecap now go through the wrapper.
- Fixed ColorPickerFrame:SetupColorPickerAndShow() crash on Classic/TBC.
  This API is Retail 10.2.5+ only. Added compat path using legacy
  ColorPickerFrame direct-property method for Classic clients.
- Fixed SecretValue arithmetic crash in Threat mode (Retail PvP). The
  threat value can be wrapped by WoW 12.0+ SecretValue. Now washed
  through tonumber(tostring()) before arithmetic.
- Fixed mode menu ESCAPE-to-close broken by OnShow overwrite. Two
  SetScript("OnShow") handlers were registered on the same frame —
  the second killed the first (which enabled keyboard propagation).
  Merged into a single OnShow handler.
- Fixed Database.lua nil crash when profile.combat sub-table is nil.
  All 5 occurrences of EDM.db.profile.combat.X now have full nil-guard
  chain.
- Fixed C_DamageMeter session fetch crash on Retail. GetCombatSessionFrom
  Type/ID calls can throw if the session is tainted. Now wrapped in pcall.
- Fixed duplicate "Max Spells" slider in tooltip settings (one with wrong
  range 3-15 and default 6 vs correct 3-30 default 30). Removed duplicate.
- Fixed Skins.list nil crash in Setup Wizard Step 2 when Skins module
  hasn't loaded yet.
- Improved client detection: granular per-expansion flags (isVanilla, isTBC,
  isWrath, isCata, isMoP) instead of binary isRetail/isClassic. isRetail is
  now strictly Midnight (12.x+). Added hasMythicPlus flag (Retail only).
- Mythic Dungeon settings tab now hidden on non-Retail clients (Classic Era,
  TBC Anniversary, Wrath, MoP). Config sidebar dynamically filters categories
  based on client capabilities.

Version 2.4.5
-------------
Classic/TBC Font + Settings Visibility Fix
- Fixed "FontString:SetText(): Font not set" error spam (70x per combat)
  on TBC Anniversary and Classic servers. SetFont() can silently fail on
  Classic clients — pcall returns true but the font is internally nil.
  Added Utils.SafeSetFont() (shared) that verifies GetFont() after SetFont
  and falls back to FRIZQT__.TTF. Applied to ALL FontStrings across the
  entire addon: MainFrame bars/statusbar/titlebar/tooltip, Config settings
  panel (75 labels), DetailWindow, Graph, Widgets, Bars, Tooltip, Skins,
  Core_Classic, Core_Retail. Zero raw :SetFont() calls remain in addon code.
- Fixed Settings panel text completely invisible on TBC Anniversary.
  All 75 label/header/button FontStrings in Config.lua now use the safe
  font wrapper. Sidebar was visible because it used different frame timing;
  right panel labels were created with silently-failed fonts.
- Fixed missing icons in mode dropdown on TBC Anniversary. DPS icon
  (ability_hunter_focusfire) and Enemy Damage Taken icon
  (achievement_boss_lichking) are MoP/WotLK-only textures that don't
  exist in Classic/TBC. Replaced with Classic-safe alternatives:
  ability_warrior_innerrage (DPS) and ability_warrior_decisivestrike
  (Enemy Damage Taken).

Version 2.4.4
-------------
Theme Toggle Fix + DPS Display Fix + Expanded Debug System
- Fixed DPS showing "1 DPS" or "0 DPS" on the first tick of a new
  combat session (C_DamageMeter path). The Blizzard API's
  amountPerSecond field can return stale/wrong values before the
  session's internal timer stabilises. DPS text is now computed from
  totalAmount / durationSeconds for plain values, matching the actual
  data. SecretValue (PvP) path still uses API's amountPerSecond.
- Fixed tooltip showing "No spell data available" after combat ends.
  C_DamageMeter session persists briefly after PLAYER_REGEN_ENABLED,
  so UpdateBarsFromDamageMeter still renders bars — but with a minimal
  actorData (empty abilities). GetTooltipSpells now has a third fallback:
  lazy fetch from C_DamageMeter on hover, trying Current session first,
  then expired session via stored blzSessionId. Result is cached for
  subsequent hovers. Details! equivalent: CallSpellAPI fall-through.
- Fixed theme toggle button showing as a square in settings title bar.
  Unicode ☀/☽ characters aren't supported by WoW's game fonts.
  Replaced FontString with texture-based icons: golden circle with rays
  (sun) and silver crescent (moon) drawn from WoW's built-in
  TempPortraitAlphaMask texture. Icons highlight to accent blue on hover.
- Expanded debug system with categorised logging (CORE, COMBAT, DATA,
  PVP, UI, GRAPH, PARSER, ERROR). Each category has its own colour code
  and timestamp. Debug messages stored in a 500-entry ring buffer.
- NEW: /edm diag — full system diagnostic scan. Checks all modules,
  WoW API availability, current segment state, SecretValue taint status,
  and error log. Prints a pass/fail checklist to chat.
- NEW: /edm errors — shows the last 25 captured errors. Errors are
  silently logged even when debug mode is off, so you can check after
  something breaks without needing to reproduce it.
- NEW: /edm log [count] [category] — shows the debug log buffer.
  Filter by category (e.g. /edm log 20 PVP) to isolate specific systems.
- Utils.LogError() now captures errors silently (always stored, printed
  only in debug mode). Event callbacks, UI updates, and SafeCall all
  route through the error log for post-mortem analysis.
- Utils.ValidateNum() helper: validates and washes values in one call —
  checks nil, NaN, SecretValue taint. Returns (washed_number, ok).

Version 2.4.3
-------------
Live Combat Updates Fix + Enemy Data Toggle
- Fixed data only appearing after combat ends (PvE and PvP).
  Root cause 1: Parser_Retail.lua was replacing Parser_Classic.lua's
  CLEU handler with a no-op, preventing database population during
  PvE combat. Parser_Retail.lua now extends Parser_Classic instead
  of replacing it, preserving the CLEU handler for PvE data collection.
  Root cause 2: CLEU event lookup tables (DAMAGE_EVENTS, HEAL_EVENTS,
  etc.) were only defined on Classic, causing Parser_Classic's CLEU
  handler to error on every event on Retail. Tables are now defined
  on both clients. CLEU works in PvE on 12.0 — only PvP restricts
  the data values (safeNum silently skips SecretValues).
- Rendering pipeline now always tries C_DamageMeter first (like
  Details! ShouldUseGameData), regardless of combat state. Fallback
  to database path kicks in only when C_DamageMeter has no data.
- Default refresh rate lowered from 0.5s to 0.3s (matches Details!).
  Minimum clamp lowered from 0.5s to 0.1s for users who want faster.
- Enemy Damage Taken and Avoidable Damage Taken modes now use
  C_DamageMeter (Enum.DamageMeterType.EnemyDamageTaken /
  AvoidableDamageTaken) for live arena/BG rendering.
- DAMAGE_METER event handlers always trigger C_DamageMeter rendering
  (previously gated on combat state, missing boundary updates).
- Added debug logging at each C_DamageMeter failure point.
- NEW: Team/Enemy toggle button in toolbar. Click "Team" to switch
  to "Enemy" view — shows enemy player data (damage taken by each
  enemy) during PvP via C_DamageMeter.EnemyDamageTaken. Works live
  during arena and BG combat. Title bar shows [Enemy] indicator.
- Existing profiles with refreshRate 0.5s auto-migrated to 0.3s.
- Complete settings panel UI/UX redesign: Inzoi-style light translucent
  desktop GUI. Frosted-glass light-gray panels, white card sections,
  coral/pink slider thumbs, iOS-green toggle switches with white knobs,
  thin 2px tracks, minimal sidebar navigation with accent bar, generous
  spacing. Setup Wizard matches new light style. All existing
  functionality preserved — zero feature loss.
- NEW: Dark / Light mode toggle for settings panel. Click the
  sun/moon icon in the title bar to switch themes. Choice is
  saved per profile. Dark mode features Discord/VS Code-inspired
  charcoal panels with brighter accent colors. All ~40 palette
  variables and ~60 inline color sites consolidated into a
  centralized theme system (ApplyTheme mutates tables in-place).
- Fixed SecretValue taint crash in PvP arenas: Utils.FormatTime,
  Utils.FormatPercent, Graph.lua FormatTime/FormatValue, and
  Database:GetSegmentDuration now wash values through
  tonumber(tostring()) to strip SecretValue wrappers before
  numeric comparisons. Graph data feeding also washes
  segment.totalDamage/totalHealing and _graphDPS/_graphHPS.
- Fixed DPS/HPS values showing excessive decimal places (e.g.
  "628.85714285714" instead of "629") when values are below 1K.
  Blizzard's AbbreviateNumbers doesn't round small numbers; now
  uses Utils.FormatNumber for plain values and AbbreviateNumbers
  only for SecretValues. Fixed in MainFrame and DetailWindow.

Version 2.4.2
-------------
PvP / Arena / Battleground Rendering Fix
- Fixed bars not displaying during arena/BG combat. Root cause: the
  rendering pipeline lacked server-side combat detection and could
  silently error when processing SecretValues from C_DamageMeter.
- Server combat detection ticker (like Details! control.lua):
  C_Timer.NewTicker(0) polls C_DamageMeter every frame to detect
  server-side combat state via issecretvalue(session.totalAmount).
  Ensures bars render even when PLAYER_REGEN events are delayed or
  the server-side combat session outlives the client-side flag.
- Parser_Retail.lua now loads on Retail (added to TOC). Replaces
  Parser_Classic.lua on Retail with better arena/BG GUID tracking
  (nameplate scanning, arena opponent scanning, BG detection).
- DPS and HPS display modes now use correct Blizzard enum types
  (Enum.DamageMeterType.Dps / .Hps) instead of DamageDone/HealingDone,
  matching Details! behavior for proper sort order from the API.
- Secret bar rendering uses Enum.StatusBarInterpolation.ExponentialEaseOut
  (matching Details! UpdateBarApocalypseWow) for smooth bar transitions.
- isSecret detection now checks session.maxAmount and session.totalAmount
  in addition to sources[1].totalAmount, catching edge cases where the
  first source is plain but the session-level values are still secret.
- Arena names use Ambiguate(name, "short") for SecretValue names
  (same technique as Details! class_damage.lua).
- Status bar total now shows AbbreviateNumbers(session.totalAmount)
  during PvP combat instead of just the player count.
- Duration uses session.durationSeconds from C_DamageMeter when
  available, matching Details! behavior.
- UpdateBarsFromDamageMeter errors are now logged via Utils.Debug
  instead of being silently swallowed by pcall.

Version 2.4.1
-------------
PvP / Arena / Battleground Fixes
- Full PvP support for WoW 12.0+ Retail: arenas and battlegrounds now
  track damage, healing, interrupts, dispels and deaths correctly.
- CLEU parser (Parser_Classic.lua) is now SecretValue-aware: all
  CombatLogGetCurrentEventInfo() values are checked with safeNum/safeStr
  before arithmetic or string comparison. Events with secret amounts
  are gracefully skipped instead of causing taint errors.
- Smart post-combat sync inspired by Details! WaitServerDropCombat:
  after combat ends, polls C_DamageMeter session data every 0.3s until
  SecretValues resolve (up to 9s). In PvE this resolves instantly; in
  PvP it waits for the server to drop the restriction state.
- Uses C_RestrictedActions.GetAddOnRestrictionState to detect PvP
  restriction state (Combat + PvPMatch flags) before attempting sync.
- IsSessionStillSecret() checks whether the C_DamageMeter session
  still contains SecretValue fields, preventing premature sync.
- specIconID → GUID cache: when syncing C_DamageMeter data, specIconID
  is cached to GUID mapping, allowing tooltip/detail resolution when
  sourceGUID is a SecretValue (same technique as Details! guidCache).
- SyncFromDamageMeter now falls back to name-based GUID lookup via
  group/arena/nameplate units when sourceGUID is still secret.
- Parser_Retail.lua registers NAME_PLATE_UNIT_ADDED event for live
  GUID tracking in battlegrounds.
- ScanBattlegroundNameplates() added to Parser_Retail.lua for opponent
  GUID resolution in large-scale PvP.
- Parser_Retail.lua now detects BG instance type and scans nameplates
  during UpdateGroupGUIDs.

Version 2.4.0
-------------
New Features
- Window-group lock icon redesign — small chain-link button at the
  bottom-right of each meter window, well above the footer/status bar.
  Left-click + drag to reposition anywhere inside the frame; position
  persists per character. The icon fades out while the cursor is over
  the meter window so it doesn't get in the way of clicking bars, then
  reappears when the cursor leaves. Clamped to the frame so it can't
  be dragged outside the visible area. Click = detach this window from
  its group; Shift-Click = dissolve the entire group.

Tweaks
- Window-group snap distance bumped from 14px to 22px for a more
  magnetic feel when sliding two meter windows close together.

Bug Fixes
- Threat module SecretValue taint on retail. The CoerceNum helper had
  an `if type(v) == "number" then return v end` early-return which
  short-circuits SecretValue numbers (those are typed as number too,
  so the check was a no-op for them). actor.threat ended up storing
  the raw SecretValue, then any subsequent comparison
  (`actor.threat > 0` in ClearThreat, `(actor.threat or 0) > topValue`
  in MainFrame) tainted the whole call frame. Fix:
    * CoerceNum now always washes through string.format + tonumber
      and bails to nil when issecretvalue(v) is true.
    * ClearThreat resets unconditionally (no comparison).
    * Threat:Update re-coerces at the assignment boundary.
    * MainFrame threat-bar compare is pcall-guarded.
  Resolves the "attempt to compare field 'threat' (a secret number
  value, while execution tainted by 'EpicDamageMeter')" error in
  retail dungeons / raids.

- Reverted live-bar-render-path regressions introduced in 2.3.9
  (valueText SetWidth(64) + SetWordWrap(false), spell-cache plainGUID
  gate change with pcall, abilities mirror block, status-bar / title-
  text / mode-button width caps and word-wrap blocks). The render path
  now matches the working pre-regression behaviour again. Locale-aware
  font fallback, threat-module SecretValue hardening, and the
  Current→Expired session fallback in SyncFromDamageMeter are kept.

----

Version 2.3.9
-------------
New Features
- Mode Picker rebuilt as a 3-column Details-style grid (modeled on
  Details\frames\window_main.lua mode menu): right-click the mode button to
  open a Damage / Healing / Other column layout with Blizzard spell icons per
  entry, violet → blue hover gradient, and a left stripe + brightened label
  to mark the active mode. Two new modes are wired up:
    * Avoidable Damage — heuristic detection in Parser_Classic.lua
      (ENVIRONMENTAL_DAMAGE or any source outside the group counts as
      avoidable) so floor damage / boss mechanics show up without curating
      a per-spell list.
    * Potions — SPELL_CAST_SUCCESS dispatches into ProcessPotion which keys
      a curated POTION_SPELLS table covering TWW (431416/431473/431932/
      431930), DF, SL, BFA, Legion/WoD, MoP, Cata, Wrath, TBC and Vanilla.
  Database.lua's CreateActorData stores avoidableDamageTaken,
  enemyDamageTaken, potionsUsed and a per-spell potionList so the new modes
  sort correctly via GetSortedActors. Constants.lua adds DISPLAY_MODE
  entries 19/20 plus order entries for both Retail and Classic, and Retail
  now also lists Overhealing in the menu order. CalculateTotals dispatches
  the new modes alongside the existing ones.
- Language Selector — a new "Language" tab on the right of the Settings
  sidebar lets you pick the addon language independently of the WoW client
  locale. Locale files no longer gate themselves on GetLocale(); each one
  registers into a shared EpicDamageMeter_LocaleData[code] registry, and a
  new Locales/_Apply.lua (loaded last in every TOC) builds the active
  EpicDamageMeter_Locale by merging the chosen overlay onto the English
  base. Choices are Auto / English / Deutsch / Español / Italiano /
  Русский / 繁體中文; "Auto" follows GetLocale(). Switching live calls
  EpicDamageMeter_ApplyLocale to refresh the table in place; a /reload
  button is provided for full UI re-translation. Comprehensive L("…")
  wrapping across MainFrame, ArenaSummary, DeathRecap, DetailWindow,
  Tooltip, Graph, WindowGroup, Core slash output, and the entire Config
  panel (tab labels, every section header, every row label via the row
  helpers themselves, every dropdown option). German overlay covers
  ~270 strings each across all six locales (enUS, deDE, esES, itIT, ruRU,
  zhTW) — Profiles tab fully covered (Switch/Create/Copy/Delete sections,
  static popups). Any not-yet-translated key still falls back through
  key-as-value to English. Setter on the Language dropdown rebuilds
  sidebar labels and re-runs the active tab's BuildXTab so the change is
  visible immediately without /reload.
- Locale-aware font picker (Locales/_FontMap.lua) — resolves a font
  path that can render the active addon-language's glyphs. Probes
  candidates with FontString:SetFont + GetFont:
    ruRU: FRIZQT___CYR.TTF → ARIALN.TTF → FRIZQT__.TTF
    zhTW: ARKai_T.TTF → ARHei.TTF → bHEI01B.ttf → FRIZQT__.TTF
    Latin locales stay on FRIZQT__.TTF
  If the user drops Interface\AddOns\EpicDamageMeter\Fonts\addon.ttf,
  that pan-Unicode override wins for every locale. Wiring:
    * Core/Utils.lua exposes EDM_F() / Utils.GetFont() globally
    * MainFrame's ApplyBarFont overrides the skin/profile font with
      the locale font when a non-Latin font was actually resolved
      (skin font kept on Western locales)
    * UI:RefreshLocaleFonts() walks every active instance and re-sets
      titleText / modeBtn / segmentBtn / status-bar / bar-pool fonts
    * Config.lua's local FONT re-resolves on every SelectTab so the
      Settings panel picks up the right font even after the saved
      language overrides the parse-time default
    * Hooked into the Language dropdown setter so the change takes
      effect immediately, without /reload
  Note: this only helps when the resolved font actually exists on
  disk. A German WoW client has no Cyrillic-capable font shipped, so
  for full ruRU/zhTW rendering on a Western client the user must
  drop their own TTF at the override path.

- Setup Wizard fully localized across all six locales — every step
  title, every section header, every toggle label and description, the
  Welcome / Theme / Font / Display / Layout / Colors / Behavior / Done
  step labels, and the summary card lines (Theme: / Font: / Bars: /
  Format: / Window:) all go through L() and are translated. WizSection
  / WizToggle / MakeSelectCard helpers auto-translate their first label
  argument so future wizard rows are translated for free. Roughly 120
  new wizard-specific keys per locale.

- Sounds promoted to a dedicated sidebar tab — moved out of the inline
  Sounds section that lived under Settings, and given its own left-rail
  entry between Settings and Profiles. Four sub-sections inside: Sound
  Cues (master enable + Use-Custom-Files), Events (per-cue toggles for
  combat start, combat end, new record, reset), Preview (four ▶ test
  buttons), About (info card). Sidebar SelectTab dispatch shifted
  Profiles/Language/Credits to indices 5/6/7; the four C_Timer.After
  SelectTab(4) calls inside BuildProfilesTab were updated to (5).

- Sound cues — Core/Sounds.lua module fires on combat start, combat
  end, a new personal-best player DPS, and on /edm reset. Three-tier
  source resolution: LibSharedMedia entry (if profile.sounds.lsm[event]
  is set) → custom OGG in Sounds\<file>.ogg if present → Blizzard built-
  in FileDataID (defaults: UI_Bnet_Toast 567432, PVPFlagTaken 569883,
  RaidWarning 8959, MapPing 8454). PlaySoundFile returns willPlay=false
  silently when a custom file is missing, so the fallback FileDataID
  always plays without any user-supplied audio. Personal-record check
  walks the segment's actor list at combat-end +1s, finds the player
  by UnitName, computes damage/duration, and only plays the cue when
  the new DPS strictly beats db.profile.records.bestDPS (skips the
  very first combat). Master toggle + per-event toggles + four ▶
  preview buttons added under Settings → Sounds.
- Window Groups (modeled on Details' snap system in
  Details\frames\window_main.lua): drag any two windows so their edges
  come within 14 px of each other and they snap into a group — from
  that moment they move together. Groups form a transitive closure, so
  three or more windows can be chained horizontally, vertically, or in
  L-shapes. A chain-link pill button appears top-right on every window
  that belongs to a multi-member group; click it to detach just that
  window, Shift-Click to dissolve the entire group. Snap relationships
  survive reload — the snap set is persisted in SavedVariables alongside
  position/size and cleanup runs automatically when a window is destroyed.
- Death Recap overlay (modeled on Details\frames\window_deathrecap.lua):
  a styled panel that replaces Blizzard's stock death recap when it opens.
  Hooks DeathRecap_LoadUI so DeathRecapFrame_OpenRecap is intercepted,
  hides Blizzard's Recap1-5 lines, then reads DeathRecap_GetEvents(recapID)
  and renders up to five gradient-pill rows with spell icon, time delta,
  spell name, damage (crit = gold, normal = red), source class color, and
  HP% at time of hit. Matches the Settings redesign palette (ACCENT blue
  + ACCENT_HOT violet). /edm dr to preview.
- Arena Summary window (modeled on Details\frames\window_arenasummary.lua):
  on PVP_MATCH_COMPLETE inside an arena, after a 0.5s settle, freezes a
  snapshot of the current segment and renders a two-column scoreboard —
  your team (blue stripe) vs enemy team (red stripe), sorted by damage,
  each row showing damage + DPS, healing + HPS, and a compact misc strip
  for deaths / interrupts / dispels. Footer totals team damage and team
  healing. Outcome badge reads VICTORY / DEFEAT / DRAW from
  C_PvP.GetActiveMatchWinner vs GetBattlefieldArenaFaction. Classification
  is GUID-based: group members = friendly, arena1-5 = enemy. Lighter than
  Details' 1285-line implementation (no long-term archive, no compressed
  save), but covers the core use case. /edm arena to preview.
- Threat display mode (modeled on Details! Tiny Threat): right-click the mode button
  and pick Threat under Utility. Resolves a reference mob (boss1-5, target, focus,
  mouseover, or targettarget) and polls UnitDetailedThreatSituation for every group
  member on UNIT_THREAT_LIST_UPDATE and PLAYER_TARGET_CHANGED. Percent column shows
  the scaled threat% (100 = aggro threshold); bars are color-coded by threat status:
  red = tanking, orange = about to pull, yellow = getting close, class color = safe.
  Creates actor entries on the fly so non-damaging group members still appear.

Bug Fixes
- Hardened Threat module against Retail 12.0 SecretValue boolean taint: every WoW API
  return that could be a SecretValue (UnitExists, UnitIsDeadOrGhost, UnitCanAttack,
  UnitDetailedThreatSituation's isTanking) is now either pcall-guarded or coerced
  through tostring before any boolean test. Queries the 5 return values of
  UnitDetailedThreatSituation inside a pcall closure so isTanking is dropped at the
  pcall boundary and can never reach a Lua boolean expression. Also forces
  actor.threatIsTank to a strict true/false Lua boolean. Fixes the
  "attempt to perform boolean test on local 'isTanking' (a secret boolean value
  tainted by 'EpicDamageMeter')" error spam on new-patch raid/instance content
  (e.g., Windrunner Spire, Seat of the Triumvirate). (Thanks to ursiDEE and
  MrPapaBear for reporting)
- Fixed detail window showing "No damage data" when clicking an enemy arena
  player or battleground opponent. Two fixes, based on how Details handles
  the same case:
    (1) Removed the blanket SecretValue-GUID rejection in CallSpellAPI.
        Details\boot.lua:328 (Details222.B.GetSpells) and
        Details\core\parser_nocleu1.lua:552 (getSpells) both pass the raw
        sourceGUID from C_DamageMeter.combatSources directly to
        GetCombatSessionSourceFromType / GetCombatSessionSourceFromID — no
        SecretValue filtering. We now do the same (still wrapped in pcall
        for safety), so arena/BG opponents whose GUIDs arrive as
        SecretValues reach the API and return their spell breakdown.
    (2) MainFrame's plain-GUID map now also covers arena1-5 + their pets
        and walks C_NamePlate.GetNamePlates to pick up battleground
        opponents via nameplate unit tokens — UnitGUID on those returns
        plain strings, giving us a guaranteed-plain fallback if the
        session-source GUID path ever stumbles.
  (Thanks to tidgey for reporting)
- Fixed overkill damage being counted in damage done/DPS totals. Effective damage is now
  tracked (amount - overkill), matching how Details and other meters handle it.
  (Thanks to Undercover for reporting)
- Fixed PvP combat data being wiped when leaving arena or battleground. Zone change auto-reset
  now only triggers when entering a new dungeon or raid, not when returning to the open world.
  (Thanks to user_myc0mwa16b49mnts for reporting)
- Fixed meter disappearing after PvP combat ends due to aggressive zone-change reset logic.
- Fixed Threat module crashing on Retail 12.0 with "attempt to perform boolean test on
  a secret boolean value". Rewrote the module to use the same patterns as Details! does
  in unitframe.lua and plugins_statusbar.lua: `local _, status, ...` discards isTanking
  at the call site, and tank state is read from UnitGroupRolesAssigned instead of the
  SecretValue return. No pcall wrapper needed — Details has shipped this pattern for
  years without taint issues. (Thanks to ursiDEE for reporting)

UI Redesign
- Settings panel got a full visual overhaul (not just a polish pass):
  - New "glass indigo" palette: vivid sky-blue accent plus a violet secondary used
    for gradients and highlights, deeper panel body, tighter contrast ladder between
    sidebar, content, section cards, and rows.
  - Panel resized to 860x640 (from 820x620); sidebar widened to 180px so longer
    category labels (and future ones) breathe.
  - Title bar is now a premium header (54px): deep vertical gradient base with a
    horizontal violet→blue accent wash layered on top, a two-segment rainbow top
    line (violet → blue → cyan), bottom separator tinted with the accent gradient,
    and a soft drop shadow for clear separation from the body.
  - New emblem logo: 26x26 rounded square with a violet→blue vertical gradient,
    inner sheen on the top half, soft horizontal halo behind it, and a white "E"
    glyph with outline + shadow. Paired with a larger 16pt wordmark (cyan-teal
    "Epic" / gold "Damage" / soft-red "Meter") and a breadcrumb-style subtitle
    ("/ Settings" in dim text).
  - Setup Wizard button is a gradient pill: violet→blue fill, top sheen, white
    icon + outlined bold label, decorative sparkle dots on the right, and an
    outer halo glow that fades in on hover (plus a brighter gradient + sheen
    shift). The border brightens from 28% to 55% white on hover.
  - Section headers became card-style: flat dark card with 1px inner border, chunky
    accent→violet vertical pill on the left, soft glow around the pill, larger 13pt
    outline title, right-aligned + / − collapse chevron (instead of v / >).
  - Rows redesigned from the ground up: no more alternating background stripes,
    no per-row left accent edge — just flat rows with a 1px hairline divider at the
    bottom and an accent-tinted hover fill. Rows are taller (32px toggle/color,
    34px dropdown, 48px slider) with more breathing room.
  - Sliders got a custom track: thin 4px dark pill with an accent-to-violet
    horizontal-gradient fill that grows from the left as you drag. Default WoW
    slider track graphics are hidden, keeping only the handle.
  - Dropdowns are now pill-shaped, with an accent left-stripe inside the button
    that brightens on hover, plus an accent-colored chevron.
  - Every interactive element now uses the same "gradient pill + halo" language
    as the Setup Wizard button, giving the panel a coherent premium look:
    - Sidebar nav active tab: violet→blue horizontal gradient fill, top sheen
      band, outer halo glow bleeding out of the button, left accent pill in
      violet→blue vertical gradient, outlined bold label with shadow.
    - Reset Data / Reload UI buttons: gradient pill style with per-button
      brand colors (red for Reset, green for Reload), halo that fades in on
      hover, top sheen, outlined 12pt label. Slightly taller (34px).
    - Toggle switches: gradient fill (violet→blue) + outer halo when ON,
      flat neutral when OFF. Knob is pure white on, dim gray off.
    - Dropdowns: gradient fill + top sheen + hover halo + violet→blue left
      accent stripe that brightens on hover. Border switches from neutral to
      accent-blue on hover. The opened menu has an accent gradient top line
      and an accent-tinted border.
  - Sidebar has a subtle vertical violet wash layered on top of its dark base,
    and its right divider is now a vertical accent gradient line.
  - Content area has its own subtle violet top-down wash for depth.
  - Toggle switches are slightly larger (40x18).

- Setup Wizard now speaks the exact same premium visual language as the main
  settings panel:
  - Frame border picks up an accent-tinted color instead of the old grey edge.
  - Title bar rebuilt to match settings (54px): vertical gradient base + violet
    →blue horizontal wash, top sheen band, two-segment rainbow top line
    (violet → blue → cyan), accent-gradient bottom separator. Hosts the same
    26x26 emblem logo with "E" glyph + halo, 15pt wordmark, breadcrumb "/",
    and subtitle reading "Setup Wizard / step name".
  - Skip button is a proper pill now: dark gradient fill, top sheen, 1px
    border, outlined SKIP label — hovers into a warm red border + halo glow
    + brighter fill instead of the old text-with-underline look.
  - Progress bar sized up to 30px with a violet wash, accent-gradient
    bottom divider, 8px step dots, and a violet→blue horizontal gradient
    track that fills from the first to the last completed step. Active step
    now uses a pure white 10x10 dot with white outlined label; completed
    steps shift to the accent color; upcoming stays dim.
  - Bottom nav bar redesigned (56px): violet wash + accent-gradient top
    divider. Back / Next are full gradient pill buttons with halo, fill,
    sheen, and outlined label — primary (Next) in violet→blue, secondary
    (Back) in dark with an accent hover border + subtle halo.
  - Section headers use the same violet→blue vertical pill (3x12) +
    outlined 11pt title + accent gradient underline as the main panel.
  - Selection cards (theme / layout picker) are now gradient pill cards
    with a halo glow and a left violet→blue stripe when selected, a pure
    white checkmark, and an accent-border hover state when unselected.
  - Wizard content area is now scrollable (mouse wheel). Fixes the Behavior
    step rendering its last toggles underneath the Back / Next buttons on
    smaller wizard heights. Scroll position resets to top on step change.
  - Wizard toggle rows match the main toggle rows: flat row with hairline
    divider, accent-tinted hover wash, and the premium 40x18 gradient
    switch (violet→blue fill + halo when ON, white knob; neutral dark
    when OFF, dim gray knob).

Code Quality
- Removed AI-generated comment patterns (section separators, marketing language, obvious
  state-the-obvious comments) across all UI and Core files.
- Headers now describe file contents without adjectives or version markers.

Version 2.3.8
-------------
Bug Fixes (Community Reports)
- Fixed ALL dropdown menus not saving selections: CreateDropdownRow passed numeric array
  index to setter instead of string value. Affected every dropdown in settings (Skin,
  Combat Behavior, Font Face, Bar Texture, etc.). Arrays now preserve intended order.
  (Thanks to Undercover for reporting the symptom on MoP Classic)
- Fixed "Combat Behavior" changes not applying immediately: Selecting a mode now calls
  UpdateVisibility right away instead of waiting for next combat event.
- Fixed visibility features (Hide in Combat, Only in Group, Combat Alpha, etc.) completely
  missing on MoP, TBC, and Classic Era: Added defaults, UpdateVisibility(), and per-instance
  ShowWindow/HideWindow to all clients.
- Fixed segments not separating between fights on MoP/TBC/Classic Era: Added auto-segment
  logic with configurable "Segment Gap" setting (default 3s). Retail threshold reduced from
  hardcoded 10s to use the same setting.
- Fixed class/spec icons not showing on bars: Replaced string-path icon lookup with
  fileDataID-based table for all 13 classes + special types. Fixes nil dirty-check bypass.

Version 2.3.6
-------------
New Features
- Setup Wizard: 7-step first-run experience (Welcome, Theme, Font, Display, Layout, Behavior, Done).
  Appears automatically on first login. Features: step progress bar with labeled dots, skin cards
  with mini-bar previews, font list with live Cyrillic preview, bar overlay/format/info toggles,
  window size/scale/opacity sliders, visibility & combat behavior toggles, summary card on finish.
  All changes apply live. Skip button cleanly placed in title bar.
- Per-skin unique bar effects: Each of the 23 themes now has its own distinct visual identity beyond
  just colors. Each skin defines unique overlays, accent lines, inner glow, spark color/intensity,
  animation easing, and hover behavior:
  • Overlay types: topShine, bottomFade, innerGlow, duoTone, scanline, holographic, glossy, shadow
  • Accent lines: per-skin colored top/bottom/left edge accents
  • Inner glow: gradient glow strips from top/bottom edges (neon, cyberpunk, midnight, etc.)
  • Spark: per-skin spark color and intensity at bar leading edge
  • Animation: smooth (exponential), snappy (instant), elastic (bouncy overshoot) per skin
  • Hover styles: glow, brighten, neonPulse, borderFlash, ember, frost, arcane, subtle
  • Flash color: per-skin crit flash tint
- Per-skin bar layout system: Each theme defines a complete bar design beyond just effects:
  • Icon shape: square (default), round (with minimap ring border), or hidden per skin
  • Value position: right (standard), overlay (on bar fill), or stacked (vertical name+value)
  • Rank style: number (#1, #2), colored dot (●), or hidden per skin
  • Background style: gradient, solid, dark (dimmed), or none per skin
  • Background direction: VERTICAL or HORIZONTAL gradient per skin

New Features
- Enemy Damage Taken display mode: Shows damage taken per enemy NPC/boss. Hover to see
  which players dealt the most damage to that target. Useful for tracking add damage and
  identifying who's slacking on priority targets. Available in mode dropdown under Damage.
- Hide in Combat visibility mode: Opposite of "Auto-Hide OOC" — hides the meter when
  entering combat and shows it when leaving. Perfect for overlapping with a threat meter
  so both share the same screen space with opposing visibility rules.
  (Thanks to Undercover for the suggestion)
- Separate combat alpha: New "Combat Alpha" toggle with independent In-Combat and
  Out-of-Combat opacity sliders (0-100%). Can be used instead of hide/show for a
  smooth fade-based approach to combat visibility control.

Bug Fixes (Community Reports)
- Fixed pet damage not merging with owner on MoP, Classic, and TBC clients: Parser was
  reading from wrong config path (profile.general.mergePets instead of profile.combat.mergePlayerPets).
  Treants, Water Elementals, and all other pets now correctly merge with owner.
  (Thanks to pawnzee for the detailed report)

Visual Improvements
- All per-skin effects boosted to be clearly visible (overlays, accents, glow strips, spark 2-3× stronger)
- Accent lines widened to 2px (top/bottom) and 3px (left) for clear visibility
- Inner glow strips now cover 60% of bar height (was 40%)
- More layout variety: Glass, Midnight, Ocean, Warcraft now use stacked text; Toxic uses overlay text

Bug Fixes
- Fixed name text disappearing on skins with hidden rank/icon (ElvUI, Minimal, Shadow, Toxic):
  rankText had no anchor after ClearAllPoints when rankStyle="none", causing nameText to vanish.
  Now nameText anchors directly to icon when rank is hidden.
- Fixed name text appearing right-aligned on some skins (missing SetJustifyH after ClearAllPoints)
- Removed "stacked" text layout (name+value vertical) — caused text overlap on bars under 28px
- Fixed critical bug: profile settings (theme, fonts, scale, opacity, title bar visibility, etc.)
  were lost on every reload/login. The custom AceDB-3.0-EDM library's copyTable() unconditionally
  overwrote saved profile values with hardcoded defaults from Constants.lua each time db.profile
  was first accessed. Now only missing keys are filled in, preserving user changes across sessions.
  (Thanks to Pentara [Mind] for the detailed bug report identifying Constants.lua as the culprit.)
- Fixed crash spam "attempt to compare local 'str' (a secret string value tainted)" in Parser_Classic.lua:96
  (safeStr() now uses issecretvalue() to bail out before comparing tainted WoW 12.0 strings)
- Fixed Settings crash "attempt to call upvalue 'setValue' (a nil value)" — Minimap Icon toggle on Retail
  was missing the tooltip nil arg, causing the getter to land in the tooltip slot and setValue to be nil
- Removed "Enable Addon" toggle from Settings (the addon is always enabled when loaded)
- Fixed /edm slash command not working on Retail (AceConsole-3.0-EDM lib name didn't match the
  "AceConsole-3.0" mixin string in NewAddon, so RegisterChatCommand silently no-op'd; added
  direct SlashCmdList fallback to match Classic)
- Fixed misleading login hint: "/edm for options" now correctly says "/edm config for settings"
  (in all 5 locales — bare /edm shows the main window, /edm config opens Settings)
- Font dropdowns now only show fonts with full Cyrillic AND special character (äöüéèàñß) support —
  player names with accents or Cyrillic letters no longer appear as missing-glyph boxes. Detection
  uses width-ratio tests (Cyrillic "Ж" must be ≥ 1.5× wider than "I"; accented Latin chars must
  differ from uniform notdef boxes). Currently-saved fonts that don't pass remain visible with a
  "(missing glyphs)" hint so users can change them.

Settings Panel Redesign (visual only — no functional changes)
- Larger, more readable section headers with bottom divider line and breathing room between groups
- Section titles now use mixed case + larger 12pt outlined font (was 9pt uppercase)
- Sidebar nav: bigger tabs (42px), larger icons + labels, animated hover state, glow + accent strip on selected
- Title bar redesigned: 42px tall, gradient accent glow line, logo dot, separate subtitle
- Reset Data / Reload UI buttons larger and more polished (32px, gradient hover)
- Slightly wider panel (820x620) for better content density
- Row heights and label fonts bumped for readability (toggles 28px, sliders 42px, dropdowns 30px, colors 28px)
- Softer alternating row backgrounds — less visual noise
- Universal row hover highlight on every row (toggle/slider/dropdown/color/dual)
- Each row has a subtle blue left-edge accent that ties section content together visually
- Larger toggle switches (34x16) and color swatches (40x18)
- Color swatches now glow with accent border on hover
- Dropdown buttons highlight with accent border on hover
- Slider value labels now outlined for better visibility
- Wider dropdowns (180px) with more legible 10pt text

Version 2.2
-------------
Profile System
- Simplified profile UI (Details-style: dropdown switch, create, copy, delete — no collapsible sections)
- Profiles now save/restore window position, size, scale and display mode per instance
- Multiple windows fully supported: each window remembers its own layout per profile
- Fixed "Profile not found" error (dropdown passed array index instead of profile name)
- Reset Profile preserves window positions

Font System
- Font Face and Font Style changes now trigger a confirmation popup + UI reload (required for WoW to apply)
- Font validation: only fonts that actually render text are shown in the dropdown
- Runtime font discovery from Blizzard font objects (WoW 12.0+ compatible — no more hardcoded broken paths)
- Fonts apply correctly to all bars including new bars created during combat (cached font system)
- Removed broken font registrations (2002, Morpheus, Skurri paths that no longer exist in TWW)

Names & Display
- Fixed names not showing on bars (dirty-check was skipping name updates when values unchanged)
- Fixed realm names showing on bars ("Player-Realm" now correctly stripped to "Player")
- Fixed special characters in names breaking color formatting (switched from string.format %s to concatenation)
- NickTag nickname support (Details-compatible — shows nicknames set in Details, configurable toggle)

Pets
- Merge Pet Damage option now only available on Classic/TBC/Wrath/MoP (toggle ON/OFF)
- Retail always merges pet damage (Blizzard's C_DamageMeter API pre-merges pets — cannot be separated)
- Classic: pet damage correctly merged into owner or shown separately depending on setting

Graph
- Graph v3: Premium visual redesign with layered glow effects, live pulse dot, gradient fills, bottom info bar
- Graph now reads data directly from the tracker (same data source as bars — fixes Retail SecretValue issue)
- Graph data buffer uses batch trim instead of repeated table.remove (performance fix)

Data Persistence
- Fixed damage data disappearing after combat ends (C_DamageMeter sync no longer zeroes out segment when API returns empty)
- Damage bars now persist between fights, only reset when new combat begins

Stability & Internals
- Removed all debug print() statements from production code (converted to Utils.Debug)
- Profile errors now show popup dialogs instead of chat messages
- Profile success messages removed (silent operation)
- Database nil guards: safe instance info lookup, timeline nil check
- Database timeline buffer uses batch trim (same as Graph)
- DetailWindow C_DamageMeter API calls wrapped in pcall (prevents errors on expired sessions)
- Font test FontString reused instead of leaking a new one each time
- Event registration wrapped in SafeRegisterEvent with debug logging on failure
- WotLK Reforged support (new TOC file, Interface: 30800)
- TOC versions updated to 2.2 across all game versions

Version 2.1
-------------
- Fixed window width/height not applying on login (ApplySettings now called after UI initialize)
- All font SetFont calls protected with pcall fallback (prevents broken text on invalid font paths)

Version 2.0
-------------
- Unified addon: Single folder supports Retail (12.0.1), Classic (1.15.8), TBC Anniversary (2.5.5), MoP Classic (5.5.3)
- Multi-TOC architecture with runtime API detection
- Retail WoW 12.0 support: C_DamageMeter API integration with Secret Values handling
- Real-time combat data display on Retail (renders directly from C_DamageMeter during combat)
- Fixed font, font size, and font flag changes not applying to bars
- Fixed Show Percent toggle not working
- Fixed Show Rank toggle not working
- Fixed graph not displaying data on Retail (now queries C_DamageMeter for totals)
- Fixed window scale not applying on reload (value saved but not rendered — reported on TBCA)
- Scale now saved per-character and re-applied in ApplySettings
- Fixed Current/Overall tabs both showing overall data
- Fixed combat timer stuck at 0:01
- Fixed left-click toggle window not working
- Fixed combat duration not accumulating across multiple combat phases
- Show Fight Duration toggle now works in status bar
- Separated DPS from Damage Done display (DPS shows per-second + percent, Damage Done shows total + DPS)
- Separated HPS from Healing Done display (same distinction)
- Fixed sorting not updating during combat (insertion sort with per-comparison pcall for secret values)
- Detail window opens on bar click with spell breakdown
- Mouseover tooltips on bars showing DPS/HPS and top abilities
- Graph anchors to main window instead of UIParent
- Graph collects data during combat even when hidden
- Isolated Ace3 libraries (-EDM suffix) for all clients
- Updated Twitch link to twitch.tv/JugoBetrugoTV
- Removed interface version from config display
- Performance: Rewrote Retail combat rendering to use GetCombatSessionFromType (1 API call for all players)
- Performance: Eliminated per-player roster building and per-GUID API queries (40→1 calls in BGs)
- Performance: Uses amountPerSecond from API for DPS/HPS (no manual division needed)
- Performance: Uses session.maxAmount + SetMinMaxValues for secret-safe bar proportions
- Performance: Spec icons from API (specIconID, NeverSecret) instead of generic class icons
- Performance: isLocalPlayer (NeverSecret) for player highlight instead of UnitName comparison
- Performance: Dirty-check rendering (skip GPU calls when bar data unchanged)
- Performance: Cached settings per update cycle (avoid per-bar profile lookups)
- Performance: Insertion sort for secret values during combat (per-comparison pcall)
- Performance: Roster cache with 3s TTL (avoid rebuilding raid roster every tick)
- Performance: Minimum 0.5s refresh rate clamp (prevents excessive update frequency)
- Performance: LayoutBars skip when bar count/scroll/height unchanged
- Performance: Color/texture updates only on class or rank change
- Performance: Name text updates only on actor name change
- Performance: Icon texture updates only on class change
- Performance: C_Timer polyfill, bit-lib compatibility, tainted value detection

Version 1.1.0
-------------
- Added multi-client support (Retail, Classic Era, MoP Remix)
- Interface versions: 110207 (Retail), 11508 (Classic), 50503 (MoP)
- Fixed Damage Taken mode to correctly show damage received
- Fixed DPS display calculations
- Improved click handler for damage bars
- Enhanced graph smoothing with Catmull-Rom spline interpolation
- Performance optimizations:
  - Localized globals across all modules for faster lookups
  - Added UI update throttling (100ms) to reduce CPU usage
  - Implemented table reuse to reduce garbage collection
  - Pre-computed bit masks for flag checks
  - Added class color caching

Version 1.0.0
-------------
- Initial release
- Real-time damage and healing tracking
- Beautiful graph visualization with animations
- Skin system support
- Detail window with spell breakdown
- Minimap button integration
- Combat segment tracking