@energy8platform/shell 0.2.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.
Files changed (67) hide show
  1. package/dist/html.cjs.js +3894 -0
  2. package/dist/html.cjs.js.map +1 -0
  3. package/dist/html.d.ts +616 -0
  4. package/dist/html.esm.js +3879 -0
  5. package/dist/html.esm.js.map +1 -0
  6. package/dist/index.cjs.js +1593 -0
  7. package/dist/index.cjs.js.map +1 -0
  8. package/dist/index.d.ts +554 -0
  9. package/dist/index.esm.js +1582 -0
  10. package/dist/index.esm.js.map +1 -0
  11. package/dist/pixi.cjs.js +5740 -0
  12. package/dist/pixi.cjs.js.map +1 -0
  13. package/dist/pixi.d.ts +682 -0
  14. package/dist/pixi.esm.js +5726 -0
  15. package/dist/pixi.esm.js.map +1 -0
  16. package/package.json +79 -0
  17. package/src/core/EventEmitter.ts +55 -0
  18. package/src/core/ShellController.ts +234 -0
  19. package/src/core/colors.ts +32 -0
  20. package/src/core/fonts-digits.ts +12 -0
  21. package/src/core/fonts.ts +13 -0
  22. package/src/core/format.ts +39 -0
  23. package/src/core/i18n.ts +96 -0
  24. package/src/core/index.ts +35 -0
  25. package/src/core/keyboard.ts +229 -0
  26. package/src/core/locales.ts +864 -0
  27. package/src/core/motion.ts +10 -0
  28. package/src/core/renderer.ts +121 -0
  29. package/src/core/state.ts +31 -0
  30. package/src/core/theme.ts +81 -0
  31. package/src/core/types.ts +269 -0
  32. package/src/core/version.ts +3 -0
  33. package/src/ui/html/HtmlRenderer.ts +292 -0
  34. package/src/ui/html/components/BottomBar.ts +240 -0
  35. package/src/ui/html/components/BuyBonus.ts +326 -0
  36. package/src/ui/html/components/GameInfo.ts +384 -0
  37. package/src/ui/html/components/Modal.ts +36 -0
  38. package/src/ui/html/components/ReplayModal.ts +58 -0
  39. package/src/ui/html/components/Settings.ts +59 -0
  40. package/src/ui/html/components/pickers.ts +146 -0
  41. package/src/ui/html/icons-preview.svg +224 -0
  42. package/src/ui/html/icons.ts +31 -0
  43. package/src/ui/html/index.ts +36 -0
  44. package/src/ui/html/motion-dom.ts +29 -0
  45. package/src/ui/html/primitives.ts +85 -0
  46. package/src/ui/html/shell.css.ts +528 -0
  47. package/src/ui/html/theme-css.ts +21 -0
  48. package/src/ui/pixi/PixiRenderer.ts +350 -0
  49. package/src/ui/pixi/components/BottomBar.ts +477 -0
  50. package/src/ui/pixi/components/BuyBonus.ts +763 -0
  51. package/src/ui/pixi/components/GameInfo.ts +559 -0
  52. package/src/ui/pixi/components/Modal.ts +40 -0
  53. package/src/ui/pixi/components/ReplayModal.ts +80 -0
  54. package/src/ui/pixi/components/Settings.ts +117 -0
  55. package/src/ui/pixi/components/pickers.ts +170 -0
  56. package/src/ui/pixi/context.ts +52 -0
  57. package/src/ui/pixi/icons.ts +45 -0
  58. package/src/ui/pixi/index.ts +56 -0
  59. package/src/ui/pixi/motion-pixi.ts +58 -0
  60. package/src/ui/pixi/pixi-icon.ts +119 -0
  61. package/src/ui/pixi/primitives/card.ts +227 -0
  62. package/src/ui/pixi/primitives/controls.ts +243 -0
  63. package/src/ui/pixi/primitives/flex.ts +335 -0
  64. package/src/ui/pixi/primitives/overlay.ts +181 -0
  65. package/src/ui/pixi/primitives/scroll.ts +117 -0
  66. package/src/ui/pixi/primitives/widgets.ts +680 -0
  67. package/src/ui/pixi/text.ts +131 -0
@@ -0,0 +1,131 @@
1
+ import { CanvasTextMetrics, Text, TextStyle } from 'pixi.js';
2
+ import { SHELL_FONT_CSS } from '@/core/fonts';
3
+ import { SHELL_DIGIT_FONT_CSS } from '@/core/fonts-digits';
4
+
5
+ // The Pixi shell renders text on canvas, so it relies on the same bundled Inter webfont as the
6
+ // DOM shell being registered in `document.fonts`. We inject the identical @font-face CSS (base64
7
+ // woff2) once; text measured before the font swaps in is re-laid-out on `whenFontReady`.
8
+
9
+ const STYLE_ID = '__ge-pixi-shell-font__';
10
+
11
+ /** Inter leads the stack (same as the DOM shell) so the shell renders identically everywhere;
12
+ * the system fonts stay as graceful fallback if the webfont ever fails to load. */
13
+ export const FONT_FAMILY = "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
14
+
15
+ /** Numeric font — Oswald (digit-only subset) leads, falling back to Inter. Used for the readout
16
+ * VALUES (balance/bet/win/FS counter, autoplay countdown), mirroring the DOM's `.ge-rd-val`. */
17
+ export const NUM_FONT_FAMILY = `OswaldNum, ${FONT_FAMILY}`;
18
+ /** Match the DOM's `.ge-rd-val { font-size:1.15em }` bump (offsets Oswald's narrower glyphs). */
19
+ export const NUM_FONT_SCALE = 1.15;
20
+
21
+ let installed = false;
22
+
23
+ /** Idempotently register the bundled Inter font in the document. No-op outside a DOM. */
24
+ export function installShellFont(): void {
25
+ if (installed || typeof document === 'undefined') return;
26
+ installed = true;
27
+ if (document.getElementById(STYLE_ID)) return;
28
+ const style = document.createElement('style');
29
+ style.id = STYLE_ID;
30
+ style.textContent = SHELL_FONT_CSS + SHELL_DIGIT_FONT_CSS;
31
+ document.head.appendChild(style);
32
+ // Nudge the browser to actually fetch/decode the faces so text measured later is correct.
33
+ // `.load()` returns a PROMISE — a try/catch only traps a synchronous throw, NOT a rejection.
34
+ // WebKit rejects this with `NetworkError: A network error occurred.` when the (base64) face
35
+ // fails to decode; left unhandled it trips the host's global `unhandledrejection` handler and
36
+ // surfaces a fatal "reload" modal over a purely cosmetic font miss. Swallow each explicitly.
37
+ try {
38
+ const fonts = (document as Document & { fonts?: FontFaceSet }).fonts;
39
+ fonts?.load('1em Inter')?.catch(() => {/* ignore — fallback fonts render meanwhile */});
40
+ fonts?.load('1em OswaldNum')?.catch(() => {/* ignore — fallback fonts render meanwhile */});
41
+ } catch {
42
+ /* ignore — fonts API access threw synchronously */
43
+ }
44
+ }
45
+
46
+ /** Run `cb` once the Inter face is ready (or immediately when fonts aren't observable). */
47
+ export function whenFontReady(cb: () => void): void {
48
+ const fonts = typeof document !== 'undefined'
49
+ ? (document as Document & { fonts?: FontFaceSet }).fonts
50
+ : undefined;
51
+ if (!fonts) {
52
+ cb();
53
+ return;
54
+ }
55
+ fonts.ready.then(() => cb()).catch(() => cb());
56
+ }
57
+
58
+ export interface TextOpts {
59
+ size: number;
60
+ /** font-family override (e.g. NUM_FONT_FAMILY for numeric values); defaults to the Inter stack. */
61
+ family?: string;
62
+ /** numeric (e.g. 700) or keyword. */
63
+ weight?: TextStyle['fontWeight'];
64
+ color?: string;
65
+ /** px letter-spacing (CSS `letter-spacing`). */
66
+ letterSpacing?: number;
67
+ /** UPPERCASE the string (CSS `text-transform:uppercase`). */
68
+ upper?: boolean;
69
+ align?: 'left' | 'center' | 'right';
70
+ /** word-wrap width in px. */
71
+ wrapWidth?: number;
72
+ lineHeight?: number;
73
+ /** drop shadow (the floating readouts use `text-shadow:0 1px 3px rgba(0,0,0,.65)`). */
74
+ shadow?: boolean;
75
+ /** Trim to ink bounds (default true). Set false when bottom-/baseline-aligning a row of
76
+ * same-size texts: trimmed glyph-tight boxes vary per content (e.g. a comma's descent), so
77
+ * `align:'end'` would misalign their baselines; an untrimmed line box is uniform. */
78
+ trim?: boolean;
79
+ }
80
+
81
+ /** Distance (px) from an untrimmed text's top to its alphabetic baseline, for the given size/weight.
82
+ * Pixi's flex has no baseline cross-align, so a row of different-size texts (e.g. a 10px label next
83
+ * to a 14px value) must be baseline-aligned by hand: place each at `maxAscent - thisAscent`. Uses
84
+ * the same font-metrics Pixi uses to lay text out; falls back to a ratio with no DOM/canvas. */
85
+ export function textBaseline(size: number, weight: TextStyle['fontWeight'] = '400'): number {
86
+ try {
87
+ return CanvasTextMetrics.measureFont(`${weight} ${size}px ${FONT_FAMILY}`).ascent;
88
+ } catch {
89
+ return size * 0.92; // headless fallback (Inter ascender ≈ 0.92em)
90
+ }
91
+ }
92
+
93
+ /** Create a Pixi Text in the shell font, resolution-bumped for crisp small text. */
94
+ export function makeText(str: string, opts: TextOpts): Text {
95
+ const style = new TextStyle({
96
+ fontFamily: opts.family ?? FONT_FAMILY,
97
+ fontSize: opts.size,
98
+ fontWeight: opts.weight ?? '400',
99
+ fill: opts.color ?? '#ffffff',
100
+ letterSpacing: opts.letterSpacing ?? 0,
101
+ align: opts.align ?? 'left',
102
+ });
103
+ if (opts.wrapWidth != null) {
104
+ style.wordWrap = true;
105
+ style.wordWrapWidth = opts.wrapWidth;
106
+ }
107
+ if (opts.lineHeight != null) style.lineHeight = opts.lineHeight;
108
+ if (opts.shadow) {
109
+ style.dropShadow = {
110
+ color: '#000000',
111
+ alpha: 0.65,
112
+ blur: 3,
113
+ distance: 1,
114
+ angle: Math.PI / 2,
115
+ };
116
+ }
117
+ // Trim to the ink bounds so vertical centring centres the visible glyphs, not the line box
118
+ // (Pixi Text height includes ascent/descent leading → centred text otherwise sits high).
119
+ style.trim = opts.trim ?? true;
120
+ const t = new Text({
121
+ text: opts.upper ? str.toUpperCase() : str,
122
+ style,
123
+ resolution: Math.min(3, (globalThis.devicePixelRatio || 1) * 2),
124
+ });
125
+ return t;
126
+ }
127
+
128
+ /** Mutate an existing Text's content (and re-uppercase if configured). */
129
+ export function setText(t: Text, str: string, upper = false): void {
130
+ t.text = upper ? str.toUpperCase() : str;
131
+ }