@freegamestore/games 0.15.1 → 0.18.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 (91) hide show
  1. package/README.md +61 -8
  2. package/TOKENS.md +132 -0
  3. package/dist/Confetti.d.ts +20 -0
  4. package/dist/Confetti.d.ts.map +1 -0
  5. package/dist/Confetti.js +76 -0
  6. package/dist/Confetti.js.map +1 -0
  7. package/dist/GameConfirm.d.ts +1 -1
  8. package/dist/GameConfirm.d.ts.map +1 -1
  9. package/dist/GameModal.d.ts +1 -1
  10. package/dist/GameModal.d.ts.map +1 -1
  11. package/dist/GameOverScreen.d.ts +1 -1
  12. package/dist/GameOverScreen.d.ts.map +1 -1
  13. package/dist/GameResultScreen.d.ts +46 -0
  14. package/dist/GameResultScreen.d.ts.map +1 -0
  15. package/dist/GameResultScreen.js +157 -0
  16. package/dist/GameResultScreen.js.map +1 -0
  17. package/dist/GameTextSizeToggle.d.ts +1 -1
  18. package/dist/GameTextSizeToggle.d.ts.map +1 -1
  19. package/dist/GameThemeToggle.d.ts +1 -1
  20. package/dist/GameThemeToggle.d.ts.map +1 -1
  21. package/dist/GameThemeToggle.js +43 -7
  22. package/dist/GameThemeToggle.js.map +1 -1
  23. package/dist/GameTopbar.d.ts +19 -7
  24. package/dist/GameTopbar.d.ts.map +1 -1
  25. package/dist/GameTopbar.js +113 -168
  26. package/dist/GameTopbar.js.map +1 -1
  27. package/dist/GameTopbar.module.css +201 -0
  28. package/dist/StartScreen.d.ts +48 -0
  29. package/dist/StartScreen.d.ts.map +1 -0
  30. package/dist/StartScreen.js +159 -0
  31. package/dist/StartScreen.js.map +1 -0
  32. package/dist/feel/ScorePopup.d.ts +25 -0
  33. package/dist/feel/ScorePopup.d.ts.map +1 -0
  34. package/dist/feel/ScorePopup.js +67 -0
  35. package/dist/feel/ScorePopup.js.map +1 -0
  36. package/dist/feel/easing.d.ts +14 -0
  37. package/dist/feel/easing.d.ts.map +1 -0
  38. package/dist/feel/easing.js +12 -0
  39. package/dist/feel/easing.js.map +1 -0
  40. package/dist/feel/haptics.d.ts +19 -0
  41. package/dist/feel/haptics.d.ts.map +1 -0
  42. package/dist/feel/haptics.js +24 -0
  43. package/dist/feel/haptics.js.map +1 -0
  44. package/dist/feel/index.d.ts +17 -0
  45. package/dist/feel/index.d.ts.map +1 -0
  46. package/dist/feel/index.js +17 -0
  47. package/dist/feel/index.js.map +1 -0
  48. package/dist/feel/particles.d.ts +46 -0
  49. package/dist/feel/particles.d.ts.map +1 -0
  50. package/dist/feel/particles.js +61 -0
  51. package/dist/feel/particles.js.map +1 -0
  52. package/dist/feel/reducedMotion.d.ts +6 -0
  53. package/dist/feel/reducedMotion.d.ts.map +1 -0
  54. package/dist/feel/reducedMotion.js +10 -0
  55. package/dist/feel/reducedMotion.js.map +1 -0
  56. package/dist/feel/shake.d.ts +6 -0
  57. package/dist/feel/shake.d.ts.map +1 -0
  58. package/dist/feel/shake.js +17 -0
  59. package/dist/feel/shake.js.map +1 -0
  60. package/dist/feel/tween.d.ts +9 -0
  61. package/dist/feel/tween.d.ts.map +1 -0
  62. package/dist/feel/tween.js +15 -0
  63. package/dist/feel/tween.js.map +1 -0
  64. package/dist/feel/useHaptics.d.ts +12 -0
  65. package/dist/feel/useHaptics.d.ts.map +1 -0
  66. package/dist/feel/useHaptics.js +16 -0
  67. package/dist/feel/useHaptics.js.map +1 -0
  68. package/dist/feel/useParticles.d.ts +18 -0
  69. package/dist/feel/useParticles.d.ts.map +1 -0
  70. package/dist/feel/useParticles.js +79 -0
  71. package/dist/feel/useParticles.js.map +1 -0
  72. package/dist/feel/useShake.d.ts +14 -0
  73. package/dist/feel/useShake.d.ts.map +1 -0
  74. package/dist/feel/useShake.js +17 -0
  75. package/dist/feel/useShake.js.map +1 -0
  76. package/dist/index.d.ts +5 -2
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js +14 -2
  79. package/dist/index.js.map +1 -1
  80. package/dist/tokens.d.ts +112 -0
  81. package/dist/tokens.d.ts.map +1 -0
  82. package/dist/tokens.js +106 -0
  83. package/dist/tokens.js.map +1 -0
  84. package/dist/useGameSounds.d.ts.map +1 -1
  85. package/dist/useGameSounds.js +37 -15
  86. package/dist/useGameSounds.js.map +1 -1
  87. package/dist/useLeaderboard.d.ts +8 -1
  88. package/dist/useLeaderboard.d.ts.map +1 -1
  89. package/dist/useLeaderboard.js +21 -11
  90. package/dist/useLeaderboard.js.map +1 -1
  91. package/package.json +21 -6
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Particle bursts: a pure model with no DOM or canvas, so it can be tested on its own
3
+ * and drawn by any renderer. `useParticles` draws it to a canvas.
4
+ */
5
+ /** One particle. Positions and sizes are in CSS pixels, times in seconds. */
6
+ export interface Particle {
7
+ x: number;
8
+ y: number;
9
+ vx: number;
10
+ vy: number;
11
+ size: number;
12
+ colour: string;
13
+ age: number;
14
+ life: number;
15
+ }
16
+ export interface BurstOptions {
17
+ /** How many particles to emit. */
18
+ count: number;
19
+ /** Launch speed range, in px per second. */
20
+ speed: readonly [number, number];
21
+ /** Square size range, in px. */
22
+ size: readonly [number, number];
23
+ /** Seconds a particle lives. Each one varies by up to a quarter. */
24
+ life: number;
25
+ /** Centre of the launch arc, in radians. 0 points right and π/2 points down. Default 0. */
26
+ angle?: number;
27
+ /** Width of the launch arc, in radians. Default 2π, which is a full radial burst. */
28
+ spread?: number;
29
+ }
30
+ /** Downward pull, in px per second squared. */
31
+ export declare const GRAVITY = 900;
32
+ /** Upper bound on live particles, so a burst on a slow device cannot pile up. */
33
+ export declare const MAX_PARTICLES = 160;
34
+ /**
35
+ * Emit a burst of `options.count` particles from (x, y). Colours are used in
36
+ * turn. `random` returns values in [0, 1), and tests can pass a fixed one.
37
+ */
38
+ export declare function createBurst(x: number, y: number, colours: readonly string[], options: BurstOptions, random?: () => number): Particle[];
39
+ /**
40
+ * Advance every particle by `dt` seconds, drop the ones past their life, and
41
+ * return the survivors. Works in place, so a frame allocates nothing.
42
+ */
43
+ export declare function stepParticles(particles: Particle[], dt: number, gravity?: number): Particle[];
44
+ /** Opacity for a particle: fully opaque when born, fading to nothing at the end of its life. */
45
+ export declare function particleAlpha(particle: Particle): number;
46
+ //# sourceMappingURL=particles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particles.d.ts","sourceRoot":"","sources":["../../src/feel/particles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6EAA6E;AAC7E,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,gCAAgC;IAChC,IAAI,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,+CAA+C;AAC/C,eAAO,MAAM,OAAO,MAAM,CAAC;AAE3B,iFAAiF;AACjF,eAAO,MAAM,aAAa,MAAM,CAAC;AAQjC;;;GAGG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,YAAY,EACrB,MAAM,GAAE,MAAM,MAAoB,GACjC,QAAQ,EAAE,CAqBZ;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,SAAU,GAAG,QAAQ,EAAE,CAY9F;AAED,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAExD"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Particle bursts: a pure model with no DOM or canvas, so it can be tested on its own
3
+ * and drawn by any renderer. `useParticles` draws it to a canvas.
4
+ */
5
+ /** Downward pull, in px per second squared. */
6
+ export const GRAVITY = 900;
7
+ /** Upper bound on live particles, so a burst on a slow device cannot pile up. */
8
+ export const MAX_PARTICLES = 160;
9
+ const FALLBACK_COLOUR = '#888888';
10
+ function lerp(a, b, t) {
11
+ return a + (b - a) * t;
12
+ }
13
+ /**
14
+ * Emit a burst of `options.count` particles from (x, y). Colours are used in
15
+ * turn. `random` returns values in [0, 1), and tests can pass a fixed one.
16
+ */
17
+ export function createBurst(x, y, colours, options, random = Math.random) {
18
+ if (colours.length === 0)
19
+ return [];
20
+ const angle = options.angle ?? 0;
21
+ const spread = options.spread ?? Math.PI * 2;
22
+ const particles = [];
23
+ for (let i = 0; i < options.count; i++) {
24
+ const direction = angle + (random() - 0.5) * spread;
25
+ const speed = lerp(options.speed[0], options.speed[1], random());
26
+ particles.push({
27
+ x,
28
+ y,
29
+ vx: Math.cos(direction) * speed,
30
+ vy: Math.sin(direction) * speed,
31
+ size: lerp(options.size[0], options.size[1], random()),
32
+ colour: colours[i % colours.length] ?? FALLBACK_COLOUR,
33
+ age: 0,
34
+ life: options.life * (0.75 + 0.25 * random()),
35
+ });
36
+ }
37
+ return particles;
38
+ }
39
+ /**
40
+ * Advance every particle by `dt` seconds, drop the ones past their life, and
41
+ * return the survivors. Works in place, so a frame allocates nothing.
42
+ */
43
+ export function stepParticles(particles, dt, gravity = GRAVITY) {
44
+ let alive = 0;
45
+ for (const particle of particles) {
46
+ particle.age += dt;
47
+ if (particle.age >= particle.life)
48
+ continue;
49
+ particle.vy += gravity * dt;
50
+ particle.x += particle.vx * dt;
51
+ particle.y += particle.vy * dt;
52
+ particles[alive++] = particle;
53
+ }
54
+ particles.length = alive;
55
+ return particles;
56
+ }
57
+ /** Opacity for a particle: fully opaque when born, fading to nothing at the end of its life. */
58
+ export function particleAlpha(particle) {
59
+ return Math.max(0, 1 - particle.age / particle.life);
60
+ }
61
+ //# sourceMappingURL=particles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"particles.js","sourceRoot":"","sources":["../../src/feel/particles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6BH,+CAA+C;AAC/C,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC;AAE3B,iFAAiF;AACjF,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,MAAM,eAAe,GAAG,SAAS,CAAC;AAElC,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IAC3C,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,CAAS,EACT,CAAS,EACT,OAA0B,EAC1B,OAAqB,EACrB,SAAuB,IAAI,CAAC,MAAM;IAElC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAe,EAAE,CAAC;IAEjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,KAAK,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACjE,SAAS,CAAC,IAAI,CAAC;YACb,CAAC;YACD,CAAC;YACD,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK;YAC/B,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK;YAC/B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;YACtD,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,eAAe;YACtD,GAAG,EAAE,CAAC;YACN,IAAI,EAAE,OAAO,CAAC,IAAI,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;SAC9C,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAqB,EAAE,EAAU,EAAE,OAAO,GAAG,OAAO;IAChF,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,QAAQ,CAAC,GAAG,IAAI,EAAE,CAAC;QACnB,IAAI,QAAQ,CAAC,GAAG,IAAI,QAAQ,CAAC,IAAI;YAAE,SAAS;QAC5C,QAAQ,CAAC,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC;QAC5B,QAAQ,CAAC,CAAC,IAAI,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QAC/B,QAAQ,CAAC,CAAC,IAAI,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QAC/B,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC;IAChC,CAAC;IACD,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,aAAa,CAAC,QAAkB;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * True when the player has asked the system for less motion. Safe to call where
3
+ * `matchMedia` is missing (server render, jsdom, old browsers), where it returns false.
4
+ */
5
+ export declare function prefersReducedMotion(): boolean;
6
+ //# sourceMappingURL=reducedMotion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducedMotion.d.ts","sourceRoot":"","sources":["../../src/feel/reducedMotion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAI9C"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * True when the player has asked the system for less motion. Safe to call where
3
+ * `matchMedia` is missing (server render, jsdom, old browsers), where it returns false.
4
+ */
5
+ export function prefersReducedMotion() {
6
+ return typeof window !== 'undefined' && typeof window.matchMedia === 'function'
7
+ ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
8
+ : false;
9
+ }
10
+ //# sourceMappingURL=reducedMotion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducedMotion.js","sourceRoot":"","sources":["../../src/feel/reducedMotion.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU;QAC7E,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO;QAC/D,CAAC,CAAC,KAAK,CAAC;AACZ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Keyframes for a short, decaying shake: a few side-to-side swings that shrink to rest.
3
+ * Pure, so the motion can be checked on its own and passed to `element.animate`.
4
+ */
5
+ export declare function shakeKeyframes(amplitude: number): Keyframe[];
6
+ //# sourceMappingURL=shake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shake.d.ts","sourceRoot":"","sources":["../../src/feel/shake.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,EAAE,CAW5D"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Keyframes for a short, decaying shake: a few side-to-side swings that shrink to rest.
3
+ * Pure, so the motion can be checked on its own and passed to `element.animate`.
4
+ */
5
+ export function shakeKeyframes(amplitude) {
6
+ const a = amplitude;
7
+ return [
8
+ { transform: 'translate3d(0, 0, 0)', offset: 0 },
9
+ { transform: `translate3d(${-a}px, 0, 0)`, offset: 0.15 },
10
+ { transform: `translate3d(${a * 0.8}px, 0, 0)`, offset: 0.3 },
11
+ { transform: `translate3d(${-a * 0.6}px, 0, 0)`, offset: 0.45 },
12
+ { transform: `translate3d(${a * 0.4}px, 0, 0)`, offset: 0.6 },
13
+ { transform: `translate3d(${-a * 0.2}px, 0, 0)`, offset: 0.8 },
14
+ { transform: 'translate3d(0, 0, 0)', offset: 1 },
15
+ ];
16
+ }
17
+ //# sourceMappingURL=shake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shake.js","sourceRoot":"","sources":["../../src/feel/shake.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,CAAC,GAAG,SAAS,CAAC;IACpB,OAAO;QACL,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,EAAE,CAAC,EAAE;QAChD,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE;QACzD,EAAE,SAAS,EAAE,eAAe,CAAC,GAAG,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE;QAC7D,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,GAAG,GAAG,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE;QAC/D,EAAE,SAAS,EAAE,eAAe,CAAC,GAAG,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE;QAC7D,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,GAAG,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE;QAC9D,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,EAAE,CAAC,EAAE;KACjD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type Easing } from './easing.js';
2
+ /** Progress through a tween, from 0 to 1. Clamped, so a zero duration or a time past the end is safe. */
3
+ export declare function tweenProgress(elapsed: number, duration: number): number;
4
+ /**
5
+ * The value of a tween `elapsed` ms into a move from `from` to `to` over `duration` ms.
6
+ * Pure: the caller keeps the clock and decides when to draw the next frame.
7
+ */
8
+ export declare function tweenValue(from: number, to: number, elapsed: number, duration: number, easing?: Easing): number;
9
+ //# sourceMappingURL=tween.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tween.d.ts","sourceRoot":"","sources":["../../src/feel/tween.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAe,MAAM,aAAa,CAAC;AAEvD,yGAAyG;AACzG,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGvE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAoB,GAC3B,MAAM,CAER"}
@@ -0,0 +1,15 @@
1
+ import { easeOutQuad } from './easing.js';
2
+ /** Progress through a tween, from 0 to 1. Clamped, so a zero duration or a time past the end is safe. */
3
+ export function tweenProgress(elapsed, duration) {
4
+ if (duration <= 0)
5
+ return 1;
6
+ return Math.min(1, Math.max(0, elapsed / duration));
7
+ }
8
+ /**
9
+ * The value of a tween `elapsed` ms into a move from `from` to `to` over `duration` ms.
10
+ * Pure: the caller keeps the clock and decides when to draw the next frame.
11
+ */
12
+ export function tweenValue(from, to, elapsed, duration, easing = easeOutQuad) {
13
+ return from + (to - from) * easing(tweenProgress(elapsed, duration));
14
+ }
15
+ //# sourceMappingURL=tween.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tween.js","sourceRoot":"","sources":["../../src/feel/tween.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,yGAAyG;AACzG,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,QAAgB;IAC7D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,EAAU,EACV,OAAe,EACf,QAAgB,EAChB,SAAiB,WAAW;IAE5B,OAAO,IAAI,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;AACvE,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type VibrationPattern } from './haptics.js';
2
+ export interface Haptics {
3
+ /** Vibrate with a pattern. Silent when haptics are off or unsupported. */
4
+ pulse(pattern: VibrationPattern): void;
5
+ }
6
+ /**
7
+ * Haptic pulses for a game. Haptics are not visual motion, so reduced motion does
8
+ * not switch them off. Pass `enabled: false` to mute them, for example from a
9
+ * settings toggle. The returned object is stable while `enabled` is unchanged.
10
+ */
11
+ export declare function useHaptics(enabled?: boolean): Haptics;
12
+ //# sourceMappingURL=useHaptics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHaptics.d.ts","sourceRoot":"","sources":["../../src/feel/useHaptics.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAW,MAAM,cAAc,CAAC;AAE9D,MAAM,WAAW,OAAO;IACtB,0EAA0E;IAC1E,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,UAAO,GAAG,OAAO,CASlD"}
@@ -0,0 +1,16 @@
1
+ import { useMemo } from 'react';
2
+ import { vibrate } from './haptics.js';
3
+ /**
4
+ * Haptic pulses for a game. Haptics are not visual motion, so reduced motion does
5
+ * not switch them off. Pass `enabled: false` to mute them, for example from a
6
+ * settings toggle. The returned object is stable while `enabled` is unchanged.
7
+ */
8
+ export function useHaptics(enabled = true) {
9
+ return useMemo(() => ({
10
+ pulse(pattern) {
11
+ if (enabled)
12
+ vibrate(pattern);
13
+ },
14
+ }), [enabled]);
15
+ }
16
+ //# sourceMappingURL=useHaptics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useHaptics.js","sourceRoot":"","sources":["../../src/feel/useHaptics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAyB,OAAO,EAAE,MAAM,cAAc,CAAC;AAO9D;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAO,GAAG,IAAI;IACvC,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC;QACL,KAAK,CAAC,OAAO;YACX,IAAI,OAAO;gBAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;KACF,CAAC,EACF,CAAC,OAAO,CAAC,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { type RefObject } from 'react';
2
+ import { type BurstOptions } from './particles.js';
3
+ export interface ParticleEmitter {
4
+ /**
5
+ * Launch a burst from (x, y), in the canvas's own CSS-pixel space. Does nothing
6
+ * under reduced motion, or before the canvas is mounted.
7
+ */
8
+ burst(x: number, y: number, colours: readonly string[], options: BurstOptions): void;
9
+ }
10
+ /**
11
+ * Particle bursts drawn to a full-size canvas. Pass the ref of an `<canvas>`
12
+ * that is positioned over the play area and has `pointer-events: none`. The
13
+ * frame loop runs only while particles are alive, so an idle board costs
14
+ * nothing. The live count is written to `data-particles`, so tests can read it
15
+ * without a re-render per frame.
16
+ */
17
+ export declare function useParticles(canvasRef: RefObject<HTMLCanvasElement | null>): ParticleEmitter;
18
+ //# sourceMappingURL=useParticles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useParticles.d.ts","sourceRoot":"","sources":["../../src/feel/useParticles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAA8B,MAAM,OAAO,CAAC;AACnE,OAAO,EACL,KAAK,YAAY,EAMlB,MAAM,gBAAgB,CAAC;AASxB,MAAM,WAAW,eAAe;IAC9B;;;OAGG;IACH,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;CACtF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,GAAG,eAAe,CA2C5F"}
@@ -0,0 +1,79 @@
1
+ import { useEffect, useMemo, useRef } from 'react';
2
+ import { createBurst, MAX_PARTICLES, particleAlpha, stepParticles, } from './particles.js';
3
+ import { prefersReducedMotion } from './reducedMotion.js';
4
+ /** Highest device pixel ratio used for the canvas. Higher ratios cost fill time for no visible gain. */
5
+ const MAX_DPR = 2;
6
+ /** Longest step a single frame may take, in seconds, so a tab switch does not fling particles across the screen. */
7
+ const MAX_FRAME_DT = 0.1;
8
+ /**
9
+ * Particle bursts drawn to a full-size canvas. Pass the ref of an `<canvas>`
10
+ * that is positioned over the play area and has `pointer-events: none`. The
11
+ * frame loop runs only while particles are alive, so an idle board costs
12
+ * nothing. The live count is written to `data-particles`, so tests can read it
13
+ * without a re-render per frame.
14
+ */
15
+ export function useParticles(canvasRef) {
16
+ const particlesRef = useRef([]);
17
+ const frameRef = useRef(0);
18
+ const lastRef = useRef(null);
19
+ const runningRef = useRef(false);
20
+ useEffect(() => () => {
21
+ cancelAnimationFrame(frameRef.current);
22
+ runningRef.current = false;
23
+ }, []);
24
+ return useMemo(() => {
25
+ const frame = (now) => {
26
+ // The first frame after a burst has no previous time to measure from, so it takes no step.
27
+ const last = lastRef.current;
28
+ const dt = last === null ? 0 : Math.min(Math.max((now - last) / 1000, 0), MAX_FRAME_DT);
29
+ lastRef.current = now;
30
+ const particles = stepParticles(particlesRef.current, dt);
31
+ const canvas = canvasRef.current;
32
+ if (canvas)
33
+ drawFrame(canvas, particles);
34
+ if (particles.length === 0) {
35
+ runningRef.current = false;
36
+ return;
37
+ }
38
+ frameRef.current = requestAnimationFrame(frame);
39
+ };
40
+ return {
41
+ burst(x, y, colours, options) {
42
+ if (!canvasRef.current || prefersReducedMotion())
43
+ return;
44
+ const particles = particlesRef.current;
45
+ particles.push(...createBurst(x, y, colours, options));
46
+ if (particles.length > MAX_PARTICLES)
47
+ particles.splice(0, particles.length - MAX_PARTICLES);
48
+ if (runningRef.current)
49
+ return;
50
+ runningRef.current = true;
51
+ lastRef.current = null;
52
+ frameRef.current = requestAnimationFrame(frame);
53
+ },
54
+ };
55
+ }, [canvasRef]);
56
+ }
57
+ /** Match the backing store to the CSS size, scaled for the screen, then draw one frame. */
58
+ function drawFrame(canvas, particles) {
59
+ canvas.dataset.particles = String(particles.length);
60
+ const dpr = Math.min(window.devicePixelRatio || 1, MAX_DPR);
61
+ const width = canvas.clientWidth;
62
+ const height = canvas.clientHeight;
63
+ if (canvas.width !== Math.round(width * dpr) || canvas.height !== Math.round(height * dpr)) {
64
+ canvas.width = Math.round(width * dpr);
65
+ canvas.height = Math.round(height * dpr);
66
+ }
67
+ const ctx = canvas.getContext('2d');
68
+ if (!ctx)
69
+ return;
70
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
71
+ ctx.clearRect(0, 0, width, height);
72
+ for (const particle of particles) {
73
+ ctx.globalAlpha = particleAlpha(particle);
74
+ ctx.fillStyle = particle.colour;
75
+ ctx.fillRect(particle.x - particle.size / 2, particle.y - particle.size / 2, particle.size, particle.size);
76
+ }
77
+ ctx.globalAlpha = 1;
78
+ }
79
+ //# sourceMappingURL=useParticles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useParticles.js","sourceRoot":"","sources":["../../src/feel/useParticles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACnE,OAAO,EAEL,WAAW,EACX,aAAa,EAEb,aAAa,EACb,aAAa,GACd,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,wGAAwG;AACxG,MAAM,OAAO,GAAG,CAAC,CAAC;AAElB,oHAAoH;AACpH,MAAM,YAAY,GAAG,GAAG,CAAC;AAUzB;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,SAA8C;IACzE,MAAM,YAAY,GAAG,MAAM,CAAa,EAAE,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEjC,SAAS,CACP,GAAG,EAAE,CAAC,GAAG,EAAE;QACT,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACvC,UAAU,CAAC,OAAO,GAAG,KAAK,CAAC;IAC7B,CAAC,EACD,EAAE,CACH,CAAC;IAEF,OAAO,OAAO,CAAkB,GAAG,EAAE;QACnC,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE;YAC5B,2FAA2F;YAC3F,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC;YAC7B,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;YACxF,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC;YACtB,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,CAAC;YACjC,IAAI,MAAM;gBAAE,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACzC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC3B,UAAU,CAAC,OAAO,GAAG,KAAK,CAAC;gBAC3B,OAAO;YACT,CAAC;YACD,QAAQ,CAAC,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAClD,CAAC,CAAC;QAEF,OAAO;YACL,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO;gBAC1B,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,oBAAoB,EAAE;oBAAE,OAAO;gBACzD,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;gBACvC,SAAS,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;gBACvD,IAAI,SAAS,CAAC,MAAM,GAAG,aAAa;oBAAE,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC;gBAC5F,IAAI,UAAU,CAAC,OAAO;oBAAE,OAAO;gBAC/B,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC;gBAC1B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;gBACvB,QAAQ,CAAC,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAClD,CAAC;SACF,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,2FAA2F;AAC3F,SAAS,SAAS,CAAC,MAAyB,EAAE,SAA8B;IAC1E,MAAM,CAAC,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC;IACnC,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;QAC3F,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,GAAG;QAAE,OAAO;IACjB,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACvC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,GAAG,CAAC,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC1C,GAAG,CAAC,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC;QAChC,GAAG,CAAC,QAAQ,CACV,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,EAC9B,QAAQ,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,EAC9B,QAAQ,CAAC,IAAI,EACb,QAAQ,CAAC,IAAI,CACd,CAAC;IACJ,CAAC;IACD,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC;AACtB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { type RefObject } from 'react';
2
+ export interface ShakeOptions {
3
+ /** Peak offset in px. Keep it small: a few pixels reads as impact, more reads as a glitch. */
4
+ amplitude?: number;
5
+ /** Length of the shake in ms. */
6
+ duration?: number;
7
+ }
8
+ /**
9
+ * A function that shakes the element behind `ref`. It uses the Web Animations API,
10
+ * so nothing re-renders and the animation runs on the compositor. It does nothing
11
+ * under reduced motion, or where `element.animate` is missing.
12
+ */
13
+ export declare function useShake<T extends HTMLElement>(ref: RefObject<T | null>, { amplitude, duration }?: ShakeOptions): () => void;
14
+ //# sourceMappingURL=useShake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShake.d.ts","sourceRoot":"","sources":["../../src/feel/useShake.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,OAAO,CAAC;AAIpD,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,WAAW,EAC5C,GAAG,EAAE,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,EACxB,EAAE,SAAa,EAAE,QAAc,EAAE,GAAE,YAAiB,GACnD,MAAM,IAAI,CAMZ"}
@@ -0,0 +1,17 @@
1
+ import { useCallback } from 'react';
2
+ import { prefersReducedMotion } from './reducedMotion.js';
3
+ import { shakeKeyframes } from './shake.js';
4
+ /**
5
+ * A function that shakes the element behind `ref`. It uses the Web Animations API,
6
+ * so nothing re-renders and the animation runs on the compositor. It does nothing
7
+ * under reduced motion, or where `element.animate` is missing.
8
+ */
9
+ export function useShake(ref, { amplitude = 4, duration = 320 } = {}) {
10
+ return useCallback(() => {
11
+ const element = ref.current;
12
+ if (!element || prefersReducedMotion() || typeof element.animate !== 'function')
13
+ return;
14
+ element.animate(shakeKeyframes(amplitude), { duration, easing: 'ease-out' });
15
+ }, [ref, amplitude, duration]);
16
+ }
17
+ //# sourceMappingURL=useShake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShake.js","sourceRoot":"","sources":["../../src/feel/useShake.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,WAAW,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAS5C;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CACtB,GAAwB,EACxB,EAAE,SAAS,GAAG,CAAC,EAAE,QAAQ,GAAG,GAAG,KAAmB,EAAE;IAEpD,OAAO,WAAW,CAAC,GAAG,EAAE;QACtB,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,oBAAoB,EAAE,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU;YAAE,OAAO;QACxF,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;IAC/E,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;AACjC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -12,18 +12,21 @@
12
12
  * What you get:
13
13
  * <GameShell topbar={<GameTopbar score={42} />}>{your game}</GameShell>
14
14
  */
15
+ export { CONFETTI_COLOURS, CONFETTI_CSS, Confetti, type ConfettiProps, } from './Confetti.js';
16
+ export * from './feel/index.js';
15
17
  export { GameAuth } from './GameAuth.js';
16
18
  export { GameButton, type GameButtonProps, type GameButtonSize, type GameButtonVariant, } from './GameButton.js';
17
19
  export { GameConfirm, type GameConfirmProps } from './GameConfirm.js';
18
20
  export { GameModal, type GameModalProps } from './GameModal.js';
19
21
  export { GameOverScreen, type GameOverScreenProps } from './GameOverScreen.js';
22
+ export { GameResultScreen, type GameResultScreenProps, type GameResultStat, RESULT_SCREEN_CSS, } from './GameResultScreen.js';
20
23
  export { GameShell, type GameShellProps } from './GameShell.js';
21
24
  export { GameTextSizeToggle } from './GameTextSizeToggle.js';
22
25
  export { GameThemeToggle } from './GameThemeToggle.js';
23
26
  export { GameTopbar, type GameTopbarProps, type GameTopbarStat, } from './GameTopbar.js';
24
- export { Leaderboard, type LeaderboardProps } from './Leaderboard.js';
25
27
  export { useSound } from './SoundContext.js';
28
+ export { START_SCREEN_CSS, StartScreen, type StartScreenProps, type StartScreenStep, } from './StartScreen.js';
29
+ export { type ColourTokens, DARK_COLOUR_OVERRIDES, DARK_COLOURS, GAME_TOKENS_CSS, LIGHT_COLOURS, MOTION_TOKENS, type MotionTokens, REDUCED_MOTION_DURATIONS, SHAPE_TOKENS, type ShapeTokens, TYPOGRAPHY_TOKENS, type TypographyTokens, } from './tokens.js';
26
30
  export { type User, useAuth } from './useAuth.js';
27
31
  export { useGameSounds } from './useGameSounds.js';
28
- export { type LeaderboardEntry, useLeaderboard, } from './useLeaderboard.js';
29
32
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,KAAK,aAAa,GACnB,MAAM,eAAe,CAAC;AACvB,cAAc,iBAAiB,CAAC;AAChC,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,EACL,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,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;AAKzB,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,KAAK,gBAAgB,EACrB,KAAK,eAAe,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,YAAY,EACjB,qBAAqB,EACrB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,KAAK,YAAY,EACjB,wBAAwB,EACxB,YAAY,EACZ,KAAK,WAAW,EAChB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -12,18 +12,30 @@
12
12
  * What you get:
13
13
  * <GameShell topbar={<GameTopbar score={42} />}>{your game}</GameShell>
14
14
  */
15
+ export { CONFETTI_COLOURS, CONFETTI_CSS, Confetti, } from './Confetti.js';
16
+ export * from './feel/index.js';
15
17
  export { GameAuth } from './GameAuth.js';
16
18
  export { GameButton, } from './GameButton.js';
17
19
  export { GameConfirm } from './GameConfirm.js';
18
20
  export { GameModal } from './GameModal.js';
19
21
  export { GameOverScreen } from './GameOverScreen.js';
22
+ export { GameResultScreen, RESULT_SCREEN_CSS, } from './GameResultScreen.js';
20
23
  export { GameShell } from './GameShell.js';
21
24
  export { GameTextSizeToggle } from './GameTextSizeToggle.js';
22
25
  export { GameThemeToggle } from './GameThemeToggle.js';
23
26
  export { GameTopbar, } from './GameTopbar.js';
24
- export { Leaderboard } from './Leaderboard.js';
27
+ // Hidden: leaderboard not currently surfaced to users (issue #31) — remove the
28
+ // comment markers to re-enable. Leaderboard.tsx and useLeaderboard.ts are left
29
+ // intact on purpose, as is the workers/leaderboard API and its data.
30
+ // export { Leaderboard, type LeaderboardProps } from './Leaderboard.js';
25
31
  export { useSound } from './SoundContext.js';
32
+ export { START_SCREEN_CSS, StartScreen, } from './StartScreen.js';
33
+ export { DARK_COLOUR_OVERRIDES, DARK_COLOURS, GAME_TOKENS_CSS, LIGHT_COLOURS, MOTION_TOKENS, REDUCED_MOTION_DURATIONS, SHAPE_TOKENS, TYPOGRAPHY_TOKENS, } from './tokens.js';
26
34
  export { useAuth } from './useAuth.js';
27
35
  export { useGameSounds } from './useGameSounds.js';
28
- export { useLeaderboard, } from './useLeaderboard.js';
36
+ // Hidden: see the note on Leaderboard above (issue #31).
37
+ // export {
38
+ // type LeaderboardEntry,
39
+ // useLeaderboard,
40
+ // } from './useLeaderboard.js';
29
41
  //# sourceMappingURL=index.js.map
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,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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,GAET,MAAM,eAAe,CAAC;AACvB,cAAc,iBAAiB,CAAC;AAChC,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,EACL,gBAAgB,EAGhB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAC/B,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,+EAA+E;AAC/E,+EAA+E;AAC/E,qEAAqE;AACrE,yEAAyE;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EACL,gBAAgB,EAChB,WAAW,GAGZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,qBAAqB,EACrB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EAEb,wBAAwB,EACxB,YAAY,EAEZ,iBAAiB,GAElB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAa,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,yDAAyD;AACzD,WAAW;AACX,2BAA2B;AAC3B,oBAAoB;AACpB,gCAAgC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Brand, typography and motion design tokens for FreeGameStore games.
3
+ *
4
+ * These values are lifted from the Mosaic Rush pilot (`web/src/index.css`),
5
+ * which is the first game to be built on them. They are the single definition
6
+ * the SDK documents. See TOKENS.md for the full reference.
7
+ *
8
+ * The SDK components read these as CSS custom properties (`var(--paper)` and
9
+ * so on), with their own fallbacks. This module does not inject anything on
10
+ * import (the package is `sideEffects: false`). A game that wants the tokens
11
+ * adds GAME_TOKENS_CSS to its stylesheet, or copies it into index.css.
12
+ *
13
+ * Changing a value here changes every game that adopts the tokens, so treat
14
+ * it as a platform-wide decision.
15
+ */
16
+ /** Colour tokens. Each key becomes `--<kebab-case>`, for example `lineStrong` becomes `--line-strong`. */
17
+ export interface ColourTokens {
18
+ /** Page and board background. */
19
+ paper: string;
20
+ /** Primary text. */
21
+ ink: string;
22
+ /** Secondary text and captions. */
23
+ muted: string;
24
+ /** Hairline borders on empty surfaces. */
25
+ line: string;
26
+ /** Borders on buttons and toggles. */
27
+ lineStrong: string;
28
+ /** Raised cards and overlays. */
29
+ panel: string;
30
+ /** Translucent surface, for frosted overlays. */
31
+ glass: string;
32
+ /** Bottom dock surface. */
33
+ dock: string;
34
+ /** Status: success. */
35
+ success: string;
36
+ /** Status: warning. */
37
+ warning: string;
38
+ /** Status: error. */
39
+ error: string;
40
+ /** Primary actions and highlights. */
41
+ accent: string;
42
+ /** Quiet fill behind accent text, for badges and tints. */
43
+ accentSoft: string;
44
+ /** Accent used as text (titles, badges). Lighter in dark mode so it passes AA on dark surfaces. */
45
+ accentInk: string;
46
+ }
47
+ /** Light theme. The default when `data-theme` is absent. */
48
+ export declare const LIGHT_COLOURS: ColourTokens;
49
+ /**
50
+ * Only the tokens that differ in dark mode. Applied under `:root[data-theme="dark"]`.
51
+ * `accent` is not listed: it is the same in both themes.
52
+ */
53
+ export declare const DARK_COLOUR_OVERRIDES: Partial<ColourTokens>;
54
+ /** Dark theme, resolved: the light values with the dark overrides applied. */
55
+ export declare const DARK_COLOURS: ColourTokens;
56
+ /** Typography tokens. Only display headings and score values use the display face. */
57
+ export interface TypographyTokens {
58
+ /** Display face for titles and score values. */
59
+ fontDisplay: string;
60
+ /** Body face for everything else. */
61
+ fontBody: string;
62
+ /** Score and stat values. */
63
+ textStat: string;
64
+ /** Overlay titles. */
65
+ textTitle: string;
66
+ /** The start-screen title. */
67
+ textDisplay: string;
68
+ }
69
+ export declare const TYPOGRAPHY_TOKENS: TypographyTokens;
70
+ /** Shape tokens. */
71
+ export interface ShapeTokens {
72
+ /** Corner radius for cards and overlays. */
73
+ radius: string;
74
+ }
75
+ export declare const SHAPE_TOKENS: ShapeTokens;
76
+ /** Motion tokens. Durations and easings every animation reads. */
77
+ export interface MotionTokens {
78
+ /** Quick feedback, such as a press or a pop. */
79
+ fast: string;
80
+ /** Standard transitions. */
81
+ base: string;
82
+ /** Larger entrances and overlays. */
83
+ slow: string;
84
+ /** Default ease-out curve. */
85
+ easeOut: string;
86
+ /** Overshooting spring curve for pops. */
87
+ easeSpring: string;
88
+ /**
89
+ * The default duration for a control transition, such as a press or a hover.
90
+ * It follows `fast`, and reduced motion sets it to 0ms.
91
+ */
92
+ duration: string;
93
+ /** The default easing for a control transition. It follows `easeOut`. */
94
+ easing: string;
95
+ }
96
+ export declare const MOTION_TOKENS: MotionTokens;
97
+ /**
98
+ * Durations under `prefers-reduced-motion: reduce`. The animations finish at
99
+ * once and only the final state is shown. Easings are unchanged.
100
+ */
101
+ export declare const REDUCED_MOTION_DURATIONS: Pick<MotionTokens, 'fast' | 'base' | 'slow' | 'duration'>;
102
+ /**
103
+ * A stylesheet that defines every token. It has three blocks:
104
+ * - `:root`, the light values and the type, shape and motion tokens;
105
+ * - `:root[data-theme="dark"]`, the dark colour overrides;
106
+ * - `prefers-reduced-motion: reduce`, which zeroes the durations.
107
+ *
108
+ * Add it to a game's stylesheet, for example `<style>{GAME_TOKENS_CSS}</style>`
109
+ * or the top of `index.css`.
110
+ */
111
+ export declare const GAME_TOKENS_CSS: string;
112
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,0GAA0G;AAC1G,MAAM,WAAW,YAAY;IAC3B,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,mGAAmG;IACnG,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,4DAA4D;AAC5D,eAAO,MAAM,aAAa,EAAE,YAe3B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,OAAO,CAAC,YAAY,CAcvD,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,YAAY,EAAE,YAA6D,CAAC;AAEzF,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,sBAAsB;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,gBAM/B,CAAC;AAEF,oBAAoB;AACpB,MAAM,WAAW,WAAW;IAC1B,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,YAAY,EAAE,WAE1B,CAAC;AAEF,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,aAAa,EAAE,YAQ3B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAK9F,CAAC;AASF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,QASd,CAAC"}