File Details
v1.9.2
- R
- May 9, 2026
- 402.21 KB
- 0
- 12.0.5+3
- Classic + 2
File Name
Carrot-v1.9.2.zip
Supported Versions
- 12.0.5
- 12.0.1
- 2.5.5
- 1.15.8
Changelog
v1.9.1
Prey UX
- "KILL SOMETHING!" CTA styling. Prey-affix timer rows that ship with an instruction (currently Bloody Command —
rec.instruction ~= nil) get bolder treatment than data-display rows: bar height 14→22, label font 11→14 OUTLINE, label uppercased, and an always-on gentle alpha pulse (sin × 3, range 0.7–1.0). The existing sub-3s desperation pulse (sin × 7, range 0.55–1.0) still kicks in over the top in the final seconds. Non-CTA timer rows (Torment, etc.) keep their current layout. - Focus Mode keeps Prey alongside Scenario. Hardcoded
key ~= "scenario"replaced with aFOCUS_MODE_KEEP = { scenario = true, prey = true }allow-list. When Focus Mode auto-engages on a public-event scenario in the same zone as an active prey hunt, both sections stay visible — burying the prey row under "scenario only" was the original UX bug inZonefail-open on active affix.EvaluatePrey's zone gate used to bewidget broadcasting OR mapID match; both signals can transiently miss in flight form (GetBestMapForUnitbriefly returns a parent map). Added a third signal: an active prey-affix debuff on the player (Bloody Command / Torment / Bloodsworn). Blizzard only applies these inside an active hunt encounter, so their presence is unambiguous proof you're in the hunt context- Affix scan no longer gated on
isComplete. Earlier "suppress affixes when complete" gate caused the bar to vanish during real combat:C_QuestLog.IsComplete(questID)flips true the moment you engage the final-stage encounter on a Nightmare hunt, while the CTA debuffs are still actively threatening you. Brief residual debuffs after turn-in are bounded by theUNIT_AURA → CT:Refresh~100–300ms cleanup path
Appearance tab UX overhaul
Layoutsub-group removed.rowHeightandpaddingmoved intoGeneralunder a newSpacingdivider;headerHeightmoved intoSection Headersunder a newLayoutdivider. The emptyLayoutnode was clutterGeneralsub-group internally organized with five header dividers:Frame,Tier Colors,Quest Type Colors,Spacing,Behavior. TheBehaviorblock holdshoverHighlight+ color, the two text-overflow selects, andcolorizeSubHeadersFontsub-group internally organized with three header dividers:Base(face + 3 sizes),Per-Element Overrides(3 selects),Outline & Shadow(outline, shadow toggle, shadow color, shadow offset X / Y)- Per-widget sub-groups (
Quest Rows,World Quest Rows,Achievement Rows,Scenario Rows,Recipe Rows) each getTitleandObjectivedividers between their override-pair blocks. The override toggle labels shorten to plainOverridesince the parent header now disambiguates Section Headerssub-group internally organized withLayout(height),Font(override + size + face),Colors(3 colors)Progress Barssub-group gains the height slider.progressBarHeightmoved here from the deletedLayoutnode so the bar's height lives next to its color knobs in one place
Bug fixes
- Secret-value
pcallprotection. Some auras (PvP, scripted encounters, certain scenarios) ship with field values marked secret by the server. Reading or comparing them throws a Lua error AND taints Carrot's call stack — which cascades intoADDON_ACTION_BLOCKEDon the next tracker refresh'srow:Hide()(16× failures observed in the wild from a single restricted aura).ScanPreyAffixAurasand the/carrot preydiagnostic now wrap per-aura field access inpcall, so secret auras get silently skipped instead of taking out every subsequent UI mutation in the same call chain. The aura iteration also distinguishes "end of aura list" (okA and not aura) from "secret aura at retrieval" (not okA), so a secret slot can't truncate the scan and miss prey-affix slots beyond it. Prey-affix detection is unchanged for non-secret auras (Bloody Command / Torment / Bloodsworn aren't restricted)
v1.9.0
Major: Per-widget appearance overrides + Appearance-tab tree layout
- New
CT.db.appearance.widgets[<widgetKey>]table holds optional per-widget overrides forfont/fontSize/fontOutlineper role (title/objective/progressLabel/header). All values default tonil; the newCT.Utils.WidgetGet/WidgetSetaccessors andApplyFontFor(fs, widgetKey, role)resolver inTrackerFrame.luafall back through per-element appearance keys → base globals so a user with no per-widget customization sees identical output to v1.8.0 - Six widget types wired through
ApplyFontForat row creation:questRow,worldQuestRow,achievementRow,scenarioRow(re-applied on pool checkout inGetScenarioRow),recipeRow(now re-applies on pool checkout — previously fonts leaked across pool reuse),sectionHeader. Twenty-one wired call sites total - Appearance tab restructured into a left-rail tree (
childGroups = "tree"). Six core sub-groups (General,Tab Bar,Objectives,Font,Layout) plus six per-widget sub-groups (Quest Rows,World Quest Rows,Achievement Rows,Scenario Rows,Recipe Rows,Section Headers). Each per-widget sub-group exposes title + objective override toggles + size ranges +__default__-sentinel font selects - Colors tab absorbed into Appearance —
Carrot_ColorsBlizzard sub-category deregistered. Frame / tier / quest-type colors land inGeneral(withFrame,Tier Colors,Quest Type Colorsheader dividers); section header colors land inSection Headers; newProgress BarsandMythic+sub-groups host their respective color knobs (M+ hidden viaHiddenUnless("mPlus")on flavors without it).CT.db.colorsstorage paths unchanged — pure UI relocation - Per-widget override toggles render side-by-side with their value sliders (
width = "half"on toggle+range pairs across all 6 widget sub-groups, 22 widgets total) - AceGUI Flow layout re-registered with a 6px inter-widget horizontal gap per the hook-after-load policy (never modify Libs/). Surfaces breathing room between adjacent options widgets across the entire UI session
Major: Zone-aware quest bucketing + collapsible "Other Quests"
BuildQuestEntrynow setsentry.inZone = zoneQuestIDs[questID] == truefromC_QuestLog.GetQuestsOnMap(currentMapID). The Quests section render uses this signal to surface only zone-relevant buckets at the top, demoting global-tracking clutter under a collapsible parent- Top-level (always visible): Pinned, Important (in-zone), Ready to Turn In (in-zone), Zone Quests, Campaign (in-zone). Pinned bypasses zone filtering entirely (user pin is intentional)
- "Other Quests (N)" collapsible parent header groups the demoted buckets — Important (elsewhere), Ready to Turn In (elsewhere), Campaign (elsewhere), Dailies (always), Weeklies (always), Other tracked. Click to expand/collapse; count badge always visible. State persisted in
CT.db.collapseOtherQuests, reset totrueon every/reloadso the tracker boots clean — the user explicitly opts into the noise per session CT.SUBCAT_LABELSextended with zone-aware variants (complete_inZone,complete_elsewhere,important_inZone/elsewhere,campaign_inZone/elsewhere,normal_inZone→ "Zone Quests",normal_elsewhere→ "Other Quests",otherParent→ "Other Quests")- Backward-compat layer in the renderer: existing
winConfig.subCategoryOrderconfigs are detected — exact match on the legacy default falls through to the new spec ordering; customized legacy orders get key-mapped to_inZonevariants. Daily/Weekly references in old configs are dropped (those buckets are now Other-only)
Major: Default section order — context-relevant first
- Section registration order in
TrackerFrame.luareordered so a fresh tracker boot reads top-to-bottom in priority order:scenario,prey,worldQuests,bonusObjectives,quests,events,recipes. Previouslypreywas at the bottom, which buried the most contextually-relevant section (active prey hunt) under tracked quests - Existing-user migration:
IsLegacyDefaultSectionOrderhelper + check inMigrateWindowdetects tabs whosesectionOrderexactly matches the legacy default and replaces them with the new default. Customized orders are preserved untouched
Bug fixes
- Progress-bar achievements now show their progress count + bar fill (e.g. "Look I'm Just Trying to Fish Here" — 46/100 fish caught while in Prey Nightmare).
BuildAchievementEntrynow capturesdescription(8th return ofGetAchievementInfo),flags(7th), andquantityString(9th) fromGetAchievementCriteriaInfo; the row renderer detectsEVALUATION_TREE_FLAG_PROGRESS_BAR(flags & 0x1) and surfaces the X/Y count inprogressText, the fraction-based fill in the bar, and the achievement description on the criterion line. Previously these achievements showed as "0/1 criteria" with an empty bar until earned. The "recommended" tier promotion also reads the progress fraction now, so an 85% fish counter promotes correctly - World Quest percentage label ("8%" / etc.) now renders centered on the bar for percent-based WQs like "Disrupting the Void".
CreateWorldQuestRowgains aprogressLabelFontString (mirrors quest row pattern), andBuildTaskQuestEntryfalls back toGetQuestProgressBarPercent(questID)when any objective isprogressbar-typed andC_TaskQuest.GetQuestProgressBarInforeturns nil — Blizzard'sBonusObjectiveTracker.lua:503does the same. Without this,progressBarPctwas nil for some WQs and the bar showed at 0% until earned - WQ row no longer indents under a phantom item-use button when there's no group-finder eye. Title left-anchor re-anchors per-populate based on
entry.suggestedGroup(eye gutter when shown, flush-left when not) - Public-event scenarios with weighted-progress criteria (Defiled Relics of the Sedge / Void Strike sub-events) now surface as scenario rows. The legacy-dungeon skip guard at
ZoneEngine.lua:633previously dropped these because their single criterion hascriteriaType == 0andstepInfo.widgetSetID == nil— the same shape as stale Cata-era 5-man records. The guard now also requires "no criterion isisWeightedProgress" before skipping; a live weighted-progress criterion (e.g.quantity=64,totalQuantity=10000,quantityString="6464%") is unambiguous proof the scenario is real - Progress bar height now respects the font size of the centered label. Default
progressBarHeightbumped 4 → 12, and a runtime floor atBarHeight()(max ofprogressBarHeightandfontSizeSmall) ensures the percent label is never clipped above/below the bar even on legacy saved values. Option range raised: min 2 → 8, max 12 → 24
Prey UX
/carrot preydiagnostic dumps player HARMFUL auras (spellID, name, stacks, duration, remaining, source) so future prey-affix spellID captures don't need binary-edit guesswork- Real prey-affix spellIDs populated:
1245522Torment (stacks, +4% damage taken / stack),1245767Bloody Command (20s timer),1248214Bloodsworn (plain marker). Replaces the negative-key placeholders that never matched a real aura. Header comment inZoneEngine.luanow documents the full Midnight prey affix matrix (Normal Ambush, Hard Torment / Hunter's Momentum / Seeping Gore, Nightmare Echo of Predation / Bloody Command / Bloodsworn) and which carry player auras vs. surfacing through nameplate / on-death messaging only
v1.8.0
Major: Classic Era + TBC Anniversary support
Single codebase, per-flavor TOC. Capability-flag system gates every retail-only feature so Classic users never see Achievements / World Quests / Scenarios / M+ / Delves / Recipes / Events / Prey / Focus Mode rows or settings.
- Per-flavor TOCs:
Carrot.toc(Mainline 12.x),Carrot_Vanilla.toc(Classic Era 1.15+, interface 11508),Carrot_TBC.toc(TBC Anniversary 2.5+, interface 20505). Each flavor's TOC lists only the files that flavor needs — Classic doesn't loadZoneEngine/ProximityEngine/Widgets/QuestTracker/AchievementTracker, so retail-only API calls can't even reach the addon code paths on Classic Compat.lua— single source of truth for flavor detection (CT.Flavor.isMainline/isVanilla/isTBC/isWrath/isCata/isMists) and capability flags (CT.Flavor.has.cQuestLog,classicQuestLog,dailyQuests,weeklyQuests,campaignQuests,worldQuests,bonusObjectives,scenarios,mPlus,delves,achievements,prey,damageMeter,uiWidgets,recipes,eventWidgets,autoQuestPopups,focusMode, …). Every flavor check elsewhere routes through this table;WOW_PROJECT_IDis referenced only in this one file. Adding a new flavor is one row in this tableClassicEngine.lua— Classic-flavored quest evaluator usingGetNumQuestLogEntries+GetQuestLogTitle+GetQuestLogLeaderBoard. Extractsfrequency(position 7) so dailies/weeklies are tagged correctly on flavors that have them (TBC dailies, Wrath/Cata/Mists weeklies). Bucketing is subzone-aware (Coldridge Valley quests show even whenGetRealZoneTextreturns Dun Morogh); class/race-tagged quests (Warlock "Beginnings", etc.) land under "Other Quests"; pinned quests partition out into their own bucket regardless of zoneTF:PopulateClassicQuestSectioninTrackerFrame.luareuses retail's row pool, font helpers, and reset hooks. Subcategory rendering iterateswinConfig.subCategoryOrder(capability-filtered viaCT.SUBCAT_REQUIRES), so Pinned / Ready to Turn In / Daily / Weekly / Zone / Other Quests subheaders appear in the user's configured order with the right colors. Tier-bar paint and tier-line tooltip color matchColors.{pinned,complete,daily,weekly,recommended}so the legend, the bar, and the tooltip stay in sync- Right-click menu on Classic rows:
CM:ShowForClassicQuestopens a flavor-appropriate menu (Pin / Open Quest Log / Link in Chat / Stop Blizzard Watch / Abandon) using the legacy quest-log API. Previously right-click silently no-op'd because the shared row pool registered both buttons but Classic only handledLeftButton - Mojibake fixes: Classic's
FRIZQT__.TTFdoesn't carry the U+2713 (dingbats checkmark) or U+2588 (full-block) glyphs. Objective checkmarks now use|TInterface\\RaidFrame\\ReadyCheck-Ready:12:12|t; tier-color swatches in tooltips use|TInterface\\Buttons\\WHITE8x8:14:14:0:0:1:1:0:1:0:1:R:G:B|trecolored textures - Custom carrot icon shipped at
assets/carrot.tga(64×64 RGBA, exported from the existing PNG via PIL). Mainline keeps the in-gameINV_Misc_Food_Vendor_Carrotart (added in BfA 8.0); Classic flavors load the bundled TGA so the title bar / minimap button / addon-list icon all render correctly. Single source of truth viaCT.Assets.carrotIcon
Major: Data-driven section dispatch (Framework/SectionRegistry.lua)
- Sections register themselves declaratively via
CT.RegisterSection({ key, label, requires, defaultEnabled, evaluate, populate, count }). The framework walks the registry on every refresh; sections whoserequirescapability flag isn't satisfied are silently skipped at registration time so retail-only code paths never even register on Classic - Replaces four previously-out-of-sync dispatch points:
CT.SECTION_LABELS,DEFAULT_SECTIONS,TF:PopulateSection's if/elseif chain, andCOUNT_FNS. Adding a section is oneCT.RegisterSectioncall instead of editing four files in lockstep - Retail's eight sections (quests / worldQuests / bonusObjectives / scenario / achievements / recipes / events / prey) each register with appropriate
requirescapability flags. ClassicEngine'squestsregistration usesrequires = "classicQuestLog", mutually exclusive with retail'srequires = "cQuestLog"— only one is ever active per flavor Engine:Evaluate()now walks the registry instead of callingEvaluateQuests / EvaluateWorldQuests / ...in sequence.CT:DoRefreshcallsCT.SectionRegistry:RunEvaluators()directly so Classic (which doesn't loadZoneEngine.lua) follows the same evaluation pathCT.SECTION_LABELStable removed entirely. Each section's localized label lives in itsCT.RegisterSectioncall; consumers read throughCT.SectionRegistry:LabelFor(key)
Major: Unified CT.state.quests shape across engines
- New shape:
{ buckets = { <key> = list, ... }, context = { zone, subzone } }. Every engine — retailZoneEngine,ClassicEngine, futureWrathEngine— fills the same contract. Consumers (GetQuestCount,PopulateQuestSection,PopulateClassicQuestSection,ProximityEngine,QuestTracker:GetVisibleQuests, the prey-quest filter) iteratepairs(state.quests.buckets)which is type-stable - Fixes a HIGH-severity Classic bug:
pairs(CT.state.quests)previously walked the zone/subzone strings ClassicEngine stored alongside bucket lists and counted their byte lengths, which inflatedGetQuestCountand broke the minimized-window auto-expand check on Classic - Classic engine now produces buckets keyed by subcategory (
pinned / complete / daily / weekly / normal / elsewhere) so the renderer is a generic loop overCT.SUBCAT_REQUIRES-filtered keys. Adding a new flavor's engine = populate the buckets the flavor cares about; renderer code doesn't change
Major: Declarative migrations
MIGRATIONStable of{ id, requires, run }specs inCore.lua. Each migration'srun(db)returns true when it actually mutated state — the "done" flag is set only on a true return. Migrations whoserequirescapability flag isn't met on the current flavor stay un-marked, so they re-evaluate when the user moves to a flavor that does meet them- Fixes a HIGH-severity bug: gated migrations (scenarioSection / recipesSection / eventsSection / preySection / achievementsTab) previously set the "done" flag unconditionally, so importing a Classic-built profile onto Mainline permanently kept retail sections out of the user's tabs
flavorPrunepromoted from one-time migration to always-on invariant:PruneUnknownSections()runs on every load — strips section keys not in the registry on the current flavor, drops emptied tabs, hides single-tab UI bars. Cheap, idempotent, self-correcting across imports and flavor swaps- Profile import (Options → Import / Export) now confirms via
StaticPopup_Showshowing the active profile name (no more silent overwrite-on-misclick), wipes the active profile, and routes through a sharedCT:RebuildAfterProfileSwappath that runs migrations, prunes orphan sections, and rebuilds the tracker — same pipeline AceDB'sOnProfileChangedruns
UX
- First-launch defaults flipped for discoverability:
showComplete = true(turn-ins are the most actionable rows),showAchievements = true(was visible-but-empty by default),showTierBars = true(the tier indicator is one of the addon's killer features),bgColor = { 0, 0, 0, 0.2 }(subtle background so a fresh install has a discoverable frame edge),borderColor = { 0, 0, 0, 0 }(fully transparent — power users routinely turn it off) - Filters dropdown decluttered: removed
Show World Quests,Show Bonus Objectives,Show Scenarios,Show Achievements,Show Recipesrows. Section visibility is now owned exclusively by the Windows panel; the Filters dropdown only carries quest-type toggles (Important / Daily / Weekly / Campaign / Complete) and behavior (Recommended / Sort by Proximity). Each item gates onCT.Flavor.has.<flag>so Vanilla shows only Important+Complete, TBC adds Daily, Mainline shows the full set Important Questsfilter gated oncQuestLog(purple-! tag is aC_QuestLogfeature; Classic never carries it)- Quest Subcategory Order in the Windows panel iterates
CT:GetVisibleSubcatOrderagainstCT.SUBCAT_REQUIRES, so Vanilla shows 4 rows (Pinned / Ready to Turn In / Important / Zone), TBC adds Daily, Mainline shows all 8. Up/down arrows operate on visible neighbors but swap stored indices, so reordering on Classic doesn't bury Daily under hidden Campaign - Right-click context menu: thin divider added between
Stop TrackingandAbandon Questso an off-by-one click can't accidentally trigger the destructive action - Minimap right-click toggles the tracker (was registered but unhandled). Tooltip updated to advertise the binding
- Show Tier Indicator Bars tooltip (General tab) is now data-driven: legend rebuilds per-flavor with capability-gated entries, color swatches read from
CT.db.colors[<key>]so customizing colors updates the legend the next time the panel opens. Labels match the Colors panel's slot names exactly (Pinned / Complete / Recommended / Auto-Tracked / Daily / Weekly / Campaign / Important / Scenario / Delve / World Quest / Default) so a user jumping between tabs sees the same wording - Mythic+ color rows hidden on Classic in the Colors panel (M+ is Legion+; Classic never paints with those colors). Quest-type colors (Daily / Weekly / Campaign / Scenario / World Quest) gate on the same flags as their corresponding filter rows so Vanilla / TBC users only see colors that paint something on their flavor
- Dungeons & Instances options panel hidden on flavors without
focusMode. Vanilla / TBC / Wrath / Cata don't get the panel at all (it covers Focus Mode auto-trigger, M+, and Delves — none of which exist on those flavors). Re-appears automatically on Mists / Mainline - Tooltip tier line: hovering a quest now shows its tier label colored to match the tier bar (Pinned / Ready to Turn In / Daily / Weekly / Campaign / Important / Recommended / Auto-Tracked / Zone / Other Quests). Single source of truth via
CT.GetEntryTier(entry, isCurrent)— same priority order the renderer uses to color the bar - Pinning works end-to-end on Classic: the engine flags
entry.isPinned(consultingCT:IsQuestPinned) and partitions pinned quests into a dedicatedpinnedbucket. Renderer surfaces them under a single "Pinned" subheader regardless of zone. Tier color usesColors.pinned(blue), matching retail showTierBars = falseactually hides the bars on Classic:PopulateClassicQuestSectionwas missing theApplyTierBar(row)call that retail populate paths use. Toggling the option now affects Classic rows correctly
Bug fixes (also)
CT:RegisterEventvalidates events viaC_EventUtils.IsEventValidbefore registering — retail-only events (TRACKED_RECIPE_UPDATE,CONTENT_TRACKING_UPDATE,CHALLENGE_MODE_*) silently no-op on Classic without per-callsite guards. Deferred-event flushes inPLAYER_LOGINandPLAYER_REGEN_ENABLEDare pcall-wrapped as belt-and-suspenders- Legacy dungeons no longer render a stuck "0/1 boss defeated" scenario row: pre-scenario five-mans (Lower Blackrock Spire and other Vanilla / TBC / WotLK / Cata dungeons) surface through the 12.x unified
C_ScenarioInfoAPI but their criteria are stale records — every entry hascriteriaType == 0,quantity == 0,completed == falseeven after every boss is dead. Blizzard's ownScenarioObjectiveTrackerdecides not to render these blocks (every child hasshown = false); we now do the same. Detection requires ALL of: at least one criterion with everycriteriaType == 0, ANDstepInfo.widgetSetID == nil(real scenarios like Abundance carry a step set with the timer + bag bar; losing them blanks the section entirely), AND not in M+ challenge mode, AND not in a tiered-entrance scenario, AND not in an active Delve viaC_DelvesUI.HasActiveDelve. Any one of those failing means it's a real scenario we should render — narrow gate to keep Abundance / Ritual Sites / Corrupted Visions / Delves / M+ keys all working - Delve cluster lingered after entering M+: transitioning Delve → Mythic+ left the delve heart icon, currency rows, and spell icons anchored to the scenario section's scrollChild from the prior session. Cause:
PopulateScenarioSectiononly invokedRenderScenarioWidgets(which owns the event-widget frame pool'sReleaseUnusedEventFramescall) whenscenario.widgetSetIDwas truthy. M+ scenario steps commonly have a nilstepInfo.widgetSetID, so the entire render+cleanup pass was skipped and the pool kept the stale delve frames. Fix: always callRenderScenarioWidgets; its early-returns now also callTF.ReleaseAllEventFrames(section)and clearCT.state.scenarioBlockWidgetSetsso no stale frames or widget-set push acceptance carries across scenario types - Tooltip taint cascade — completed v1.7.3 migration: every Carrot OnEnter / OnLeave handler that wrote to Blizzard's shared
GameTooltipnow writes to our privateCarrotTooltipframe (CT.Utils.GetTooltip()). v1.7.3 introduced the safe path for one call site (delve cluster currency rows) as a diagnostic; the rest of the addon kept writing toGameTooltip, leaving residue inprocessingInfo/infoList/widgetContainerstate. During scenarios with active widget sets — particularly Void Assaults in Voidstorm (widgetSetID 2042) — the cascade fired 1000+ times per session as world-quest pin OnLeave handlers ranGameTooltip:Hide → ClearWidgetSet → UnregisterForWidgetSet → DefaultWidgetLayout → LayoutFrame:Layoutin a still-tainted execution context, tripping on secret-value comparisons. Migrated 22 handlers acrossWidgets.luaandTrackerFrame.lua(event widgets, scenario criteria, world quest rows, achievement rows, prey rows, M+ affixes/deaths, recipe / Auctionator buttons, classic quest rows, ...). The 3 remainingGameTooltipwrites (toolbar buttons + minimap button) are low-frequency and outside the scenario render path, so they stay on the shared tooltip to keep their behavior consistent with other addons
Tooling
.luacheckrcallowlistsWOW_PROJECT_*flavor constants, Classic-flavored quest log API names (GetNumQuestLogEntries,GetQuestLogTitle,GetNumQuestLeaderBoards,GetQuestLogLeaderBoard,SelectQuestLogEntry,QuestLogFrame,RemoveQuestWatch,SetAbandonQuest),C_EventUtils, andStaticPopupDialogsMakefilelint target picks up the new files (Compat.lua,Framework/SectionRegistry.lua,ClassicEngine.lua)- All 16 lint targets clean; stylua format check clean
v1.7.5
Feature: Hard / Nightmare Prey Hunt Affixes Surface in the Tracker
- Hard and Nightmare prey hunts apply player debuffs (Bloody Command's "kill in 20s or take a DoT", Torment's stacking +damage-taken, Echo of Predation, Seeping Gore standing-in-it). Blizzard's default UI shows these as anonymous icons in the buff bar; the tracker now surfaces them inline on the prey row so the relevant context lives next to the quest you're working on
- Stacking / plain affixes (Torment etc.) render as 16x16 icons right-aligned on the prey title row with a stack count overlay. Tooltip on hover shows the spell description (read via
C_Spell.GetSpellDescription— noSetSpellByIDtaint vector) - Timer affixes (Bloody Command etc.) render as a thin horizontal bar below the stage bar that depletes green → amber → red as time runs out, with the affix icon on the left and the instruction text + remaining seconds inside ("Kill something! 5s"). Pulses alpha in the last 3 seconds for "act now" emphasis. Multiple active timers stack vertically
- Curated affix table (
CT.PREY_AFFIXESinZoneEngine.lua) maps spell IDs → render hints (mode,instructionKey,textFmtKey,perStack). Spell IDs are TODO until captured in-game — Blizzard doesn't publish them on Wowhead. Capture path documented at the table. Affixes that don't apply a player aura (Echo of Predation as an NPC, Hunter's Momentum as a death penalty) cannot be tracked from this table - Aura scan is gated on prey being active so the
UNIT_AURA(player)listener stays dormant outside Hard / Nightmare hunts (it was previously Delve-only — no extra event traffic in non-Delve / non-prey content)
v1.7.4
Bug Fix: Manual Focus-Mode Override Persists Across Arena/Abandon
- A manual focus-mode toggle (toolbar focus button) writes a hard
true/falsetoCT.db.focusMode. The only reset path wasSCENARIO_COMPLETED, which doesn't fire when the scenario ends via arena/BG entry, hearth-out, manual abandon, or relog. Result: clicking focus-on inside a Delve / M+ left focus stuck on after dropping the run for an arena and returning to the world - Fix (transition reset):
EvaluateScenarioinZoneEngine.luanow revertsfocusModeto"auto"wheneverCT.state.scenariogoes from non-nil to nil — covers all "scenario ended without firing SCENARIO_COMPLETED" paths in one place. Demo data is exempt - Fix (catch-all):
PLAYER_ENTERING_WORLDinCore.luaalso reverts a hardtrue/falseoverride to"auto"whenIsInInstance()is false. Catches the in-town toggle path and relog-in-city paths where the scenario state never became active to begin with - Both resets are no-ops when
focusMode == "auto", so the normal auto flow is unchanged
Bug Fix: Challenges Row No Longer Shows in Regular Delves
- v1.7.0's "Challenges (N)" row was rendering in every regular Delve as
Challenges (0)— Delves are technically tiered-entrance scenarios perC_ScenarioInfo.IsTieredEntranceScenario, but they don't have a player-selectable challenge system (only Ritual Sites Tier 3+, Corrupted Visions, and the old Torment scenarios do) - Fix: gate the row on
n > 0instead of justIsTieredEntranceScenario. Hides cleanly in Delves (always 0), in Ritual Sites Tier 1-2 (challenges not yet available), and shows in Ritual Sites Tier 3+ / Corrupted Visions when the player has actually picked challenge spells. Tradeoff: the "Pick some at the Curious Obelisk" hint at low Ritual Site tiers no longer surfaces — a worthwhile trade vs. cluttering every Delve
Bug Fix: Weekly Profession Quests Now Bucket Under Weekly
Enum.QuestFrequencyin 12.x has four values (Default,Daily,Weekly,ResetByScheduler), and many "weekly" profession quests, world-boss kills, and other newer time-gated quests useResetByScheduler— they reset on Blizzard's central scheduler rather than via the legacyWeeklyflag. OurisWeekly = (frequency == Weekly)check missed them, so they appeared under the Zone subcategory instead of Weekly- Fix:
isWeeklynow also matchesResetByScheduler. Profession weeklies, world boss weeklies, and similar appear under the Weekly subheader where users expect
v1.7.3
Diagnostic: Currency Tooltip Uses Custom Frame
- Routes the highest-frequency hover handler (Ritual Site / Delve currency icons) through a private
CarrotTooltipframe (ownGameTooltipTemplateinstance) instead of Blizzard's sharedGameTooltip. Eliminates any state-pollution path from ourSetText/AddLinewrites into Blizzard'sprocessingInfo/infoList/widgetContainertables that get reused on subsequent world-cursor unit tooltips - Diagnostic in nature: if the secret-value taint cascade through Oilvl / BestInSlotRedux's tooltip hooks (
SetWorldCursor→ProcessInfo→UnitName(unit)failures) drops measurably after this, we'll expand the same pattern to every other Carrot OnEnter handler. If error rate stays the same, it confirms the cascade source isn't us - New helper:
CT.Utils.GetTooltip()returns the singletonCarrotTooltipframe;CT.Utils.AddWrappedTooltipLinenow writes there too. Callers that want to keep usingGameTooltipfor richer tooltips (e.g. quest reward tooltips) still can
v1.7.2
Bug Fix: Two More Tooltip Pipeline Taint Vectors
- v1.7.1 fixed
GameTooltip:SetSpellByIDbut missed the same pattern in two other places. Users still saw 200+LayoutFrame.lua:491: attempt to compare a secret number valueerrors per session, surfacing through other addons' tooltip hooks (Oilvl, BestInSlotRedux, etc.) on world-cursor hover. Both addons readUnitName(unit)from tooltipData where the unit is now a 12.x secret value — they only error when the execution context is tainted, and our remainingSet…ByIDcalls were the upstream taint - Fix: replaced the two remaining tooltip-pipeline calls with safe non-pipeline equivalents:
Widgets.luaRenderItemDisplayWidget— wasGameTooltip:SetItemByID(itemID), now readsC_Item.GetItemInfofor name + quality color and renders viaGameTooltip:SetText. No widget pipeline involvementTrackerFrame.luaquest item buttons — wasGameTooltip:SetHyperlink(itemLink), now extracts the item name viaC_Item.GetItemInfo(itemLink)(accepts links too), tinted by quality color
- Tradeoff: tooltips on quest item buttons and event item displays now show name + quality only, not the full item tooltip with stats / requirements. We trade rich tooltip content for zero taint cascade — the pipeline calls couldn't be made safe without polluting
GameTooltip's widget container state
v1.7.1
Bug Fix: Tooltip Pipeline Taint Cascade
- Hovering Delve spell icons (Nemesis, tier perks, etc.) was tainting Blizzard's GameTooltip widget pipeline, surfacing as 800+ errors per session of
LayoutFrame.lua:491: attempt to compare a secret number value (execution tainted by 'Carrot')on every subsequent tooltip hide. Stack:GameTooltip:OnHide→GameTooltip_ClearWidgetSet→UnregisterForWidgetSet→UpdateWidgetLayout→DefaultWidgetLayout→ secret-value compare - Root cause:
GameTooltip:SetSpellByID(spellID)routes through Blizzard's sharedTooltipDataHandlerpipeline AND can register a UIWidget set on GameTooltip when the spell ships embedded widgets (observedwidgetSetID = 1801). When our subsequent tooltip operations cleared the tooltip on the next hover (SetOwner/ClearLines), the registered widget set got unregistered from our addon's tainted execution context — propagating taint into Blizzard's widget layout path - Fix: replaced all three
GameTooltip:SetSpellByIDcall sites with aShowSpellTooltip(owner, spellID)helper that uses plainC_Spell.GetSpellInfo+C_Spell.GetSpellDescriptiongetters and builds the tooltip viaAddLine. No widget pipeline involvement, no taint. Same pattern the Challenges renderer already uses. Affected paths: genericSpellDisplaywidget hover, Delve scenario header tier-perk hover, and Delve per-spell-icon hover - CLAUDE.md flagged this exact pattern under tooltip-pipeline taint warnings, but our older Delve code predated the warning
Bug Fix: Scenario Criterion Row Heights Now Uniform
- Boss criteria rows in regular (non-M+) dungeon scenarios were rendering with inconsistent heights when some titles fit on one line and others wrapped to two. With
wrap=true(inherited from pool checkout via the user'stitleOverflow=wrappreference),GetStringHeightreturned 2-line height for the wrapping titles, but WoW's render layer auto-truncated visible text to one line with…— leaving an empty band inside the longer rows that read as uneven gaps between criteria - Fix: scenario criterion titles now explicitly
SetWordWrap(false), mirroringMPlusTextRow's same-line policy and Blizzard's tracker behavior for criterion lines (truncate, don't wrap). Pure-text criterion descriptions like "Arcanotron Custos Defeated" / "Gemellus Defeated" now sit at uniform heights regardless of which fit on one line at the current row width
v1.7.0
New Feature: Focus Mode
- Toolbar Focus Mode toggle (next to the filter funnel). When on, every section except the active scenario / Delve / M+ is hidden so the tracker becomes a single-purpose readout. Custom crosshair icon (gold = on, gold-with-slash = off) shipped at
Textures/focus_{on,off}.tga— TGA chosen after WoW's PNG loader returned a "missing texture" placeholder for our addon-shipped PNGs even when the files were structurally valid (correct IHDR, no metadata chunks) - Tri-state
focusMode:"auto"(default) /true(manual on) /false(manual off). The toolbar button cycles between true/false and bypasses auto;SCENARIO_COMPLETEDresets to"auto"so each new entry starts clean.IsFocusModeActive()is the single source of truth used by both the renderer and the icon-state lookup - Auto-enable in instances option (default on) + per-content overrides (Delves / Mythic+ / Scenarios) on the Dungeons & Instances tab. Lets users opt out of auto-focus per content type ("auto in M+ but not in Ritual Sites")
- Migration from the legacy per-content
hideOtherSectionstoggles into the unifiedfocusOverridesmap; old keys nilled out viamigrations.focusModeUnify - Dynamic toolbar tooltip flips between "Turn Focus Mode On" / "Turn Focus Mode Off" with a description below
New Feature: Ritual Site / Tiered Entrance Challenges
Challenges (N)row under the scenario block wheneverC_ScenarioInfo.IsTieredEntranceScenario()returns true (Ritual Sites, Bountiful Delves, etc.). Count comes from#C_ScenarioInfo.GetTieredEntranceActiveSpells()— refreshes onSCENARIO_SPELL_UPDATEso adding/removing challenges at the Curious Obelisk reflects without a/reload- Spell icon flow below the count when N > 0: each active challenge renders its
C_Spell.GetSpellTextureicon in a horizontal row that wraps on overflow. Hovering an icon shows the spell name + description viaC_Spell.GetSpellInfo+C_Spell.GetSpellDescription— deliberately notSetSpellByID, since that goes through the taintingTooltipDataHandlerpipeline - N=0 case shows the title row alone with a "no challenges active, set them at the Curious Obelisk" hover hint
New Feature: Failed Achievement Criteria Render Red
- In-instance meta achievements (no avoidable damage, no deaths, etc.) where a criterion is failed mid-run now render their failed lines in red — both the bullet icon and the text. Mirrors Blizzard's
AchievementObjectiveTrackerbehavior - Wired through
GetAchievementCriteriaInfo's 11th return (eligibleboolean) —failed = (eligible == false)— and threaded into the existingRenderObjectiveLinescolumnar renderer. Refresh hooks added for the genericCRITERIA_UPDATEevent so eligibility flips reflect immediately
New Feature: Title Bar Carrot Icon
- Window title now shows a 14×14 carrot icon to the left of the name, rendered as a real
Texturechild of the title bar. The previous inline|T...|tfont escape was getting clipped by the small-font line height (~10px), so it would render at the wrong size or not at all on some setups - The per-window "Hide Icon" option still works; the title text re-anchors flush-left when the icon is hidden
Bug Fix: Currency Tooltips Wrap Correctly
- Hovering Spoils / Deaths / Delve currency icons now wraps the description across multiple lines instead of stretching as one wide line. Switched to Blizzard's own helpers (
SplitTextIntoHeaderAndNonHeader+GameTooltip_SetTitle+GameTooltip_AddNormalLine) — the same code pathUIWidgetTemplateTooltipFrameMixin:OnEnteruses for the same widget tooltip in the default UI. Whatever font / template scaffolding Blizzard requires forwrap=trueto actually engage onAddLine, this path inherits it
Bug Fix: Scenario Step Transitions No Longer Collapse Spacing
- Scenario row pool checkout (
GetScenarioRow) now restores the title's default anchor (TOPLEFT, padding+4, -3andRIGHT → timeText.LEFT) on every reuse. The Challenges row's custom anchor was leaking back into pooled criterion rows, causing visible spacing collapse the first time a scenario advanced to a new step. Mirrors the existing checkout resets forSetWordWrap,SetIndentedWordWrap, font, and overflow
Bug Fix: Spoils / Deaths Currency Value Shows Correctly
RenderScenarioHeaderCurrenciesWidgetnow readscur.textfirst (Blizzard's pre-formatted display string like"120"), falling back tocur.quantityonly when text is absent. Previously we looked forcur.quantity/cur.currentAmountfirst — neither exists on theScenarioHeaderCurrenciesAndBackgroundwidget — and fell into theelsebranch that rendered0. User reported Spoils displaying as0while the real value was 120- Unbounded counters (no
max) skip the empty-bar render entirely; label re-anchors flush-left next to the icon for a compact[icon] 120look. Currencies flow horizontally with wrap-on-overflow; leftmost icon respects the samepadding + 4gutter as text rows above
Bug Fix: Show Progress Bars Toggle Affects Scenarios
- The "Show Progress Bars" toggle now also hides scenario criterion bars (previously only quest / WQ / achievement bars). When off, criteria render Blizzard-style as
2/3 Captured Void Creatures Slainin the title — the count is preserved, just inline instead of in a bar. Row collapses to title-only height so spacing stays tight. M+ and prey bars stay always-visible (gameplay-critical state)
Bug Fix: Scenario Criterion Title Hangs Indented Under Itself
- Wrapped scenario criterion titles now hang-indent their continuation lines under the text portion (e.g. "Lady" / "2/3"), not under the bullet icon — same visual style
RenderObjectiveLinesalready uses for quest objectives. One-line fix viaFontString:SetIndentedWordWrap(true)on pool checkout
Other
- Step description text brightened from
0.75to0.95gray — was reading too faint against the green tracker bg at default UI scale SCENARIO_ROW_GAPreduced from 6 → 2 px so criteria don't feel cramped with bars on but also don't have huge empty space when bars are off- New
/carrot diagcommand — printsCT._addonName, computed texture paths,IsAddOnLoadedresults, and current focus state; canonical tool for "icon doesn't render" / "wrong folder name" investigations - New
CT.Utils.AddWrappedTooltipLine(text, r, g, b, charsPerLine)helper for any future tooltip that needs reliable multi-line rendering - New
CT.Utils.WrapText(text, maxLen)— manual word-wrap utility used internally by the tooltip helper CT._addonNameexposed (set from...first vararg) so texture paths resolve against the actual install folder name (Carrot vs wow-carrot, etc.) without a hardcoded constant. Avoided adding new file-level locals — Lua VM has a 200-per-chunk limit andTrackerFrame.luawas at the boundary
Internal: Code Organization
- Extracted ~2.8K lines of toolbar + widget rendering code out of
TrackerFrame.luainto dedicated files:Toolbar.lua(~350 lines: settings gear, filter dropdown, focus mode toggle, zone text) andWidgets.lua(~2.5K lines: 25+ UIWidget renderers, dispatch table, dual ticker setup, event-widget frame pool, currency/spell helpers). No behavior change — pure organizational cleanup.TrackerFrame.luanow ~6.2K lines (was ~9K) and focused on tracker frame, sections, layout, and refresh - Public surface for the extracted subsystems is exposed on
TF.*for cross-file calls (TF.CreateToolbar,TF.UpdateToolbar,TF.RenderEventWidget,TF.GetEventWidgetFrame, etc.); shared helpers likeTF.ApplyFractionalFill,TF.ComputeBarWidth, and the font helpers stay on the main file and are re-aliased at file scope inside the split modules
v1.6.1
Bug Fix: Important Quest Detection
- Important quest detection was calling the wrong API: the original implementation called
C_QuestLog.GetQuestClassification, which doesn't exist in 12.x — the guardand C_QuestLog.GetQuestClassificationreturned nil and the whole branch silently no-op'd, so no quest ever got flagged Important. Reported live by a user whose purple-!quests (e.g. Void Strike, An Elementary Voidcore) weren't appearing under the new Important subcategory - Fix: switched to
C_QuestInfoSystem.GetQuestClassification, the canonical accessor Blizzard's ownQuestUtil.GetQuestClassificationDetailsuses (seeBlizzard_FrameXMLUtil/Mainline/QuestUtils.lua). Theimportantavailablequesticonatlas is bound exclusively toEnum.QuestClassification.Importanting_classificationInfoTable, so the single-enum check is correct — it was just calling the wrong namespace
v1.6.0
New Feature: "Important" Quest Category
- Dedicated subcategory for Important quests (the purple-! classification) with its own subheader between "Ready to Turn In" and "Campaign". Detected via
C_QuestLog.GetQuestClassification(questID) == Enum.QuestClassification.Important(pcall-wrapped for older builds). Newimportantcolor slot on the Colors tab defaults to purple ({ 0.70, 0.30, 0.95 }) and drives the quest title, tier bar, and tooltip badge - Always visible: Important quests bypass the daily / weekly / campaign / complete filters and auto-promote to the
recommendedtier so they stay on the tracker even when the player is outside the home zone. The newshowImportantfilter defaults to true (vs. false for the other type filters) — the point of the Important flag is that the user shouldn't have to dig for them - One-shot
importantSubcatmigration splices"important"into each tab'ssubCategoryOrderfor existing installs so the section lands in the expected place without users reordering. After running once, users can remove it freely — the migration flag prevents re-insertion - Filter toggle added to the filter dropdown (at the top of the quest-type list); color picker added alongside daily / weekly / campaign on the Colors tab
New Feature: Tracker Lock
- Lock Tracker toggle in General options (or
/carrot lock). Titlebar drag and corner resize grip both short-circuit in their own handlers whenCT.db.appearance.lockedis true. The grip texture also stays hidden on hover so the UI doesn't suggest resize is possible - Live state:
TF:UpdateLocked()walks all tracker windows and reapplies the lock on toggle — no reload required. Default off; users dial in a frame position and opt in when they want it pinned
New Feature: Category Header Colorization
- "Colorize Category Headers" toggle on the Colors tab. When on, each subcategory header (Pinned / Complete / Important / Campaign / Daily / Weekly) tints with its matching quest-type color so the visual grouping reads at a glance. When off (default), all subheaders use the plain Section Header color as before
- Reuses existing quest-type colors — no duplicate pickers. Tweaking a category color in one place cascades to both quest titles and the subcategory header; no coupling for the user to reason about
- Incidentally fixes a pool bug:
GetSubHeaderonly appliedSetTextColorat frame-creation time, so a pooled subheader reused for a different category kept the old color. Color application now runs on every pool checkout, regardless of toggle state
New Feature: Delve UI Scale
- Delve UI Scale slider (Dungeons tab, 75%–200%). Scales Delve-specific elements — the spell icons, currency icons, and the "heart + lives" IconAndText widget — while inside a Delve. Other sections, other scenarios, and the tracker frame itself are untouched
- Gated on
CT.state.scenario.isDelvevia a localDelveUIScale()helper so the scale factor returns 1.0 outside Delves. Fonts scale alongside icons so the lives counter and currency text don't look shrunken next to an enlarged icon
New Feature: Nemesis Pack Inline Counter
- Counter renders inline next to the Nemesis spell icon ("4/4" fresh, ticking down as packs die). Previously the progress only surfaced in the hover tooltip. Resolution order:
sp.text→sp.tooltip→C_Spell.GetSpellDescription(spellID)parsed forN/M. The description path covers Nemesis specifically, since Blizzard doesn't populate either widget field for that spell - Green checkmark on completion. Nemesis uses "remaining" semantics — "0/4" means done, not "4/4" — so the checkmark swap triggers on
cur == 0, notcur >= total. UsesInterface\RaidFrame\ReadyCheck-Ready, sized to the Delve UI scale - Wrap-on-overflow layout: currencies and spell icons flow on a single row until the next element would exceed the frame's usable width (measured via
ComputeBarWidth), at which point subsequent elements break to a new row. Compact when there's room, legible when there isn't - Hover hit rect extends over the label:
SetHitRectInsets(0, -(labelW + 4), 0, 0)grows the icon's mouse region rightward so hovering anywhere on "4/4" opens the spell tooltip — not just the small icon - Non-tainting data path: explicitly does not use
C_TooltipInfo.GetSpellByIDor a scanner-GameTooltipto pull the counter. Both route through Blizzard's sharedTooltipDataHandler/infoListstate, and taint in our populate path would poison every later tooltip (world cursor, units, bags).C_Spell.GetSpellDescriptionis a plain string getter with no such risk
New Feature: Injected Player-Buff Icons in the Delve Header
- Player auras can be injected into the Delve spell-icon cluster, starting with Trovehunter's Bounty (spellID 1254631). When the player has the buff active inside a Delve, its icon appears alongside the affix/tier icons with the standard tooltip on hover and the stack count overlay from
aura.applications. Driven byC_UnitAuras.GetPlayerAuraBySpellIDat render time;info.spellsis never mutated (shallow copy only on inject) UNIT_AURAhandler is Delve-gated viaCT.state.scenario.isDelve, so we don't thrash the tracker on every buff tick during regular combat. Inside a Delve the event routes through the standard debouncedCT:Refresh()- Extension-friendly:
EXTRA_DELVE_BUFFSis a plain spellID list in the renderer — additional Delve-relevant auras are a one-line addition
v1.5.1
New Feature: Auto-Apply Class Colors on Character Swap
- "Auto-apply on character swap" toggle next to the Apply Class Colors button on the Colors tab. When on, the four class-color slots (
borderColor/header/sectionHeaderLine/subHeaderLine) are retinted to the current character's class on login and on profile switch, so alts sharing a profile pick up their own class color without a manual click. Alpha is preserved on each slot, same as the one-shot button - Default off — existing users' hand-tuned colors stay untouched unless they opt in. The toggle tooltip explicitly warns that enabling it will overwrite any hand-tuned RGB in those four slots every login
- Shared write path: extracted
CT:ApplyPlayerClassColors()so the button and the auto-apply hook use the same code. Auto-apply runs before the UI builds on login and beforeTrackerFrame:Rebuild()on profile change, so the first paint already matches the new class — no flash of the previous color
v1.5.0
12.0.5 (Lingering Shadows) Compatibility
- TOC now declares
Interface: 120001, 120005so the addon loads on live 12.0.1 and the 12.0.5 PTR without the out-of-date flag. Audited the 12.0.5 API change list (C_SpellBook/Spell/ActionBar.Get*ChargeDurationzero-span-at-max,UnitName/Ambiguate/GetRaidRosterInfotweaks, new cooldown/formatter APIs) — no call sites in Carrot are affected. Zone/scenario detection is zone-agnostic, so Void Assaults, Ritual Sites, Abyss Anglers, Decor Duels surface automatically through the existingC_Map/C_QuestLog/C_ScenarioInfopaths
Bug Fix: Achievement Progress Bar Rendering
- Single/zero-criteria achievements no longer display a noisy
0/0or0/1progress counter — the criterion text already renders as an objective line, so the fraction is redundant.PopulateAchievementRownow gatesprogressTextonnumCriteria > 1 11/12bar visually full:PopulateAchievementRowre-anchorsprogressBgeach populate (TOP →row.objective.BOTTOM) so it sits below the dynamic criteria block.SetPointdefers layout, soprogressBg:GetWidth()returned 0 andApplyBarFillFromBG→GetKnownWidthwalked up to the row — 20 px wider than the actual inset bar. Fill width now computed explicitly asrowWidth − (achPadding + 24) − achPadding, independent of layout timing- Bar overlapping the next row's title:
RenderObjectiveLinesstored_objBlockHeight = sum(textH) + spacing, omitting the 4 pxOBJ_LINE_TITLE_GAPit applies betweentopAnchor.BOTTOMand the first criterion.CalcRowHeightthen returned a row height 4 px too short and the trailing bar stuck into the next row._objBlockHeightnow includesOBJ_LINE_TITLE_GAPwhen at least one line is rendered — fixes achievement, quest, WQ, and recipe rows in one place - Achievement bar gap tightened from
-4to-2so the vertical spacing matchesCalcRowHeight's(2 + barH)accounting
New Feature: General Toggles
- Show Progress Bars: global toggle to hide the thin bar under quest / world quest / achievement rows. Scenario, M+, and prey bars stay visible — they carry functional state, not decoration
- Show Quest Level: prepends
[level]to quest titles, read fromC_QuestLog.GetInfo(questID).level - Hide Completed Objectives: drops finished objectives / criteria lines from the render loop inside
RenderObjectiveLines, so only what's left to do remains visible. Covers quests, WQs, achievements — all routed through the same helper. Ready-to-turn-in quests are intentionally NOT hidden; those still need user action
New Feature: Per-Element Font Overrides
- Section Header Font Size slider, independent from objective size (previously shared
fontSizeSmall) - Three per-element font dropdowns (Quest Title / Objective / Section Header) with a
Defaultfallback that routes through the base Font. Backed by new nullablefontTitle/fontObjective/fontHeaderappearance keys —ApplyFontgained an optionalfontKeyparam that checks the override before falling back tofont. NewApplyTitleFont/ApplyHeaderFonthelpers sit at the known call sites (quest title, achievement name, WQ title, scenario row title, section headers, sub-category headers) - Font dropdowns alphabetized via AceConfig's
sortingcallback — the base Font dropdown and the three per-element ones all sort A→Z by display name (case-insensitive), withDefaultpinned to the top of the overrides
New Feature: Auto-Flip Tooltip Anchor
- Tooltips flip to the opposite side of the owner when the owner sits in the left half of the screen, so a tracker docked at the screen edge no longer shows tooltips over itself. All 24
GameTooltip:SetOwnercall sites route throughCT.Utils.GetTooltipAnchor(owner)— picksANCHOR_RIGHTifowner:GetCenter() < screenW/2, elseANCHOR_LEFT. No setting: it just works based on tracker position
New Feature: Apply Class Colors Button
- One-shot button on the Colors tab that overwrites
borderColor/header/sectionHeaderLine/subHeaderLinewith the player's class color. RGB replaced; alpha preserved as-is (a transparent border stays transparent — transparent-by-default wasn't bumped, since surfacing a border "out of nowhere" on toggle is confusing) - Color pickers stay authoritative: no runtime overrides, no hidden state. The Colors / Appearance pickers show the real applied values and tweaks just work. Re-run the button after class swaps
Options Reorganization
- Frame Background moved from Appearance → Colors tab. All frame color controls now live in one place, with the Apply Class Colors button next to them
- Each tab's
Defaultsbutton now resets only what that tab owns. General Defaults no longer resets hover-highlight (which lives on Appearance); Appearance Defaults snapshots + restoresbgColor/borderColoraround the reset (they live on Colors); Colors Defaults resets colors + the frame bg/border - Appearance tab bottom padding — added a trailing description spacer so Blizzard's Settings panel stops clipping the last slider's value input box
Localization
- All strings added in this release routed through
L[…]— new toggles, font controls, Apply Class Colors button, Frame header, moved Background/Border color entries, tab-scoped reset descriptions, and the class-colorsCT:Printmessage. Translators can add entries to any locale block inLocales.lua; the fallback metatable keeps English working without explicit enUS entries
v1.4.5
Bug Fix: Stale Tracker After Arena/Dungeon Exit
- Title-only rows with no objectives or progress bar after returning from an arena (and similar instance exits). On exit, Blizzard fires
PLAYER_ENTERING_WORLDfollowed by a burst ofQUEST_LOG_UPDATEevents within ~100 ms. Carrot'sCT:Refreshdebounce collapsed the entire burst into a singleDoRefresh, and that one tick frequently ran whileC_QuestLog.GetQuestObjectiveswas still returning empty tables — leaving the tracker frozen on titles until the next unrelated event happened to fire (/reloadwas the only reliable workaround) - Fix:
CT:Refreshnow flips arefreshDirtyflag when an event arrives during the debounce window, and the pending tick schedules a follow-up afterDoRefreshruns. Bursts no longer get silently dropped — late events are honored on the next ~100 ms boundary, by which timeC_QuestLoghas populated the real objective data
Bug Fix: ADDON_ACTION_BLOCKED on CarrotScroll_*:SetPoint()
- Spammy taint in BugSack:
[ADDON_ACTION_BLOCKED] AddOn 'Carrot' tried to call the protected function 'CarrotScroll_1:SetPoint()'fromRefreshTabBaron every refresh. The scroll frame is "protected" because its descendants includeSecureActionButtonquest item buttons; once anything taints Carrot's call stack (widget-info / secret-value reads), even a same-point SetPoint trips the guard - Fix: cache the last
contentTopoffset on the instance and skip the SetPoint when it hasn't changed (which is the steady-state for every refresh after the tab bar visibility settles). Drop the per-refreshBOTTOMRIGHTre-anchor entirely — it's static (0, 2) and already set once at frame creation. Seed the cache at instance creation so the very firstRefreshTabBarcall doesn't re-anchor unnecessarily either
New Feature: Auto-Scroll to "Click to Complete" Popups
- Click-to-complete popups now scroll into view at the top of the window when they first appear, so they can't be missed when the user is scrolled into a different section (achievements, recipes, etc.). Per-window
seenCompletePopupsset tracks which COMPLETE-type popup IDs were rendered last pass; anything in the new pass that wasn't seen is treated as a fresh arrival and queued for scroll - Re-completion still triggers: IDs are evicted from
seenwhen their popup leaves the list, so a later re-completion of the same quest scrolls into view again instead of being silently suppressed - Math is anchor-based, not
GetTop()-based:PopulateQuestSectionstores each popup row's intra-section yOffset andUpdateWindowLayoutstores the section's offset within the scroll child. After layout finalizes, the absolute Y issection._contentYOffset + row._popupYOffset, clamped to[0, contentH - viewH]. Robust against the two-pass layout — popups detected on the first pass are markedseen, so the second pass doesn't double-scroll - Pool-reuse safety:
ResetQuestRowSharedStateclears_popupQuestID/_popupType/_popupYOffset, so a row that was a popup last refresh but is now a regular quest can't be mis-identified by the diff
v1.4.4
Bug Fix: Empty Tab No Longer Strands the Tracker
- Switching to an empty tab (e.g., an Achievements tab in a zone with no tracked achievements) on a multi-tab window used to fire
autoCollapseEmptyand shrink the frame to a thin title bar. The tab buttons live at-(20 + TOOLBAR_HEIGHT)and got clipped outside the collapsed frame, leaving the user with no clickable way back to the previous tab — only the tiny "+" button in the (often hover-hidden) title bar - Fix:
UpdateWindownow skips auto-collapse when the window has 2+ enabled tabs. The empty tab renders normally with(0)section headers; the tab bar stays reachable. Single-tab / no-tab windows keep the thin-bar idle behavior - Auto-expand path also re-opens a minimized window if it gains a multi-tab bar (so prior-session minimized state can't strand the tabs either)
- Setting tooltip updated to mention the multi-tab exception
Layout: Wrapped-Text Heights Settle on First Refresh
- Two-pass layout in
TF:Update: WoW computes wrap-enabledFontStringheights lazily at render time, soGetStringHeighton a freshly-populated multi-line objective returns the single-line fallback on its first read. That under-computedCalcRowHeight, left rows visually overlapping, and shrunkwindowScrollChildso the scrollbar couldn't reach the real content bottom — only unsticking when something else (resize, header click) re-ran the layout - Fix: right after the first populate pass,
Show()a dedicated helper frame whoseOnUpdatefires on the very next render tick and re-runsTF:Updateunder a recursion guard. By then the render cycle has resolved wrapped-text metrics, so the second pass anchors everything at correct offsets - ~3× faster to settle than a
C_Timer.After(0.05)approach and doesn't keep an idle timer alive between refreshes
v1.4.3
Auto-Popup "Click to Complete" Overhaul
- Stale-anchor overlap fix: the auto-popup and Prey paths grabbed a pool-reused row via
GetQuestRow()but never hidtimerBg/timerFill/timerLabelnor re-anchoredrow.objective. If the slot last held a timed Abundance quest,row.objectivestayed anchored totimerBg.BOTTOMLEFT, floating "Click to complete" below a hidden-but-still-layout-active timer bar and overlapping the previous quest's_objLines. Both paths nowResetQuestRowSharedState(row)+HideObjectiveLines(row)+ explicittimerBg/Fill/Label:Hide()+ClearAllPoints/ re-anchor ofrow.objectiveunderrow.title.BOTTOMLEFT - Bigger inline quest-type badge:
|TInterface\GossipFrame\ActiveQuestIcon:18|t(up from:12) for COMPLETE popups and the same bump for theAvailableQuestIconon OFFER popups, echoing the 60×60 question-mark icon Blizzard'sAutoQuestPopUpBlockuses, scaled to fit our compact tracker row - Prominent call-to-action text: "Click to complete" / "New quest!" jumps from
fontSizeSmall(10) tofontSize + 2(14) — above the title — so the action reads as the loudest glyph on the row.ApplyFontnow accepts a numeric size directly in addition to the string DB key - Pulsing gold overlay (
_completePulse): lazily-created full-row ARTWORK texture with aREPEATanimation group — alpha 0 → 1 → 0 on 0.55 s halves. Started viaStartCompletePulse(row)on auto-popup COMPLETE and prey-complete rows; stopped + hidden inResetQuestRowSharedStateso pool reuse cleanly hands a non-pulsing row back to a normal quest. Mirrors the role of Blizzard'sShine.FlashAnimationGroup onAutoQuestPopUpBlock
Layout / Visual Density
- No 100% progress bar on "Ready to turn in" quests: title color +
L["Ready to turn in"]line + gold row glow already carry the signal.PopulateQuestRownow gatesshowBaron(hasProgressBarObj or hasNumericProgress) and not entry.isComplete, and the non-bar branch still shows the glow for complete quests - 2 px gap between quest rows: with the yellow bar gone, adjacent "Ready to turn in" rows were visually merging into one tall glowing block.
PopulateQuestSectionnow advancesyOffsetbyactualHeight + QUEST_ROW_GAPfor both auto-popup rows and regular quest rows — scoped to the quest section, other sections keep their tight spacing ResetQuestRowSharedStatealso restoresrow.objectiveto the small font so the bumped "Click to complete" size doesn't leak into a pool-reused regular quest row
v1.4.2
New Feature: Auction House Search Button on Quests
- Item-collection quests (profession dailies, "Collect 10 Felsteel Bar"-type) and world quests now get a magnifying-glass button on the row when the auction house is open. Click to shop for the items Blizzard's map pin can't help you with
- Uses Auctionator when installed (
Auctionator.API.v1.MultiSearch("Carrot", names)— queues every needed item in the shopping list at once), falls back to Blizzard's nativeAuctionHouseFrame:SendBrowseQuery(firstName, 0, 0, {})when it isn't. Profession quests almost always ask for one item type, so a single native browse query is usually what the player wants - Objective parsing handles all three common locale formats:
"0/10 Felsteel Bar"(enUS leading progress),"Felsteel Bar: 0/10"(trailing-colon variant), and"Felsteel Bar 0/10"(no-colon variant). Fails open to the raw objective text so an unanticipated locale still searches something sensible - Position: anchored to the left of
row.timeTextwith a 4 px gap, so it never collides with the WQ expiration label and regular quest rows (with emptytimeText) get it flush at the top-right corner - Tooltip: title flips between
L["Search on Auctionator"]andL["Search on Auction House"]based on which backend will handle the click, followed by the list of items to be searched. Both strings localized in all 10 locales
v1.4.1
Critical Taint Fix
[ADDON_ACTION_BLOCKED] AddOn 'Carrot' tried to call SetPassThroughButtons():DefaultTrackerHider'shooksecurefunchook onObjectiveTrackerFrame.Showwas synchronously callingHider:DoHide()→SafeSetAlpha(ObjectiveTrackerFrame, 0)from inside the hook body. The hook fires during Blizzard's internalShow()calls, which happen insidesecureexecuterange(dataProviders, ...)on quest / map canvas events. Mutating a Blizzard-owned frame from within that secure range leaked Carrot's taint into every subsequent Blizzard function in the same range, surfacing asSetPassThroughButtonsblocked whenQuestDataProvider.RefreshAllData→AcquirePin→CheckMouseButtonPassthroughtried to configure a map pin- Fix: wrap the
Hider:DoHide()call inC_Timer.After(0, ...)so theSetAlphamutation runs on the next frame in a plain (non-secure) context. Re-checksisHidden/hideDefaultTracker/inCombatinside the deferred closure since state can change between the hook and the timer fire.SetAlphaon its own wasn't the taint vector (v1.3.2 replacedSetScalefor that reason) — the new factor is context: v1.4.0 added enough Blizzard hooks firing insidesecureexecuterange(scenario widget tracking, block discovery, etc.) that this latent "mutation inside secure range" path finally triggered
v1.4.0
New Feature: Prey Section (Midnight Prey Hunt system)
- Dedicated tracker section for the 12.x Prey Hunt world feature (Hunt Tables, not a Hunter class ability). Replaces Blizzard's tiny red on-screen crystal with a proper row: quest title + zone + stage-colored horizontal fill bar (Cold → Warm → Hot → Final, stage-mapped to 0 / 33 / 66 / 100% via Preydator's "Thirds" convention)
- Zone-gated: only visible when the player is in (or a sub-zone of) the prey's zone, with a canonical map-ID equivalence table + parent-map walk so sub-instances inside the right parent zone still count. Stays visible after zone exit when the quest is ready to turn in, so the turn-in prompt survives a hearthstone
- Click-to-complete: prey quests are auto-popup style. Click handler honors
prey.isComplete(same signal the renderer uses) and firesShowQuestComplete(questID)+RemoveAutoQuestPopUp(questID)when the quest is ready — no running to an NPC. Blizzard's nativeSOUNDKIT.UI_AUTO_QUEST_COMPLETEstill plays since we never touch the popup list - Completion visuals: vertical yellow → dark amber gradient fill on the gold bar, WCAG AAA-contrast dark brown
COMPLETElabel with no shadow, completion glow,L["Click to complete"]objective line - Duplicate suppression: filter removes the prey quest from
CT.state.questsand skips its auto-popup inPopulateQuestSectionso "Click to complete" only appears once, in the Prey section - Live stage updates from the
PreyHuntProgresswidget viaUPDATE_UI_WIDGET(matched by type, not set ID — the widget lives in whichever ambient set Blizzard picks)
New Feature: Events Section + Full UIWidget Renderer Coverage
- Events section: reads
C_UIWidgetManager.GetAllWidgetsBySetID(GetObjectiveTrackerWidgetSetID())read-only (never callsRegisterForWidgetSet— the taint rule from v1.3.1 still stands). Replaces the Abundance / zone event UI that was lost when we removed the tainted widget container - Scenario step widget set (
C_ScenarioInfo.GetScenarioStepInfo().widgetSetID): renders Blizzard'sScenarioHeaderTimer(02:52 countdown) andScenarioHeaderCurrenciesAndBackground(e.g. Abundance Bag 0/1500) inside the scenario section — the API channel the default tracker uses for Abundance-style events - 30 widget type renderers covering every entry in
Enum.UIWidgetVisualizationType:- Text/label: IconAndText, IconTextAndBackground, TextureAndText, TextureAndTextRow, TextWithState, TextWithSubtext, TextColumnRow, BulletTextList, ButtonHeader, DoubleIconAndText
- Bars/progress: StatusBar, DoubleStatusBar, CaptureBar, CaptureZone, ZoneControl, TugOfWar, DiscreteProgressSteps, FillUpFrames
- Resources/currencies: HorizontalCurrencies, StackedResourceTracker, IconTextAndCurrencies
- Spell/item: SpellDisplay (with tooltip, colored border from
spellInfo.borderColor), ItemDisplay (withGameTooltip:SetItemByIDhover and asyncItem:CreateFromItemIDloading) - State/indicator: DoubleStateIconRow, PreyHuntProgress, TextureWithAnimation
- Scenario-specific: ScenarioHeaderTimer, ScenarioHeaderCurrenciesAndBackground, ScenarioHeaderDelves
- Layout: Spacer
- Generic fallback renderer catches any future widget type Blizzard ships — inspects the info table for text/icon/bar fields and renders what it finds. No widget type silently vanishes
- Self-registering registry:
WIDGET_RENDERERS[EnumName] = RenderFn, keys matchEnum.UIWidgetVisualizationTypeverbatim. Dispatcher auto-resolves the accessor viaGet<Name>WidgetVisualizationInfo/Get<Name>VisualizationInfo(Blizzard's naming is inconsistent across types). Adding a new renderer is 2 lines - Skipped:
MapPinAnimation,UnitPowerBar(nameplate/map only, not tracker)
Quest Row Timer Bar
- Big purple fill bar for timed quests (
C_QuestLog.GetTimeAllowed > 0) — Abundance / bonus objective timers now render as a full-width countdown bar between title and objective withMM:SScentered inside. Replaces the tiny cornertimeText. Color shifts purple → orange → red as time drains - Applied to both regular quest rows and world quest rows.
ZoneEngine.BuildTaskQuestEntryalso now collectsquestTimerSeconds/questTimerMax
Layout
- World quest group-finder eye moved to the left of the title (just inside the tier bar). Long WQ titles no longer get truncated by the eye on the right, wrap mode looks cleaner, click target stays at a fixed X regardless of title length / item button presence
- Quest item button slots between the eye and the title when present, so the eye always sits at the same absolute X
Correctness Fixes
- Taint hazards:
C_QuestLog.GetTimeAllowed,GetNumAutoQuestPopUps,GetAutoQuestPopUp,C_ScenarioInfo/C_UIWidgetManager/C_Spell.GetSpellInfo/GetQuestProgressBarPercentall nowpcall-wrapped per the 12.x AllowedWhenUntainted rules. Never registers aUIWidgetContaineragainst any widget set - "Ready to turn in" 0.1% sliver bug: when a progressbar-type quest was complete,
GetQuestProgressBarPercentcould return a tiny residual value that bypassed theisComplete → fraction=1fallback, leaving a 1-px fill on pool reuse. Rewrote to force fraction=1 onisCompleteand to always compute fill widths via a parent-walkGetKnownWidthhelper instead of the anchor-derivedprogressBg:GetWidth()(which returns 0 on first render / stale on pool reuse) - "EVENTS (0)" ghost header:
UpdateWindowLayoutwas re-computing counts viaCOUNT_FNSwhich could disagree withPopulateSection's actual rendered count. Now readsinstance._lastCountsstashed by the populate pass - Pool reuse leaks: quest row pool shared with the Prey section — tall 16-px bar, dark-brown label colors, suppressed shadow were leaking back into the next quest populate (visible as "Midnight: Prey" rendering with a giant gold bar). Introduced
ResetQuestRowSharedStatecalled at the top of everyPopulateQuestRow/PopulateWorldQuestRow - First-render widget bars:
RenderEventWidgetran beforeSetPointanchors resolved, causing every event widget bar to compute against a 0-width frame. Anchor now applied before render, and internal renderers useComputeBarWidth/ApplyFractionalFillhelpers that walk up to the first ancestor with an explicitSetWidth - Achievement tracking from in-game UI: listen for
CONTENT_TRACKING_UPDATE(modern 12.xC_ContentTrackingpath) in addition to the legacyTRACKED_ACHIEVEMENT_UPDATEevent. Tracking an achievement via the achievement pane no longer silently waits for/reload
DRY Refactor
- Extracted shared helpers:
GetKnownWidth,ComputeBarWidth,ApplyFractionalFill,ResetQuestRowSharedState,FindAutoQuestPopUp,RegisterExtra(forward-declared),LazyFontString,LazyTexture. Collapsed ~6 duplicated parent-walk width blocks and 3 hand-rolled auto-popup iterations into single call sites
Abundance Bag Bar + Scenario Block Widget Set Discovery
- Abundance bag progress bar (the
1201/1500"Bag" counter that appears below the timer) now renders inline under the scenario header. Lives in widget set514in a siblingScenarioObjectiveTracker.ContentsFrameblock — distinct from the scenario step's widget set (1480, the timer).GetAllWidgetsBySetID(stepSetID)only returns the timer; we discover the extra set IDs by walking the liveContentsFramechildren and reading each block'sWidgetContainer.widgetSetID(read-only, noRegisterForWidgetSet, no taint) UPDATE_UI_WIDGETfilter picks up pushes for any discovered scenario-block set so deliveries / counter changes driveCT:Refresh()immediately. Cache invalidates onSCENARIO_UPDATE/SCENARIO_COMPLETED/ZONE_CHANGED_NEW_AREAand lazy-rebuilds on cache miss so a newly mounted block can't be permanently rejected- Block walk extended to
BonusObjectiveTracker.ContentsFrameso any future event that parks its progress bar in the bonus tracker is auto-discovered RenderStatusBarWidgetfully mirrors Blizzard'sUIWidgetBaseStatusBarTemplate:SetBarText:math.ceildisplayed value,GENERIC_FRACTION_STRINGforValueOverMax(rawbarMax, notbarMax-barMin), respectoverrideBarTextShownType.Always, empty label forHidden/Time/ unknown — same data Blizzard's tracker shows- Smooth value tweening (300 ms linear) on every StatusBar widget value change. Dedicated per-frame ticker that auto-stops when no bars are animating, resumes from the current interpolated position on mid-tween pushes, and stays out of
hasTimerbars (those are driven byactiveEventTimersalready) - Compact widget rows when
info.textis empty:RenderStatusBarWidgetandRenderScenarioHeaderTimerWidgetnow collapse toEVENT_BAR_HEIGHT + 6(16 px) instead ofEVENT_ROW_BAR_H(32 px), so the Abundance timer sits flush under "0/1 Complete Event" instead of leaving 16 px of blank title area - Scenario timer purple gradient: Abundance / scenario timer fill is a horizontal
(0.28, 0.16, 0.55) → (0.60, 0.50, 1.00)SetGradientinstead of a flat purple, matching Blizzard's stage-bar visual. White base set once outside the per-tickapplyFillbecause per-tickSetColorTextureclobbersSetGradientin 12.x
Scenario Bonus Objectives
EvaluateScenarioBonusmirrorsBonusObjectiveTrackerMixin:ProcessScenarioBonusObjectives— walksC_Scenario.GetBonusSteps(), reads each step viaC_Scenario.GetStepInfo+C_ScenarioInfo.GetCriteriaInfoByStep, filters byshouldShowBonusObjective, and feedsCT.state.scenarioBonusSteps- Supersession pruning via
C_Scenario.GetSupersededObjectives(): when step B replaces step A, we drop A while B is incomplete and drop B once A's criteria are all satisfied — same logic Blizzard uses PopulateBonusSectionextended to render scenario bonus steps after its quest-based entries: a header row (step name + description in scenario-orange) followed by compact criteria rows viaPopulateScenarioRow, reusing the existing scenario row pool- New events registered:
SCENARIO_BONUS_VISIBILITY_UPDATE,CRITERIA_COMPLETE,QUEST_WATCH_LIST_CHANGED— matchingBonusObjectiveTrackerMixin's subscription list
Recipe Section Overhaul
248/1reagent count fix:C_Item.GetItemCountnow passed all 5 arguments includingincludeAccountBank=true(warband bank, new in 12.x). Previous 4-arg form silently excluded the warband bank, so reagents stored there read as0. We preferProfessionsUtil.AccumulateReagentsInPossession(slot.reagents)(Blizzard's canonical helper that wraps the count), withItemUtil.GetCraftingReagentCountand a manualC_Item.GetItemCount(itemID, true, false, true, true)as defensive fallbacks- Multi-tier reagent slots: now use
slot.required(Blizzard's canonicalIsReagentSlotRequiredflag) instead of a hand-rolleddataSlotType == Reagentcheck. Catches legacy single-item slots, multi-tierModifiedReagentslots ("Any quality of X"), and modifying-required slots — recipes with 4 ingredients no longer drop to 2 when half are multi-tier. Currency slots are still skipped (not shoppable) - Slot category names: prefer
slot.slotInfo.slotText("Fine Chorus Petal") over a specific tier's item name, matching Blizzard's tracker wording - Completed reagent visuals: rendering switched from the legacy
row.objective:SetText("line1\nline2")path toRenderObjectiveLines(row, reagentLines, row.title, -2)— completed reagents now get the same green checkmark + dimmed-text treatment as completed quest / achievement / scenario criteria, via the shared columnar helper - Click-to-open for unknown professions: tracked recipes from personal crafting orders (player doesn't own the profession) used to be silent no-ops. Click handler now mirrors
Blizzard_ProfessionsRecipeTracker.lua— checksC_TradeSkillUI.IsRecipeProfessionLearned(recipeID). Profession learned →OpenRecipe(recipeID, isRecraft)(recraft flag honored so tracked recrafts open the recraft pane). Not learned →Professions.InspectRecipe(recipeID)for the read-only inspect view. Foreign-profession recrafts skip the inspect path (not meaningful) - Auctionator integration: magnifying-glass button (
common-search-magnifyingglassatlas) appears on the top-left of recipe rows when (a)Auctionator.API.v1is loaded, (b)AuctionHouseFrame:IsShown(), and (c) the recipe has unmet reagents. Click queues every missing reagent's item name intoAuctionator.API.v1.MultiSearch("Carrot", names). Item names are resolved fromrecipe.reagents[i].itemIDsat click time viaC_Item.GetItemInfo— not cached display names — so multi-tier slots search every accepted item, not the slot's category label. Title shifts right by 20 px when the button is visible, restores its default anchor when hidden. Refreshes onAUCTION_HOUSE_SHOW/AUCTION_HOUSE_CLOSED
Delve Enhancements
- Delve detection rewrite:
Enum.ScenarioType.Delvesno longer exists in 12.x. Now usesC_DelvesUI.HasActiveDelve(mapID)— the same call Blizzard's ownInstanceDifficultyMixin:IsInDelve()uses. Was silently returning false on every player, breaking every code path gated onscenario.isDelve - Compact
ScenarioHeaderDelveslayout: header inlines the tier ("Shadowguard Point (Tier 11)") via the newL["TIER_LABEL_FORMAT"]localized format string. Currencies and spell icons share a single row instead of two; currency rows are sized to their measured content width instead of a fixed 90 px so spell icons sit immediately to the right with no wasted gap - Tooltips on every delve icon:
- Spell icons —
OnEntercallsGameTooltip:SetSpellByID(spellID), surfacing the full spell tooltip on hover (these icons were otherwise unidentifiable since they only showed an icon + stack overlay) - Currency icons — hover shows the currency name, widget tooltip text, and (via
C_CurrencyInfo.GetCurrencyInfo) the description + current/max amounts - Tier text — overlay button on the title FontString uses
info.tierTooltipSpellIDfor the tier perk's spell tooltip, falling back to the widget's general tooltip
- Spell icons —
- Spell side label always shown:
sp.text(e.g."Nemesis Strongbox 2/4") now always renders next to the icon when present, ignoring Blizzard'stextShownState == Hiddenflag. Their default UI surfaces this only via tooltip; players want it inline. Stack overlay still respectsstackDisplay > 0per Blizzard's check (previous coalescesp.stackDisplay or ""was buggy because0is truthy in Lua, painting "0" on every stackless spell) hideTrackerCompletelyfor delves: matches the existing M+ option. Hides every Carrot tracker window while inside a delve, for users with a dedicated delve addon. Default off- Scenario peek toggle ("Show All" / "Focus"): when M+ or Delve
hideOtherSectionsis active, a button appears on the scenario section header that togglesCT.state.exclusivePeek. Flipping peek on temporarily overrideshideOtherSectionsso every section becomes visible until the user clicks "Focus" again. Resets onSCENARIO_COMPLETED,PLAYER_ENTERING_WORLD, andCHALLENGE_MODE_RESETso the next exclusive scenario starts focused
World Quest Row Improvements
timeTextanchor fix: the"3d"expiration label was anchoringRIGHT → RIGHT(mid-edge to mid-edge), so on tall WQ rows it floated down to the row's vertical midpoint and overlapped the objective's(54%)progress text. Switched toTOPRIGHT → TOPRIGHTso it stays pinned to the title line regardless of row height. 2 px top margin for breathing roomApplyBarFillFromBGnew helper: reads the bar background's own resolved width viaGetKnownWidth(bg)instead ofComputeBarWidth(row). WQ rows insetprogressBgby the eye-icon gutter (20 px), so the row-based calc was ~20 px too wide and saturated the fill at ~84 % actual progress. WQ rows now use the new helper; quest / scenario rows continue with the legacy path because theirprogressBgstarts at the standard padding inset- Both title and objective right edges respect
timeText.LEFT:RenderObjectiveLinesaccepts arightInsetparameter, set topadding + 60for WQ rows so wrapped objective text never slides under the "3d" column FormatTimeLeftPrecise: WQ tooltip "Time remaining" line now reads"2d 14h 35m"(full breakdown, drops zero segments) instead of the compact"3d"shown on the row itself- Group-finder eye gated on real eligibility:
C_LFGList.CanCreateQuestGroup(questID)(the canonical check Blizzard'sQuestUtil.CanCreateQuestGroupwraps) replaces our blanketsuggestedGroup = truefor every world quest. Solo-runnable WQs no longer show the eye — only elites and explicitly group-suggested content do, matching Blizzard's default
Other Fixes
- Achievement progress bar gap:
progressBgnow anchorsTOP → row.objective:BOTTOMinstead ofBOTTOMLEFT → row.BOTTOMLEFTso the bar sits directly under the criteria block. The previous bottom anchor combined withCalcRowHeight's padding overestimate left a visible gap between the last criteria line and the bar. Falls back to the row-bottom anchor for collapsed / no-criteria rows - Criteria icon vertical alignment: removed the spurious
+2Y offset onpair.text:SetPoint("TOPLEFT", pair.icon, "TOPRIGHT", gap, 2). Was pushing text up 2 px relative to the icon, leaving the icon visually below the text baseline on every row that usesRenderObjectiveLines(quests, achievements, scenario criteria, recipes) - Prey zone detection: now uses
GetQuestUiMapID(questID, true)(the same call Blizzard's ownUIWidgetTemplatePreyHuntProgress:OnMouseUpuses) as the primary source. Prey quests aren't task quests soC_TaskQuest.GetQuestZoneIDwas returning nil and the section never showed. Also fails open: ifFindPreyHuntProgressInfo()returned awidgetInfo, we markinZone = trueregardless of the map-ID match — Blizzard's widget only broadcasts when the player is in range, so its presence is the ground truth GetExtraScenarioBlockWidgetSetIDsscopes a temporarysupersededPairsinstead ofpairs— earlier draft accidentally shadowed thepairsbuiltin which would have broken any futurepairs(...)call inside the block
Localization
L["TIER_LABEL_FORMAT"] = "Tier %s"— locale-controlled format string for the delve tier suffix. CJK locales invert to"%s 등급"/"%s 级"/"%s 級"so the number comes before the unit. English default added explicitly in theenUSblock since the metatable fallback would otherwise return the literal identifierL["Show All"]/L["Focus"]— peek button label, in all 10 locales (English + 9)L["Search on Auctionator"]— recipe row tooltip header, in all 10 locales.luacheckrcglobals added:C_DelvesUI,C_LFGList,Auctionator,AuctionHouseFrame,ItemUtil,Professions,ProfessionsUtil
Diagnostics
/carrot widgets— dumps tracker widget set + every field on each widget's visualization info (for debugging event/scenario rendering)/carrot widgetapi— lists everyC_UIWidgetManageraccessor available on the current client/carrot barinfo— dumps everyGetStatusBarWidgetVisualizationInfotable for StatusBars parented underScenarioObjectiveTracker.ContentsFrameblocks. The canonical tool for "why doesn't Carrot render this scenario / event bar?" investigations — covers the per-block sibling widget sets the primaryGetAllWidgetsBySetIDquery doesn't reach- Removed exploratory diagnostics (
eventquest,tracehook,statusbars,stagedump) once the Abundance investigation wrapped —Config.luashrank from 938 lines to 346
v1.3.2
Critical Taint Fix
- Quest log / QuickJoin / GameTooltip "tainted by 'Carrot'" errors:
DefaultTrackerHiderwas callingSetScale(0.001)onObjectiveTrackerFrameplus walkingObjectiveTrackerManager.moduleToContainerMapand mutating every module. Module frames contain protected children (quest-special-itemSecureActionButtons, LFG queue bits) and in 12.xSetScaleon such frames taints downstream Blizzard code, surfacing as"attempt to compare a secret number value","attempt to perform numeric conversion on a secret number value", and"Secret values are only allowed during untainted execution"insideQuestMapFrame,LayoutFrame, andQuickJoinToast - Fix: rewrote the hider to
SetAlpha(0)only (noSetScale, noSetPoint), dropped the module-container walk, added anIsProtected()guard so secure frames are skipped, and wrapped every mutation inpcall. Children inherit the parent's alpha so a single call cascades through the tracker tree
v1.3.1
Critical Taint Fix
- GameTooltip taint ("tainted by 'Carrot'"): removed Carrot's
UIWidgetContainerthat was registering againstC_UIWidgetManager.GetObjectiveTrackerWidgetSetID()— a widget set shared with Blizzard's own tracker and tooltip code. Registering our own container against the shared set ID was poisoningDefaultWidgetLayout/GameTooltip_ClearWidgetSet, producing errors like"attempt to compare a secret number value (tainted by 'Carrot')"insideLayoutFrame. Trade-off: Carrot's tracker no longer renders Abundance event bars / zone event widget progress inline
Mythic+ / Scenario Fixes
- Enemy forces percent: now parsed from
criteriaInfo.quantityStringvia%d+(matching AngryKeystones and Blizzard's own ScenarioObjectiveTracker). The previousquantity/totalQuantityformula gave nonsense like 112% at the start of a key becausequantityis an internal encoded value for weighted progress, not the displayable current count - Death tooltip crash:
"table index is secret"—C_DamageMetercombat sourcenameanddeathTimeSecondsfields are secret values in 12.x that can't be read, compared, or used as table keys. Death log now stores onlyclassFilename+deathRecapID, and the tooltip groups deaths by class withLOCALIZED_CLASS_NAMES_MALE+RAID_CLASS_COLORS - Binary scenario criteria bar: hidden for
totalQuantity <= 1(boss kills etc.), since the checkmark/dash prefix already conveys state — no more redundant "0/1" bars overlapping adjacent rows in Skyreach-style scenarios
Quest Item Buttons
- Shown on world quests and bonus objectives: the existing
PopulateQuestSectionalready wired item buttons for regular quests, butPopulateWorldQuestSection/PopulateBonusSectionnever did. ExtractedAttachItemButtonToWQRowthat runs the sameC_QuestLog.GetLogIndexForQuestID→GetQuestLogSpecialItemInfolookup (verified against AngryKeystones, OPie, Plumber). World quests like "Mobilize! Enlist! Recruit!" now get their clickable Flyer button in Carrot's tracker - Padding no longer oscillates across refreshes: anchor shifting was previously driven by whether we successfully placed a
SecureActionButtonthis render, which fails in combat lockdown. Combat/non-combat refreshes were flipping the title left/right every tick during active world quests. Split the decision: combat-safeQuestHasSpecialItemprobe drives the layout, button placement itself is a best-effort combat-gated step. Applied to both regular quests and WQ/bonus rows
Layout & Row Fixes
- World quest row overlap: two WQs sometimes overlapped briefly and corrected themselves after a refresh. Root cause:
timeTextauto-sized from countdown content ("5h 32m" → "5h" → "30m" → "NOW!"), cascading throughgroupFinderBtn.RIGHT → title.RIGHTand flipping title wrap every second. Row-below yOffsets were computed with the old wrapped height. Fixed by lockingtimeText:SetWidth(56)on bothCreateQuestRowandCreateWorldQuestRow
Default Tracker Hider
- Hide all objective tracker modules, not just the main frame: 12.x split the tracker across independent module frames (quest, world quest, scenario, bonus, achievement, campaign) held in
ObjectiveTrackerManager.moduleToContainerMap. Hiding onlyObjectiveTrackerFrameleft module containers peeking from the right. Now walks the map and suppresses every frame - Hide WorldQuestTracker (Tercio) frames when installed: auto-detects
_G.WorldQuestTrackerScreenPaneland applies the same treatment, gated on the existinghideDefaultTrackertoggle. WQT anchored its panels relative toObjectiveTrackerFrame.topleft/toprightand computed offsets viaObjectiveTrackerManager.moduleToContainerMap, so with Blizzard's tracker suppressed its own frames ended up at unexpected positions
v1.3.0
Achievements
- Criteria list: Each achievement now shows its individual criteria below the progress count, using the same bullet/checkmark/faded styling as quest objectives
- Per-achievement expand/collapse: Small
+/-arrow in the top right of every achievement row toggles the criteria list; state saved per-achievement inCT.db.achievementCollapsed - Row layout fix: Name and progress text no longer overlap at larger font sizes — anchored top-to-bottom relative to each other instead of centered on the icon with hardcoded offsets
- Dynamic row height:
CalcRowHeightnow accounts forprogressTextand criteria list so achievement rows grow correctly with font size changes - Default Achievements tab for existing installs: One-time migration adds an "Achievements" tab (and enables tabs) on the first load so upgraders get the new default layout. After the migration runs, users can freely delete the tab without it being recreated
Mythic+ / Delves
- Auto-focus scenario tab on M+ start: New
TF:FocusSectionhelper automatically switches the containing window to whichever tab owns the scenario section whenCHALLENGE_MODE_STARTfires (and on/carrot mplusdemo), so users with the Achievements tab active don't miss the timer/boss/EF bar - Checkmark + faded styling for bosses and delve criteria: Completed M+ boss rows and delve criteria now use Blizzard's green
ReadyCheck-Readyicon plus the configuredCompleted Objectivecolor/alpha, matching quest objectives. M+ split times fade with the title; delta and "Best:" hints stay full color - Enemy forces bar race fix: Explicit progressBg/progressFill re-anchoring and a
scrollChild → row → 200fallback chain that handles the Lua truthiness bug whereX or 200returns0whenXis0. No more "shows 0.1% then corrects itself after a reload" - Boss row font leak fix:
timerTextRow:SetFont(..., +4)used to stick on rows the pool handed back toMPlusTextRow, causing one apparently-random boss row to render with a larger font.GetScenarioRowandMPlusTextRownow reapply the base font defensively - Timer bar marker draw order:
+2/+3threshold markers are now created atOVERLAYsublevel 7 viaSetDrawLayer, soprogressFillcan never cover them regardless of pool-reuse creation order. Also guardedmarkerWidthagainst a saved0value (Luaor 1fails for0)
Migrations & Data Model
- One-time migration flags: New
CT.db.migrationstable tracks which "ensure section exists" migrations have run. Thescenario,recipes, andachievementsensures now run exactly once per profile, after which users can delete those sections or tabs without them being re-added on every reload (previously they were silently re-added on every load)
v1.2.0
Window Tabs
- Tabs inside windows: Each tracker window can now host multiple tabs, each with its own filtered sections, order, collapsed state, and quest subcategory order
- Per-tab section filtering: Sections are assigned to exactly one tab, so you can split quests/M+ into one tab and achievements into another
- Tab bar: Button-style tabs at the top of the window with active/inactive states using the tracker's yellow accent color; hidden when tabs are disabled
- Renameable tabs: Tab names are user-editable from the Windows options panel
- Configurable tab opacity: New
Active Tab AlphaandInactive Tab Alphasliders in Appearance → Tab Bar - New default layout: Fresh installs ship with tabs enabled and a "Main" / "Achievements" split to surface both features
- Saved data migration: Existing profiles are migrated into the tabs model on load without touching visible behavior
Objective Styling
- Blizzard-style completed objectives: Finished objective lines are greyed out and semi-transparent so players can focus on what's left
- Green checkmark icon: Completed objectives are prefixed with Blizzard's
ReadyCheck-Readygreen check; in-progress ones get a dash bullet - Yellow quest titles: Auto-tracked quest titles now default to Blizzard's classic gold (
1.0, 0.82, 0) - Configurable: New Appearance → Objectives section with
Show Bullets,Show Checkmark, andCompleted Objective Alphasettings; newCompleted Objectivecolor in the Colors panel
M+ Bug Fixes
- Enemy forces bar: Now parses the percent from
criteriaInfo.quantityString(matching Blizzard's ScenarioObjectiveTracker) instead ofquantity/totalQuantity, which isn't normalized to 100 for weighted progress. Fill now reliably climbs 0 → 100% - Boss row layout drift: Boss name rows no longer oscillate between one and two lines. Root cause was
GetStringHeight()returning inconsistent values across refreshes on wrapping font strings (unresolved layout on first pass). M+ rows now disable wrap and use a fixed single-line height; scenario row pool restores wrap on checkout so delve criteria still wrap correctly
v1.1.0
Mythic+ Tracker
- Text-based M+ display: MythicPlusTimer-style layout — remaining time, elapsed/total, +2/+3 threshold countdowns
- Segmented timer bar: Visual countdown bar with +3/+2 threshold markers (configurable color and width)
- Blizzard timer hook: Synced via ScenarioObjectiveTracker.ChallengeModeBlock:UpdateTime (zero drift)
- Boss split timing: Records boss kill times, compares against personal best per dungeon
- Affix display: Text (dash-separated) or icons mode (configurable in Settings → Dungeons)
- Enemy forces: Blue progress bar with percentage text inside
- Death counter: Shows count + time penalty; tooltip with per-player breakdown via C_DamageMeter
- Auto-hide sections: Option to hide all non-scenario sections during M+ or Delves
- Hide tracker option: For players using external M+ addons
- Demo mode:
/carrot mplusdemoto preview,/carrot stopdemoto clear - M+ colors: Customizable +3/+2/+1/Depleted/Enemy Forces/Marker colors in Colors panel
- Marker width slider: 1-4px (Settings → Dungeons)
Recipe Tracking
- New "Recipes" section showing tracked profession recipes with reagent have/need counts
- Progress bar showing fraction of reagent types fulfilled
- Left-click opens recipe in profession UI, right-click untracks
- "Show Recipes" filter toggle, auto-enabled when a recipe is tracked
- Reagent names resolved via C_Item.GetItemInfo with fallbacks
New Features
- Quest type colors: Daily (blue), Weekly (teal), Campaign (orange) — distinct colors for titles and tier bars
- Group finder button: Eye icon on all world quest rows to open LFG
- Quest reward preview: Tooltip shows XP, gold, item, and currency rewards
- Quest countdown timers: Timed quests (e.g., Abundance events) show live MM:SS countdown
- UIWidget support: Zone event progress bars (Abundance) via C_UIWidgetManager
- Progress bar percentage: "progressbar" type objectives show % text inside the bar
- Auto quest popups: OFFER (new quest) and COMPLETE (turn-in) via GetNumAutoQuestPopUps API
- Campaign chapter progress: Tooltip shows "Campaign Quest (Chapter 3/7)"
- Minimap button: Click opens settings, shift-drag to reposition
- Delve improvements: Step title, compact 12px scenario bars with text inside, stage indicator
- Dungeons & Instances settings panel: M+ and Delve toggles
Context Menu
- Share Quest: Share quest with party members
- Abandon Quest: Abandon with Blizzard confirmation dialog (shown in red)
Bug Fixes
- Interface version: Updated to 120001 (WoW 12.0.1)
- M+ timer always 0: Uses GetActiveChallengeMapID (not GetActiveKeystoneInfo) for map ID
- M+ GetActiveKeystoneInfo: Fixed return value unpacking (2 values, not 3)
- Delve objectives not showing: Added "scenario" to default window sections with migration
- Font dropdown clipping: Custom
Carrot-Compact-Dropdownwidget with 300px max height (library-safe, no Libs/ modifications) - Text overlap on progress bar: Progress bar anchored below objective text; height accounts for content
- Wrap/ellipsis flickering: Permanent anchors at row creation, never cleared during populate
- Combat lockdown errors: Guarded C_SuperTrack/QuestMapFrame; deferred event registration
- ADDON_ACTION_FORBIDDEN: Removed COMBAT_LOG_EVENT_UNFILTERED (restricted in 12.x)
- Tracker blank after /reload in combat: Removed blanket combat bail-out from TF:Update()
- World quest type detection: Uses Enum.QuestWQType instead of Enum.QuestTagType
- World quest progress bar: Uses progressBarPct from C_TaskQuest when available
- Quest item icon overflow: Inset icon texture by 2px inside border frame
- Quest item indentation: Pooled rows reset anchors when no item button present
- Progress bar on non-numeric quests: Hidden for 0/1 objectives
- Stale objective/bar heights: Reset when text empty or bar hidden
- Boss names overlapping bars: M+ boss rows rendered directly, bar hidden
- Timer dividers on wrong row: Hidden in GetScenarioRow for pooled reuse
- Layout jump on first frame: All Get*Row functions set explicit width from parent
- Minimap button error: Uses CT.Options:Open() instead of Settings.OpenToCategory
- MakeColorOption crash: Fallback to COLOR_DEFAULTS for new color keys
UI Polish
- Filter icon changed to visible notepad icon
- Scenario criteria: compact 12px bar with count text inside
- Tier bar colors: Scenario and World Quest/default now customizable
- Timer text +4pt larger for main countdown
- Timer bar aligned with content indentation
- Enemy forces default color changed from purple to blue
- Localization: All new strings translated for 9 languages (ptBR, deDE, frFR, esES/esMX, itIT, ruRU, koKR, zhCN, zhTW)
v1.0.0
- Initial release
- Context-aware quest tracking: auto-shows quests in your current zone
- Three-tier quest visibility: Pinned, Recommended, Zone (auto-tracked)
- Quest subcategories: Pinned, Ready to Turn In, Campaign, Daily, Weekly, Zone
- World quest tracking with time remaining display
- Bonus objective tracking
- Scenario/dungeon objective tracking
- Achievement tracking with zone story auto-detection
- Quest item buttons (SecureActionButton, combat-safe)
- Proximity sorting (closest quests first)
- Subzone highlighting
- Configurable appearance: fonts, colors, sizes, outlines, shadows
- Collapsible sections and subcategories with drag-to-resize handles
- Customizable section and subcategory display order
- Filter dropdown for toggling quest types
- Profile management via AceDB (switch, copy, import/export)
- Hover highlight effect
- Blizzard default tracker suppression (combat-safe)

