@freegamestore/games 0.13.3 → 0.15.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/README.md +279 -0
- package/dist/GameButton.d.ts +1 -1
- package/dist/GameButton.d.ts.map +1 -1
- package/dist/GameButton.js +4 -0
- package/dist/GameButton.js.map +1 -1
- package/dist/GameConfirm.d.ts +16 -0
- package/dist/GameConfirm.d.ts.map +1 -0
- package/dist/GameConfirm.js +17 -0
- package/dist/GameConfirm.js.map +1 -0
- package/dist/GameModal.d.ts +14 -0
- package/dist/GameModal.d.ts.map +1 -0
- package/dist/GameModal.js +61 -0
- package/dist/GameModal.js.map +1 -0
- package/dist/GameOverScreen.d.ts +18 -0
- package/dist/GameOverScreen.d.ts.map +1 -0
- package/dist/GameOverScreen.js +47 -0
- package/dist/GameOverScreen.js.map +1 -0
- package/dist/GameTextSizeToggle.d.ts +6 -0
- package/dist/GameTextSizeToggle.d.ts.map +1 -0
- package/dist/GameTextSizeToggle.js +53 -0
- package/dist/GameTextSizeToggle.js.map +1 -0
- package/dist/GameThemeToggle.d.ts +6 -0
- package/dist/GameThemeToggle.d.ts.map +1 -0
- package/dist/GameThemeToggle.js +59 -0
- package/dist/GameThemeToggle.js.map +1 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/useAuth.d.ts.map +1 -1
- package/dist/useAuth.js +5 -2
- package/dist/useAuth.js.map +1 -1
- package/dist/useGameSounds.d.ts.map +1 -1
- package/dist/useGameSounds.js +9 -1
- package/dist/useGameSounds.js.map +1 -1
- package/dist/useLeaderboard.d.ts.map +1 -1
- package/dist/useLeaderboard.js +15 -3
- package/dist/useLeaderboard.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# @freegamestore/games
|
|
2
|
+
|
|
3
|
+
Shared React UI primitives for games on **freegamestore.online**. Brand-consistent layout, scroll-free viewport lock, touch-friendly controls, and synthesized sound effects.
|
|
4
|
+
|
|
5
|
+
**Version:** 0.14.0
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @freegamestore/games
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @freegamestore/games
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer dependency: React 19+.
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { GameShell, GameTopbar } from '@freegamestore/games'
|
|
21
|
+
|
|
22
|
+
export default function App() {
|
|
23
|
+
return (
|
|
24
|
+
<GameShell topbar={<GameTopbar title="Chess" score={42} />}>
|
|
25
|
+
{/* your game canvas / DOM */}
|
|
26
|
+
</GameShell>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Components
|
|
32
|
+
|
|
33
|
+
### GameShell
|
|
34
|
+
|
|
35
|
+
Root layout wrapper. Locks the game to `100svh`, prevents document scroll, disables text selection and touch callout. Every game wraps its content in this.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
<GameShell topbar={<GameTopbar title="Tetris" score={42} />}>
|
|
39
|
+
<MyGame />
|
|
40
|
+
</GameShell>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Prop | Type | Description |
|
|
44
|
+
|------|------|-------------|
|
|
45
|
+
| `topbar` | `ReactNode?` | Topbar element (typically `<GameTopbar />`) |
|
|
46
|
+
| `children` | `ReactNode` | Game content — fills remaining viewport |
|
|
47
|
+
|
|
48
|
+
### GameTopbar
|
|
49
|
+
|
|
50
|
+
The single allowed topbar. Same font, padding, and color tokens across every game.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// Simple: just a score
|
|
54
|
+
<GameTopbar title="Tetris" score={42} />
|
|
55
|
+
|
|
56
|
+
// Custom stats + play/pause controls
|
|
57
|
+
<GameTopbar
|
|
58
|
+
title="Pac-Man"
|
|
59
|
+
stats={[
|
|
60
|
+
{ label: 'Score', value: 1200, accent: true },
|
|
61
|
+
{ label: 'Lives', value: 3 },
|
|
62
|
+
{ label: 'Level', value: 5 },
|
|
63
|
+
]}
|
|
64
|
+
rules={<p>Eat all the dots. Avoid ghosts.</p>}
|
|
65
|
+
onPlayPause={togglePause}
|
|
66
|
+
paused={isPaused}
|
|
67
|
+
onRestart={restart}
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Prop | Type | Description |
|
|
72
|
+
|------|------|-------------|
|
|
73
|
+
| `title` | `string?` | Game name, left side |
|
|
74
|
+
| `score` | `number?` | Convenience for single-score games |
|
|
75
|
+
| `stats` | `GameTopbarStat[]?` | Custom stat lineup (replaces `score`) |
|
|
76
|
+
| `actions` | `ReactNode?` | Right-side buttons (max 2) |
|
|
77
|
+
| `rules` | `ReactNode?` | Game instructions — shows info icon, opens overlay on tap |
|
|
78
|
+
| `onPlayPause` | `() => void?` | Renders play/pause icon button |
|
|
79
|
+
| `paused` | `boolean?` | Controls play/pause icon state |
|
|
80
|
+
| `onRestart` | `() => void?` | Renders restart icon button |
|
|
81
|
+
|
|
82
|
+
### GameAuth
|
|
83
|
+
|
|
84
|
+
Sign-in / avatar widget for the topbar `actions` slot. Shows "Sign in" when signed out, avatar + dropdown when signed in.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<GameTopbar title="Chess" score={42} actions={<GameAuth />} />
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
No props — uses `useAuth` internally.
|
|
91
|
+
|
|
92
|
+
### GameButton
|
|
93
|
+
|
|
94
|
+
Touch-friendly button. Min 44px touch target. Four variants, three sizes.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
<GameButton variant="primary" size="lg" onClick={start}>Play Again</GameButton>
|
|
98
|
+
<GameButton variant="secondary" size="sm" onClick={undo}>Undo</GameButton>
|
|
99
|
+
<GameButton variant="ghost" size="sm" onClick={flip}>Flip</GameButton>
|
|
100
|
+
<GameButton variant="danger" onClick={quit}>Quit</GameButton>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
| Prop | Type | Default | Description |
|
|
104
|
+
|------|------|---------|-------------|
|
|
105
|
+
| `children` | `ReactNode` | (required) | Button content |
|
|
106
|
+
| `variant` | `'primary' \| 'secondary' \| 'ghost' \| 'danger'` | `'primary'` | Visual style |
|
|
107
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Touch-target size |
|
|
108
|
+
| `onClick` | `() => void?` | | Click handler |
|
|
109
|
+
| `disabled` | `boolean?` | `false` | Disabled state |
|
|
110
|
+
| `block` | `boolean?` | `false` | Full width |
|
|
111
|
+
|
|
112
|
+
Sizes: `sm` (44px), `md` (48px), `lg` (56px).
|
|
113
|
+
|
|
114
|
+
### GameModal
|
|
115
|
+
|
|
116
|
+
Fullscreen modal overlay. Closes on backdrop click or Escape.
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
<GameModal open={showSettings} onClose={() => setShowSettings(false)} title="Settings">
|
|
120
|
+
<p>Game settings here</p>
|
|
121
|
+
</GameModal>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| Prop | Type | Description |
|
|
125
|
+
|------|------|-------------|
|
|
126
|
+
| `open` | `boolean` | Whether the modal is visible |
|
|
127
|
+
| `onClose` | `() => void` | Called on backdrop click or Escape |
|
|
128
|
+
| `title` | `string?` | Header text |
|
|
129
|
+
| `children` | `ReactNode` | Modal content |
|
|
130
|
+
|
|
131
|
+
### GameOverScreen
|
|
132
|
+
|
|
133
|
+
Standard game-over overlay with score, high score, and play again button.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<GameOverScreen
|
|
137
|
+
score={1200}
|
|
138
|
+
highScore={1500}
|
|
139
|
+
onPlayAgain={restart}
|
|
140
|
+
>
|
|
141
|
+
<Leaderboard {...leaderboard} />
|
|
142
|
+
</GameOverScreen>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Prop | Type | Description |
|
|
146
|
+
|------|------|-------------|
|
|
147
|
+
| `score` | `number` | Final score |
|
|
148
|
+
| `highScore` | `number?` | Personal best (shows "New high score!" when beaten) |
|
|
149
|
+
| `onPlayAgain` | `() => void` | Play again handler |
|
|
150
|
+
| `children` | `ReactNode?` | Extra content below the score |
|
|
151
|
+
|
|
152
|
+
### GameConfirm
|
|
153
|
+
|
|
154
|
+
Confirm/cancel dialog built on GameModal.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<GameConfirm
|
|
158
|
+
open={showQuit}
|
|
159
|
+
title="Quit Game?"
|
|
160
|
+
message="Your progress will be lost."
|
|
161
|
+
onConfirm={quit}
|
|
162
|
+
onCancel={() => setShowQuit(false)}
|
|
163
|
+
variant="danger"
|
|
164
|
+
/>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
| Prop | Type | Default | Description |
|
|
168
|
+
|------|------|---------|-------------|
|
|
169
|
+
| `open` | `boolean` | | Whether visible |
|
|
170
|
+
| `title` | `string` | | Dialog title |
|
|
171
|
+
| `message` | `string` | | Body text |
|
|
172
|
+
| `onConfirm` | `() => void` | | Confirm handler |
|
|
173
|
+
| `onCancel` | `() => void` | | Cancel handler |
|
|
174
|
+
| `confirmLabel` | `string?` | `'Confirm'` | Confirm button text |
|
|
175
|
+
| `cancelLabel` | `string?` | `'Cancel'` | Cancel button text |
|
|
176
|
+
| `variant` | `'default' \| 'danger'` | `'default'` | Confirm button style |
|
|
177
|
+
|
|
178
|
+
### GameThemeToggle
|
|
179
|
+
|
|
180
|
+
Compact sun/moon theme toggle (28px). Cycles system/light/dark. Designed for the topbar `actions` slot.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
<GameTopbar title="Chess" actions={<><GameThemeToggle /><GameTextSizeToggle /></>} />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
No props.
|
|
187
|
+
|
|
188
|
+
### GameTextSizeToggle
|
|
189
|
+
|
|
190
|
+
Compact text-size toggle (28px). Cycles default/large/small. Shows A/A+/A-. Designed for the topbar `actions` slot.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
<GameTopbar title="Chess" actions={<GameTextSizeToggle />} />
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
No props.
|
|
197
|
+
|
|
198
|
+
### Leaderboard
|
|
199
|
+
|
|
200
|
+
Tabbed leaderboard display (Top / Recent). Use with the `useLeaderboard` hook.
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
const { topScores, recentScores, loading } = useLeaderboard('chess')
|
|
204
|
+
|
|
205
|
+
<Leaderboard topScores={topScores} recentScores={recentScores} loading={loading} />
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
| Prop | Type | Description |
|
|
209
|
+
|------|------|-------------|
|
|
210
|
+
| `topScores` | `LeaderboardEntry[]` | All-time top scores |
|
|
211
|
+
| `recentScores` | `LeaderboardEntry[]` | Recent scores |
|
|
212
|
+
| `loading` | `boolean` | Loading state |
|
|
213
|
+
|
|
214
|
+
## Hooks
|
|
215
|
+
|
|
216
|
+
### useAuth
|
|
217
|
+
|
|
218
|
+
Auth state for FreeGameStore (Google OAuth via `auth.freegamestore.online`).
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
const { user, loading, signIn, signOut } = useAuth()
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Returns `{ user: User | null, loading: boolean, signIn: () => void, signOut: () => void }`.
|
|
225
|
+
|
|
226
|
+
### useLeaderboard
|
|
227
|
+
|
|
228
|
+
Fetch and submit scores to the platform leaderboard.
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
const { topScores, recentScores, submitScore, loading, refresh } = useLeaderboard('tetris')
|
|
232
|
+
|
|
233
|
+
// Submit a score
|
|
234
|
+
const { ok, rank } = await submitScore(1200)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### useGameSounds
|
|
238
|
+
|
|
239
|
+
Synthesized sound effects via Web Audio API. Zero audio files. All sounds auto-respect the mute toggle.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
const { playMove, playScore, playError, playGameOver, playLevelUp, playDrop, playClear, playTick } = useGameSounds()
|
|
243
|
+
|
|
244
|
+
// Play a sound (no-op when muted)
|
|
245
|
+
playScore()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
| Sound | Use case |
|
|
249
|
+
|-------|----------|
|
|
250
|
+
| `playMove` | Piece moved, card flipped, button pressed |
|
|
251
|
+
| `playScore` | Scored a point, matched, correct answer |
|
|
252
|
+
| `playError` | Wrong answer, hit obstacle, lost life |
|
|
253
|
+
| `playGameOver` | Game over — descending tones |
|
|
254
|
+
| `playLevelUp` | Level up / achievement — ascending arpeggio |
|
|
255
|
+
| `playDrop` | Hard drop / thud |
|
|
256
|
+
| `playClear` | Line clear / combo |
|
|
257
|
+
| `playTick` | Countdown tick / timer warning |
|
|
258
|
+
|
|
259
|
+
### useSound
|
|
260
|
+
|
|
261
|
+
Low-level mute state from the SDK's SoundProvider. Games should check `muted` before playing any custom audio.
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
const { muted, toggle } = useSound()
|
|
265
|
+
if (!muted) myCustomAudio.play()
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Exports
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
@freegamestore/games → GameShell, GameTopbar, GameAuth, GameButton, GameModal,
|
|
272
|
+
GameOverScreen, GameConfirm, GameThemeToggle,
|
|
273
|
+
GameTextSizeToggle, Leaderboard, useAuth, useLeaderboard,
|
|
274
|
+
useGameSounds, useSound
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|
package/dist/GameButton.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as React from 'react';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
|
-
export type GameButtonVariant = 'primary' | 'secondary' | 'ghost';
|
|
3
|
+
export type GameButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger';
|
|
4
4
|
export type GameButtonSize = 'sm' | 'md' | 'lg';
|
|
5
5
|
export interface GameButtonProps {
|
|
6
6
|
children: ReactNode;
|
package/dist/GameButton.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameButton.d.ts","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"GameButton.d.ts","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;AAC7E,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,wCAAwC;IACxC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,yEAAyE;IACzE,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA0BD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,OAAmB,EACnB,IAAW,EACX,OAAO,EACP,QAAgB,EAChB,KAAa,GACd,EAAE,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA8DrC"}
|
package/dist/GameButton.js
CHANGED
|
@@ -59,6 +59,10 @@ export function GameButton({ children, variant = 'primary', size = 'md', onClick
|
|
|
59
59
|
background: 'transparent',
|
|
60
60
|
color: 'var(--muted, #6b7280)',
|
|
61
61
|
},
|
|
62
|
+
danger: {
|
|
63
|
+
background: '#dc2626',
|
|
64
|
+
color: '#ffffff',
|
|
65
|
+
},
|
|
62
66
|
};
|
|
63
67
|
return (_jsx("button", { style: { ...base, ...variantStyles[variant] }, onClick: disabled ? undefined : onClick, disabled: disabled, onPointerDown: (e) => {
|
|
64
68
|
if (!disabled)
|
package/dist/GameButton.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameButton.js","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":";AAkBA,MAAM,IAAI,GAGN;IACF,EAAE,EAAE;QACF,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,aAAa;QACtB,QAAQ,EAAE,SAAS;QACnB,YAAY,EAAE,UAAU;KACzB;IACD,EAAE,EAAE;QACF,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,kBAAkB;QAC3B,QAAQ,EAAE,QAAQ;QAClB,YAAY,EAAE,SAAS;KACxB;IACD,EAAE,EAAE;QACF,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,iBAAiB;QAC1B,QAAQ,EAAE,MAAM;QAChB,YAAY,EAAE,UAAU;KACzB;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,GAAG,SAAS,EACnB,IAAI,GAAG,IAAI,EACX,OAAO,EACP,QAAQ,GAAG,KAAK,EAChB,KAAK,GAAG,KAAK,GACG;IAChB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAErB,MAAM,IAAI,GAAwB;QAChC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa;QACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACjC,UAAU,EAAE,QAAQ;QACpB,cAAc,EAAE,QAAQ;QACxB,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,kCAAkC;QAC9C,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACxC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3B,UAAU,EAAE,0CAA0C;QACtD,uBAAuB,EAAE,aAAa;QACtC,WAAW,EAAE,cAAc;KAC5B,CAAC;IAEF,MAAM,aAAa,GAAmD;QACpE,OAAO,EAAE;YACP,UAAU,EAAE,wBAAwB;YACpC,KAAK,EAAE,MAAM;SACd;QACD,SAAS,EAAE;YACT,UAAU,EAAE,uBAAuB;YACnC,KAAK,EAAE,qBAAqB;YAC5B,SAAS,EAAE,sCAAsC;SAClD;QACD,KAAK,EAAE;YACL,UAAU,EAAE,aAAa;YACzB,KAAK,EAAE,uBAAuB;SAC/B;KACF,CAAC;IAEF,OAAO,CACL,iBACE,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,EAC7C,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EACvC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;YACnB,IAAI,CAAC,QAAQ;gBAAG,CAAC,CAAC,aAA6B,CAAC,KAAK,CAAC,SAAS,GAAG,aAAa,CAAC;QAClF,CAAC,EACD,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE;YAChB,CAAC,CAAC,aAA6B,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QACxD,CAAC,EACD,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;YACnB,CAAC,CAAC,aAA6B,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QACxD,CAAC,YAEA,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"GameButton.js","sourceRoot":"","sources":["../src/GameButton.tsx"],"names":[],"mappings":";AAkBA,MAAM,IAAI,GAGN;IACF,EAAE,EAAE;QACF,SAAS,EAAE,SAAS;QACpB,OAAO,EAAE,aAAa;QACtB,QAAQ,EAAE,SAAS;QACnB,YAAY,EAAE,UAAU;KACzB;IACD,EAAE,EAAE;QACF,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,kBAAkB;QAC3B,QAAQ,EAAE,QAAQ;QAClB,YAAY,EAAE,SAAS;KACxB;IACD,EAAE,EAAE;QACF,SAAS,EAAE,QAAQ;QACnB,OAAO,EAAE,iBAAiB;QAC1B,QAAQ,EAAE,MAAM;QAChB,YAAY,EAAE,UAAU;KACzB;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,EACzB,QAAQ,EACR,OAAO,GAAG,SAAS,EACnB,IAAI,GAAG,IAAI,EACX,OAAO,EACP,QAAQ,GAAG,KAAK,EAChB,KAAK,GAAG,KAAK,GACG;IAChB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAErB,MAAM,IAAI,GAAwB;QAChC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa;QACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACjC,UAAU,EAAE,QAAQ;QACpB,cAAc,EAAE,QAAQ;QACxB,GAAG,EAAE,QAAQ;QACb,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,UAAU,EAAE,kCAAkC;QAC9C,UAAU,EAAE,GAAG;QACf,UAAU,EAAE,CAAC;QACb,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,MAAM,EAAE,MAAM;QACd,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;QACxC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3B,UAAU,EAAE,0CAA0C;QACtD,uBAAuB,EAAE,aAAa;QACtC,WAAW,EAAE,cAAc;KAC5B,CAAC;IAEF,MAAM,aAAa,GAAmD;QACpE,OAAO,EAAE;YACP,UAAU,EAAE,wBAAwB;YACpC,KAAK,EAAE,MAAM;SACd;QACD,SAAS,EAAE;YACT,UAAU,EAAE,uBAAuB;YACnC,KAAK,EAAE,qBAAqB;YAC5B,SAAS,EAAE,sCAAsC;SAClD;QACD,KAAK,EAAE;YACL,UAAU,EAAE,aAAa;YACzB,KAAK,EAAE,uBAAuB;SAC/B;QACD,MAAM,EAAE;YACN,UAAU,EAAE,SAAS;YACrB,KAAK,EAAE,SAAS;SACjB;KACF,CAAC;IAEF,OAAO,CACL,iBACE,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,EAAE,EAC7C,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EACvC,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE;YACnB,IAAI,CAAC,QAAQ;gBAAG,CAAC,CAAC,aAA6B,CAAC,KAAK,CAAC,SAAS,GAAG,aAAa,CAAC;QAClF,CAAC,EACD,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE;YAChB,CAAC,CAAC,aAA6B,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QACxD,CAAC,EACD,cAAc,EAAE,CAAC,CAAC,EAAE,EAAE;YACnB,CAAC,CAAC,aAA6B,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QACxD,CAAC,YAEA,QAAQ,GACF,CACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface GameConfirmProps {
|
|
2
|
+
open: boolean;
|
|
3
|
+
title: string;
|
|
4
|
+
message: string;
|
|
5
|
+
confirmLabel?: string;
|
|
6
|
+
cancelLabel?: string;
|
|
7
|
+
onConfirm: () => void;
|
|
8
|
+
onCancel: () => void;
|
|
9
|
+
variant?: 'default' | 'danger';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Confirm/cancel dialog for games. Built on GameModal.
|
|
13
|
+
* Use for: quit game, restart, delete save, etc.
|
|
14
|
+
*/
|
|
15
|
+
export declare function GameConfirm({ open, title, message, confirmLabel, cancelLabel, onConfirm, onCancel, variant, }: GameConfirmProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=GameConfirm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameConfirm.d.ts","sourceRoot":"","sources":["../src/GameConfirm.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,KAAK,EACL,OAAO,EACP,YAAwB,EACxB,WAAsB,EACtB,SAAS,EACT,QAAQ,EACR,OAAmB,GACpB,EAAE,gBAAgB,2CA4BlB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { GameButton } from './GameButton.js';
|
|
3
|
+
import { GameModal } from './GameModal.js';
|
|
4
|
+
/**
|
|
5
|
+
* Confirm/cancel dialog for games. Built on GameModal.
|
|
6
|
+
* Use for: quit game, restart, delete save, etc.
|
|
7
|
+
*/
|
|
8
|
+
export function GameConfirm({ open, title, message, confirmLabel = 'Confirm', cancelLabel = 'Cancel', onConfirm, onCancel, variant = 'default', }) {
|
|
9
|
+
return (_jsxs(GameModal, { open: open, onClose: onCancel, title: title, children: [_jsx("p", { style: {
|
|
10
|
+
fontSize: '0.9rem',
|
|
11
|
+
color: 'var(--muted, #64748b)',
|
|
12
|
+
margin: '0 0 1.25rem',
|
|
13
|
+
lineHeight: 1.5,
|
|
14
|
+
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
15
|
+
}, children: message }), _jsxs("div", { style: { display: 'flex', gap: '0.5rem', justifyContent: 'flex-end' }, children: [_jsx(GameButton, { onClick: onCancel, variant: "secondary", size: "md", children: cancelLabel }), _jsx(GameButton, { onClick: onConfirm, variant: variant === 'danger' ? 'danger' : 'primary', size: "md", children: confirmLabel })] })] }));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=GameConfirm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameConfirm.js","sourceRoot":"","sources":["../src/GameConfirm.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAa3C;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,EAC1B,IAAI,EACJ,KAAK,EACL,OAAO,EACP,YAAY,GAAG,SAAS,EACxB,WAAW,GAAG,QAAQ,EACtB,SAAS,EACT,QAAQ,EACR,OAAO,GAAG,SAAS,GACF;IACjB,OAAO,CACL,MAAC,SAAS,IAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,aACpD,YACE,KAAK,EAAE;oBACL,QAAQ,EAAE,QAAQ;oBAClB,KAAK,EAAE,uBAAuB;oBAC9B,MAAM,EAAE,aAAa;oBACrB,UAAU,EAAE,GAAG;oBACf,UAAU,EAAE,kCAAkC;iBAC/C,YAEA,OAAO,GACN,EACJ,eAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,aACxE,KAAC,UAAU,IAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,IAAI,YACzD,WAAW,GACD,EACb,KAAC,UAAU,IACT,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EACpD,IAAI,EAAC,IAAI,YAER,YAAY,GACF,IACT,IACI,CACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface GameModalProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fullscreen modal overlay for games. Used for rules, settings, confirmations.
|
|
10
|
+
* Closes on backdrop click or Escape key.
|
|
11
|
+
* Styled with platform design tokens for consistency across all games.
|
|
12
|
+
*/
|
|
13
|
+
export declare function GameModal({ open, onClose, title, children }: GameModalProps): import("react/jsx-runtime").JSX.Element | null;
|
|
14
|
+
//# sourceMappingURL=GameModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameModal.d.ts","sourceRoot":"","sources":["../src/GameModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,cAAc,kDAiF3E"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Fullscreen modal overlay for games. Used for rules, settings, confirmations.
|
|
5
|
+
* Closes on backdrop click or Escape key.
|
|
6
|
+
* Styled with platform design tokens for consistency across all games.
|
|
7
|
+
*/
|
|
8
|
+
export function GameModal({ open, onClose, title, children }) {
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!open)
|
|
11
|
+
return;
|
|
12
|
+
const handler = (e) => {
|
|
13
|
+
if (e.key === 'Escape')
|
|
14
|
+
onClose();
|
|
15
|
+
};
|
|
16
|
+
document.addEventListener('keydown', handler);
|
|
17
|
+
return () => document.removeEventListener('keydown', handler);
|
|
18
|
+
}, [open, onClose]);
|
|
19
|
+
if (!open)
|
|
20
|
+
return null;
|
|
21
|
+
return (_jsx("div", { onClick: onClose, style: {
|
|
22
|
+
position: 'fixed',
|
|
23
|
+
inset: 0,
|
|
24
|
+
zIndex: 900,
|
|
25
|
+
background: 'rgba(0,0,0,0.6)',
|
|
26
|
+
backdropFilter: 'blur(4px)',
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
justifyContent: 'center',
|
|
30
|
+
padding: '1rem',
|
|
31
|
+
}, children: _jsxs("div", { onClick: (e) => e.stopPropagation(), style: {
|
|
32
|
+
background: 'var(--panel, #ffffff)',
|
|
33
|
+
border: '1px solid var(--line, #e2e8f0)',
|
|
34
|
+
borderRadius: '1rem',
|
|
35
|
+
maxWidth: 400,
|
|
36
|
+
width: '100%',
|
|
37
|
+
maxHeight: '80dvh',
|
|
38
|
+
overflow: 'auto',
|
|
39
|
+
}, children: [title && (_jsxs("div", { style: {
|
|
40
|
+
display: 'flex',
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
justifyContent: 'space-between',
|
|
43
|
+
padding: '0.85rem 1rem',
|
|
44
|
+
borderBottom: '1px solid var(--line, #e2e8f0)',
|
|
45
|
+
}, children: [_jsx("span", { style: {
|
|
46
|
+
fontWeight: 700,
|
|
47
|
+
fontSize: '1rem',
|
|
48
|
+
color: 'var(--ink, #1e293b)',
|
|
49
|
+
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
50
|
+
}, children: title }), _jsx("button", { onClick: onClose, "aria-label": "Close", style: {
|
|
51
|
+
background: 'none',
|
|
52
|
+
border: 'none',
|
|
53
|
+
cursor: 'pointer',
|
|
54
|
+
fontSize: '1.25rem',
|
|
55
|
+
lineHeight: 1,
|
|
56
|
+
color: 'var(--muted, #64748b)',
|
|
57
|
+
padding: '0.25rem',
|
|
58
|
+
fontFamily: 'inherit',
|
|
59
|
+
}, children: "\u00D7" })] })), _jsx("div", { style: { padding: '1rem' }, children: children })] }) }));
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=GameModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameModal.js","sourceRoot":"","sources":["../src/GameModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAkB,SAAS,EAAE,MAAM,OAAO,CAAC;AASlD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAkB;IAC1E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,OAAO,GAAG,CAAC,CAAgB,EAAE,EAAE;YACnC,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ;gBAAE,OAAO,EAAE,CAAC;QACpC,CAAC,CAAC;QACF,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC9C,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAChE,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpB,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,OAAO,CACL,cACE,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE;YACL,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,iBAAiB;YAC7B,cAAc,EAAE,WAAW;YAC3B,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,OAAO,EAAE,MAAM;SAChB,YAED,eACE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,EAAE,EACnC,KAAK,EAAE;gBACL,UAAU,EAAE,uBAAuB;gBACnC,MAAM,EAAE,gCAAgC;gBACxC,YAAY,EAAE,MAAM;gBACpB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,MAAM;gBACb,SAAS,EAAE,OAAO;gBAClB,QAAQ,EAAE,MAAM;aACjB,aAEA,KAAK,IAAI,CACR,eACE,KAAK,EAAE;wBACL,OAAO,EAAE,MAAM;wBACf,UAAU,EAAE,QAAQ;wBACpB,cAAc,EAAE,eAAe;wBAC/B,OAAO,EAAE,cAAc;wBACvB,YAAY,EAAE,gCAAgC;qBAC/C,aAED,eACE,KAAK,EAAE;gCACL,UAAU,EAAE,GAAG;gCACf,QAAQ,EAAE,MAAM;gCAChB,KAAK,EAAE,qBAAqB;gCAC5B,UAAU,EAAE,kCAAkC;6BAC/C,YAEA,KAAK,GACD,EACP,iBACE,OAAO,EAAE,OAAO,gBACL,OAAO,EAClB,KAAK,EAAE;gCACL,UAAU,EAAE,MAAM;gCAClB,MAAM,EAAE,MAAM;gCACd,MAAM,EAAE,SAAS;gCACjB,QAAQ,EAAE,SAAS;gCACnB,UAAU,EAAE,CAAC;gCACb,KAAK,EAAE,uBAAuB;gCAC9B,OAAO,EAAE,SAAS;gCAClB,UAAU,EAAE,SAAS;6BACtB,uBAGM,IACL,CACP,EACD,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAAG,QAAQ,GAAO,IAC7C,GACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface GameOverScreenProps {
|
|
3
|
+
/** Final score to display. */
|
|
4
|
+
score: number;
|
|
5
|
+
/** Personal best (if available). */
|
|
6
|
+
highScore?: number;
|
|
7
|
+
/** Called when user taps "Play Again". */
|
|
8
|
+
onPlayAgain: () => void;
|
|
9
|
+
/** Optional extra content below the score (leaderboard link, stats, etc.). */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Standard game-over overlay. Shows score, optional high score,
|
|
14
|
+
* play again button, and optional extra content.
|
|
15
|
+
* Renders as a centered overlay on top of the game board.
|
|
16
|
+
*/
|
|
17
|
+
export declare function GameOverScreen({ score, highScore, onPlayAgain, children }: GameOverScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=GameOverScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameOverScreen.d.ts","sourceRoot":"","sources":["../src/GameOverScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,WAAW,mBAAmB;IAClC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CA+E9F"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { GameButton } from './GameButton.js';
|
|
3
|
+
/**
|
|
4
|
+
* Standard game-over overlay. Shows score, optional high score,
|
|
5
|
+
* play again button, and optional extra content.
|
|
6
|
+
* Renders as a centered overlay on top of the game board.
|
|
7
|
+
*/
|
|
8
|
+
export function GameOverScreen({ score, highScore, onPlayAgain, children }) {
|
|
9
|
+
const isNewHigh = highScore !== undefined && score >= highScore && score > 0;
|
|
10
|
+
return (_jsx("div", { style: {
|
|
11
|
+
position: 'absolute',
|
|
12
|
+
inset: 0,
|
|
13
|
+
zIndex: 800,
|
|
14
|
+
background: 'rgba(0,0,0,0.65)',
|
|
15
|
+
backdropFilter: 'blur(6px)',
|
|
16
|
+
display: 'flex',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
justifyContent: 'center',
|
|
19
|
+
padding: '1.5rem',
|
|
20
|
+
}, children: _jsxs("div", { style: {
|
|
21
|
+
textAlign: 'center',
|
|
22
|
+
maxWidth: 320,
|
|
23
|
+
width: '100%',
|
|
24
|
+
}, children: [_jsx("div", { style: {
|
|
25
|
+
fontSize: '0.75rem',
|
|
26
|
+
fontWeight: 700,
|
|
27
|
+
textTransform: 'uppercase',
|
|
28
|
+
letterSpacing: '0.1em',
|
|
29
|
+
color: 'rgba(255,255,255,0.6)',
|
|
30
|
+
marginBottom: '0.5rem',
|
|
31
|
+
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
32
|
+
}, children: "Game Over" }), _jsx("div", { style: {
|
|
33
|
+
fontSize: '3rem',
|
|
34
|
+
fontWeight: 800,
|
|
35
|
+
color: '#ffffff',
|
|
36
|
+
lineHeight: 1,
|
|
37
|
+
marginBottom: '0.25rem',
|
|
38
|
+
fontFamily: '"Fraunces", serif',
|
|
39
|
+
}, children: score.toLocaleString() }), highScore !== undefined && (_jsx("div", { style: {
|
|
40
|
+
fontSize: '0.8rem',
|
|
41
|
+
fontWeight: 600,
|
|
42
|
+
color: isNewHigh ? 'var(--accent, #10b981)' : 'rgba(255,255,255,0.5)',
|
|
43
|
+
marginBottom: '1rem',
|
|
44
|
+
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
45
|
+
}, children: isNewHigh ? 'New high score!' : `Best: ${highScore.toLocaleString()}` })), !highScore && _jsx("div", { style: { marginBottom: '1rem' } }), _jsx(GameButton, { onClick: onPlayAgain, variant: "primary", size: "lg", children: "Play Again" }), children && (_jsx("div", { style: { marginTop: '1rem', color: 'rgba(255,255,255,0.7)', fontSize: '0.85rem' }, children: children }))] }) }));
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=GameOverScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameOverScreen.js","sourceRoot":"","sources":["../src/GameOverScreen.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAa7C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAuB;IAC7F,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,IAAI,KAAK,IAAI,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC;IAE7E,OAAO,CACL,cACE,KAAK,EAAE;YACL,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,kBAAkB;YAC9B,cAAc,EAAE,WAAW;YAC3B,OAAO,EAAE,MAAM;YACf,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,OAAO,EAAE,QAAQ;SAClB,YAED,eACE,KAAK,EAAE;gBACL,SAAS,EAAE,QAAQ;gBACnB,QAAQ,EAAE,GAAG;gBACb,KAAK,EAAE,MAAM;aACd,aAED,cACE,KAAK,EAAE;wBACL,QAAQ,EAAE,SAAS;wBACnB,UAAU,EAAE,GAAG;wBACf,aAAa,EAAE,WAAW;wBAC1B,aAAa,EAAE,OAAO;wBACtB,KAAK,EAAE,uBAAuB;wBAC9B,YAAY,EAAE,QAAQ;wBACtB,UAAU,EAAE,kCAAkC;qBAC/C,0BAGG,EAEN,cACE,KAAK,EAAE;wBACL,QAAQ,EAAE,MAAM;wBAChB,UAAU,EAAE,GAAG;wBACf,KAAK,EAAE,SAAS;wBAChB,UAAU,EAAE,CAAC;wBACb,YAAY,EAAE,SAAS;wBACvB,UAAU,EAAE,mBAAmB;qBAChC,YAEA,KAAK,CAAC,cAAc,EAAE,GACnB,EAEL,SAAS,KAAK,SAAS,IAAI,CAC1B,cACE,KAAK,EAAE;wBACL,QAAQ,EAAE,QAAQ;wBAClB,UAAU,EAAE,GAAG;wBACf,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,uBAAuB;wBACrE,YAAY,EAAE,MAAM;wBACpB,UAAU,EAAE,kCAAkC;qBAC/C,YAEA,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,SAAS,CAAC,cAAc,EAAE,EAAE,GAClE,CACP,EAEA,CAAC,SAAS,IAAI,cAAK,KAAK,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,GAAI,EAEvD,KAAC,UAAU,IAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,2BAEhD,EAEZ,QAAQ,IAAI,CACX,cAAK,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,uBAAuB,EAAE,QAAQ,EAAE,SAAS,EAAE,YACnF,QAAQ,GACL,CACP,IACG,GACF,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact text-size toggle for game topbars. 28px square, shows A/A+/A-.
|
|
3
|
+
* Designed to fit in GameTopbar's `actions` slot alongside GameThemeToggle.
|
|
4
|
+
*/
|
|
5
|
+
export declare function GameTextSizeToggle(): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=GameTextSizeToggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameTextSizeToggle.d.ts","sourceRoot":"","sources":["../src/GameTextSizeToggle.tsx"],"names":[],"mappings":"AAsBA;;;GAGG;AACH,wBAAgB,kBAAkB,4CAqCjC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
const KEY = 'stores-text-size';
|
|
4
|
+
function getSize() {
|
|
5
|
+
if (typeof window === 'undefined')
|
|
6
|
+
return 'default';
|
|
7
|
+
const s = window.localStorage.getItem(KEY);
|
|
8
|
+
if (s === 'lg' || s === 'sm')
|
|
9
|
+
return s;
|
|
10
|
+
return 'default';
|
|
11
|
+
}
|
|
12
|
+
function apply(size) {
|
|
13
|
+
if (typeof document === 'undefined')
|
|
14
|
+
return;
|
|
15
|
+
if (size === 'default') {
|
|
16
|
+
delete document.documentElement.dataset.text;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
document.documentElement.dataset.text = size;
|
|
20
|
+
}
|
|
21
|
+
window.localStorage.setItem(KEY, size);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Compact text-size toggle for game topbars. 28px square, shows A/A+/A-.
|
|
25
|
+
* Designed to fit in GameTopbar's `actions` slot alongside GameThemeToggle.
|
|
26
|
+
*/
|
|
27
|
+
export function GameTextSizeToggle() {
|
|
28
|
+
const [size, setSize] = useState(getSize);
|
|
29
|
+
const cycle = useCallback(() => {
|
|
30
|
+
const order = ['default', 'lg', 'sm'];
|
|
31
|
+
const next = order[(order.indexOf(size) + 1) % order.length];
|
|
32
|
+
setSize(next);
|
|
33
|
+
apply(next);
|
|
34
|
+
}, [size]);
|
|
35
|
+
const label = size === 'lg' ? 'A+' : size === 'sm' ? 'A\u2212' : 'A';
|
|
36
|
+
return (_jsx("button", { onClick: cycle, "aria-label": `Text: ${size}`, title: `Text: ${size}`, style: {
|
|
37
|
+
width: 28,
|
|
38
|
+
height: 28,
|
|
39
|
+
borderRadius: 8,
|
|
40
|
+
border: '1px solid var(--line, #e2e8f0)',
|
|
41
|
+
background: 'var(--panel, #ffffff)',
|
|
42
|
+
color: 'var(--ink, #1e293b)',
|
|
43
|
+
display: 'inline-flex',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
justifyContent: 'center',
|
|
46
|
+
cursor: 'pointer',
|
|
47
|
+
padding: 0,
|
|
48
|
+
fontFamily: '"Manrope", system-ui, sans-serif',
|
|
49
|
+
fontSize: '0.7rem',
|
|
50
|
+
fontWeight: 700,
|
|
51
|
+
}, children: label }));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=GameTextSizeToggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameTextSizeToggle.js","sourceRoot":"","sources":["../src/GameTextSizeToggle.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE9C,MAAM,GAAG,GAAG,kBAAkB,CAAC;AAG/B,SAAS,OAAO;IACd,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACpD,MAAM,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IACvC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,KAAK,CAAC,IAAU;IACvB,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO;IAC5C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAC/C,CAAC;IACD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAO,OAAO,CAAC,CAAC;IAEhD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,MAAM,KAAK,GAAW,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,CAAE,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,KAAK,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;IAErE,OAAO,CACL,iBACE,OAAO,EAAE,KAAK,gBACF,SAAS,IAAI,EAAE,EAC3B,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,KAAK,EAAE;YACL,KAAK,EAAE,EAAE;YACT,MAAM,EAAE,EAAE;YACV,YAAY,EAAE,CAAC;YACf,MAAM,EAAE,gCAAgC;YACxC,UAAU,EAAE,uBAAuB;YACnC,KAAK,EAAE,qBAAqB;YAC5B,OAAO,EAAE,aAAa;YACtB,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,kCAAkC;YAC9C,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,GAAG;SAChB,YAEA,KAAK,GACC,CACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameThemeToggle.d.ts","sourceRoot":"","sources":["../src/GameThemeToggle.tsx"],"names":[],"mappings":"AA8BA;;;GAGG;AACH,wBAAgB,eAAe,4CAoE9B"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
const THEME_KEY = 'stores-theme';
|
|
4
|
+
function getStoredPref() {
|
|
5
|
+
if (typeof window === 'undefined')
|
|
6
|
+
return 'system';
|
|
7
|
+
const s = window.localStorage.getItem(THEME_KEY);
|
|
8
|
+
if (s === 'light' || s === 'dark' || s === 'system')
|
|
9
|
+
return s;
|
|
10
|
+
return 'system';
|
|
11
|
+
}
|
|
12
|
+
function resolve(pref) {
|
|
13
|
+
if (pref !== 'system')
|
|
14
|
+
return pref;
|
|
15
|
+
return typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
16
|
+
? 'dark'
|
|
17
|
+
: 'light';
|
|
18
|
+
}
|
|
19
|
+
function apply(pref) {
|
|
20
|
+
if (typeof document === 'undefined')
|
|
21
|
+
return;
|
|
22
|
+
const theme = resolve(pref);
|
|
23
|
+
if (theme === 'dark') {
|
|
24
|
+
document.documentElement.dataset.theme = 'dark';
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
delete document.documentElement.dataset.theme;
|
|
28
|
+
}
|
|
29
|
+
window.localStorage.setItem(THEME_KEY, pref);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compact theme toggle for game topbars. Sun/moon icon, 28px square.
|
|
33
|
+
* Designed to fit in GameTopbar's `actions` slot.
|
|
34
|
+
*/
|
|
35
|
+
export function GameThemeToggle() {
|
|
36
|
+
const [pref, setPref] = useState(getStoredPref);
|
|
37
|
+
const theme = resolve(pref);
|
|
38
|
+
const cycle = useCallback(() => {
|
|
39
|
+
const order = ['system', 'light', 'dark'];
|
|
40
|
+
const next = order[(order.indexOf(pref) + 1) % order.length];
|
|
41
|
+
setPref(next);
|
|
42
|
+
apply(next);
|
|
43
|
+
}, [pref]);
|
|
44
|
+
return (_jsx("button", { onClick: cycle, "aria-label": `Theme: ${pref}`, title: `Theme: ${pref}`, style: {
|
|
45
|
+
width: 28,
|
|
46
|
+
height: 28,
|
|
47
|
+
borderRadius: 8,
|
|
48
|
+
border: '1px solid var(--line, #e2e8f0)',
|
|
49
|
+
background: 'var(--panel, #ffffff)',
|
|
50
|
+
color: 'var(--ink, #1e293b)',
|
|
51
|
+
display: 'inline-flex',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
cursor: 'pointer',
|
|
55
|
+
padding: 0,
|
|
56
|
+
fontFamily: 'inherit',
|
|
57
|
+
}, children: theme === 'dark' ? (_jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("circle", { cx: "12", cy: "12", r: "5" }), _jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }), _jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }), _jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }), _jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }), _jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }), _jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }), _jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }), _jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })] })) : (_jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) })) }));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=GameThemeToggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GameThemeToggle.js","sourceRoot":"","sources":["../src/GameThemeToggle.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
2
|
+
* @freegamestore/games — shared React UI primitives for FreeGameStore games.
|
|
3
3
|
*
|
|
4
4
|
* Why this exists:
|
|
5
5
|
* - Games on the platform must be **brand-consistent** (no per-game custom
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export { GameAuth } from './GameAuth.js';
|
|
16
16
|
export { GameButton, type GameButtonProps, type GameButtonSize, type GameButtonVariant, } from './GameButton.js';
|
|
17
|
+
export { GameConfirm, type GameConfirmProps } from './GameConfirm.js';
|
|
18
|
+
export { GameModal, type GameModalProps } from './GameModal.js';
|
|
19
|
+
export { GameOverScreen, type GameOverScreenProps } from './GameOverScreen.js';
|
|
17
20
|
export { GameShell, type GameShellProps } from './GameShell.js';
|
|
21
|
+
export { GameTextSizeToggle } from './GameTextSizeToggle.js';
|
|
22
|
+
export { GameThemeToggle } from './GameThemeToggle.js';
|
|
18
23
|
export { GameTopbar, type GameTopbarProps, type GameTopbarStat, } from './GameTopbar.js';
|
|
19
24
|
export { Leaderboard, type LeaderboardProps } from './Leaderboard.js';
|
|
20
25
|
export { useSound } from './SoundContext.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,KAAK,gBAAgB,EACrB,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EACL,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EACL,KAAK,gBAAgB,EACrB,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @
|
|
2
|
+
* @freegamestore/games — shared React UI primitives for FreeGameStore games.
|
|
3
3
|
*
|
|
4
4
|
* Why this exists:
|
|
5
5
|
* - Games on the platform must be **brand-consistent** (no per-game custom
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
export { GameAuth } from './GameAuth.js';
|
|
16
16
|
export { GameButton, } from './GameButton.js';
|
|
17
|
+
export { GameConfirm } from './GameConfirm.js';
|
|
18
|
+
export { GameModal } from './GameModal.js';
|
|
19
|
+
export { GameOverScreen } from './GameOverScreen.js';
|
|
17
20
|
export { GameShell } from './GameShell.js';
|
|
21
|
+
export { GameTextSizeToggle } from './GameTextSizeToggle.js';
|
|
22
|
+
export { GameThemeToggle } from './GameThemeToggle.js';
|
|
18
23
|
export { GameTopbar, } from './GameTopbar.js';
|
|
19
24
|
export { Leaderboard } from './Leaderboard.js';
|
|
20
25
|
export { useSound } from './SoundContext.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,GAIX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,UAAU,GAGX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAa,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAEL,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,GAIX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAuB,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EACL,UAAU,GAGX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAyB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAa,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAEL,cAAc,GACf,MAAM,qBAAqB,CAAC"}
|
package/dist/useAuth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,OAAO,IAAI;IACzB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,
|
|
1
|
+
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,OAAO,IAAI;IACzB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CA+CA"}
|
package/dist/useAuth.js
CHANGED
|
@@ -4,7 +4,10 @@ export function useAuth() {
|
|
|
4
4
|
const [loading, setLoading] = useState(true);
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
let cancelled = false;
|
|
7
|
-
|
|
7
|
+
// Fire-and-forget with terminal .catch/.finally; `void` marks the non-await.
|
|
8
|
+
// Versioned endpoint (additive-only contract — see platform/docs/API-CONTRACT.md).
|
|
9
|
+
// The worker still serves the unversioned /me, so older bundles keep working.
|
|
10
|
+
void fetch('https://auth.freegamestore.online/v1/me', { credentials: 'include' })
|
|
8
11
|
.then((res) => {
|
|
9
12
|
if (!cancelled && res.ok)
|
|
10
13
|
return res.json();
|
|
@@ -29,7 +32,7 @@ export function useAuth() {
|
|
|
29
32
|
window.location.href = `https://auth.freegamestore.online/login?redirect=${encodeURIComponent(window.location.href)}`;
|
|
30
33
|
}, []);
|
|
31
34
|
const signOut = useCallback(() => {
|
|
32
|
-
fetch('https://auth.freegamestore.online/logout', {
|
|
35
|
+
void fetch('https://auth.freegamestore.online/v1/logout', {
|
|
33
36
|
method: 'POST',
|
|
34
37
|
credentials: 'include',
|
|
35
38
|
})
|
package/dist/useAuth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAQzD,MAAM,UAAU,OAAO;IAMrB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAc,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAQzD,MAAM,UAAU,OAAO;IAMrB,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAc,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,6EAA6E;QAC7E,mFAAmF;QACnF,8EAA8E;QAC9E,KAAK,KAAK,CAAC,yCAAyC,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;aAC9E,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACZ,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,EAAE;gBAAE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;YAC5C,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,IAAiB,EAAE,EAAE;YAC1B,IAAI,CAAC,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,+CAA+C;QACjD,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,SAAS;gBAAE,UAAU,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QACL,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,oDAAoD,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACxH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,KAAK,KAAK,CAAC,6CAA6C,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,WAAW,EAAE,SAAS;SACvB,CAAC;aACC,KAAK,CAAC,GAAG,EAAE;YACV,cAAc;QAChB,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC;YACd,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3B,CAAC,CAAC,CAAC;IACP,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC5C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGameSounds.d.ts","sourceRoot":"","sources":["../src/useGameSounds.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,aAAa;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"useGameSounds.d.ts","sourceRoot":"","sources":["../src/useGameSounds.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,aAAa;;;;;;;;;EAwG5B"}
|
package/dist/useGameSounds.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useRef } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { useSound } from './SoundContext.js';
|
|
3
3
|
/**
|
|
4
4
|
* Synthesized game sound effects via Web Audio API.
|
|
@@ -8,6 +8,14 @@ import { useSound } from './SoundContext.js';
|
|
|
8
8
|
export function useGameSounds() {
|
|
9
9
|
const { muted } = useSound();
|
|
10
10
|
const ctxRef = useRef(null);
|
|
11
|
+
// Release the AudioContext when the game unmounts so it isn't leaked
|
|
12
|
+
// (browsers cap concurrent contexts).
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
return () => {
|
|
15
|
+
void ctxRef.current?.close();
|
|
16
|
+
ctxRef.current = null;
|
|
17
|
+
};
|
|
18
|
+
}, []);
|
|
11
19
|
const getCtx = useCallback(() => {
|
|
12
20
|
if (muted)
|
|
13
21
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGameSounds.js","sourceRoot":"","sources":["../src/useGameSounds.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useGameSounds.js","sourceRoot":"","sources":["../src/useGameSounds.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLeaderboard.d.ts","sourceRoot":"","sources":["../src/useLeaderboard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useLeaderboard.d.ts","sourceRoot":"","sources":["../src/useLeaderboard.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAmBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;IAC9C,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,YAAY,EAAE,gBAAgB,EAAE,CAAC;IACjC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAqEA"}
|
package/dist/useLeaderboard.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
-
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
// Versioned endpoint (additive-only contract — see platform/docs/API-CONTRACT.md).
|
|
3
|
+
// The worker also serves the unversioned paths, so games built against older
|
|
4
|
+
// SDK releases keep working.
|
|
5
|
+
const API_BASE = 'https://leaderboard.freegamestore.online/v1';
|
|
3
6
|
export function useLeaderboard(gameId) {
|
|
4
7
|
const [topScores, setTopScores] = useState([]);
|
|
5
8
|
const [recentScores, setRecentScores] = useState([]);
|
|
6
9
|
const [loading, setLoading] = useState(true);
|
|
10
|
+
// Guards against setState after unmount. load() is called both from the
|
|
11
|
+
// mount effect and from submitScore(); a ref covers every caller, unlike a
|
|
12
|
+
// per-effect cancelled flag.
|
|
13
|
+
const mountedRef = useRef(true);
|
|
14
|
+
useEffect(() => () => { mountedRef.current = false; }, []);
|
|
7
15
|
const load = useCallback(() => {
|
|
8
16
|
setLoading(true);
|
|
9
|
-
|
|
17
|
+
// Fire-and-forget: inner fetches each .catch() to [], so this never
|
|
18
|
+
// rejects; `void` marks the intentional non-await.
|
|
19
|
+
void Promise.all([
|
|
10
20
|
fetch(`${API_BASE}/api/leaderboard/${gameId}?limit=50`, { credentials: 'include' })
|
|
11
21
|
.then(async (r) => {
|
|
12
22
|
if (!r.ok)
|
|
@@ -26,6 +36,8 @@ export function useLeaderboard(gameId) {
|
|
|
26
36
|
})
|
|
27
37
|
.catch(() => []),
|
|
28
38
|
]).then(([top, recent]) => {
|
|
39
|
+
if (!mountedRef.current)
|
|
40
|
+
return;
|
|
29
41
|
setTopScores(top);
|
|
30
42
|
setRecentScores(recent);
|
|
31
43
|
setLoading(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLeaderboard.js","sourceRoot":"","sources":["../src/useLeaderboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useLeaderboard.js","sourceRoot":"","sources":["../src/useLeaderboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjE,mFAAmF;AACnF,6EAA6E;AAC7E,6BAA6B;AAC7B,MAAM,QAAQ,GAAG,6CAA6C,CAAC;AA2B/D,MAAM,UAAU,cAAc,CAAC,MAAc;IAO3C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAqB,EAAE,CAAC,CAAC;IACnE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAqB,EAAE,CAAC,CAAC;IACzE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,wEAAwE;IACxE,2EAA2E;IAC3E,6BAA6B;IAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAChC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE3D,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,oEAAoE;QACpE,mDAAmD;QACnD,KAAK,OAAO,CAAC,GAAG,CAAC;YACf,KAAK,CAAC,GAAG,QAAQ,oBAAoB,MAAM,WAAW,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;iBAChF,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gBAChB,IAAI,CAAC,CAAC,CAAC,EAAE;oBAAE,OAAO,EAAwB,CAAC;gBAC3C,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAwB,CAAC;gBACrD,OAAO,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;YAC3B,CAAC,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,CAAC,EAAwB,CAAC;YACxC,KAAK,CAAC,GAAG,QAAQ,oBAAoB,MAAM,kBAAkB,EAAE;gBAC7D,WAAW,EAAE,SAAS;aACvB,CAAC;iBACC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;gBAChB,IAAI,CAAC,CAAC,CAAC,EAAE;oBAAE,OAAO,EAAwB,CAAC;gBAC3C,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAwB,CAAC;gBACrD,OAAO,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;YAC3B,CAAC,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,CAAC,EAAwB,CAAC;SACzC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE;YACxB,IAAI,CAAC,UAAU,CAAC,OAAO;gBAAE,OAAO;YAChC,YAAY,CAAC,GAAG,CAAC,CAAC;YAClB,eAAe,CAAC,MAAM,CAAC,CAAC;YACxB,UAAU,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,EAAE,CAAC;IACT,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,WAAW,GAAG,WAAW,CAC7B,KAAK,EAAE,KAAa,EAA2C,EAAE;QAC/D,IAAI,CAAC;YACH,oEAAoE;YACpE,kEAAkE;YAClE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,aAAa,EAAE;gBAChD,MAAM,EAAE,MAAM;gBACd,WAAW,EAAE,SAAS;gBACtB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;aAC9C,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;YAClC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwB,CAAC;YACvD,kCAAkC;YAClC,IAAI,EAAE,CAAC;YACP,MAAM,MAAM,GAAmC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YACzE,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;YACrD,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;QACvB,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,IAAI,CAAC,CACf,CAAC;IAEF,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1E,CAAC"}
|
package/package.json
CHANGED