local addonName, SinsToolkit = ...

if SinsToolkit.UIProvider ~= "ElvUI" or not _G.ElvUI then
    return
end

local E = unpack(_G.ElvUI)
local EP = E.Libs.EP
local Plugin = E:NewModule("SinsToolkit", "AceHook-3.0", "AceEvent-3.0")

local function EnsureDB()
    if SinsToolkit.EnsureDB then
        SinsToolkit.EnsureDB()
    end
end

local function GetToolkitValue(key)
    EnsureDB()
    return SinsToolkitDB and SinsToolkitDB[key]
end

local function SetToolkitValue(key, value)
    EnsureDB()
    SinsToolkitDB[key] = value
    if SinsToolkit.TriggerUpdate then
        SinsToolkit.TriggerUpdate(key)
    end
end

local mainMinimapLauncher

local function EnsureMainMinimapDB()
    EnsureDB()
    SinsToolkitDB.minimap = SinsToolkitDB.minimap or { hide = false }
    if SinsToolkitDB.minimap.hide == nil then
        SinsToolkitDB.minimap.hide = false
    end
    return SinsToolkitDB.minimap
end

local function OpenElvUIPluginOptions()
    local ACD = E and E.Libs and E.Libs.AceConfigDialog
    if not ACD then return end

    local frame = E.Config_GetWindow and E:Config_GetWindow()
    if not frame then
        if E.ToggleOptions then
            E:ToggleOptions()
        else
            ACD:Open("ElvUI")
        end
    end

    local function SelectToolkitGroup()
        if ACD.OpenFrames and ACD.OpenFrames.ElvUI and ACD.SelectGroup then
            ACD:SelectGroup("ElvUI", "sinsToolkit")
        end
    end

    if C_Timer then
        C_Timer.After(0, SelectToolkitGroup)
    else
        SelectToolkitGroup()
    end
end

function SinsToolkit.RefreshMainMinimapButton()
    local DBIcon = LibStub and LibStub("LibDBIcon-1.0", true)
    if not DBIcon or not SinsToolkitDB then return end

    local db = EnsureMainMinimapDB()
    if db.hide then
        DBIcon:Hide(addonName)
    else
        DBIcon:Show(addonName)
    end
    if DBIcon.Refresh then
        DBIcon:Refresh(addonName, db)
    end
end

local function CreateMainMinimapButton()
    if mainMinimapLauncher then return end

    local LDB = LibStub and LibStub("LibDataBroker-1.1", true)
    local DBIcon = LibStub and LibStub("LibDBIcon-1.0", true)
    if not LDB or not DBIcon then return end

    mainMinimapLauncher = LDB:NewDataObject(addonName, {
        type = "launcher",
        text = "Sin's Toolkit: QOL",
        icon = "Interface\\AddOns\\SinsToolkit\\Media\\Logo.tga",
        OnClick = function(_, button)
            if button == "RightButton" then
                if SinsToolkit.TeleportHub and SinsToolkit.TeleportHub.Open then
                    SinsToolkit.TeleportHub:Open()
                end
            else
                OpenElvUIPluginOptions()
            end
        end,
        OnTooltipShow = function(tooltip)
            tooltip:AddLine("|cffb868dcSin's Toolkit: QOL|r", 1, 1, 1)
            tooltip:AddLine("Left-Click: Open ElvUI plugin settings", 0.74, 0.45, 0.95)
            tooltip:AddLine("Right-Click: Open Teleport Hub", 0.85, 0.66, 0.30)
        end,
    })

    DBIcon:Register(addonName, mainMinimapLauncher, EnsureMainMinimapDB())
    SinsToolkit.RefreshMainMinimapButton()
end

local uiErrorListCache
local function GetUIErrorList()
    if not uiErrorListCache then
        uiErrorListCache = {}
        if GetGameMessageInfo then
            for i = 1, 1600 do
                local stringID = GetGameMessageInfo(i)
                local text = stringID and _G[stringID]
                if stringID and stringID:match("^ERR_") and text and text ~= "" then
                    uiErrorListCache[#uiErrorListCache + 1] = { key = stringID, text = text, searchText = (stringID .. " " .. text):lower() }
                end
            end
        end
        table.sort(uiErrorListCache, function(a, b) return a.text < b.text end)
    end
    return uiErrorListCache
end

local errorSearchString = ""

local profileVaultBackupName = ""
local selectedProfileVaultBackup = ""
local cvarSearchString = ""
local cvarModifiedOnly = false
local selectedCVar = ""
local cvarPendingValue = ""
local uiErrorGroupCache = {}

local GEAR_SLOT_VALUES = {
    [INVSLOT_HEAD] = HEADSLOT,
    [INVSLOT_NECK] = NECKSLOT,
    [INVSLOT_SHOULDER] = SHOULDERSLOT,
    [INVSLOT_BACK] = BACKSLOT,
    [INVSLOT_CHEST] = CHESTSLOT,
    [INVSLOT_WRIST] = WRISTSLOT,
    [INVSLOT_HAND] = HANDSSLOT,
    [INVSLOT_WAIST] = WAISTSLOT,
    [INVSLOT_LEGS] = LEGSSLOT,
    [INVSLOT_FEET] = FEETSLOT,
    [INVSLOT_FINGER1] = FINGER0SLOT,
    [INVSLOT_FINGER2] = FINGER1SLOT,
    [INVSLOT_TRINKET1] = TRINKET0SLOT,
    [INVSLOT_TRINKET2] = TRINKET1SLOT,
    [INVSLOT_MAINHAND] = MAINHANDSLOT,
    [INVSLOT_OFFHAND] = SECONDARYHANDSLOT,
}

local function GetProfileVaultBackupValues()
    EnsureDB()
    local values = { [""] = "None" }
    if not SinsToolkit.ProfileVault then return values end

    for _, backup in ipairs(SinsToolkit.ProfileVault:GetBackups()) do
        local label = ("%s - %s - %s"):format(
            backup.name or "Backup",
            backup.className or backup.classToken or "Unknown",
            backup.createdAt or "Unknown date"
        )
        values[backup.id] = label
    end
    return values
end

local function GetSelectedProfileVaultBackup()
    EnsureDB()
    return selectedProfileVaultBackup ~= ""
        and SinsToolkitDB.profileVaultBackups
        and SinsToolkitDB.profileVaultBackups[selectedProfileVaultBackup]
        or nil
end

local function GetProfileVaultSummary()
    local backup = GetSelectedProfileVaultBackup()
    if not backup then
        return "Select a saved backup to view its details."
    end

    local actionCount = backup.actions and #backup.actions or 0
    local accountMacroCount = backup.macros and backup.macros.account and #backup.macros.account or 0
    local characterMacroCount = backup.macros and backup.macros.character and #backup.macros.character or 0
    local bindingCount = backup.bindings and #backup.bindings or 0

    return ("Backup: %s\nCharacter: %s\nRealm: %s\nClass: %s\nDate: %s\n\nIncludes: %d action slots, %d account macros, %d character macros, %d key bindings."):format(
        backup.name or "Backup",
        backup.character or "Unknown",
        backup.realm or "Unknown",
        backup.className or backup.classToken or "Unknown",
        backup.createdAt or "Unknown",
        actionCount,
        accountMacroCount,
        characterMacroCount,
        bindingCount
    )
end

local function GetCVarValues()
    if not SinsToolkit.CVars then
        return { [""] = "CVar browser unavailable" }
    end
    return SinsToolkit.CVars:GetValues(cvarSearchString, cvarModifiedOnly)
end

local function GetSelectedCVarInfo()
    if not SinsToolkit.CVars or selectedCVar == "" then
        return nil
    end
    return SinsToolkit.CVars:GetInfo(selectedCVar)
end

local function GetSelectedCVarSummary()
    if not SinsToolkit.CVars then
        return "The CVar browser module is not loaded."
    end
    return SinsToolkit.CVars:GetSummary(selectedCVar)
end

local function GetModifiedCVarsSummary()
    if not SinsToolkit.CVars then
        return "The CVar browser module is not loaded."
    end
    return SinsToolkit.CVars:GetModifiedSummary(12)
end

local function GetGearProviderStatus()
    local pawnActive = SinsToolkit.IsPawnAvailable and SinsToolkit.IsPawnAvailable()
    local classCodexActive = SinsToolkit.IsClassCodexAvailable and SinsToolkit.IsClassCodexAvailable()
    return ("|cffd8a84aScoring Providers|r\nPawn: %s\nClassCodex: %s\nMode: %s"):format(
        pawnActive and "|cff00ff00Active|r" or "|cffff4040Inactive|r",
        classCodexActive and "|cff00ff00Active|r" or "|cffff4040Inactive|r",
        SinsToolkit.GetGearProviderModeText and SinsToolkit.GetGearProviderModeText() or "Unavailable."
    )
end

local function UpdateErrorToggles()
    if not E.Options.args.sinsToolkit then return end
    local args = E.Options.args.sinsToolkit.args.uiErrors.args
    
    for k in pairs(args) do
        if type(k) == "string" and k:match("^ERR_") then args[k] = nil end
    end
    
    local search = (errorSearchString or ""):lower()
    local blocked = SinsToolkitDB and SinsToolkitDB.blockedUIErrors or {}
    local sorted = uiErrorGroupCache[search]

    if not sorted then
        local groupedByText = {}
        sorted = {}
        for _, err in ipairs(GetUIErrorList()) do
            if search == "" or err.searchText:find(search, 1, true) then
                local grouped = groupedByText[err.text]
                if grouped then
                    grouped.keys[#grouped.keys + 1] = err.key
                    grouped.searchText = grouped.searchText .. " " .. err.searchText
                else
                    grouped = {
                        key = err.key,
                        keys = { err.key },
                        text = err.text,
                        searchText = err.searchText,
                    }
                    groupedByText[err.text] = grouped
                    sorted[#sorted + 1] = grouped
                end
            end
        end

        table.sort(sorted, function(a, b)
            return a.text < b.text
        end)
        uiErrorGroupCache[search] = sorted
    end
    
    table.sort(sorted, function(a, b)
        local function IsAnyBlocked(err)
            for _, key in ipairs(err.keys) do
                if blocked[key] == true then return true end
            end
            return false
        end

        local aBlocked = IsAnyBlocked(a)
        local bBlocked = IsAnyBlocked(b)
        if aBlocked ~= bBlocked then return aBlocked end
        return a.text < b.text
    end)
    
    for i, err in ipairs(sorted) do
        args[err.key] = {
            type = "toggle",
            name = err.text,
            order = 1000 + i,
            width = "double",
            get = function()
                EnsureDB()
                if not SinsToolkitDB.blockedUIErrors then return false end
                for _, key in ipairs(err.keys) do
                    if SinsToolkitDB.blockedUIErrors[key] then return true end
                end
                return false
            end,
            set = function(_, value)
                EnsureDB()
                if not SinsToolkitDB.blockedUIErrors then SinsToolkitDB.blockedUIErrors = {} end
                for _, key in ipairs(err.keys) do
                    SinsToolkitDB.blockedUIErrors[key] = value or nil
                end
                if SinsToolkit.TriggerUpdate then SinsToolkit.TriggerUpdate("blockedUIErrors") end
                UpdateErrorToggles()
                if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
            end,
        }
    end
end

if C_Timer then
    C_Timer.After(1, GetUIErrorList)
    C_Timer.After(5, function()
        if SinsToolkit.CVars then
            SinsToolkit.CVars:GetValues("", false)
        end
    end)
end

local BRAND_NAME = "|cffb868dcSin's|r |cffe8b4ecToolkit: QOL|r"
local BRAND_SUBTITLE = "|cffbca7d6Quality-of-life enhancements|r"
local BRAND_LOGO = "Interface\\AddOns\\SinsToolkit\\Media\\Logo.tga"
local BRAND_BANNER = "Interface\\AddOns\\SinsToolkit\\Media\\Banner.tga"
local BANNER_ASPECT = 640 / 213
local BANNER_MIN_HEIGHT = 150
local BANNER_MAX_HEIGHT = 260

local function GetBannerSize()
    local configFrame = E and E.Libs and E.Libs.AceConfigDialog and E.Libs.AceConfigDialog.OpenFrames and E.Libs.AceConfigDialog.OpenFrames.ElvUI
    local parentWidth = (configFrame and configFrame.frame and configFrame.frame:GetWidth())
        or (UIParent and UIParent:GetWidth())
        or 1280
    local width = math.floor(math.max(520, math.min(920, parentWidth - 420)))
    local height = math.floor(math.max(BANNER_MIN_HEIGHT, math.min(BANNER_MAX_HEIGHT, width / BANNER_ASPECT)))
    width = math.floor(height * BANNER_ASPECT)
    return width, height
end

local function GradientText(text)
    local startColor = { 126, 79, 168 }
    local endColor = { 246, 239, 255 }
    local out, visibleIndex = {}, 0
    local visibleCount = 0
    for i = 1, #text do
        if text:sub(i, i) ~= " " then
            visibleCount = visibleCount + 1
        end
    end

    for i = 1, #text do
        local char = text:sub(i, i)
        if char == " " then
            out[#out + 1] = char
        else
            visibleIndex = visibleIndex + 1
            local t = visibleCount > 1 and ((visibleIndex - 1) / (visibleCount - 1)) or 1
            local r = math.floor(startColor[1] + ((endColor[1] - startColor[1]) * t) + 0.5)
            local g = math.floor(startColor[2] + ((endColor[2] - startColor[2]) * t) + 0.5)
            local b = math.floor(startColor[3] + ((endColor[3] - startColor[3]) * t) + 0.5)
            out[#out + 1] = ("|cff%02x%02x%02x%s|r"):format(r, g, b, char)
        end
    end
    return table.concat(out)
end

local function PageIntro(title, body)
    return {
        type = "description",
        name = "|cffb868dc" .. title .. "|r\n|cffbca7d6" .. body .. "|r",
        fontSize = "medium",
        order = 0,
    }
end

local function GetChangelogText()
    return [[|cffd8a84aVersion 2.37 - June 24, 2026|r
- Fixed release note generation so GitHub and CurseForge receive normal Markdown headers and bullet lists.

|cffd8a84aVersion 2.36 - June 24, 2026|r
- Fixed invalid Frame Mover startup event registration.
- Fixed Frame Mover initialization after login and reload so moving works without toggling the feature.
- Fixed Ctrl + Mouse Wheel scaling initialization after login and reload.

|cffd8a84aVersion 2.35 - June 24, 2026|r
- Fixed Frame Mover initialization after login and reload so moving works without toggling the feature.
- Fixed Ctrl + Mouse Wheel scaling initialization and scale saving.
- Removed legacy third-party UI provider support and external frame-mover conflict checks.
- Replaced addon-name conflict warnings with generic frame movement guidance.
- Added and refreshed the Modified / Enabled CVars summary section.
- Improved startup, Frame Mover, and CVar refresh performance.

|cffd8a84aVersion 2.34 - June 24, 2026|r
- Updated Frame Tools conflict handling so frame moving is controlled by Sin's Toolkit settings instead of installed addon names.
- Kept Frame Mover and Ctrl + Mouse Wheel scaling active when Frame Mover is enabled.
- Updated Frame Tools wording to recommend using one frame-moving feature at a time.

|cffd8a84aVersion 2.33 - June 24, 2026|r
- Fixed Frame Mover re-activation so supported windows regain their Toolkit drag area after toggling the feature.
- Fixed Ctrl + Mouse Wheel scaling by handling scale input on the Toolkit drag area as well as the parent frame.
- Improved Frame Mover reset behavior so reset position restores the captured original placement when possible.
- Added a Modified / Enabled CVars summary section with current and default values.
- Improved CVar summary caching and Frame Mover event checks to avoid unnecessary refresh work.
- Added a manual Commit-And-Push helper script for reviewed local changes.

|cffd8a84aVersion 2.32 - June 24, 2026|r
- Converted the standalone Information page to use the shared Toolkit card system added in 2.24.
- Reworked the ElvUI Information page into compact dashboard-style status blocks with action buttons.
- Replaced long Information-page documentation with Toolkit Status, Feature Status, Environment, and Quick Actions sections.
- Updated Auto Unshift tooltip wording to match the cleaner Feral/Guardian-only explanation.
- Updated Frame Tools conflict wording so it recommends choosing one frame-moving feature owner instead of disabling an entire multi-feature addon.
- Removed legacy third-party UI integration references so Sin's Toolkit is standardized as Standalone + ElvUI.
- Fixed the ElvUI Information panel opening error caused by a stale header refresh after the dashboard redesign.
- Cleaned up release note generation so GitHub and CurseForge changelogs use normal Markdown.

|cffd8a84aVersion 2.24 - June 23, 2026|r
- Added a shared Sin's Toolkit UI component foundation for cards, sections, status badges, action buttons, warning panels, and branded dialogs.
- Added shared dialog helpers for warnings, confirmations, notifications, and future modal prompts.
- Moved the Frame Tools warning onto the shared Toolkit dialog framework so it raises above ElvUI, Blizzard settings, and Toolkit windows.
- Started the dashboard/card modernization foundation without removing or changing existing feature behavior.

|cffd8a84aVersion 2.23 - June 23, 2026|r
- Redesigned the Information page into a compact dashboard with Provider, Status, Minimap Button, Feature Status, Environment, and Frame Tools sections.
- Rewrote the Auto Unshift tooltip to clearly explain Feral/Guardian activation, GSE usefulness, UI-error suppression, and safe out-of-combat CVar handling.
- Added a styled Frame Tools notice for possible frame movement overlap.
- Added a right-click Frame Mover reset menu with Reset Position, Reset Scale, and Reset Position and Scale actions.
- Added the per-frame scale saved-variable table while preserving existing frame mover scale data.

|cffd8a84aVersion 2.22 - June 23, 2026|r
- Frame Mover now disables Sin's Toolkit drag handling when an external mover owner is selected.
- Added an in-game warning for possible duplicate frame movement behavior.
- Hidden Toolkit mover drag strips now stop capturing clicks when Sin's Toolkit does not own frame moving.

|cffd8a84aVersion 2.21 - June 23, 2026|r
- Improved Frame Mover active-state checks.
- Frame Mover now uses clearer active-feature wording.
- Updated Frame Tools text to explain No Auto Close and frame moving behavior.

|cffd8a84aVersion 2.20 - June 23, 2026|r
- Locked Auto Unshift for Druids to Feral or Guardian specialization only.
- Auto Unshift now requires both Feral/Guardian spec and Cat/Bear Form before changing Blizzard's autoUnshift behavior.
- Balance and Restoration Druids now keep Blizzard's default autoUnshift behavior even if they enter a shapeshift form.

|cffd8a84aVersion 2.19 - June 23, 2026|r
- Limited Auto Unshift for Druids to Cat Form and Bear Form only.
- Auto Unshift now restores Blizzard's autoUnshift behavior when you are not in Cat Form or Bear Form.
- The shapeshift-form UI error is now hidden only while Auto Unshift is active in Cat Form or Bear Form.

|cffd8a84aVersion 2.18 - June 23, 2026|r
- Fixed a protected-action block caused by Auto Unshift writing the autoUnshift CVar during combat lockdown.
- Auto Unshift now writes autoUnshift only outside combat and keeps the safer value while the Druid feature is active.
- Updated Auto Unshift wording in standalone and ElvUI options to describe the safer Retail-compatible behavior.

|cffd8a84aVersion 2.17 - June 23, 2026|r
- Fixed the Bug Catcher minimap bag so it creates and refreshes immediately when captured errors exist.
- Raised the Bug Catcher minimap button frame level so minimap skins are less likely to bury it.
- Made /stkbug minimap recreate the bag button when it is toggled back on.

|cffd8a84aVersion 2.16 - June 23, 2026|r
- Moved the addon list category from UI Overhaul to Quality of Life.
- Repainted standalone navigation buttons on show/page changes so theme borders no longer wait for mouseover.
- Tightened the standalone shell opacity and flat-panel styling so it reads closer to an ElvUI-style options window.
- Reworked Teleport Hub grid spacing, grid label shortening, and favorite marker placement to prevent scrunched teleport names.

|cffd8a84aVersion 2.15 - June 23, 2026|r
- Reworked the standalone Toolkit shell into an independent ElvUI-inspired dark options panel.
- Added flatter dark panels, compact left category navigation, active-page highlighting, and Sin's purple/gold branding.
- Kept ElvUI plugin mode native while giving non-ElvUI mode a cleaner standalone options layout.
- Standalone mode now uses the same independent shell with stable Toolkit accent colors.
- Added responsive text-width clamping so page descriptions fit better when the window is resized.

|cffd8a84aVersion 2.14 - June 23, 2026|r
- Removed unsafe external panel-scale coupling so standalone windows no longer balloon from other UI settings.
- Added sane restore clamps for the standalone Toolkit window after resize or reload.
- Cleaned up embedded Teleport Hub layout so duplicate floating-toolbar controls no longer overlap the Teleports options page.
- Increased Teleport Hub list/menu row spacing so labels and favorite controls do not collide.

|cffd8a84aVersion 2.13 - June 23, 2026|r
- Cleaned up provider detection for standalone mode without modifying or depending on other UI addon files.
- Standalone mode now uses clean Blizzard-style framing when ElvUI is not loaded.
- Added a Scotty-style Teleport Hub menu view with a dropdown selector, selected teleport details, favorite controls, and a secure Use button.
- Added Teleport Hub Menu/Grid/List view choices, /stktp menu, /stktp grid, /stktp list, and a Rescan Teleports action.
- Improved Teleport Hub search so it also matches destination, category, spell ID, and item ID.

|cffd8a84aVersion 2.12 - June 22, 2026|r
- Removed the old Export Strings storage page from standalone and ElvUI plugin mode.
- Added a saved-variable cleanup so the legacy export string table is removed on load.
- Updated Gear Scanner's Pawn integration to prefer Pawn's definitive upgrade result before falling back to ClassCodex.
- Cleared Gear Scanner's local score cache at the start of each bag scan so provider decisions stay fresh.
- Reviewed PortalHub's layout approach and kept Sin's Toolkit Teleport Hub self-contained without adding external dependencies.

|cffd8a84aVersion 2.11 - June 21, 2026|r
- Fixed Auto Unshift for Druids so it no longer requires GSE to be installed.
- Auto Unshift now re-applies after login, entering world, and specialization changes.
- Auto Unshift now always restores Blizzard's autoUnshift CVar when the feature stops applying.
- Hardened CVar writes and Bug Catcher script-error CVar handling so rejected client writes do not throw Lua errors.
- Hardened Quick Link name/realm parsing against protected secret values.

|cffd8a84aVersion 2.10 - June 21, 2026|r
- Added the exact Retail ProfessionsBookFrame target so the professions book can be moved.
- Added delayed Frame Mover re-apply hooks when Blizzard panels open, fixing late-created windows such as Delve companion configuration.

|cffd8a84aVersion 2.9 - June 21, 2026|r
- Improved Frame Mover support for Retail profession windows by adding a safe title drag strip and additional profession frame names.
- Added Delve companion and dashboard frames to Frame Mover's supported-frame list.
- Excluded Delve companion and dashboard windows from No Auto Close so Blizzard can close them normally.

|cffd8a84aVersion 2.8 - June 21, 2026|r
- Removed the discontinued frame coloring feature and its related options from Sin's Toolkit.
- Scrubbed the old module, saved-variable defaults, status rows, tabs, and options from both UI modes.

|cffd8a84aVersion 2.7 - June 21, 2026|r
- Fixed Toolkit feature ownership so standalone features automatically work when matching external addons are not installed.
- Restored self-contained Ctrl+C Quick Link behavior without saving or clearing WoW keybindings.

|cffd8a84aVersion 2.6 - June 21, 2026|r
- Added Toolkit-owned Frame Tools for No Auto Close settings, default panel placement, and supported-frame movement/scaling.
- Added guidance so Sin's Toolkit can avoid duplicate frame handling when similar features are installed.
- Added matching standalone Frame Tools while keeping the ElvUI plugin layout native to ElvUI.
- Updated reusable standalone buttons and sidebar navigation with a darker violet treatment.

|cffd8a84aVersion 2.5 - June 19, 2026|r
- Bug Catcher now suppresses Blizzard's ScriptErrorsFrame while Sin's Toolkit is handling Lua errors.
- Toolkit preserves the prior script error CVar value and restores it if Bug Catcher is disabled or blocked by BugSack.
- Added an extra frame-hide guard for clients or UI skins that still try to show the standard Lua error window.

|cffd8a84aVersion 2.4 - June 19, 2026|r
- Bug Catcher now keeps captured Lua errors inside Sin's Toolkit instead of forwarding them to Blizzard's default Lua error popup.
- This prevents duplicate error windows when the Toolkit Bug Catcher is enabled.

|cffd8a84aVersion 2.3 - June 16, 2026|r
- Reviewed the pasted official 12.0.7 API change list against Sin's Toolkit runtime API usage.
- Confirmed no active calls depend on the 12.0.7 removed APIs.
- Hardened Gear Scanner's final unbound-item check so it safely exits if the Retail item binding API is unavailable.

|cffd8a84aVersion 2.2 - June 16, 2026|r
- Reviewed Sin's Toolkit against the 12.0.7 API-sensitive areas.
- Removed automatic Quick Link keybinding writes on login.
- Quick Link bindings remain available through WoW's normal Key Bindings panel.
- Confirmed existing Retail-safe guards for gossip quest APIs, protected/secret values, CVar access, and secure teleport/profession actions.

|cffd8a84aVersion 2.1 - June 13, 2026|r
- Cleaned up the standalone Blizzard Settings launcher and removed an unreachable legacy options table.
- Removed the old UIDropDownMenu-based path from the standalone Toolkit window.
- Standalone dropdown-style choices now use the Toolkit's safer cycle-button control.
- Fixed the ElvUI minimap launcher so left-click opens and selects the Sin's Toolkit plugin settings.
- Updated the in-game changelog for the 2.1 release.

|cffd8a84aVersion 2.0 - June 12, 2026|r
- Converted Sin's Toolkit: QOL into one addon that supports both modes: ElvUI plugin mode when ElvUI is installed, and standalone mode when ElvUI is not installed.
- Added provider guards so the standalone window does not activate while ElvUI is running.
- Restored standalone Information and Changelog pages.
- Restored the full standalone Gear Scanner options for loot rolls, soulbinding, Pawn use, tooltips, minimum score, unbound upgrades, max-level behavior, and locked slots.
- Split standalone settings into clear pages while preserving the existing SinsToolkitDB saved-variable keys from the ElvUI plugin version.
- Moved detailed feature guidance into the individual sections and kept the Information page as a general overview.
- Centered and resized the standalone Information banner dynamically as the Toolkit window changes size.
- Updated the Information panel to explain the dual ElvUI/standalone behavior and minimap button controls.

|cffd8a84aVersion 1.1.56 - June 11, 2026|r
- Fixed a WoW Lua parser warning caused by the in-game changelog being too deeply nested inside the ElvUI options table.
- Kept the recent release notes visible while simplifying the changelog expression so the options panel loads cleanly.

|cffd8a84aVersion 1.1.55 - June 11, 2026|r
- Made Teleport Hub favorite controls much more visible with a clear + Fav / Fav button on each entry.
- Added empty Favorites helper text and adjusted Teleport Hub spacing so favorite buttons do not crowd teleport labels.

|cffd8a84aVersion 1.1.54 - June 11, 2026|r
- Fixed the ElvUI options validation error caused by dynamic banner image sizing callbacks.
- Banner dimensions are now calculated before options registration so AceConfig receives valid numeric image sizes.

|cffd8a84aVersion 1.1.53 - June 11, 2026|r
- Fixed Teleport Hub Favorites so the section is always visible, including an empty Favorites (0) state.
- Refined the Information banner sizing so the existing artwork scales with the ElvUI options panel while preserving aspect ratio and padding.

|cffd8a84aVersion 1.1.52 - June 11, 2026|r
- Fixed a Delve runtime error caused by parsing Blizzard secret unit GUID strings during Automate Gossip checks.
- Automate Gossip now safely skips NPC allow-list parsing when the interaction target is protected by the client.
- Hardened similar Wowhead Quick Link unit/name parsing paths against secret values without forcing protected strings through tostring().

|cffd8a84aVersion 1.1.51 - June 10, 2026|r
- Polished Teleport Hub with Favorites, Recently Used, remembered collapsible sections, and Expand/Collapse controls.
- Added a Bug Catcher option to only show the minimap button when errors exist, enabled by default for new installs while preserving existing users.
- Updated the Information tab so it documents Advanced Settings, provider status, Fallback Mode, Bug Catcher self-test, Profile Vault confirmations, and Teleport Hub's new layout tools.
- Softened the purple sidebar gradient and adjusted the banner dimensions to reduce cramped or distorted presentation in ElvUI options.
- Raised Profile Vault confirmation dialogs above addon windows so restore/delete prompts are harder to miss.

|cffd8a84aVersion 1.1.50 - June 10, 2026|r
- Centralized saved variable defaults in Core and added database versioning for safer future migrations.
- Added Profile Vault confirmation prompts before restore or delete actions touch action bars, macros, or keybindings.
- Reworked UI error filtering to use a dedicated forwarding frame instead of replacing UIErrorsFrame's OnEvent script.
- Added optional BugSack compatibility mode, off by default for new installs and preserved for existing users through migration.
- Added /stkbug test and a Bug Catcher self-test button.
- Added Gear Scanner provider status and a top-level Advanced settings page for higher-risk options.]]
end

local function GetInformationOverviewText()
    return "|cffd8a84aEnvironment|r\n"
        .. "Sin's Toolkit adapts to the active UI framework.\n"
        .. "Current Mode: |cffb868dcElvUI|r"
end

local function GetInformationProviderStatusText()
    return "|cffd8a84aToolkit Status|r\n"
        .. "|cffbca7d6Provider:|r ElvUI\n"
        .. "|cffbca7d6Status:|r |cff00ff00Ready|r"
end

local function GetInformationFeatureStatusText()
    local enabledRows = {}
    local disabledRows = {}

    local function AddStatus(label, enabled, suffix, disabledText)
        local row
        if enabled then
            row = "|cff00ff00+|r " .. label .. (suffix or "")
            enabledRows[#enabledRows + 1] = row
        else
            row = "|cffff4040-|r " .. label .. (disabledText and (" |cffff4040(" .. disabledText:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "") .. ")|r") or "") .. (suffix or "")
            disabledRows[#disabledRows + 1] = row
        end
    end

    local bugBlocked = SinsToolkit.BugCatcher and SinsToolkit.BugCatcher.IsBlockedByExternalAddon and SinsToolkit.BugCatcher:IsBlockedByExternalAddon()

    AddStatus("Gear Scanner", GetToolkitValue("gearScannerEnabled"))
    AddStatus("No Auto Close Panels", GetToolkitValue("keepPanelsOpen"))
    AddStatus("Frame Mover", GetToolkitValue("frameMoverEnabled"))
    AddStatus("UI Error Filtering", GetToolkitValue("hideOutOfRangeErrors"))
    AddStatus("Auto Gossip", GetToolkitValue("automateGossip"), " (" .. (GetToolkitValue("automateGossipModifier") or "SHIFT") .. ")")
    AddStatus("Auto Unshift for Druids", GetToolkitValue("gseAutoUnshift"))
    AddStatus("Druid Auto Flight Form", GetToolkitValue("druidAutoFlightForm"))
    AddStatus("Profession Item Hotkey", GetToolkitValue("professionItemHotkeyEnabled"), " (" .. (GetToolkitValue("professionItemHotkeyModifier") or "ALT") .. ")")
    AddStatus("Quick Links", GetToolkitValue("wowheadSearchEnabled"))
    AddStatus("Teleport Hub", GetToolkitValue("teleportHubEnabled"))
    AddStatus("Bug Catcher", GetToolkitValue("bugCatcherEnabled") and not bugBlocked, nil, bugBlocked and "|cffff4040Disabled by external BugSack|r" or nil)
    AddStatus("Identifier Hover Mode", GetToolkitValue("identifierEnabled"))

    return "|cffd8a84aFeature Status|r\n"
        .. "|cffbca7d6Gear Mode:|r " .. (SinsToolkit.GetGearProviderModeText and SinsToolkit.GetGearProviderModeText() or "Unavailable.") .. "\n"
        .. "\n|cff00ff00Enabled|r\n" .. (#enabledRows > 0 and table.concat(enabledRows, "\n") or "None") .. "\n"
        .. "\n|cffff4040Disabled|r\n" .. (#disabledRows > 0 and table.concat(disabledRows, "\n") or "None") .. "\n"
end

local AUTO_UNSHIFT_TOOLTIP = "Auto Unshift for Druids\n\nWhen enabled, this feature only activates while playing a Feral or Guardian Druid.\n\nIf you are not Feral or Guardian, this feature remains inactive and does not modify any settings.\n\nHelpful for players using GSE (Gnome Sequencer Enhanced), though GSE is not required.\n\nWhile active:\n- Prevents unwanted auto-unshifting behavior\n- Suppresses the SPELL_FAILED_NOT_SHAPESHIFT UI error\n\nChanges are applied safely outside combat and automatically restored when no longer needed."

function Plugin:InsertOptions()
    if not E.Options or not E.Options.args then return end
    local bannerWidth, bannerHeight = GetBannerSize()

    E.Options.args.sinsToolkit = {
        type = "group",
        name = "|T" .. BRAND_LOGO .. ":16:16|t " .. GradientText("Sin's Toolkit: QOL"),
        desc = "Quality-of-life enhancements for World of Warcraft.",
        order = 1000,
        childGroups = "tree",
        args = {
            info = {
                type = "group",
                name = GradientText("Information"),
                order = 1,
                args = {
                    brand = {
                        type = "description",
                        name = "\n",
                        image = BRAND_BANNER,
                        imageWidth = bannerWidth,
                        imageHeight = bannerHeight,
                        fontSize = "medium",
                        order = 0,
                    },
                    tagline = {
                        type = "description",
                        name = GetInformationProviderStatusText,
                        fontSize = "medium",
                        order = 0.5,
                    },
                    mainMinimap = {
                        type = "toggle",
                        name = "Show Main Minimap Button",
                        desc = "Shows the Sin's Toolkit launcher on the minimap. In ElvUI mode, left-click opens this plugin panel and right-click opens the Teleport Hub.",
                        order = 0.75,
                        get = function()
                            return not EnsureMainMinimapDB().hide
                        end,
                        set = function(_, value)
                            EnsureMainMinimapDB().hide = not value
                            if SinsToolkit.RefreshMainMinimapButton then
                                SinsToolkit.RefreshMainMinimapButton()
                            end
                        end,
                    },
                    featureStatus = {
                        type = "description",
                        name = GetInformationFeatureStatusText,
                        fontSize = "medium",
                        order = 0.9,
                    },
                    environment = {
                        type = "description",
                        name = GetInformationOverviewText,
                        fontSize = "medium",
                        order = 1,
                    },
                    actionsHeader = {
                        type = "header",
                        name = "Quick Actions",
                        order = 1.1,
                    },
                    runGearScan = {
                        type = "execute",
                        name = "Run Gear Scan",
                        desc = "Runs /stk scan with your current Gear Scanner settings.",
                        order = 1.2,
                        func = function()
                            if SinsToolkit.ForceGearScan then SinsToolkit.ForceGearScan() end
                        end,
                    },
                    openTeleports = {
                        type = "execute",
                        name = "Open Teleport Hub",
                        desc = "Opens the Teleport Hub. Slash command: /stktp.",
                        order = 1.3,
                        func = function()
                            if SinsToolkit.TeleportHub and SinsToolkit.TeleportHub.Open then
                                SinsToolkit.TeleportHub:Open()
                            end
                        end,
                    },
                    openBugCatcher = {
                        type = "execute",
                        name = "Open Bug Catcher",
                        desc = "Opens the Bug Catcher. Slash command: /stkbug.",
                        order = 1.4,
                        func = function()
                            if SinsToolkit.BugCatcher and SinsToolkit.BugCatcher.OpenSack then
                                SinsToolkit.BugCatcher:OpenSack()
                            end
                        end,
                    },
                    openProfileVault = {
                        type = "execute",
                        name = "Open Profile Vault",
                        desc = "Switches to the Profile Vault page.",
                        order = 1.5,
                        func = function()
                            if E.Libs and E.Libs.AceConfigDialog then
                                E.Libs.AceConfigDialog:SelectGroup("ElvUI", "sinsToolkit", "profiles")
                            end
                        end,
                    },
                },
            },
            gear = {
                type = "group",
                name = GradientText("Gear Scanner"),
                order = 4,
                args = {
                    intro = PageIntro("Gear Scanner", "Automatic and manual gear scanning powered by Pawn when installed, with ClassCodex stat priorities as fallback."),
                    providerStatus = {
                        type = "description",
                        name = GetGearProviderStatus,
                        fontSize = "medium",
                        order = 0.5,
                    },
                    enabled = {
                        type = "toggle",
                        name = "Enable Gear Scanner",
                        desc = "Master switch for Gear Scanner. When enabled, Sin's Toolkit can scan bag items, compare them against your equipped gear, and equip upgrades using Pawn when installed or ClassCodex fallback scoring.",
                        order = 1,
                        disabled = function() return SinsToolkit.IsGearScannerProviderAvailable and not SinsToolkit.IsGearScannerProviderAvailable() end,
                        get = function() return GetToolkitValue("gearScannerEnabled") end,
                        set = function(_, value) SetToolkitValue("gearScannerEnabled", value) end,
                    },
                    maxLevel = {
                        type = "toggle",
                        name = "Disable at Max Level",
                        desc = "Turns Gear Scanner off automatically when your character reaches the current expansion level cap. This keeps leveling automation from interfering with max-level gearing decisions.",
                        order = 2,
                        get = function() return GetToolkitValue("gearScannerMaxLevelDisable") end,
                        set = function(_, value) SetToolkitValue("gearScannerMaxLevelDisable", value) end,
                    },
                    silent = {
                        type = "toggle",
                        name = "Silent Mode",
                        desc = "Suppresses Gear Scanner chat messages when items are equipped, scans are run, or loot-roll actions are taken.",
                        order = 3,
                        get = function() return GetToolkitValue("gearScannerSilent") end,
                        set = function(_, value) SetToolkitValue("gearScannerSilent", value) end,
                    },
                    scan = {
                        type = "execute",
                        name = "Force Gear Scan",
                        desc = "Runs an immediate bag scan using the current Gear Scanner settings. This is the same action as /stk scan.",
                        order = 4,
                        func = function()
                            if SinsToolkit.ForceGearScan then
                                SinsToolkit.ForceGearScan()
                            end
                        end,
                    },
                    behaviorHeader = {
                        type = "header",
                        name = "Advanced Auto-Equip Behavior",
                        order = 5,
                    },
                    scanOnLoot = {
                        type = "toggle",
                        name = "Scan When Bags Change",
                        desc = "Automatically scans your bags after loot or bag changes. Turn this off if you only want Gear Scanner to run when you click Force Gear Scan or type /stk scan.",
                        order = 6,
                        get = function() return GetToolkitValue("gearScannerScanOnLoot") end,
                        set = function(_, value) SetToolkitValue("gearScannerScanOnLoot", value) end,
                    },
                    allowUnbound = {
                        type = "toggle",
                        name = "Allow Unbound Upgrades",
                        desc = "Allows Gear Scanner to equip unbound upgrades from your bags. Leave this off if you want BoE items left untouched for selling, trading, mailing, or auctioning.",
                        order = 7,
                        get = function() return GetToolkitValue("gearScannerAllowUnbound") end,
                        set = function(_, value) SetToolkitValue("gearScannerAllowUnbound", value) end,
                    },
                    minimumUpgrade = {
                        type = "range",
                        name = "Minimum Upgrade Score",
                        desc = "Requires a new item to beat your equipped item by at least this many score points before it is equipped. Raise this if you only want larger upgrades equipped.",
                        order = 8,
                        min = 0,
                        max = 500,
                        step = 5,
                        get = function() return GetToolkitValue("gearScannerMinimumUpgrade") or 0 end,
                        set = function(_, value) SetToolkitValue("gearScannerMinimumUpgrade", value) end,
                    },
                    pawnHeader = {
                        type = "header",
                        name = "Pawn Preference",
                        order = 9,
                    },
                    usePawn = {
                        type = "toggle",
                        name = "Use Pawn When Installed",
                        desc = "When Pawn is installed, Sin's Toolkit asks Pawn's own upgrade advisor first. If Pawn is missing, disabled, throttled, or cannot evaluate an item, Gear Scanner falls back to ClassCodex stat priorities.",
                        order = 9.1,
                        get = function() return GetToolkitValue("gearScannerUsePawn") end,
                        set = function(_, value) SetToolkitValue("gearScannerUsePawn", value) end,
                    },
                    lootRollHeader = {
                        type = "header",
                        name = "Loot Rolls",
                        order = 9.2,
                    },
                    autoRollGreensBopBlues = {
                        type = "toggle",
                        name = "Automatically Roll on Greens and BoP Blues",
                        desc = "Automatically handles green gear and blue bind-on-pickup gear loot rolls. If an item is an upgrade, Sin's Toolkit rolls Need unless Never Auto-Need is enabled. Non-upgrades roll Greed when Greed is available.",
                        order = 9.3,
                        get = function() return GetToolkitValue("gearScannerAutoRollGreensBopBlues") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoRollGreensBopBlues", value) end,
                    },
                    autoRollBoeBlues = {
                        type = "toggle",
                        name = "Automatically Roll on BoE Blues",
                        desc = "Automatically handles blue bind-on-equip gear loot rolls. Use this carefully if you prefer to sell, trade, or auction BoE blue items instead of treating them like leveling upgrades.",
                        order = 9.4,
                        get = function() return GetToolkitValue("gearScannerAutoRollBoeBlues") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoRollBoeBlues", value) end,
                    },
                    autoRollEpics = {
                        type = "toggle",
                        name = "Automatically Roll on Epics",
                        desc = "Automatically handles epic gear loot rolls. This is off by default because many players want manual control over epic loot.",
                        order = 9.5,
                        get = function() return GetToolkitValue("gearScannerAutoRollEpics") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoRollEpics", value) end,
                    },
                    rollNonGearLoot = {
                        type = "toggle",
                        name = "Roll on Non-Gear Loot",
                        desc = "Automatically rolls Greed on handled non-gear loot. This does not evaluate upgrades because the item is not equippable gear.",
                        order = 9.6,
                        get = function() return GetToolkitValue("gearScannerRollNonGearLoot") end,
                        set = function(_, value) SetToolkitValue("gearScannerRollNonGearLoot", value) end,
                    },
                    neverAutoNeed = {
                        type = "toggle",
                        name = "Never Auto-Need; Only Greed",
                        desc = "Prevents Sin's Toolkit from rolling Need automatically. When this is enabled, upgrade candidates will roll Greed instead of Need when Greed is available.",
                        order = 9.7,
                        get = function() return GetToolkitValue("gearScannerNeverAutoNeed") end,
                        set = function(_, value) SetToolkitValue("gearScannerNeverAutoNeed", value) end,
                    },
                    bindingHeader = {
                        type = "header",
                        name = "Soulbind Confirmation",
                        order = 9.8,
                    },
                    autoConfirmBindOther = {
                        type = "toggle",
                        name = "Confirm Soulbinding for Non-Blue/Non-Epic Gear",
                        desc = "Automatically confirms the soulbind popup when Gear Scanner equips an upgrade below blue quality. Leave this off if you want to manually confirm binding items.",
                        order = 9.9,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindOther") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoConfirmBindOther", value) end,
                    },
                    autoConfirmBindBlues = {
                        type = "toggle",
                        name = "Confirm Soulbinding for Blues",
                        desc = "Automatically confirms the soulbind popup when Gear Scanner equips a blue upgrade. Leave this off if you want manual control over binding blue items.",
                        order = 9.91,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindBlues") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoConfirmBindBlues", value) end,
                    },
                    autoConfirmBindEpics = {
                        type = "toggle",
                        name = "Confirm Soulbinding for Epics",
                        desc = "Automatically confirms the soulbind popup when Gear Scanner equips an epic upgrade. This is off by default because epic items are usually worth reviewing manually.",
                        order = 9.92,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindEpics") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoConfirmBindEpics", value) end,
                    },
                    tooltipsHeader = {
                        type = "header",
                        name = "Tooltips",
                        order = 10,
                    },
                    scoreTooltips = {
                        type = "toggle",
                        name = "Show Item Score",
                        desc = "Adds a Sin's Toolkit gear score line to item tooltips. When Use Pawn When Installed is enabled and Pawn is available, Pawn upgrade candidates are prioritized; otherwise scores use ClassCodex stat priorities.",
                        order = 11,
                        get = function() return GetToolkitValue("gearScannerScoreTooltips") end,
                        set = function(_, value) SetToolkitValue("gearScannerScoreTooltips", value) end,
                    },
                    reasonTooltips = {
                        type = "toggle",
                        name = "Show Won't-Equip Reasons",
                        desc = "Adds a tooltip note explaining why an item will or will not equip, such as not being an upgrade, being blocked by a locked slot, Pawn fallback behavior, or being an upgrade candidate.",
                        order = 12,
                        get = function() return GetToolkitValue("gearScannerReasonsTooltips") end,
                        set = function(_, value) SetToolkitValue("gearScannerReasonsTooltips", value) end,
                    },
                    lockHeader = {
                        type = "header",
                        name = "Locked Slots",
                        order = 20,
                    },
                    lockSlots = {
                        type = "toggle",
                        name = "Lock Gear Slots",
                        desc = "Enables the locked slot list below. Locked slots are still scored, but Gear Scanner will not replace items in those slots.",
                        order = 21,
                        get = function() return GetToolkitValue("gearScannerLockSlots") end,
                        set = function(_, value) SetToolkitValue("gearScannerLockSlots", value) end,
                    },
                    lockedSlots = {
                        type = "multiselect",
                        name = "Slots to Lock",
                        desc = "Choose the equipment slots Gear Scanner should never replace during automatic scans or manual /stk scan checks.",
                        order = 22,
                        values = GEAR_SLOT_VALUES,
                        disabled = function() return not GetToolkitValue("gearScannerLockSlots") end,
                        get = function(_, key)
                            EnsureDB()
                            return SinsToolkitDB.gearScannerLockedSlots and SinsToolkitDB.gearScannerLockedSlots[key] == true
                        end,
                        set = function(_, key, value)
                            EnsureDB()
                            SinsToolkitDB.gearScannerLockedSlots = SinsToolkitDB.gearScannerLockedSlots or {}
                            SinsToolkitDB.gearScannerLockedSlots[key] = value or nil
                        end,
                    },
                },
            },
            tweaks = {
                type = "group",
                name = GradientText("UI Tweaks & Automation"),
                order = 5,
                args = {
                    intro = PageIntro("UI Tweaks & Automation", "Small interface behavior changes and optional automation helpers for repeated interactions and Druid form handling."),
                    panels = {
                        type = "toggle",
                        name = "No Auto Close Panels",
                        desc = "Uses Sin's Toolkit No Auto Close behavior so standard Blizzard UI panels can stay open together. Reload after disabling.",
                        order = 2,
                        get = function() return GetToolkitValue("keepPanelsOpen") end,
                        set = function(_, value) SetToolkitValue("keepPanelsOpen", value) end,
                    },
                    automateGossip = {
                        type = "toggle",
                        name = "Automate Gossip",
                        desc = "Automatically selects safe single-option gossip while avoiding city direction, trainer, bank, auction house, and storyteller-style gossip.",
                        order = 3,
                        get = function() return GetToolkitValue("automateGossip") end,
                        set = function(_, value) SetToolkitValue("automateGossip", value) end,
                    },
                    automateGossipModifier = {
                        type = "select",
                        name = "Gossip Bypass Modifier",
                        desc = "Hold this modifier key to temporarily bypass auto-gossip.",
                        order = 4,
                        values = { NONE = "None", SHIFT = "Shift", CTRL = "Ctrl", ALT = "Alt" },
                        get = function() return GetToolkitValue("automateGossipModifier") or "SHIFT" end,
                        set = function(_, value) SetToolkitValue("automateGossipModifier", value) end,
                    },
                    gseAutoUnshift = {
                        type = "toggle",
                        name = "Auto Unshift for Druids",
                        desc = AUTO_UNSHIFT_TOOLTIP,
                        order = 5,
                        disabled = function()
                            local _, classToken = UnitClass("player")
                            return classToken ~= "DRUID"
                        end,
                        get = function() return GetToolkitValue("gseAutoUnshift") end,
                        set = function(_, value) SetToolkitValue("gseAutoUnshift", value) end,
                    },
                    druidAutoFlightForm = {
                        type = "toggle",
                        name = "Druid Auto Flight Form",
                        desc = "When you are a Druid in ground Travel Form and move into an outdoor flyable area, Sin's Toolkit QOL cancels ground Travel Form so the client can move you into Flight Form on your next travel-form action. This is separate from Auto Unshift for Druids and never runs in combat, instances, challenge modes, while flying, or while submerged.",
                        order = 6,
                        disabled = function()
                            local _, classToken = UnitClass("player")
                            return classToken ~= "DRUID"
                        end,
                        get = function() return GetToolkitValue("druidAutoFlightForm") end,
                        set = function(_, value) SetToolkitValue("druidAutoFlightForm", value) end,
                    },
                },
            },
            uiErrors = {
                type = "group",
                name = GradientText("UI Errors"),
                order = 6,
                args = {
                    intro = PageIntro("UI Errors", "Choose which red UI error messages Sin's Toolkit should block."),
                    hideOutOfRangeErrors = {
                        type = "toggle",
                        name = "Enable UI Error Filtering",
                        desc = "Enable or disable the filtering of UI error messages entirely.",
                        order = 1,
                        get = function() return GetToolkitValue("hideOutOfRangeErrors") end,
                        set = function(_, value) SetToolkitValue("hideOutOfRangeErrors", value) end,
                    },
                    search = {
                        type = "input",
                        name = "Search Errors",
                        desc = "Filter the error list below. Text updates dynamically.",
                        order = 2,
                        width = "full",
                        get = function() return errorSearchString end,
                        set = function(_, value)
                            errorSearchString = value
                            UpdateErrorToggles()
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    listHeader = {
                        type = "header",
                        name = "Blocked Messages",
                        order = 999,
                    },
                },
            },
            cvars = {
                type = "group",
                name = GradientText("CVars"),
                order = 7,
                args = {
                    intro = {
                        type = "description",
                        name = "Search live client CVars, compare current values against Blizzard defaults, and edit selected values. Modified CVars appear |cff55ff55green|r in the selector. The list is validated through the WoW client at runtime so removed or renamed CVars are skipped instead of breaking the panel.",
                        order = 1,
                        fontSize = "medium",
                    },
                    search = {
                        type = "input",
                        name = "Search CVars",
                        desc = "Search by CVar name, category, or client-provided description.",
                        order = 2,
                        width = "full",
                        get = function() return cvarSearchString end,
                        set = function(_, value)
                            cvarSearchString = value or ""
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    modifiedOnly = {
                        type = "toggle",
                        name = "Show Modified Only",
                        desc = "Only show CVars where the current value differs from the client default.",
                        order = 3,
                        get = function() return cvarModifiedOnly end,
                        set = function(_, value)
                            cvarModifiedOnly = value
                            if selectedCVar ~= "" and SinsToolkit.CVars then
                                local values = SinsToolkit.CVars:GetValues(cvarSearchString, cvarModifiedOnly)
                                if not values[selectedCVar] then
                                    selectedCVar = ""
                                    cvarPendingValue = ""
                                end
                            end
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    refresh = {
                        type = "execute",
                        name = "Refresh List",
                        desc = "Refresh the CVar list and current/default values from the WoW client.",
                        order = 4,
                        func = function()
                            if SinsToolkit.CVars then
                                SinsToolkit.CVars:Refresh()
                                local info = GetSelectedCVarInfo()
                                cvarPendingValue = info and tostring(info.value or "") or ""
                            end
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    selector = {
                        type = "select",
                        name = "CVar",
                        desc = "Choose a CVar to inspect or edit.",
                        order = 5,
                        width = "full",
                        values = GetCVarValues,
                        get = function() return selectedCVar end,
                        set = function(_, value)
                            selectedCVar = value or ""
                            local info = GetSelectedCVarInfo()
                            cvarPendingValue = info and tostring(info.value or "") or ""
                        end,
                    },
                    details = {
                        type = "description",
                        name = GetSelectedCVarSummary,
                        order = 6,
                        fontSize = "medium",
                    },
                    newValue = {
                        type = "input",
                        name = "New Value",
                        desc = "Enter the value to apply to the selected CVar.",
                        order = 7,
                        width = "full",
                        disabled = function()
                            local info = GetSelectedCVarInfo()
                            return not info or info.readOnly or info.locked
                        end,
                        get = function() return cvarPendingValue end,
                        set = function(_, value) cvarPendingValue = value or "" end,
                    },
                    apply = {
                        type = "execute",
                        name = "Apply Value",
                        desc = "Apply the new value to the selected CVar.",
                        order = 8,
                        disabled = function()
                            local info = GetSelectedCVarInfo()
                            return not info or info.readOnly or info.locked or cvarPendingValue == ""
                        end,
                        func = function()
                            if not SinsToolkit.CVars then return end
                            local ok, message = SinsToolkit.CVars:SetValue(selectedCVar, cvarPendingValue)
                            local info = GetSelectedCVarInfo()
                            cvarPendingValue = info and tostring(info.value or "") or cvarPendingValue
                            print("|cff9b59b6[Sin's Toolkit]|r " .. (message or (ok and "CVar updated." or "CVar update failed.")))
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    reset = {
                        type = "execute",
                        name = "Reset to Default",
                        desc = "Set the selected CVar back to the default value reported by the WoW client.",
                        order = 9,
                        disabled = function()
                            local info = GetSelectedCVarInfo()
                            return not info or info.readOnly or info.locked
                        end,
                        func = function()
                            if not SinsToolkit.CVars then return end
                            local ok, message = SinsToolkit.CVars:ResetToDefault(selectedCVar)
                            local info = GetSelectedCVarInfo()
                            cvarPendingValue = info and tostring(info.value or "") or ""
                            print("|cff9b59b6[Sin's Toolkit]|r " .. (message or (ok and "CVar reset." or "CVar reset failed.")))
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                        end,
                    },
                    modifiedSummary = {
                        type = "description",
                        name = GetModifiedCVarsSummary,
                        order = 10,
                        fontSize = "medium",
                    },
                },
            },
            automation = {
                type = "group",
                name = GradientText("Automation"),
                order = 8,
                hidden = true,
                args = {
                    intro = PageIntro("Automation", "Optional helpers for repeated interaction and Druid form handling."),
                    automateGossip = {
                        type = "toggle",
                        name = "Automate Gossip",
                        desc = "Automatically selects safe single-option gossip while avoiding city direction, trainer, bank, auction house, and storyteller-style gossip.",
                        order = 1,
                        get = function() return GetToolkitValue("automateGossip") end,
                        set = function(_, value) SetToolkitValue("automateGossip", value) end,
                    },
                    automateGossipModifier = {
                        type = "select",
                        name = "Gossip Bypass Modifier",
                        desc = "Hold this modifier key to temporarily bypass auto-gossip.",
                        order = 2,
                        values = { NONE = "None", SHIFT = "Shift", CTRL = "Ctrl", ALT = "Alt" },
                        get = function() return GetToolkitValue("automateGossipModifier") or "SHIFT" end,
                        set = function(_, value) SetToolkitValue("automateGossipModifier", value) end,
                    },
                    gseAutoUnshift = {
                        type = "toggle",
                        name = "Auto Unshift for Druids",
                        desc = AUTO_UNSHIFT_TOOLTIP,
                        order = 3,
                        disabled = function()
                            local _, classToken = UnitClass("player")
                            return classToken ~= "DRUID"
                        end,
                        get = function() return GetToolkitValue("gseAutoUnshift") end,
                        set = function(_, value) SetToolkitValue("gseAutoUnshift", value) end,
                    },
                    druidAutoFlightForm = {
                        type = "toggle",
                        name = "Druid Auto Flight Form",
                        desc = "When you are a Druid in ground Travel Form and move into an outdoor flyable area, Sin's Toolkit QOL cancels ground Travel Form so the client can move you into Flight Form on your next travel-form action. This is separate from Auto Unshift for Druids and never runs in combat, instances, challenge modes, while flying, or while submerged.",
                        order = 4,
                        disabled = function()
                            local _, classToken = UnitClass("player")
                            return classToken ~= "DRUID"
                        end,
                        get = function() return GetToolkitValue("druidAutoFlightForm") end,
                        set = function(_, value) SetToolkitValue("druidAutoFlightForm", value) end,
                    },
                },
            },
            professions = {
                type = "group",
                name = GradientText("Professions & Lookups"),
                order = 9,
                args = {
                    intro = PageIntro("Professions & Lookups", "Hold your selected modifier over an eligible bag item to show the secure profession overlay, then click to mill, prospect, disenchant, or open supported items. Quick Link creates copyable web lookups from supported tooltips."),
                    professionItemHotkeyEnabled = {
                        type = "toggle",
                        name = "Profession Item Hotkey",
                        desc = "When enabled, hold the selected modifier over an eligible bag item. Sin's Toolkit highlights supported items and routes your click through a secure profession action for milling, prospecting, disenchanting, or opening.",
                        order = 1,
                        get = function() return GetToolkitValue("professionItemHotkeyEnabled") end,
                        set = function(_, value) SetToolkitValue("professionItemHotkeyEnabled", value) end,
                    },
                    professionItemHotkeyModifier = {
                        type = "select",
                        name = "Modifier Key",
                        desc = "The modifier key to hold to show the profession item overlay.",
                        order = 2,
                        values = { ALT = "Alt", CTRL = "Alt + Ctrl", SHIFT = "Alt + Shift" },
                        get = function() return GetToolkitValue("professionItemHotkeyModifier") or "ALT" end,
                        set = function(_, value) SetToolkitValue("professionItemHotkeyModifier", value) end,
                    },
                    wowheadSearchEnabled = {
                        type = "toggle",
                        name = "Wowhead Quick Link",
                        desc = "Mouse over supported UI elements and press Ctrl+C to open a copyable Wowhead, Armory, or Raider.IO link.",
                        order = 3,
                        get = function() return GetToolkitValue("wowheadSearchEnabled") end,
                        set = function(_, value) SetToolkitValue("wowheadSearchEnabled", value) end,
                    },
                },
            },
            identifier = {
                type = "group",
                name = GradientText("Identifier"),
                order = 10,
                args = {
                    intro = PageIntro("Identifier", "Inspect frames under your cursor, copy frame details, and look up global frame names."),
                    enabled = {
                        type = "toggle",
                        name = "Enable Hover Identifier",
                        desc = "Shows a small identifier tooltip while you move your cursor over UI frames. Use /stkid for a one-time identification without leaving hover mode enabled.",
                        order = 1,
                        get = function() return GetToolkitValue("identifierEnabled") end,
                        set = function(_, value) SetToolkitValue("identifierEnabled", value) end,
                    },
                    tooltip = {
                        type = "toggle",
                        name = "Show Identifier Tooltip",
                        desc = "Displays the current frame details near your cursor while hover identifier mode is enabled.",
                        order = 2,
                        get = function() return GetToolkitValue("identifierTooltip") end,
                        set = function(_, value) SetToolkitValue("identifierTooltip", value) end,
                    },
                    detailed = {
                        type = "toggle",
                        name = "Detailed Output",
                        desc = "Includes frame size, strata, level, alpha, point, and source-location details when the client exposes them.",
                        order = 3,
                        get = function() return GetToolkitValue("identifierDetailed") end,
                        set = function(_, value) SetToolkitValue("identifierDetailed", value) end,
                    },
                    identifyNow = {
                        type = "execute",
                        name = "Identify Cursor Frame",
                        desc = "Prints information about the frame currently under your cursor. Same as /stkid.",
                        order = 4,
                        func = function()
                            if SinsToolkit.Identifier then SinsToolkit.Identifier:IdentifyNow() end
                        end,
                    },
                    copyLast = {
                        type = "execute",
                        name = "Copy Last Result",
                        desc = "Opens a copyable popup for the last identified frame.",
                        order = 5,
                        func = function()
                            if SinsToolkit.Identifier then SinsToolkit.Identifier:CopyLast() end
                        end,
                    },
                    commands = {
                        type = "description",
                        name = "|cffd8a84aCommands:|r\n|cff3498db/stkid|r - Identify the frame under your cursor.\n|cff3498db/stkid on|r / |cff3498dboff|r - Enable or disable hover mode.\n|cff3498db/stkid copy|r - Copy the last result.\n|cff3498db/stkid name <FrameName>|r - Identify a global frame by name.\n|cff3498db/stkid simple|r / |cff3498dbdetail|r - Change output detail.",
                        fontSize = "medium",
                        order = 6,
                    },
                },
            },
            bugCatcher = {
                type = "group",
                name = GradientText("Bug Catcher"),
                order = 11,
                args = {
                    intro = PageIntro("Bug Catcher", "Capture Lua errors into a compact BugSack-style window with a minimap button. For testing, this can run alongside !BugGrabber. If BugSack is loaded, Sin's Toolkit disables this plugin version because BugSack owns the display window."),
                    enabled = {
                        type = "toggle",
                        name = "Enable Bug Catcher",
                        desc = "Captures Lua errors, protected-action errors, and Lua warnings inside Sin's Toolkit. This can run alongside !BugGrabber for testing, but is disabled when BugSack is loaded.",
                        order = 1,
                        get = function() return GetToolkitValue("bugCatcherEnabled") end,
                        set = function(_, value) SetToolkitValue("bugCatcherEnabled", value) end,
                    },
                    minimap = {
                        type = "toggle",
                        name = "Show Minimap Button",
                        desc = "Shows the Bug Catcher minimap button. Click to show the last error, Shift-click to reload UI, Alt-click to clear errors, Right-click to hide the button.",
                        order = 2,
                        get = function() return GetToolkitValue("bugCatcherMinimap") end,
                        set = function(_, value) SetToolkitValue("bugCatcherMinimap", value) end,
                    },
                    minimapOnlyErrors = {
                        type = "toggle",
                        name = "Only Show Minimap Button When Errors Exist",
                        desc = "Keeps the Bug Catcher minimap button hidden while there are no stored errors. It appears automatically after an error is captured. Turn this off if you prefer the button to always be visible.",
                        order = 2.5,
                        disabled = function() return not GetToolkitValue("bugCatcherMinimap") end,
                        get = function() return GetToolkitValue("bugCatcherMinimapOnlyErrors") end,
                        set = function(_, value) SetToolkitValue("bugCatcherMinimapOnlyErrors", value) end,
                    },
                    maxErrors = {
                        type = "range",
                        name = "Stored Error Limit",
                        desc = "Maximum number of Lua errors Sin's Toolkit stores in its own saved variables.",
                        order = 3,
                        min = 25,
                        max = 250,
                        step = 25,
                        get = function() return GetToolkitValue("bugCatcherMaxErrors") or 100 end,
                        set = function(_, value) SetToolkitValue("bugCatcherMaxErrors", value) end,
                    },
                    sound = {
                        type = "select",
                        name = "Error Sound",
                        desc = "Choose the sound Sin's Toolkit plays when Bug Catcher captures a Lua error.",
                        order = 4,
                        values = {
                            NONE = "None",
                            RAID_WARNING = "Raid Warning",
                            ALARM = "Alarm",
                            WHISPER = "Whisper",
                            READY_CHECK = "Ready Check",
                            BUGSACK_FATALITY = "BugSack: Fatality",
                        },
                        get = function() return GetToolkitValue("bugCatcherSound") or "NONE" end,
                        set = function(_, value) SetToolkitValue("bugCatcherSound", value) end,
                    },
                    advancedHeader = {
                        type = "header",
                        name = "Advanced Compatibility",
                        order = 4.5,
                    },
                    bugSackCompatibility = {
                        type = "toggle",
                        name = "Enable BugSack Compatibility",
                        desc = "Exposes BugSack-style globals so skinning addons such as WindTools can skin the Bug Catcher window. Off by default for new installs to avoid confusing addons that detect the real BugSack addon.",
                        order = 4.6,
                        get = function() return GetToolkitValue("bugCatcherBugSackCompatibility") end,
                        set = function(_, value) SetToolkitValue("bugCatcherBugSackCompatibility", value) end,
                    },
                    open = {
                        type = "execute",
                        name = "Open Last Error",
                        desc = "Opens the Bug Catcher window and displays the most recent captured error.",
                        order = 5,
                        func = function()
                            if SinsToolkit.BugCatcher then SinsToolkit.BugCatcher:ShowLast() end
                        end,
                    },
                    testSound = {
                        type = "execute",
                        name = "Test Error Sound",
                        desc = "Plays the currently selected Bug Catcher sound. If BugSack: Fatality is selected but BugSack media is not available, Sin's Toolkit uses a built-in Blizzard alert sound.",
                        order = 6,
                        func = function()
                            if SinsToolkit.BugCatcher and SinsToolkit.BugCatcher.TestSound then
                                SinsToolkit.BugCatcher:TestSound()
                            end
                        end,
                    },
                    selfTest = {
                        type = "execute",
                        name = "Run Self-Test",
                        desc = "Captures a safe test entry, opens Bug Catcher, and plays the configured alert sound without generating a real Lua error.",
                        order = 6.5,
                        func = function()
                            if SinsToolkit.BugCatcher and SinsToolkit.BugCatcher.RunSelfTest then
                                SinsToolkit.BugCatcher:RunSelfTest()
                            end
                        end,
                    },
                    clear = {
                        type = "execute",
                        name = "Clear Errors",
                        desc = "Clears captured errors from Sin's Toolkit.",
                        order = 7,
                        func = function()
                            if SinsToolkit.BugCatcher then SinsToolkit.BugCatcher:Reset() end
                        end,
                    },
                    commands = {
                        type = "description",
                        name = "|cffd8a84aCommands:|r\n|cff3498db/stkbug|r - Show the last captured Lua error.\n|cff3498db/stkbug clear|r - Clear all captured errors.\n|cff3498db/stkbug copy|r - Copy the currently selected error.\n|cff3498db/stkbug sound|r - Test the selected Bug Catcher sound.\n|cff3498db/stkbug test|r - Capture a safe self-test entry.\n|cff3498db/stkbug minimap|r - Show or hide the minimap button.\n|cff3498db/stkbug sync|r - Sync captured errors from !BugGrabber for testing.\n|cff3498db/stkbug on|r / |cff3498dboff|r - Enable or disable Bug Catcher.\n|cff3498db/stkbug reload|r - Reload the UI.\n\n|cffd8a84aMinimap:|r Click shows the last error, Shift-click reloads the UI, Alt-click clears errors, and Right-click hides the minimap button. The button can also hide itself automatically while no errors are stored.",
                        fontSize = "medium",
                        order = 7,
                    },
                },
            },
            teleportHub = {
                type = "group",
                name = GradientText("Teleport Hub"),
                order = 12,
                args = {
                    intro = PageIntro("Teleport Hub", "One-button access to known teleports, portals, hearthstones, and teleport toys. Menu, grid, and list views let you choose between a compact dropdown selector or visual browsing."),
                    enabled = {
                        type = "toggle",
                        name = "Enable Teleport Hub",
                        desc = "Enables Sin's Toolkit QOL Teleport Hub and the /stktp commands.",
                        order = 1,
                        get = function() return GetToolkitValue("teleportHubEnabled") end,
                        set = function(_, value) SetToolkitValue("teleportHubEnabled", value) end,
                    },
                    minimap = {
                        type = "toggle",
                        name = "Show Minimap Button",
                        desc = "Shows a small Sin's Toolkit QOL minimap button for opening the Teleport Hub. You can also use /stktp if you prefer no minimap button.",
                        order = 2,
                        disabled = function() return not GetToolkitValue("teleportHubEnabled") end,
                        get = function() return GetToolkitValue("teleportHubMinimap") end,
                        set = function(_, value) SetToolkitValue("teleportHubMinimap", value) end,
                    },
                    showLocked = {
                        type = "toggle",
                        name = "Show Locked Teleports",
                        desc = "Shows tracked teleports you have not learned or toys you do not own yet. Locked entries are dimmed and not clickable.",
                        order = 3,
                        get = function() return GetToolkitValue("teleportHubShowLocked") end,
                        set = function(_, value) SetToolkitValue("teleportHubShowLocked", value) end,
                    },
                    showDungeonNames = {
                        type = "toggle",
                        name = "Show Dungeon Names",
                        desc = "Shows dungeon or destination names when available instead of only spell names.",
                        order = 4,
                        get = function() return GetToolkitValue("teleportHubShowDungeonNames") end,
                        set = function(_, value) SetToolkitValue("teleportHubShowDungeonNames", value) end,
                    },
                    view = {
                        type = "select",
                        name = "Panel View",
                        desc = "Choose the Teleport Hub layout. Menu is a compact dropdown-style selector; Grid is visual browsing; List is compact rows.",
                        order = 5,
                        values = {
                            menu = "Menu",
                            grid = "Grid",
                            list = "List",
                        },
                        get = function() return GetToolkitValue("teleportHubView") or "menu" end,
                        set = function(_, value) SetToolkitValue("teleportHubView", value) end,
                    },
                    open = {
                        type = "execute",
                        name = "Open Teleport Hub",
                        desc = "Opens the Teleport Hub panel. Same as /stktp.",
                        order = 6,
                        disabled = function() return not GetToolkitValue("teleportHubEnabled") end,
                        func = function()
                            if SinsToolkit.TeleportHub then SinsToolkit.TeleportHub:Open() end
                        end,
                    },
                    scan = {
                        type = "execute",
                        name = "Rescan Teleports",
                        desc = "Refreshes known teleport, hearthstone, toy, and class teleport availability for the current character.",
                        order = 7,
                        func = function()
                            if SinsToolkit.TeleportHub then SinsToolkit.TeleportHub:Scan() end
                        end,
                    },
                    commands = {
                        type = "description",
                        name = "|cffd8a84aCommands:|r\n|cff3498db/stktp|r - Open or close Teleport Hub.\n|cff3498db/stktp on|r / |cff3498dboff|r - Enable or disable Teleport Hub.\n|cff3498db/stktp minimap|r - Show or hide the minimap button.\n|cff3498db/stktp scan|r - Rescan known teleport availability.\n|cff3498db/stktp locked|r - Show or hide locked teleports.\n|cff3498db/stktp view|r - Switch between menu, grid, and list view.\n|cff3498db/stktp menu|r / |cff3498dbgrid|r / |cff3498dblist|r - Choose a Teleport Hub view.\n|cff3498db/stktp expand|r / |cff3498dbcollapse|r - Expand or collapse teleport sections.\n\n|cffd8a84aFavorites:|r Click the |cffffd100+ Fav|r button on any teleport to pin it near the top of the hub. Favorite entries show |cffffd100Fav|r and can be clicked again to unpin. Recently Used updates after successful clicks.",
                        fontSize = "medium",
                        order = 8,
                    },
                },
            },
            profileVault = {
                type = "group",
                name = GradientText("Profile Vault"),
                order = 13,
                args = {
                    intro = PageIntro("Profile Vault", "Save and restore character setup data for action bars, macros, and keybinds."),
                    backupName = {
                        type = "input",
                        name = "Backup Name",
                        desc = "Name this backup before saving your current action bars, macros, and keybinds.",
                        order = 1,
                        width = "full",
                        get = function() return profileVaultBackupName end,
                        set = function(_, value) profileVaultBackupName = value end,
                    },
                    createBackup = {
                        type = "execute",
                        name = "Create Backup",
                        desc = "Save this character's action bars, account macros, character macros, and keybinds.",
                        order = 2,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            local backup = SinsToolkit.ProfileVault:CreateBackup(profileVaultBackupName)
                            selectedProfileVaultBackup = backup.id
                            if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                            print("|cff9b59b6[Sin's Toolkit]|r Profile backup saved: " .. backup.name)
                        end,
                    },
                    savedBackups = {
                        type = "select",
                        name = "Saved Backups",
                        desc = "Choose a backup to restore or delete.",
                        order = 3,
                        width = "full",
                        values = GetProfileVaultBackupValues,
                        get = function() return selectedProfileVaultBackup end,
                        set = function(_, value) selectedProfileVaultBackup = value end,
                    },
                    summary = {
                        type = "description",
                        name = GetProfileVaultSummary,
                        fontSize = "medium",
                        order = 4,
                    },
                    destructiveHeader = {
                        type = "header",
                        name = "Restore and Delete Actions",
                        order = 4.5,
                    },
                    restore = {
                        type = "execute",
                        name = "Restore to This Character",
                        desc = "Apply the selected backup to the current character after confirmation. This can replace action bars, macros, and keybindings. Cannot be used in combat.",
                        order = 5,
                        disabled = function() return selectedProfileVaultBackup == "" end,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            SinsToolkit.ProfileVault:ConfirmRestoreBackup(selectedProfileVaultBackup, function(ok, message)
                                print("|cff9b59b6[Sin's Toolkit]|r " .. (message or (ok and "Profile restored." or "Profile restore failed.")))
                            end)
                        end,
                    },
                    delete = {
                        type = "execute",
                        name = "Delete Backup",
                        desc = "Delete the selected backup from Sin's Toolkit saved variables after confirmation.",
                        order = 6,
                        disabled = function() return selectedProfileVaultBackup == "" end,
                        func = function()
                            if not SinsToolkit.ProfileVault then return end
                            SinsToolkit.ProfileVault:ConfirmDeleteBackup(selectedProfileVaultBackup, function(ok, message)
                                if ok then
                                    selectedProfileVaultBackup = ""
                                    if E.Libs.AceConfigRegistry then E.Libs.AceConfigRegistry:NotifyChange("ElvUI") end
                                end
                                print("|cff9b59b6[Sin's Toolkit]|r " .. (message or (ok and "Profile backup deleted." or "Profile backup delete failed.")))
                            end)
                        end,
                    },
                },
            },
            advanced = {
                type = "group",
                name = GradientText("Advanced"),
                order = 15,
                hidden = true,
                args = {
                    intro = PageIntro("Advanced", "Higher-risk settings gathered in one place for review before enabling."),
                    gearHeader = {
                        type = "header",
                        name = "Gear Scanner",
                        order = 1,
                    },
                    allowUnbound = {
                        type = "toggle",
                        name = "Allow Unbound Upgrades",
                        desc = "Allows Gear Scanner to equip unbound upgrades from your bags. Leave this off if you want BoE items left untouched.",
                        order = 2,
                        get = function() return GetToolkitValue("gearScannerAllowUnbound") end,
                        set = function(_, value) SetToolkitValue("gearScannerAllowUnbound", value) end,
                    },
                    autoRollEpics = {
                        type = "toggle",
                        name = "Automatically Roll on Epics",
                        desc = "Allows Gear Scanner to automatically roll on epic loot when its loot-roll rules say to handle the item.",
                        order = 3,
                        get = function() return GetToolkitValue("gearScannerAutoRollEpics") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoRollEpics", value) end,
                    },
                    confirmEpics = {
                        type = "toggle",
                        name = "Confirm Epic Soulbinding",
                        desc = "Automatically confirms soulbind prompts for epic items when Gear Scanner equips them.",
                        order = 4,
                        get = function() return GetToolkitValue("gearScannerAutoConfirmBindEpics") end,
                        set = function(_, value) SetToolkitValue("gearScannerAutoConfirmBindEpics", value) end,
                    },
                    bugHeader = {
                        type = "header",
                        name = "Bug Catcher Compatibility",
                        order = 5,
                    },
                    bugSackCompatibility = {
                        type = "toggle",
                        name = "Enable BugSack Compatibility",
                        desc = "Exposes BugSack-style globals for skinning addons such as WindTools. Off by default for new installs.",
                        order = 6,
                        get = function() return GetToolkitValue("bugCatcherBugSackCompatibility") end,
                        set = function(_, value) SetToolkitValue("bugCatcherBugSackCompatibility", value) end,
                    },
                    bugMinimapOnlyErrors = {
                        type = "toggle",
                        name = "Hide Bug Catcher Minimap Button When Clean",
                        desc = "Only shows the Bug Catcher minimap button after errors are captured. Existing users keep their previous setting; new installs use this cleaner default.",
                        order = 6.5,
                        get = function() return GetToolkitValue("bugCatcherMinimapOnlyErrors") end,
                        set = function(_, value) SetToolkitValue("bugCatcherMinimapOnlyErrors", value) end,
                    },
                    profileHeader = {
                        type = "header",
                        name = "Profile Vault Safety",
                        order = 7,
                    },
                    profileNote = {
                        type = "description",
                        name = "Profile Vault restore and delete actions now require confirmation before replacing action bars, macros, or keybindings.",
                        fontSize = "medium",
                        order = 8,
                    },
                },
            },
            changelog = {
                type = "group",
                name = GradientText("Changelog"),
                order = 16,
                args = {
                    intro = PageIntro("Changelog", "Recent Sin's Toolkit updates with the date and what changed."),
                    notes = {
                        type = "description",
                        order = 1,
                        fontSize = "medium",
                        name = GetChangelogText(), --[[
                            "|cffd8a84aVersion 1.1.55 - June 11, 2026|r\n"
                            .. "- Made Teleport Hub favorite controls much more visible with a clear + Fav / Fav button on each entry.\n"
                            .. "- Added empty Favorites helper text and adjusted Teleport Hub spacing so favorite buttons do not crowd teleport labels.\n\n"
                            .. "|cffd8a84aVersion 1.1.54 - June 11, 2026|r\n"
                            .. "- Fixed the ElvUI options validation error caused by dynamic banner image sizing callbacks.\n"
                            .. "- Banner dimensions are now calculated before options registration so AceConfig receives valid numeric image sizes.\n\n"
                            .. "|cffd8a84aVersion 1.1.53 - June 11, 2026|r\n"
                            .. "- Fixed Teleport Hub Favorites so the section is always visible, including an empty Favorites (0) state.\n"
                            .. "- Refined the Information banner sizing so the existing artwork scales with the ElvUI options panel while preserving aspect ratio and padding.\n\n"
                            .. "|cffd8a84aVersion 1.1.52 - June 11, 2026|r\n"
                            .. "- Fixed a Delve runtime error caused by parsing Blizzard secret unit GUID strings during Automate Gossip checks.\n"
                            .. "- Automate Gossip now safely skips NPC allow-list parsing when the interaction target is protected by the client.\n"
                            .. "- Hardened similar Wowhead Quick Link unit/name parsing paths against secret values without forcing protected strings through tostring().\n\n"
                            .. "|cffd8a84aVersion 1.1.51 - June 10, 2026|r\n"
                            .. "- Polished Teleport Hub with Favorites, Recently Used, remembered collapsible sections, and Expand/Collapse controls.\n"
                            .. "- Added a Bug Catcher option to only show the minimap button when errors exist, enabled by default for new installs while preserving existing users.\n"
                            .. "- Updated the Information tab so it documents Advanced Settings, provider status, Fallback Mode, Bug Catcher self-test, Profile Vault confirmations, and Teleport Hub's new layout tools.\n"
                            .. "- Softened the purple sidebar gradient and adjusted the banner dimensions to reduce cramped or distorted presentation in ElvUI options.\n"
                            .. "- Raised Profile Vault confirmation dialogs above addon windows so restore/delete prompts are harder to miss.\n\n"
                            .. "|cffd8a84aVersion 1.1.50 - June 10, 2026|r\n"
                            .. "- Centralized saved variable defaults in Core and added database versioning for safer future migrations.\n"
                            .. "- Added Profile Vault confirmation prompts before restore or delete actions touch action bars, macros, or keybindings.\n"
                            .. "- Reworked UI error filtering to use a dedicated forwarding frame instead of replacing UIErrorsFrame's OnEvent script.\n"
                            .. "- Added optional BugSack compatibility mode, off by default for new installs and preserved for existing users through migration.\n"
                            .. "- Added /stkbug test and a Bug Catcher self-test button.\n"
                            .. "- Added Gear Scanner provider status and a top-level Advanced settings page for higher-risk options.\n\n"
                            .. "|cffd8a84aVersion 1.1.49 - June 9, 2026|r\n"
                            .. "- Fixed Automate Gossip quest checks for modern Retail clients by using table-returning C_GossipInfo quest APIs when available.\n"
                            .. "- Added a compatibility fallback for older gossip quest count APIs so the feature fails more gracefully across client changes.\n\n"
                            .. "|cffd8a84aVersion 1.1.48 - June 8, 2026|r\n"
                            .. "- Updated Automate Gossip to behave more like Leatrix-style safe gossip handling.\n"
                            .. "- Automate Gossip now only auto-selects safe single-option gossip with no active or available gossip quests.\n"
                            .. "- Added safeguards to avoid city direction, bank, auction house, trainer, profession, and storyteller-style gossip options.\n\n"
                            .. "|cffd8a84aVersion 1.1.47 - June 8, 2026|r\n"
                            .. "- Bundled the Bug Catcher Fatality alert sound inside Sin's Toolkit so the sound works even when BugSack is not installed.\n\n"
                            .. "|cffd8a84aVersion 1.1.46 - June 8, 2026|r\n"
                            .. "- Fixed Bug Catcher sound alerts after removing BugSack/BugGrabber by falling back to a built-in Blizzard alert when external BugSack media is unavailable.\n"
                            .. "- Added a Test Error Sound button and /stkbug sound to test the currently selected Bug Catcher alert sound in-game.\n\n"
                            .. "|cffd8a84aVersion 1.1.45 - June 8, 2026|r\n"
                            .. "- Fixed a Bug Catcher window error by guarding FontString wrapping methods that are not available in every 12.0.x client load context.\n\n"
                            .. "|cffd8a84aVersion 1.1.44 - June 8, 2026|r\n"
                            .. "- Updated Bug Catcher Clear so it wipes current and saved Toolkit errors, clears mirrored BugGrabber IDs, and clears BugGrabber's DB when BugGrabber is enabled for testing.\n\n"
                            .. "|cffd8a84aVersion 1.1.43 - June 8, 2026|r\n"
                            .. "- Added a Copy button and /stkbug copy command for copying the currently selected Bug Catcher error.\n"
                            .. "- Copy opens a selectable text window with the current error highlighted for Ctrl+C.\n\n"
                            .. "|cffd8a84aVersion 1.1.42 - June 8, 2026|r\n"
                            .. "- Replaced the Bug Catcher error renderer with a scrollable FontString so captured error text displays reliably instead of rendering blank.\n"
                            .. "- Changed the minimap error state to tint only the bag red, without a full red glow around the minimap icon.\n\n"
                            .. "|cffd8a84aVersion 1.1.41 - June 8, 2026|r\n"
                            .. "- Fixed Bug Catcher window creation on 12.0.x by replacing the missing CharacterFrameTabButtonTemplate dependency with safe panel buttons.\n"
                            .. "- Kept All, Session, and Last view switching while preventing the window from stopping before the scroll text area is created.\n\n"
                            .. "|cffd8a84aVersion 1.1.40 - June 8, 2026|r\n"
                            .. "- Added BugSack: Fatality as a Bug Catcher error sound option when BugSack/LibSharedMedia provides that sound.\n\n"
                            .. "|cffd8a84aVersion 1.1.39 - June 8, 2026|r\n"
                            .. "- Fixed the Bug Catcher display area so captured errors render in the scroll window instead of appearing blank.\n"
                            .. "- Added explicit text sizing, anchoring, color, wrapping, and a clearer empty-view message with Toolkit/BugGrabber sync guidance.\n\n"
                            .. "|cffd8a84aVersion 1.1.38 - June 8, 2026|r\n"
                            .. "- Made Bug Catcher's !BugGrabber testing bridge more robust by syncing on window open, addon load, and short delayed passes after login.\n"
                            .. "- Added /stkbug sync to manually pull captured errors from !BugGrabber during testing.\n"
                            .. "- Hardened the BugGrabber callback argument handling so captured error IDs are read correctly.\n\n"
                            .. "|cffd8a84aVersion 1.1.37 - June 8, 2026|r\n"
                            .. "- Fixed Bug Catcher testing with !BugGrabber by mirroring BugGrabber.BugGrabbed events into Sin's Toolkit's own error list.\n"
                            .. "- Added an initial BugGrabber database sync so existing captured errors appear in the Sin's Toolkit window after login/reload.\n"
                            .. "- Registered Sin's Toolkit as a BugGrabber display while testing so BugGrabber does not need BugSack to show captured errors.\n\n"
                            .. "|cffd8a84aVersion 1.1.36 - June 8, 2026|r\n"
                            .. "- Allowed Sin's Toolkit Bug Catcher to run alongside !BugGrabber for testing.\n"
                            .. "- Kept real BugSack as a conflict because it owns the BugSack display globals and window skinning path.\n"
                            .. "- Updated Bug Catcher status and option text to explain the testing behavior.\n\n"
                            .. "|cffd8a84aVersion 1.1.35 - June 8, 2026|r\n"
                            .. "- Added a Bug Catcher sound dropdown for choosing an alert sound when Lua errors are captured.\n"
                            .. "- Changed the clean minimap icon to a purple WoW bag icon and kept the red glow hidden unless captured errors exist.\n\n"
                            .. "|cffd8a84aVersion 1.1.34 - June 8, 2026|r\n"
                            .. "- Changed the Bug Catcher minimap icon to a bag-style indicator.\n"
                            .. "- The Bug Catcher bag is purple when clean and turns red when Lua errors are captured.\n\n"
                            .. "|cffd8a84aVersion 1.1.33 - June 8, 2026|r\n"
                            .. "- Changed Bug Catcher to fully stand down when BugSack or !BugGrabber is loaded.\n"
                            .. "- Added clear user messaging that BugSack and !BugGrabber must be removed or disabled before using the Sin's Toolkit plugin version.\n"
                            .. "- Kept the toolkit Bug Catcher self-contained with its own saved error list, minimap button, and BugSack-style window frames.\n\n"
                            .. "|cffd8a84aVersion 1.1.32 - June 8, 2026|r\n"
                            .. "- Added Bug Catcher, a compact BugSack-style Lua error viewer inside Sin's Toolkit QOL.\n"
                            .. "- Added /stkbug and /sinbugs commands for opening the last error, clearing errors, toggling the minimap button, and reloading the UI.\n"
                            .. "- Added a Bug Catcher minimap button: click shows the last error, Shift-click reloads UI, Alt-click clears errors, and Right-click hides the button.\n"
                            .. "- Added BugSack-compatible frame/global names for skin compatibility when real BugSack is not loaded.\n"
                            .. "- Bug Catcher uses its own lightweight Lua error catcher when BugSack and !BugGrabber are not loaded.\n\n"
                            .. "|cffd8a84aVersion 1.1.31 - June 8, 2026|r\n"
                            .. "- Reworked Teleport Hub secure buttons to match the original OneButton pattern more closely.\n"
                            .. "- Pre-created a secure button pool with no parent, then assigned buttons to the scroll panel during render.\n"
                            .. "- Restored generic secure attributes for teleports: type/spell, type/item, and type/toy.\n"
                            .. "- Registered teleport buttons for AnyUp and AnyDown clicks like the source addon.\n\n"
                            .. "|cffd8a84aVersion 1.1.30 - June 8, 2026|r\n"
                            .. "- Removed the purple tile fill behind Teleport Hub icons for a cleaner look.\n"
                            .. "- Changed Teleport Hub spell clicks to explicit left-click secure spell attributes using type1 and spell1.\n"
                            .. "- Kept toy and hearthstone actions on explicit left-click secure actions.\n"
                            .. "- Left the Teleport Hub panel open after clicks so any remaining client-side cast issue is visible while testing.\n\n"
                            .. "|cffd8a84aVersion 1.1.29 - June 8, 2026|r\n"
                            .. "- Added Grid/List view selection for Teleport Hub.\n"
                            .. "- Added /stktp view, /stktp grid, and /stktp list commands.\n"
                            .. "- Made the full Teleport Hub tile or list row clickable instead of only the icon texture.\n"
                            .. "- Tested secure macrotext actions for Teleport Hub during click-path troubleshooting.\n"
                            .. "- Stopped closing the Teleport Hub panel immediately after clicking so failed clicks are visible while testing.\n\n"
                            .. "|cffd8a84aVersion 1.1.28 - June 8, 2026|r\n"
                            .. "- Made the Teleport Hub window resizable and saved its size and position.\n"
                            .. "- Increased the default Teleport Hub window size and tightened the teleport grid to reduce scrolling.\n"
                            .. "- Slightly reduced Teleport Hub icon size and widened label cells so longer names are easier to read.\n"
                            .. "- Updated Teleport Hub secure spell buttons to cast by resolved spell name for more reliable clicking.\n\n"
                            .. "|cffd8a84aVersion 1.1.27 - June 8, 2026|r\n"
                            .. "- Removed the duplicate No Auto Close page from Blizzard's AddOns settings list.\n"
                            .. "- Kept No Auto Close controlled from the Sin's Toolkit QOL ElvUI plugin page.\n"
                            .. "- Removed the confusing default-position mover UI from the user-facing No Auto Close settings.\n\n"
                            .. "|cffd8a84aVersion 1.1.26 - June 8, 2026|r\n"
                            .. "- Added Teleport Hub with /stktp slash commands.\n"
                            .. "- Added a searchable secure-button panel for known teleports, portals, hearthstones, and teleport toys.\n"
                            .. "- Added Teleport Hub options for enabling the feature, showing the minimap button, showing locked teleports, and choosing destination names.\n"
                            .. "- Added an optional minimap button that can be hidden from the panel or with /stktp minimap.\n\n"
                            .. "|cffd8a84aVersion 1.1.25 - June 8, 2026|r\n"
                            .. "- Updated the in-game Information tab with the full current Sin's Toolkit QOL feature set.\n"
                            .. "- Added clearer descriptions for Gear Scanner providers, Druid automation, Identifier, Profile Vault, and available commands.\n\n"
                            .. "|cffd8a84aVersion 1.1.24 - June 8, 2026|r\n"
                            .. "- Added the new Identifier page and /stkid commands.\n"
                            .. "- Added hover-identify mode for inspecting UI frames under the cursor.\n"
                            .. "- Added one-time cursor identification, global frame-name lookup, detailed/simple output, and copyable result popups.\n"
                            .. "- Included secret-safe frame inspection checks for newer WoW client behavior.\n\n"
                            .. "|cffd8a84aVersion 1.1.23 - June 8, 2026|r\n"
                            .. "- Renamed the addon display to Sin's Toolkit QOL.\n"
                            .. "- Added a separate Druid Auto Flight Form toggle under Automation.\n"
                            .. "- Druid Auto Flight Form can cancel ground Travel Form in safe outdoor flyable areas so Druids can move toward Flight Form without touching Auto Unshift.\n"
                            .. "- Kept Auto Unshift for Druids as its own separate toggle and behavior.\n\n"
                            .. "|cffd8a84aVersion 1.1.22 - June 7, 2026|r\n"
                            .. "- Cleaned up Gear Scanner Pawn upgrade messages so huge upgrades display as major upgrade instead of an oversized percentage.\n"
                            .. "- Hid raw Pawn item strings from auto-equip chat output unless Pawn provides a proper clickable item link.\n\n"
                            .. "|cffd8a84aVersion 1.1.21 - June 7, 2026|r\n"
                            .. "- Updated Gear Scanner dependency handling so Pawn or ClassCodex can power upgrade evaluation.\n"
                            .. "- Gear Scanner no longer pauses only because ClassCodex is missing when Pawn is installed and loaded.\n"
                            .. "- Updated Gear Scanner panel text and /stk scan messages to say Pawn or ClassCodex is required.\n\n"
                            .. "|cffd8a84aVersion 1.1.20 - June 7, 2026|r\n"
                            .. "- Added Gear Scanner auto-equip explanations to the chat message when an upgrade is equipped.\n"
                            .. "- When Pawn is installed and provides details, the message can show the Pawn scale, percentage upgrade, and compared item.\n"
                            .. "- When ClassCodex fallback scoring is used, the message shows the score difference and replacement slot/item.\n\n"
                            .. "|cffd8a84aVersion 1.1.19 - June 7, 2026|r\n"
                            .. "- Renamed the Gear Scanner Pawn option to Use Pawn When Installed.\n"
                            .. "- Made Pawn preference enabled by default for new profiles while keeping Pawn an optional dependency.\n"
                            .. "- Gear Scanner now prefers Pawn's installed upgrade advisor when available, then falls back to ClassCodex if Pawn is missing or cannot evaluate an item.\n"
                            .. "- Updated Gear Scanner tooltip wording so Pawn fallback behavior is clearer.\n\n"
                            .. "|cffd8a84aVersion 1.1.18 - June 7, 2026|r\n"
                            .. "- Reworked Gear Scanner upgrade evaluation so ClassCodex remains the primary scoring source while Pawn acts as an optional advisory upgrade signal.\n"
                            .. "- Updated Pawn support to use Pawn's intended bag-upgrade API when available instead of relying on lower-level internals.\n"
                            .. "- Fixed hybrid primary-stat detection, including Feral and Guardian Druids using Agility correctly.\n"
                            .. "- Added minimum-level checks so Gear Scanner does not try to equip items your character cannot use yet.\n"
                            .. "- Added a Pawn-style 0.5% upgrade threshold on top of the configurable Minimum Upgrade Score slider.\n"
                            .. "- Fixed manual /stk scan timing so it waits for item data callbacks before reporting no upgrades found.\n\n"
                            .. "|cffd8a84aVersion 1.1.17 - June 7, 2026|r\n"
                            .. "- Updated the Information page banner using the cleaner Sin's Toolkit banner asset.\n"
                            .. "- Fixed the banner display ratio so it no longer appears cramped or stretched at the top.\n"
                            .. "- Optimized UI Errors tab loading by caching grouped error-message results.\n"
                            .. "- Optimized the CVar selector by caching dropdown values per search and modified-only filter.\n\n"
                            .. "|cffd8a84aVersion 1.1.16 - June 7, 2026|r\n"
                            .. "- Fixed UI Error Filtering so duplicate Blizzard error IDs with the same displayed text are blocked together.\n"
                            .. "- Cleaned up the UI Errors list so duplicate displayed messages behave as one checkbox.\n"
                            .. "- Verified Auto Unshift for Druids still only suppresses its shapeshift-form error while that feature is enabled.\n\n"
                            .. "|cffd8a84aVersion 1.1.15 - June 7, 2026|r\n"
                            .. "- Fixed Gear Scanner scan filtering so valid gear is not rejected by an overly broad usable-item check.\n"
                            .. "- Changed the ClassCodex dependency guard so Gear Scanner pauses when ClassCodex is unavailable instead of silently turning off the saved toggle.\n\n"
                            .. "|cffd8a84aVersion 1.1.14 - June 7, 2026|r\n"
                            .. "- Adjusted Gear Scanner scoring for leveling so item level and item quality are considered more clearly.\n"
                            .. "- ClassCodex stat priorities remain the primary comparison, with rarity and item level used as supporting score factors.\n\n"
                            .. "|cffd8a84aVersion 1.1.13 - June 7, 2026|r\n"
                            .. "- Fixed Gear Scanner armor filtering so it will not equip the wrong armor type for your class.\n"
                            .. "- Added clearer tooltip feedback when an item is rejected because it is not your class armor type.\n\n"
                            .. "|cffd8a84aVersion 1.1.12 - June 7, 2026|r\n"
                            .. "- Added this in-game Changelog page.\n\n"
                            .. "|cffd8a84aVersion 1.1.11 - June 7, 2026|r\n"
                            .. "- Updated Auto Unshift for Druids tooltip wording.\n"
                            .. "- Updated the Information panel description for Auto Unshift for Druids.\n"
                            .. "- Added clarification that Auto Unshift can be helpful for GSE, also known as Gnome Sequencer.\n"
                            .. "- Added suppression for the shapeshift-form UI error while Auto Unshift for Druids is enabled.\n"
                            .. "- Restored Blizzard's default shapeshift error behavior when Auto Unshift for Druids is disabled.\n\n"
                            .. "|cffd8a84aVersion 1.1.10 - June 7, 2026|r\n"
                            .. "- Expanded Gear Scanner tooltips.\n"
                            .. "- Added clearer Pawn setup instructions.\n"
                            .. "- Improved loot-roll and soulbind confirmation option descriptions.\n"
                            .. "- Fixed the sidebar gradient so it flows from dark purple to white.\n\n"
                            .. "|cffd8a84aVersion 1.1.9 - June 7, 2026|r\n"
                            .. "- Added optional Gear Scanner loot-roll settings.\n"
                            .. "- Added optional soulbind confirmation settings.\n"
                            .. "- Added optional Pawn support and Pawn as an optional dependency.\n"
                            .. "- Added support for showing won't-equip reasons in item tooltips.\n"
                            .. "- Updated the addon banner image.\n\n"
                            .. "|cffd8a84aVersion 1.1.8 - June 7, 2026|r\n"
                            .. "- Added custom Sin's Toolkit banner artwork.\n"
                            .. "- Updated the ElvUI options panel branding.\n"
                            .. "- Added purple and violet visual styling to the options layout.\n\n"
                            .. "|cffd8a84aVersion 1.1.7 - June 7, 2026|r\n"
                            .. "- Redesigned the options layout inside ElvUI.\n"
                            .. "- Changed the options panel from top tabs to left-side navigation.\n"
                            .. "- Added compact section introductions.\n"
                            .. "- Added a branded Sin's Toolkit information area.",]]
                    },
                },
            },
        },
    }

    if SinsToolkit.GetFrameToolsElvUIOptions then
        local frameTools = SinsToolkit.GetFrameToolsElvUIOptions(GradientText, PageIntro)
        E.Options.args.sinsToolkit.args.frameTools = frameTools
    end

    if C_Timer then
        C_Timer.After(0.1, UpdateErrorToggles)
    else
        UpdateErrorToggles()
    end
end

function Plugin:Initialize()
    CreateMainMinimapButton()
end

local function OptionsCallback()
    Plugin:InsertOptions()
end

if EP then
    EP:RegisterPlugin(addonName, OptionsCallback)
end

E:RegisterModule(Plugin:GetName())
