promotional bannermobile promotional banner

Letterbox

Letterbox - Mailbox Helper for all WoW Versions
Back to Files

Letterbox-v0.0.2

File nameLetterbox-Letterbox-v0.0.2.zip
Uploader
PmptastyPmptasty
Uploaded
Oct 3, 2026
Downloads
10
Size
100.8 KB
Flavors
RetailMoP ClassicClassic TBCClassicForever
File ID
9052259
Type
R
Release
Supported game versions
  • 12.1.0
  • 12.0.7
  • 12.0.5
  • 5.5.4
  • 2.5.6
  • 1.60.1
  • 1.15.9

What's new

Letterbox Changelog

[v0.0.2] (2026-10-03) - Fixes from the first test

Played on Classic Era and WoW Forever; Anniversary, MoP Classic and Retail load the same files and have not been tried in game.

Fixes

  • The List view button stayed on the Send Mail tab, over the To box. InboxWindow:PlaceSwitch anchored it in the inbox's button band but left it parented to MailFrame, which every tab shares. When the band is in use it is now parented to InboxFrame, like Open and Return, so it hides with the inbox. The old specs asserted the MailFrame parent; they now assert InboxFrame, and a new spec hides InboxFrame and checks that the button goes with it.
  • Blizzard's "Postage:" line ran into the settings arrow on the Send Mail tab (operator's screenshot, 2026-10-03). The arrow is on MailFrame, so it shows on both tabs, and its band spot overlaps SendMailCostMoneyFrame's right edge (-50,-34). Settings:PlacePostage now centres the line between the contact book's arrow (or the To: box) and the arrow's visible left edge, at Blizzard's height, and runs again after every SendMailFrame_Update, where Blizzard re-sizes the amount. Only placed when the band layout is in use. New specs in settings_spec.lua. Second look (screenshot): centred, it still touched both arrows. The "Postage:" label is now GameFontNormalSmall (operator: "maybe we need smaller font with a tooltip?"), and a hover area over the label and amount gives a tooltip with the full postage from GetSendMailPrice, the call Blizzard's SendMailFrame_Update makes.
  • The docked Mailbox window and send box covered the quick attach buttons on the Send tab (operator's screenshot, 2026-10-03). Both docked to the mail frame's right edge, and the quick attach column sits just outside it. The dock point now moves right past the column, plus ns.DOCK_GAP, while the column shows, and back when it hides: the column's OnShow/OnHide call ns.Redock (Core.lua), which re-docks each window through LibAceGUIWidgets' DockWindow with the new offset (ns.DockOffset, measured on screen because the column is scaled). New spec in inboxwindow_spec.lua.
  • The Mailbox window left out letter-only mail, so an inbox holding only letters looked empty (operator's Forever screenshot, 2026-10-03). InboxWindow.BuildMails dropped any mail with no attachment and no money, on purpose; it now lists every mail. A letter-only row shows the header's stationery icon (or INV_Letter_01), its tooltip says there is nothing to take, and right-click, Shift-click and Take Shown skip it (InboxWindow.HasContents). The spec that asserted the old rule now asserts the new one, and the row-position specs moved down one for Bob's letter.

Release notes

  • docs/Curseforge_Description.html had no Recent Updates section. Added one, grouped New / Changed / Fixed, with v0.0.2 above v0.0.1. The page's opening line now says "an early test release" instead of "the first", and README.txt's status line names v0.0.2.
  • README.txt and the CurseForge page brought into step, feature for feature: every one of the 15 registered features (ns.RegisterFeature) plus the always-on ones (full subject, minimap icon, Postage) is described in both with the same detail. README.txt gained the details it had dropped (Express's shortcut hints, Contact book's newest-first order, Name completion's Blizzard options, Quick attach's fill-until-full and recipient fill, Coin subject clearing) and a Recent Updates section matching the page. Both now say the Mailbox window keeps Open All's free bag slots (from its in-game Help), docks past the Quick attach buttons, and that WoW Forever has been played.

[v0.0.1] (2026-09-30) - First test release

The first release, for player testing. Every section below this heading went into it. Played on Classic Era only; the other four game versions load the same files and have not been tried in game.

Rename to Letterbox (2026-10-01)

  • The addon is renamed to Letterbox, because CurseForge rejected the old name. The folder, the TOC files (Letterbox.toc and its _TBC, _Camelot, _Mists and _Mainline siblings), the SavedVariables (LetterboxDB), the slash command (/letterbox), the dialog names and the text window's frame name all changed.
  • Known cost: settings saved under the old name are not carried over, so testers start with the defaults.
  • The Name completion setting "Complete names as you type" is now saved under the key complete.

Release preparation (2026-09-30)

  • .pkgmeta did not list LibAceGUIWidgets, though all five TOCs require it, so a CurseForge install would not have fetched it and the addon would not have loaded. Added as libaceguiwidgets, the slug TOGBankClassic's .pkgmeta uses.
  • Every comment removed from .pkgmeta (operator: comments there "have caused release issues"). The harness's pkgmeta.lua checks only tracked files, so it could not check the file before the first commit.
  • The in-game Help told players to report problems on CurseForge. It now points to the Discord, the only place reports are tracked.
  • README.txt and docs/Curseforge_Description.html rewritten for release: every feature and its settings, the settings menu, the slash command, LibAceGUIWidgets as the third requirement, and the v0.0.1 notes.

Select layout, from the first in-game look (2026-09-30)

  • All of Letterbox's top buttons now sit in the band under the title bar (operator: "all of the mailbox stuff could be there, spaced well to fit into that 'dead' space"). Left to right: List view, Open, Return, the settings arrow, centred over Blizzard's rows. The settings arrow was under TSM's title-bar button, and List view covered the "Inbox" title beside TOG Bank's. The title bar is left to Blizzard, TOG Bank and TSM. One marker for the rows' original place now lives in Core (ns.RowsMarker), shared by Select, Settings and the Mailbox window. The TOG Bank placement below is now only the fallback for a client without Blizzard's row frames. Not checked on the Send tab. Second look (screenshot): the group leaned left, because List view stuck out further than the arrow. Open and Return stay centred and List view and the arrow now end equally far out, with List view as tall as Open and Return (ns.BAND, Core.lua). Third look (screenshot): List view ran into the round portrait, every gap was different, and the row sat at the bottom of the band. Now one 10-unit gap between all four, the row centred across the empty strip right of the portrait (ns.BAND_X), and raised to the band's vertical middle (ns.BAND_Y). Both numbers are measured from the Classic Era screenshot only. Two Select specs that asserted the old layout (buttons inside the rows' width, Open and Return centred over the rows) now assert the new one, plus a spec for the equal gaps and the centring. Fourth look (screenshot): List view still touched the portrait, and the gaps still looked uneven because the settings arrow's art is about 2 units narrower than its frame on each side. List view's left edge moved 4 right (ns.BAND_LEFT), the arrow's visible right edge stays put (ns.BAND_RIGHT), and the gaps are now equal between what shows (ns.ARROW_INSET).

  • The Mailbox window and send box dock to the mail frame (operator: "use the snaptowindow function and have the list view snap to the mailbox"). LibAceGUIWidgets' DockWindow, to the right of Blizzard's mail frame, docked by default; a drag that ends near that edge docks again, anywhere else stays put, and both are saved (ns.DockToMailbox, Core.lua). This replaces PersistWindow, which the library refuses on the same window as DockWindow; the ClearFrame's own status table now keeps the size, with the 420x300 floor set on its resize handle. ClearFrame moves by its title bar and never fires OnDragStop, so the end of a title drag calls the dock's Moved by hand. The over placement in both windows' Open is gone: the dock's show hook placed them anyway.

  • The list view's search box text wrapped onto two lines (screenshot). Its placeholder is now "Item, sender or subject".

  • The "List view" button sat on top of TOG Bank's button (operator's screenshot): I placed it on TOG Bank's own measured spot. With TOGBankClassic loaded it now sits just right of TOG Bank's button; without it, in the title bar's left corner. Decided by whether the addon is loaded, since both buttons are made on the first mailbox open, in either order.

  • The select boxes hung off the left of the inbox, over its border, crowding the row numbers (operator's screenshot). They were anchored left of each row's icon and nothing made room for them. Select now moves Blizzard's rows right by 24 and narrows each by the same, so the boxes and their numbers sit inside the inbox. Blizzard's layout is read once and put back when Select is turned off.

  • Open and Return were right of centre (screenshot). They were centred on the inbox frame, whose right edge carries a shadow, so its centre is not the rows' centre. They are now centred over Blizzard's rows, just above the first one.

  • Both are placed from Classic Era's layout (MailFrame.xml:12, :352) and read the row anchor at run time; Forever has the same row frames (Blizzard_MailFrame/MailFrame.xml:359). Only Classic Era has been seen in game.

  • Forward and the send box shared a copy-pasted lookup of the mail body box; it is now one function in Core (self-audit, inbox 3c3f6e55). Money text is likewise one Core helper (Open All and the Mailbox window).

  • From Peer Review on that audit: the send box now says "Not sent" when Blizzard's Send button is not enabled, instead of pressing it silently; Take Shown chooses mail by position as well as by contents, so of two identical-looking mails only the one shown is ever taken from; the send box registers its close callback once; and the Mailbox window's help says it keeps Open All's free bag slots. Whether two windows may share one saved size/position table is asked of LibAceGUIWidgets.

  • 391 examples pass, coverage 100% (2703 lines).

The Mailbox window (2026-09-30)

  • New optional inbox: TOGBankClassic's inbox-as-inventory window, lifted in (todo #2; the operator: "can we lift that UI in, make it optional"). One row per mail with something to take; the arrow opens it into its attachments and money, each with the real game tooltip. Click an attachment to take it, Shift-click or right-click a mail to take all of it, filter by item, sender or subject, Take Shown takes what the filter left, and the envelope returns a mail. It is not shown unless chosen (profile.inboxWindow.shown, off by default).
  • The switch (todo #4): a small "List view" button at the left of Blizzard's mail frame title bar, where the operator placed TOGBankClassic's own button, switches to the window. The window's "Blizzard's inbox" button, or its own Close, switches back. The choice is saved in the profile, so the next mailbox opens the same way. Until the player moves it, the window opens over Blizzard's mail frame. Blizzard's frame stays open underneath it: its OnHide closes the mailbox (Classic Era MailFrame.xml:847; the other trees not checked). The button's position is TOGBank's Classic Era measurement and has not been checked in game on the other flavours.
  • The send box (todo: Send tab reskin): the window's "Send mail" button swaps in a send box in the same look. TOGBankClassic has no send window of its own, so this is its look applied to sending: one list with an "Attached" group and an "In your bags" group (every item that is not soulbound), filtered by name; click a bag item to attach it, click an attached one to put it back. To, Subject and Text sit above, with Blizzard's recipient copied in when it opens. Send writes the three into Blizzard's own boxes and presses Blizzard's own Send button, so the send, money, COD and every check are Blizzard's; money and COD are still set on Blizzard's Send tab. "Inbox" goes back. Its To box has none of the name completion Blizzard's has. Layout not checked in game.
  • Built on LibAceGUIWidgets (its RowList, ClearFrame and search box), now a required dependency in all five manifests. That library is the shared form of TOGBankClassic's own list.
  • Every take and return goes through the engine, so the window has no loop or timer of its own. The engine gained job.pick(index, attachment), which chooses single attachments and the money; an open mail is remembered by its signature, so it stays open when the inbox renumbers.
  • GM mail never offers Return; COD mail does, since returning it is how a player refuses to pay.
  • Harness cb8f710: typing over highlighted text replaces it, so the name completion spec's typeIn workaround is gone. Harness c9905e1: GameTooltip:SetInboxItem (inbox d6cec081), so the window spec's stand-in is gone and it checks the harness's own record.
  • Approved and applied: efb5f535 and 71f61db9 (the new window spec), b6034fee (a no-op check in the engine pick example) and 103b66df (Quick attach's Forever list, which clears last session's pinned failure). c474f1c4 and d17f3a42 were approved but not applied: neither was needed. d16de7e8 (the doubled blank line in a copied invoice with no body) was re-filed as 409806b5 and de59852e and approved. Copy mail no longer puts two blank lines above an invoice when the mail has no text.
  • All 386 examples pass and coverage is 100% over twenty-two files (2669 lines). Two send box examples read a cell on a group heading, which the library draws without cells; their corrections (d3d9ab5d, 86cd0d3f) were approved and applied. 29f8e490 was approved but not needed.

Peer Review fixes and harness 90c979d (2026-09-30)

  • Quick attach on WoW Forever attached nothing from its Trade Goods button (found by Peer Review). Forever was given Classic Era's set, whose Trade Goods button matches subclass 0 only, but Forever's trade goods are split into subclasses like TBC's. Forever now has its own list, built from the subclasses its 598 trade goods actually use in LibItemDB's in-game data (1 and 5-12), then the all-Trade-Goods button. The spec line that pinned the old list waits for your approval (test change 103b66df); until then that one example fails.
  • Quick attach icons: five of the representative items were in the wrong category (found by Peer Review against LibItemDB). Every icon item is now checked against LibItemDB's TBC, Forever and Mists data, cited in the code, and a new spec reads those files and checks each item's subclass. It caught one more: Weapon Enchantment had borrowed Item Enchantment's item. Categories with no data (Weapon Enchantment, Inscription, Optional Reagents) show the question mark.
  • Forward reads "Moving..." while it moves items, so a click to nudge it no longer looks like a new forward. If it ends with items taken but not attached, one chat line says how many are in the bags (Peer Review).
  • ExpiryIcon's mail header and the engine's ns.MailSignature each say why the other exists and must not be merged.
  • Harness 90c979d: the To: box spec is now made from the harness's AutoCompleteEditBoxTemplate and typed with frames.typeText, so Blizzard's own OnTextChanged-to-popup path runs in the tests. My copy of Blizzard's completion code, the Battle.net, GetInboxText, MoneyFrame_Update and string stand-ins are gone. One gap is left and requested: typing over highlighted text should replace it (the spec's typeIn does that for now).
  • All 340 examples but the one pinned line pass, and coverage is 100% over twenty files (2171 lines).

Quick attach (2026-09-30)

  • QuickAttach.lua, features 73-77, its own feature ("Quick attach"). A column of category buttons hangs off the top right of the mail frame and shows with the Send tab.
    • Left-click: fills the category's default recipient into the To: box, highlighted, if it has one. Then it attaches every matching item from the bags in use until the mail is full. Locked and soulbound items are skipped, whatever made them soulbound, so none is tried and failed.
    • Right-click: a popup sets or clears the category's default recipient. The tooltip names it.
  • Categories per flavour, by the client's interface number:
    • Classic Era: Trade Goods and Reagent. WoW Forever gets the same set; not checked in game.
    • TBC: the twelve TBC Trade Goods kinds, then all Trade Goods.
    • Wrath: the TBC set plus Materials, Armor and Weapon Enchantment.
    • Cataclysm and Mists: the TBC set plus Materials and Item Enchantment.
    • Retail: its own set, with Inscription and Optional Reagents.
    • Button names are the client's own, so they are localised.
  • No item database needed, Retail included. An item's kind comes from the client's GetItemInfoInstant, which needs no item cache and exists on every flavour. The plan named LibItemDB, which has no Retail data.
  • Bag toggles and recipients are kept apart. Clearing the last recipient does not wipe the settings or turn every bag back on; clearing a recipient touches nothing else. The options offer the backpack, each bag, and the reagent bag only where the client has one.
  • Not checked in game: the representative item IDs behind the button icons (an unknown ID shows a question mark), and where the column sits.
  • Tests/quickattach_spec.lua, 12 examples. All 335 pass, and coverage is 100% over twenty files (2163 lines).
  • Test change c474f1c4 was raised by an edit I then did not need; it can be declined.

Forward (2026-09-30)

  • Forward.lua, features 70-72, its own feature ("Forward"). A Forward button sits left of Blizzard's Reply on an open mail. Clicking it:
    • switches to the Send tab with Blizzard's own tab switch, as Reply does;
    • empties the To: box (after the To: box helpers' autofill), sets the subject to "FW: " and the original (never twice), copies the body, and puts the cursor in the To: box;
    • takes each attachment into the bags and attaches it to the new mail. Nothing is sent.
  • When it is off: money, C.O.D., an item that stacks, an item not yet in the client's cache, or fewer free general-purpose bag slots than attachments. Profession bags are not counted. The state is worked out again when the open mail updates, the bags change, or item data arrives, not only when an inbox row is clicked. It is also off while another feature's run is going.
  • Moving the items: taking goes through the shared engine, filtered to this mail, so it is paced by the client's answers. Each item is attached when it lands in a slot that was empty at the click, on the bags' update and when the run ends. An item that lands locked is attached at the next bag update, or when Forward is clicked again, which also nudges the engine. Items already in the bags are never touched. A slot is looked at again once its item is attached, because the next attachment can land there.
  • Stackable items are refused because a taken stack can merge into one already in the bags, and then there is no new slot to find it in.
  • Known cost: the items really are taken. If the player abandons the forward, they stay in the bags and the original mail is left empty.
  • The send body is MailEditBox on Classic Era and SendMailBodyEditBox on Live and Forever; whichever the client has is used.
  • Express's attach and freeSendSlot moved to Core as ns.AttachFromBag and ns.FreeSendSlot, and the engine shares ns.FreeGeneralSlots, so Express and Forward use one copy of each.
  • Tests/forward_spec.lua, 14 examples. All 323 pass, and coverage is 100% over nineteen files (2023 lines).
  • Not checked in game: where the button sits, and whether an attached item leaves its bag slot at once (the attach logic works either way).

Copy mail (2026-09-30)

  • CopyMail.lua, features 68-69, its own feature ("Copy mail"). An open mail with text, or an auction invoice, gets a small button at the top right of its text. It grows when the pointer is on it. Clicking it opens the text window with the mail written out, selected, so Ctrl+C copies it:
    • the sender (or Unknown) and the subject, with Blizzard's "From:" and "Subject:";
    • the body text;
    • for an auction invoice, the invoice in the order Blizzard's open-mail view shows it, with Blizzard's labels.
  • Invoice amounts are plain coin words ("1 Gold 20 Silver 0 Copper"), which copy as text where coin icons do not.
    • A seller's amount received is the sale price plus the deposit minus the cut, as Blizzard works it out.
    • A stack shows its count.
    • An invoice with no one player name says Multiple Buyers or Multiple Sellers.
    • An invoice for funds not yet sent gets no block, because its amounts are not final.
  • ns.HookOpenMailUpdate in Core hooks the open mail's update, which is a global on four trees and a mixin method on WoW Forever.
  • The text window's Show can now select its text and focus it.
  • CoinSubject.Words is the coin-words half of the coin subject's formatter, shared with the invoice.
  • Offline gap: the harness has no GetInboxText and none of the invoice labels. The env sets enUS stand-ins (requested on the WoWAPITesting inbox).
  • Tests/copymail_spec.lua, 12 examples. All 309 pass (with the expiry icon's two below), and coverage is 100% over eighteen files (1902 lines).
  • Known cost: an invoice mail with no body text gets two blank lines before the invoice. The fix changes two tests' expected text, so it waits for your approval (test change d16de7e8, reason filed).
  • The button uses the friends list's note icon, a path Blizzard's own XML uses in Classic Era and Live, rather than an icon path that is nowhere in the UI source (Peer Review).
  • Not checked in game: where the button sits.

Expiry icon (2026-09-30)

  • ExpiryIcon.lua, features 66-67, its own feature ("Expiry icon"). Under each inbox row's time left, a small icon shows what happens to the mail when it runs out:
    • a red X if it will be deleted;
    • an arrow pointing back if it will be returned to its sender.
  • Hovering the icon says Delete or Return, then the expiry time's own tooltip.
  • Clicking the icon makes the same choices as Blizzard's Delete button on an open mail:
    • mail that would go back is returned at once, and a C.O.D. confirmation is closed;
    • mail that would be deleted, with items, asks first, naming the first attachment;
    • mail that would be deleted, with money, asks first, showing the amount;
    • mail that would be deleted, with nothing in it, is deleted at once.
  • The confirmation remembers the mail, not its row, so mail that arrives or goes before the player clicks Accept cannot make a different mail be deleted. The dialog keeps the mail's sender, subject, money, C.O.D. and attachment count, and Accept deletes the mail that still matches. If no mail matches, nothing is deleted. Time left is not part of the match, because it counts down between the click and the accept.
  • The click itself works the same way: each icon keeps its mail's details from the last repaint, so a return or delete goes to that mail even if the inbox moved without a repaint (found by the self-audit).
  • Blizzard's own confirmation dialogs delete the open mail, so they could not be reused for a row. Ours use Blizzard's text.
  • A post-hook on the inbox repaint, in whichever shape the client has. Nothing of Blizzard's is replaced, InboxFrame_Update included. The icons hide while a run is going, as Select's boxes do.
  • ns.InboxIndex(row) in Core is the row-to-mail number that Select and the icons both use. It was a local in Select.
  • Offline gap: the harness has no MAIL_RETURN, no delete confirmation strings and no MoneyFrame_Update. The env sets enUS stand-ins (requested on the WoWAPITesting inbox).
  • Tests/expiryicon_spec.lua, 14 examples.
  • Not checked in game: where the icon sits, and how the mirrored arrow looks.

Name completion (2026-09-30)

  • NameCompletion.lua, features 62-65, its own feature ("Name completion").
  • Typing in the To: box with the cursor at the end fills in the first name that starts with what was typed. The rest is highlighted and the cursor stays after what was typed. The sources are checked in this order, and the first match wins:
    1. alts on this realm and faction;
    2. alts on other realms, as Name-Realm without the realm's spaces;
    3. recently mailed;
    4. contacts;
    5. Battle.net friends;
    6. guild members.
  • Each source has a toggle. "Complete names as you type" turns all six off and greys them.
  • Blizzard's own completion runs first, and a match here replaces its fill. This is done with post-hooks on AutoCompleteEditBox_AddHighlightedText (which records what was typed before Blizzard's fill) and the To: box's OnChar. Every flavour's To: box sets addHighlightedText, so Blizzard always makes that call when the cursor is at the end.
  • Battle.net friends: one path on all five flavours. Every tree documents C_BattleNet.GetFriendAccountInfo, and every tree's own AccountUtil.lua walks BNGetNumFriends()'s online count into it. A friend's character is offered when:
    • the friend is online in WoW on this game version (WOW_PROJECT_ID);
    • the character has a realm ID above 0 and is on the player's faction. This is Blizzard's CanCooperateWithGameAccount rule, written out here because that function takes a different argument on the Classic trees than on Live and Forever.
  • Blizzard's completion settings: Offer friends, Offer guild members, Leave out people you only met, and Hide Blizzard's name list, under "Blizzard's completion".
    • The flags are pushed to the To: box the moment a setting changes, with no reload needed.
    • "Leave out people you only met" offers this account's characters, the group, friends and guild. Battle.net accounts are always left out, as the To: box's own flags do.
    • Turning the feature off puts back AUTOCOMPLETE_LIST.MAIL.
    • Hiding the name list uses Blizzard's own AutoComplete_HideIfAttachedTo, after AutoComplete_Update.
  • ContactBook entries now carry the name they fill (target) and, for alts, the realm. Name completion reads the same lists the contact menu shows.
  • Offline gap: the harness has no C_AutoComplete results, no Blizzard_AutoComplete code, no GetUTF8CursorPosition and no Battle.net friend list. Tests/env_letterbox.lua installs a port of each on every reset (requested on the WoWAPITesting inbox, d287b2e9).
  • Tests/namecompletion_spec.lua, 18 examples. All 283 pass, and coverage is 100% over sixteen files (1682 lines).
  • Not checked in game: whether Blizzard's popup and fill look right with ours on top of them.

To: box helpers (2026-09-30)

  • Recipient.lua, features 58-61:
    • Opening the Send tab with an empty To: box fills in the newest name recently mailed from this realm and faction, highlighted so typing replaces it. This can be switched off under "To: box helpers options".
    • After a send goes through, the name used is put back, highlighted, and added to the To: box's history, which now keeps 15 names (the client's own edit-box history).
    • Focusing the To: box highlights its text.
    • Turning the feature off puts back Blizzard's history setting.
  • All of it is post-hooks; SendMailFrame_Reset and MailFrameTab_OnClick are not replaced:
    • SendMailFrame's OnShow covers the Send tab opening.
    • SendMailFrame_Reset covers the form being emptied after a send. Blizzard calls it by name on MAIL_SEND_SUCCESS in every tree, so the refill lands after the form is emptied.
    • SendMail notes who the send was for, and MAIL_FAILED forgets it. A reset that did not follow a send refills nothing.
  • ContactBook:MostRecent gives the autofill its name, from the same recently-mailed list the menu shows.
  • The spec reads the To: box's history and highlights from the harness (957dbe2, inbox 9f708080). Its stand-ins for them are gone, as is the contact book spec's friends-list stand-in (17fc549, inbox e78fb83c).
  • Tests/recipient_spec.lua, 9 examples. All 265 pass, and coverage is 100% over fifteen files (1549 lines).

Contact book (2026-09-30)

  • ContactBook.lua, features 49-57. An arrow beside the Send tab's To: box opens a menu. Picking a name fills the To: box, and moves the cursor to the subject if the To: box had it. The menu has:
    • Contacts: saved names, sorted, with Add (the name in the To: box; no blanks or duplicates in any case) and Remove.
    • Recently mailed: the last 20 names mailed from this realm and faction, newest first, never this character, with Clear list. The 20 are counted per realm and faction, so one realm's names never push out another's.
    • Alts: this account's other characters on this realm and faction, with class-coloured level and class, and a Delete sub-menu.
    • All alts: every other character anywhere. Picking one fills Name-Realm, using the realm as the client normalises it (no spaces). It has its own Delete sub-menu.
    • Friends: sorted, except on Korean and Chinese clients, where the client's order is kept.
    • Guild: every member, online or not, with class-coloured level and class, and rank.
    • An empty list is greyed out. Any list over 25 is split into sub-menus of 25.
  • The character record (feature 57) is account-wide: each character's name, realm, faction, level and class. It is rewritten at login and at every mailbox open, so a level gained mid-session shows, not just once a session.
  • Recently mailed is recorded from the send itself. A post-hook on SendMail notes the recipient, MAIL_SEND_SUCCESS files it, and MAIL_FAILED drops it. Blizzard's SendMailFrame_Reset is not replaced.
  • Two other bugs avoided:
    • The paged alt lists never show the current character.
    • A guild member's name is never parsed back out of the menu label. It comes from the roster, so a rank holding the separator is harmless.
  • Not verified in game: where the arrow sits, and whether Ambiguate(name, "mail") gives the form the mail server wants for a guild member on a connected realm.
  • C_FriendList is a stand-in in the spec until the harness has it (requested on the WoWAPITesting inbox).
  • Tests/contactbook_spec.lua, 18 examples. All 256 pass, and coverage is 100% over fourteen files (1489 lines).

Gold summary (2026-09-30)

  • GoldSummary.lua, feature 48. When the mailbox closes after a visit that made money, one chat line says how much: "Collected <amount> this visit.", with the amount in the client's coin icons (C_CurrencyInfo.GetCoinTextureString). The figure is simply the money at the close less the money at the open, so postage and COD payments are already taken off. A visit that made nothing, or cost money, says nothing. Turned on with the mailbox open, it counts from that moment.
  • Tests/goldsummary_spec.lua, 4 examples. All 238 pass, and coverage is 100% over thirteen files (1285 lines).

Trade block (2026-09-30)

  • TradeBlock.lua, features 46 and 47. While the mailbox is open, trade requests are blocked and guild or arena charter requests are declined, so neither pops up over the mail. Both go back to normal when it closes, when the player leaves the world, and when the feature is turned off.
  • Trades: the blockTrades CVar, the name Blizzard's Social settings use in all five trees. It is set only if it was off, and only what this feature set is put back. It avoids three failure modes:
    • Turning the feature off never forces trades back on, so the player's own permanent block always survives.
    • A crash with the mailbox open could leave trades blocked for good. The "we set it" mark is saved account-wide, and the next login undoes it.
    • A change the player makes during the visit is never overridden. That change stands.
  • The addon Lexan manages trade blocking, so with it loaded the trade setting is left alone.
  • Charters: Blizzard's PetitionFrame stops hearing PETITION_SHOW during the visit, and the petition is declined with ClosePetition, as Blizzard's frame does itself when it cannot show. Charters do not depend on Lexan, so they never stay muted for good with Lexan loaded.
  • Tests: env.reset now disables the addon copy the previous example loaded. Nothing ever collected those copies (each one's Express hooks the shared GameTooltip). While enabled, they kept hearing MAIL_SHOW, so an old copy blocked trades inside the next example. Earlier features only touched their own frames, so this had not shown up before.
  • Tests/tradeblock_spec.lua, 10 examples. All 234 pass, and coverage is 100% over twelve files (1264 lines).

Coin subject (2026-09-30)

  • CoinSubject.lua, feature 45. When money is typed on a mail with no subject, the subject becomes the amount, for example "[2 Gold 30 Silver 7 Copper]". Leading zero coins are left out. Taking the money back to zero clears it again, and a subject the player typed is never changed. Its own subject is recognised for any amount, so it is rewritten as the money changes.
  • The words are the client's GOLD_AMOUNT / SILVER_AMOUNT / COPPER_AMOUNT, so they appear in the player's language. Those strings carry grammar escapes (|4one:few:many;); each escape is replaced with its first form, which needs no per-language table or hard-coded fallback. Not verified in a Russian client.
  • It post-hooks OnTextChanged on SendMailMoney's gold, silver and copper edit boxes, the same keys in all five trees. Blizzard's onValueChangedFunc field is left alone, so its send-button check never runs through this addon's code.
  • The harness delivered the coin words and Classic's MoneyInputFrame_GetCopper (3e5a472, inbox 5250672f). The submodule is pinned there and the stand-ins are gone. The spec's money input now has the named boxes the harness reads (SendMailMoneyGold and the rest), as well as the keys the addon hooks.
  • Tests/coinsubject_spec.lua, 7 examples. All 224 pass, and coverage is 100% over eleven files (1212 lines).

Express on the bags (2026-09-30)

  • Express.lua, features 41-44. These work only while the Send tab is shown and the cursor is empty.
    • Alt-click a bag item to attach it to the first free send slot.
    • With auto-send on and a recipient typed, the mail is sent at once, once. It sends only if the slot holds what was clicked (same icon and count).
    • Ctrl-click attaches the clicked stack, then works through wider and wider matches. It stops after the first match that attached anything else, or when the mail's 12 slots are full. The matches, in order:
      1. Every poor item for a poor one, or every uncommon piece of gear for an uncommon piece of gear.
      2. The same item.
      3. The same class and subclass.
      4. The same class.
      5. The same quality.
    • Ctrl-click skips bound and locked (already attached) stacks. A stack the client refuses to attach goes back to the bag.
    • The bag tooltip gets "Alt-click to send it to <name>." and "Ctrl-click to attach similar items.".
  • One click hook per client, installed once per session, so hooks never pile up with every visit and one Alt-click never sends more than once.
    • Classic Era, Classic and Anniversary: a post-hook on the global ContainerFrameItemButton_OnModifiedClick.
    • Live and Forever: the bag click there is a mixin method on pooled buttons, so the hook is the global HandleModifiedItemClick(link, itemLocation). It acts only for a bag-and-slot location in the player's own bags, reagent bag included.
    • Tooltips: the post-call where C_TooltipInfo.GetBagItem exists (Live, Forever), otherwise OnTooltipSetItem.
  • Auto-send happens inside the click, never from an event. SendMail is a restricted function. It works the way Blizzard's own send does:
    • An empty subject is filled with the item name and count, as SendMailFrame_Update would.
    • Then Blizzard's Send button is clicked, so its money and COD handling runs. Tested with the harness's protected-call check turned on.
  • Similar items are compared using GetItemInfoInstant, which needs no item cache, so items the client has not loaded are not skipped. An unknown class never counts as a match.
  • Known cost: a post-hook cannot stop Blizzard's own Ctrl-click first. On gear, that opens the dressing room. Also not verified in game: that the attachment appears straight after the click. If it does not, auto-send does not fire, and the item stays attached.
  • The feature is renamed "Express clicks", since it now covers bag clicks too. Its options gain the three bag toggles, and auto-send is greyed out while Alt-click attaching is off. The inbox spec now looks for the new submenu name (approved test change 20e4ead455bd9f60).
  • Tests/express_bags_spec.lua, 30 examples. All 217 tests pass, and coverage is 100% over ten files (1155 lines).

Self-audit and Peer Review fixes (2026-09-30)

  • WoW Forever had no inbox hooks at all. Forever has no InboxFrame_Update global. Its repaint is the mixin method InboxMixin:Update, called as InboxFrame:Update() (forever Blizzard_MailFrame/MailFrame.lua:147, :316). My Select hook had no guard, so on Forever it raised on the first open, and its boxes never followed a page change after that. The Engine and Overflow hooks were guarded, so they silently did nothing. All three now use one helper, ns.HookInboxUpdate, which hooks the global when it exists and the frame's method otherwise. ns.RefreshInbox repaints the same way. Found by Peer Review (thread 679c0e42).
  • Forever's gamepad toggle showed Blizzard's Open All button again beside ours (InboxMixin:UninitializeGamepad, :293-294). Open All now hides it again from a post-hook on that method.
  • Waiting for mail the server holds kept the inbox locked. While the engine waits for more mail to arrive, the rows are now clickable and not greyed. They lock again when the mail arrives.
  • A stuck Select run could only be ended by closing the mailbox. Select's buttons ignored clicks during a run. Any feature's button now nudges whatever run is going (Engine:Resume). Open All no longer stores a job that never started when it is clicked during another feature's run.
  • One copy each: the inbox row count is ns.InboxRows(), read when it is needed rather than at load. Retail loads the mail frame on demand, so its INBOXITEMS_TO_DISPLAY may not exist when the addon loads. The arrow textures are ns.ARROW / ns.ARROW_DOWN, and the one-line-per-mail chat output is ns.AnnounceEachMail, shared by Open All and Select.
  • Test fix: "keeps a feature ... at the next login" never tested login. A new test builds AceDB from saved variables at login, with no SetProfile, and checks that the switched-off feature is not running. The old test is renamed to what it proves: choosing a saved profile applies its switches (approved test change 8a639a6f016e5b11).
  • Looked into and nothing to fix: the order of the three repaint post-hooks does not matter. Each one changes something different (row icons, the too-much-mail warning, Select's boxes).
  • 187 tests pass, and coverage is 100% over ten files (1024 lines).

Express on the inbox (2026-09-30)

  • Express.lua, features 37-40, only post-hooks. Blizzard's row click sends the auto-loot modifier's click to InboxFrame_OnModifiedClick, which already takes everything from a non-COD mail, and every other click to InboxFrame_OnClick. The hook on InboxFrame_OnClick takes everything on Shift, for a player who has rebound auto-loot to another key, and never from COD. On Ctrl it returns the mail when it can go back: not system, auction or GM mail, not already returned, and one that can be replied to. The default auto-loot key per flavour has not been verified.
  • The row tooltip, via a hook on InboxFrameItem_OnEnter, lists every attachment of a mail with several (icon, link, count), which can be switched off under "Express inbox clicks options". It also adds a hint line for each shortcut that applies to that mail.
  • Fixed before it shipped: express_spec caught my existence check reading GetInboxHeaderInfo's first return. That return is the package icon, nil for every mail without a package, so every letter would have been ignored. It now checks the index against GetInboxNumItems().
  • Tests/express_spec.lua, 10 examples; 179 in all, coverage 100% over ten files.

Select (2026-09-30)

  • Select.lua, features 29-35: a checkbox (UICheckButtonTemplate, all five trees) beside each of the seven inbox rows, labelled with its mail's inbox number. Clicking checks or unchecks. Shift checks everything between the last click and this one and never unchecks. Ctrl sets every mail from the same sender to this box's new state. Selections are kept by inbox index, so they survive paging, and they move with their mail when new mail arrives at the front (seen by the old first mail turning up further down). They clear when a run ends or the mailbox closes.
  • Open and Return buttons at the top of the inbox run the shared engine over the checked mail only. The pressed button relabels, the other hides, and the boxes hide until the run ends. Return sends back only mail that can go back, and the end-of-run line counts what could not.
  • Fixed before it shipped: select_spec caught the run following the checked indexes. Returning mail 4 moved mail 5 down to index 4, which was still checked, so it would have been returned too, and the one after it. A run now fixes the checked mail by signature when it starts.
  • Its own keep-free slots (default 1) and per-mail chat line (default on), under "Select options" in the settings menu. The settings menu now gives any feature with settings of its own such a submenu, so Open All's options appear there as well as on its arrow.
  • The end-of-run "Left some mail" line moved into Engine.SkipSummary, shared by Open All and Select, and the mail signature is shared as ns.MailSignature.
  • Where the boxes and buttons sit has not been checked in game. Tests/select_spec.lua, 17 examples.

Open All filters and messages (2026-09-30)

  • OpenAll.lua, features 19-21: which mail a plain click takes. Auction mail is sorted by the client's own subject templates (AUCTION_REMOVED/EXPIRED/OUTBID/SOLD/WON_MAIL_SUBJECT), each matched whole with its %s as a wildcard, so it works in every locale. That sorting is ns.ClassifyMail(subject), for Select to reuse. Each auction type has its own switch. Postmaster items are taken when that option is on. Everything else follows "All other mail with attachments", which also governs gold-only mail. Shift-click takes it all. COD and GM mail are never taken: the engine skips them before the filter runs.
  • Postmaster senders are matched by English name only ("The Postmaster", "Thaumaturge Vashreen"), because the client has no string for either; neither is in GlobalStrings/enUS.lua. In other locales that mail falls under the other-attachments option. Not verified in game.
  • Feature 18: at the end of a run, one chat line says what was left and why: COD, from a GM, bag space, unique items already carried, refused, or never answered. Bag space is left out when the keep-free notice already said so during the run.
  • Feature 28: an optional line per mail as it is opened, on by default. It gives the index, the subject, the gold (C_CurrencyInfo.GetCoinTextureString, all five trees), and for a sold or won auction the buyer or seller from GetInboxInvoiceInfo's third return.
  • The options arrow's menu gains a "Take" submenu with the seven filter switches, and "Say each mail in chat".
  • Tests/openall_spec.lua grows to 23 examples. GetInboxInvoiceInfo and the five subject strings come from the harness, pinned at 3730ff7 (WoWAPITesting inbox 12e9a8c7). A spec's invoice fields use the client's own names (invoiceType, itemName, playerName). The env now holds no stand-ins.

Open All button (2026-09-30)

  • OpenAll.lua, features 14-16: Letterbox's own Open All button, the same 120 x 24 as Blizzard's OpenAllMail (all five client trees). It is anchored on Blizzard's button, not at a fixed offset, so it sits wherever each flavour puts that. Blizzard's is hidden while the feature is on and shown again when it is turned off, which also stops a run in progress. The label reads Blizzard's own "Opening" string during a run and "Open All" otherwise.
  • A click starts the shared engine. Clicking during a run is the nudge that skips a step the server never answered. Shift at the click is kept for the whole run as override, for the type filters to read. The bags-nearly-full and waiting-for-more-mail notices go to chat.
  • The options arrow beside it opens a menu with "Keep bag slots free": 0, 1, 2, 3, 5, 10, 15, 20, 25 or 30, default 1. That choice becomes the engine's keepFree.
  • The button's look and the arrow's position have not been checked in game.
  • Tests/openall_spec.lua, 11 examples.
  • The bag-space spec correction was approved by the operator (writ test change e52894b2) and is in: all 140 examples pass. The withdrawn overflow_spec rewrite (57c2d461) was approved too but not applied. The spec it would have replaced already passes as written.

Full subject on truncated inbox rows (2026-09-30)

  • SubjectTooltip.lua, feature 11: hovering an inbox row whose subject is cut off adds the whole subject, in white and wrapped, to the row's tooltip. It counts as cut off when the text is wider than the field minus a 4-pixel margin. It is a post-hook on InboxFrameItem_OnEnter, which all five client trees define, installed at the first mailbox open. The subject is read from GetInboxHeaderInfo for the row's mail, not from the truncated text on screen. Always on, not a switchable feature.
  • Tests/subjecttooltip_spec.lua, 7 examples.
  • Test harness pinned at c1b3b43, which models MenuUtil.CreateContextMenu and a description's CreateCheckbox / CreateRadio (WoWAPITesting inbox dd80a662). The local stand-in is deleted, and specs read the menu from wow.lastMenu and its owner from wow.lastMenuOwner.

Text window and Help (2026-09-30)

  • TextWindow.lua, feature 10: one reusable window with a title, a close button, and a scrolling body the player can select and copy from. Typing into it puts the text back. Escape closes it, through UISpecialFrames and the body's Escape handler. It is built on first use from UIPanelCloseButton and UIPanelScrollFrameTemplate, which all five client trees define. Its look has not been checked in game.
  • Settings.lua, feature 9: "Help" in the settings menu opens that window with the addon and version, each feature's name and what it does, and where to report a problem. ns.RegisterFeature now takes the feature's description as its fourth argument.
  • Tests/textwindow_spec.lua, 6 examples.

Profiles (2026-09-30)

  • Settings.lua, feature 7: a "Profiles" submenu to choose a profile, copy from another, delete another, create a new one, or reset this one. The current profile is never offered to copy from or delete. "New profile..." opens a popup filled in with the current name, and accepting switches to the typed name (trimmed; a blank name does nothing). The popup reads its edit box with dialog:GetEditBox(), which is how all five client trees' StaticPopup exposes it. Every action is AceDB's own.
  • Core.lua, feature 7: choosing, copying or resetting a profile applies its feature switches at once (AceDB's OnProfileChanged, OnProfileCopied and OnProfileReset).
  • Core.lua, feature 8: each character now starts on its own profile. The account-wide "New characters share one profile" checkbox (global.shareProfile) is read from the raw saved variables before AceDB opens, so it takes effect at the next load. This changes the first build's behaviour, where every character shared "Default". That build was never released.
  • core_spec's "keeps what LetterboxDB already holds" now sets the share option in its saved data, because its data lives in "Default". Its assertion is unchanged.

Settings menu and chat output (2026-09-30)

  • Settings.lua, features 1 and 2: a small arrow button at the mail frame's top-right, made the first time the mailbox opens, opens a menu built with Blizzard's MenuUtil. Blizzard_Menu is in all five client trees, so no flavour uses the legacy UIDropDownMenu. The menu has a title, a checkbox per feature, and a "Chat window" submenu listing each window that is configured and shown or docked. The button's position beside Blizzard's close button has not been checked in game.
  • Core.lua, features 2 and 3: features register with ns.RegisterFeature(key, module, label). Their saved on/off (profile.features, all on by default) is applied before AceAddon enables anything. SetFeature enables or disables the module at once. The too-much-mail notice is the first switchable feature: switched on with the mailbox open it speaks straight away, and switched off it hands Blizzard's warning back.
  • Core.lua, features 5 and 6: Print writes the coloured "Letterbox" tag and the text to the chosen chat window. A chosen window that is no longer configured, shown or docked falls back to window 1, and the setting is rewritten to 1.
  • Not built: the opening-speed setting (feature 4). It would be a delay between takes, and the engine has no timer (docs/PLAN.md).
  • Tests/settings_spec.lua, 18 examples. MenuUtil.CreateContextMenu and a description's CreateCheckbox / CreateRadio are local stand-ins until the harness models them (WoWAPITesting inbox dd80a662).

Mail the server holds back (2026-09-30)

  • Engine.lua, feature 27: when the shown inbox is done and the server holds more (below the 100 it ever shows), the run stays going. It calls CheckInbox once, from the event handler (Blizzard's InboxGetMoreMail does the same from MAIL_SUCCESS), tells the job (onWaitMore(hidden)) and continues from the new last mail when MAIL_INBOX_UPDATE brings more. Resume asks again. The 10 s re-check and the 3 s pause are not built, because they are timers (docs/PLAN.md). Feature 26, restarting from the new end mid-run, was already in the engine.
  • Overflow.lua, feature 36: Blizzard's too-much-mail line under the inbox is hidden at open and after every repaint (a post-hook on InboxFrame_Update). Instead, one chat line per visit says how much mail is held back. While fewer than 100 are shown and under 60 s have passed since the shown count last grew, it also says roughly how long until more arrives.
  • Fixed before it shipped: overflow_spec caught the first inbox update of a visit being treated as the shown mail growing from zero, which made every estimate "about 60 seconds". The count at open is now the baseline.

Bag space in the engine (2026-09-30)

  • Engine.lua, features 22 and 23: a job's keepFree stops item taking once the free general-purpose bag slots (bag family 0, backpack and bags 1 to NUM_BAG_SLOTS) reach it. It tells the job once (onLowSpace(free)) and still takes money. While bags count as full, by keepFree or by the client's inventory-full error, an item is still taken if it fits whole on a partial stack of the same item already in the bags. That check reads the stack size from C_Item.GetItemInfo, so an uncached item never counts as fitting. Feature 24 (slowing to one take a second) is not built: it is a timer.
  • One engine_spec example of mine had wrong expectations. Its correction waits on the operator's approval (writ test change e52894b2c1fdb972), and it is the one red example until then.

The take/return engine (2026-09-30)

  • Engine.lua: the one loop Open All, Select, Express and Forward will drive (Engine:Start{ mode, filter, onStep, onDone }, Resume, Stop). It works from the last mail to the first, attachments from the highest slot down, and takes money after every item. Take mode never takes COD or GM mail. Return mode returns only mail that InboxItemCanDelete says can go back and that has not already been returned; it skips the rest. onDone gets counts of items, money and returns, and each skip reason.
  • No timer. The first command comes from the player's click. Every later one comes from the client's own answer: MAIL_INBOX_UPDATE, MAIL_SUCCESS, MAIL_FAILED, UI_ERROR_MESSAGE, BAG_UPDATE_DELAYED or PLAYER_MONEY. A step counts as done only when that attachment, that money or that mail is gone, not when the inbox totals move. The next command waits for C_Mail.IsCommandPending() to clear. writ refused both a pacing timer and a watchdog timer (the operator: "never recommend a timer for anything that interacts outside of the addon"). So a step that never gets an answer ends in a click instead: Resume asks IsCommandPending, does nothing while the server is busy, and otherwise skips the step.
  • Full bags and unique items stop the step instead of hanging it. The handler reads UI_ERROR_MESSAGE's message, which is its second argument, and compares it with ERR_INV_FULL (no more items this run, money still taken) and ERR_ITEM_MAX_COUNT (that item ID is skipped in every mail). A MAIL_FAILED that names an item blocks that item too.
  • Mail that arrives at the front of the inbox mid-run restarts the loop from the new last mail. Mail already worked is either empty or remembered as skipped, by a signature of sender, subject, stationery, COD, returned, text and GM. Two otherwise identical mails share a signature.
  • Inbox lock: while a run is going, the seven inbox rows ignore the mouse (EnableMouse) and their icons are greyed. A post-hook on InboxFrame_Update re-greys them if Blizzard repaints. Both are restored when the run ends, and the run stops when the mailbox closes. No Blizzard function is replaced.
  • Fixed before it shipped: engine_spec caught a bags-full or unique-item failure being counted twice, because the failed slot was not marked done and the loop came back to it.
  • Tests/engine_spec.lua, 32 examples; coverage of Core.lua, Mailbox.lua and Engine.lua is 100%. C_Mail and the two error strings come from the harness, pinned at b1f9386 (WoWAPITesting inbox 81fb2a8a); the stand-ins that preceded it are deleted. Not yet run in game on any flavour.

Mailbox visits (2026-09-29)

  • Mailbox.lua: an AceAddon module that marks when a mailbox visit starts (MAIL_SHOW) and ends (MAIL_CLOSED, or PLAYER_LEAVING_WORLD with the mailbox open). Both are idempotent, so a repeated MAIL_CLOSED ends the visit once. Features register Mailbox:OnOpen / Mailbox:OnClose for per-visit setup and reset. The event pair is documented in all five client trees, so there is no per-flavour branch.
  • On close, when every mail of that visit's last inbox update was read and the server held no unsent mail, the minimap mail icon is hidden: MiniMapMailFrame on the Classic flavours, MinimapCluster.IndicatorFrame.MailFrame (then its Layout) on Retail.
  • Built from docs/BEHAVIOUR_SPEC.md features 12 and 78. Not yet run in game on any flavour.
  • Tests/mailbox_spec.lua, 17 examples; env.login() in the test environment fires PLAYER_LOGIN so AceAddon enables the modules.

The addon's framework (2026-09-29)

  • Created the repository's framework, modelled on Questbook: three manifests (Letterbox.toc for Classic Era 11509, Letterbox_TBC.toc for TBC Classic and Anniversary 20506, Letterbox_Camelot.toc for WoW Forever 16001), .pkgmeta, the packager's release.yml, .luacheckrc, the MIT LICENSE, README.txt, docs/Curseforge_Description.html and the dev replication watcher.
  • Core.lua: the AceAddon object, LetterboxDB through AceDB, the VersionCheck-1.0 registration and /letterbox, which prints the running version. No mailbox feature yet.
  • Tests: the WoWAPITesting harness as the Tests/wowapi submodule, Tests/env_letterbox.lua, core_spec.lua (ADDON_LOADED wiring, raw version string, VersionCheck, the slash command, and that the original addon's saved settings are never read or modified) and env_spec.lua (the environment loads the TOC's files, and the three manifests agree on files and header fields).
  • The game versions follow Questbook's set. docs/PLAN.md recorded this as undecided; this commit takes Questbook's three as the default, and adding or dropping a flavour is one manifest plus one line in env_spec.lua.
  • CurseForge project 1718729: curseforge-project-id in .pkgmeta and ## X-Curse-Project-ID in every TOC; env_spec.lua now asserts every manifest carries the same id.
  • Added MoP Classic (Letterbox_Mists.toc, 50504) and Retail (Letterbox_Mainline.toc, 120005/120007/120100), the interface numbers GuildRoster ships. The replication watcher now copies to _classic_ and _retail_ too. Neither has been loaded on its client yet.

This mod has no additional files