@energy8platform/shell 0.6.4 → 0.6.6

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/dist/html.d.ts CHANGED
@@ -390,8 +390,10 @@ interface ShellRenderer {
390
390
  setLayout(layout: ShellLayoutMode): void;
391
391
  /** Apply colour tokens (CSS vars in DOM / repaint in Pixi). */
392
392
  applyTheme(tokens: ShellTokens): void;
393
- /** Count a money readout from→to on the freshly-rendered bar (DOM rAF / Pixi ticker). */
394
- animateMoney(field: 'balance' | 'win', from: number, to: number): void;
393
+ /** Count a money readout from→to on the freshly-rendered bar (DOM rAF / Pixi ticker).
394
+ * `durationMs` overrides the renderer's default count-up length a cascade/tumble scene
395
+ * reporting a win per step needs each count-up to fit inside its step. */
396
+ animateMoney(field: 'balance' | 'win', from: number, to: number, durationMs?: number): void;
395
397
  /** Build + show an overlay from a controller-supplied model; return a handle for key routing
396
398
  * and programmatic close. Returns void when nothing was shown. */
397
399
  openOverlay(req: OverlayRequest): OverlayHandle | void;
@@ -450,6 +452,11 @@ interface ShellHost {
450
452
  setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
451
453
  /** Logic-bearing actions invoked by renderer controls. */
452
454
  readonly actions: ShellActions;
455
+ /** Re-show the replay summary modal through the controller (keeps its OverlayHandle in sync).
456
+ * Used as the modal's own reopen callback after START REPLAY, so a renderer never re-pushes a
457
+ * replay modal behind the controller's back (which would strand `overlay` and dead-end the
458
+ * next close). */
459
+ openReplay(opts: ReplayModalOptions): void;
453
460
  }
454
461
  /** Every state-changing thing a control can do. Each runs logic in the controller, emits the
455
462
  * matching event, and triggers a re-render. Renderers MUST route input through these. */
@@ -511,7 +518,7 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
511
518
  tokens: ShellTokens;
512
519
  layout: ShellLayoutMode;
513
520
  soundOn: boolean;
514
- readonly engineVersion = "0.6.4";
521
+ readonly engineVersion = "0.6.6";
515
522
  readonly actions: ShellActions;
516
523
  private renderer;
517
524
  private i18n;
@@ -555,9 +562,12 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
555
562
  setBalance(n: number): void;
556
563
  /** Set the WIN readout. Counts up/down from the previous value by default. Pass
557
564
  * `{ animate: false }` to SNAP instantly (renderBar cancels any in-flight count-up) — used by the
558
- * host to clear WIN to 0 at spin start, where an animated count-DOWN would look wrong. */
565
+ * host to clear WIN to 0 at spin start, where an animated count-DOWN would look wrong.
566
+ * `{ durationMs }` shortens/lengthens the count-up (default 450ms) — a scene reporting a win per
567
+ * cascade step passes its step length so each count-up finishes before the next step lands. */
559
568
  setWin(n: number, opts?: {
560
569
  animate?: boolean;
570
+ durationMs?: number;
561
571
  }): void;
562
572
  setBet(n: number): void;
563
573
  setMode(mode: ShellMode): void;
@@ -593,7 +603,7 @@ interface I18n {
593
603
  declare function createI18n(opts: I18nOptions): I18n;
594
604
 
595
605
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
596
- declare const PACKAGE_VERSION = "0.6.4";
606
+ declare const PACKAGE_VERSION = "0.6.6";
597
607
 
598
608
  /** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
599
609
  * an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
@@ -631,7 +641,7 @@ declare class HtmlRenderer implements ShellRenderer {
631
641
  applyTheme(tokens: ShellTokens): void;
632
642
  renderBar(): void;
633
643
  setLayout(): void;
634
- animateMoney(field: 'balance' | 'win', from: number, to: number): void;
644
+ animateMoney(field: 'balance' | 'win', from: number, to: number, durationMs?: number): void;
635
645
  openOverlay(req: OverlayRequest): OverlayHandle | void;
636
646
  closeOverlay(): void;
637
647
  refreshSoundIcon?(_on: boolean): void;
@@ -640,8 +650,6 @@ declare class HtmlRenderer implements ShellRenderer {
640
650
  fitBar(): void;
641
651
  private cancelMoneyAnims;
642
652
  private buildOverlay;
643
- /** Opens a replay modal and shows it — used as the reopen callback after START REPLAY. */
644
- private openReplayInternal;
645
653
  private showModal;
646
654
  /** Uniformly scale every open centred card modal (.ge-sheet) down so it fits a short/narrow
647
655
  * popout. Covers pickers, generic + replay modals, AND the buy-bonus confirm (which is hosted
package/dist/html.esm.js CHANGED
@@ -1453,7 +1453,7 @@ class KeyboardController {
1453
1453
 
1454
1454
  // AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
1455
1455
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
1456
- const PACKAGE_VERSION = '0.6.4';
1456
+ const PACKAGE_VERSION = '0.6.6';
1457
1457
 
1458
1458
  /** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
1459
1459
  function resolveConfig(config) {
@@ -1720,10 +1720,10 @@ class ShellController extends EventEmitter {
1720
1720
  this.renderer.renderBar();
1721
1721
  }
1722
1722
  // ── game-facing public API (mirrors GameShell/PixiGameShell) ───────────────────
1723
- money(field, from, to) {
1723
+ money(field, from, to, durationMs) {
1724
1724
  this.renderer.renderBar();
1725
1725
  if (to !== from)
1726
- this.renderer.animateMoney(field, from, to);
1726
+ this.renderer.animateMoney(field, from, to, durationMs);
1727
1727
  }
1728
1728
  setBalance(n) {
1729
1729
  const from = this.prevBalance;
@@ -1733,7 +1733,9 @@ class ShellController extends EventEmitter {
1733
1733
  }
1734
1734
  /** Set the WIN readout. Counts up/down from the previous value by default. Pass
1735
1735
  * `{ animate: false }` to SNAP instantly (renderBar cancels any in-flight count-up) — used by the
1736
- * host to clear WIN to 0 at spin start, where an animated count-DOWN would look wrong. */
1736
+ * host to clear WIN to 0 at spin start, where an animated count-DOWN would look wrong.
1737
+ * `{ durationMs }` shortens/lengthens the count-up (default 450ms) — a scene reporting a win per
1738
+ * cascade step passes its step length so each count-up finishes before the next step lands. */
1737
1739
  setWin(n, opts) {
1738
1740
  const from = this.prevWin;
1739
1741
  this.state.win = n;
@@ -1742,7 +1744,7 @@ class ShellController extends EventEmitter {
1742
1744
  this.renderer.renderBar(); // instant repaint from state; cancels running money anims
1743
1745
  return;
1744
1746
  }
1745
- this.money('win', from, n);
1747
+ this.money('win', from, n, opts?.durationMs);
1746
1748
  }
1747
1749
  setBet(n) {
1748
1750
  this.state.bet = n;
@@ -3888,13 +3890,13 @@ function buildReplayModal(host, opts, reopen) {
3888
3890
  const REMOVE_FADE_MS = 300;
3889
3891
  /** Count-up the value span (.ge-rd-val) of a readout, leaving its label untouched.
3890
3892
  * Returns the count-up canceler so the renderer can stop it before the node is replaced. */
3891
- function animateReadout(el, from, to, fmt) {
3893
+ function animateReadout(el, from, to, fmt, durationMs) {
3892
3894
  const val = el.querySelector('.ge-rd-val');
3893
3895
  if (!val) {
3894
3896
  el.textContent = fmt(to);
3895
3897
  return () => { };
3896
3898
  }
3897
- return countUp(val, from, to, fmt);
3899
+ return durationMs == null ? countUp(val, from, to, fmt) : countUp(val, from, to, fmt, durationMs);
3898
3900
  }
3899
3901
  class HtmlRenderer {
3900
3902
  host;
@@ -3953,11 +3955,11 @@ class HtmlRenderer {
3953
3955
  setLayout() {
3954
3956
  this.renderBar();
3955
3957
  }
3956
- animateMoney(field, from, to) {
3958
+ animateMoney(field, from, to, durationMs) {
3957
3959
  const fmt = (n) => this.host.formatCurrency(n, field === 'win');
3958
3960
  const el = this.barHost.querySelector(`[data-ge="${field}"]`);
3959
3961
  if (el)
3960
- this.moneyAnims.push(animateReadout(el, from, to, fmt));
3962
+ this.moneyAnims.push(animateReadout(el, from, to, fmt, durationMs));
3961
3963
  }
3962
3964
  openOverlay(req) {
3963
3965
  const built = this.buildOverlay(req);
@@ -4025,7 +4027,9 @@ class HtmlRenderer {
4025
4027
  }
4026
4028
  case 'replay': {
4027
4029
  const opts = req.opts;
4028
- const reopen = () => { this.openReplayInternal(opts); };
4030
+ // Reopen through the controller (not a renderer-direct re-push) so its OverlayHandle stays
4031
+ // in sync — otherwise a reopened replay modal is untracked and the next close no-ops.
4032
+ const reopen = () => { this.host.openReplay(opts); };
4029
4033
  const root = buildReplayModal(this.host, opts, reopen);
4030
4034
  return { root };
4031
4035
  }
@@ -4035,14 +4039,6 @@ class HtmlRenderer {
4035
4039
  }
4036
4040
  }
4037
4041
  }
4038
- /** Opens a replay modal and shows it — used as the reopen callback after START REPLAY. */
4039
- openReplayInternal(opts) {
4040
- if (this.destroyed)
4041
- return;
4042
- const reopen = () => { this.openReplayInternal(opts); };
4043
- const root = buildReplayModal(this.host, opts, reopen);
4044
- this.showModal(root);
4045
- }
4046
4042
  showModal(el, onKey) {
4047
4043
  // Drop focus from any open shell control so a stray Space/Enter doesn't re-activate it
4048
4044
  const active = document.activeElement;