@open-slot-ui/pixi 0.0.1

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.
@@ -0,0 +1,933 @@
1
+ import * as pixi_js from 'pixi.js';
2
+ import { Container, Ticker, Texture, Application, TextStyleOptions, Graphics } from 'pixi.js';
3
+ import { ScreenState, ReadoutControl, OpenUI, Control, BlockSpec, Theme, EventLog, SpinControl, ValueDisplay, ButtonControl, ToggleControl, TurboControl, AutoplayControl, SliderControl, PanelControl, SelectControl, StepperControl, Signal, NoticeAction, OpenUIEvents, Dispose, CurrencySpec, JurisdictionConfig, NoticeOptions, RgsErrorOptions, ControlSnapshot, HostHooks, UISpec, Transition } from '@open-slot-ui/core';
4
+
5
+ type StatusBarSide = 'top' | 'bottom';
6
+ /**
7
+ * A thin, full-width black-and-white status strip pinned to the top or bottom edge,
8
+ * holding the compliance readouts (net · RTP · session) as inline items — white
9
+ * text on a dark bar, no theme accent. Only the jurisdiction-revealed readouts take
10
+ * a slot, so it adapts from 0→3 items (and hides itself when empty). A full-screen
11
+ * overlay (OpenUIPixi manages it + insets the HUD by {@link StatusBarView.heightFor}
12
+ * so controls never sit under it).
13
+ */
14
+ declare class StatusBarView extends Container {
15
+ private readonly ui;
16
+ private readonly side;
17
+ private readonly bg;
18
+ private readonly items;
19
+ private screen;
20
+ private readonly disposers;
21
+ /** The strip's pixel height for the current screen (used for the HUD margin too). */
22
+ static heightFor(screen: ScreenState): number;
23
+ constructor(controls: ReadoutControl[], ui: OpenUI, ticker: Ticker, side: StatusBarSide);
24
+ applyLayout(screen: ScreenState): void;
25
+ private relayout;
26
+ dispose(): void;
27
+ }
28
+
29
+ /**
30
+ * Base view: a Pixi Container bound to a core Control. Owns layout placement and
31
+ * registers the introspection hook so the core can report bounds/animation state.
32
+ * Subclasses draw and wire input. Dumb on its own (Charter B3).
33
+ */
34
+ declare abstract class ControlView extends Container {
35
+ protected readonly control: Control;
36
+ protected readonly ui: OpenUI;
37
+ protected animating: boolean;
38
+ protected readonly disposers: Array<() => void>;
39
+ constructor(control: Control, ui: OpenUI);
40
+ /** Position + fit-scale from the control's layout spec against the screen. */
41
+ applyLayout(screen: ScreenState): void;
42
+ private computeRect;
43
+ dispose(): void;
44
+ }
45
+
46
+ /** Per-id view override — swap a control's renderer without forking (Charter P7). */
47
+ type ControlViewFactory = (control: Control, ui: OpenUI, ticker: Ticker) => ControlView;
48
+ interface BlockColumnOptions {
49
+ controlSkins?: Partial<Record<string, ControlViewFactory>>;
50
+ /** Unclipped layer for select dropdowns (so a scroll mask doesn't clip them). */
51
+ dropdownLayer?: Container;
52
+ }
53
+ interface BlockColumn {
54
+ /** Rows laid top→down (first row near y=0); the caller positions/scrolls it. */
55
+ content: Container;
56
+ /** Interactive child views, for disposal. */
57
+ views: ControlView[];
58
+ /** Total column height. */
59
+ height: number;
60
+ /** Inner content width (body width minus padding). */
61
+ innerW: number;
62
+ }
63
+ /**
64
+ * Render a declarative `BlockSpec[]` into a column of rows — interactive controls
65
+ * (slider/toggle/button/select/value/stepper) and static content (heading/text/
66
+ * callout/stat-grid/steps/paytable/image). All text flows through `ui.t`. This is
67
+ * the single renderer shared by the small panel body AND the scrollable menu, so
68
+ * blocks look identical wherever they appear (Charter B3/B9).
69
+ */
70
+ declare function buildBlockColumn(blocks: BlockSpec[], controls: Control[], ui: OpenUI, ticker: Ticker, bodyW: number, opts?: BlockColumnOptions): BlockColumn;
71
+
72
+ /**
73
+ * A skin owns a control's *look* and nothing else. The view drives it with the
74
+ * current state name; the skin swaps art accordingly. This is the seam where a
75
+ * game's design (Graphics, SVG, Spine, …) plugs in without touching control logic.
76
+ */
77
+ interface SpinSkin {
78
+ /** The display object the view adds into its animated `art` container. */
79
+ readonly view: Container;
80
+ /** React to a state change (swap art, recolor, etc.). */
81
+ update(state: string): void;
82
+ destroy(): void;
83
+ }
84
+ type SpinSkinFactory = (theme: Theme) => SpinSkin;
85
+
86
+ interface CellRenderer<C extends Container = Container> {
87
+ createCell(digit: number): C;
88
+ setDigit(cell: C, digit: number): void;
89
+ setFiller(cell: C, digit: number): void;
90
+ createSeparator?(char: string): Container;
91
+ destroyCell?(cell: C): void;
92
+ }
93
+ /**
94
+ * Minimal GSAP-compatible API the Counter uses for `fit` animation.
95
+ * Passing the default `gsap` import satisfies this shape — no need for type imports.
96
+ */
97
+ interface GsapLike {
98
+ to(target: object, vars: Record<string, unknown>): unknown;
99
+ killTweensOf(target: object): void;
100
+ }
101
+
102
+ interface OpenUIIcons {
103
+ settingsIdle?: Texture;
104
+ settingsActive?: Texture;
105
+ close?: Texture;
106
+ rules?: Texture;
107
+ sliderMusic?: Texture;
108
+ sliderSound?: Texture;
109
+ turboOff?: Texture;
110
+ turboOn?: Texture;
111
+ /** One texture per turbo mode (index-aligned) — for 3-mode art. Wins over off/on. */
112
+ turboModes?: Texture[];
113
+ autoIdle?: Texture;
114
+ autoActive?: Texture;
115
+ bonus?: Texture;
116
+ betPlus?: Texture;
117
+ betMinus?: Texture;
118
+ }
119
+ interface OpenUIPixiOptions {
120
+ /** Expose `window.__OPENUI__` for e2e/introspection. Default true. */
121
+ expose?: boolean;
122
+ /** Override the spin control's skin (default = the art-free Graphics placeholder). */
123
+ spinSkin?: SpinSkinFactory;
124
+ /** Real art for the menu/close/slider controls (else neutral placeholders). */
125
+ icons?: OpenUIIcons;
126
+ /**
127
+ * The composed MENU blocks (Settings → Paytable → Rules) the ☰ button opens in a
128
+ * scrollable sheet. Usually built by `mountHud` via `composeMenu(spec.menu, …)`;
129
+ * omitted → a default Settings-only menu (Music/Sound). Pass `false` to skip the
130
+ * built-in Pixi menu entirely (e.g. when supplying your own HTML/DOM menu).
131
+ */
132
+ menu?: BlockSpec[] | false;
133
+ /** Header title for the menu sheet (localizable). Default 'Menu'. */
134
+ menuTitle?: string;
135
+ /** Per-id view override — swap a control's renderer without forking (Charter P7). */
136
+ controlSkins?: Partial<Record<string, ControlViewFactory>>;
137
+ /**
138
+ * Host `gsap` — enables the value counter's auto-downscale so wide currencies
139
+ * (8-decimal BTC, big SATS counts, long codes) shrink to fit instead of spilling.
140
+ * Kept out of the lib's deps (Charter B5); the host passes its own gsap.
141
+ */
142
+ gsap?: GsapLike;
143
+ /**
144
+ * Autoplay count-picker presentation. `'drawer'` (default) is a bottom sheet;
145
+ * `'radial'` fans the count chips around the spin button.
146
+ */
147
+ autoplayPicker?: 'drawer' | 'radial';
148
+ /**
149
+ * Put the compliance readouts (net · RTP · session) in a thin status strip at the
150
+ * `'top'` or `'bottom'` edge instead of at screen corners. Each readout still only
151
+ * shows when its jurisdiction `display*` flag is set.
152
+ */
153
+ statusBar?: StatusBarSide;
154
+ /**
155
+ * How the interactive HUD appears on mount: `'shown'` (default), `'hidden'` (off
156
+ * screen + non-interactive; reveal later with `showControls()`), or `'slide-in'`
157
+ * (start hidden, then slide in from the edges).
158
+ */
159
+ intro?: 'shown' | 'hidden' | 'slide-in';
160
+ }
161
+ /**
162
+ * The controller: mounts ONE root Container onto the host's existing stage,
163
+ * shares the host ticker/renderer, drives resize, and exposes introspection.
164
+ * `unmount()` removes the layer and every listener it created (Charter P2/P12).
165
+ */
166
+ declare class OpenUIPixi {
167
+ private readonly ui;
168
+ private readonly opts;
169
+ readonly root: Container<pixi_js.ContainerChild>;
170
+ private readonly views;
171
+ /** Full-screen overlays (e.g. the autoplay drawer) that own their own layout. */
172
+ private readonly overlays;
173
+ private readonly disposers;
174
+ private _eventLog?;
175
+ /** Show/hide slide: each interactive view slides toward its anchored edge (bottom
176
+ * controls down, top up — behind the status-bar plaque). Views stay direct children
177
+ * of `root` (introspection bounds unchanged); only their y moves, and the
178
+ * ref-counted input lock makes them non-interactive while moving/hidden. */
179
+ private slideProg;
180
+ private slideTarget;
181
+ private slideHeld;
182
+ private lastScreenH;
183
+ private appTicker?;
184
+ private slideTickFn?;
185
+ private readonly slideBaseY;
186
+ private readonly slideSign;
187
+ constructor(ui: OpenUI, opts?: OpenUIPixiOptions);
188
+ /** The bus event log backing `window.__OPENUI__.events` (available after mount). */
189
+ get eventLog(): EventLog | undefined;
190
+ mount(app: Application): void;
191
+ /**
192
+ * Build the unified scrollable MENU bound to the ☰ panel. Composed `menu` blocks
193
+ * are given by `mountHud`; absent → a default Settings-only menu. Built-in ids
194
+ * ('music'/'sfx') are reused, not shadowed (P10); button blocks wire `closePanel`.
195
+ */
196
+ private buildMenu;
197
+ /**
198
+ * Slide the whole interactive HUD in (`true`) or out (`false`): bottom-anchored
199
+ * controls travel down, top-anchored travel up (behind the status-bar plaque).
200
+ * Pure translation (no scaling); controls are non-interactive while moving/hidden.
201
+ */
202
+ setControlsVisible(visible: boolean): void;
203
+ /** Apply the current slide progress: translate each interactive view toward its
204
+ * anchored edge (pure translation — no scaling). */
205
+ private applySlide;
206
+ unmount(): void;
207
+ private expose;
208
+ }
209
+
210
+ /**
211
+ * Pixi view for the spin control. Owns input forwarding + transition playback;
212
+ * delegates ALL drawing to a swappable skin (Charter B3/P8). The layout scale
213
+ * lives on `this`; transitions animate the inner `art` container, so squish/turn
214
+ * never fight the responsive fit-scale — and the skin's art animates with them.
215
+ */
216
+ interface SpinViewOptions {
217
+ /** Skin factory for the spin art. */
218
+ skin?: SpinSkinFactory;
219
+ /** How long (ms) a press must be held before hold-to-spin engages. Default 260. */
220
+ holdDelayMs?: number;
221
+ }
222
+ declare class SpinView extends ControlView {
223
+ private readonly spin;
224
+ private readonly art;
225
+ private readonly skin;
226
+ private readonly tween;
227
+ private readonly holdDelayMs;
228
+ private holdTimer;
229
+ private holding;
230
+ /** Free-spins face — a ring + remaining count + "FS"; shown when `spin.freeSpins > 0`. */
231
+ private readonly fsFace;
232
+ private readonly fsCount;
233
+ private readonly fsLabel;
234
+ constructor(spin: SpinControl, ui: OpenUI, ticker: Ticker, skinOrOpts?: SpinSkinFactory | SpinViewOptions);
235
+ /** Swap the spin face: free-spins counter when `freeSpins > 0`, else the skin. */
236
+ private updateFreeSpins;
237
+ private readonly onOver;
238
+ private readonly onOut;
239
+ private readonly onDown;
240
+ private readonly onUp;
241
+ private readonly onUpOutside;
242
+ private readonly endHoldGlobal;
243
+ private clearHoldTimer;
244
+ private endHold;
245
+ private updateInteractive;
246
+ private play;
247
+ dispose(): void;
248
+ }
249
+
250
+ /**
251
+ * View for a value display (balance / bet). Wraps pixi-text-counter's `Counter`
252
+ * (mechanical-reel digit roll) for the number with the currency symbol/code as a
253
+ * tight prefix/suffix. The odometer is sized to the VALUE (no hidden leading-zero
254
+ * reserve), so the symbol hugs the number and large balances never clamp; it grows
255
+ * when the value grows. Rebuilds on currency / locale change.
256
+ */
257
+ declare class ValueDisplayView extends ControlView {
258
+ private readonly vd;
259
+ private readonly ticker;
260
+ /** Host `gsap` — when present, the counter auto-downscales wide values to fit
261
+ * (Charter B5: no hard gsap dep; the host passes it). */
262
+ private readonly fitGsap?;
263
+ private counter;
264
+ private caption;
265
+ constructor(vd: ValueDisplay, ui: OpenUI, ticker: Ticker,
266
+ /** Host `gsap` — when present, the counter auto-downscales wide values to fit
267
+ * (Charter B5: no hard gsap dep; the host passes it). */
268
+ fitGsap?: GsapLike | undefined);
269
+ private side;
270
+ private build;
271
+ /** (Re)create the rolling counter sized to `total` columns, starting at `fromMinor`. */
272
+ private buildCounter;
273
+ private rebuild;
274
+ dispose(): void;
275
+ }
276
+
277
+ /** Built-in placeholder glyphs the button can draw without art. */
278
+ type ButtonGlyph = 'menu' | 'close' | 'speaker' | 'speaker-mute' | 'fullscreen' | 'fullscreen-exit' | 'none';
279
+ interface ButtonViewOptions {
280
+ shape?: 'circle' | 'pill';
281
+ radius?: number;
282
+ height?: number;
283
+ /** Placeholder icon drawn as simple lines (when no texture). */
284
+ glyph?: ButtonGlyph;
285
+ /** Skin with real art: a Sprite shown instead of placeholder geometry. */
286
+ iconTexture?: Texture;
287
+ /** Target sprite height in reference px. */
288
+ iconTarget?: number;
289
+ /** Monochrome look — white circle, black ring, black glyph (matches the turbo
290
+ * button's b&w art). Only affects the drawn (no-texture) circle path. */
291
+ mono?: boolean;
292
+ }
293
+ /**
294
+ * Generic button view. With `iconTexture` it renders the provided art as a Sprite
295
+ * (swappable via `setIconTexture`, e.g. ☰ ↔ ✕); otherwise a neutral token placeholder.
296
+ * Pointer input drives the control's state machine; entry transitions play on `art`.
297
+ */
298
+ declare class ButtonView extends ControlView {
299
+ private readonly btn;
300
+ private readonly art;
301
+ private readonly bg;
302
+ private sprite;
303
+ private labelText;
304
+ private readonly tween;
305
+ private readonly shape;
306
+ private readonly radius;
307
+ private readonly pillHeight;
308
+ private glyph;
309
+ private readonly iconTarget;
310
+ private readonly mono;
311
+ constructor(btn: ButtonControl, ui: OpenUI, ticker: Ticker, opts?: ButtonViewOptions);
312
+ /** Swap the displayed art (e.g. ☰ → ✕). */
313
+ setIconTexture(tex: Texture): void;
314
+ /** Swap the placeholder glyph (e.g. speaker ↔ speaker-mute, fullscreen ↔ exit). */
315
+ setGlyph(glyph: ButtonGlyph): void;
316
+ private fitSprite;
317
+ private readonly onOver;
318
+ private readonly onOut;
319
+ private readonly onDown;
320
+ private readonly onUp;
321
+ private readonly onUpOutside;
322
+ private updateHit;
323
+ private updateInteractive;
324
+ private redraw;
325
+ private play;
326
+ dispose(): void;
327
+ }
328
+
329
+ interface ToggleViewOptions {
330
+ offTexture?: Texture;
331
+ onTexture?: Texture;
332
+ target?: number;
333
+ radius?: number;
334
+ }
335
+ /**
336
+ * On/off toggle view (e.g. Turbo). Swaps the off/on art on state change; falls
337
+ * back to a token-colored circle when no textures are given. Tap → `toggle()`.
338
+ */
339
+ declare class ToggleView extends ControlView {
340
+ private readonly toggle;
341
+ private readonly art;
342
+ private readonly bg;
343
+ private sprite;
344
+ private readonly tween;
345
+ private readonly offTex;
346
+ private readonly onTex;
347
+ private readonly target;
348
+ private readonly radius;
349
+ constructor(toggle: ToggleControl, ui: OpenUI, ticker: Ticker, opts?: ToggleViewOptions);
350
+ private readonly onUp;
351
+ private texFor;
352
+ private fit;
353
+ private redraw;
354
+ private play;
355
+ dispose(): void;
356
+ }
357
+
358
+ interface TurboViewOptions {
359
+ /** One texture per mode (index-aligned). Wins over off/on when provided. */
360
+ modeTextures?: Texture[];
361
+ /** 2-mode art: off frame + engaged frame (used when modeTextures is absent). */
362
+ offTexture?: Texture;
363
+ onTexture?: Texture;
364
+ target?: number;
365
+ radius?: number;
366
+ }
367
+ /**
368
+ * Turbo switcher view. One tap cycles to the next mode (`cycle()`), wrapping back
369
+ * to off. Renders whatever art it has — per-mode textures, a 2-mode off/on pair,
370
+ * or a token-drawn fallback — and, for 3+ modes, overlays a small level indicator
371
+ * (lit pips) so "turbo" vs "super" reads at a glance even on 2-frame art.
372
+ */
373
+ declare class TurboView extends ControlView {
374
+ private readonly turbo;
375
+ private readonly art;
376
+ private readonly bg;
377
+ private readonly pips;
378
+ private sprite;
379
+ private readonly tween;
380
+ private readonly modeTex;
381
+ private readonly offTex;
382
+ private readonly onTex;
383
+ private readonly target;
384
+ private readonly radius;
385
+ constructor(turbo: TurboControl, ui: OpenUI, ticker: Ticker, opts?: TurboViewOptions);
386
+ private readonly onUp;
387
+ private texFor;
388
+ private fit;
389
+ private redraw;
390
+ private play;
391
+ dispose(): void;
392
+ }
393
+
394
+ interface AutoplayViewOptions {
395
+ idleTexture?: Texture;
396
+ activeTexture?: Texture;
397
+ target?: number;
398
+ radius?: number;
399
+ /**
400
+ * Count-picker presentation. `'drawer'` (default) defers picking to the bottom
401
+ * AutoplayDrawerView and keeps this button still; `'radial'` fans the count
402
+ * chips on an arc around the spin button (the classic in-place picker).
403
+ */
404
+ picker?: 'drawer' | 'radial';
405
+ /** Spin-button center in THIS view's local coords — the chips arc around it. */
406
+ arcCenter?: {
407
+ x: number;
408
+ y: number;
409
+ };
410
+ /** Fixed angle between chips (degrees). */
411
+ arcStepDeg?: number;
412
+ /** Arc radius (default = distance to arcCenter). */
413
+ arcRadius?: number;
414
+ /** Sweep direction along the arc (+1 over the top, -1 under). */
415
+ arcDir?: 1 | -1;
416
+ }
417
+ /**
418
+ * Autoplay button with a RADIAL count picker.
419
+ * - tap (idle): button rotates −60°, the count chips pop in one-by-one along an
420
+ * arc around the spin button (fixed angle apart);
421
+ * - pick a chip: the fan collapses and the button becomes a STOP square + count;
422
+ * - tap (picking): button rotates +60° back and the fan hides;
423
+ * - tap (active): stops autoplay.
424
+ * The host runs the loop and feeds the live count.
425
+ */
426
+ declare class AutoplayView extends ControlView {
427
+ private readonly auto;
428
+ private readonly ticker;
429
+ private readonly art;
430
+ private readonly bg;
431
+ private sprite;
432
+ private readonly countText;
433
+ private readonly chipLayer;
434
+ private readonly chips;
435
+ private readonly idleTex;
436
+ private readonly activeTex;
437
+ private readonly target;
438
+ private readonly radius;
439
+ private rot;
440
+ private rotTarget;
441
+ private phase;
442
+ private running;
443
+ private readonly useRadial;
444
+ private readonly tick;
445
+ constructor(auto: AutoplayControl, ui: OpenUI, ticker: Ticker, opts?: AutoplayViewOptions);
446
+ private fit;
447
+ private fmt;
448
+ private buildChips;
449
+ private onState;
450
+ private update;
451
+ private drawButton;
452
+ dispose(): void;
453
+ }
454
+
455
+ /**
456
+ * Autoplay picker as a BOTTOM DRAWER, in black & white. On the autoplay control's
457
+ * `'picking'` state it slides up, dims the HUD, and offers the count choices — plus,
458
+ * when configured, the responsible-gambling limits: "stop on loss" and "stop on
459
+ * single win" (multiples of bet; ∞ = no limit). Pick → `autoplay.pick(count,
460
+ * {lossLimit, singleWinLimit})`; the host enforces them via `hud.reportRound`. Tap
461
+ * the backdrop → `cancelPicker()`. A full-screen overlay (OpenUIPixi-managed).
462
+ */
463
+ declare class AutoplayDrawerView extends Container {
464
+ private readonly auto;
465
+ private readonly ui;
466
+ private readonly ticker;
467
+ private readonly backdrop;
468
+ private readonly sheet;
469
+ private readonly sheetBg;
470
+ private readonly handle;
471
+ private readonly title;
472
+ private readonly startBtn;
473
+ private readonly startBg;
474
+ private readonly startLabel;
475
+ private readonly groups;
476
+ private screen;
477
+ private prog;
478
+ private running;
479
+ private sheetH;
480
+ private kk;
481
+ private readonly disposers;
482
+ private readonly tick;
483
+ constructor(auto: AutoplayControl, ui: OpenUI, ticker: Ticker);
484
+ private addGroup;
485
+ applyLayout(screen: ScreenState): void;
486
+ /** Base scale from the screen's shorter edge (before fit-to-height). */
487
+ private baseK;
488
+ private relayout;
489
+ /** Lay the sheet out at scale `k`; returns the full slide height (sheetH + 40). */
490
+ private layoutAt;
491
+ private drawGroup;
492
+ private select;
493
+ private valueOf;
494
+ private start;
495
+ private onState;
496
+ private update;
497
+ private render2;
498
+ dispose(): void;
499
+ }
500
+
501
+ /**
502
+ * Slider view. With a `trackTexture` (the handle-stripped ScrollBar art) it renders
503
+ * the real gold/white track + draws the moving handle on top. Without one, it draws
504
+ * a clean token-styled slider: a thin track, an accent-filled portion, and a white
505
+ * knob with an accent ring. Origin = track top-left.
506
+ */
507
+ declare class SliderView extends ControlView {
508
+ private readonly slider;
509
+ private readonly handle;
510
+ private readonly hasTexture;
511
+ private dragging;
512
+ constructor(slider: SliderControl, ui: OpenUI, trackTexture?: Texture);
513
+ private redraw;
514
+ private valueFromEvent;
515
+ private readonly onDown;
516
+ private readonly onMove;
517
+ private readonly onUp;
518
+ }
519
+
520
+ interface PopoverParts {
521
+ music: SliderControl;
522
+ sfx: SliderControl;
523
+ rules: ButtonControl;
524
+ }
525
+ interface PopoverArt {
526
+ rulesTexture?: Texture;
527
+ musicTrack?: Texture;
528
+ soundTrack?: Texture;
529
+ }
530
+ /**
531
+ * The settings flyout as a VERTICAL FAN: on open, the options swoosh up and pop
532
+ * in one-by-one (bottom→top); on close they retract together. Items are
533
+ * right-aligned and rise above the (right-side) menu button. No static panel.
534
+ */
535
+ declare class PopoverView extends ControlView {
536
+ private readonly panel;
537
+ private readonly ticker;
538
+ private readonly music;
539
+ private readonly sfx;
540
+ private readonly rules;
541
+ private readonly items;
542
+ private phase;
543
+ private running;
544
+ private readonly tick;
545
+ constructor(panel: PanelControl, parts: PopoverParts, ui: OpenUI, ticker: Ticker, art?: PopoverArt);
546
+ private applyState;
547
+ private update;
548
+ dispose(): void;
549
+ }
550
+
551
+ type InfoContentBuilder = (theme: Theme, width: number) => Container;
552
+ interface PanelViewOptions {
553
+ closeTexture?: Texture;
554
+ content?: InfoContentBuilder;
555
+ }
556
+ /**
557
+ * Full-screen modal: dim backdrop + dark header (title + ✕ close) + a white,
558
+ * scrollable content card. Content is built dynamically (so it scrolls and is
559
+ * data-driven). Faithful to the provided Mobile Rules design.
560
+ */
561
+ declare class PanelView extends ControlView {
562
+ private readonly panel;
563
+ private readonly opts;
564
+ private readonly backdrop;
565
+ private readonly card;
566
+ private readonly headerBg;
567
+ private readonly title;
568
+ private readonly viewport;
569
+ private readonly maskG;
570
+ private readonly catcher;
571
+ private readonly close;
572
+ private content;
573
+ private builtWidth;
574
+ private scrollY;
575
+ private contentH;
576
+ private viewportH;
577
+ private dragging;
578
+ private lastY;
579
+ private wheelHandler;
580
+ constructor(panel: PanelControl, closeBtn: ButtonControl, ui: OpenUI, ticker: Ticker, opts?: PanelViewOptions);
581
+ applyLayout(screen: ScreenState): void;
582
+ private fallback;
583
+ private readonly onDown;
584
+ private readonly onMove;
585
+ private readonly onUp;
586
+ private clampScroll;
587
+ private applyOpen;
588
+ dispose(): void;
589
+ }
590
+
591
+ interface SelectViewOptions {
592
+ width?: number;
593
+ height?: number;
594
+ /**
595
+ * An UNCLIPPED layer (above any scroll mask) to render the open dropdown list
596
+ * into. When given, tapping opens a real list; without it, tapping cycles.
597
+ */
598
+ dropdownLayer?: Container;
599
+ }
600
+ /**
601
+ * Select view: a pill showing the caption + current value + a chevron. With a
602
+ * `dropdownLayer`, tapping opens a real dropdown LIST (rendered in that unclipped
603
+ * layer so a scroll mask never clips it); choosing a row selects it. Without one,
604
+ * it falls back to cycle-on-tap. Token-styled; the headless logic is SelectControl.
605
+ */
606
+ declare class SelectView extends ControlView {
607
+ private readonly select;
608
+ private readonly art;
609
+ private readonly bg;
610
+ private readonly chevron;
611
+ private readonly captionText;
612
+ private readonly valueText;
613
+ private readonly tween;
614
+ private readonly w;
615
+ private readonly h;
616
+ private readonly dropdownLayer;
617
+ private dropdown;
618
+ constructor(select: SelectControl, ui: OpenUI, ticker: Ticker, opts?: SelectViewOptions);
619
+ private readonly onUp;
620
+ private syncDropdown;
621
+ private showDropdown;
622
+ private hideDropdown;
623
+ private redraw;
624
+ private play;
625
+ dispose(): void;
626
+ }
627
+
628
+ /**
629
+ * Stepper view for a panel body: a `[−] value [+]` row (optionally captioned),
630
+ * driving the StepperControl. The ± buttons dim + go non-interactive at the ends
631
+ * (canDec/canInc). Token-styled, locale-reactive.
632
+ */
633
+ declare class StepperView extends ControlView {
634
+ private readonly stepper;
635
+ private readonly minus;
636
+ private readonly plus;
637
+ private readonly valueText;
638
+ private readonly captionText?;
639
+ constructor(stepper: StepperControl, ui: OpenUI, _ticker: Ticker);
640
+ private fmt;
641
+ private redraw;
642
+ }
643
+
644
+ interface PanelBodyOptions {
645
+ width?: number;
646
+ controlSkins?: Partial<Record<string, ControlViewFactory>>;
647
+ }
648
+ /**
649
+ * A small, CENTERED panel body (the settings popover / declarative panels): walks
650
+ * a `BlockSpec[]` via the shared {@link buildBlockColumn} renderer, draws a branded
651
+ * card behind it, and follows the PanelControl's open/closed state. A `modal`
652
+ * variant also gets a dismissable backdrop. Text re-renders on `ui.locale` change.
653
+ * (The big scrollable menu uses {@link MenuView}, which shares the same renderer.)
654
+ */
655
+ declare class PanelBodyView extends ControlView {
656
+ private readonly panelControl;
657
+ private readonly controls;
658
+ private readonly blocks;
659
+ private readonly ticker;
660
+ private readonly opts;
661
+ private readonly backdrop;
662
+ private readonly card;
663
+ private readonly content;
664
+ private childViews;
665
+ private readonly bodyW;
666
+ constructor(panelControl: PanelControl, controls: Control[], blocks: BlockSpec[], ui: OpenUI, ticker: Ticker, opts?: PanelBodyOptions);
667
+ private rebuild;
668
+ private drawChrome;
669
+ private applyVisibility;
670
+ dispose(): void;
671
+ }
672
+
673
+ interface MenuViewOptions {
674
+ controlSkins?: Partial<Record<string, ControlViewFactory>>;
675
+ /** Header title (localizable). Default 'Menu'. */
676
+ title?: string;
677
+ /** Max card width in px. Default 560. */
678
+ maxWidth?: number;
679
+ }
680
+ /**
681
+ * The unified MENU: one full-screen, scrollable sheet (Settings → Paytable →
682
+ * Rules) opened by the ☰ button. It renders the composed `BlockSpec[]` through the
683
+ * shared {@link buildBlockColumn} renderer inside a masked, drag/wheel-scrollable
684
+ * viewport — and, crucially, the scroll catcher sits BEHIND the content, so the
685
+ * interactive settings (volume sliders, language select) stay usable while empty
686
+ * space scrolls. Rebuilds on `ui.locale` change so a language switch re-renders it.
687
+ */
688
+ declare class MenuView extends ControlView {
689
+ private readonly panel;
690
+ private readonly controls;
691
+ private readonly blocks;
692
+ private readonly ticker;
693
+ private readonly opts;
694
+ private readonly backdrop;
695
+ private readonly card;
696
+ private readonly headerBar;
697
+ private readonly title;
698
+ private readonly closeBtn;
699
+ private readonly viewport;
700
+ private readonly maskG;
701
+ private readonly catcher;
702
+ private readonly content;
703
+ /** Unclipped layer above the scroll mask for open select dropdowns. */
704
+ private readonly dropdownLayer;
705
+ private childViews;
706
+ private readonly titleKey;
707
+ private readonly maxWidth;
708
+ private vpW;
709
+ private vpH;
710
+ private scrollY;
711
+ private contentH;
712
+ private dragging;
713
+ private lastY;
714
+ private wheelHandler;
715
+ constructor(panel: PanelControl, controls: Control[], blocks: BlockSpec[], ui: OpenUI, ticker: Ticker, opts?: MenuViewOptions);
716
+ private buildClose;
717
+ private rebuildContent;
718
+ applyLayout(screen: ScreenState): void;
719
+ /** Close any open select dropdown (so scrolling/closing doesn't strand it). */
720
+ private closeSelects;
721
+ private readonly onDown;
722
+ private readonly onMove;
723
+ private readonly onUp;
724
+ private clampScroll;
725
+ private applyOpen;
726
+ dispose(): void;
727
+ }
728
+
729
+ interface ReadoutViewOptions {
730
+ /** Compact one-line layout (`CAPTION value`) for the status bar. Default false (stacked). */
731
+ inline?: boolean;
732
+ /** Force black-and-white text (white on a dark bar), ignoring the theme accent/text hue. */
733
+ mono?: boolean;
734
+ }
735
+ /**
736
+ * A compact, non-interactive readout for the Stake Engine jurisdiction `display*`
737
+ * elements — RTP, net position, session timer. Two layouts: `stacked` (a dim
738
+ * uppercase caption over a bold value — for a screen corner) and `inline`
739
+ * (`CAPTION value` on one line, centered on the seam — for the status bar). `mono`
740
+ * forces white text for the dark b&w status bar. The `'duration'` kind advances
741
+ * itself off the shared ticker while running. Net position shows an explicit +/-.
742
+ */
743
+ declare class ReadoutView extends ControlView {
744
+ private readonly ro;
745
+ private readonly ticker;
746
+ private readonly caption?;
747
+ private readonly valueText;
748
+ private readonly tick?;
749
+ constructor(ro: ReadoutControl, ui: OpenUI, ticker: Ticker, opts?: ReadoutViewOptions);
750
+ private render;
751
+ dispose(): void;
752
+ }
753
+
754
+ interface DialogViewOptions {
755
+ controlSkins?: Partial<Record<string, ControlViewFactory>>;
756
+ /** Max card width in px. Default 520. */
757
+ maxWidth?: number;
758
+ }
759
+ /**
760
+ * A centered, auto-sized modal rendered in the SAME style as the unified menu — the
761
+ * accent-stroked card on a dimmed backdrop, the same {@link buildBlockColumn}
762
+ * renderer — so notices/errors are themed for free. Content comes from a
763
+ * `Signal<BlockSpec[]>` and the footer buttons from a `Signal<NoticeAction[]>`;
764
+ * every label runs through `ui.t`, so the host's exact text or i18n keys are used.
765
+ * The Stake Engine error/notice surface (`showError` / `showRgsError`).
766
+ */
767
+ declare class DialogView extends ControlView {
768
+ private readonly panel;
769
+ private readonly blocks;
770
+ private readonly actions;
771
+ private readonly ticker;
772
+ private readonly opts;
773
+ private readonly backdrop;
774
+ private readonly card;
775
+ private readonly closeBtn;
776
+ private readonly content;
777
+ private readonly maskG;
778
+ private readonly buttons;
779
+ private childViews;
780
+ private screen;
781
+ private readonly maxWidth;
782
+ constructor(panel: PanelControl, blocks: Signal<BlockSpec[]>, actions: Signal<NoticeAction[]>, ui: OpenUI, ticker: Ticker, opts?: DialogViewOptions);
783
+ private buildClose;
784
+ applyLayout(screen: ScreenState): void;
785
+ private relayout;
786
+ /** Build the footer buttons from `noticeActions` into a centered row (shrunk to
787
+ * fit if needed). Returns the row height (0 when there are no actions). */
788
+ private buildButtons;
789
+ private makeButton;
790
+ private drawButton;
791
+ private applyOpen;
792
+ dispose(): void;
793
+ }
794
+
795
+ interface HudOptions extends OpenUIPixiOptions {
796
+ hooks?: HostHooks;
797
+ }
798
+ /**
799
+ * The complete-HUD handle returned by `mountHud`. Wraps the headless OpenUI + the
800
+ * Pixi controller with the day-to-day game verbs, so the host rarely touches the
801
+ * lower layers. `dispose()` is the single, leak-free teardown (Charter P12).
802
+ */
803
+ interface BootedHud {
804
+ readonly ui: OpenUI;
805
+ readonly pixi: OpenUIPixi;
806
+ on<K extends keyof OpenUIEvents>(type: K, fn: (p: OpenUIEvents[K]) => void): Dispose;
807
+ setBalance(major: number): void;
808
+ setBet(major: number): void;
809
+ setCurrency(spec: CurrencySpec): void;
810
+ /** Apply a Stake Engine jurisdiction config (the compliance switchboard) at runtime. */
811
+ applyJurisdiction(jur: JurisdictionConfig): void;
812
+ /** Report a settled round (major units): updates net position + enforces autoplay limits. */
813
+ reportRound(win: number, bet: number): void;
814
+ /** Set the RTP percentage shown by the RTP readout (when `displayRTP`). */
815
+ setRtp(percent: number): void;
816
+ /** Master mute / unmute (music + sfx). */
817
+ setMuted(muted: boolean): void;
818
+ /** Show a menu-style notice modal: declarative blocks + optional action buttons. */
819
+ showNotice(blocks: BlockSpec[], actions?: NoticeAction[]): void;
820
+ /** Show a menu-style error modal — `message`/`opts.title` are literal text OR i18n
821
+ * keys, and `opts.actions` adds custom buttons (e.g. a "Reload"). */
822
+ showError(message: string, opts?: NoticeOptions): void;
823
+ /** Show the default (localizable, per-call overridable) message for an RGS status
824
+ * code (e.g. `'ERR_IPB'`, `'ERR_IS'`, `'ERR_MAINTENANCE'`). */
825
+ showRgsError(code: string, opts?: RgsErrorOptions): void;
826
+ /** Show a FATAL error: a blocking modal that LOCKS the HUD and is dismissable only
827
+ * in code (`hideNotice`) — for unrecoverable RGS states. */
828
+ showFatal(message: string, opts?: NoticeOptions): void;
829
+ /** Dismiss the notice / error modal. */
830
+ hideNotice(): void;
831
+ /** Social / sweepstakes mode: one switch swaps gambling wording (+ a coin → GC/SC). */
832
+ setSocial(on: boolean, coin?: string): void;
833
+ /** Switch the spin button to / from its free-spins face: `n > 0` shows "N FS". */
834
+ setFreeSpins(n: number): void;
835
+ /** Enter/leave replay mode (Stake `replay=true`) — locks the HUD + shows a REPLAY badge. */
836
+ setReplay(on: boolean): void;
837
+ /** Show a buy-feature confirm modal (Cancel / Confirm). Texts are literal-or-key. */
838
+ confirmBuy(opts: {
839
+ title?: string;
840
+ message?: string;
841
+ onConfirm: () => void;
842
+ confirmLabel?: string;
843
+ cancelLabel?: string;
844
+ }): void;
845
+ /** Slide the whole interactive HUD in / out — bottom controls go down, top ones up
846
+ * (behind the status-bar plaque). Non-interactive while moving. */
847
+ showControls(): void;
848
+ hideControls(): void;
849
+ setControlsVisible(visible: boolean): void;
850
+ snapshot(): ControlSnapshot[];
851
+ readonly events: EventLog | undefined;
852
+ readonly inputLocked: Signal<boolean>;
853
+ lockInput(): void;
854
+ unlockInput(): void;
855
+ unmount(): void;
856
+ dispose(): void;
857
+ }
858
+ /**
859
+ * The one-call, out-of-the-box entry: `mountHud(app)` boots the full reference
860
+ * HUD; `mountHud(app, spec, opts)` configures it from one JSON UISpec + renderer
861
+ * art/skins. Pure assembly over `createUI` + `OpenUIPixi` (Charter B9).
862
+ */
863
+ declare function mountHud(app: Application, spec?: UISpec, opts?: HudOptions): BootedHud;
864
+
865
+ interface TextCellRendererOptions {
866
+ style: TextStyleOptions;
867
+ digitWidth: number;
868
+ digitHeight: number;
869
+ /** Glyph shown on intermediate (filler) frames during a roll. '' = blank. */
870
+ fillerChar?: string;
871
+ }
872
+ /**
873
+ * A zero-asset CellRenderer for pixi-text-counter using plain Pixi `Text`.
874
+ * Mirrors the shipped BitmapFontCellRenderer's cell convention: each digit is
875
+ * centered within a digitWidth x digitHeight box.
876
+ */
877
+ declare class TextCellRenderer implements CellRenderer<Container> {
878
+ private readonly o;
879
+ constructor(o: TextCellRendererOptions);
880
+ createCell(digit: number): Container;
881
+ setDigit(cell: Container, digit: number): void;
882
+ setFiller(cell: Container, digit: number): void;
883
+ createSeparator(char: string): Container;
884
+ destroyCell(cell: Container): void;
885
+ }
886
+
887
+ /**
888
+ * The default skin: art-free, drawn from theme tokens (Charter B2 default skin).
889
+ * Deliberately generic placeholder geometry — a game swaps this for its own skin.
890
+ */
891
+ declare function drawSpin(g: Graphics, theme: Theme, state: string): void;
892
+ /** Factory wrapping the placeholder Graphics drawing as a SpinSkin. */
893
+ declare const defaultSpinSkin: SpinSkinFactory;
894
+
895
+ interface SvgSpinTextures {
896
+ /** Default look (the spin / rotating-arrows icon). */
897
+ default: Texture;
898
+ /** Autoplay look (the STOP-over-count variant). */
899
+ auto: Texture;
900
+ }
901
+ /**
902
+ * A skin that renders the provided spin-button SVGs (as textures) faithfully,
903
+ * swapping between the default and autoplay art by state. The control logic is
904
+ * untouched — this is purely the look.
905
+ */
906
+ declare function svgSpinSkin(textures: SvgSpinTextures): SpinSkin;
907
+
908
+ /**
909
+ * Minimal ticker-driven tween that interprets core `Transition` descriptors.
910
+ * Kill-and-replace: every `run()` resets the target and cancels the prior tween,
911
+ * so re-triggering a state never stacks transforms (Charter B6).
912
+ */
913
+ declare class Tweener {
914
+ private readonly ticker;
915
+ private readonly frames;
916
+ private loopResolve;
917
+ constructor(ticker: Ticker);
918
+ stop(): void;
919
+ run(target: Container, transition: Transition | undefined, _theme: Theme): Promise<void>;
920
+ private add;
921
+ private done;
922
+ private exec;
923
+ private scaleYoyo;
924
+ private fadeTo;
925
+ /** Continuous rotation; the promise resolves when `stop()` is next called. */
926
+ private turn;
927
+ private runSequence;
928
+ }
929
+
930
+ /** Treat touch devices as non-desktop so we don't fire phantom hover states. */
931
+ declare function isDesktop(): boolean;
932
+
933
+ export { AutoplayDrawerView, AutoplayView, type AutoplayViewOptions, type BlockColumn, type BlockColumnOptions, type BootedHud, type ButtonGlyph, ButtonView, type ButtonViewOptions, ControlView, type ControlViewFactory, DialogView, type DialogViewOptions, type HudOptions, type InfoContentBuilder, MenuView, type MenuViewOptions, type OpenUIIcons, OpenUIPixi, type OpenUIPixiOptions, type PanelBodyOptions, PanelBodyView, PanelView, type PanelViewOptions, type PopoverArt, type PopoverParts, PopoverView, ReadoutView, type ReadoutViewOptions, SelectView, type SelectViewOptions, SliderView, type SpinSkin, type SpinSkinFactory, SpinView, type SpinViewOptions, type StatusBarSide, StatusBarView, StepperView, type SvgSpinTextures, TextCellRenderer, type TextCellRendererOptions, ToggleView, type ToggleViewOptions, TurboView, type TurboViewOptions, Tweener, ValueDisplayView, buildBlockColumn, defaultSpinSkin, drawSpin, isDesktop, mountHud, svgSpinSkin };