@energy8platform/shell 0.8.0 → 0.10.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.cjs.js +91 -11
- package/dist/html.cjs.js.map +1 -1
- package/dist/html.d.ts +24 -5
- package/dist/html.esm.js +91 -12
- package/dist/html.esm.js.map +1 -1
- package/dist/index.cjs.js +56 -3
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +24 -5
- package/dist/index.esm.js +56 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/pixi.cjs.js +136 -30
- package/dist/pixi.cjs.js.map +1 -1
- package/dist/pixi.d.ts +24 -5
- package/dist/pixi.esm.js +136 -31
- package/dist/pixi.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ShellController.ts +10 -2
- package/src/core/device.ts +37 -0
- package/src/core/index.ts +1 -0
- package/src/core/locales.ts +14 -0
- package/src/core/types.ts +7 -2
- package/src/core/version.ts +1 -1
- package/src/ui/html/components/BottomBar.ts +20 -5
- package/src/ui/html/components/GameInfo.ts +8 -3
- package/src/ui/html/shell.css.ts +5 -0
- package/src/ui/pixi/components/BottomBar.ts +22 -3
- package/src/ui/pixi/components/GameInfo.ts +8 -3
- package/src/ui/pixi/primitives/widgets.ts +65 -19
package/dist/html.d.ts
CHANGED
|
@@ -303,8 +303,13 @@ interface AutoplayConfig {
|
|
|
303
303
|
}
|
|
304
304
|
interface ShellFeatures {
|
|
305
305
|
turbo: 0 | 1 | 2 | 3;
|
|
306
|
-
/** Master keyboard-shortcut switch
|
|
307
|
-
*
|
|
306
|
+
/** Master keyboard-shortcut switch: `false` disables ALL hotkeys (overrides `spacebar` and any
|
|
307
|
+
* future hotkey) AND hides the Hotkeys section of Game Info, including one the game supplied
|
|
308
|
+
* itself — a keycap chart for keys that do nothing is worse than no chart.
|
|
309
|
+
*
|
|
310
|
+
* Left unset, the shell measures the client (`core/device.ts`): a touchscreen has no keys to
|
|
311
|
+
* press, so it gets neither the shortcuts nor the chart. Set it explicitly when you know better
|
|
312
|
+
* than the media query — which is what the host does with the platform's `device` field. */
|
|
308
313
|
hotkeys?: boolean;
|
|
309
314
|
/** Spacebar starts a spin in base mode. Defaults to `true`; set `false` to disable the
|
|
310
315
|
* keyboard shortcut (e.g. jurisdictions that forbid quick-spin keys). */
|
|
@@ -636,7 +641,7 @@ declare class ShellController extends EventEmitter<ShellEvents> implements Shell
|
|
|
636
641
|
tokens: ShellTokens;
|
|
637
642
|
layout: ShellLayoutMode;
|
|
638
643
|
soundOn: boolean;
|
|
639
|
-
readonly engineVersion = "0.
|
|
644
|
+
readonly engineVersion = "0.10.0";
|
|
640
645
|
readonly actions: ShellActions;
|
|
641
646
|
private renderer;
|
|
642
647
|
private i18n;
|
|
@@ -780,8 +785,22 @@ interface I18n {
|
|
|
780
785
|
}
|
|
781
786
|
declare function createI18n(opts: I18nOptions): I18n;
|
|
782
787
|
|
|
788
|
+
/**
|
|
789
|
+
* The disclaimer body every launch shows, in source English — and the SAME strings that key the
|
|
790
|
+
* translations below. One constant for both on purpose: a host that kept its own copy would drift
|
|
791
|
+
* from these keys by one character and the lookup would miss in silence, leaving a player on any
|
|
792
|
+
* language with English legal text. Certification asks for the opposite ("if multiple languages are
|
|
793
|
+
* supported, the disclaimer is translated and displayed in each language").
|
|
794
|
+
*
|
|
795
|
+
* Brand-free by construction. Stake's own template ends with a seventh line — "TM and © {year}
|
|
796
|
+
* Stake Engine." — and that one belongs to a Stake launch alone: it arrives with the Stake bridge's
|
|
797
|
+
* `INIT.config.disclaimerLines`, which outrank this default, and renders verbatim (see the host's
|
|
798
|
+
* `isBrandLine`). Every other platform gets the same mandated wording without someone else's mark.
|
|
799
|
+
*/
|
|
800
|
+
declare const DISCLAIMER_LINES: readonly string[];
|
|
801
|
+
|
|
783
802
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
784
|
-
declare const PACKAGE_VERSION = "0.
|
|
803
|
+
declare const PACKAGE_VERSION = "0.10.0";
|
|
785
804
|
|
|
786
805
|
/** A shell: the renderer-agnostic controller plus the surface facade (safeArea/barHeight/setVisible)
|
|
787
806
|
* an embedding host reads. `createShell`, `createGameShell` and `createPixiShell` all return this. */
|
|
@@ -861,5 +880,5 @@ declare function createGameShell(config: HtmlShellConfig): ShellController;
|
|
|
861
880
|
/** Tear down the active shell (no argument — singleton). Resolves immediately when nothing is active. */
|
|
862
881
|
declare function removeGameShell(): Promise<void>;
|
|
863
882
|
|
|
864
|
-
export { DEFAULT_ACCENT, DEFAULT_MENU, ShellController as GameShell, HtmlRenderer, PACKAGE_VERSION, POPOVER, SCHEMES, ShellController, createGameShell, createI18n, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removeGameShell, resolveConfig, resolveMenu, resolveTheme, seedMenuValues, socialize };
|
|
883
|
+
export { DEFAULT_ACCENT, DEFAULT_MENU, DISCLAIMER_LINES, ShellController as GameShell, HtmlRenderer, PACKAGE_VERSION, POPOVER, SCHEMES, ShellController, createGameShell, createI18n, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removeGameShell, resolveConfig, resolveMenu, resolveTheme, seedMenuValues, socialize };
|
|
865
884
|
export type { AutoplayConfig, AutoplayOptions, BonusCardContext, BonusOption, BonusReadout, CellRef, CreateShellOptions, CurrencyConfig, FreeSpinsState, GameInfoContent, GameInfoSection, GameMode, HtmlShellConfig, I18n, I18nOptions, Lang, MenuHost, MenuItem, MenuPresetId, MenuRow, ModalAction, ModalOptions, OverlayHandle, OverlayRequest, PaylineDef, PaytableRow, PopoverPlacement, Rect as PopoverRect, 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
|
@@ -1306,6 +1306,19 @@ const D = {
|
|
|
1306
1306
|
L4: 'The game display is not representative of any physical device and is for illustrative purposes only.',
|
|
1307
1307
|
L5: 'Winnings are settled according to the amount received from the Remote Game Server and not from events within the web browser.',
|
|
1308
1308
|
};
|
|
1309
|
+
/**
|
|
1310
|
+
* The disclaimer body every launch shows, in source English — and the SAME strings that key the
|
|
1311
|
+
* translations below. One constant for both on purpose: a host that kept its own copy would drift
|
|
1312
|
+
* from these keys by one character and the lookup would miss in silence, leaving a player on any
|
|
1313
|
+
* language with English legal text. Certification asks for the opposite ("if multiple languages are
|
|
1314
|
+
* supported, the disclaimer is translated and displayed in each language").
|
|
1315
|
+
*
|
|
1316
|
+
* Brand-free by construction. Stake's own template ends with a seventh line — "TM and © {year}
|
|
1317
|
+
* Stake Engine." — and that one belongs to a Stake launch alone: it arrives with the Stake bridge's
|
|
1318
|
+
* `INIT.config.disclaimerLines`, which outrank this default, and renders verbatim (see the host's
|
|
1319
|
+
* `isBrandLine`). Every other platform gets the same mandated wording without someone else's mark.
|
|
1320
|
+
*/
|
|
1321
|
+
const DISCLAIMER_LINES = [D.L1, D.L2, D.L3, D.L4, D.L5];
|
|
1309
1322
|
const DISCLAIMER_LOCALES = {
|
|
1310
1323
|
da: {
|
|
1311
1324
|
[D.L1]: 'Fejlfunktion annullerer alle gevinster og spil.',
|
|
@@ -1689,9 +1702,41 @@ class KeyboardController {
|
|
|
1689
1702
|
}
|
|
1690
1703
|
}
|
|
1691
1704
|
|
|
1705
|
+
/**
|
|
1706
|
+
* Can the player in front of this client actually press a key?
|
|
1707
|
+
*
|
|
1708
|
+
* The shell documents its shortcuts in a Hotkeys section and binds Spacebar to spin. On a phone
|
|
1709
|
+
* neither is reachable, and showing a keycap chart to someone holding a touchscreen is a promise
|
|
1710
|
+
* the game can't keep — a certification lab reads it as a feature offered where it doesn't work.
|
|
1711
|
+
*
|
|
1712
|
+
* The question is deliberately NOT "is the layout narrow" (a portrait desktop window still has a
|
|
1713
|
+
* keyboard) and NOT "is there a touchscreen" (a touch laptop has both). It is: what does the
|
|
1714
|
+
* PRIMARY pointer look like, and can it hover? Coarse-and-hoverless is a touchscreen, and a
|
|
1715
|
+
* touchscreen is the one case where the keys genuinely aren't there.
|
|
1716
|
+
*
|
|
1717
|
+
* A tablet with a keyboard case answers "coarse, no hover" too and loses the chart. That is the
|
|
1718
|
+
* right side to be wrong on: the chart is a convenience, and the keys keep working for anyone who
|
|
1719
|
+
* has them — the media query only decides what the shell ADVERTISES (hosts can still say outright,
|
|
1720
|
+
* via `features.hotkeys`, and the platform's own `device` field does exactly that).
|
|
1721
|
+
*/
|
|
1722
|
+
/** A touchscreen: the primary pointer is a finger, and nothing can hover. */
|
|
1723
|
+
const TOUCH_ONLY = '(pointer: coarse) and (hover: none)';
|
|
1724
|
+
function keyboardCapable(win = typeof window === 'undefined' ? undefined : window) {
|
|
1725
|
+
// No window (SSR, node tests) or a browser too old for matchMedia: assume a keyboard rather than
|
|
1726
|
+
// silently stripping shortcuts from a desktop we simply failed to measure.
|
|
1727
|
+
if (typeof win?.matchMedia !== 'function')
|
|
1728
|
+
return true;
|
|
1729
|
+
try {
|
|
1730
|
+
return !win.matchMedia(TOUCH_ONLY).matches;
|
|
1731
|
+
}
|
|
1732
|
+
catch {
|
|
1733
|
+
return true;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1692
1737
|
// AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
|
|
1693
1738
|
/** The @energy8platform/shell package version, stamped into the game-info footer. */
|
|
1694
|
-
const PACKAGE_VERSION = '0.
|
|
1739
|
+
const PACKAGE_VERSION = '0.10.0';
|
|
1695
1740
|
|
|
1696
1741
|
/** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
|
|
1697
1742
|
function resolveConfig(config) {
|
|
@@ -1705,7 +1750,10 @@ function resolveConfig(config) {
|
|
|
1705
1750
|
win: config.win,
|
|
1706
1751
|
mode: config.mode,
|
|
1707
1752
|
gameInfo: config.gameInfo,
|
|
1708
|
-
|
|
1753
|
+
// `hotkeys` unset means "decide for me": a touchscreen has no keys to press, so the shell
|
|
1754
|
+
// neither binds them nor advertises them there. A host that knows better — the platform's own
|
|
1755
|
+
// `device` field, a jurisdiction rule — says so outright and that wins. See core/device.ts.
|
|
1756
|
+
features: { ...config.features, hotkeys: config.features.hotkeys ?? keyboardCapable() },
|
|
1709
1757
|
theme: config.theme,
|
|
1710
1758
|
onBonusBuy: config.onBonusBuy,
|
|
1711
1759
|
volumes: config.volumes,
|
|
@@ -1799,7 +1847,11 @@ class ShellController extends EventEmitter {
|
|
|
1799
1847
|
this.renderer.renderBar();
|
|
1800
1848
|
},
|
|
1801
1849
|
toggleAutoplay: () => {
|
|
1802
|
-
|
|
1850
|
+
// A halted run (stopped, but with spins still owed after a lost connection) counts as
|
|
1851
|
+
// "autoplay is on screen": the toggle retires its leftover count, exactly as it stops a
|
|
1852
|
+
// running one. Resuming those spins is the disc's job, not this one's.
|
|
1853
|
+
const { active, remaining } = this.state.autoplay;
|
|
1854
|
+
if (active || remaining > 0)
|
|
1803
1855
|
a.stopAutoplay();
|
|
1804
1856
|
else
|
|
1805
1857
|
this.openAutoplayPicker();
|
|
@@ -2262,6 +2314,11 @@ const SHELL_CSS = SHELL_FONT_CSS + SHELL_DIGIT_FONT_CSS + `
|
|
|
2262
2314
|
/* the STOP glyph is a solid dark square, so the autoplay count is always pure white to read on it. */
|
|
2263
2315
|
#${SHELL_ROOT_ID} .ge-spin-count { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
|
|
2264
2316
|
font-size:22px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; color:#fff; }
|
|
2317
|
+
/* autoplay halted with spins still owed (a lost connection): the run is stopped, so no STOP square —
|
|
2318
|
+
the auto glyph sits above the leftover count, both in the disc's own ink, and a tap resumes. */
|
|
2319
|
+
#${SHELL_ROOT_ID} .ge-shell-spin.ge-auto-paused { flex-direction:column; gap:1px; position:relative; }
|
|
2320
|
+
#${SHELL_ROOT_ID} .ge-auto-paused .ge-spin-auto { display:flex; font-size:.46em; line-height:0; }
|
|
2321
|
+
#${SHELL_ROOT_ID} .ge-auto-paused .ge-spin-count { position:static; inset:auto; color:inherit; font-size:20px; }
|
|
2265
2322
|
|
|
2266
2323
|
/* BUY BONUS — round accent badge, 2-line label, text pulses + accent glow on hover */
|
|
2267
2324
|
#${SHELL_ROOT_ID} .ge-shell-buybonus { pointer-events:auto; cursor:pointer; box-sizing:border-box;
|
|
@@ -3083,22 +3140,36 @@ function buyBtn(host) {
|
|
|
3083
3140
|
function betLocked(host) {
|
|
3084
3141
|
return host.state.busy || host.state.autoplay.active;
|
|
3085
3142
|
}
|
|
3086
|
-
/**
|
|
3143
|
+
/**
|
|
3144
|
+
* SPIN disc — rotates while busy; becomes a STOP + countdown while autoplay runs; becomes an
|
|
3145
|
+
* autoplay glyph + the SAME countdown when a run was halted with spins still owed (a lost
|
|
3146
|
+
* connection), where a tap resumes it. That third state is what a certification lab means by "after
|
|
3147
|
+
* reconnection the counter is displayed correctly": the run stopped, but the spins the player asked
|
|
3148
|
+
* for are still on screen and one tap away, instead of silently reset to zero.
|
|
3149
|
+
*/
|
|
3087
3150
|
function spinButton(host) {
|
|
3088
3151
|
const { state } = host;
|
|
3089
3152
|
const sp = document.createElement('button');
|
|
3090
3153
|
sp.className = 'ge-shell-spin';
|
|
3091
3154
|
sp.dataset.ge = 'spin';
|
|
3155
|
+
const rem = state.autoplay.remaining;
|
|
3156
|
+
const count = Number.isFinite(rem) ? String(rem) : '∞';
|
|
3092
3157
|
if (state.autoplay.active) {
|
|
3093
3158
|
sp.classList.add('ge-stop');
|
|
3094
|
-
|
|
3095
|
-
const label = Number.isFinite(rem) ? String(rem) : '∞';
|
|
3096
|
-
sp.innerHTML = `<span class="ge-spin-stop">${icon('stop')}</span><span class="ge-spin-count">${label}</span>`;
|
|
3159
|
+
sp.innerHTML = `<span class="ge-spin-stop">${icon('stop')}</span><span class="ge-spin-count">${count}</span>`;
|
|
3097
3160
|
sp.addEventListener('click', () => {
|
|
3098
3161
|
if (!sp.disabled)
|
|
3099
3162
|
host.actions.stopAutoplay();
|
|
3100
3163
|
});
|
|
3101
3164
|
}
|
|
3165
|
+
else if (rem > 0) {
|
|
3166
|
+
sp.classList.add('ge-auto-paused');
|
|
3167
|
+
sp.innerHTML = `<span class="ge-spin-auto">${icon('autoplay')}</span><span class="ge-spin-count">${count}</span>`;
|
|
3168
|
+
sp.addEventListener('click', () => {
|
|
3169
|
+
if (!sp.disabled)
|
|
3170
|
+
host.actions.startAutoplay(rem);
|
|
3171
|
+
});
|
|
3172
|
+
}
|
|
3102
3173
|
else {
|
|
3103
3174
|
sp.innerHTML = icon('spin');
|
|
3104
3175
|
if (state.busy)
|
|
@@ -3118,8 +3189,11 @@ function autoButton(host) {
|
|
|
3118
3189
|
b.classList.add('ge-glow');
|
|
3119
3190
|
return b;
|
|
3120
3191
|
}
|
|
3192
|
+
/** Same button, three jobs: stop a running run, retire a halted run's leftover count (which frees
|
|
3193
|
+
* the disc for a manual spin again), or open the picker. */
|
|
3121
3194
|
function onAutoplay(host) {
|
|
3122
|
-
|
|
3195
|
+
const { active, remaining } = host.state.autoplay;
|
|
3196
|
+
if (active || remaining > 0)
|
|
3123
3197
|
host.actions.stopAutoplay();
|
|
3124
3198
|
else
|
|
3125
3199
|
host.actions.openAutoplayPicker();
|
|
@@ -3475,10 +3549,15 @@ function openGameInfoModal(host) {
|
|
|
3475
3549
|
onBack: () => { root.remove(); host.actions.openMenu(); },
|
|
3476
3550
|
});
|
|
3477
3551
|
root.dataset.ge = 'info-modal';
|
|
3478
|
-
const
|
|
3479
|
-
// Auto-inject a hotkeys section unless the game already provides one
|
|
3552
|
+
const allSections = host.config.gameInfo.sections ?? [];
|
|
3553
|
+
// Auto-inject a hotkeys section unless the game already provides one. With hotkeys off — a
|
|
3554
|
+
// jurisdiction that forbids them, or a touchscreen that has no keys at all (see core/device.ts) —
|
|
3555
|
+
// there is no keyboard surface to document, and a game-supplied section is dropped along with the
|
|
3556
|
+
// auto-injected one: a keycap chart for keys the player cannot press is a promise the game breaks.
|
|
3557
|
+
const keys = host.config.features.hotkeys !== false;
|
|
3558
|
+
const rawSections = keys ? allSections : allSections.filter((s) => s.type !== 'hotkeys');
|
|
3480
3559
|
const sectionsWithHotkeys = [...rawSections];
|
|
3481
|
-
if (
|
|
3560
|
+
if (keys && !rawSections.some((s) => s.type === 'hotkeys')) {
|
|
3482
3561
|
sectionsWithHotkeys.push({ type: 'hotkeys', order: HOTKEYS_DEFAULT_ORDER });
|
|
3483
3562
|
}
|
|
3484
3563
|
const sections = sectionsWithHotkeys;
|
|
@@ -4736,5 +4815,5 @@ function removeGameShell() {
|
|
|
4736
4815
|
return shell.destroy();
|
|
4737
4816
|
}
|
|
4738
4817
|
|
|
4739
|
-
export { DEFAULT_ACCENT, DEFAULT_MENU, ShellController as GameShell, HtmlRenderer, PACKAGE_VERSION, POPOVER, SCHEMES, ShellController, createGameShell, createI18n, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removeGameShell, resolveConfig, resolveMenu, resolveTheme, seedMenuValues, socialize };
|
|
4818
|
+
export { DEFAULT_ACCENT, DEFAULT_MENU, DISCLAIMER_LINES, ShellController as GameShell, HtmlRenderer, PACKAGE_VERSION, POPOVER, SCHEMES, ShellController, createGameShell, createI18n, createShell, isPresetId, normalizeLang, placePopover, popoverWidth, rangeBounds, removeGameShell, resolveConfig, resolveMenu, resolveTheme, seedMenuValues, socialize };
|
|
4740
4819
|
//# sourceMappingURL=html.esm.js.map
|