@kids-games/core 0.1.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 (73) hide show
  1. package/dist/celebrations/confetti.d.ts +18 -0
  2. package/dist/celebrations/confetti.d.ts.map +1 -0
  3. package/dist/celebrations/confetti.js +35 -0
  4. package/dist/celebrations/confetti.js.map +1 -0
  5. package/dist/celebrations/index.d.ts +3 -0
  6. package/dist/celebrations/index.d.ts.map +1 -0
  7. package/dist/celebrations/index.js +3 -0
  8. package/dist/celebrations/index.js.map +1 -0
  9. package/dist/celebrations/types.d.ts +13 -0
  10. package/dist/celebrations/types.d.ts.map +1 -0
  11. package/dist/celebrations/types.js +21 -0
  12. package/dist/celebrations/types.js.map +1 -0
  13. package/dist/components/CelebrationOverlay.d.ts +20 -0
  14. package/dist/components/CelebrationOverlay.d.ts.map +1 -0
  15. package/dist/components/CelebrationOverlay.js +92 -0
  16. package/dist/components/CelebrationOverlay.js.map +1 -0
  17. package/dist/components/MuteButton.d.ts +14 -0
  18. package/dist/components/MuteButton.d.ts.map +1 -0
  19. package/dist/components/MuteButton.js +16 -0
  20. package/dist/components/MuteButton.js.map +1 -0
  21. package/dist/components/PersonaPicker.d.ts +17 -0
  22. package/dist/components/PersonaPicker.d.ts.map +1 -0
  23. package/dist/components/PersonaPicker.js +62 -0
  24. package/dist/components/PersonaPicker.js.map +1 -0
  25. package/dist/components/index.d.ts +4 -0
  26. package/dist/components/index.d.ts.map +1 -0
  27. package/dist/components/index.js +4 -0
  28. package/dist/components/index.js.map +1 -0
  29. package/dist/sfx/index.d.ts +2 -0
  30. package/dist/sfx/index.d.ts.map +1 -0
  31. package/dist/sfx/index.js +2 -0
  32. package/dist/sfx/index.js.map +1 -0
  33. package/dist/sfx/synth.d.ts +18 -0
  34. package/dist/sfx/synth.d.ts.map +1 -0
  35. package/dist/sfx/synth.js +97 -0
  36. package/dist/sfx/synth.js.map +1 -0
  37. package/dist/tokens/colors.d.ts +51 -0
  38. package/dist/tokens/colors.d.ts.map +1 -0
  39. package/dist/tokens/colors.js +51 -0
  40. package/dist/tokens/colors.js.map +1 -0
  41. package/dist/tokens/index.d.ts +3 -0
  42. package/dist/tokens/index.d.ts.map +1 -0
  43. package/dist/tokens/index.js +3 -0
  44. package/dist/tokens/index.js.map +1 -0
  45. package/dist/tokens/spacing.d.ts +55 -0
  46. package/dist/tokens/spacing.d.ts.map +1 -0
  47. package/dist/tokens/spacing.js +55 -0
  48. package/dist/tokens/spacing.js.map +1 -0
  49. package/dist/voice/hooks.d.ts +52 -0
  50. package/dist/voice/hooks.d.ts.map +1 -0
  51. package/dist/voice/hooks.js +91 -0
  52. package/dist/voice/hooks.js.map +1 -0
  53. package/dist/voice/index.d.ts +6 -0
  54. package/dist/voice/index.d.ts.map +1 -0
  55. package/dist/voice/index.js +5 -0
  56. package/dist/voice/index.js.map +1 -0
  57. package/dist/voice/personas.d.ts +7 -0
  58. package/dist/voice/personas.d.ts.map +1 -0
  59. package/dist/voice/personas.js +143 -0
  60. package/dist/voice/personas.js.map +1 -0
  61. package/dist/voice/player.d.ts +54 -0
  62. package/dist/voice/player.d.ts.map +1 -0
  63. package/dist/voice/player.js +224 -0
  64. package/dist/voice/player.js.map +1 -0
  65. package/dist/voice/provider.d.ts +35 -0
  66. package/dist/voice/provider.d.ts.map +1 -0
  67. package/dist/voice/provider.js +82 -0
  68. package/dist/voice/provider.js.map +1 -0
  69. package/dist/voice/types.d.ts +52 -0
  70. package/dist/voice/types.d.ts.map +1 -0
  71. package/dist/voice/types.js +2 -0
  72. package/dist/voice/types.js.map +1 -0
  73. package/package.json +51 -0
@@ -0,0 +1,18 @@
1
+ export interface ConfettiParticle {
2
+ x: number;
3
+ y: number;
4
+ vx: number;
5
+ vy: number;
6
+ rotation: number;
7
+ rotationSpeed: number;
8
+ color: string;
9
+ size: number;
10
+ opacity: number;
11
+ /** Shape determined at creation — "circle" or "rect" */
12
+ shape: "circle" | "rect";
13
+ }
14
+ /** Create a burst of confetti particles */
15
+ export declare function createConfettiBurst(count: number): ConfettiParticle[];
16
+ /** Advance confetti particles by one frame */
17
+ export declare function tickConfetti(particles: ConfettiParticle[]): ConfettiParticle[];
18
+ //# sourceMappingURL=confetti.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confetti.d.ts","sourceRoot":"","sources":["../../celebrations/confetti.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,KAAK,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC1B;AAQD,2CAA2C;AAC3C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAarE;AAED,8CAA8C;AAC9C,wBAAgB,YAAY,CAAC,SAAS,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAW9E"}
@@ -0,0 +1,35 @@
1
+ "use client";
2
+ const CONFETTI_COLORS = [
3
+ "#FF6B6B", "#FFE66D", "#4ECDC4", "#45B7D1",
4
+ "#96CEB4", "#FFEAA7", "#DDA0DD", "#98D8C8",
5
+ "#F7DC6F", "#82E0AA", "#F1948A", "#85C1E9",
6
+ ];
7
+ /** Create a burst of confetti particles */
8
+ export function createConfettiBurst(count) {
9
+ return Array.from({ length: count }, () => ({
10
+ x: Math.random() * 100,
11
+ y: -10 - Math.random() * 20,
12
+ vx: (Math.random() - 0.5) * 3,
13
+ vy: Math.random() * 2 + 1,
14
+ rotation: Math.random() * 360,
15
+ rotationSpeed: (Math.random() - 0.5) * 10,
16
+ color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)],
17
+ size: Math.random() * 8 + 4,
18
+ opacity: 1,
19
+ shape: Math.random() > 0.5 ? "circle" : "rect",
20
+ }));
21
+ }
22
+ /** Advance confetti particles by one frame */
23
+ export function tickConfetti(particles) {
24
+ return particles
25
+ .map((p) => ({
26
+ ...p,
27
+ x: p.x + p.vx,
28
+ y: p.y + p.vy,
29
+ vy: p.vy + 0.05, // gravity
30
+ rotation: p.rotation + p.rotationSpeed,
31
+ opacity: p.opacity - 0.005,
32
+ }))
33
+ .filter((p) => p.opacity > 0 && p.y < 120);
34
+ }
35
+ //# sourceMappingURL=confetti.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confetti.js","sourceRoot":"","sources":["../../celebrations/confetti.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAgBb,MAAM,eAAe,GAAG;IACtB,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IAC1C,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IAC1C,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;CAC3C,CAAC;AAEF,2CAA2C;AAC3C,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC1C,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG;QACtB,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE;QAC3B,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC;QAC7B,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC;QACzB,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG;QAC7B,aAAa,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,EAAE;QACzC,KAAK,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;QAC1E,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC;QAC3B,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;KAC/C,CAAC,CAAC,CAAC;AACN,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,YAAY,CAAC,SAA6B;IACxD,OAAO,SAAS;SACb,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,GAAG,CAAC;QACJ,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE;QACb,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE;QACb,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,UAAU;QAC3B,QAAQ,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,aAAa;QACtC,OAAO,EAAE,CAAC,CAAC,OAAO,GAAG,KAAK;KAC3B,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { CELEBRATION_CONFIGS, type CelebrationConfig } from "./types";
2
+ export { createConfettiBurst, tickConfetti, type ConfettiParticle } from "./confetti";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../celebrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { CELEBRATION_CONFIGS } from "./types";
2
+ export { createConfettiBurst, tickConfetti } from "./confetti";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../celebrations/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAA0B,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAyB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CelebrationLevel } from "../voice/types";
2
+ export interface CelebrationConfig {
3
+ /** Celebration intensity */
4
+ level: CelebrationLevel;
5
+ /** Number of confetti particles */
6
+ particleCount: number;
7
+ /** Duration in ms */
8
+ duration: number;
9
+ /** Show overlay text */
10
+ showText: boolean;
11
+ }
12
+ export declare const CELEBRATION_CONFIGS: Record<CelebrationLevel, CelebrationConfig>;
13
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../celebrations/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,KAAK,EAAE,gBAAgB,CAAC;IACxB,mCAAmC;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,gBAAgB,EAAE,iBAAiB,CAmB3E,CAAC"}
@@ -0,0 +1,21 @@
1
+ export const CELEBRATION_CONFIGS = {
2
+ small: {
3
+ level: "small",
4
+ particleCount: 15,
5
+ duration: 1200,
6
+ showText: false,
7
+ },
8
+ medium: {
9
+ level: "medium",
10
+ particleCount: 40,
11
+ duration: 2000,
12
+ showText: true,
13
+ },
14
+ big: {
15
+ level: "big",
16
+ particleCount: 80,
17
+ duration: 3000,
18
+ showText: true,
19
+ },
20
+ };
21
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../celebrations/types.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,mBAAmB,GAAgD;IAC9E,KAAK,EAAE;QACL,KAAK,EAAE,OAAO;QACd,aAAa,EAAE,EAAE;QACjB,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;KAChB;IACD,MAAM,EAAE;QACN,KAAK,EAAE,QAAQ;QACf,aAAa,EAAE,EAAE;QACjB,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;KACf;IACD,GAAG,EAAE;QACH,KAAK,EAAE,KAAK;QACZ,aAAa,EAAE,EAAE;QACjB,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,IAAI;KACf;CACF,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { CelebrationLevel } from "../voice/types";
2
+ interface CelebrationOverlayProps {
3
+ /** Celebration level to show. Set to null to hide. */
4
+ level: CelebrationLevel | null;
5
+ /** Text to display (e.g., "Great Job!"). Only shown for medium/big. */
6
+ text?: string;
7
+ /** Called when the celebration animation completes */
8
+ onComplete?: () => void;
9
+ }
10
+ /**
11
+ * Full-screen celebration overlay with confetti and optional text.
12
+ * Respects prefers-reduced-motion — shows static text banner instead of confetti.
13
+ *
14
+ * ```tsx
15
+ * <CelebrationOverlay level="medium" text="Great Job!" onComplete={() => setLevel(null)} />
16
+ * ```
17
+ */
18
+ export declare function CelebrationOverlay({ level, text, onComplete }: CelebrationOverlayProps): import("react/jsx-runtime").JSX.Element | null;
19
+ export {};
20
+ //# sourceMappingURL=CelebrationOverlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CelebrationOverlay.d.ts","sourceRoot":"","sources":["../../components/CelebrationOverlay.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAIvD,UAAU,uBAAuB;IAC/B,sDAAsD;IACtD,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC/B,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,uBAAuB,kDAyGtF"}
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState, useRef } from "react";
4
+ import { CELEBRATION_CONFIGS } from "../celebrations/types";
5
+ import { createConfettiBurst, tickConfetti } from "../celebrations/confetti";
6
+ /**
7
+ * Full-screen celebration overlay with confetti and optional text.
8
+ * Respects prefers-reduced-motion — shows static text banner instead of confetti.
9
+ *
10
+ * ```tsx
11
+ * <CelebrationOverlay level="medium" text="Great Job!" onComplete={() => setLevel(null)} />
12
+ * ```
13
+ */
14
+ export function CelebrationOverlay({ level, text, onComplete }) {
15
+ const [visible, setVisible] = useState(false);
16
+ const particlesRef = useRef([]);
17
+ const animRef = useRef(0);
18
+ const timerRef = useRef(undefined);
19
+ const onCompleteRef = useRef(onComplete);
20
+ const containerRef = useRef(null);
21
+ const [reducedMotion, setReducedMotion] = useState(false);
22
+ // Keep onComplete ref current without triggering re-renders (P0 fix)
23
+ onCompleteRef.current = onComplete;
24
+ // Check reduced motion preference (P1 fix)
25
+ useEffect(() => {
26
+ if (typeof window === "undefined")
27
+ return;
28
+ const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
29
+ setReducedMotion(mq.matches);
30
+ const handler = (e) => setReducedMotion(e.matches);
31
+ mq.addEventListener("change", handler);
32
+ return () => mq.removeEventListener("change", handler);
33
+ }, []);
34
+ // Start/stop celebration based on level prop
35
+ useEffect(() => {
36
+ if (!level) {
37
+ setVisible(false);
38
+ particlesRef.current = [];
39
+ return;
40
+ }
41
+ const config = CELEBRATION_CONFIGS[level];
42
+ if (!reducedMotion) {
43
+ particlesRef.current = createConfettiBurst(config.particleCount);
44
+ // Ref-based animation loop — no setState per frame (P0 + P2 perf fix)
45
+ const animate = () => {
46
+ particlesRef.current = tickConfetti(particlesRef.current);
47
+ renderParticles();
48
+ if (particlesRef.current.length > 0) {
49
+ animRef.current = requestAnimationFrame(animate);
50
+ }
51
+ };
52
+ animRef.current = requestAnimationFrame(animate);
53
+ }
54
+ setVisible(true);
55
+ timerRef.current = setTimeout(() => {
56
+ setVisible(false);
57
+ particlesRef.current = [];
58
+ if (animRef.current)
59
+ cancelAnimationFrame(animRef.current);
60
+ onCompleteRef.current?.();
61
+ }, config.duration);
62
+ return () => {
63
+ if (timerRef.current)
64
+ clearTimeout(timerRef.current);
65
+ if (animRef.current)
66
+ cancelAnimationFrame(animRef.current);
67
+ };
68
+ }, [level, reducedMotion]);
69
+ /** Direct DOM manipulation for confetti — avoids React re-renders at 60fps */
70
+ function renderParticles() {
71
+ const container = containerRef.current;
72
+ if (!container)
73
+ return;
74
+ const confettiLayer = container.querySelector("[data-confetti]");
75
+ if (!confettiLayer)
76
+ return;
77
+ // Clear and re-render (simpler than tracking individual elements)
78
+ confettiLayer.innerHTML = "";
79
+ for (const p of particlesRef.current) {
80
+ const el = document.createElement("div");
81
+ el.style.cssText = `position:absolute;left:${p.x}%;top:${p.y}%;width:${p.size}px;height:${p.size}px;background:${p.color};border-radius:${p.shape === "circle" ? "50%" : "2px"};transform:rotate(${p.rotation}deg);opacity:${p.opacity};will-change:transform;`;
82
+ confettiLayer.appendChild(el);
83
+ }
84
+ }
85
+ if (!visible || !level)
86
+ return null;
87
+ const config = CELEBRATION_CONFIGS[level];
88
+ return (_jsxs("div", { ref: containerRef, className: "fixed inset-0 z-[100] pointer-events-none overflow-hidden", "aria-hidden": "true", children: [!reducedMotion && _jsx("div", { "data-confetti": "" }), config.showText && text && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx("div", { className: `text-4xl sm:text-5xl md:text-6xl font-bold text-white drop-shadow-lg ${reducedMotion ? "" : "animate-bounce"}`, style: {
89
+ textShadow: "0 4px 12px rgba(0,0,0,0.3), 0 2px 4px rgba(0,0,0,0.2)",
90
+ }, children: text }) }))] }));
91
+ }
92
+ //# sourceMappingURL=CelebrationOverlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CelebrationOverlay.js","sourceRoot":"","sources":["../../components/CelebrationOverlay.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ interface MuteButtonProps {
2
+ /** CSS class overrides. Defaults to fixed top-right. */
3
+ className?: string;
4
+ /** Size of the button in pixels. Default 48. */
5
+ size?: number;
6
+ }
7
+ /**
8
+ * Unified mute toggle button for all kids games.
9
+ * Reads mute state from KidsAudioProvider context.
10
+ * Must be inside a <KidsAudioProvider>.
11
+ */
12
+ export declare function MuteButton({ className, size }: MuteButtonProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
14
+ //# sourceMappingURL=MuteButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuteButton.d.ts","sourceRoot":"","sources":["../../components/MuteButton.tsx"],"names":[],"mappings":"AAKA,UAAU,eAAe;IACvB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,IAAS,EAAE,EAAE,eAAe,2CAiDnE"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useContext } from "react";
4
+ import { KidsAudioContext } from "../voice/provider";
5
+ /**
6
+ * Unified mute toggle button for all kids games.
7
+ * Reads mute state from KidsAudioProvider context.
8
+ * Must be inside a <KidsAudioProvider>.
9
+ */
10
+ export function MuteButton({ className, size = 48 }) {
11
+ const { muted, toggleMute } = useContext(KidsAudioContext);
12
+ const defaultClass = "fixed top-4 right-4 z-50 rounded-full bg-white/80 backdrop-blur-sm shadow-lg flex items-center justify-center active:scale-90 transition-transform";
13
+ const iconSize = Math.round(size * 0.5);
14
+ return (_jsx("button", { onClick: toggleMute, className: className ?? defaultClass, style: { width: size, height: size }, "aria-label": muted ? "Unmute sounds" : "Mute sounds", type: "button", children: muted ? (_jsxs("svg", { width: iconSize, height: iconSize, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5", fill: "#374151" }), _jsx("line", { x1: "23", y1: "9", x2: "17", y2: "15", stroke: "#ef4444" }), _jsx("line", { x1: "17", y1: "9", x2: "23", y2: "15", stroke: "#ef4444" })] })) : (_jsxs("svg", { width: iconSize, height: iconSize, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5", fill: "#374151" }), _jsx("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07", stroke: "#374151" }), _jsx("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14", stroke: "#374151" })] })) }));
15
+ }
16
+ //# sourceMappingURL=MuteButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MuteButton.js","sourceRoot":"","sources":["../../components/MuteButton.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AASrD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAmB;IAClE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAE3D,MAAM,YAAY,GAChB,oJAAoJ,CAAC;IAEvJ,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC;IAExC,OAAO,CACL,iBACE,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,SAAS,IAAI,YAAY,EACpC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,gBACxB,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,aAAa,EACnD,IAAI,EAAC,QAAQ,YAEZ,KAAK,CAAC,CAAC,CAAC,CACP,eACE,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,kBAAS,MAAM,EAAC,mCAAmC,EAAC,IAAI,EAAC,SAAS,GAAG,EACrE,eAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,MAAM,EAAC,SAAS,GAAG,EACxD,eAAM,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,MAAM,EAAC,SAAS,GAAG,IACpD,CACP,CAAC,CAAC,CAAC,CACF,eACE,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,QAAQ,EAChB,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,aAEtB,kBAAS,MAAM,EAAC,mCAAmC,EAAC,IAAI,EAAC,SAAS,GAAG,EACrE,eAAM,CAAC,EAAC,8BAA8B,EAAC,MAAM,EAAC,SAAS,GAAG,EAC1D,eAAM,CAAC,EAAC,iCAAiC,EAAC,MAAM,EAAC,SAAS,GAAG,IACzD,CACP,GACM,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { PersonaId } from "../voice/types";
2
+ interface PersonaPickerProps {
3
+ /** Which personas are available in this game */
4
+ availablePersonas: PersonaId[];
5
+ /** Called when user picks a persona (also called on mount with initial selection) */
6
+ onSelect: (personaId: PersonaId) => void;
7
+ /** localStorage key for persisting choice */
8
+ storageKey?: string;
9
+ }
10
+ /**
11
+ * Simple character selector for kids. Shows available personas as tappable cards.
12
+ * Persists selection to localStorage if storageKey provided.
13
+ * Reports initial selection to parent on mount.
14
+ */
15
+ export declare function PersonaPicker({ availablePersonas, onSelect, storageKey, }: PersonaPickerProps): import("react/jsx-runtime").JSX.Element;
16
+ export {};
17
+ //# sourceMappingURL=PersonaPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PersonaPicker.d.ts","sourceRoot":"","sources":["../../components/PersonaPicker.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,UAAU,kBAAkB;IAC1B,gDAAgD;IAChD,iBAAiB,EAAE,SAAS,EAAE,CAAC;IAC/B,qFAAqF;IACrF,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAmBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,iBAAiB,EACjB,QAAQ,EACR,UAAU,GACX,EAAE,kBAAkB,2CAgEpB"}
@@ -0,0 +1,62 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useRef } from "react";
4
+ import { PERSONAS } from "../voice/personas";
5
+ /** Persona visual placeholders (emoji-based until real character art is added) */
6
+ const PERSONA_EMOJI = {
7
+ sunny: "\u2600\uFE0F",
8
+ bloop: "\uD83D\uDC99",
9
+ maple: "\uD83D\uDC3B",
10
+ fizz: "\uD83E\uDDEA",
11
+ pip: "\uD83D\uDC26",
12
+ };
13
+ const PERSONA_COLORS = {
14
+ sunny: "#FFD93D",
15
+ bloop: "#6CB4EE",
16
+ maple: "#C4A882",
17
+ fizz: "#77DD77",
18
+ pip: "#FFB6C1",
19
+ };
20
+ /**
21
+ * Simple character selector for kids. Shows available personas as tappable cards.
22
+ * Persists selection to localStorage if storageKey provided.
23
+ * Reports initial selection to parent on mount.
24
+ */
25
+ export function PersonaPicker({ availablePersonas, onSelect, storageKey, }) {
26
+ const [selected, setSelected] = useState(availablePersonas[0]);
27
+ const mountedRef = useRef(false);
28
+ // Resolve initial selection from localStorage and notify parent
29
+ useEffect(() => {
30
+ let initial = availablePersonas[0];
31
+ if (storageKey && typeof window !== "undefined") {
32
+ const saved = localStorage.getItem(storageKey);
33
+ if (saved && availablePersonas.includes(saved)) {
34
+ initial = saved;
35
+ }
36
+ }
37
+ setSelected(initial);
38
+ onSelect(initial);
39
+ mountedRef.current = true;
40
+ }, [storageKey, availablePersonas]);
41
+ const handleSelect = (id) => {
42
+ setSelected(id);
43
+ onSelect(id);
44
+ if (storageKey && typeof window !== "undefined") {
45
+ localStorage.setItem(storageKey, id);
46
+ }
47
+ };
48
+ return (_jsx("div", { className: "flex gap-4 justify-center flex-wrap", children: availablePersonas.map((id) => {
49
+ const persona = PERSONAS[id];
50
+ const isSelected = selected === id;
51
+ return (_jsxs("button", { onClick: () => handleSelect(id), className: "flex flex-col items-center gap-2 p-3 rounded-2xl transition-all active:scale-95 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-400", style: {
52
+ backgroundColor: isSelected
53
+ ? PERSONA_COLORS[id]
54
+ : `${PERSONA_COLORS[id]}40`,
55
+ border: isSelected
56
+ ? `3px solid ${PERSONA_COLORS[id]}`
57
+ : "3px solid transparent",
58
+ minWidth: 80,
59
+ }, "aria-label": `Choose ${persona.name}`, "aria-pressed": isSelected, type: "button", children: [_jsx("span", { className: "text-3xl", role: "img", "aria-hidden": "true", children: PERSONA_EMOJI[id] }), _jsx("span", { className: "text-sm font-bold", style: { color: isSelected ? "#1a1a2e" : "#555" }, children: persona.name })] }, id));
60
+ }) }));
61
+ }
62
+ //# sourceMappingURL=PersonaPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PersonaPicker.js","sourceRoot":"","sources":["../../components/PersonaPicker.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { MuteButton } from "./MuteButton";
2
+ export { CelebrationOverlay } from "./CelebrationOverlay";
3
+ export { PersonaPicker } from "./PersonaPicker";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { MuteButton } from "./MuteButton";
2
+ export { CelebrationOverlay } from "./CelebrationOverlay";
3
+ export { PersonaPicker } from "./PersonaPicker";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { playTap, playPop, playCelebrationChime, playCorrect, playIncorrect, playWhoosh, } from "./synth";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../sfx/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,OAAO,EACP,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,UAAU,GACX,MAAM,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { playTap, playPop, playCelebrationChime, playCorrect, playIncorrect, playWhoosh, } from "./synth";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../sfx/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,OAAO,EACP,oBAAoB,EACpB,WAAW,EACX,aAAa,EACb,UAAU,GACX,MAAM,SAAS,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Standalone synthesized sound effects (no React dependency).
3
+ * These are the same effects available via useSoundEffects() hook,
4
+ * but can be used outside React components if needed.
5
+ */
6
+ /** Play a short tap/ding sound (880Hz → 1320Hz → 880Hz) */
7
+ export declare function playTap(ctx: AudioContext): void;
8
+ /** Play a descending pop sound (600Hz → 200Hz) */
9
+ export declare function playPop(ctx: AudioContext): void;
10
+ /** Play ascending celebration chime (C5-E5-G5-C6 arpeggio) */
11
+ export declare function playCelebrationChime(ctx: AudioContext): void;
12
+ /** Play two ascending notes — gentle "ding-ding!" for correct answers */
13
+ export declare function playCorrect(ctx: AudioContext): void;
14
+ /** Play gentle descending tone — never harsh, for "try again" moments */
15
+ export declare function playIncorrect(ctx: AudioContext): void;
16
+ /** Play a short whoosh transition sound */
17
+ export declare function playWhoosh(ctx: AudioContext): void;
18
+ //# sourceMappingURL=synth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synth.d.ts","sourceRoot":"","sources":["../../sfx/synth.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,2DAA2D;AAC3D,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAa/C;AAED,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAW/C;AAED,8DAA8D;AAC9D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAe5D;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAcnD;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAYrD;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAalD"}
@@ -0,0 +1,97 @@
1
+ "use client";
2
+ /**
3
+ * Standalone synthesized sound effects (no React dependency).
4
+ * These are the same effects available via useSoundEffects() hook,
5
+ * but can be used outside React components if needed.
6
+ */
7
+ /** Play a short tap/ding sound (880Hz → 1320Hz → 880Hz) */
8
+ export function playTap(ctx) {
9
+ const osc = ctx.createOscillator();
10
+ const gain = ctx.createGain();
11
+ osc.connect(gain);
12
+ gain.connect(ctx.destination);
13
+ osc.type = "sine";
14
+ osc.frequency.setValueAtTime(880, ctx.currentTime);
15
+ osc.frequency.exponentialRampToValueAtTime(1320, ctx.currentTime + 0.05);
16
+ osc.frequency.exponentialRampToValueAtTime(880, ctx.currentTime + 0.1);
17
+ gain.gain.setValueAtTime(0.3, ctx.currentTime);
18
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.15);
19
+ osc.start(ctx.currentTime);
20
+ osc.stop(ctx.currentTime + 0.15);
21
+ }
22
+ /** Play a descending pop sound (600Hz → 200Hz) */
23
+ export function playPop(ctx) {
24
+ const osc = ctx.createOscillator();
25
+ const gain = ctx.createGain();
26
+ osc.connect(gain);
27
+ gain.connect(ctx.destination);
28
+ osc.frequency.setValueAtTime(600, ctx.currentTime);
29
+ osc.frequency.exponentialRampToValueAtTime(200, ctx.currentTime + 0.1);
30
+ gain.gain.setValueAtTime(0.3, ctx.currentTime);
31
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.1);
32
+ osc.start(ctx.currentTime);
33
+ osc.stop(ctx.currentTime + 0.1);
34
+ }
35
+ /** Play ascending celebration chime (C5-E5-G5-C6 arpeggio) */
36
+ export function playCelebrationChime(ctx) {
37
+ const notes = [523.25, 659.25, 783.99, 1046.5];
38
+ notes.forEach((freq, i) => {
39
+ const osc = ctx.createOscillator();
40
+ const gain = ctx.createGain();
41
+ osc.connect(gain);
42
+ gain.connect(ctx.destination);
43
+ osc.type = "sine";
44
+ osc.frequency.value = freq;
45
+ const startTime = ctx.currentTime + i * 0.12;
46
+ gain.gain.setValueAtTime(0.25, startTime);
47
+ gain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.3);
48
+ osc.start(startTime);
49
+ osc.stop(startTime + 0.3);
50
+ });
51
+ }
52
+ /** Play two ascending notes — gentle "ding-ding!" for correct answers */
53
+ export function playCorrect(ctx) {
54
+ [660, 880].forEach((freq, i) => {
55
+ const osc = ctx.createOscillator();
56
+ const gain = ctx.createGain();
57
+ osc.connect(gain);
58
+ gain.connect(ctx.destination);
59
+ osc.type = "sine";
60
+ osc.frequency.value = freq;
61
+ const startTime = ctx.currentTime + i * 0.12;
62
+ gain.gain.setValueAtTime(0.25, startTime);
63
+ gain.gain.exponentialRampToValueAtTime(0.01, startTime + 0.2);
64
+ osc.start(startTime);
65
+ osc.stop(startTime + 0.2);
66
+ });
67
+ }
68
+ /** Play gentle descending tone — never harsh, for "try again" moments */
69
+ export function playIncorrect(ctx) {
70
+ const osc = ctx.createOscillator();
71
+ const gain = ctx.createGain();
72
+ osc.connect(gain);
73
+ gain.connect(ctx.destination);
74
+ osc.type = "sine";
75
+ osc.frequency.setValueAtTime(440, ctx.currentTime);
76
+ osc.frequency.exponentialRampToValueAtTime(330, ctx.currentTime + 0.2);
77
+ gain.gain.setValueAtTime(0.15, ctx.currentTime);
78
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
79
+ osc.start(ctx.currentTime);
80
+ osc.stop(ctx.currentTime + 0.3);
81
+ }
82
+ /** Play a short whoosh transition sound */
83
+ export function playWhoosh(ctx) {
84
+ const osc = ctx.createOscillator();
85
+ const gain = ctx.createGain();
86
+ osc.connect(gain);
87
+ gain.connect(ctx.destination);
88
+ osc.type = "sawtooth";
89
+ osc.frequency.setValueAtTime(200, ctx.currentTime);
90
+ osc.frequency.exponentialRampToValueAtTime(800, ctx.currentTime + 0.1);
91
+ osc.frequency.exponentialRampToValueAtTime(100, ctx.currentTime + 0.2);
92
+ gain.gain.setValueAtTime(0.1, ctx.currentTime);
93
+ gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.25);
94
+ osc.start(ctx.currentTime);
95
+ osc.stop(ctx.currentTime + 0.25);
96
+ }
97
+ //# sourceMappingURL=synth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synth.js","sourceRoot":"","sources":["../../sfx/synth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Kid-friendly color palette.
3
+ * Bright, warm, high-contrast colors suitable for ages 2-6.
4
+ * All colors meet WCAG AA contrast requirements when paired with white or dark text.
5
+ */
6
+ export declare const COLORS: {
7
+ /** Primary game colors — bright and engaging */
8
+ readonly primary: {
9
+ readonly red: "#FF6B6B";
10
+ readonly orange: "#FFA07A";
11
+ readonly yellow: "#FFD93D";
12
+ readonly green: "#77DD77";
13
+ readonly blue: "#6CB4EE";
14
+ readonly purple: "#DDA0DD";
15
+ readonly pink: "#FFB6C1";
16
+ readonly teal: "#4ECDC4";
17
+ };
18
+ /** Background colors — soft and easy on the eyes */
19
+ readonly background: {
20
+ readonly warm: "#FFF8F0";
21
+ readonly cool: "#F0F8FF";
22
+ readonly neutral: "#F5F5F5";
23
+ readonly sky: "#E8F4FD";
24
+ readonly meadow: "#F0FFF0";
25
+ readonly sunset: "#FFF0E6";
26
+ };
27
+ /** Text colors */
28
+ readonly text: {
29
+ readonly primary: "#2D3748";
30
+ readonly secondary: "#4A5568";
31
+ readonly muted: "#A0AEC0";
32
+ readonly onDark: "#FFFFFF";
33
+ readonly onLight: "#1A202C";
34
+ };
35
+ /** Feedback colors */
36
+ readonly feedback: {
37
+ readonly success: "#48BB78";
38
+ readonly warning: "#F6AD55";
39
+ readonly error: "#FC8181";
40
+ readonly info: "#63B3ED";
41
+ };
42
+ /** Persona colors */
43
+ readonly persona: {
44
+ readonly sunny: "#FFD93D";
45
+ readonly bloop: "#6CB4EE";
46
+ readonly maple: "#C4A882";
47
+ readonly fizz: "#77DD77";
48
+ readonly pip: "#FFB6C1";
49
+ };
50
+ };
51
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../tokens/colors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,MAAM;IACjB,gDAAgD;;;;;;;;;;;IAYhD,oDAAoD;;;;;;;;;IAUpD,kBAAkB;;;;;;;;IASlB,sBAAsB;;;;;;;IAQtB,qBAAqB;;;;;;;;CAQb,CAAC"}