@energy8platform/shell 0.7.0 → 0.7.2
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 +9 -5
- package/dist/html.cjs.js.map +1 -1
- package/dist/html.d.ts +5 -2
- package/dist/html.esm.js +9 -5
- package/dist/html.esm.js.map +1 -1
- package/dist/index.cjs.js +9 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.esm.js +9 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/pixi.cjs.js +34 -6
- package/dist/pixi.cjs.js.map +1 -1
- package/dist/pixi.d.ts +5 -2
- package/dist/pixi.esm.js +34 -6
- package/dist/pixi.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/core/format.ts +8 -4
- package/src/core/types.ts +3 -0
- package/src/core/version.ts +1 -1
- package/src/ui/pixi/motion-pixi.ts +16 -1
- package/src/ui/pixi/primitives/widgets.ts +9 -0
package/package.json
CHANGED
package/src/core/format.ts
CHANGED
|
@@ -8,15 +8,19 @@ import type { CurrencyConfig } from './types';
|
|
|
8
8
|
* trailing zeros are trimmed down to (but never past) `minDecimals`, so small wins keep their
|
|
9
9
|
* significant digits. Balance / bet / prices stay fixed at `minDecimals`.
|
|
10
10
|
*
|
|
11
|
+
* Separators default to the platform convention — `.` decimal, `,` thousands (`€1,234.50`).
|
|
12
|
+
* A comma decimal is NOT safe as a default: players read "2,50" as 250 and believe they were
|
|
13
|
+
* paid far more than they were. Games that need another convention pass `currency.separator`.
|
|
14
|
+
*
|
|
11
15
|
* Example with `maxDecimals: 4, minDecimals: 2`:
|
|
12
|
-
* fixed → 0.0673 → 0
|
|
13
|
-
* variable → 0.0673 → 0
|
|
16
|
+
* fixed → 0.0673 → 0.07 0.3 → 0.30
|
|
17
|
+
* variable → 0.0673 → 0.0673 0.067 → 0.067 0.3 → 0.30 0 → 0.00
|
|
14
18
|
*/
|
|
15
19
|
export function formatCurrency(value: number, currency: CurrencyConfig, variableDecimals = false): string {
|
|
16
20
|
const maxDecimals = currency.maxDecimals ?? 2;
|
|
17
21
|
const minDecimals = Math.max(0, Math.min(maxDecimals, currency.minDecimals ?? maxDecimals));
|
|
18
|
-
const thousands = currency.separator?.thousands ?? '
|
|
19
|
-
const decimal = currency.separator?.decimal ?? '
|
|
22
|
+
const thousands = currency.separator?.thousands ?? ',';
|
|
23
|
+
const decimal = currency.separator?.decimal ?? '.';
|
|
20
24
|
const safe = Number.isFinite(value) ? value : 0;
|
|
21
25
|
|
|
22
26
|
// fixed callers round at minDecimals; variable callers round at maxDecimals then trim back down.
|
package/src/core/types.ts
CHANGED
|
@@ -20,6 +20,9 @@ export interface CurrencyConfig {
|
|
|
20
20
|
* trimmed down to this many places so small wins keep their significant digits (e.g. 0.0673)
|
|
21
21
|
* while round amounts stay compact (e.g. 0.30). Everything else is shown at exactly this many. */
|
|
22
22
|
minDecimals?: number;
|
|
23
|
+
/** Defaults to the platform convention: `{ thousands: ',', decimal: '.' }` → `€1,234.50`.
|
|
24
|
+
* Override only for a jurisdiction that requires another convention — a comma decimal reads
|
|
25
|
+
* as a thousands group to most players and inflates the perceived payout. */
|
|
23
26
|
separator?: { thousands?: string; decimal?: string };
|
|
24
27
|
}
|
|
25
28
|
|
package/src/core/version.ts
CHANGED
|
@@ -9,7 +9,22 @@ export interface TweenOpts {
|
|
|
9
9
|
onComplete?: () => void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Tween 0→1 on the Pixi ticker. Returns a canceler. Skips to the end when motion is reduced.
|
|
14
|
+
*
|
|
15
|
+
* CONTRACT — when motion is reduced (or `duration <= 0`) this tween does NOT animate: it applies
|
|
16
|
+
* the final value and calls `onComplete` SYNCHRONOUSLY, before returning. Callers depend on that
|
|
17
|
+
* (`PixiRenderer.destroy` resolves its teardown promise from `onComplete`, and must not wait on a
|
|
18
|
+
* ticker that may already be stopped), so it must stay synchronous.
|
|
19
|
+
*
|
|
20
|
+
* The consequence for callers: `onComplete` is NOT an async boundary. An `onComplete` that starts
|
|
21
|
+
* another tween is then direct recursion with no unwind — tween → onComplete → tween → … until
|
|
22
|
+
* `RangeError: Maximum call stack size exceeded`. That shipped once: the CTA pulse loop
|
|
23
|
+
* (`widgets.ts` startPulse) restarted itself from `onComplete`, so every player running the OS
|
|
24
|
+
* "reduce motion" setting crashed the instant the buy-bonus panel painted its hovered CTA. Any
|
|
25
|
+
* looping animation must therefore check `prefersReducedMotion()` and not loop — which is also
|
|
26
|
+
* what reduced motion is asking for.
|
|
27
|
+
*/
|
|
13
28
|
export function tween(ticker: Ticker, opts: TweenOpts): () => void {
|
|
14
29
|
const ease = opts.ease ?? easeOutCubic;
|
|
15
30
|
if (prefersReducedMotion() || opts.duration <= 0) {
|
|
@@ -4,6 +4,7 @@ import type { IconName } from '../icons';
|
|
|
4
4
|
import { makeIcon, IconView } from '../pixi-icon';
|
|
5
5
|
import { makeText, setText, NUM_FONT_FAMILY, NUM_FONT_SCALE } from '../text';
|
|
6
6
|
import { tween, type TweenOpts } from '../motion-pixi';
|
|
7
|
+
import { prefersReducedMotion } from '@/core/motion';
|
|
7
8
|
import { FlexBox, type Sizable } from './flex';
|
|
8
9
|
|
|
9
10
|
// Shared interactive primitives, ported from the DOM shell's CSS rules. Each widget reproduces
|
|
@@ -620,6 +621,14 @@ export class BuyBonusBadge extends Container implements Sizable {
|
|
|
620
621
|
|
|
621
622
|
private startPulse(): void {
|
|
622
623
|
if (this.pulseCancel) return;
|
|
624
|
+
// A pulse is exactly the kind of looping decoration "reduce motion" asks us to drop — and
|
|
625
|
+
// under it `tween` has nothing to animate anyway: it snaps to the end and calls `onComplete`
|
|
626
|
+
// SYNCHRONOUSLY (see its contract), which this loop would answer by starting another one, and
|
|
627
|
+
// another, with no unwind — `RangeError: Maximum call stack size exceeded`. That was a live
|
|
628
|
+
// Stake crash: with the OS "reduce motion" setting on, opening the buy-bonus panel painted its
|
|
629
|
+
// hovered CTA and killed the game, so the player could never buy. Now the button simply sits
|
|
630
|
+
// still at scale 1.
|
|
631
|
+
if (prefersReducedMotion()) return;
|
|
623
632
|
// both the label (anchor 0.5) and the icon (pivot centred) scale around their centre
|
|
624
633
|
const loop = (): void => {
|
|
625
634
|
this.pulseCancel = tween(this.ticker, {
|