wuira 0.2.5 → 0.2.7

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,35 @@ with the release date and highlight breaking changes explicitly.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ## 0.2.7 — 2026-10-16
11
+
12
+ ### Added
13
+
14
+ - Generic `Nameplates.Apply` / `Replace` registration groups, with global first-match
15
+ precedence, fresh per-unit JSX factories, reversible native visual helpers,
16
+ event-driven pooled-frame lifecycle, explicit refresh, and independent disposal.
17
+ Forbidden/protected frames and secret identities are skipped; secure JSX is rejected.
18
+ - Lua integration coverage for nameplate reuse, rule conflicts, rollback, native
19
+ restoration and HP subscription cleanup. These are mock tests, not in-game validation.
20
+
21
+ ### Fixed
22
+
23
+ - Continue unmount cleanup after a binding or decoration disposer throws, so remaining
24
+ subscriptions and frames are released before the first error is reported.
25
+
26
+ ## 0.2.6 — 2026-10-16
27
+
28
+ ### Changed
29
+
30
+ - Keep wuira source conversion callback-based. Case mappings and arithmetic
31
+ formula factories now belong to LumaUI's builder layer.
32
+
33
+ ### Removed
34
+
35
+ - **Breaking:** Remove the `CaseConverter` and `MathConverter` APIs introduced in
36
+ 0.2.5, including the expression parser. Use `new SourceConverter(source,
37
+ value => ...)` in wuira code; LumaUI owns its editable case/math definitions.
38
+
10
39
  ## 0.2.5 — 2026-10-16
11
40
 
12
41
  ### Added
package/README.md CHANGED
@@ -30,7 +30,7 @@ wuira.Mount(App);
30
30
  - **Native widgets** — Cells mount to WoW frames; progress bars use the native StatusBar widget.
31
31
  - **Layout primitives** — Arrange children with `flow` and `grid`.
32
32
  - **Data sources** — HP, Mana, auras, cooldowns, casts, swings, group members, class/spec, spellbook.
33
- - **Source converters** — Compose checked value transformations, case mappings and arithmetic expressions. See [source converters](docs/source-converters.md).
33
+ - **Source converters** — Transform checked values with typed callbacks. See [source converters](docs/source-converters.md).
34
34
  - **Slash commands** — Register commands with quoting and escaping support.
35
35
  - **Windows and settings** — `Window`, `AddonSettings`, `FrameMover`, `KeybindCapture`.
36
36
  - **Secure buttons** — Spell, item, macro, action, and target actions with `SecureButton`.
@@ -76,6 +76,29 @@ Other sources include `AuraTimer`, `GlobalCooldown`, `CastTimer`, `SpellCooldown
76
76
 
77
77
  Supported action types: `spell`, `item`, `macro`, `action`, and `target`. Actions and units are static mount-time attributes. Protected trees cannot be mounted during combat.
78
78
 
79
+ ## Nameplates
80
+
81
+ ```tsx
82
+ import { Nameplates, UI } from "wuira";
83
+ import { HP } from "wuira/datasources";
84
+
85
+ const plates = Nameplates
86
+ .Apply(p => p.GUID === configuredGUID, p => {
87
+ if (p.Native.healthBar) p.Size(p.Native.healthBar, 140, 12);
88
+ })
89
+ .Replace(() => true, p => (
90
+ <progress width={140} height={12} source={new HP(p.Unit)} />
91
+ ));
92
+
93
+ // plates.Dispose();
94
+ ```
95
+
96
+ The first matching rule wins across Apply and Replace; they do not stack.
97
+ Each factory creates fresh JSX for its unit. Use `Nameplates.Create()` with
98
+ `collection.forEach(...)` to register presets, and `Refresh()` after preset changes.
99
+ See [nameplate lifecycle and cleanup](docs/nameplates.md). Secure nameplate controls
100
+ require a separate lifecycle and are not supported by this visual API.
101
+
79
102
  ## Stores
80
103
 
81
104
  ```ts
@@ -118,7 +141,7 @@ For global installation, use `npm install -g wuira`. To test unpublished changes
118
141
 
119
142
  wuira is an early framework. It is not a complete UI replacement, an in-game visual editor, or a WeakAuras replacement. JSX describes widgets; there is no React runtime or automatic reactive rendering layer. Combat restrictions and secret-value rules still apply and cannot be bypassed by the framework.
120
143
 
121
- Current core includes Cells, flow and grid layouts, progress bars, decorations, health and mana sources, a local console polyfill, and project tooling. Not yet implemented: a complete WoW API surface, click-cast abstractions, nameplate adapters, and preset management.
144
+ Current core includes Cells, layouts, progress bars, decorations, unit sources, native nameplate adapters, and project tooling. Not yet implemented: a complete WoW API surface, nameplate click-cast abstractions, and preset management. Nameplate lifecycle tests use a mock Lua runtime; real-client validation is still required.
122
145
 
123
146
  The target client is WoW Forever. Compatibility with Retail or other Classic clients is not promised without verification.
124
147
 
@@ -0,0 +1,89 @@
1
+ # Nameplates
2
+
3
+ Import `Nameplates` from `wuira` or `wuira/skins`. Wuira owns native access,
4
+ events, unit bindings and visual cleanup. Preset selection, NPC rules, editor
5
+ data and component collections belong to the consuming addon (for example LumaUI).
6
+ No class or mob presets are built into Wuira.
7
+
8
+ `NativeSkin` remains the adapter for fixed named frames. Nameplates use a separate
9
+ event-driven manager because both carriers and unit visuals are dynamically pooled.
10
+
11
+ ## Rules and ownership
12
+
13
+ `Nameplates.Apply(selector, customize)` and `Nameplates.Replace(selector, render)`
14
+ start independent groups. Chained calls append to that same group. `Nameplates.Create()`
15
+ starts an empty group, useful for collections:
16
+
17
+ ```tsx
18
+ const plates = Nameplates.Create();
19
+ collection.forEach(component => {
20
+ plates.Replace(p => component.matches(p.Unit, p.GUID), p => component.render(p));
21
+ });
22
+ // When preset data changes:
23
+ plates.Refresh();
24
+ // On addon feature shutdown:
25
+ plates.Dispose();
26
+ ```
27
+
28
+ Rules are ordered by registration time across all groups. **The first matching
29
+ rule wins, regardless of whether it is Apply or Replace.** Apply rules do not
30
+ accumulate before/after a replacement. Put specific rules before fallbacks.
31
+ Disposing a losing group leaves the winning binding intact. Disposing a winning
32
+ group restores its changes and then activates the next matching rule, if any.
33
+ Dispose is idempotent; adding rules to a disposed group is an error.
34
+
35
+ Selectors receive `Unit`, public `GUID`, engine-owned `Frame`, and native visual
36
+ `Native` (with optional `healthBar` / `castBar`). Restricted identities/frames
37
+ never reach selectors. A selector error or secret result leaves the plate native;
38
+ it does not activate a less specific fallback. Callback errors go to WoW's error
39
+ handler. A failing winner is retried on refresh or the next relevant native event.
40
+ Callbacks must not add/dispose/refresh groups while reconciliation is running.
41
+
42
+ ## Apply
43
+
44
+ Apply reuses Blizzard's visuals and bars. The context exposes reversible
45
+ `Alpha(frame, value)`, `Scale(frame, value)`, `Size(frame, width, height)`, and
46
+ `BarColor(bar, r, g, b, a?)`. These accept the native visual or its accessible,
47
+ unprotected descendants; the engine-owned carrier is excluded.
48
+
49
+ Cleanup restores a property only if it still equals the value this binding wrote,
50
+ so a later external write is preserved. Blizzard or another addon can overwrite
51
+ Apply styling; it is reapplied on relevant identity/faction/target/world events or
52
+ explicit refresh, rather than continuously polled. Direct native writes through
53
+ `Frame`/`Native` are outside rollback ownership and should be avoided.
54
+
55
+ Register each owned subscription immediately with `p.OnDispose(stop)` before
56
+ doing more work that could throw. Disposers run in reverse order, and a failing
57
+ disposer cannot prevent the remaining cleanup operations.
58
+
59
+ ## Replace
60
+
61
+ Replace mounts the factory's fresh visual JSX under the native carrier, centered
62
+ on it, and suppresses native UnitFrame alpha only after mounting succeeds. It
63
+ keeps native scripts and events registered. A dormant hook per pooled UnitFrame
64
+ maintains suppression and remembers subsequent public native alpha writes for
65
+ restoration. This does not arbitrate competing nameplate addons that also hide,
66
+ reparent or replace native visuals.
67
+
68
+ Factories run per unit binding. Do not return one cached JSX instance for several
69
+ plates: mounting rejects simultaneous reuse. Bind sources inside the factory,
70
+ for example `new HP(p.Unit)`; mounting owns their widget subscriptions. Register
71
+ additional manual subscriptions through `OnDispose`.
72
+
73
+ Nameplate add/remove events, GUID changes and reuse of either carrier or UnitFrame
74
+ dispose the previous binding before a fresh factory runs. Already visible plates
75
+ are discovered on registration and world entry. Name/faction/target events and
76
+ `Refresh()` reevaluate selection and visuals. Health events are handled by data
77
+ sources, without polling selectors. Refresh also rebuilds matching views so edited
78
+ preset values take effect. Protected/forbidden frames and secret GUIDs are skipped.
79
+
80
+ Native positioning, movement, click bounds and ordinary targeting stay with the
81
+ engine. This API does not resize the carrier or alter native click areas. Ordinary
82
+ unprotected visuals can mount/unmount during combat. Secure JSX is rejected,
83
+ including outside combat: secure nameplate buttons need a separate design for
84
+ combat-time creation, positioning and correct targeting. Fixed arena secure cells
85
+ do not establish that dynamic nameplate buttons are safe.
86
+
87
+ The Lua integration tests exercise the compiled framework with mock WoW APIs,
88
+ including actual HP source subscription cleanup and mounting failures. They do
89
+ not establish real-client behavior or compatibility with other nameplate addons.
@@ -1,34 +1,27 @@
1
1
  # Source converters
2
2
 
3
- `SourceConverter` transforms a readable source value. It consumes the input's change notifications, shares one upstream subscription across its observers, and does not create a timer or dispose the shared input. Sources without notifications remain available as checked one-time reads.
3
+ `SourceConverter` transforms a readable source with a typed callback. It consumes change notifications, shares one upstream subscription across observers, and does not create a timer or dispose the shared input. Sources without notifications remain available as checked one-time reads.
4
4
 
5
5
  ```ts
6
- import { ClassSource, SourceConverter, CaseConverter, HP, MathConverter, Unit } from "wuira/datasources";
6
+ import { SourceConverter, HP, ClassSource, Unit } from "wuira/datasources";
7
7
 
8
+ const hp = new HP(Unit.Player);
9
+ const doubled = new SourceConverter(hp, value => value * 2);
8
10
  const characterClass = new ClassSource(Unit.Player);
9
11
  const token = new SourceConverter(characterClass, info => info?.token);
10
- const color = new CaseConverter(token, [
11
- { equals: "PALADIN", value: { r: 1, g: 0.5, b: 0.7, a: 1 } },
12
- { equals: "MAGE", value: { r: 0.4, g: 0.8, b: 1, a: 1 } },
13
- ], { r: 0.35, g: 0.4, b: 0.45, a: 1 });
14
-
15
- const hp = new HP(Unit.Player);
16
- const doubled = new MathConverter(hp, "2 * input");
17
12
  const stop = doubled.BindTo(value => console.log(value));
13
+
18
14
  // On removal:
19
15
  stop();
20
16
  doubled.Dispose();
21
- color.Dispose();
22
17
  token.Dispose();
23
18
  characterClass.Dispose();
24
19
  ```
25
20
 
26
- `Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data, without invoking the callback. Converter chains use it to avoid repeated leaf reads. `onChange()` observes future readable output changes; `BindTo()` also applies the current readable value immediately. Scalar outputs with the same value do not emit again. `When()` filters emissions without changing the checked `Value` getter. Call `Dispose()` when the converter is no longer owned by a component.
27
-
28
- Cases compare strings, finite numbers and booleans with strict equality; `1` and `"1"` are different keys. Duplicate keys are rejected. Unmatched values return the supplied default, or `undefined` when none was provided. Case outputs can be colors, texture IDs, text or other data.
21
+ `Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data without invoking the callback. Converter chains use it to read their leaf once, including when the data is restricted.
29
22
 
30
- Math expressions support `input`, finite numeric literals, parentheses, unary signs, `+`, `-`, `*`, `/`, `%`, and exponentiation (`^` or `**`). Exponents associate to the right; `-input^2` means `-(input^2)`. The bounded parser runs once at construction. It does not evaluate JavaScript or Lua, access globals, or call functions. Invalid syntax, division by zero and non-finite results throw errors. `MathConverter.Validate(expression)` checks syntax without reading the input.
23
+ `onChange()` observes future readable output changes; `BindTo()` also applies the current readable value immediately. Identical scalar outputs do not emit again. `When()` filters emissions without changing the checked getter. `Dispose()` releases observers while leaving the input available to other consumers.
31
24
 
32
- Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when values are consumed; combat alone does not mark a value secret. ClassSource subscriptions follow unit identity changes and relevant native events, and release those listeners when the last consumer unsubscribes.
25
+ Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when consumed; combat alone does not mark a value secret. ClassSource subscriptions follow unit identity changes and native events, releasing listeners when the last consumer unsubscribes.
33
26
 
34
- Converters produce data. They do not grant permission to change protected buttons or their actions during combat.
27
+ The SDK accepts TypeScript callbacks. Editable case rules and math formulas are LumaUI builder factories, implemented using this generic source. Converters produce data; protected button actions remain subject to native combat restrictions.
package/lib/WuiraApp.lua CHANGED
@@ -372,22 +372,58 @@ function WuiraApp.prototype.Unmount(self, frame)
372
372
  tree.pendingUnmount = true
373
373
  return
374
374
  end
375
+ local failed = false
376
+ local failure
377
+ local function attempt(____, action)
378
+ do
379
+ local function ____catch(caught)
380
+ if not failed then
381
+ failed = true
382
+ failure = caught
383
+ end
384
+ end
385
+ local ____try, ____hasReturned = pcall(function()
386
+ action(nil)
387
+ end)
388
+ if not ____try then
389
+ ____catch(____hasReturned)
390
+ end
391
+ end
392
+ end
375
393
  for ____, dispose in ipairs(tree.bindings) do
376
- dispose(nil)
394
+ attempt(nil, dispose)
377
395
  end
378
396
  for ____, decoration in ipairs(tree.decorations) do
379
- decoration:dispose()
397
+ attempt(
398
+ nil,
399
+ function() return decoration:dispose() end
400
+ )
380
401
  end
381
402
  for ____, child in ipairs(tree.frames) do
382
- child:UnregisterAllEvents()
383
- child:SetScript("OnEvent", nil)
384
- child:SetScript("OnUpdate", nil)
385
- child:Hide()
403
+ attempt(
404
+ nil,
405
+ function() return child:UnregisterAllEvents() end
406
+ )
407
+ attempt(
408
+ nil,
409
+ function() return child:SetScript("OnEvent", nil) end
410
+ )
411
+ attempt(
412
+ nil,
413
+ function() return child:SetScript("OnUpdate", nil) end
414
+ )
415
+ attempt(
416
+ nil,
417
+ function() return child:Hide() end
418
+ )
386
419
  end
387
420
  for ____, cell in ipairs(tree.cells) do
388
421
  self.mountedFrames:delete(cell.props)
389
422
  end
390
423
  self.mountedTrees:delete(frame)
424
+ if failed then
425
+ error(failure, 0)
426
+ end
391
427
  end
392
428
  function WuiraApp.prototype.Protect(self, root, tree)
393
429
  local ____temp_8 = not tree.protected
@@ -28,12 +28,3 @@ export declare class SourceConverter<TInput, TOutput> extends DataSource<TOutput
28
28
  BindTo(apply: (value: TOutput) => void): () => void;
29
29
  Dispose(): void;
30
30
  }
31
- export type CaseKey = string | number | boolean;
32
- export interface SourceCase<T> {
33
- readonly equals: CaseKey;
34
- readonly value: T;
35
- }
36
- /** Strict equality, first matching case, then the optional default value. */
37
- export declare class CaseConverter<TInput, TOutput> extends SourceConverter<TInput, TOutput | undefined> {
38
- constructor(input: ValueSource<TInput>, cases: readonly SourceCase<TOutput>[], fallback?: TOutput);
39
- }
@@ -319,62 +319,6 @@ local function __TS__ClassExtends(target, base)
319
319
  target.prototype.__tostring = base.prototype.__tostring
320
320
  end
321
321
  end
322
-
323
- local function __TS__ObjectAssign(target, ...)
324
- local sources = {...}
325
- for i = 1, #sources do
326
- local source = sources[i]
327
- if type(source) == "table" then
328
- for key in pairs(source) do
329
- target[key] = source[key]
330
- end
331
- end
332
- end
333
- return target
334
- end
335
-
336
- local function __TS__ArrayMap(self, callbackfn, thisArg)
337
- local result = {}
338
- for i = 1, #self do
339
- result[i] = callbackfn(thisArg, self[i], i - 1, self)
340
- end
341
- return result
342
- end
343
-
344
- local function __TS__TypeOf(value)
345
- local luaType = type(value)
346
- if luaType == "table" then
347
- return "object"
348
- elseif luaType == "nil" then
349
- return "undefined"
350
- else
351
- return luaType
352
- end
353
- end
354
-
355
- local function __TS__ArrayIncludes(self, searchElement, fromIndex)
356
- if fromIndex == nil then
357
- fromIndex = 0
358
- end
359
- local len = #self
360
- local k = fromIndex
361
- if fromIndex < 0 then
362
- k = len + fromIndex
363
- end
364
- if k < 0 then
365
- k = 0
366
- end
367
- for i = k + 1, len do
368
- if self[i] == searchElement then
369
- return true
370
- end
371
- end
372
- return false
373
- end
374
-
375
- local function __TS__NumberIsFinite(value)
376
- return type(value) == "number" and value == value and value ~= math.huge and value ~= -math.huge
377
- end
378
322
  -- End of Lua Library inline imports
379
323
  local ____exports = {}
380
324
  local ____DataSource = require("datasources.DataSource")
@@ -528,41 +472,4 @@ function SourceConverter.prototype.Dispose(self)
528
472
  self.stop = nil
529
473
  self.listeners:clear()
530
474
  end
531
- --- Strict equality, first matching case, then the optional default value.
532
- ____exports.CaseConverter = __TS__Class()
533
- local CaseConverter = ____exports.CaseConverter
534
- CaseConverter.name = "CaseConverter"
535
- __TS__ClassExtends(CaseConverter, ____exports.SourceConverter)
536
- function CaseConverter.prototype.____constructor(self, input, cases, fallback)
537
- local entries = __TS__ArrayMap(
538
- cases,
539
- function(____, entry) return __TS__ObjectAssign({}, entry) end
540
- )
541
- local seen = __TS__New(Set)
542
- for ____, entry in ipairs(entries) do
543
- local key = entry.equals
544
- if issecretvalue(key) or not __TS__ArrayIncludes(
545
- {"string", "number", "boolean"},
546
- __TS__TypeOf(key)
547
- ) or type(key) == "number" and not __TS__NumberIsFinite(key) then
548
- error("CaseConverter: invalid match value")
549
- end
550
- if seen:has(key) then
551
- error("CaseConverter: duplicate match value")
552
- end
553
- seen:add(key)
554
- end
555
- CaseConverter.____super.prototype.____constructor(
556
- self,
557
- input,
558
- function(____, value)
559
- for ____, entry in ipairs(entries) do
560
- if value == entry.equals then
561
- return entry.value
562
- end
563
- end
564
- return fallback
565
- end
566
- )
567
- end
568
475
  return ____exports
@@ -1,9 +1,8 @@
1
1
  /// <reference path="../platform/forever.d.ts" />
2
2
  export { DataSource } from "./DataSource";
3
3
  export type { ReadResult } from "./DataSource";
4
- export { SourceConverter, CaseConverter } from "./SourceConverter";
5
- export type { ValueSource, CaseKey, SourceCase } from "./SourceConverter";
6
- export { MathConverter } from "./MathConverter";
4
+ export { SourceConverter } from "./SourceConverter";
5
+ export type { ValueSource } from "./SourceConverter";
7
6
  export { EditModeLayouts } from "./EditModeLayouts";
8
7
  export type { EditModeLayout, EditModeLayoutSnapshot, EditModeLayoutsStatus } from "./EditModeLayouts";
9
8
  export { ProgressSource } from "./ProgressSource";
@@ -7,11 +7,6 @@ end
7
7
  do
8
8
  local ____SourceConverter = require("datasources.SourceConverter")
9
9
  ____exports.SourceConverter = ____SourceConverter.SourceConverter
10
- ____exports.CaseConverter = ____SourceConverter.CaseConverter
11
- end
12
- do
13
- local ____MathConverter = require("datasources.MathConverter")
14
- ____exports.MathConverter = ____MathConverter.MathConverter
15
10
  end
16
11
  do
17
12
  local ____EditModeLayouts = require("datasources.EditModeLayouts")
package/lib/index.d.ts CHANGED
@@ -16,6 +16,8 @@ export { UI } from "./ui/primitives/jsx";
16
16
  export { HoverTracker } from "./ui/HoverTracker";
17
17
  export type { HoverTrackerOptions, HoverFrame } from "./ui/HoverTracker";
18
18
  export { MicroBar } from "./skins/MicroBar";
19
+ export { Nameplates, NameplateRules, NameplateContext } from "./skins/Nameplates";
20
+ export type { NameplateInfo, NameplateSelector, NameplateCustomize, NameplateRender, NativeNameplate, NativeNameplateVisual } from "./skins/Nameplates";
19
21
  export type { Element, Component } from "./ui/primitives/jsx";
20
22
  export type { Cell } from "./ui/Cell";
21
23
  export { Store, StoreRegistry } from "./store/index";
package/lib/index.lua CHANGED
@@ -38,6 +38,12 @@ do
38
38
  local ____MicroBar = require("skins.MicroBar")
39
39
  ____exports.MicroBar = ____MicroBar.MicroBar
40
40
  end
41
+ do
42
+ local ____Nameplates = require("skins.Nameplates")
43
+ ____exports.Nameplates = ____Nameplates.Nameplates
44
+ ____exports.NameplateRules = ____Nameplates.NameplateRules
45
+ ____exports.NameplateContext = ____Nameplates.NameplateContext
46
+ end
41
47
  do
42
48
  local ____index = require("store.index")
43
49
  ____exports.Store = ____index.Store
@@ -35,6 +35,8 @@ interface wuiraNativeFrame {
35
35
  SetScript(script: "OnMouseWheel", handler: ((this: void, frame: wuiraNativeFrame, delta: number) => void) | undefined): void;
36
36
  GetName(): string | undefined;
37
37
  IsProtected?(): boolean;
38
+ IsForbidden?(): boolean;
39
+ GetAlpha(): number;
38
40
  GetParent(): wuiraNativeFrame;
39
41
  GetNumPoints(): number;
40
42
  GetPoint(index: number): wuiraNativeReturns<[wuiraAnchor, wuiraNativeFrame, wuiraAnchor, number, number]>;
@@ -95,6 +97,7 @@ declare const UIParent: wuiraNativeFrame;
95
97
  declare const _G: Record<string, unknown>;
96
98
  declare const SlashCmdList: Record<string, (this: void, message: string) => void>;
97
99
  interface wuiraNativeStatusBar extends wuiraNativeFrame {
100
+ GetStatusBarColor(): wuiraNativeReturns<[number, number, number, number]>;
98
101
  GetStatusBarTexture(): wuiraNativeTexture;
99
102
  SetMinMaxValues(min: number, max: number): void;
100
103
  SetValue(value: number): void;
@@ -0,0 +1,56 @@
1
+ import type { Cell } from "../ui/Cell";
2
+ export interface NativeNameplate extends wuiraNativeFrame {
3
+ readonly UnitFrame?: NativeNameplateVisual;
4
+ readonly namePlateUnitToken?: string;
5
+ GetUnit?(): string | undefined;
6
+ }
7
+ export interface NativeNameplateVisual extends wuiraNativeFrame {
8
+ readonly healthBar?: wuiraNativeStatusBar;
9
+ readonly castBar?: wuiraNativeStatusBar;
10
+ }
11
+ export interface NameplateInfo {
12
+ readonly Unit: string;
13
+ readonly GUID: string;
14
+ /** Engine-owned carrier. Do not mutate it or attach secure controls through this API. */
15
+ readonly Frame: NativeNameplate;
16
+ readonly Native: NativeNameplateVisual;
17
+ }
18
+ export type NameplateSelector = (this: void, plate: NameplateInfo) => boolean;
19
+ export type NameplateCustomize = (this: void, plate: NameplateContext) => void;
20
+ export type NameplateRender = (this: void, plate: NameplateContext) => Cell;
21
+ /** One unit binding. Use the reversible methods; direct native writes are not tracked. */
22
+ export declare class NameplateContext implements NameplateInfo {
23
+ readonly Unit: string;
24
+ readonly GUID: string;
25
+ readonly Frame: NativeNameplate;
26
+ readonly Native: NativeNameplateVisual;
27
+ private active;
28
+ private readonly cleanup;
29
+ constructor(Unit: string, GUID: string, Frame: NativeNameplate, Native: NativeNameplateVisual);
30
+ /** Register owned subscriptions/sources immediately, before subsequent work can throw. */
31
+ OnDispose(cleanup: () => void): void;
32
+ private check;
33
+ Alpha(frame: wuiraNativeFrame, value: number): void;
34
+ Scale(frame: wuiraNativeFrame, value: number): void;
35
+ Size(frame: wuiraNativeFrame, width: number, height: number): void;
36
+ BarColor(bar: wuiraNativeStatusBar, r: number, g: number, b: number, a?: number): void;
37
+ /** Internal lifecycle endpoint; cleanup failures cannot prevent other restorations. */
38
+ Dispose(): void;
39
+ }
40
+ /** An independently disposable ordered registration group. First matching rule wins globally. */
41
+ export declare class NameplateRules {
42
+ private disposed;
43
+ private add;
44
+ Apply(selector: NameplateSelector, customize: NameplateCustomize): this;
45
+ Replace(selector: NameplateSelector, render: NameplateRender): this;
46
+ /** Re-evaluate after external preset/selector data changes; native events handle unit changes. */
47
+ Refresh(): void;
48
+ Dispose(): void;
49
+ }
50
+ /** Each entry call creates a new group; chained calls append to that group. */
51
+ export declare class Nameplates {
52
+ private constructor();
53
+ static Apply(selector: NameplateSelector, customize: NameplateCustomize): NameplateRules;
54
+ static Replace(selector: NameplateSelector, render: NameplateRender): NameplateRules;
55
+ static Create(): NameplateRules;
56
+ }