@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/pixi.d.ts CHANGED
@@ -23,6 +23,9 @@ declare class EventEmitter<TEvents extends {}> {
23
23
  * `bonus` pairs with `setBonus()` to show a game-supplied label + value (adventure, hold-and-spin,
24
24
  * respins — anything that isn't a plain free-spins counter). */
25
25
  type ShellMode = 'base' | 'bonus' | 'freeSpins' | 'replay';
26
+ /** The three independent volume sliders shown in the Settings overlay. */
27
+ type VolumeKey = 'master' | 'music' | 'sfx';
28
+ type VolumeLevels = Record<VolumeKey, number>;
26
29
  interface CurrencyConfig {
27
30
  symbol: string;
28
31
  position: 'left' | 'right';
@@ -284,9 +287,13 @@ interface ShellConfig {
284
287
  * opening the built-in buy-bonus overlay (e.g. the game shows its own bonus UI). The button
285
288
  * is shown whenever this OR `features.buyBonus` is set. */
286
289
  onBonusBuy?: () => void;
290
+ /** Initial Settings-overlay volume slider positions (each 0..1, defaults to 1 = 100%). The shell
291
+ * keeps them stateful across opens; read/update at runtime via `shell.getVolume()` /
292
+ * `shell.setVolume()`, and listen to `settingChange` ({ key: 'master'|'music'|'sfx' }) to apply. */
293
+ volumes?: Partial<VolumeLevels>;
287
294
  }
288
295
  /** ShellConfig after the controller applies defaults (version, isSocial, replay, theme). No mount. */
289
- type ResolvedShellConfig = Required<Pick<ShellConfig, 'language' | 'currency' | 'availableBets' | 'defaultBet' | 'balance' | 'win' | 'mode' | 'features' | 'gameInfo' | 'version' | 'isSocial' | 'replay'>> & Pick<ShellConfig, 'currentBet' | 'theme' | 'onBonusBuy'>;
296
+ type ResolvedShellConfig = Required<Pick<ShellConfig, 'language' | 'currency' | 'availableBets' | 'defaultBet' | 'balance' | 'win' | 'mode' | 'features' | 'gameInfo' | 'version' | 'isSocial' | 'replay'>> & Pick<ShellConfig, 'currentBet' | 'theme' | 'onBonusBuy' | 'volumes'>;
290
297
  interface ShellState {
291
298
  mode: ShellMode;
292
299
  /** Sticky replay marker — true for a historical-round replay, regardless of the current
@@ -311,6 +318,9 @@ interface ShellState {
311
318
  /** The currently activated `feature` option (e.g. Ante), or null. Drives the
312
319
  * effective-bet readout tint and the BUY BONUS → DISABLE toggle on the bar. */
313
320
  activeFeature: BonusOption$1 | null;
321
+ /** Volume slider positions (0..1) surfaced in the Settings overlay. Stateful across opens so a
322
+ * reopened overlay reflects the last-set positions instead of resetting to 100%. */
323
+ volumes: VolumeLevels;
314
324
  }
315
325
  interface ShellEvents {
316
326
  spin: void;
@@ -433,6 +443,13 @@ interface ShellHost {
433
443
  setSound(on: boolean): void;
434
444
  /** An open Settings overlay registers an icon updater here (null clears it on close). */
435
445
  setSoundRefresh(fn: ((on: boolean) => void) | null): void;
446
+ /** Current volume slider position (0..1) for master/music/sfx. */
447
+ getVolume(key: VolumeKey): number;
448
+ /** Set a volume slider (0..1): clamps, stores, emits `settingChange`, and live-updates an open
449
+ * Settings overlay. Called by the slider control on drag AND by game code as the public API. */
450
+ setVolume(key: VolumeKey, value: number): void;
451
+ /** An open Settings overlay registers a slider updater here (null clears it on close). */
452
+ setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
436
453
  /** Logic-bearing actions invoked by renderer controls. */
437
454
  readonly actions: ShellActions;
438
455
  }
@@ -496,13 +513,14 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
496
513
  tokens: ShellTokens;
497
514
  layout: ShellLayoutMode;
498
515
  soundOn: boolean;
499
- readonly engineVersion = "0.5.0";
516
+ readonly engineVersion = "0.6.0";
500
517
  readonly actions: ShellActions;
501
518
  private renderer;
502
519
  private i18n;
503
520
  private kbd?;
504
521
  private overlay;
505
522
  private soundRefresh;
523
+ private volumeRefresh;
506
524
  private prevBalance;
507
525
  private prevWin;
508
526
  private destroyed;
@@ -527,6 +545,12 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
527
545
  closeModal(): void;
528
546
  setSound(on: boolean): void;
529
547
  setSoundRefresh(fn: ((on: boolean) => void) | null): void;
548
+ getVolume(key: VolumeKey): number;
549
+ /** Set a volume slider (0..1). Shared by the slider control (drag) and game code (public API):
550
+ * clamps, stores so a reopened Settings overlay reflects it, emits `settingChange`, and
551
+ * live-updates the slider if the overlay is currently open. */
552
+ setVolume(key: VolumeKey, value: number): void;
553
+ setVolumeRefresh(fn: ((key: VolumeKey, value: number) => void) | null): void;
530
554
  activateFeature(bonus: BonusOption$1): void;
531
555
  deactivateFeature(): void;
532
556
  private money;
@@ -566,7 +590,7 @@ interface I18n {
566
590
  declare function createI18n(opts: I18nOptions): I18n;
567
591
 
568
592
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
569
- declare const PACKAGE_VERSION = "0.5.0";
593
+ declare const PACKAGE_VERSION = "0.6.0";
570
594
 
571
595
  /** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
572
596
  * an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
@@ -706,4 +730,4 @@ declare function createPixiShell(config: PixiShellConfig): PixiGameShell;
706
730
  declare function removePixiShell(): Promise<void>;
707
731
 
708
732
  export { DEFAULT_ACCENT, PACKAGE_VERSION, PixiRenderer, SCHEMES, ShellController, createI18n, createPixiShell, createShell, normalizeLang, removePixiShell, resolveConfig, resolveTheme, socialize };
709
- export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, I18n, I18nOptions, Lang, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PixiGameShell, PixiShellConfig, PixiShellSurface, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, WinSection };
733
+ export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, I18n, I18nOptions, Lang, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PixiGameShell, PixiShellConfig, PixiShellSurface, ReplayModalOptions, ResolvedShellConfig, SafeArea, ShapeDef, Shell, ShellActions, ShellConfig, ShellEvents, ShellFeatures, ShellHost, ShellLayoutMode, ShellMode, ShellRenderer, ShellState, ShellSurface, ShellTokens, ThemeConfig, VolumeKey, VolumeLevels, WinSection };
package/dist/pixi.esm.js CHANGED
@@ -66,8 +66,17 @@ function createInitialState(config) {
66
66
  freeSpins: { current: 0, total: 0, totalWin: 0 },
67
67
  bonus: null,
68
68
  activeFeature: null,
69
+ volumes: {
70
+ master: clampVolume(config.volumes?.master),
71
+ music: clampVolume(config.volumes?.music),
72
+ sfx: clampVolume(config.volumes?.sfx),
73
+ },
69
74
  };
70
75
  }
76
+ /** Clamp a configured volume to 0..1, defaulting to full (1) when unset/invalid. */
77
+ function clampVolume(v) {
78
+ return typeof v === 'number' && Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 1;
79
+ }
71
80
  /** Step bet up/down within availableBets, clamped at the ends. */
72
81
  function stepBet(state, direction) {
73
82
  const idx = state.availableBets.indexOf(state.bet);
@@ -1438,7 +1447,7 @@ class KeyboardController {
1438
1447
 
1439
1448
  // AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
1440
1449
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
1441
- const PACKAGE_VERSION = '0.5.0';
1450
+ const PACKAGE_VERSION = '0.6.0';
1442
1451
 
1443
1452
  /** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
1444
1453
  function resolveConfig(config) {
@@ -1455,6 +1464,7 @@ function resolveConfig(config) {
1455
1464
  features: config.features,
1456
1465
  theme: config.theme,
1457
1466
  onBonusBuy: config.onBonusBuy,
1467
+ volumes: config.volumes,
1458
1468
  version: config.version ?? '1.0.0',
1459
1469
  isSocial: config.isSocial ?? false,
1460
1470
  replay: config.replay ?? config.mode === 'replay',
@@ -1476,6 +1486,7 @@ class ShellController extends EventEmitter {
1476
1486
  kbd;
1477
1487
  overlay = null;
1478
1488
  soundRefresh = null;
1489
+ volumeRefresh = null;
1479
1490
  prevBalance;
1480
1491
  prevWin;
1481
1492
  destroyed = false;
@@ -1659,6 +1670,7 @@ class ShellController extends EventEmitter {
1659
1670
  return;
1660
1671
  this.overlay = null;
1661
1672
  this.soundRefresh = null;
1673
+ this.volumeRefresh = null;
1662
1674
  this.renderer.closeOverlay();
1663
1675
  }
1664
1676
  // ── sound ──────────────────────────────────────────────────────────────────
@@ -1671,6 +1683,22 @@ class ShellController extends EventEmitter {
1671
1683
  setSoundRefresh(fn) {
1672
1684
  this.soundRefresh = fn;
1673
1685
  }
1686
+ // ── volume ─────────────────────────────────────────────────────────────────
1687
+ getVolume(key) {
1688
+ return this.state.volumes[key];
1689
+ }
1690
+ /** Set a volume slider (0..1). Shared by the slider control (drag) and game code (public API):
1691
+ * clamps, stores so a reopened Settings overlay reflects it, emits `settingChange`, and
1692
+ * live-updates the slider if the overlay is currently open. */
1693
+ setVolume(key, value) {
1694
+ const v = Math.max(0, Math.min(1, value));
1695
+ this.state.volumes[key] = v;
1696
+ this.emit('settingChange', { key, value: v });
1697
+ this.volumeRefresh?.(key, v);
1698
+ }
1699
+ setVolumeRefresh(fn) {
1700
+ this.volumeRefresh = fn;
1701
+ }
1674
1702
  // ── features ─────────────────────────────────────────────────────────────────
1675
1703
  activateFeature(bonus) {
1676
1704
  this.state.activeFeature = bonus;
@@ -3947,6 +3975,12 @@ class Slider extends Container {
3947
3975
  this.thumb.circle(cx, cy, r).fill('#ffffff');
3948
3976
  this.hitArea = new Rectangle(0, 0, this.w, h);
3949
3977
  }
3978
+ /** Programmatically move the thumb (0..1) without firing onInput — for live updates driven by
3979
+ * `host.setVolume()` while the overlay is open. */
3980
+ setValue(v) {
3981
+ this._value = Math.max(0, Math.min(1, v));
3982
+ this.draw();
3983
+ }
3950
3984
  setLayoutSize(w) {
3951
3985
  if (w != null)
3952
3986
  this.w = w;
@@ -4035,10 +4069,13 @@ function buildBody$1(host, width) {
4035
4069
  speaker.active = on;
4036
4070
  });
4037
4071
  col.add(glassRow(host, [textNode(host, host.t('Sound')), new Spacer(), speaker]));
4038
- // Volume sliders
4039
- col.add(sliderRow(host, width, 'master', host.t('Master volume')));
4040
- col.add(sliderRow(host, width, 'music', host.t('Music')));
4041
- col.add(sliderRow(host, width, 'sfx', host.t('SFX')));
4072
+ // Volume sliders — positions read from the shell's stored volumes (stateful across opens); the
4073
+ // registered refreshers let `host.setVolume()` from game code move the thumbs live.
4074
+ const updaters = {};
4075
+ col.add(sliderRow(host, width, 'master', host.t('Master volume'), updaters));
4076
+ col.add(sliderRow(host, width, 'music', host.t('Music'), updaters));
4077
+ col.add(sliderRow(host, width, 'sfx', host.t('SFX'), updaters));
4078
+ host.setVolumeRefresh?.((key, v) => updaters[key]?.(v));
4042
4079
  // Game info link
4043
4080
  const infoIcon = makeIcon('info', 22, '#ffffff');
4044
4081
  const infoLabel = textNode(host, host.t('Game info'));
@@ -4082,7 +4119,7 @@ function glassRow(host, children, opts = {}) {
4082
4119
  return row;
4083
4120
  }
4084
4121
  /** A column row: head (label + live % value) over a draggable slider. */
4085
- function sliderRow(host, bodyWidth, key, label) {
4122
+ function sliderRow(host, bodyWidth, key, label, updaters) {
4086
4123
  const row = new FlexBox({
4087
4124
  direction: 'column',
4088
4125
  align: 'stretch',
@@ -4091,14 +4128,19 @@ function sliderRow(host, bodyWidth, key, label) {
4091
4128
  background: { fill: host.tokens.plaqueGlass, radius: 16 },
4092
4129
  });
4093
4130
  const head = new FlexBox({ direction: 'row', align: 'center', justify: 'space-between' });
4094
- const valueText = makeText('100%', { size: 13, weight: '700', color: host.tokens.plaqueLabel });
4131
+ const initial = host.getVolume(key);
4132
+ const valueText = makeText(`${Math.round(initial * 100)}%`, { size: 13, weight: '700', color: host.tokens.plaqueLabel });
4095
4133
  head.add(makeText(label, { size: 14, weight: '600', color: '#ffffff' }));
4096
4134
  head.add(new Spacer(), { grow: 1 });
4097
4135
  head.add(valueText);
4098
- const slider = new Slider(host, 1, (v) => {
4136
+ const slider = new Slider(host, initial, (v) => {
4099
4137
  valueText.text = `${Math.round(v * 100)}%`;
4100
- host.emit('settingChange', { key, value: v });
4138
+ host.setVolume(key, v);
4101
4139
  });
4140
+ updaters[key] = (v) => {
4141
+ valueText.text = `${Math.round(v * 100)}%`;
4142
+ slider.setValue(v);
4143
+ };
4102
4144
  row.add(head);
4103
4145
  row.add(slider);
4104
4146
  return row;
@@ -6128,6 +6170,9 @@ class PixiRenderer {
6128
6170
  notifyResize: (w, h) => host.notifyResize(w, h),
6129
6171
  setSound: (on) => host.setSound(on),
6130
6172
  setSoundRefresh: (fn) => host.setSoundRefresh(fn),
6173
+ getVolume: (key) => host.getVolume(key),
6174
+ setVolume: (key, v) => host.setVolume(key, v),
6175
+ setVolumeRefresh: (fn) => host.setVolumeRefresh(fn),
6131
6176
  // — Pixi-specific surface —
6132
6177
  get ticker() { return self.app.ticker; },
6133
6178
  get canvas() { return self.app.canvas; },