Files
benilla-octo/crates/benilla-app/assets/ui/ScriptLogFrame.xml
T
SamandClaude Fable 5.1 5432a8bfbd a slider snaps to its step, a pushed button releases, and warnings show
- a slider with a value step snaps to it: a stepped drag fires once per row instead of once per pixel, a scrollbar cannot rest between two rows, and one wheel notch on the trainer list reaches the bottom (2133)
- setting a step on a live slider moves its value onto the new lattice and runs its handler, which an addon on the real client can count on (2143)
- a button pushed from script releases on the next mouse-up, so Tablet-2.0's rows stop staying pressed forever; a release off the button's rect un-presses it, and a locked state holds (2134)
- the mail body and the GM ticket box scroll as you type past their bottom, and the Use UI Scale checkbox reads a registered setting (2141)
- an engine warning, a call that was accepted and did nothing, reaches the /errors window in grey instead of one terminal line; a text row trimmed mid-character no longer kills the window's repaint (2135, 2138)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 20:52:20 +07:00

430 lines
26 KiB
XML

<Ui>
<!--
benilla's **script error log** (decision 1495) — the window B293 asked for, and it is
entirely OURS: 1.12 has no counterpart, so every name here wears the `Benilla` prefix (1254's
rule, `tests::reference_surface`'s gate).
## Why it exists beside `ScriptErrors`
`BasicControls.xml`'s red `ScriptErrors` dialog is the reference's whole answer to a Lua
fault, transcribed and installed by 1305 — and it stays exactly as it is. It shows **one**
message, because `_ERRORMESSAGE` guards on `ScriptErrors:IsVisible()`, and it remembers
nothing. Two reporters said so within days of it shipping: Goudy, *minutes after
screenshotting that dialog working*, *"some kind of lua error frame probably needs to be
implemented, as there are a lot of addons that still doesn't work"*; nazriel_0, *"probably we
should start using something like Bug Grabber to report those stack traces better"*.
So: the dialog is the interrupt, this is the log. Additive, no divergence — the reference
simply never had this instrument.
## Shape
`CraftFrame.xml`'s shape, which is the house idiom for "windowed panel, faux-scrolled row
list, detail pane that repaints on selection": 13 fixed 16px rows over a
`FauxScrollFrameTemplate`, and a **real** `<ScrollFrame>` detail pane below (the real kit,
not the faux one — a message is a `mlua` error string with its `stack traceback:` tail
attached, so the detail is genuinely variable-height content, which is what the real kit is
for; `GossipFrame.xml`'s greeting pane is the same wiring).
The window registers itself in `UIPanelWindows` (below; the table is the stock UIParent.lua's
since 1988), which is what seats it in the
left slot and gets it closed by `CloseAllWindows()` — i.e. by ESC — with no code here. Our
own toplevels deliberately stay OUT of `UISpecialFrames`: that table is the addons' (0544's
note, 1206).
## Reading the model
Three engine reads, all from the retained log on the model
(`benilla_ui::script::diagnostics`): `BenillaGetNumScriptErrors()` →
`retained, totalEverRecorded`; `BenillaGetScriptErrorInfo(i)` → `seq, kind, message, count`
(1-based, oldest first, nil past the end); `BenillaClearScriptErrors()`. There is no per-frame
feed and nothing to keep in step — the window polls the log it is a view of.
-->
<Script><![CDATA[
-- The slot-manager row (decision 1495): a left-area window one tier above every stock
-- panel's pushable, so opening a game window pushes the log aside rather than the reverse.
UIPanelWindows["BenillaScriptLogFrame"] = { area = "left", pushable = 9, whileDead = 1 }
BENILLA_SCRIPTLOG_ROWS = 13
BENILLA_SCRIPTLOG_ROW_HEIGHT = 16
-- How much of a message a ROW shows. The full text is the detail pane's job; a row is an
-- index, and a row that wraps would break the fixed-height list the faux kit requires.
BENILLA_SCRIPTLOG_ROW_CHARS = 52
-- Which row is selected (a `seq`, not an index — indices shift when the cap evicts, and a
-- selection that silently slides onto a different error is worse than one that goes away).
BenillaScriptLogSelected = nil
-- The first line of a message, trimmed to a row's width. A traceback's later lines are
-- indented continuations; on one row they read as noise.
-- `strsub` is BYTE-indexed (Lua 5.0 strings are byte strings, and benilla keeps them that
-- way — decision 1193), so a cut that lands inside a multi-byte character leaves half of
-- one. Every message here is UTF-8 and ours are full of em dashes, so this was not
-- theoretical: the log window's own repaint raised on the first warning it was ever asked
-- to show (2135/2138). Back the cut up over any continuation byte (`10xxxxxx`, i.e.
-- 0x80..0xBF) so it lands on a character boundary.
function BenillaScriptLog_RowText(message)
local cut = strfind(message, "\n", 1, 1)
local head = message
if cut then head = strsub(message, 1, cut - 1) end
if strlen(head) > BENILLA_SCRIPTLOG_ROW_CHARS then
local last = BENILLA_SCRIPTLOG_ROW_CHARS - 3
while last > 0 do
local b = strbyte(head, last + 1)
if not b or b < 128 or b >= 192 then break end
last = last - 1
end
head = strsub(head, 1, last) .. "..."
end
return head
end
-- The three kinds, coloured by CONSEQUENCE — how much of the addon is broken:
-- `load` red an addon is not running at all (the thing a player can act on)
-- `error` amber code ran and raised; the addon is otherwise up
-- `warn` grey a call was accepted and quietly did not do what it said (2135)
-- Grey rather than a fourth bright colour because a warning is the least urgent of the
-- three and there are more of them: colour has to keep ranking the list, not just label
-- it. Colours come from the shipped tables so this window matches the rest of the UI.
function BenillaScriptLog_RowColor(kind)
if kind == "load" then return 1.0, 0.3, 0.3 end
if kind == "warn" then return 0.65, 0.65, 0.65 end
return 1.0, 0.82, 0.0
end
function BenillaScriptLog_Update()
local shown, total = BenillaGetNumScriptErrors()
-- The summary line carries the one number a bug report needs and the window cannot
-- otherwise show: how many were DROPPED. `total` counts distinct failures ever
-- recorded; `shown` is what survived the cap.
-- "problems", not "errors": the log carries three kinds now and two of them are not
-- errors (2135). The window is still `/errors` and still opens on the same key —
-- what changed is that a count of 7 stopped meaning "7 things raised".
local summary
if total == 0 then
summary = "No problems this session."
elseif total > shown then
summary = format("%d problems (%d older not kept)", total, total - shown)
elseif total == 1 then
summary = "1 problem"
else
summary = format("%d problems", total)
end
getglobal("BenillaScriptLogSummary"):SetText(summary)
local scrollFrame = getglobal("BenillaScriptLogListScrollFrame")
FauxScrollFrame_Update(scrollFrame, shown, BENILLA_SCRIPTLOG_ROWS, BENILLA_SCRIPTLOG_ROW_HEIGHT)
BenillaScrollTrough_Follow("BenillaScriptLogListScrollFrame")
local offset = FauxScrollFrame_GetOffset(scrollFrame)
local selectedRow = nil
for i = 1, BENILLA_SCRIPTLOG_ROWS do
local button = getglobal("BenillaScriptLogRow" .. i)
local index = offset + i
local seq, kind, message, count = BenillaGetScriptErrorInfo(index)
if seq then
button.seq = seq
button.message = message
-- The count rides the index, not the text: `#12 x1113` says "this is one
-- failure that happened 1,113 times", which is the whole reason the log
-- deduplicates instead of listing 1,113 rows.
local label = "#" .. seq
if count > 1 then label = label .. " x" .. count end
button.index:SetText(label)
button.label:SetText(BenillaScriptLog_RowText(message))
button.label:SetTextColor(BenillaScriptLog_RowColor(kind))
button:Show()
if seq == BenillaScriptLogSelected then selectedRow = button end
else
button.seq = nil
button.message = nil
button:Hide()
end
end
-- The moving highlight, seated onto the selected row only when that row is on screen —
-- scrolling the selection away hides the glow rather than stranding it (CraftFrame's
-- own behaviour).
local highlight = getglobal("BenillaScriptLogHighlightFrame")
if selectedRow then
highlight:SetPoint("TOPLEFT", selectedRow:GetName(), "TOPLEFT", 0, 0)
highlight:Show()
else
highlight:Hide()
end
BenillaScriptLog_UpdateDetail()
end
-- The detail pane: the FULL message of the selected row, traceback and all. Selection is by
-- `seq`, so this walks the log rather than indexing it.
function BenillaScriptLog_UpdateDetail()
local text = ""
if BenillaScriptLogSelected then
local shown = BenillaGetNumScriptErrors()
for i = 1, shown do
local seq, _, message = BenillaGetScriptErrorInfo(i)
if seq == BenillaScriptLogSelected then
text = message
break
end
end
end
if text == "" then
text = "Select a row above to read it in full."
end
getglobal("BenillaScriptLogDetailText"):SetText(text)
end
-- Frame-late by nature (the shared kit's own note): text rects resolve after Lua runs, so
-- the child is re-measured from OnUpdate until it stops changing, after which this is a
-- no-op compare per frame. OnUpdate only fires on visible frames, so a closed window costs
-- nothing.
function BenillaScriptLogFrame_OnUpdate()
BenillaScroll_ResizeChild("BenillaScriptLogDetailScroll", { "BenillaScriptLogDetailText" })
end
function BenillaScriptLogRow_OnClick(button)
if not button.seq then return end
-- Clicking the selected row deselects: the detail pane is a drawer, and a list with no
-- way back to "nothing selected" is a small trap.
if BenillaScriptLogSelected == button.seq then
BenillaScriptLogSelected = nil
else
BenillaScriptLogSelected = button.seq
end
BenillaScriptLog_Update()
end
function BenillaScriptLogFrame_OnLoad(self)
-- The ring's icon, through the portrait verb rather than left at its `file=` — see the
-- texture's own note. MailFrame.xml's open-letter icon is the same fix.
SetPortraitToTexture("BenillaScriptLogFramePortrait",
"Interface\\Icons\\INV_Misc_Note_01")
end
-- The shared helper steps the faux frame's bar, whose OnValueChanged runs the kit's
-- offset handler and thence `updateFunc` — so the repaint is wired once, at OnLoad, and
-- not repeated here.
function BenillaScriptLogFrame_OnMouseWheel(delta)
BenillaFauxScrollFrame_OnMouseWheel(getglobal("BenillaScriptLogListScrollFrame"), delta)
end
-- ── The three entry points the client and the player reach this by ────────────────────
function BenillaScriptLog_Toggle()
local frame = getglobal("BenillaScriptLogFrame")
if frame:IsVisible() then
HideUIPanel(frame)
else
ShowUIPanel(frame)
end
end
-- `/errors clear`. Forgets the rows, keeps the numbering (the engine's own rule — a number
-- must never name two different failures across a screenshot). Prints a confirmation
-- because a command whose whole effect is "a window you may not have open is now empty"
-- is otherwise indistinguishable from a typo.
function BenillaScriptLog_Clear()
BenillaClearScriptErrors()
BenillaScriptLogSelected = nil
if getglobal("BenillaScriptLogFrame"):IsVisible() then
BenillaScriptLog_Update()
end
DEFAULT_CHAT_FRAME:AddMessage("Script log cleared.", 1.0, 1.0, 0.0)
end
]]></Script>
<!-- One list row: the `#N xCount` index column, then the message head. Fixed columns, 1.12's
own list idiom — not a width limitation: a FontString with a zero-authored axis takes its
measured box into the resolve (`benilla-ui` `script/layout.rs`, decision 1664). -->
<Button name="BenillaScriptLogRowTemplate" virtual="true" hidden="true">
<Size><AbsDimension x="293" y="16"/></Size>
<Layers>
<Layer level="ARTWORK">
<FontString name="$parentIndex" inherits="GameFontNormalSmall" justifyH="LEFT">
<Size><AbsDimension x="56" y="16"/></Size>
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="4" y="0"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="$parentLabel" inherits="GameFontNormalSmall" justifyH="LEFT">
<Size><AbsDimension x="229" y="16"/></Size>
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="62" y="0"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<HighlightTexture file="Interface\Buttons\UI-Listbox-Highlight2" alphaMode="ADD"/>
<Scripts>
<OnLoad>
self.index = getglobal(self:GetName() .. "Index")
self.label = getglobal(self:GetName() .. "Label")
</OnLoad>
<OnClick>BenillaScriptLogRow_OnClick(self)</OnClick>
</Scripts>
</Button>
<Frame name="BenillaScriptLogFrame" hidden="true">
<!-- 384x512, the standard left-slot panel size. No <Anchors>: ShowUIPanel seats it. -->
<Size><AbsDimension x="384" y="512"/></Size>
<Layers>
<Layer level="BACKGROUND">
<!-- The same square ITEM icon the mail window's ring holds, and it needs the same
portrait verb for the same reason (`SetPortraitToTexture` in OnLoad below):
`file=` alone draws it unmasked and its corners show through the ring's
transparent ones. -->
<Texture name="BenillaScriptLogFramePortrait" file="Interface\Icons\INV_Misc_Note_01">
<Size><AbsDimension x="60" y="60"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<!-- The four quadrant slabs. The ClassTrainer family, all four, because it is the one
UNIFORM set in the 1.12 art (CraftFrame's own note) and this window belongs to no
profession — borrowing a themed half would make it look like a window it isn't. -->
<Layer level="BORDER">
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-TopLeft"><Size><AbsDimension x="256" y="256"/></Size><Anchors><Anchor point="TOPLEFT"/></Anchors></Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-TopRight"><Size><AbsDimension x="128" y="256"/></Size><Anchors><Anchor point="TOPRIGHT"/></Anchors></Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-BotLeft"><Size><AbsDimension x="256" y="256"/></Size><Anchors><Anchor point="BOTTOMLEFT"/></Anchors></Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-BotRight"><Size><AbsDimension x="128" y="256"/></Size><Anchors><Anchor point="BOTTOMRIGHT"/></Anchors></Texture>
<FontString name="BenillaScriptLogTitle" inherits="GameFontNormal" justifyH="CENTER" text="Script Errors">
<Anchors><Anchor point="TOP" relativePoint="TOP"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</FontString>
<!-- The summary line, and the ONLY place the dropped-row count can be told. -->
<FontString name="BenillaScriptLogSummary" inherits="GameFontHighlightSmall" justifyH="LEFT" text="">
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="26" y="-76"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- The top-right X: the chain's own `UIPanelCloseButton` (seated at benilla.toc's
`Interface\FrameXML\UIPanelTemplates.xml`, 757 lines above this file). Its
32x32, its three minimize-button state textures and its
`HideUIPanel(this:GetParent())` are byte-for-byte what stood here authored. -->
<Button name="BenillaScriptLogCloseButton" inherits="UIPanelCloseButton">
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="-29" y="-8"/></Offset></Anchor></Anchors>
</Button>
<!-- Declared BEFORE the rows so row text paints over the glow (the house z-tiebreak). -->
<Frame name="BenillaScriptLogHighlightFrame" hidden="true">
<Size><AbsDimension x="293" y="16"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
<Layers><Layer level="ARTWORK">
<Texture file="Interface\Buttons\UI-Listbox-Highlight2">
<Anchors><Anchor point="TOPLEFT"/><Anchor point="BOTTOMRIGHT"/></Anchors>
</Texture>
</Layer></Layers>
</Frame>
<Button name="BenillaScriptLogRow1" inherits="BenillaScriptLogRowTemplate">
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-94"/></Offset></Anchor></Anchors>
</Button>
<Button name="BenillaScriptLogRow2" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow1" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow3" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow2" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow4" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow3" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow5" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow4" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow6" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow5" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow7" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow6" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow8" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow7" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow9" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow8" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow10" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow9" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow11" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow10" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow12" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow11" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaScriptLogRow13" inherits="BenillaScriptLogRowTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaScriptLogRow12" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<!-- The faux kit over the 13-row span; only its right-hand Slider renders. -->
<ScrollFrame name="BenillaScriptLogListScrollFrame" inherits="FauxScrollFrameTemplate">
<Size><AbsDimension x="293" y="208"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-94"/></Offset></Anchor></Anchors>
<Scripts>
<!-- The reference's kit writes `frame.offset` ONLY from here; its
`updateFunc` field does not exist, so without this the bar moves and
the list does not follow (1868). -->
<OnVerticalScroll>FauxScrollFrame_OnVerticalScroll(BENILLA_SCRIPTLOG_ROW_HEIGHT, BenillaScriptLog_Update)</OnVerticalScroll>
</Scripts>
</ScrollFrame>
<!-- The detail pane: the REAL scroll kit, because a message carries its `stack
traceback:` tail and is genuinely variable-height. GossipFrame's greeting pane is
the same wiring. -->
<ScrollFrame name="BenillaScriptLogDetailScroll">
<Size><AbsDimension x="293" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-314"/></Offset></Anchor></Anchors>
<Scripts>
<OnLoad>self:SetScrollChild(getglobal(self:GetName() .. "Child"))</OnLoad>
<OnMouseWheel>BenillaScroll_OnMouseWheel(self, arg1)</OnMouseWheel>
<OnVerticalScroll>BenillaScroll_SyncBar(self)</OnVerticalScroll>
<OnScrollRangeChanged>BenillaScroll_SyncBar(self)</OnScrollRangeChanged>
</Scripts>
<Frames>
<Slider name="$parentBar" inherits="BenillaScrollBarTemplate" hidden="true">
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="6" y="-16"/></Offset></Anchor>
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="6" y="16"/></Offset></Anchor>
</Anchors>
<Scripts><OnValueChanged>BenillaScrollBar_OnValueChanged(self, arg1)</OnValueChanged></Scripts>
</Slider>
<Frame name="$parentChild">
<Size><AbsDimension x="293" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
<Layers>
<Layer level="ARTWORK">
<!-- 270 wide so the engine wraps the traceback rather than clipping
it; the pane pans over whatever height that comes to. -->
<FontString name="BenillaScriptLogDetailText" inherits="GameFontHighlightSmall" justifyH="LEFT" justifyV="TOP" text="">
<Size><AbsDimension x="270" y="0"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="6" y="-6"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
</Frame>
</Frames>
</ScrollFrame>
<Button name="BenillaScriptLogClearButton" inherits="UIPanelButtonTemplate" text="Clear">
<Size><AbsDimension x="96" y="22"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-455"/></Offset></Anchor></Anchors>
<Scripts><OnClick>BenillaScriptLog_Clear()</OnClick></Scripts>
</Button>
<Button name="BenillaScriptLogCloseBottomButton" inherits="UIPanelButtonTemplate" text="Close">
<Size><AbsDimension x="96" y="22"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="219" y="-455"/></Offset></Anchor></Anchors>
<Scripts><OnClick>HideUIPanel(getglobal("BenillaScriptLogFrame"))</OnClick></Scripts>
</Button>
</Frames>
<Scripts>
<OnLoad>BenillaScriptLogFrame_OnLoad(self)</OnLoad>
<!-- The list wheel lives on the WINDOW: the engine bubbles a spin up the parent chain
(CraftFrame's own note). The detail pane has its own, which wins over its own rect. -->
<OnMouseWheel>BenillaScriptLogFrame_OnMouseWheel(arg1)</OnMouseWheel>
<OnShow>BenillaScriptLog_Update()</OnShow>
<OnUpdate>BenillaScriptLogFrame_OnUpdate()</OnUpdate>
</Scripts>
</Frame>
<!-- benilla's own commands over the reference's SlashCmdList (decision 1948). The stock
ChatEdit_ParseText walks SlashCmdList after its own built-ins, so each of benilla's commands
(ui_chat::commands — the dev instruments and the three additions) hands its line to
SubmitChatInput, where ui_chat::input's parser still owns it. This file loads after the
reference's ChatFrame.xml, which is what declares the table. -->
<Script><![CDATA[
local BENILLA_COMMANDS = {
{ "CHATTEST", "/chattest" },
{ "SHOT", "/shot" },
{ "LIQUID", "/liquid" },
{ "CASTVIS", "/castvis" },
{ "PARTYTEST", "/partytest" },
{ "REACTION", "/reaction", "/react" },
{ "RELOAD", "/reload" },
{ "ERRORS", "/errors", "/err" },
{ "CONVERTRAID", "/convertraid" },
}
for _, row in ipairs(BENILLA_COMMANDS) do
local key = "BENILLA_" .. row[1]
for i = 2, table.getn(row) do
setglobal("SLASH_" .. key .. (i - 1), row[i])
end
SlashCmdList[key] = function(msg)
SubmitChatInput(row[2] .. " " .. (msg or ""))
end
end
]]></Script>
</Ui>