wuira 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,22 @@ with the release date and highlight breaking changes explicitly.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.1.9 — 2026-10-15
11
+
12
+ ### Added
13
+
14
+ - Generic `Encoding.Encode` / `Decode` using native CBOR, compression and Base64,
15
+ with decode size checks. Import formats and schema validation stay in applications.
16
+ - Native `ScrollHost` for clipped, mouse-wheel scrolling inside mounted Cells.
17
+ - Native `TextInput` hosts for copy/paste and text entry inside mounted Cells,
18
+ including focus selection, Escape callbacks and cleanup.
19
+
20
+ ### Fixed
21
+
22
+ - `TextInput` passes the required font flags to Forever's native EditBox API.
23
+ - `Window.Open` retains its current content if a replacement fails to render or
24
+ mount. Header cleanup only stops a drag when the window is actually moving.
25
+
10
26
  ## 0.1.8 — 2026-10-14
11
27
 
12
28
  ### Added
package/lib/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import "./console-polyfill";
3
3
  export { wuira } from "./wuira";
4
4
  export type { CommandCallback } from "./platform/SlashCommands";
5
5
  export { AddonSettings } from "./platform/AddonSettings";
6
+ export { Encoding } from "./platform/Encoding";
6
7
  export { Spells } from "./Spells";
7
8
  export { BlizzardUI } from "./platform/BlizzardUI";
8
9
  export { ActionBars } from "./platform/ActionBars";
package/lib/index.lua CHANGED
@@ -9,6 +9,10 @@ do
9
9
  local ____AddonSettings = require("platform.AddonSettings")
10
10
  ____exports.AddonSettings = ____AddonSettings.AddonSettings
11
11
  end
12
+ do
13
+ local ____Encoding = require("platform.Encoding")
14
+ ____exports.Encoding = ____Encoding.Encoding
15
+ end
12
16
  do
13
17
  local ____Spells = require("Spells")
14
18
  ____exports.Spells = ____Spells.Spells
@@ -0,0 +1,6 @@
1
+ /** Native CBOR, compression and Base64 transport for serializable data. */
2
+ export declare class Encoding {
3
+ static Encode(value: unknown): string;
4
+ /** Returns unvalidated data; the size bound is also checked after decompression. */
5
+ static Decode(value: string, maxBytes?: number): unknown;
6
+ }
@@ -0,0 +1,58 @@
1
+ --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
+ -- Lua Library inline imports
3
+ local function __TS__Class(self)
4
+ local c = {prototype = {}}
5
+ c.prototype.__index = c.prototype
6
+ c.prototype.constructor = c
7
+ return c
8
+ end
9
+
10
+ local function __TS__NumberIsFinite(value)
11
+ return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
12
+ end
13
+ -- End of Lua Library inline imports
14
+ local ____exports = {}
15
+ --- Native CBOR, compression and Base64 transport for serializable data.
16
+ ____exports.Encoding = __TS__Class()
17
+ local Encoding = ____exports.Encoding
18
+ Encoding.name = "Encoding"
19
+ function Encoding.prototype.____constructor(self)
20
+ end
21
+ function Encoding.Encode(self, value)
22
+ if C_EncodingUtil == nil then
23
+ error("Encoding: native codec is unavailable")
24
+ end
25
+ local compressed = C_EncodingUtil.CompressString(C_EncodingUtil.SerializeCBOR(value))
26
+ if compressed == nil then
27
+ error("Encoding: compression failed")
28
+ end
29
+ local encoded = C_EncodingUtil.EncodeBase64(compressed)
30
+ if encoded == nil then
31
+ error("Encoding: encoding failed")
32
+ end
33
+ return encoded
34
+ end
35
+ function Encoding.Decode(self, value, maxBytes)
36
+ if maxBytes == nil then
37
+ maxBytes = 262144
38
+ end
39
+ if not __TS__NumberIsFinite(maxBytes) or maxBytes < 1 then
40
+ error("Encoding: invalid size limit")
41
+ end
42
+ if type(value) ~= "string" or issecretvalue(value) or #value > maxBytes then
43
+ error("Encoding: invalid encoded data")
44
+ end
45
+ if C_EncodingUtil == nil then
46
+ error("Encoding: native codec is unavailable")
47
+ end
48
+ local decoded = C_EncodingUtil.DecodeBase64(value)
49
+ if decoded == nil then
50
+ error("Encoding: invalid encoding")
51
+ end
52
+ local decompressed = C_EncodingUtil.DecompressString(decoded)
53
+ if decompressed == nil or #decompressed > maxBytes then
54
+ error("Encoding: invalid or oversized compressed data")
55
+ end
56
+ return C_EncodingUtil.DeserializeCBOR(decompressed)
57
+ end
58
+ return ____exports
@@ -30,14 +30,16 @@ interface wuiraNativeFrame {
30
30
  StopMovingOrSizing(): void;
31
31
  EnableKeyboard(enabled: boolean): void;
32
32
  SetPropagateKeyboardInput(propagate: boolean): void;
33
- SetScript(script: "OnClick" | "OnDragStart" | "OnDragStop" | "OnEnter" | "OnLeave", handler: ((this: void, frame: wuiraNativeFrame) => void) | undefined): void;
33
+ SetScript(script: "OnClick" | "OnDragStart" | "OnDragStop" | "OnEnter" | "OnLeave" | "OnEscapePressed", handler: ((this: void, frame: wuiraNativeFrame) => void) | undefined): void;
34
34
  SetScript(script: "OnKeyDown", handler: ((this: void, frame: wuiraNativeFrame, key: string) => void) | undefined): void;
35
+ SetScript(script: "OnMouseWheel", handler: ((this: void, frame: wuiraNativeFrame, delta: number) => void) | undefined): void;
35
36
  GetName(): string | undefined;
36
37
  IsProtected?(): boolean;
37
38
  GetParent(): wuiraNativeFrame;
38
39
  GetNumPoints(): number;
39
40
  GetPoint(index: number): wuiraNativeReturns<[wuiraAnchor, wuiraNativeFrame, wuiraAnchor, number, number]>;
40
41
  GetScale(): number;
42
+ SetScale(scale: number): void;
41
43
  GetChildren(): wuiraNativeFrame[] & { readonly __tstlMultiReturn: unknown };
42
44
  IsMouseOver(): boolean;
43
45
  SetFrameStrata(strata: "BACKGROUND" | "LOW" | "MEDIUM" | "HIGH" | "DIALOG" | "FULLSCREEN" | "FULLSCREEN_DIALOG" | "TOOLTIP"): void;
@@ -175,3 +177,12 @@ declare const GetTotemInfo: ((this: void, slot: number) => wuiraNativeReturns<[
175
177
  number | undefined, number | undefined, number | undefined,
176
178
  ]>) | undefined;
177
179
  declare const GetTotemDuration: ((this: void, slot: number) => import("../datasources/TimerSource").NativeDuration) | undefined;
180
+
181
+ declare const C_EncodingUtil: {
182
+ SerializeCBOR(this: void, value: unknown): string;
183
+ DeserializeCBOR(this: void, value: string): unknown;
184
+ CompressString(this: void, value: string): string | undefined;
185
+ DecompressString(this: void, value: string): string | undefined;
186
+ EncodeBase64(this: void, value: string): string | undefined;
187
+ DecodeBase64(this: void, value: string): string | undefined;
188
+ } | undefined;
@@ -0,0 +1,19 @@
1
+ interface NativeScrollFrame extends wuiraNativeFrame {
2
+ SetScrollChild(child: wuiraNativeFrame): void;
3
+ UpdateScrollChildRect(): void;
4
+ GetVerticalScrollRange(): number;
5
+ GetVerticalScroll(): number;
6
+ SetVerticalScroll(offset: number): void;
7
+ }
8
+ /** Native clipping and mouse-wheel scrolling for content mounted inside a Cell. */
9
+ export declare class ScrollHost {
10
+ private readonly onScroll?;
11
+ readonly Frame: NativeScrollFrame;
12
+ private disposed;
13
+ constructor(parent: wuiraNativeFrame, onScroll?: ((offset: number) => void) | undefined);
14
+ SetContent(content: wuiraNativeFrame, offset?: number): void;
15
+ ScrollBy(amount: number): void;
16
+ ScrollTo(offset: number): void;
17
+ Dispose(): void;
18
+ }
19
+ export {};
@@ -0,0 +1,67 @@
1
+ --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
+ -- Lua Library inline imports
3
+ local function __TS__Class(self)
4
+ local c = {prototype = {}}
5
+ c.prototype.__index = c.prototype
6
+ c.prototype.constructor = c
7
+ return c
8
+ end
9
+ -- End of Lua Library inline imports
10
+ local ____exports = {}
11
+ --- Native clipping and mouse-wheel scrolling for content mounted inside a Cell.
12
+ ____exports.ScrollHost = __TS__Class()
13
+ local ScrollHost = ____exports.ScrollHost
14
+ ScrollHost.name = "ScrollHost"
15
+ function ScrollHost.prototype.____constructor(self, parent, onScroll)
16
+ self.onScroll = onScroll
17
+ self.disposed = false
18
+ self.Frame = CreateFrame("ScrollFrame", nil, parent)
19
+ self.Frame:SetSize(
20
+ parent:GetWidth(),
21
+ parent:GetHeight()
22
+ )
23
+ self.Frame:SetAllPoints(parent)
24
+ self.Frame:EnableMouse(true)
25
+ self.Frame:SetScript(
26
+ "OnMouseWheel",
27
+ function(_frame, delta) return self:ScrollBy(-delta * 32) end
28
+ )
29
+ self.Frame:Show()
30
+ end
31
+ function ScrollHost.prototype.SetContent(self, content, offset)
32
+ if offset == nil then
33
+ offset = 0
34
+ end
35
+ self.Frame:SetScrollChild(content)
36
+ self.Frame:UpdateScrollChildRect()
37
+ self:ScrollTo(offset)
38
+ end
39
+ function ScrollHost.prototype.ScrollBy(self, amount)
40
+ self:ScrollTo(self.Frame:GetVerticalScroll() + amount)
41
+ end
42
+ function ScrollHost.prototype.ScrollTo(self, offset)
43
+ if self.disposed then
44
+ return
45
+ end
46
+ local value = math.max(
47
+ 0,
48
+ math.min(
49
+ self.Frame:GetVerticalScrollRange(),
50
+ offset
51
+ )
52
+ )
53
+ self.Frame:SetVerticalScroll(value)
54
+ local ____opt_0 = self.onScroll
55
+ if ____opt_0 ~= nil then
56
+ ____opt_0(self, value)
57
+ end
58
+ end
59
+ function ScrollHost.prototype.Dispose(self)
60
+ if self.disposed then
61
+ return
62
+ end
63
+ self.disposed = true
64
+ self.Frame:SetScript("OnMouseWheel", nil)
65
+ self.Frame:Hide()
66
+ end
67
+ return ____exports
@@ -0,0 +1,26 @@
1
+ interface NativeEditBox extends wuiraNativeFrame {
2
+ SetAutoFocus(value: boolean): void;
3
+ SetMultiLine(value: boolean): void;
4
+ SetFont(path: string, size: number, flags: string): boolean;
5
+ SetTextInsets(left: number, right: number, top: number, bottom: number): void;
6
+ SetMaxBytes(bytes: number): void;
7
+ SetText(text: string): void;
8
+ GetText(): string;
9
+ HighlightText(): void;
10
+ SetFocus(): void;
11
+ ClearFocus(): void;
12
+ }
13
+ /** Native text entry hosted by a mounted Cell. Dispose it in that Cell's onMount cleanup. */
14
+ export declare class TextInput {
15
+ readonly Frame: NativeEditBox;
16
+ private disposed;
17
+ constructor(parent: wuiraNativeFrame, options?: {
18
+ text?: string;
19
+ maxBytes?: number;
20
+ onEscape?: () => void;
21
+ });
22
+ get Value(): string;
23
+ Focus(selectAll?: boolean): void;
24
+ Dispose(): void;
25
+ }
26
+ export {};
@@ -0,0 +1,206 @@
1
+ --[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
2
+ -- Lua Library inline imports
3
+ local function __TS__Class(self)
4
+ local c = {prototype = {}}
5
+ c.prototype.__index = c.prototype
6
+ c.prototype.constructor = c
7
+ return c
8
+ end
9
+
10
+ local function __TS__CloneDescriptor(____bindingPattern0)
11
+ local value
12
+ local writable
13
+ local set
14
+ local get
15
+ local configurable
16
+ local enumerable
17
+ enumerable = ____bindingPattern0.enumerable
18
+ configurable = ____bindingPattern0.configurable
19
+ get = ____bindingPattern0.get
20
+ set = ____bindingPattern0.set
21
+ writable = ____bindingPattern0.writable
22
+ value = ____bindingPattern0.value
23
+ local descriptor = {enumerable = enumerable == true, configurable = configurable == true}
24
+ local hasGetterOrSetter = get ~= nil or set ~= nil
25
+ local hasValueOrWritableAttribute = writable ~= nil or value ~= nil
26
+ if hasGetterOrSetter and hasValueOrWritableAttribute then
27
+ error("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute.", 0)
28
+ end
29
+ if get or set then
30
+ descriptor.get = get
31
+ descriptor.set = set
32
+ else
33
+ descriptor.value = value
34
+ descriptor.writable = writable == true
35
+ end
36
+ return descriptor
37
+ end
38
+
39
+ local __TS__DescriptorGet
40
+ do
41
+ local getmetatable = _G.getmetatable
42
+ local ____rawget = _G.rawget
43
+ function __TS__DescriptorGet(self, metatable, key)
44
+ while metatable do
45
+ local rawResult = ____rawget(metatable, key)
46
+ if rawResult ~= nil then
47
+ return rawResult
48
+ end
49
+ local descriptors = ____rawget(metatable, "_descriptors")
50
+ if descriptors then
51
+ local descriptor = descriptors[key]
52
+ if descriptor ~= nil then
53
+ if descriptor.get then
54
+ return descriptor.get(self)
55
+ end
56
+ return descriptor.value
57
+ end
58
+ end
59
+ metatable = getmetatable(metatable)
60
+ end
61
+ end
62
+ end
63
+
64
+ local __TS__DescriptorSet
65
+ do
66
+ local getmetatable = _G.getmetatable
67
+ local ____rawget = _G.rawget
68
+ local rawset = _G.rawset
69
+ function __TS__DescriptorSet(self, metatable, key, value)
70
+ while metatable do
71
+ local descriptors = ____rawget(metatable, "_descriptors")
72
+ if descriptors then
73
+ local descriptor = descriptors[key]
74
+ if descriptor ~= nil then
75
+ if descriptor.set then
76
+ descriptor.set(self, value)
77
+ else
78
+ if descriptor.writable == false then
79
+ error(
80
+ ((("Cannot assign to read only property '" .. key) .. "' of object '") .. tostring(self)) .. "'",
81
+ 0
82
+ )
83
+ end
84
+ descriptor.value = value
85
+ end
86
+ return
87
+ end
88
+ end
89
+ metatable = getmetatable(metatable)
90
+ end
91
+ rawset(self, key, value)
92
+ end
93
+ end
94
+
95
+ local __TS__SetDescriptor
96
+ do
97
+ local getmetatable = _G.getmetatable
98
+ local function descriptorIndex(self, key)
99
+ return __TS__DescriptorGet(
100
+ self,
101
+ getmetatable(self),
102
+ key
103
+ )
104
+ end
105
+ local function descriptorNewIndex(self, key, value)
106
+ return __TS__DescriptorSet(
107
+ self,
108
+ getmetatable(self),
109
+ key,
110
+ value
111
+ )
112
+ end
113
+ function __TS__SetDescriptor(target, key, desc, isPrototype)
114
+ if isPrototype == nil then
115
+ isPrototype = false
116
+ end
117
+ local ____isPrototype_0
118
+ if isPrototype then
119
+ ____isPrototype_0 = target
120
+ else
121
+ ____isPrototype_0 = getmetatable(target)
122
+ end
123
+ local metatable = ____isPrototype_0
124
+ if not metatable then
125
+ metatable = {}
126
+ setmetatable(target, metatable)
127
+ end
128
+ if not isPrototype and not rawget(metatable, "_isOwnDescriptorMetatable") then
129
+ local instanceMetatable = {}
130
+ instanceMetatable._isOwnDescriptorMetatable = true
131
+ setmetatable(instanceMetatable, metatable)
132
+ setmetatable(target, instanceMetatable)
133
+ metatable = instanceMetatable
134
+ end
135
+ local value = rawget(target, key)
136
+ if value ~= nil then
137
+ rawset(target, key, nil)
138
+ end
139
+ if not rawget(metatable, "_descriptors") then
140
+ metatable._descriptors = {}
141
+ end
142
+ metatable._descriptors[key] = __TS__CloneDescriptor(desc)
143
+ metatable.__index = descriptorIndex
144
+ metatable.__newindex = descriptorNewIndex
145
+ end
146
+ end
147
+ -- End of Lua Library inline imports
148
+ local ____exports = {}
149
+ --- Native text entry hosted by a mounted Cell. Dispose it in that Cell's onMount cleanup.
150
+ ____exports.TextInput = __TS__Class()
151
+ local TextInput = ____exports.TextInput
152
+ TextInput.name = "TextInput"
153
+ function TextInput.prototype.____constructor(self, parent, options)
154
+ if options == nil then
155
+ options = {}
156
+ end
157
+ self.disposed = false
158
+ self.Frame = CreateFrame("EditBox", nil, parent)
159
+ self.Frame:SetAutoFocus(false)
160
+ self.Frame:SetMultiLine(false)
161
+ self.Frame:SetFont("Fonts\\FRIZQT__.TTF", 12, "")
162
+ self.Frame:SetTextInsets(8, 8, 4, 4)
163
+ self.Frame:SetMaxBytes(options.maxBytes or 262144)
164
+ self.Frame:SetAllPoints(parent)
165
+ self.Frame:SetText(options.text or "")
166
+ self.Frame:SetScript(
167
+ "OnEscapePressed",
168
+ function()
169
+ self.Frame:ClearFocus()
170
+ local ____this_1
171
+ ____this_1 = options
172
+ local ____opt_0 = ____this_1.onEscape
173
+ if ____opt_0 ~= nil then
174
+ ____opt_0(____this_1)
175
+ end
176
+ end
177
+ )
178
+ self.Frame:Show()
179
+ end
180
+ function TextInput.prototype.Focus(self, selectAll)
181
+ if selectAll == nil then
182
+ selectAll = false
183
+ end
184
+ self.Frame:SetFocus()
185
+ if selectAll then
186
+ self.Frame:HighlightText()
187
+ end
188
+ end
189
+ function TextInput.prototype.Dispose(self)
190
+ if self.disposed then
191
+ return
192
+ end
193
+ self.disposed = true
194
+ self.Frame:ClearFocus()
195
+ self.Frame:SetScript("OnEscapePressed", nil)
196
+ self.Frame:Hide()
197
+ end
198
+ __TS__SetDescriptor(
199
+ TextInput.prototype,
200
+ "Value",
201
+ {get = function(self)
202
+ return self.Frame:GetText()
203
+ end},
204
+ true
205
+ )
206
+ return ____exports
@@ -9,6 +9,7 @@ export declare class Window {
9
9
  private content?;
10
10
  private pendingClose;
11
11
  private disposed;
12
+ private moving;
12
13
  constructor(options: WindowOptions);
13
14
  get IsOpen(): boolean;
14
15
  Open(view: Cell | (() => Cell | void)): void;
@@ -16,4 +17,5 @@ export declare class Window {
16
17
  BindDragHandle(handle: wuiraNativeFrame): () => void;
17
18
  Close(): void;
18
19
  Dispose(): void;
20
+ private StopMoving;
19
21
  }
package/lib/ui/Window.lua CHANGED
@@ -159,6 +159,7 @@ Window.name = "Window"
159
159
  function Window.prototype.____constructor(self, options)
160
160
  self.pendingClose = false
161
161
  self.disposed = false
162
+ self.moving = false
162
163
  if not __TS__NumberIsFinite(options.width) or not __TS__NumberIsFinite(options.height) or options.width <= 0 or options.height <= 0 then
163
164
  error("Window requires positive finite dimensions")
164
165
  end
@@ -215,11 +216,12 @@ function Window.prototype.Open(self, view)
215
216
  error("Window: update protected content outside combat")
216
217
  end
217
218
  self.pendingClose = false
218
- if self.content ~= nil then
219
- wuira:Unmount(self.content)
219
+ local next = wuira:Mount(view, nil, self.Frame)
220
+ local previous = self.content
221
+ self.content = next
222
+ if previous ~= nil then
223
+ wuira:Unmount(previous)
220
224
  end
221
- self.content = nil
222
- self.content = wuira:Mount(view, nil, self.Frame)
223
225
  self.Frame:SetPropagateKeyboardInput(true)
224
226
  self.Frame:EnableKeyboard(true)
225
227
  self.Frame:Show()
@@ -232,15 +234,16 @@ function Window.prototype.BindDragHandle(self, handle)
232
234
  function()
233
235
  if not InCombatLockdown() then
234
236
  self.Frame:StartMoving()
237
+ self.moving = true
235
238
  end
236
239
  end
237
240
  )
238
241
  handle:SetScript(
239
242
  "OnDragStop",
240
- function() return self.Frame:StopMovingOrSizing() end
243
+ function() return self:StopMoving() end
241
244
  )
242
245
  return function()
243
- self.Frame:StopMovingOrSizing()
246
+ self:StopMoving()
244
247
  handle:RegisterForDrag()
245
248
  handle:SetScript("OnDragStart", nil)
246
249
  handle:SetScript("OnDragStop", nil)
@@ -255,7 +258,7 @@ function Window.prototype.Close(self)
255
258
  return
256
259
  end
257
260
  self.pendingClose = false
258
- self.Frame:StopMovingOrSizing()
261
+ self:StopMoving()
259
262
  self.Frame:EnableKeyboard(false)
260
263
  self.Frame:Hide()
261
264
  if self.content ~= nil then
@@ -279,6 +282,13 @@ function Window.prototype.Dispose(self)
279
282
  self.Frame:SetScript("OnKeyDown", nil)
280
283
  self.disposed = true
281
284
  end
285
+ function Window.prototype.StopMoving(self)
286
+ if not self.moving then
287
+ return
288
+ end
289
+ self.Frame:StopMovingOrSizing()
290
+ self.moving = false
291
+ end
282
292
  __TS__SetDescriptor(
283
293
  Window.prototype,
284
294
  "IsOpen",
package/lib/ui/index.d.ts CHANGED
@@ -15,6 +15,8 @@ export type { CellProps } from "./Cell";
15
15
  export { FlowLayout } from "./FlowLayout";
16
16
  export { ForEach } from "./ForEach";
17
17
  export { Window } from "./Window";
18
+ export { TextInput } from "./TextInput";
19
+ export { ScrollHost } from "./ScrollHost";
18
20
  export type { WindowOptions } from "./Window";
19
21
  export type { ForEachProps } from "./ForEach";
20
22
  export { GridLayout } from "./GridLayout";
package/lib/ui/index.lua CHANGED
@@ -40,6 +40,14 @@ do
40
40
  local ____Window = require("ui.Window")
41
41
  ____exports.Window = ____Window.Window
42
42
  end
43
+ do
44
+ local ____TextInput = require("ui.TextInput")
45
+ ____exports.TextInput = ____TextInput.TextInput
46
+ end
47
+ do
48
+ local ____ScrollHost = require("ui.ScrollHost")
49
+ ____exports.ScrollHost = ____ScrollHost.ScrollHost
50
+ end
43
51
  do
44
52
  local ____GridLayout = require("ui.GridLayout")
45
53
  ____exports.GridLayout = ____GridLayout.GridLayout
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wuira",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "author": {
5
5
  "name": "Kerim Cetinbas",
6
6
  "email": "a.cetinbas@icloud.com"