===========================================================================
    Yapper Public API  (Src/API.lua)
===========================================================================
    Full documentation are available on GitHub: https://github.com/TenaarFeiri/WoW-Yapper/tree/main/Documentation
    This will always be up to date.
    I am always looking for new opportunities to expand the API, so if you have need
    for a hook point that doesn't exist yet, please open an issue or PR on the Yapper repository.
    Alternatively, you can DM me on GitHub, CurseForge (Symphicat), or send me an email at symphicat@gmail.com


    This file creates `_G.YapperAPI`, a safe, public-facing object that
    lets other addons hook into Yapper without touching the internal
    `YapperTable`.  Everything exposed here is sandboxed — external code
    cannot break Yapper's runtime even if it errors.

    Two systems are provided:

      • FILTERS   — fire *before* an action.  Can inspect, modify, or
                     cancel the operation.
      • CALLBACKS — fire *after* something happened (or on state change).
                     Notification only; cannot cancel.

---------------------------------------------------------------------------
1.  FILTERS (pre-hooks)
---------------------------------------------------------------------------

    Filters let you intercept an operation, inspect its data, change it,
    or cancel it entirely.

    Signature:
        local handle = YapperAPI:RegisterFilter(hookPoint, callback, priority)

    • hookPoint  — string name (see list below).
    • callback   — `function(payload) ... return payload end`
                     Receives a single TABLE with named keys.
                     Must return the (possibly modified) payload to continue,
                     or return `false` to cancel the operation.
    • priority   — optional number; lower fires first (default 10).
    • handle     — opaque value you pass to UnregisterFilter later.

    Example — strip custom tags before spellcheck:

        local h = YapperAPI:RegisterFilter("PRE_SPELLCHECK", function(p)
            p.text = p.text:gsub("%[MyTag%]", "")
            return p
        end)

    Example — block sends that contain a keyword:

        YapperAPI:RegisterFilter("PRE_SEND", function(p)
            if p.text:find("FORBIDDEN") then
                return false   -- cancel the send entirely
            end
            return p
        end)

    Example — rewrite outgoing text:

        YapperAPI:RegisterFilter("PRE_SEND", function(p)
            p.text = p.text:gsub("brb", "be right back")
            return p
        end, 5)   -- priority 5 fires before the default 10

    Unregistering:

        YapperAPI:UnregisterFilter(h)

    Available filter hook points
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Hook              Payload keys                   Cancellable?
    ────              ────────────                   ────────────
    PRE_EDITBOX_SHOW  chatType, target               yes
      Fires before the overlay opens.  Return false to suppress it.
      Used by WIMBridge to yield focus when WIM owns the whisper window.
      Emission sites: Src/Hooks/Blizzard.lua#L122, Src/EditBox/Keybinds.lua#L155.

    PRE_MULTILINE_SHOW text, chatType, language, target yes
      Fires before the expanded multiline editor opens.
      Modify payload to change initial text/channel or return false to block.
      Emission site: Src/Multiline.lua#L574.

    PRE_SEND          text, chatType, language,      yes
                      target
      Fires after the user presses Enter but before the message is routed.
      Modify payload fields to rewrite the message or return false to block.
      Emission sites: Src/Chat.lua#L104, Src/Multiline.lua#L889.

    PRE_CHUNK         text, limit                    yes
      Fires before the chunker splits a long message.  Modify text or limit,
      or return false to prevent chunking entirely.
      Emission site: Src/Chat.lua#L169.

    PRE_SPELLCHECK    text                           yes
      Fires before the spellchecker runs on the current input.  Return false
      to skip spellchecking for this particular text.
      Emission site: Src/Spellcheck/Engine.lua#L83.

    PRE_SPELLCHECK_SUGGESTIONS  word, suggestions, locale     yes
      Fires after the engine has scored, sorted, and formatted the suggestion
      list for a misspelled word, but before the result is cached and shown.
      Plugins may reorder, append, remove, or rewrite suggestions by mutating
      the `suggestions` array and returning the payload.  Return false to
      suppress the suggestion popup entirely.

      Each entry in `suggestions` is a table:
        { kind="word",    value=string, score=number, baseScore=number }
        { kind="add",     value=string }
        { kind="ignore",  value=string }
        { kind="split",   value=string }

      To force a refresh after changing plugin state, call
      YapperAPI:ClearSuggestionCache().
      Emission site: Src/Spellcheck/Engine.lua#L1231.

    PRE_DELIVER       text, chatType, language,      yes
                      target
      Fires in DirectSend just before Router:Send.  A filter may return false
      to "claim" the message — Yapper will not send it via Router and will
      instead start a delegation timer.  The claiming addon receives a
      POST_CLAIMED callback with a handle and must call
      YapperAPI:ResolvePost(handle) within the timeout (default 5 s).
      If the timeout expires, Yapper sends the message itself and prints an
      error attributing the failure to the claiming addon.
      Emission site: Src/Chat.lua#L251.

    PRE_ICON_GALLERY_SHOW  rawEditBox, query            yes
      Fires before the raid-icon gallery popup is shown.
      Modify `query` to change the pre-filter string or return false to suppress.
      Emission site: Src/IconGallery.lua#L81.

---------------------------------------------------------------------------
2.  CALLBACKS (post-hooks / event notifications)
---------------------------------------------------------------------------

    Callbacks fire *after* something happened.  They receive data but
    cannot modify or cancel the action.

    Signature:
        local handle = YapperAPI:RegisterCallback(event, callback)

    • event    — string name (see list below).
    • callback — `function(...)` called with event-specific arguments.
    • handle   — opaque value you pass to UnregisterCallback later.

    Example — log every sent message:

        YapperAPI:RegisterCallback("POST_SEND", function(text, chatType, target)
            print("Yapper sent:", text, "to", chatType, target)
        end)

    Example — react to settings changes:

        YapperAPI:RegisterCallback("CONFIG_CHANGED", function(path, value)
            -- path is a dot-delimited string like "EditBox.FontSize"
            -- value is the new setting value
            if path == "Spellcheck.Locale" then
                print("Spellcheck locale changed to", value)
            end
        end)

    Example — track overlay visibility:

        YapperAPI:RegisterCallback("EDITBOX_SHOW", function(chatType, target)
            print("Yapper overlay opened on", chatType)
        end)

        YapperAPI:RegisterCallback("EDITBOX_HIDE", function()
            print("Yapper overlay closed")
        end)

    Unregistering:

        YapperAPI:UnregisterCallback(h)

    Available callback events
    ~~~~~~~~~~~~~~~~~~~~~~~~~
    Event                    Arguments
    ─────                    ─────────
    POST_SEND                text, chatType, language, target
      Fires after a message has been handed to the WoW send API.

    CONFIG_CHANGED           path (string), value
      Fires when a Yapper setting changes.  path is dot-delimited,
      e.g. "EditBox.FontSize", "Spellcheck.Locale".

    EDITBOX_SHOW             chatType, target
      Fires when the Yapper overlay becomes visible.

    EDITBOX_HIDE             (none)
      Fires when the Yapper overlay is hidden.

    EDITBOX_TEXT_CHANGED     text, isUserInput, box
      Fires when the content of the overlay editbox is modified.
      box is the WoW EditBox frame object.

    EDITBOX_CHANNEL_CHANGED  chatType, target
      Fires when the user switches chat channel (Tab, slash command, etc.).
      Emission site: Src/Hooks/Label.lua#L251.

    EDITBOX_LABEL_UPDATED    label, r, g, b
      Fires when the UI label (e.g. "To [Name]:") is refreshed.
      Emission site: Src/Hooks/Label.lua#L218.

    THEME_CHANGED            themeName
      Fires when the active theme is changed.

    API_ERROR                kind, hook, handler_info, errorMessage, data, ...
      Fires when a filter or callback handler errors.  Delivered only to the
      addon that owns the faulting handler (matched by source file path).

    SPELLCHECK_SUGGESTION    word, suggestions (array of strings)
      Fires when the spellcheck suggestion popup is shown for a misspelled word.

    SPELLCHECK_APPLIED       original, replacement
      Fires when the user accepts a spellcheck suggestion.

    SPELLCHECK_CLOSED        (none)
      Fires when the spellcheck suggestion tooltip is closed.

    SPELLCHECK_SUGGESTION_HIGHLIGHTED text, index, total
      Fires when a suggestion is highlighted in the popup (via keyboard or mouse).
      text is the label (color codes stripped); index is the 1-based global
      index; total is the total number of suggestions.

    SPELLCHECK_WORD_ADDED    word, locale
      Fires when a word is added to the user dictionary (manually or via YAS).

    SPELLCHECK_WORD_IGNORED  word, locale
      Fires when the user marks a word as "ignored".

    YALLM_WORD_LEARNED       word, locale
      [DEPRECATED - Use YAS_WORD_LEARNED instead]
      Automatically aliased to YAS_WORD_LEARNED for backward compatibility.
      External addons that register for YALLM_WORD_LEARNED will receive
      YAS_WORD_LEARNED events. This alias will be removed in a future version.

    YAS_WORD_LEARNED        word, locale
      Fires when YAS auto-promotes a word to the user dictionary after
      persistent usage (reaching the auto-learn threshold).

    POST_CLAIMED             handle, text, chatType, language, target
      Fires when a PRE_DELIVER filter claims a message.  The handle must
      be passed to YapperAPI:ResolvePost(handle) within the delegation
      timeout to confirm delivery.  If not resolved, Yapper sends the
      message itself and blames the claiming addon.

    QUEUE_STALL              chatType, policyClass, chunksRemaining
      Fires when the ack-event stall timer expires before the server
      confirmed a chunk.  Indicates the Continue prompt is now visible.
      `chatType` is the WoW chat type string (e.g. "SAY"), `policyClass`
      is the internal policy class name, `chunksRemaining` is the total
      number of chunks still waiting (including the one that stalled).

    QUEUE_COMPLETE           (no arguments)
      Fires when the queue finishes delivering all chunks (successfully
      or after a cancel).  Paired with QUEUE_STALL for addons that need
      to track active queue sessions.

    ICON_GALLERY_SHOW        query (string)
      Fires when the raid-icon gallery popup opens.  query is the
      partial word the user typed after '{' (may be empty string).

    ICON_GALLERY_HIDE        (no arguments)
      Fires when the raid-icon gallery popup closes.

    ICON_GALLERY_SELECT      index (int), text (string), code (string)
      Fires when the user picks a raid icon.  index is 1-8; text is the
      icon name (e.g. "skull"); code is the shorthand (e.g. "rt8").

    STATE_CHANGED            newState (string), oldState (string), ...
      Fires whenever the internal state machine transitions.
      Common states: "IDLE", "EDITING", "MULTILINE", "SENDING", "STALLED".
      Any additional metadata passed to SetState is forwarded in ...

---------------------------------------------------------------------------
3.  READ-ONLY ACCESSORS
---------------------------------------------------------------------------

    YapperAPI:GetVersion()          → "1.3.0" (string) [Src/API.lua#L334]
    YapperAPI:GetCurrentTheme()     → theme name (string) or nil [Src/API.lua#L342]
    YapperAPI:IsOverlayShown()      → boolean [Src/API.lua#L353]
    YapperAPI:GetConfig(path)       → value at dot-path, e.g. "Chat.DELINEATOR" [Src/API.lua#L371]
    YapperAPI:GetDelineator()       → string|nil (marker used for chunking) [Src/API.lua#L397]

    Theme helpers:

    YapperAPI:RegisterTheme(name, data) [Src/API.lua#L814]
      Register a custom theme. `data` follows the same schema as built-in
      themes: inputBg, labelBg, textColor, borderColor, border, allowRoundedCorners,
      allowDropShadow, font, and optional OnApply.

    YapperAPI:SetTheme(name) [Src/API.lua#L824]
      Activate a registered theme and persist it as the current selection.

    YapperAPI:GetRegisteredThemes() → array [Src/API.lua#L832]
      Return a sorted list of registered theme names.

    YapperAPI:GetTheme(name) → table|nil [Src/API.lua#L840]
      Return the data table for a registered theme, or nil if not found.

    Queue accessors:

    YapperAPI:GetQueueState()       → table with fields: [Src/API.lua#L785]
                                        active (bool), stalled (bool),
                                        chatType (string|nil),
                                        policyClass (string|nil),
                                        pending (int), inFlight (int)
    YapperAPI:CancelQueue()         → int (number of chunks discarded) [Src/API.lua#L798]

    Icon Gallery accessors:

    YapperAPI:ShowIconGallery(editBox, anchorFrame, query) [Src/API.lua#L996]
      Shows the raid-icon gallery anchored to an external EditBox widget.
      editBox must be a raw WoW EditBox; anchorFrame is the frame the popup
      anchors to (defaults to editBox); query is an optional pre-filter string.

    YapperAPI:HideIconGallery() [Src/API.lua#L1004]
      Hides the gallery.

    YapperAPI:IsIconGalleryShown()  → boolean [Src/API.lua#L1010]

    YapperAPI:GetRaidIconData()     → array of 8 tables, each with: [Src/API.lua#L1017]
                                        index (int), text (string), code (string)

    Bypass / Lifecycle:

    YapperAPI:OpenBlizzardChat() [Src/API.lua#L363]
      Force the Yapper overlay to close and open the original Blizzard editbox.
      Equivalent to the user pressing the "Bypass Yapper" keybind (Shift-Enter).
      If Yapper is not currently open, the next chat open will be bypassed.

    Utility helpers (safe wrappers around Utils.lua):

    YapperAPI:IsChatLockdown()      → boolean [Src/API.lua#L853]
      Returns true if C_ChatInfo.InChatMessagingLockdown() is active.
      Use this to guard sends in bridges the same way Yapper does internally.

    YapperAPI:IsSecret(value)       → boolean [Src/API.lua#L866]
      Returns true if a value should not be logged or persisted (Blizzard
      issecretvalue / canaccessvalue APIs, with a |K token fallback).

    YapperAPI:InsertText(text)      → boolean [Src/API.lua#L762]
      Insert text at the cursor in whichever Yapper editbox is active
      (multiline editor first, then single-line overlay).  Returns true
      if the text was inserted, false if no Yapper editbox is currently
      active.  Uses the state machine so it always targets the right box.

    YapperAPI:RegisterLinkProtocol(prefix) → boolean [Src/API.lua#L716]
      Declare that |H<prefix>:...|h[...]|h hyperlinks are known,
      first-class link tokens in Yapper.  Registers the prefix string
      (e.g. "addon:totalrp3") so the pipeline treats those hyperlinks
      the same as native WoW item/spell links (atomic in the chunker,
      ignored by the spellchecker).  Returns true on success.

    YapperAPI:GetRegisteredLinkProtocols() → table [Src/API.lua#L724]
      Returns a shallow copy of all registered link protocol prefixes.

    YapperAPI:GetChatParent()       → Frame
      Returns the correct UI parent for chat-related frames, respecting
      fullscreen panels like the housing editor.

    YapperAPI:MakeFullscreenAware(frame)
      Hooks the frame so it re-parents automatically whenever the active
      fullscreen panel changes.  Pass any frame you want to keep visible
      over panels that hide UIParent.

---------------------------------------------------------------------------
4.  STATE & UI MANAGEMENT
---------------------------------------------------------------------------

    YapperAPI:GetState()            → state name (string) [Src/API.lua#L406]
    YapperAPI:IsState(stateName)    → boolean [Src/API.lua#L415]
    YapperAPI:GetStates()           → sorted array of all valid state names [Src/API.lua#L424]

    YapperAPI:SetState(stateName, ...) [Src/API.lua#L468]
      Manually request a state transition. stateName must be a valid state
      (e.g. "IDLE", "EDITING", "MULTILINE"). Metadata passed in ... is
      forwarded to callbacks and history logs. Use with caution: forcing
      states may bypass internal logic or cause UI desync.

    YapperAPI:ListFrames()          → table [Src/API.lua#L481]
      Returns a map of internal frame names to their WoW frame objects.
      
      Flat keys for quick access:
        Overlay, OverlayEdit, LabelBg, SuggestionFrame, HintFrame,
        SuggestionClickCatcher, MultilineFrame, MultilineEdit, MultilineScroll.

      Categorized registry (advanced):
        The returned table also contains an 'All' key which is a raw map of
        categories (e.g. "Spellcheck", "Overlay", "Core") to their respective
        frame objects.

      Use this to re-parent or restyle Yapper UI without global lookups.

    Spellcheck accessors (safe wrappers — return nil/false if spellcheck is unavailable):

    YapperAPI:IsSpellcheckEnabled() → boolean [Src/API.lua#L514]
    YapperAPI:CheckWord(word)       → boolean (true if word is in dict or user dict) [Src/API.lua#L523]
    YapperAPI:GetSuggestions(word)  → array of suggestion strings, or nil [Src/API.lua#L533]
    YapperAPI:GetSpellcheckLocale() → locale string (e.g. "enUS") or nil [Src/API.lua#L554]
    YapperAPI:AddToDictionary(word) → boolean (true if added) [Src/API.lua#L564]
    YapperAPI:IgnoreWord(word)      → boolean (true if ignored) [Src/API.lua#L577]
    YapperAPI:FindMisspellings(text) → table[]|nil [Src/API.lua#L622]
      Scan a block of text and return an array of misspelled word ranges.
      Each entry is { startPos: number, endPos: number, word: string }.
      Returns nil if spellcheck is disabled or no misspellings are found.

    YapperAPI:ClearSuggestionCache() → boolean [Src/API.lua#L1125]
      Clears the spellcheck suggestion cache, forcing re-generation (and
      re-filtering) on the next request.  Returns true if the cache was
      cleared, false if spellcheck is unavailable.

    Autocomplete & Ghost Text:

    YapperAPI:GetAutocompleteSuggestion(word) → string|nil [Src/API.lua#L1032]
      Returns the best autocomplete suggestion for the given partial word
      based on YAS and dictionary data.

    YapperAPI:GetCaretOffset(editBox) → x, y, height [Src/API.lua#L1042]
      Returns the current pixel offset of the cursor/caret within an EditBox.
      Requires the EditBox to be managed or hooked by Yapper.

    YapperAPI:GetGhostFrame() → table|nil [Src/API.lua#L1059]
      Returns the shared FontString used for ghost text rendering.

    YapperAPI:ShowGhostText(text, editBox, prefix, textUpToCursor) [Src/API.lua#L1071]
      Manually show ghost text on a specific EditBox. prefix and
      textUpToCursor are optional (used for positioning).

    YapperAPI:HideGhostText() [Src/API.lua#L1087]
      Hides the ghost text.

    YapperAPI:SetGhostTextOffset(offsetX, offsetY) [Src/API.lua#L1096]
      Set a manual pixel offset for ghost text alignment.

    YapperAPI:SyncGhostTextFont() [Src/API.lua#L1104]
      Force the ghost text to synchronise its font with its current parent.

    YapperAPI:SetSpellcheckTooltipOffset(hintX, hintY, suggestX, suggestY) [Src/API.lua#L1116]
      Set manual pixel offsets for spellcheck tooltips.
      - hintX/Y: Offsets for the "Shift+Tab" hint (default: 0, -2).
      - suggestX/Y: Offsets for the suggestion dropdown (default: 0, 4).

    Dictionary / engine registration (for LOD dictionary addons):

    YapperAPI:RegisterDictionary(locale, data) [Src/API.lua#L642]
      Register a dictionary for the given locale (e.g. "enBase", "enGB").
      `data` accepts the same fields as the internal RegisterDictionary call:
        • words          — array of canonical word strings
        • phonetics      — table { [phoneticHash] = { wordId, … } }
        • extends        — string locale this dict inherits from (delta dicts)
        • isDelta        — bool (inferred from extends; can be set explicitly)
        • languageFamily — string family id, e.g. "en". Links this dict to a
                           registered language engine.
        • engine         — optional table (see RegisterLanguageEngine). When
                           present, the engine is registered atomically with
                           the dict so ordering does not matter.
      Returns true on success, false on invalid arguments.

    YapperAPI:RegisterLanguageEngine(familyId, engine) [Src/API.lua#L659]
      Register a language engine for a locale family.
      `familyId` — short string identifier, e.g. "en", "de", "fr".
      `engine`   — table with the following fields:
        • GetPhoneticHash(word) → string   (REQUIRED)
          Must produce the same hash keys used in the dict's phonetics table.
        • NormaliseVowels(word) → string   (optional; falls back to built-in)
        • HasVariantRules — bool           (optional; enables variant swaps)
        • VariantRules    — array of {from, to} pairs  (optional)
        • ScoreWeights    — partial table  (optional; overlays built-in weights)
        • KBLayouts       — table          (optional; same schema as built-in KB_LAYOUTS)
      Returns true on success, false on invalid / missing GetPhoneticHash.

    YapperAPI:IsLanguageEngineRegistered(familyId) → boolean [Src/API.lua#L673]

    Post delegation:

    YapperAPI:ResolvePost(handle)   → boolean (true if the claim was valid and resolved) [Src/API.lua#L974]

    YapperAPI:RegisterLocaleAddon(locale, addonName) [Src/API.lua#L692]
      Map a Load-On-Demand addon to a specific locale so Yapper knows what to load
      when tracking dictionaries for that region.
      `locale` — e.g. "ptBR", "esES"
      `addonName` — e.g. "Yapper_Dict_pt"

    YapperAPI:GetLanguageEngine(familyId) → table|nil [Src/API.lua#L681]
      Returns the language engine for `familyId`, or nil if not found.

    YapperAPI:RegisterAtomicPattern(pattern) [Src/API.lua#L746]
      Register a custom Lua string pattern that the Yapper chunker should
      treat as an unbreakable, atomic sequence (similar to a WoW hyperlink).
      This is useful for plugins that inject raw pseudo-link text
      (like [TRP3:Identifier]) that shouldn't be split across messages.
      Returns true on success.

    YapperAPI:GetRegisteredAtomicPatterns() → string[] [Src/API.lua#L753]
      Returns an array of all registered atomic patterns.

    YapperAPI:Deleet(word) → string [Src/API.lua#L877]
      Convert leetspeak characters back to their base alphabet equivalents.

    These never expose internal tables directly; tables are shallow-copied.

---------------------------------------------------------------------------
5.  NOTES FOR ADDON AUTHORS
---------------------------------------------------------------------------

        • Yapper wraps every external filter and callback in `pcall()`.
            If your handler errors, Yapper emits an `API_ERROR` callback so
            external addons can inspect the failure programmatically and
            optionally take corrective action.  The `API_ERROR` callback has
            the signature `(kind, hook, handler_info, errorMessage, data, ...)`:
                - `kind`: "filter", "callback", or "filter-return"
                - `hook`: the hook or event name where the failure occurred
                - `handler_info`: table `{ handle = <number>, priority = <number?> }` or nil
                - `errorMessage`: the handler's error string
                - `data`: the payload (for filters) or returned value
                - `...`: other args passed to the handler

            If handlers are registered for `API_ERROR`, Yapper will attempt
            to deliver the event only to those handlers that were registered
            by the same addon/module that owns the failing handler (this
            ownership is recorded at registration time from the handler's
            source). If one or more owner-specific handlers exist, only they
            are invoked. If none exist, the event is broadcast to all
            `API_ERROR` handlers as a fallback. If no `API_ERROR` handlers are
            registered at all, Yapper falls back to emitting a concise debug
            line via `YapperTable.Utils:DebugPrint` (or `print()`). These
            messages are informational only and intended to aid debugging; their
            exact formatting may change between releases.

    • Filters MUST return the payload table (or false).  Returning nil
      is treated as "continue unchanged" for safety, but please don't
      rely on it — always return the payload explicitly.

    • Filter payloads are plain tables.  Modify fields in-place; do not
      replace the table itself (return the same reference).

    • Do not cache YapperAPI references across files; the global is
      stable for the entire session.

    • If you need a hook point that doesn't exist yet, open an issue
      or PR on the Yapper repository.
