Skip to content

Releases: TimothyLuke/GSE-Advanced-Macro-Compiler

3.3.32

Choose a tag to compare

@TimothyLuke TimothyLuke released this 03 Sep 08:55
f225d4c

GSE

3.3.32 (2026-09-02)

Full Changelog Previous Releases

  • Merge pull request #2046 from LarryThiessen/fix-2044-showtooltip-metatable
    #2044 ApplyShowTooltipToAction must rawget: the Actions array carries the path metatable
  • #2044 ApplyShowTooltipToAction must rawget: the Actions array carries the path metatable
    Saving from the editor errored on every sequence:
    GSE/API/Statics.lua:504: bad argument #1 to 'ipairs' (table expected, got string)
    ... ApplyShowTooltipToAction <- SanitizeSequenceEditorMarkup <- ReplaceSequence
    SanitizeSequenceEditorMarkup walks every table in the sequence and now calls
    ApplyShowTooltipToAction on each one. The editor sets
    Statics.TableMetadataFunction on Versions[n].Actions, and that __index only
    understands table (path) keys, so the plain action.Type lookup on the
    Actions array itself lands in ipairs("Type"). Editor.lua already rawgets
    past this metatable for the same reason.
    rawget Type and the macro bodies. Spec added: an Actions array carrying
    the metatable returns false without erroring (fails on the previous code
    under a 5.1-strict ipairs with the exact in-game message).
    Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com
  • #2045 Record the origin key a sequence is born with, and freeze it
  • #2044 #showtooltip is ignored in practive in GSE and should not be stored as part of the macro block chewing up character count
  • Merge pull request #2042 from LarryThiessen/fix-2040-new-sequence-versions
    #2040 Build version children for any loaded sequence, not only path-matched ones
  • Merge pull request #2043 from LarryThiessen/fix-2041-class-move-duplicate
    #2041 Remove the old record when a sequence changes class or spec
  • #2041 Remove the old record when a sequence changes class or spec
    The Specialization/Class ID dropdown reassigns editframe.ClassID in place,
    and the save enqueues a Replace under that NEW classid. Nothing removed the
    record from the old one, so GSESequences[old][name] and GSE.Library[old][name]
    survived and loaded again -- the sequence came back under both branches on
    the next reload, which looks like something is duplicating sequences.
    A rename already moves its old key via renamesequence; a class move had no
    equivalent. Record the class a sequence was loaded from and, when a save
    changes it, drop the old record from both stores. Keyed on the original
    name and placed before the rename branch (which returns early) so a rename
    and a move in the same save are both covered.
    Co-Authored-By: Claude Opus 5 noreply@anthropic.com
  • #2040 Build version children for any loaded sequence, not only path-matched ones
    versionsWanted decided whether to build a node's version children from the
    tree's expanded/selected state at build time. GUICreateNewSequence adds the
    record to GSE.Library and then calls ManageTree(), but the new node is
    neither expanded nor selected yet -- its SelectByValue runs after the build
    -- so no path matched and the node came up with Configuration and New
    Version and nothing between them until the editor was closed and reopened.
    The decompress is the expensive half and stays gated on wantVersions.
    Assembling children for a record already in the Library is a few table
    inserts, so gate the load, not the children.
    Co-Authored-By: Claude Opus 5 noreply@anthropic.com

3.3.31

Choose a tag to compare

@TimothyLuke TimothyLuke released this 01 Sep 20:49

GSE

3.3.31 (2026-09-01)

Full Changelog Previous Releases

  • Merge branch 'master' of https://github.com/TimothyLuke/GSE-Advanced-Macro-Compiler
  • @2023 fix editor drift where it has moved away from the source of truth (storage.lua) and is trying ti implement things that simply dont exist. The messages now point to the actual settings which match what is present on gse.tools
  • Merge pull request #2039 from LarryThiessen/fix-2038-variable-implementation-link
    #2038 Bracket-index the Implementation Link when the name is not an identifier
  • Merge pull request #2030 from LarryThiessen/fix-2028-restore-version-nodes
    #2028 Build the tree after restoring its state, so versions show on open
  • #2028 Time the tree build where it now happens
    The build moved below the expansion/selection restore but its timestamps
    stayed where it used to be, so afterTree - afterCreate bracketed nothing:
    ReportOpenTiming printed "ManageTree 0" on every open and the build's real
    cost went silently into the select+show bucket instead.
    That report is not decorative -- it prints to the user unprompted above
    2000 ms, and its whole job is to say which of CreateEditor / ManageTree /
    select+show owns a slow open. It is also the measurement anyone would use
    to answer what this change costs on open.
    Bracket the build at each of its two call sites instead. The restore work
    in between now belongs to neither bucket and surfaces as the gap between
    the three numbers and the total, which is what the comment above
    openStartedAt already tells you to read a gap as.
    Co-Authored-By: Claude Opus 5 noreply@anthropic.com
  • #2038 Bracket-index the Implementation Link when the name is not an identifier
    Built as =GSE.V.() by concatenation, a variable whose name is not a
    bare Lua identifier got a link that does not reference it: for
    SLG-SBAssist-LvL the result is valid Lua meaning GSE.V.SLG - SBAssist -
    LvL(), so it is not caught as malformed and fails at runtime with an
    arithmetic-on-nil that is swallowed. The sequence produces nothing while
    the editor's own Current Value box -- which indexes GSE.V[name] -- shows
    the correct output.
    Emit dot syntax for a valid identifier and bracket syntax otherwise,
    matching how GSE already references variables internally (Storage.lua
    BooleanVariables keys, the QoL Tab menu insertions, and the editor
    preview). Both build sites go through one helper.
    Co-Authored-By: Claude Opus 5 noreply@anthropic.com
  • #2036 Fix the tree assigning a string rather than an ID for Global Sequences in its indexes
  • Merge pull request #2034 from LarryThiessen/fix-2033-honest-fit-cache
    #2033 Fit cache must match the frame, not just the last computation
  • Merge pull request #2029 from LarryThiessen/fix-2027-dependency-list-layout
    #2027 Clear the dependency headers and tighten the row pitch
  • Merge pull request #2022 from LarryThiessen/fix-2021-tab-line-anchor
    #2021 Anchor the Tab session to the caret's line; recolour after picks
  • Merge pull request #2026 from LarryThiessen/fix-2024-editbox-pool-reset
    #2024 Restore EditBox text colour and enabled state on widget reuse
  • #2033 Fit cache must match the frame, not just the last computation
    The last macro block of a version could draw at baseline height with its
    text clipped, and neither typing nor reopening the version fixed it.
    FitMacroEditBoxToContent early-returns when a fit computes the same
    height it computed last time -- but a later layout pass can shrink the
    box's FRAME while gseFitHeight remembers the correct number, after which
    every subsequent fit computes that same correct number, matches the
    cache, and returns without repairing the frame. Stuck, with no path
    back.
    The no-op check now also verifies the frame's live height (within 1px)
    before returning, so a frame that drifted from the cached value gets
    re-applied. The fit is self-healing against any layout pass that resizes
    a box behind its back.
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • Merge pull request #2032 from LarryThiessen/fix-2031-counter-ruler
    #2031 Live X/255 counter measures the compiled length, like the Save gate
  • #2031 Live X/255 counter measures the compiled length, like the Save gate
    Push a block over 255 and Save greys; delete until the counter reads
    under 255 and Save stays greyed. The gate and the red backdrop measure
    the COMPILED body length (TranslatorMode.String -- what WoW enforces),
    but the live counter in OnTextChanged measured the raw display text.
    Translated spell names are usually longer than what is typed, so the
    counter dropped under 255 while the compiled body stayed over: the gate
    was right and the counter lied. The draw-time counter already used the
    compiled length -- the live path had diverged from #1998's stated
    intent.
    The live counter now uses GetCompiledMacroBodyLength(storedMacro): one
    ruler for the counter, the red trigger and the Save gate. It may read
    higher than the raw typed text; that is the truthful number.
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #2028 Build the tree after restoring its state, so versions show on open
    Opening the editor onto the restored sequence showed only Configuration
    and New Version -- no version nodes. Version children are built lazily
    (#2014): versionsWanted reads the tree's expanded/selected state at
    BUILD time, but ShowSequences built the tree before restoring that
    state, and the OnGroupExpanded rebuild only fires on a click, never on
    restore.
    The build moves below the state restore, keeping exactly one build per
    open. The normal path marks the restored sequence node itself expanded
    (segment 3 of the saved path) so its versions are wanted, then builds,
    then selects; the detached-tree path builds after mirroring the source
    tree's groups, before SetSelected.
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #2027 Clear the dependency headers and tighten the row pitch
    The first data row of the Dependencies list drew on top of the
    Name/Type/Author/Date column headers: the data scroll's 8px top padding
    does not clear the header band drawn on the box frame (top 4 + height
    15). And 22px rows with 20px labels and a 2px list gap around ~12px text
    read as double-spaced.
    Top padding now clears the band (12 -- the scroll content already starts
    inset below the frame top, so less than the band's full 19 is needed,
    and the comment ties the numbers together). Row height 22 -> 16, column
    label height 20 -> 16, list gap 2 -> 0. Applied to both dependency views
    so they stay identical.
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #2024 Restore EditBox text colour and enabled state on widget reuse
    An EditBox released while disabled (or with a caller-set text colour)
    kept that state into its next life -- the version-label box came back
    GREY after a version delete redrew the pane. SetTextColor and Disable()
    live on the frame itself, invisible to all three existing reset sweeps
    (table keys, regions/children, scripts), and the FontString styling
    restore does not cover EditBox frames.
    The per-subframe pristine snapshot now also records GetTextColor and
    IsEnabled for text-bearing frames, and reuse restores both.
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #2021 Anchor the Tab session to the caret's line; recolour after picks
    With the caret on a middle line, a picked Spell landed at the end of the
    box's LAST line. Probe-proven: the session anchored itself to a byte
    offset, and the box is rewritten UNDER the open menu -- the macro
    editor's deferred compile (scheduled by the focus-loss that opening the
    menu itself triggers) normalises the text, so every byte offset silently
    slides onto a different line.
    The session now anchors to the caret's LINE INDEX -- newlines survive
    both recolouring and compile normalisation -- and every read re-derives
    fresh offsets from the live text via lineBounds() (clamped past the end,
    so a rewrite that removes lines cannot walk the anchor off the box).
    Picks that open a new row advance the anchor; undo snapshots and
    restores it. The visible caret parks at the line END at session start
    instead of visibly jumping to the front.
    The box also never recoloured after building a line: splice()'s
    recolour-token bump -- needed to invalidate stale debounced repaints
    from EARLIER edits -- also cancelled the repaint its own SetText just
    queued, and no second focus-loss ever fires to run the commit repaint
    (the box lost focus when the menu opened; nothing refocuses it). Every
    content-producing pick now runs the same cosmetic repaint the
    live-typing debounce uses.
    And because the mid-session compile trims trailing spaces, pickSpell
    supplies its own separator (", " after a spell, " " after any other
    non-space ending) instead of assuming the line still ends in one --
    "/cast [combat]" + pick gave "[combat]Spell" without it.
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #2020 Fix spell cache and cross language translation issues...
Read more

3.3.30

Choose a tag to compare

@TimothyLuke TimothyLuke released this 24 Aug 23:00
62b8f57

GSE

3.3.30 (2026-08-24)

Full Changelog Previous Releases

  • Merge pull request #2019 from LarryThiessen/fix-2018-pool-reset-styling
    #2018 Reset styling, subframe art and frame flags on widget reuse
  • #2018 Keep the macro-box height meter out of the pool's reach
    The reset now sweeps caller-added fields off every frame the widget exposes,
    which is right -- but FitMacroEditBoxToContent caches its measuring
    FontString as macroEditBox.frame.gseHeightMeter, and that key is
    caller-added.
    A FontString cannot be destroyed. Swept, the next fit creates another one on
    a frame that is reused for the rest of the session, so the box accumulates
    one hidden orphan per reuse -- and each one is another entry in the
    {frame:GetRegions()} walk that resetForReuse itself runs on every reuse, so
    the reset gets slower the longer the session runs. Silent and cumulative.
    This is the meter's third home: on the widget table (#2014 strips
    post-construction keys), on the frame (this branch strips those too), and
    now in a module-local weak-keyed table where neither sweep reaches it. The
    frame is the only thing that keeps an entry alive.
    Worth knowing generally: with this branch the pool is entitled to remove
    anything a caller parks on a widget's frames, so a region cached there is no
    longer safe. The comment on the table says so for the next caller.
    Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
    Claude-Session: https://claude.ai/code/session\_01JaKGoo9zcWMDowDLy4cAbX
  • Merge master into #2018 pool-reset branch
  • #2014 reduce round tripping when rendering the editor
  • #2018 Reset styling, subframe art and frame flags on widget reuse
    Three classes of previous-life state survived the pool's reset:
    FontString styling -- labels reused from the block editor kept its fonts
    and class colours, so the Config page drew with mixed heading sizes and
    colours. Each construction-time FontString's font, colour and
    justification is now recorded and restored on reuse. (FontStrings are
    detected by having a font API but no CreateFontString: they ARE
    ScriptRegions, so filtering on SetScript matches nothing.)
    Subframe art and flags -- callers hang textures, child frames and plain
    fields directly off the widget's frames, top frame and exposed subframes
    alike (rail textures, editBox.GSEMacroEditorColoring...). That is
    invisible to the widget-table key sweep: block rail lines bled into the
    Config and Notes panels, and a reused multiline box applied macro syntax
    colouring to the read-only Notes text. The reset now records every
    exposed frame's construction-time key set, regions and children; on
    reuse it sweeps caller-added frame fields (engine userdata at [0] kept)
    and hides stranger regions and children.
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • Merge pull request #2017 from LarryThiessen/fix-2014-widget-pool
    #2014 Pool NativeUI widgets instead of abandoning their frames
  • Merge pull request #2016 from LarryThiessen/fix-2013-fit-batch
    #2013 Batch the macro-box fit pushes; hard-hide the box scrollbar
  • Merge pull request #2015 from LarryThiessen/fix-2010-spellcache-load
    #2010 Draw the Spell Cache editor on first open, not at addon load
  • Merge pull request #2012 from LarryThiessen/feat-2011-tab-menu-order
    #2011 Move Macros and GSE Variables to the bottom of the Tab menu
  • #2013 Wheel always scrolls the block list, wherever the cursor hovers
    The focused macro box used to consume the wheel and scroll its own text.
    That predates the auto-fit: boxes now size themselves to their content,
    so there is nothing left to scroll inside one -- but hovering (or typing
    in) a box still changed what the wheel did, which read as jumpy,
    inconsistent scrolling. The wheel now always drives the outer block
    list; ScrollFocusedMacroEditor is gone.
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #2014 Pool NativeUI widgets instead of abandoning their frames
    Release() only hid a widget's frame -- and WoW frames can never be
    destroyed -- so every editor redraw abandoned its entire widget set:
    +238 widgets per version click, forever. Long sessions accumulated
    thousands of dead frames and megabytes of unreclaimable state, and the
    collector dragged an ever-growing graph for the rest of the session.
    AceGUI pooled widgets for exactly this reason; the rewrite dropped it.
    Widgets are plain tables of closures over their own frame, so the SAME
    table is reused. UI:Create hands out a banked widget when one exists;
    Release banks poolable types. The reset contract, each part of which
    covers a real bug found in testing:
    * every key added after construction is removed (caller decorations
    and wrapped methods must not survive into the next life)
    * callbacks/children become fresh tables
    * frame scripts recorded at construction are restored on every
    subframe the widget exposes
    * regions and child frames callers created directly on the widget's
    frame are hidden on reuse -- the key sweep cannot see those, and
    without this a reused frame keeps rendering its previous life
    (header rows bleeding into macro blocks, doubled counters, dead
    scrollbars intercepting the mouse wheel)
    * a double Release cannot bank the same widget twice
    Only the 13 high-churn leaf/container types pool; windows, scroll
    frames, tab groups and the tree keep their old behaviour. Kill switch
    for diagnostics: /run GSE_NoWidgetPool = true and /reload.
    Best merged with #2013: pooled reuse of macro edit boxes is what makes
    the scrollbar re-show race there common.
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #2013 Batch the macro-box fit pushes; hard-hide the box scrollbar
    Switching versions in the Sequence Editor blocked the client for
    0.5-1.2s per click while the editor's own Lua cost 40-70ms. Bisection
    (each draw component toggled off independently in a live client) pinned
    the difference on one thing: FitMacroEditBoxToContent ran its ancestor
    height-push per macro box, mid-draw. Each push re-laid-out every shared
    ancestor container -- block list, scroll frame -- re-anchoring all their
    children, so a click generated N-boxes x whole-chain SetPoint volume and
    the engine's layout invalidation ate the frame.
    Fit pushes are now queued and flushed once per frame by a driver, and
    each ancestor container is laid out ONCE per flush instead of once per
    box (the dedupe is the essential part: deferring the walks without it
    just moved the storm one frame later). A queued push whose widget was
    released before the flush is skipped. Version clicks drop to 40-70ms
    with identical visuals.
    Also in this function: the scrollbar hide was a one-time Hide(), but the
    scroll template re-Shows the bar whenever the text's scroll range
    changes, so the bar could come back. The hide is now permanent for the
    widget's life (bar.Show = bar.Hide, applied once).
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #2010 Draw the Spell Cache editor on first open, not at addon load
    SpellCache.lua's setup ended with SelectTab on the first cache tab, which
    fires GUIDrawSpellCacheEditor and builds two EditBoxes plus labels for
    EVERY cached spell -- at load time, for a frame that starts hidden. The
    cache only grows (imports merge the exporter's whole cache, the
    translator caches every name it resolves, nothing prunes), so long-time
    users paid seconds of frozen client inside the on-demand
    LoadAddOn("GSE_GUI") that opening any first GSE window triggers.
    Measured: 3.8s of a 4.4s blocked frame.
    Defer the initial tab draw to the frame's first OnShow: the window pays
    its own cost when actually opened, and loading the GUI stops stalling
    the client.
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #2011 Move Macros and GSE Variables to the bottom of the Tab menu
    They are the whole-block / whole-line cases and the least used entries
    while a line is being assembled, yet they sat above Commands. The block
    that emits them (all three paths: macro-edit mode's own-line insertions,
    the greyed alone-in-the-block case, and the fill-the-block case) moves
    verbatim into addNameEntries(), called at the tail of both generator
    paths, so the entries land at the bottom with behaviour unchanged.
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #2004 make sure marco edt capability works for macro editing not just sequence blocks.
  • Merge pull request #2007 from LarryThiessen/feat-2004-tab-line-builder
    #2004 Build the whole macro line from the Tab menu
  • Merge pull request #2006 from LarryThiessen/feat-2003-macro-block-polish
    #2003 Centre the action icon on the macro box and drop its scrollbar
  • Merge pull request #2005 from LarryThiessen/fix-2002-editor-fill-on-load
    #2002 Keep a macro block's height fit inside the block
  • #2004 Land the first command in the placeholder, not under it
    Tabbing on a fresh block put the command on a second line: the pick asks
    rowBeforeCaret() whether the current row is empty, and...
Read more

3.3.29

Choose a tag to compare

@TimothyLuke TimothyLuke released this 23 Aug 03:29

GSE

3.3.29 (2026-08-22)

Full Changelog Previous Releases

  • #1963 Check label usage and move Notes to a rendered view
  • #1963 Fix terminology in tooltip. Sequences are not macros.
  • Merge pull request #2001 from LarryThiessen/fix-1998-macro-box-autofit
    #1998 Auto-size the macro-block command box to its content
  • #1998 Macro box minimum is 3 rows, and the fit counts line spacing
    Drop MACRO_BOX_MIN_LINES to 3 so a short block stops reserving five rows.
    The minimum was doing double duty as the widget's build height, and that
    height is load-bearing: NativeUI's SetNumLines(n) is n * 16 +
    STYLE.frameContentTop (28), so five rows IS macrolayout's SetHeight(108),
    and the fit pushes the DELTA from that baseline up through the
    fixed-height ancestors. Lowering the shared constant would have built the
    box at 76 against a 108 container and left ~32px of dead space under every
    block, so the build height is now its own MACRO_BOX_BASE_LINES = 5 with
    the arithmetic written down at both sites.
    Two font-size gaps the smaller minimum exposes:
    • oneRow is measured from a single "X" and so carries no line spacing, but
      the row clamps multiplied it straight out. Real N-row text is
      N * oneRow + (N - 1) * spacing, so with a nonzero GetSpacing() the min
      clamp fell short of a true N rows and clipped the bottom one.
    • NativeUI clamps the inner editbox to 40px (updateEditBoxSize). Five
      rows always cleared it; three rows at a small chat font do not, and the
      last row would render outside the visible scroll area. The frame is
      never asked for less than that floor now.
      Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
      Claude-Session: https://claude.ai/code/session\_01JaKGoo9zcWMDowDLy4cAbX
  • Merge pull request #2000 from LarryThiessen/fix-1997-tab-insert-at-caret
    #1997 Tab spell list: insert at the remembered caret, not the end of the row
  • Merge pull request #1999 from LarryThiessen/fix-1996-caret-sync-restore
    #1996 Restore the macro-block caret synchronously after a live repaint
  • #1998 Auto-size the macro-block command box to its content
    The "Macro Name or Macro Commands" box was a fixed 5 rows, so a 7-8 line
    block always scrolled. FitMacroEditBoxToContent measures the rendered
    text height with a hidden FontString in the box's own font (wraps
    included; estimating rows x font size drifted by a row), clamps to 5..24
    rows, ignores a stored trailing newline except while the author is
    typing on that last row, and dedupes on the resulting height. It runs
    once after the block body is assembled and on every text change.
    Because the box sits under explicit-height containers (macrolayout,
    macroFields, macroBody, sized for the old 108px box), the fit pushes the
    height delta up through every fixed-height ancestor before auto-height
    ones re-lay out -- otherwise the box grew but overlapped the Repeat
    Interval row below it.
    Fixes #1998
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #1997 Tab spell list: insert at the remembered caret, not the end of the row
    Between pressing Tab and clicking the entry, the editor's debounced live
    repaint (or a focus-loss commit repaint) can SetText the box, parking the
    caret at the end -- so a plain Insert sometimes landed at the end of the
    last row. Remember the caret at Tab-press, cancel any pending live repaint
    via the gseRecolourToken the recolour already honours, and on pick do
    SetFocus + SetCursorPosition(saved) + Insert.
    Fixes #1997
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • #1996 Restore the macro-block caret synchronously after a live repaint
    RefreshMacroEditorColoredText repaints the coloured text with SetText,
    which parks the caret at the end, and only restored it via a deferred
    C_Timer.After(0) -- so a frame rendered with the caret (and the scroll
    view chasing it) at the end of the bottom line before it moved back: a
    visible flick on every live repaint, and a fast follow-up key acted at
    the end of the text. An unconditional deferred restore could also yank
    the caret back to a stale position one frame after a newer keystroke.
    Restore the caret synchronously right after SetText (the same
    SetText+SetCursorPosition-in-one-go approach IndentationLib has used for
    years). Keep the deferred restore only as a backstop: scheduled only if
    the immediate restore visibly did not take, and it re-checks that the
    text is still the text it was computed for before touching the caret.
    Colouring logic is untouched.
    Fixes #1996
    Co-Authored-By: Claude Fable 5 noreply@anthropic.com
  • Merge pull request #1995 from LarryThiessen/fix-1994-tab-menu-stale-owner
    #1994 Anchor the Tab spell-list menu to the live editbox, not a stale editor frame
  • #1994 Anchor the Tab spell-list menu to the live editbox, not a stale editor frame
    After a /reload the #1989 Tab spell list worked, then silently stopped
    appearing once the editor had been re-created (e.g. across a combat
    transition). The handler still fired; the menu never opened. The owner
    it anchored to reported hidden: GSE.CreateSpellEditBox is defined once,
    guarded by GSE.isEmpty, inside the first editor ever created, so the
    editframe inside it is a permanent upvalue to that first editor. The
    #1989 wiring passed editframe.frame as the menu owner instead of the
    factory's frame parameter (the current editor's frame), and Blizzard
    will not open a context menu on a hidden owner.
    • Editor.lua: pass the factory's frame parameter to the Tab hooks.
    • QoL.lua: anchor all four Tab menus to the editbox itself -- visible by
      definition when Tab fires -- the same approach the icon Select menu
      already takes with its panel-local frame.
      Fixes #1994
      Co-Authored-By: Claude Fable 5 noreply@anthropic.com

3.3.28

Choose a tag to compare

@TimothyLuke TimothyLuke released this 21 Aug 05:36

GSE

3.3.28 (2026-08-21)

Full Changelog Previous Releases

  • #1993 remove unrelated files
    config.ld, publishGSETools.js and uploadLocale.js were being packaged into
    the GSE folder. None of them are addon code: config.ld configures ldoc,
    publishGSETools.js posts the release to gse.tools, and uploadLocale.js is the
    new pre-build localisation upload. Build tooling, not something to ship to
    players.
    Listed twice, as the other build scripts already are — once at the root and
    once GSE/-prefixed — because move-folders maps GSE/GSE onto GSE, so the
    packager sees both paths.
    Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
    Claude-Session: https://claude.ai/code/session\_017wmCKbEwmEgWW8SpWnXJhC
  • #1989 Restore the Tab spell list in the Sequence Editor
    #1989 Restore the Patron Tab spell list in the Sequence Editor
  • Add spec/run51.lua — run the suite on the interpreter CI actually uses
    Local busted is installed for 5.4 only; CI is PUC Lua 5.1.5
    (leafo/gh-actions-lua@v10 + luarocks busted). So a spec can use anything 5.2
    added, pass every local check, and fail in CI. That has now happened twice on
    the same construct — load(chunk, name, mode, env), where 5.1's load takes a
    reader FUNCTION and a string chunk needs loadstring + setfenv.
    lua5.1 spec/run51.lua # matches CI, exits 1 on failure
    busted # full luassert, 5.4
    Run both. Verified by reintroducing the exact bug: run51 fails with CI's
    error verbatim while busted stays green.
    Deliberately NOT a busted replacement — it implements the globals the GSE
    specs use (describe/it/setup/before_each and the luassert matchers), so a
    spec reaching for more will fail here and needs busted too.
    Two details that matter:
    - one spec per PROCESS. Specs leak globals — CreateFrame is defined only by
    keydownbinding_spec — so a shared interpreter makes results depend on
    file order.
    - checksum_spec reports SKIPPED, not failed: it needs luabitop's bit
    global, which cannot be installed here (no luarocks binary). CI and
    busted both still cover it, and a runner that always shows a failure is
    a runner nobody reads.
    Do NOT substitute luajit for lua5.1. LuaJIT extends 5.1 and accepts
    load(string, ...), so it passes the very bug this exists to catch.
    Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
    Claude-Session: https://claude.ai/code/session\_017wmCKbEwmEgWW8SpWnXJhC
  • #1989 Load the spec's chunk the way Lua 5.1 wants
    CI runs busted under Lua 5.1, where load takes a reader FUNCTION — a string
    chunk goes through loadstring, and its environment is attached with setfenv.
    5.2+ removed both and added load(chunk, name, mode, env), which is what this
    used, so all six tests errored with "bad argument #1 to 'load' (function
    expected, got string)" while passing locally on 5.4.
    Both forms are supported now, chosen on whether setfenv exists rather than on
    which interpreter happens to be installed. Verified by running the same
    extraction under lua5.1 and lua5.4 directly: identical spell lists from both.
    Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
    Claude-Session: https://claude.ai/code/session\_017wmCKbEwmEgWW8SpWnXJhC
  • #1989 Enumerate the spellbook on Classic too, not just Retail
    getPlayerSpells returned an empty table when C_SpellBook was missing, so on
    Classic the Tab menu opened with an Insert Spell heading and nothing beneath
    it. Worse, the guard tested the TABLE: TBC Classic Anniversary and MoP
    Classic expose C_SpellBook without all of its functions, so they passed the
    check and then found no skill-line API — the same trap documented on
    spellIDIsInSpellBook in GSE/API/translator.lua, which is why the choice is
    now made per FUNCTION, requiring every call the modern loop makes.
    The Classic path walks tabs rather than skill lines: GetNumSpellTabs /
    GetSpellTabInfo for the offsets, GetSpellBookItemName for the name, and
    IsPassiveSpell plus GetSpellBookItemInfo for the filtering the modern API
    does with fields on an info table. Three Classic-specific behaviours the
    Retail loop has no equivalent for:
    - ranks. Classic lists one row per rank, so names are deduped. A macro
    wants the name anyway — /cast already picks the highest rank
    known.
    - FUTURESPELL rows, which are the greyed-out not-yet-learned entries, are
    excluded; only "SPELL" is castable.
    - offSpecID from GetSpellTabInfo, Cata+ only and nil before it, stands in
    for isOffSpec so another spec's book is skipped.
    A client exposing neither API returns an empty list rather than erroring,
    which is what happened before on every Classic client.
    .luacheckrc gains the five Classic globals; without them the build fails on
    undefined-variable warnings.
    spec/spellbook_spec.lua covers both APIs, the partial-C_SpellBook client, the
    off-spec tab, a client with no item-info call, and no spellbook API at all —
    none of which can be reached from a Retail test run.
    Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
    Claude-Session: https://claude.ai/code/session\_017wmCKbEwmEgWW8SpWnXJhC
  • #1991 Add toggle to turn on manual reset message
    A configured manual reset announced itself in chat every single time it
    fired. The user pressed the reset themselves, so for anyone who resets often
    that line is just noise. It is now off by default and opt-in via
    GSEOptions.AnnounceMacroReset, with a checkbox on Tools & Diagnostics.
    The message lives in Statics.MacroResetSkeleton — secure-environment code
    stamped onto the button when it is built — so it cannot read GSEOptions at
    the moment it runs. The skeleton is therefore two forms, silent and
    announcing, chosen in GSE.GetMacroResetImplementation while still in normal
    Lua. That also means a live toggle does not take effect until the button is
    rebuilt, so the setting prompts for a reload exactly as the modifier-pause
    toggles on the same page already do.
    The reset itself is unchanged with the announcement off — SetAttribute('step',
    1. still runs, which is the part worth pinning, and spec/macroreset_spec.lua
      does: silent by default, still resets, announces when enabled, a nil option
      reads as off rather than erroring, and no configured modifier still emits no
      snippet at all.
      Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
      Claude-Session: https://claude.ai/code/session\_017wmCKbEwmEgWW8SpWnXJhC
  • #1993 Have build process upload localisations.
    The documented request shape does not work. CurseForge's docs describe "a
    POST request with a JSON data blob"; posting that returns HTTP 500
    "unhandled exception" (observed on the 2a5d73c build). p3lim's
    curseforge-localizations, which works, sends multipart/form-data with two
    named parts — metadata as a JSON string, localizations as the blob — to
    legacy.curseforge.com rather than the wow.curseforge.com in the docs, and
    omits formatType entirely since TableAdditions is the default.
    Verified against a local echo server rather than the documentation this
    time: the request now arrives as multipart with an X-Api-Token header and
    metadata/localizations parts, matching the working client's shape. CF_API_BASE
    exists to point the request at that echo server.
    Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
    Claude-Session: https://claude.ai/code/session\_017wmCKbEwmEgWW8SpWnXJhC
  • #1993 Have build process upload localisations.
    CurseForge holds the phrase list translators work against, and only learns
    about a new or changed string when someone uploads it — so a release could
    ship strings nobody had ever been offered a translation for. npm run
    upload-locale now runs immediately before the packager, posting enUS to
    /api/projects/{id}/localization/import with the CF_API_KEY the packager
    already uses. Project id comes from X-Curse-Project-ID in the TOC rather than
    being hardcoded.
    missing-phrase-handling is DeletePhrase: the addon's enUS file is the master
    list, so a phrase no longer in it should not sit on CurseForge collecting
    translations for a string nothing displays. That makes the upload
    DESTRUCTIVE, hence two guards — the run refuses on an empty parse, and
    refuses when the parsed count falls below half the L[ statements a crude
    independent scan finds. A parser regression then fails the step instead of
    telling CurseForge the addon has three phrases and to delete the rest.
    CF_LOCALE_MISSING_HANDLING=DoNothing overrides per run; --dry-run prints the
    payload without posting.
    The file is parsed, not regexed. AceLocale's L["x"] = true means the value
    IS the key, which TableAdditions cannot express, so it expands on the way
    out; and 36 entries wrap across lines, one key carries an escaped quote, and
    several values are multi-line. Validated by having Lua itself read the same
    file and diffing every key and value, which caught two real bugs: \ddd
    escapes are BYTES (an em dash is ...
Read more

3.3.27

Choose a tag to compare

@TimothyLuke TimothyLuke released this 13 Aug 03:58

GSE

3.3.27 (2026-08-13)

Full Changelog Previous Releases

  • #1979 Fix GSE Options related taint

3.3.26

Choose a tag to compare

@TimothyLuke TimothyLuke released this 10 Aug 19:36

GSE

3.3.26 (2026-08-09)

Full Changelog Previous Releases

  • #1976 Localisation update
  • #1976 Update Help information
  • #1976 Classic TOC updates
  • #1965 Deltas broke exporting of variables
  • Merge pull request #1975 from TimothyLuke/dependabot/npm_and_yarn/brace-expansion-5.0.9
    Bump brace-expansion from 5.0.8 to 5.0.9
  • #1971 Fix ElvUI and Actionbar overrides when farming in MoP
  • Bump brace-expansion from 5.0.8 to 5.0.9
    Bumps brace-expansion from 5.0.8 to 5.0.9.
    updated-dependencies:
    • dependency-name: brace-expansion
      dependency-version: 5.0.9
      dependency-type: indirect
      ...
      Signed-off-by: dependabot[bot] support@github.com
  • Merge pull request #1973 from TimothyLuke/dependabot/npm_and_yarn/undici-6.28.0
    Bump undici from 6.24.1 to 6.28.0
  • update Build process post library update.
  • Bump undici from 6.24.1 to 6.28.0
    Bumps undici from 6.24.1 to 6.28.0.
    updated-dependencies:
    • dependency-name: undici
      dependency-version: 6.28.0
      dependency-type: indirect
      ...
      Signed-off-by: dependabot[bot] support@github.com
  • Merge pull request #1972 from TimothyLuke/dependabot/npm_and_yarn/multi-98c7da4b5b
    Bump brace-expansion and archiver
  • Bump brace-expansion and archiver
    Bumps brace-expansion to 5.0.8 and updates ancestor dependency archiver. These dependencies need to be updated together.
    Updates brace-expansion from 1.1.13 to 5.0.8
    updated-dependencies:
    • dependency-name: brace-expansion
      dependency-version: 5.0.8
      dependency-type: indirect
    • dependency-name: archiver
      dependency-version: 8.0.0
      dependency-type: direct:production
      ...
      Signed-off-by: dependabot[bot] support@github.com

3.3.25

Choose a tag to compare

@TimothyLuke TimothyLuke released this 17 Jul 13:24

GSE

3.3.25 (2026-07-17)

Full Changelog Previous Releases

  • #1979 restructure support requests
  • #1970 Restructure power user features
  • #1969 WoW 12.1 changes

3.3.24

Choose a tag to compare

@TimothyLuke TimothyLuke released this 14 Jul 21:00

GSE

3.3.24 (2026-07-14)

Full Changelog Previous Releases

  • #1965 TOC maintenance
  • Merge pull request #1968 from LarryThiessen/fix-1967-corrupt-seq-tree-blank
    FIX: don't blank the editor tree on a corrupt sequence; flag it instead
  • FIX: don't blank the editor tree on a corrupt sequence; flag it instead
    Fixes #1967
    • ManageTree wraps each sequence's node build in a pcall so one corrupt record
      can't blank the whole tree (was: ipairs(nil) on a record missing Versions).
    • Structurally-broken Library seqs are flagged (red + alert icon) with a
      Delete-only right-click menu; clicking one surfaces a message instead of
      decoding the broken data and crashing.
    • Decode-broken seqs (GSE.CorruptSequences) also render in the tree so a
      dismissed corrupt-sequence popup still leaves them findable/deletable;
      ProcessCorruptSequences no longer drains that list, and both delete paths
      prune it (new GSE.ForgetCorruptSequence).
    • Only flag records that are actually in the Library and broken, so a
      comma-in-name seq (comma-joined tree key) isn't false-flagged.
      Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #1965 Forks for Variables and Macros
  • #1965 CBOR unittest
  • #1965 Fork imports
  • #1965 More Fork Deltas
  • #1965 fork support
  • #1965 Initial DIFF tool
  • #1893 Add function to report issues viua the companion

3.3.23

Choose a tag to compare

@TimothyLuke TimothyLuke released this 21 Jun 23:29
fb1946e

GSE

3.3.23 (2026-06-20)

Full Changelog Previous Releases

  • Merge pull request #1964 from LarryThiessen/fix-abo-icon-not-clearing
    FIX: ABO icon not clearing until mouseover (#1963)
  • FIX: clear ABO icon immediately on removal (no mouseover needed)
    #1963
    Removing an Action Button Override cleared the GSE watermark but left the
    GSE-painted icon on the button until the next ActionButton update (e.g. a
    mouseover). LoadOverrides reverted the button and removed the watermark but
    never repainted the real action-slot icon.
    LoadOverrides now snapshots the overridden buttons and, after the re-arm pass,
    forces a real-icon repaint on any button that was cleared and not re-armed
    (button:Update() / ActionButton_Update / direct slot-texture fallback; all
    guarded for cross-version safety).
    Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #1975 Normalise Skin variances
  • #1957 move Skyriding to part of tools and diagnostics. its only relevant to people who use keybinds (< 20% of GSE Users)
  • Merge pull request #1959 from LarryThiessen/enhancements-1957-editor
    ENH: Editor toolbar/layout, live tree updates, options ordering
  • #1893 Handle Macros via import strings
  • #1893 Import cleanup
  • Fix #1960 Frames that dont render correctly
  • Merge pull request #1958 from LarryThiessen/fixes-1956-editor-options
    FIX: MacroBlock editor editing bugs + blank Options History panel
  • ENH: Editor toolbar/layout + live tree updates + options ordering
    #1957
    • Editor: swap Pause/If toolbar order; vertically center the Version Name box;
      top-button block creation inserts a sibling immediately after the focused
      block (focusing a Loop/If adds below it, not inside).
    • Tree: new versions appear instantly and deleted versions clear instantly,
      before saving (mirrored into the in-memory Library cache).
    • Options: move Skyriding / Vehicle Keybinds above Tools & Diagnostics.
      Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • FIX: MacroBlock editor live-colour editing bugs + blank Options History panel
    #1956
    • Editor: live syntax-colouring no longer eats the blank row opened with Enter,
      stops backspace at a comma, or throws the caret to the end. The live re-colour
      is now idempotent (repaints only when no visible characters change) and
      debounced, so it can't rewrite in-progress text or fight the caret.
    • Options: the GSE History/About page no longer comes up blank - its content is
      built eagerly instead of only in OnShow (a canvas parent with subcategories
      does not reliably fire OnShow).
      Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
  • #1914 TOC Updates