wuira 0.1.7 → 0.1.8
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 +31 -0
- package/README.md +66 -0
- package/lib/Spells.d.ts +20 -0
- package/lib/Spells.lua +40 -0
- package/lib/datasources/KnownSpells.d.ts +13 -1
- package/lib/datasources/KnownSpells.lua +323 -49
- package/lib/datasources/index.d.ts +1 -1
- package/lib/platform/forever.d.ts +3 -1
- package/lib/ui/Cell.d.ts +2 -0
- package/lib/ui/FlowLayout.d.ts +4 -2
- package/lib/ui/ForEach.d.ts +7 -0
- package/lib/ui/ForEach.lua +30 -0
- package/lib/ui/GridLayout.d.ts +2 -2
- package/lib/ui/Window.d.ts +19 -0
- package/lib/ui/Window.lua +290 -0
- package/lib/ui/index.d.ts +5 -1
- package/lib/ui/index.lua +8 -0
- package/lib/ui/layout/calculateFlow.lua +26 -0
- package/lib/ui/mount.lua +133 -117
- package/lib/ui/primitives/jsx.d.ts +6 -3
- package/lib/ui/primitives/jsx.lua +334 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,37 @@ with the release date and highlight breaking changes explicitly.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.1.8 — 2026-10-14
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Standalone `Window` hosts with draggable headers, Escape-to-close, content
|
|
15
|
+
cleanup, and protected-content close deferral.
|
|
16
|
+
- Frameless `ForEach` JSX expansion with typed render children and nested groups.
|
|
17
|
+
Cell's single-child rule is enforced after expansion; lists are snapshots.
|
|
18
|
+
- `Cell.condition` accepts a boolean or visibility callback, combines with existing
|
|
19
|
+
`visible`, and respects protected-tree combat deferral. Secret conditions hide cells.
|
|
20
|
+
- `KnownSpells.Filter(pattern)` matches cached localized names with whole-name
|
|
21
|
+
`*`/`?` globs and ASCII case-insensitive comparisons, returning detached metadata.
|
|
22
|
+
- Per-line `FlowLayout.justify` alignment (`start`, `center`, or `end`), including
|
|
23
|
+
wrapped rows and vertical columns.
|
|
24
|
+
- Cached `KnownSpells.Entries` metadata and rank-aware `Find`, including localized
|
|
25
|
+
names and native passive flags. Metadata changes also notify observers.
|
|
26
|
+
- Named Paladin Blessing, Greater Blessing, and Aura spell identifiers.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Layout children accept mixed JSX siblings and nested arrays with ordered
|
|
31
|
+
expansion and cycle checks.
|
|
32
|
+
|
|
33
|
+
### Validation and limitations
|
|
34
|
+
|
|
35
|
+
- Lua and package tests cover loop expansion, typed render children, visibility,
|
|
36
|
+
cached name filtering, alignment, window cleanup, and protected close deferral.
|
|
37
|
+
- ForEach expands a snapshot when its view is created. Applications own event
|
|
38
|
+
subscriptions and rebuild protected views outside combat. Serialized expression
|
|
39
|
+
parsing and the visual editor are application features.
|
|
40
|
+
|
|
10
41
|
## 0.1.7 — 2026-10-14
|
|
11
42
|
|
|
12
43
|
### Added
|
package/README.md
CHANGED
|
@@ -168,6 +168,21 @@ validate required arguments inside your callback.
|
|
|
168
168
|
|
|
169
169
|
### Addon settings and editing tools
|
|
170
170
|
|
|
171
|
+
Create a standalone window with mounted JSX content. Bind a header as its drag
|
|
172
|
+
handle; Escape or `Close()` releases the content while preserving the host's
|
|
173
|
+
screen position for the next opening:
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { Window, FlowLayout, Label } from "wuira/ui";
|
|
177
|
+
|
|
178
|
+
const window = new Window({ width: 1000, height: 680 });
|
|
179
|
+
window.Open(() => (
|
|
180
|
+
<FlowLayout onMount={frame => window.BindDragHandle(frame)}>
|
|
181
|
+
<Label height={40} text="My addon" />
|
|
182
|
+
</FlowLayout>
|
|
183
|
+
));
|
|
184
|
+
```
|
|
185
|
+
|
|
171
186
|
Register a normal JSX view in Forever's native addon settings list:
|
|
172
187
|
|
|
173
188
|
```tsx
|
|
@@ -206,14 +221,65 @@ import { KnownSpells } from "wuira/datasources";
|
|
|
206
221
|
const known = new KnownSpells();
|
|
207
222
|
const spellIds = known.Value; // readonly number[], read from the cache
|
|
208
223
|
const canJudge = known.Has(Spells.Judgement);
|
|
224
|
+
const activeSpells = known.Entries.filter(spell => spell.isPassive === false);
|
|
225
|
+
const learnedRank = known.Find(Spells.Judgement); // { id, name, isPassive } or undefined
|
|
226
|
+
const seals = known.Filter("Seal*"); // Detached matching entries, including learned ranks.
|
|
209
227
|
const stop = known.onChange(ids => console.log("Learned spells:", ids.length));
|
|
210
228
|
// On disposal: stop(); known.Dispose();
|
|
211
229
|
```
|
|
212
230
|
|
|
231
|
+
`Entries` returns detached cached metadata. An unknown passive flag stays
|
|
232
|
+
`undefined`; unreadable spellbooks return no entries. Metadata changes also notify
|
|
233
|
+
`onChange` observers, even when the spell IDs have not changed.
|
|
234
|
+
|
|
235
|
+
`Filter` matches the entire localized name. `*` matches any sequence and `?` one
|
|
236
|
+
byte; matching ignores ASCII case. Filtering reads the cache without polling the
|
|
237
|
+
spellbook. Patterns must use names in the client's language.
|
|
238
|
+
|
|
213
239
|
Protected button trees must be rebuilt outside combat; applications can defer a
|
|
214
240
|
changed selection until `PLAYER_REGEN_ENABLED`. `Cell.alpha` changes appearance
|
|
215
241
|
without changing a button's protected action or its interaction state.
|
|
216
242
|
|
|
243
|
+
For wrapped action rows, `FlowLayout` supports `grow` and per-line `justify`:
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
<FlowLayout width={220} height={52} direction="horizontal" gap={4} grow justify="center">
|
|
247
|
+
{buttons /* 24-unit buttons wrap after eight; the last row is centered. */}
|
|
248
|
+
</FlowLayout>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Use `ForEach` to expand a snapshot into layout children without wrapper frames:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { UI } from "wuira";
|
|
255
|
+
import { FlowLayout, ForEach, SecureButton } from "wuira/ui";
|
|
256
|
+
|
|
257
|
+
function SealButtons() {
|
|
258
|
+
return (
|
|
259
|
+
<FlowLayout width={220} height={24} direction="horizontal" gap={4}>
|
|
260
|
+
<ForEach items={known.Filter("Seal*")}>
|
|
261
|
+
{(spell, index) => (
|
|
262
|
+
<SecureButton width={24} height={24}
|
|
263
|
+
action={{ type: "spell", spell: spell.id }} condition={spell.isPassive === false} />
|
|
264
|
+
)}
|
|
265
|
+
</ForEach>
|
|
266
|
+
</FlowLayout>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The render function receives the item and its zero-based index. Use `ForEach`
|
|
272
|
+
inside a layout for multiple siblings; Cell still accepts at most one expanded
|
|
273
|
+
child. Nested loops are flattened in order. `ForEach` creates no subscriptions:
|
|
274
|
+
recreate the view from `known.onChange` when needed, outside combat for protected
|
|
275
|
+
trees. Each filtered rank remains a separate entry.
|
|
276
|
+
|
|
277
|
+
All visual cells accept `condition={boolean}` or `condition={() => boolean}`.
|
|
278
|
+
This controls visibility and retains the layout slot. It combines with datasource
|
|
279
|
+
activity and the existing `visible` predicate. Callbacks use the shared visibility
|
|
280
|
+
update driver; protected changes wait until combat ends. Secret conditions hide
|
|
281
|
+
the cell without inspecting their value.
|
|
282
|
+
|
|
217
283
|
### Empty native action bars
|
|
218
284
|
|
|
219
285
|
This is opt-in and removes real action-slot assignments, not just their visuals.
|
package/lib/Spells.d.ts
CHANGED
|
@@ -9,6 +9,26 @@ export declare enum Spells {
|
|
|
9
9
|
LayOnHands = 633,
|
|
10
10
|
BlessingOfProtection = 1022,
|
|
11
11
|
BlessingOfFreedom = 1044,
|
|
12
|
+
BlessingOfMight = 19740,
|
|
13
|
+
BlessingOfWisdom = 19742,
|
|
14
|
+
BlessingOfKings = 20217,
|
|
15
|
+
BlessingOfLight = 19977,
|
|
16
|
+
BlessingOfSalvation = 1038,
|
|
17
|
+
BlessingOfSanctuary = 20911,
|
|
18
|
+
BlessingOfSacrifice = 6940,
|
|
19
|
+
GreaterBlessingOfMight = 25782,
|
|
20
|
+
GreaterBlessingOfWisdom = 25894,
|
|
21
|
+
GreaterBlessingOfKings = 25898,
|
|
22
|
+
GreaterBlessingOfLight = 25890,
|
|
23
|
+
GreaterBlessingOfSalvation = 25895,
|
|
24
|
+
GreaterBlessingOfSanctuary = 25899,
|
|
25
|
+
DevotionAura = 465,
|
|
26
|
+
RetributionAura = 7294,
|
|
27
|
+
ConcentrationAura = 19746,
|
|
28
|
+
ShadowResistanceAura = 19876,
|
|
29
|
+
FrostResistanceAura = 19888,
|
|
30
|
+
FireResistanceAura = 19891,
|
|
31
|
+
SanctityAura = 20218,
|
|
12
32
|
VoiceOfTruth = 1310897,
|
|
13
33
|
HammerOfJustice = 853,
|
|
14
34
|
SealOfRighteousness = 21084,
|
package/lib/Spells.lua
CHANGED
|
@@ -20,6 +20,46 @@ ____exports.Spells.BlessingOfProtection = 1022
|
|
|
20
20
|
____exports.Spells[____exports.Spells.BlessingOfProtection] = "BlessingOfProtection"
|
|
21
21
|
____exports.Spells.BlessingOfFreedom = 1044
|
|
22
22
|
____exports.Spells[____exports.Spells.BlessingOfFreedom] = "BlessingOfFreedom"
|
|
23
|
+
____exports.Spells.BlessingOfMight = 19740
|
|
24
|
+
____exports.Spells[____exports.Spells.BlessingOfMight] = "BlessingOfMight"
|
|
25
|
+
____exports.Spells.BlessingOfWisdom = 19742
|
|
26
|
+
____exports.Spells[____exports.Spells.BlessingOfWisdom] = "BlessingOfWisdom"
|
|
27
|
+
____exports.Spells.BlessingOfKings = 20217
|
|
28
|
+
____exports.Spells[____exports.Spells.BlessingOfKings] = "BlessingOfKings"
|
|
29
|
+
____exports.Spells.BlessingOfLight = 19977
|
|
30
|
+
____exports.Spells[____exports.Spells.BlessingOfLight] = "BlessingOfLight"
|
|
31
|
+
____exports.Spells.BlessingOfSalvation = 1038
|
|
32
|
+
____exports.Spells[____exports.Spells.BlessingOfSalvation] = "BlessingOfSalvation"
|
|
33
|
+
____exports.Spells.BlessingOfSanctuary = 20911
|
|
34
|
+
____exports.Spells[____exports.Spells.BlessingOfSanctuary] = "BlessingOfSanctuary"
|
|
35
|
+
____exports.Spells.BlessingOfSacrifice = 6940
|
|
36
|
+
____exports.Spells[____exports.Spells.BlessingOfSacrifice] = "BlessingOfSacrifice"
|
|
37
|
+
____exports.Spells.GreaterBlessingOfMight = 25782
|
|
38
|
+
____exports.Spells[____exports.Spells.GreaterBlessingOfMight] = "GreaterBlessingOfMight"
|
|
39
|
+
____exports.Spells.GreaterBlessingOfWisdom = 25894
|
|
40
|
+
____exports.Spells[____exports.Spells.GreaterBlessingOfWisdom] = "GreaterBlessingOfWisdom"
|
|
41
|
+
____exports.Spells.GreaterBlessingOfKings = 25898
|
|
42
|
+
____exports.Spells[____exports.Spells.GreaterBlessingOfKings] = "GreaterBlessingOfKings"
|
|
43
|
+
____exports.Spells.GreaterBlessingOfLight = 25890
|
|
44
|
+
____exports.Spells[____exports.Spells.GreaterBlessingOfLight] = "GreaterBlessingOfLight"
|
|
45
|
+
____exports.Spells.GreaterBlessingOfSalvation = 25895
|
|
46
|
+
____exports.Spells[____exports.Spells.GreaterBlessingOfSalvation] = "GreaterBlessingOfSalvation"
|
|
47
|
+
____exports.Spells.GreaterBlessingOfSanctuary = 25899
|
|
48
|
+
____exports.Spells[____exports.Spells.GreaterBlessingOfSanctuary] = "GreaterBlessingOfSanctuary"
|
|
49
|
+
____exports.Spells.DevotionAura = 465
|
|
50
|
+
____exports.Spells[____exports.Spells.DevotionAura] = "DevotionAura"
|
|
51
|
+
____exports.Spells.RetributionAura = 7294
|
|
52
|
+
____exports.Spells[____exports.Spells.RetributionAura] = "RetributionAura"
|
|
53
|
+
____exports.Spells.ConcentrationAura = 19746
|
|
54
|
+
____exports.Spells[____exports.Spells.ConcentrationAura] = "ConcentrationAura"
|
|
55
|
+
____exports.Spells.ShadowResistanceAura = 19876
|
|
56
|
+
____exports.Spells[____exports.Spells.ShadowResistanceAura] = "ShadowResistanceAura"
|
|
57
|
+
____exports.Spells.FrostResistanceAura = 19888
|
|
58
|
+
____exports.Spells[____exports.Spells.FrostResistanceAura] = "FrostResistanceAura"
|
|
59
|
+
____exports.Spells.FireResistanceAura = 19891
|
|
60
|
+
____exports.Spells[____exports.Spells.FireResistanceAura] = "FireResistanceAura"
|
|
61
|
+
____exports.Spells.SanctityAura = 20218
|
|
62
|
+
____exports.Spells[____exports.Spells.SanctityAura] = "SanctityAura"
|
|
23
63
|
____exports.Spells.VoiceOfTruth = 1310897
|
|
24
64
|
____exports.Spells[____exports.Spells.VoiceOfTruth] = "VoiceOfTruth"
|
|
25
65
|
____exports.Spells.HammerOfJustice = 853
|
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
import { DataSource } from "./DataSource";
|
|
2
2
|
export type KnownSpellsStatus = "ready" | "unavailable" | "restricted";
|
|
3
|
+
export interface KnownSpell {
|
|
4
|
+
readonly id: number;
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
/** Undefined when the client cannot determine whether this spell is passive. */
|
|
7
|
+
readonly isPassive?: boolean;
|
|
8
|
+
}
|
|
3
9
|
/** Cached learned player spellbook IDs. Native events refresh the snapshot; no polling. */
|
|
4
10
|
export declare class KnownSpells extends DataSource<readonly number[]> {
|
|
5
11
|
private ids;
|
|
6
|
-
private
|
|
12
|
+
private entries;
|
|
7
13
|
private status;
|
|
8
14
|
private readonly listeners;
|
|
9
15
|
private readonly frame;
|
|
10
16
|
private disposed;
|
|
11
17
|
constructor();
|
|
12
18
|
get Status(): KnownSpellsStatus;
|
|
19
|
+
/** Detached cached metadata, with no native reads. Unreadable books return no entries. */
|
|
20
|
+
get Entries(): readonly KnownSpell[];
|
|
21
|
+
/** Whole-name glob over cached localized names. Supports * and ?, ignoring ASCII case. */
|
|
22
|
+
Filter(pattern: string): readonly KnownSpell[];
|
|
13
23
|
protected GetValue(): readonly number[];
|
|
14
24
|
protected HasSecretFields(): boolean;
|
|
15
25
|
/** Representative enum IDs match any learned rank through its localized name. */
|
|
16
26
|
Has(spell: number | string): boolean;
|
|
27
|
+
/** Resolve a representative spell ID or localized name to a learned rank. */
|
|
28
|
+
Find(spell: number | string): KnownSpell | undefined;
|
|
17
29
|
/** No initial emission; only changed, readable snapshots reach observers. */
|
|
18
30
|
onChange(listener: (spells: readonly number[]) => void): () => void;
|
|
19
31
|
private Refresh;
|