@energy8platform/platform-core 0.26.1 → 0.28.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/index.cjs.js +0 -3615
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -400
- package/dist/index.esm.js +1 -3613
- package/dist/index.esm.js.map +1 -1
- package/package.json +3 -9
- package/scripts/build-digit-font.mjs +72 -0
- package/src/index.ts +2 -17
- package/dist/shell.cjs.js +0 -3671
- package/dist/shell.cjs.js.map +0 -1
- package/dist/shell.d.ts +0 -433
- package/dist/shell.esm.js +0 -3664
- package/dist/shell.esm.js.map +0 -1
- package/scripts/gen-version.mjs +0 -21
- package/src/shell/GameShell.ts +0 -412
- package/src/shell/INTER-LICENSE.txt +0 -93
- package/src/shell/colors.ts +0 -32
- package/src/shell/components/BottomBar.ts +0 -237
- package/src/shell/components/BuyBonus.ts +0 -329
- package/src/shell/components/GameInfo.ts +0 -384
- package/src/shell/components/Modal.ts +0 -36
- package/src/shell/components/ReplayModal.ts +0 -57
- package/src/shell/components/Settings.ts +0 -59
- package/src/shell/components/icons.ts +0 -40
- package/src/shell/components/pickers.ts +0 -150
- package/src/shell/components/primitives.ts +0 -85
- package/src/shell/fonts.ts +0 -13
- package/src/shell/format.ts +0 -39
- package/src/shell/i18n.ts +0 -96
- package/src/shell/index.ts +0 -22
- package/src/shell/keyboard.ts +0 -229
- package/src/shell/locales.ts +0 -864
- package/src/shell/motion.ts +0 -43
- package/src/shell/shell.css.ts +0 -449
- package/src/shell/state.ts +0 -31
- package/src/shell/theme.ts +0 -54
- package/src/shell/types.ts +0 -262
- package/src/shell/version.ts +0 -3
package/src/shell/keyboard.ts
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import type { ShellState } from './types';
|
|
2
|
-
|
|
3
|
-
export interface KeyboardHost {
|
|
4
|
-
readonly state: ShellState;
|
|
5
|
-
readonly hotkeysEnabled: boolean; // features.hotkeys !== false
|
|
6
|
-
readonly spacebarEnabled: boolean; // features.spacebar !== false
|
|
7
|
-
readonly turboLevels: number; // features.turbo
|
|
8
|
-
readonly autoplayEnabled: boolean; // features.autoplay != null
|
|
9
|
-
readonly buyBonusEnabled: boolean; // features.buyBonus !== false
|
|
10
|
-
hasOpenLayer(): boolean;
|
|
11
|
-
routeToLayer(e: KeyboardEvent): boolean; // give the key to the top layer's onKey; true if consumed
|
|
12
|
-
spin(): void;
|
|
13
|
-
stepBet(dir: 1 | -1): void;
|
|
14
|
-
toggleAutoplay(): void;
|
|
15
|
-
cycleTurbo(): void;
|
|
16
|
-
openBuyBonus(): void;
|
|
17
|
-
openInfo(): void;
|
|
18
|
-
openMenu(): void;
|
|
19
|
-
toggleMute(): void;
|
|
20
|
-
closeLayer(): void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Bet key detection: bet-up needs Shift for arrow/equal, NumpadAdd is bare; same logic for down.
|
|
24
|
-
// Exported so overlays with their own bet stepper (Buy bonus) honour the SAME keys as the bar.
|
|
25
|
-
export function betDir(e: KeyboardEvent): 1 | -1 | null {
|
|
26
|
-
if (e.code === 'ArrowUp' && e.shiftKey) return 1;
|
|
27
|
-
if (e.code === 'Equal' && e.shiftKey) return 1;
|
|
28
|
-
if (e.code === 'NumpadAdd') return 1;
|
|
29
|
-
if (e.code === 'ArrowDown' && e.shiftKey) return -1;
|
|
30
|
-
if (e.code === 'Minus' && e.shiftKey) return -1;
|
|
31
|
-
if (e.code === 'NumpadSubtract') return -1;
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class KeyboardController {
|
|
36
|
-
private host: KeyboardHost;
|
|
37
|
-
private doc: Document;
|
|
38
|
-
private spaceHeld = false;
|
|
39
|
-
private holdTimer: ReturnType<typeof setTimeout> | null = null;
|
|
40
|
-
// Bet hold-repeat state
|
|
41
|
-
private betHeldCode: string | null = null;
|
|
42
|
-
private betTimer: ReturnType<typeof setTimeout> | null = null;
|
|
43
|
-
|
|
44
|
-
constructor(host: KeyboardHost, doc?: Document) {
|
|
45
|
-
this.host = host;
|
|
46
|
-
this.doc = doc ?? (typeof document !== 'undefined' ? document : (null as unknown as Document));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private isSpinAllowed(): boolean {
|
|
50
|
-
const h = this.host;
|
|
51
|
-
const s = h.state;
|
|
52
|
-
return (
|
|
53
|
-
h.spacebarEnabled &&
|
|
54
|
-
h.hotkeysEnabled &&
|
|
55
|
-
!h.hasOpenLayer() &&
|
|
56
|
-
s.mode === 'base' &&
|
|
57
|
-
!s.autoplay.active
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private isBetAllowed(): boolean {
|
|
62
|
-
const h = this.host;
|
|
63
|
-
const s = h.state;
|
|
64
|
-
return (
|
|
65
|
-
h.hotkeysEnabled &&
|
|
66
|
-
!h.hasOpenLayer() &&
|
|
67
|
-
s.mode === 'base' &&
|
|
68
|
-
!s.busy
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
private clearBetTimer(): void {
|
|
73
|
-
if (this.betTimer !== null) {
|
|
74
|
-
clearTimeout(this.betTimer);
|
|
75
|
-
this.betTimer = null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private startBetRepeat(dir: 1 | -1, elapsed: number): void {
|
|
80
|
-
// elapsed is ms already spent holding; use it to accelerate toward 45ms floor.
|
|
81
|
-
// Start at 90ms, decrease ~1ms per 10ms held after the first repeat, floor at 45ms.
|
|
82
|
-
const interval = Math.max(45, 90 - Math.floor(elapsed / 10));
|
|
83
|
-
this.betTimer = setTimeout(() => {
|
|
84
|
-
this.betTimer = null;
|
|
85
|
-
if (this.betHeldCode !== null && this.isBetAllowed()) {
|
|
86
|
-
this.host.stepBet(dir);
|
|
87
|
-
this.startBetRepeat(dir, elapsed + interval);
|
|
88
|
-
}
|
|
89
|
-
}, interval);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
private onKeyDown = (e: KeyboardEvent): void => {
|
|
93
|
-
const target = e.target as HTMLElement | null;
|
|
94
|
-
// Editable element guard — never intercept keyboard input
|
|
95
|
-
if (target && (target.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(target.tagName))) return;
|
|
96
|
-
|
|
97
|
-
// For Space: claim preventDefault early (before layer/mode/busy bail) so the browser's
|
|
98
|
-
// native "Space activates focused button" can't re-fire a shell control and flicker a modal.
|
|
99
|
-
if (e.code === 'Space' && !e.repeat) {
|
|
100
|
-
if (!this.host.spacebarEnabled || !this.host.hotkeysEnabled) return;
|
|
101
|
-
e.preventDefault();
|
|
102
|
-
if (this.host.hasOpenLayer()) {
|
|
103
|
-
this.host.routeToLayer(e);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const s = this.host.state;
|
|
107
|
-
if (s.mode !== 'base' || s.busy || s.autoplay.active) return;
|
|
108
|
-
this.spaceHeld = true;
|
|
109
|
-
this.host.spin();
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Bet step keys (Shift+arrows, Shift+=/-, NumpadAdd/Subtract) — non-repeat only
|
|
114
|
-
if (!e.repeat) {
|
|
115
|
-
const dir = betDir(e);
|
|
116
|
-
if (dir !== null && this.isBetAllowed()) {
|
|
117
|
-
this.betHeldCode = e.code;
|
|
118
|
-
this.host.stepBet(dir);
|
|
119
|
-
// First repeat after 350ms initial delay
|
|
120
|
-
this.clearBetTimer();
|
|
121
|
-
const capturedDir = dir;
|
|
122
|
-
this.betTimer = setTimeout(() => {
|
|
123
|
-
this.betTimer = null;
|
|
124
|
-
if (this.betHeldCode !== null && this.isBetAllowed()) {
|
|
125
|
-
this.host.stepBet(capturedDir);
|
|
126
|
-
this.startBetRepeat(capturedDir, 350);
|
|
127
|
-
}
|
|
128
|
-
}, 350);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Non-Space keys: give the open layer first refusal. If it consumes the key, done; Escape closes
|
|
134
|
-
// it. Anything the layer does NOT consume falls through to the chrome hotkeys below — so the
|
|
135
|
-
// Settings/Info pages still honour Shift+I (Game info), Shift+M (sound), Shift+S, etc.
|
|
136
|
-
if (this.host.hasOpenLayer()) {
|
|
137
|
-
const consumed = this.host.routeToLayer(e);
|
|
138
|
-
if (consumed) return;
|
|
139
|
-
if (e.code === 'Escape') { this.host.closeLayer(); return; }
|
|
140
|
-
// not consumed → fall through to the Shift+letter chrome hotkeys
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Shift+letter bar hotkeys — fire when no layer is open, OR when an open layer left the key
|
|
144
|
-
// unconsumed (see fall-through above); gated on hotkeys being enabled.
|
|
145
|
-
if (!e.repeat && e.shiftKey && this.host.hotkeysEnabled) {
|
|
146
|
-
const h = this.host;
|
|
147
|
-
const s = h.state;
|
|
148
|
-
switch (e.code) {
|
|
149
|
-
case 'KeyA':
|
|
150
|
-
if (h.autoplayEnabled && !s.replay) { h.toggleAutoplay(); return; }
|
|
151
|
-
break;
|
|
152
|
-
case 'KeyT':
|
|
153
|
-
if (h.turboLevels > 0 && !s.replay) { h.cycleTurbo(); return; }
|
|
154
|
-
break;
|
|
155
|
-
case 'KeyB':
|
|
156
|
-
if (h.buyBonusEnabled && s.mode === 'base' && !s.replay) { h.openBuyBonus(); return; }
|
|
157
|
-
break;
|
|
158
|
-
case 'KeyI':
|
|
159
|
-
h.openInfo(); return;
|
|
160
|
-
case 'KeyS':
|
|
161
|
-
h.openMenu(); return;
|
|
162
|
-
case 'KeyM':
|
|
163
|
-
h.toggleMute(); return;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
private onKeyUp = (e: KeyboardEvent): void => {
|
|
169
|
-
if (e.code === 'Space') {
|
|
170
|
-
this.spaceHeld = false;
|
|
171
|
-
this.clearHoldTimer();
|
|
172
|
-
}
|
|
173
|
-
// Stop bet repeat on key release
|
|
174
|
-
if (e.code === this.betHeldCode) {
|
|
175
|
-
this.betHeldCode = null;
|
|
176
|
-
this.clearBetTimer();
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
private onBlur = (): void => {
|
|
181
|
-
// Window blur — stop bet repeat AND hold-to-spin (same as releasing both keys)
|
|
182
|
-
this.betHeldCode = null;
|
|
183
|
-
this.clearBetTimer();
|
|
184
|
-
this.spaceHeld = false;
|
|
185
|
-
this.clearHoldTimer();
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
private clearHoldTimer(): void {
|
|
189
|
-
if (this.holdTimer !== null) {
|
|
190
|
-
clearTimeout(this.holdTimer);
|
|
191
|
-
this.holdTimer = null;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
attach(): void {
|
|
196
|
-
this.doc.addEventListener('keydown', this.onKeyDown);
|
|
197
|
-
this.doc.addEventListener('keyup', this.onKeyUp);
|
|
198
|
-
// Use window if available for blur events
|
|
199
|
-
if (typeof window !== 'undefined') {
|
|
200
|
-
window.addEventListener('blur', this.onBlur);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
detach(): void {
|
|
205
|
-
this.doc.removeEventListener('keydown', this.onKeyDown);
|
|
206
|
-
this.doc.removeEventListener('keyup', this.onKeyUp);
|
|
207
|
-
if (typeof window !== 'undefined') {
|
|
208
|
-
window.removeEventListener('blur', this.onBlur);
|
|
209
|
-
}
|
|
210
|
-
this.spaceHeld = false;
|
|
211
|
-
this.clearHoldTimer();
|
|
212
|
-
this.betHeldCode = null;
|
|
213
|
-
this.clearBetTimer();
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
notifyBusyChanged(busy: boolean): void {
|
|
217
|
-
if (busy) return;
|
|
218
|
-
if (!this.spaceHeld) return;
|
|
219
|
-
if (!this.isSpinAllowed()) return;
|
|
220
|
-
// Schedule the next spin after the 120 ms floor (gap between completion and next spin).
|
|
221
|
-
this.clearHoldTimer();
|
|
222
|
-
this.holdTimer = setTimeout(() => {
|
|
223
|
-
this.holdTimer = null;
|
|
224
|
-
if (this.spaceHeld && this.isSpinAllowed()) {
|
|
225
|
-
this.host.spin();
|
|
226
|
-
}
|
|
227
|
-
}, 120);
|
|
228
|
-
}
|
|
229
|
-
}
|