@energy8platform/shell 0.5.0 → 0.6.0

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
@@ -21,6 +21,9 @@ declare class EventEmitter<TEvents extends {}> {
21
21
  * `bonus` pairs with `setBonus()` to show a game-supplied label + value (adventure, hold-and-spin,
22
22
  * respins — anything that isn't a plain free-spins counter). */
23
23
  type ShellMode = 'base' | 'bonus' | 'freeSpins' | 'replay';
24
+ /** The three independent volume sliders shown in the Settings overlay. */
25
+ type VolumeKey = 'master' | 'music' | 'sfx';
26
+ type VolumeLevels = Record<VolumeKey, number>;
24
27
  interface CurrencyConfig {
25
28
  symbol: string;
26
29
  position: 'left' | 'right';
@@ -282,9 +285,13 @@ interface ShellConfig {
282
285
  * opening the built-in buy-bonus overlay (e.g. the game shows its own bonus UI). The button
283
286
  * is shown whenever this OR `features.buyBonus` is set. */
284
287
  onBonusBuy?: () => void;
288
+ /** Initial Settings-overlay volume slider positions (each 0..1, defaults to 1 = 100%). The shell
289
+ * keeps them stateful across opens; read/update at runtime via `shell.getVolume()` /
290
+ * `shell.setVolume()`, and listen to `settingChange` ({ key: 'master'|'music'|'sfx' }) to apply. */
291
+ volumes?: Partial<VolumeLevels>;
285
292
  }
286
293
  /** ShellConfig after the controller applies defaults (version, isSocial, replay, theme). No mount. */
287
- type ResolvedShellConfig = Required<Pick<ShellConfig, 'language' | 'currency' | 'availableBets' | 'defaultBet' | 'balance' | 'win' | 'mode' | 'features' | 'gameInfo' | 'version' | 'isSocial' | 'replay'>> & Pick<ShellConfig, 'currentBet' | 'theme' | 'onBonusBuy'>;
294
+ type ResolvedShellConfig = Required<Pick<ShellConfig, 'language' | 'currency' | 'availableBets' | 'defaultBet' | 'balance' | 'win' | 'mode' | 'features' | 'gameInfo' | 'version' | 'isSocial' | 'replay'>> & Pick<ShellConfig, 'currentBet' | 'theme' | 'onBonusBuy' | 'volumes'>;
288
295
  interface ShellState {
289
296
  mode: ShellMode;
290
297
  /** Sticky replay marker — true for a historical-round replay, regardless of the current
@@ -309,6 +316,9 @@ interface ShellState {
309
316
  /** The currently activated `feature` option (e.g. Ante), or null. Drives the
310
317
  * effective-bet readout tint and the BUY BONUS → DISABLE toggle on the bar. */
311
318
  activeFeature: BonusOption | null;
319
+ /** Volume slider positions (0..1) surfaced in the Settings overlay. Stateful across opens so a
320
+ * reopened overlay reflects the last-set positions instead of resetting to 100%. */
321
+ volumes: VolumeLevels;
312
322
  }
313
323
  interface ShellEvents {
314
324
  spin: void;
@@ -431,6 +441,13 @@ interface ShellHost {
431
441
  setSound(on: boolean): void;
432
442
  /** An open Settings overlay registers an icon updater here (null clears it on close). */
433
443
  setSoundRefresh(fn: ((on: boolean) => void) | null): void;
444
+ /** Current volume slider position (0..1) for master/music/sfx. */
445
+ getVolume(key: VolumeKey): number;
446
+ /** Set a volume slider (0..1): clamps, stores, emits `settingChange`, and live-updates an open
447
+ * Settings overlay. Called by the slider control on drag AND by game code as the public API. */
448
+ setVolume(key: VolumeKey, value: number): void;
449
+ /** An open Settings overlay registers a slider updater here (null clears it on close). */
450
+ setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
434
451
  /** Logic-bearing actions invoked by renderer controls. */
435
452
  readonly actions: ShellActions;
436
453
  }
@@ -494,13 +511,14 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
494
511
  tokens: ShellTokens;
495
512
  layout: ShellLayoutMode;
496
513
  soundOn: boolean;
497
- readonly engineVersion = "0.5.0";
514
+ readonly engineVersion = "0.6.0";
498
515
  readonly actions: ShellActions;
499
516
  private renderer;
500
517
  private i18n;
501
518
  private kbd?;
502
519
  private overlay;
503
520
  private soundRefresh;
521
+ private volumeRefresh;
504
522
  private prevBalance;
505
523
  private prevWin;
506
524
  private destroyed;
@@ -525,6 +543,12 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
525
543
  closeModal(): void;
526
544
  setSound(on: boolean): void;
527
545
  setSoundRefresh(fn: ((on: boolean) => void) | null): void;
546
+ getVolume(key: VolumeKey): number;
547
+ /** Set a volume slider (0..1). Shared by the slider control (drag) and game code (public API):
548
+ * clamps, stores so a reopened Settings overlay reflects it, emits `settingChange`, and
549
+ * live-updates the slider if the overlay is currently open. */
550
+ setVolume(key: VolumeKey, value: number): void;
551
+ setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
528
552
  activateFeature(bonus: BonusOption): void;
529
553
  deactivateFeature(): void;
530
554
  private money;
@@ -564,7 +588,7 @@ interface I18n {
564
588
  declare function createI18n(opts: I18nOptions): I18n;
565
589
 
566
590
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
567
- declare const PACKAGE_VERSION = "0.5.0";
591
+ declare const PACKAGE_VERSION = "0.6.0";
568
592
 
569
593
  /** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
570
594
  * an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
@@ -640,4 +664,4 @@ declare function createGameShell(config: HtmlShellConfig): ShellController;
640
664
  declare function removeGameShell(): Promise<void>;
641
665
 
642
666
  export { DEFAULT_ACCENT, ShellController as GameShell, HtmlRenderer, PACKAGE_VERSION, SCHEMES, ShellController, createGameShell, createI18n, createShell, normalizeLang, removeGameShell, resolveConfig, resolveTheme, socialize };
643
- export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, HtmlShellConfig, I18n, I18nOptions, Lang, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, HtmlShellConfig as ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, WinSection };
667
+ export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, HtmlShellConfig, I18n, I18nOptions, Lang, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, HtmlShellConfig as ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, VolumeKey, VolumeLevels, WinSection };
package/dist/html.esm.js CHANGED
@@ -65,8 +65,17 @@ function createInitialState(config) {
65
65
  freeSpins: { current: 0, total: 0, totalWin: 0 },
66
66
  bonus: null,
67
67
  activeFeature: null,
68
+ volumes: {
69
+ master: clampVolume(config.volumes?.master),
70
+ music: clampVolume(config.volumes?.music),
71
+ sfx: clampVolume(config.volumes?.sfx),
72
+ },
68
73
  };
69
74
  }
75
+ /** Clamp a configured volume to 0..1, defaulting to full (1) when unset/invalid. */
76
+ function clampVolume(v) {
77
+ return typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 1;
78
+ }
70
79
  /** Step bet up/down within availableBets, clamped at the ends. */
71
80
  function stepBet(state, direction) {
72
81
  const idx = state.availableBets.indexOf(state.bet);
@@ -1437,7 +1446,7 @@ class KeyboardController {
1437
1446
 
1438
1447
  // AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
1439
1448
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
1440
- const PACKAGE_VERSION = '0.5.0';
1449
+ const PACKAGE_VERSION = '0.6.0';
1441
1450
 
1442
1451
  /** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
1443
1452
  function resolveConfig(config) {
@@ -1454,6 +1463,7 @@ function resolveConfig(config) {
1454
1463
  features: config.features,
1455
1464
  theme: config.theme,
1456
1465
  onBonusBuy: config.onBonusBuy,
1466
+ volumes: config.volumes,
1457
1467
  version: config.version ?? '1.0.0',
1458
1468
  isSocial: config.isSocial ?? false,
1459
1469
  replay: config.replay ?? config.mode === 'replay',
@@ -1475,6 +1485,7 @@ class ShellController extends EventEmitter {
1475
1485
  kbd;
1476
1486
  overlay = null;
1477
1487
  soundRefresh = null;
1488
+ volumeRefresh = null;
1478
1489
  prevBalance;
1479
1490
  prevWin;
1480
1491
  destroyed = false;
@@ -1658,6 +1669,7 @@ class ShellController extends EventEmitter {
1658
1669
  return;
1659
1670
  this.overlay = null;
1660
1671
  this.soundRefresh = null;
1672
+ this.volumeRefresh = null;
1661
1673
  this.renderer.closeOverlay();
1662
1674
  }
1663
1675
  // ── sound ──────────────────────────────────────────────────────────────────
@@ -1670,6 +1682,22 @@ class ShellController extends EventEmitter {
1670
1682
  setSoundRefresh(fn) {
1671
1683
  this.soundRefresh = fn;
1672
1684
  }
1685
+ // ── volume ─────────────────────────────────────────────────────────────────
1686
+ getVolume(key) {
1687
+ return this.state.volumes[key];
1688
+ }
1689
+ /** Set a volume slider (0..1). Shared by the slider control (drag) and game code (public API):
1690
+ * clamps, stores so a reopened Settings overlay reflects it, emits `settingChange`, and
1691
+ * live-updates the slider if the overlay is currently open. */
1692
+ setVolume(key, value) {
1693
+ const v = Math.max(0, Math.min(1, value));
1694
+ this.state.volumes[key] = v;
1695
+ this.emit('settingChange', { key, value: v });
1696
+ this.volumeRefresh?.(key, v);
1697
+ }
1698
+ setVolumeRefresh(fn) {
1699
+ this.volumeRefresh = fn;
1700
+ }
1673
1701
  // ── features ─────────────────────────────────────────────────────────────────
1674
1702
  activateFeature(bonus) {
1675
1703
  this.state.activeFeature = bonus;
@@ -2827,7 +2855,10 @@ function openSettingsModal(host) {
2827
2855
  return row;
2828
2856
  })();
2829
2857
  body.appendChild(sound);
2830
- // Volume sliders — full-width column rows with a live value readout
2858
+ // Volume sliders — full-width column rows with a live value readout. Positions are read from the
2859
+ // shell's stored volumes (not hardcoded to 100%), so reopening the overlay reflects the last set
2860
+ // value, and `host.setVolume()` from game code updates them live via the registered refreshers.
2861
+ const updaters = {};
2831
2862
  const slider = (key, label) => {
2832
2863
  const row = document.createElement('div');
2833
2864
  row.className = 'ge-ov-row ge-col';
@@ -2835,7 +2866,6 @@ function openSettingsModal(host) {
2835
2866
  head.className = 'ge-row-head';
2836
2867
  const val = document.createElement('span');
2837
2868
  val.className = 'ge-val';
2838
- val.textContent = '100%';
2839
2869
  head.innerHTML = `<span>${label}</span>`;
2840
2870
  head.appendChild(val);
2841
2871
  const input = document.createElement('input');
@@ -2843,19 +2873,23 @@ function openSettingsModal(host) {
2843
2873
  input.min = '0';
2844
2874
  input.max = '1';
2845
2875
  input.step = '0.05';
2846
- input.value = '1';
2847
2876
  input.className = 'ge-slider';
2848
2877
  input.dataset.ge = `setting-${key}`;
2878
+ const paint = (v) => { input.value = String(v); val.textContent = `${Math.round(v * 100)}%`; };
2879
+ paint(host.getVolume(key));
2849
2880
  input.addEventListener('input', () => {
2850
2881
  val.textContent = `${Math.round(Number(input.value) * 100)}%`;
2851
- host.emit('settingChange', { key, value: Number(input.value) });
2882
+ host.setVolume(key, Number(input.value));
2852
2883
  });
2884
+ updaters[key] = paint;
2853
2885
  row.append(head, input);
2854
2886
  return row;
2855
2887
  };
2856
2888
  body.appendChild(slider('master', host.t('Master volume')));
2857
2889
  body.appendChild(slider('music', host.t('Music')));
2858
2890
  body.appendChild(slider('sfx', host.t('SFX')));
2891
+ // Live-update sliders when volume changes via host.setVolume (shell clears on close).
2892
+ host.setVolumeRefresh((key, v) => updaters[key]?.(v));
2859
2893
  // Game info — full-width row button that opens its own overlay
2860
2894
  const gameInfo = document.createElement('button');
2861
2895
  gameInfo.className = 'ge-ov-row';