@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.
- package/dist/celebrations/confetti.d.ts +18 -0
- package/dist/celebrations/confetti.d.ts.map +1 -0
- package/dist/celebrations/confetti.js +35 -0
- package/dist/celebrations/confetti.js.map +1 -0
- package/dist/celebrations/index.d.ts +3 -0
- package/dist/celebrations/index.d.ts.map +1 -0
- package/dist/celebrations/index.js +3 -0
- package/dist/celebrations/index.js.map +1 -0
- package/dist/celebrations/types.d.ts +13 -0
- package/dist/celebrations/types.d.ts.map +1 -0
- package/dist/celebrations/types.js +21 -0
- package/dist/celebrations/types.js.map +1 -0
- package/dist/components/CelebrationOverlay.d.ts +20 -0
- package/dist/components/CelebrationOverlay.d.ts.map +1 -0
- package/dist/components/CelebrationOverlay.js +92 -0
- package/dist/components/CelebrationOverlay.js.map +1 -0
- package/dist/components/MuteButton.d.ts +14 -0
- package/dist/components/MuteButton.d.ts.map +1 -0
- package/dist/components/MuteButton.js +16 -0
- package/dist/components/MuteButton.js.map +1 -0
- package/dist/components/PersonaPicker.d.ts +17 -0
- package/dist/components/PersonaPicker.d.ts.map +1 -0
- package/dist/components/PersonaPicker.js +62 -0
- package/dist/components/PersonaPicker.js.map +1 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -0
- package/dist/sfx/index.d.ts +2 -0
- package/dist/sfx/index.d.ts.map +1 -0
- package/dist/sfx/index.js +2 -0
- package/dist/sfx/index.js.map +1 -0
- package/dist/sfx/synth.d.ts +18 -0
- package/dist/sfx/synth.d.ts.map +1 -0
- package/dist/sfx/synth.js +97 -0
- package/dist/sfx/synth.js.map +1 -0
- package/dist/tokens/colors.d.ts +51 -0
- package/dist/tokens/colors.d.ts.map +1 -0
- package/dist/tokens/colors.js +51 -0
- package/dist/tokens/colors.js.map +1 -0
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +3 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/spacing.d.ts +55 -0
- package/dist/tokens/spacing.d.ts.map +1 -0
- package/dist/tokens/spacing.js +55 -0
- package/dist/tokens/spacing.js.map +1 -0
- package/dist/voice/hooks.d.ts +52 -0
- package/dist/voice/hooks.d.ts.map +1 -0
- package/dist/voice/hooks.js +91 -0
- package/dist/voice/hooks.js.map +1 -0
- package/dist/voice/index.d.ts +6 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +5 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/personas.d.ts +7 -0
- package/dist/voice/personas.d.ts.map +1 -0
- package/dist/voice/personas.js +143 -0
- package/dist/voice/personas.js.map +1 -0
- package/dist/voice/player.d.ts +54 -0
- package/dist/voice/player.d.ts.map +1 -0
- package/dist/voice/player.js +224 -0
- package/dist/voice/player.js.map +1 -0
- package/dist/voice/provider.d.ts +35 -0
- package/dist/voice/provider.d.ts.map +1 -0
- package/dist/voice/provider.js +82 -0
- package/dist/voice/provider.js.map +1 -0
- package/dist/voice/types.d.ts +52 -0
- package/dist/voice/types.d.ts.map +1 -0
- package/dist/voice/types.js +2 -0
- package/dist/voice/types.js.map +1 -0
- package/package.json +51 -0
|
@@ -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 const COLORS = {
|
|
7
|
+
/** Primary game colors — bright and engaging */
|
|
8
|
+
primary: {
|
|
9
|
+
red: "#FF6B6B",
|
|
10
|
+
orange: "#FFA07A",
|
|
11
|
+
yellow: "#FFD93D",
|
|
12
|
+
green: "#77DD77",
|
|
13
|
+
blue: "#6CB4EE",
|
|
14
|
+
purple: "#DDA0DD",
|
|
15
|
+
pink: "#FFB6C1",
|
|
16
|
+
teal: "#4ECDC4",
|
|
17
|
+
},
|
|
18
|
+
/** Background colors — soft and easy on the eyes */
|
|
19
|
+
background: {
|
|
20
|
+
warm: "#FFF8F0",
|
|
21
|
+
cool: "#F0F8FF",
|
|
22
|
+
neutral: "#F5F5F5",
|
|
23
|
+
sky: "#E8F4FD",
|
|
24
|
+
meadow: "#F0FFF0",
|
|
25
|
+
sunset: "#FFF0E6",
|
|
26
|
+
},
|
|
27
|
+
/** Text colors */
|
|
28
|
+
text: {
|
|
29
|
+
primary: "#2D3748",
|
|
30
|
+
secondary: "#4A5568",
|
|
31
|
+
muted: "#A0AEC0",
|
|
32
|
+
onDark: "#FFFFFF",
|
|
33
|
+
onLight: "#1A202C",
|
|
34
|
+
},
|
|
35
|
+
/** Feedback colors */
|
|
36
|
+
feedback: {
|
|
37
|
+
success: "#48BB78",
|
|
38
|
+
warning: "#F6AD55",
|
|
39
|
+
error: "#FC8181",
|
|
40
|
+
info: "#63B3ED",
|
|
41
|
+
},
|
|
42
|
+
/** Persona colors */
|
|
43
|
+
persona: {
|
|
44
|
+
sunny: "#FFD93D",
|
|
45
|
+
bloop: "#6CB4EE",
|
|
46
|
+
maple: "#C4A882",
|
|
47
|
+
fizz: "#77DD77",
|
|
48
|
+
pip: "#FFB6C1",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.js","sourceRoot":"","sources":["../../tokens/colors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,gDAAgD;IAChD,OAAO,EAAE;QACP,GAAG,EAAE,SAAS;QACd,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,SAAS;KAChB;IAED,oDAAoD;IACpD,UAAU,EAAE;QACV,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,SAAS;QAClB,GAAG,EAAE,SAAS;QACd,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;KAClB;IAED,kBAAkB;IAClB,IAAI,EAAE;QACJ,OAAO,EAAE,SAAS;QAClB,SAAS,EAAE,SAAS;QACpB,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,SAAS;KACnB;IAED,sBAAsB;IACtB,QAAQ,EAAE;QACR,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE,SAAS;QAClB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;KAChB;IAED,qBAAqB;IACrB,OAAO,EAAE;QACP,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,IAAI,EAAE,SAAS;QACf,GAAG,EAAE,SAAS;KACf;CACO,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../tokens/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../tokens/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spacing and sizing standards for kids games.
|
|
3
|
+
* Designed for small fingers (ages 2-6) on touch devices.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SPACING: {
|
|
6
|
+
/** Touch target sizes — critical for toddler usability */
|
|
7
|
+
readonly touchTarget: {
|
|
8
|
+
/** Absolute minimum per WCAG (48px) */
|
|
9
|
+
readonly min: 48;
|
|
10
|
+
/** Recommended for toddlers (56px) */
|
|
11
|
+
readonly recommended: 56;
|
|
12
|
+
/** Large targets for primary actions (64px) */
|
|
13
|
+
readonly large: 64;
|
|
14
|
+
};
|
|
15
|
+
/** Padding scale (in px) */
|
|
16
|
+
readonly padding: {
|
|
17
|
+
readonly xs: 4;
|
|
18
|
+
readonly sm: 8;
|
|
19
|
+
readonly md: 16;
|
|
20
|
+
readonly lg: 24;
|
|
21
|
+
readonly xl: 32;
|
|
22
|
+
readonly xxl: 48;
|
|
23
|
+
};
|
|
24
|
+
/** Gap between interactive elements — enough for small fingers */
|
|
25
|
+
readonly gap: {
|
|
26
|
+
/** Minimum gap between tappable items */
|
|
27
|
+
readonly min: 8;
|
|
28
|
+
/** Comfortable gap */
|
|
29
|
+
readonly comfortable: 16;
|
|
30
|
+
/** Spacious gap */
|
|
31
|
+
readonly spacious: 24;
|
|
32
|
+
};
|
|
33
|
+
/** Border radius — rounded = friendly */
|
|
34
|
+
readonly radius: {
|
|
35
|
+
readonly sm: 8;
|
|
36
|
+
readonly md: 12;
|
|
37
|
+
readonly lg: 16;
|
|
38
|
+
readonly xl: 24;
|
|
39
|
+
readonly full: 9999;
|
|
40
|
+
};
|
|
41
|
+
/** Font sizes — large and readable for early readers */
|
|
42
|
+
readonly fontSize: {
|
|
43
|
+
/** Minimum readable text */
|
|
44
|
+
readonly body: 18;
|
|
45
|
+
/** Standard game text */
|
|
46
|
+
readonly large: 24;
|
|
47
|
+
/** Headings and prompts */
|
|
48
|
+
readonly heading: 32;
|
|
49
|
+
/** Big numbers, single letters */
|
|
50
|
+
readonly display: 48;
|
|
51
|
+
/** Hero text */
|
|
52
|
+
readonly hero: 64;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=spacing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spacing.d.ts","sourceRoot":"","sources":["../../tokens/spacing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,OAAO;IAClB,0DAA0D;;QAExD,uCAAuC;;QAEvC,sCAAsC;;QAEtC,+CAA+C;;;IAIjD,4BAA4B;;;;;;;;;IAU5B,kEAAkE;;QAEhE,yCAAyC;;QAEzC,sBAAsB;;QAEtB,mBAAmB;;;IAIrB,yCAAyC;;;;;;;;IASzC,wDAAwD;;QAEtD,4BAA4B;;QAE5B,yBAAyB;;QAEzB,2BAA2B;;QAE3B,kCAAkC;;QAElC,gBAAgB;;;CAGV,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spacing and sizing standards for kids games.
|
|
3
|
+
* Designed for small fingers (ages 2-6) on touch devices.
|
|
4
|
+
*/
|
|
5
|
+
export const SPACING = {
|
|
6
|
+
/** Touch target sizes — critical for toddler usability */
|
|
7
|
+
touchTarget: {
|
|
8
|
+
/** Absolute minimum per WCAG (48px) */
|
|
9
|
+
min: 48,
|
|
10
|
+
/** Recommended for toddlers (56px) */
|
|
11
|
+
recommended: 56,
|
|
12
|
+
/** Large targets for primary actions (64px) */
|
|
13
|
+
large: 64,
|
|
14
|
+
},
|
|
15
|
+
/** Padding scale (in px) */
|
|
16
|
+
padding: {
|
|
17
|
+
xs: 4,
|
|
18
|
+
sm: 8,
|
|
19
|
+
md: 16,
|
|
20
|
+
lg: 24,
|
|
21
|
+
xl: 32,
|
|
22
|
+
xxl: 48,
|
|
23
|
+
},
|
|
24
|
+
/** Gap between interactive elements — enough for small fingers */
|
|
25
|
+
gap: {
|
|
26
|
+
/** Minimum gap between tappable items */
|
|
27
|
+
min: 8,
|
|
28
|
+
/** Comfortable gap */
|
|
29
|
+
comfortable: 16,
|
|
30
|
+
/** Spacious gap */
|
|
31
|
+
spacious: 24,
|
|
32
|
+
},
|
|
33
|
+
/** Border radius — rounded = friendly */
|
|
34
|
+
radius: {
|
|
35
|
+
sm: 8,
|
|
36
|
+
md: 12,
|
|
37
|
+
lg: 16,
|
|
38
|
+
xl: 24,
|
|
39
|
+
full: 9999,
|
|
40
|
+
},
|
|
41
|
+
/** Font sizes — large and readable for early readers */
|
|
42
|
+
fontSize: {
|
|
43
|
+
/** Minimum readable text */
|
|
44
|
+
body: 18,
|
|
45
|
+
/** Standard game text */
|
|
46
|
+
large: 24,
|
|
47
|
+
/** Headings and prompts */
|
|
48
|
+
heading: 32,
|
|
49
|
+
/** Big numbers, single letters */
|
|
50
|
+
display: 48,
|
|
51
|
+
/** Hero text */
|
|
52
|
+
hero: 64,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=spacing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spacing.js","sourceRoot":"","sources":["../../tokens/spacing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,0DAA0D;IAC1D,WAAW,EAAE;QACX,uCAAuC;QACvC,GAAG,EAAE,EAAE;QACP,sCAAsC;QACtC,WAAW,EAAE,EAAE;QACf,+CAA+C;QAC/C,KAAK,EAAE,EAAE;KACV;IAED,4BAA4B;IAC5B,OAAO,EAAE;QACP,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,GAAG,EAAE,EAAE;KACR;IAED,kEAAkE;IAClE,GAAG,EAAE;QACH,yCAAyC;QACzC,GAAG,EAAE,CAAC;QACN,sBAAsB;QACtB,WAAW,EAAE,EAAE;QACf,mBAAmB;QACnB,QAAQ,EAAE,EAAE;KACb;IAED,yCAAyC;IACzC,MAAM,EAAE;QACN,EAAE,EAAE,CAAC;QACL,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,IAAI,EAAE,IAAI;KACX;IAED,wDAAwD;IACxD,QAAQ,EAAE;QACR,4BAA4B;QAC5B,IAAI,EAAE,EAAE;QACR,yBAAyB;QACzB,KAAK,EAAE,EAAE;QACT,2BAA2B;QAC3B,OAAO,EAAE,EAAE;QACX,kCAAkC;QAClC,OAAO,EAAE,EAAE;QACX,gBAAgB;QAChB,IAAI,EAAE,EAAE;KACT;CACO,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type KidsAudioContextType } from "./provider";
|
|
2
|
+
import type { PersonaId, CelebrationLevel } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Main voice hook — access all audio playback functions.
|
|
5
|
+
*
|
|
6
|
+
* ```tsx
|
|
7
|
+
* const { playPhrase, playSequence, muted, toggleMute } = useVoice();
|
|
8
|
+
* await playPhrase("number-5", "bloop");
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export declare function useVoice(): KidsAudioContextType;
|
|
12
|
+
/**
|
|
13
|
+
* Get a specific persona's configuration.
|
|
14
|
+
*
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const sunny = usePersona("sunny");
|
|
17
|
+
* console.log(sunny.name); // "Sunny"
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function usePersona(id: PersonaId): import("./types").Persona;
|
|
21
|
+
/**
|
|
22
|
+
* Synthesized sound effects (no audio files needed).
|
|
23
|
+
* Delegates to sfx/synth.ts — single source of truth for all sound synthesis.
|
|
24
|
+
*
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const { playTap, playPop, playCelebrationChime } = useSoundEffects();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function useSoundEffects(): {
|
|
30
|
+
playTap: () => void;
|
|
31
|
+
playPop: () => void;
|
|
32
|
+
playCelebrationChime: () => void;
|
|
33
|
+
playCorrect: () => void;
|
|
34
|
+
playIncorrect: () => void;
|
|
35
|
+
playWhoosh: () => void;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Celebration hook — triggers audio + returns phrase ID for visual overlay.
|
|
39
|
+
*
|
|
40
|
+
* ```tsx
|
|
41
|
+
* const { celebrate } = useCelebrate("sunny");
|
|
42
|
+
* const result = celebrate("medium"); // plays chime + returns a celebration phrase ID
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function useCelebrate(defaultPersona?: PersonaId): {
|
|
46
|
+
celebrate: (level: CelebrationLevel, personaId?: PersonaId) => Promise<{
|
|
47
|
+
level: CelebrationLevel;
|
|
48
|
+
phraseId: string;
|
|
49
|
+
persona: PersonaId;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../voice/hooks.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoB,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGzE,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3D;;;;;;;GAOG;AACH,wBAAgB,QAAQ,IAAI,oBAAoB,CAQ/C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,SAAS,6BAEvC;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe;;;;;;;EAuB9B;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,cAAc,GAAE,SAAmB;uBAK9C,gBAAgB,cAAc,SAAS;;;;;EAsBxD"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useContext, useCallback, useMemo } from "react";
|
|
3
|
+
import { KidsAudioContext } from "./provider";
|
|
4
|
+
import { PERSONAS, getRandomPhrase } from "./personas";
|
|
5
|
+
import * as sfxLib from "../sfx/synth";
|
|
6
|
+
/**
|
|
7
|
+
* Main voice hook — access all audio playback functions.
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const { playPhrase, playSequence, muted, toggleMute } = useVoice();
|
|
11
|
+
* await playPhrase("number-5", "bloop");
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function useVoice() {
|
|
15
|
+
const ctx = useContext(KidsAudioContext);
|
|
16
|
+
if (!ctx.player && typeof window !== "undefined") {
|
|
17
|
+
console.warn("useVoice() called outside <KidsAudioProvider>. Wrap your app in <KidsAudioProvider>.");
|
|
18
|
+
}
|
|
19
|
+
return ctx;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get a specific persona's configuration.
|
|
23
|
+
*
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const sunny = usePersona("sunny");
|
|
26
|
+
* console.log(sunny.name); // "Sunny"
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function usePersona(id) {
|
|
30
|
+
return useMemo(() => PERSONAS[id], [id]);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Synthesized sound effects (no audio files needed).
|
|
34
|
+
* Delegates to sfx/synth.ts — single source of truth for all sound synthesis.
|
|
35
|
+
*
|
|
36
|
+
* ```tsx
|
|
37
|
+
* const { playTap, playPop, playCelebrationChime } = useSoundEffects();
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function useSoundEffects() {
|
|
41
|
+
const { player, muted } = useContext(KidsAudioContext);
|
|
42
|
+
const guardedCall = useCallback((fn) => {
|
|
43
|
+
if (muted || !player)
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
fn(player.getContext());
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Audio context not available
|
|
50
|
+
}
|
|
51
|
+
}, [player, muted]);
|
|
52
|
+
const playTap = useCallback(() => guardedCall(sfxLib.playTap), [guardedCall]);
|
|
53
|
+
const playPop = useCallback(() => guardedCall(sfxLib.playPop), [guardedCall]);
|
|
54
|
+
const playCelebrationChime = useCallback(() => guardedCall(sfxLib.playCelebrationChime), [guardedCall]);
|
|
55
|
+
const playCorrect = useCallback(() => guardedCall(sfxLib.playCorrect), [guardedCall]);
|
|
56
|
+
const playIncorrect = useCallback(() => guardedCall(sfxLib.playIncorrect), [guardedCall]);
|
|
57
|
+
const playWhoosh = useCallback(() => guardedCall(sfxLib.playWhoosh), [guardedCall]);
|
|
58
|
+
return { playTap, playPop, playCelebrationChime, playCorrect, playIncorrect, playWhoosh };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Celebration hook — triggers audio + returns phrase ID for visual overlay.
|
|
62
|
+
*
|
|
63
|
+
* ```tsx
|
|
64
|
+
* const { celebrate } = useCelebrate("sunny");
|
|
65
|
+
* const result = celebrate("medium"); // plays chime + returns a celebration phrase ID
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function useCelebrate(defaultPersona = "sunny") {
|
|
69
|
+
const { playPhrase } = useContext(KidsAudioContext);
|
|
70
|
+
const sfx = useSoundEffects();
|
|
71
|
+
const celebrate = useCallback(async (level, personaId) => {
|
|
72
|
+
const persona = personaId ?? defaultPersona;
|
|
73
|
+
const phraseId = getRandomPhrase(persona, "celebration");
|
|
74
|
+
if (level === "small") {
|
|
75
|
+
sfx.playCorrect();
|
|
76
|
+
}
|
|
77
|
+
else if (level === "medium") {
|
|
78
|
+
sfx.playCelebrationChime();
|
|
79
|
+
await new Promise((r) => setTimeout(r, 600));
|
|
80
|
+
await playPhrase(phraseId, persona);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
sfx.playCelebrationChime();
|
|
84
|
+
await new Promise((r) => setTimeout(r, 800));
|
|
85
|
+
await playPhrase(phraseId, persona);
|
|
86
|
+
}
|
|
87
|
+
return { level, phraseId, persona };
|
|
88
|
+
}, [defaultPersona, playPhrase, sfx]);
|
|
89
|
+
return { celebrate };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../voice/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { OpenAIVoice, PersonaId, Persona, VoiceScriptLine, VoiceScript, AudioState, AudioProviderConfig, CelebrationLevel, } from "./types";
|
|
2
|
+
export { PERSONAS, getPersona, getRandomPhrase } from "./personas";
|
|
3
|
+
export { AudioPlayer } from "./player";
|
|
4
|
+
export { KidsAudioProvider } from "./provider";
|
|
5
|
+
export { useVoice, usePersona, useSoundEffects, useCelebrate, } from "./hooks";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../voice/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,SAAS,EACT,OAAO,EACP,eAAe,EACf,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { PERSONAS, getPersona, getRandomPhrase } from "./personas";
|
|
2
|
+
export { AudioPlayer } from "./player";
|
|
3
|
+
export { KidsAudioProvider } from "./provider";
|
|
4
|
+
export { useVoice, usePersona, useSoundEffects, useCelebrate, } from "./hooks";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../voice/index.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACL,QAAQ,EACR,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Persona, PersonaId } from "./types";
|
|
2
|
+
export declare const PERSONAS: Record<PersonaId, Persona>;
|
|
3
|
+
/** Get a persona by ID */
|
|
4
|
+
export declare function getPersona(id: PersonaId): Persona;
|
|
5
|
+
/** Get a random phrase ID from a persona's phrase pool */
|
|
6
|
+
export declare function getRandomPhrase(personaId: PersonaId, pool: "celebration" | "encouragement" | "tryAgain"): string;
|
|
7
|
+
//# sourceMappingURL=personas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personas.d.ts","sourceRoot":"","sources":["../../voice/personas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAElD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAmI/C,CAAC;AAEF,0BAA0B;AAC1B,wBAAgB,UAAU,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAEjD;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,UAAU,GACjD,MAAM,CASR"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export const PERSONAS = {
|
|
2
|
+
sunny: {
|
|
3
|
+
id: "sunny",
|
|
4
|
+
name: "Sunny",
|
|
5
|
+
role: "Encouraging guide / lead teacher",
|
|
6
|
+
description: "Warm, patient, enthusiastic. The lead teacher voice that guides kids through activities.",
|
|
7
|
+
openaiVoice: "shimmer",
|
|
8
|
+
speed: 0.9,
|
|
9
|
+
fallback: { rate: 0.85, pitch: 1.15, preferFemale: true },
|
|
10
|
+
celebrationPhrases: [
|
|
11
|
+
"celebrate-sunny-woohoo",
|
|
12
|
+
"celebrate-sunny-amazing",
|
|
13
|
+
"celebrate-sunny-wonderful",
|
|
14
|
+
"celebrate-sunny-star",
|
|
15
|
+
],
|
|
16
|
+
encouragementPhrases: [
|
|
17
|
+
"encourage-sunny-letsgo",
|
|
18
|
+
"encourage-sunny-youcan",
|
|
19
|
+
"encourage-sunny-tryit",
|
|
20
|
+
"encourage-sunny-believe",
|
|
21
|
+
],
|
|
22
|
+
tryAgainPhrases: [
|
|
23
|
+
"tryagain-sunny-oops",
|
|
24
|
+
"tryagain-sunny-almost",
|
|
25
|
+
"tryagain-sunny-onemore",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
bloop: {
|
|
29
|
+
id: "bloop",
|
|
30
|
+
name: "Bloop",
|
|
31
|
+
role: "Silly sidekick / celebrations",
|
|
32
|
+
description: "Goofy, excited, makes funny sounds. Comic relief and celebration specialist.",
|
|
33
|
+
openaiVoice: "fable",
|
|
34
|
+
speed: 1.0,
|
|
35
|
+
fallback: { rate: 0.95, pitch: 1.25, preferFemale: false },
|
|
36
|
+
celebrationPhrases: [
|
|
37
|
+
"celebrate-bloop-youdid",
|
|
38
|
+
"celebrate-bloop-wow",
|
|
39
|
+
"celebrate-bloop-incredible",
|
|
40
|
+
"celebrate-bloop-bloop",
|
|
41
|
+
],
|
|
42
|
+
encouragementPhrases: [
|
|
43
|
+
"encourage-bloop-cmon",
|
|
44
|
+
"encourage-bloop-gogogo",
|
|
45
|
+
"encourage-bloop-ready",
|
|
46
|
+
],
|
|
47
|
+
tryAgainPhrases: [
|
|
48
|
+
"tryagain-bloop-uhoh",
|
|
49
|
+
"tryagain-bloop-oopsie",
|
|
50
|
+
"tryagain-bloop-again",
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
maple: {
|
|
54
|
+
id: "maple",
|
|
55
|
+
name: "Maple",
|
|
56
|
+
role: "Calm narrator / phonics teacher",
|
|
57
|
+
description: "Gentle, storytelling tone. For quieter learning moments and phonics instruction.",
|
|
58
|
+
openaiVoice: "nova",
|
|
59
|
+
speed: 0.85,
|
|
60
|
+
fallback: { rate: 0.8, pitch: 1.1, preferFemale: true },
|
|
61
|
+
celebrationPhrases: [
|
|
62
|
+
"celebrate-maple-beautiful",
|
|
63
|
+
"celebrate-maple-welldone",
|
|
64
|
+
"celebrate-maple-lovely",
|
|
65
|
+
"celebrate-maple-proud",
|
|
66
|
+
],
|
|
67
|
+
encouragementPhrases: [
|
|
68
|
+
"encourage-maple-listen",
|
|
69
|
+
"encourage-maple-gently",
|
|
70
|
+
"encourage-maple-take-time",
|
|
71
|
+
],
|
|
72
|
+
tryAgainPhrases: [
|
|
73
|
+
"tryagain-maple-okay",
|
|
74
|
+
"tryagain-maple-close",
|
|
75
|
+
"tryagain-maple-try",
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
fizz: {
|
|
79
|
+
id: "fizz",
|
|
80
|
+
name: "Fizz",
|
|
81
|
+
role: "Curious explorer / question-asker",
|
|
82
|
+
description: "Energetic, always wondering 'what happens if...?' Great for discovery and experimentation.",
|
|
83
|
+
openaiVoice: "echo",
|
|
84
|
+
speed: 0.95,
|
|
85
|
+
fallback: { rate: 0.9, pitch: 1.2, preferFemale: false },
|
|
86
|
+
celebrationPhrases: [
|
|
87
|
+
"celebrate-fizz-knew",
|
|
88
|
+
"celebrate-fizz-cool",
|
|
89
|
+
"celebrate-fizz-look",
|
|
90
|
+
"celebrate-fizz-whoa",
|
|
91
|
+
],
|
|
92
|
+
encouragementPhrases: [
|
|
93
|
+
"encourage-fizz-wonder",
|
|
94
|
+
"encourage-fizz-whathappens",
|
|
95
|
+
"encourage-fizz-trythis",
|
|
96
|
+
],
|
|
97
|
+
tryAgainPhrases: [
|
|
98
|
+
"tryagain-fizz-hmm",
|
|
99
|
+
"tryagain-fizz-interesting",
|
|
100
|
+
"tryagain-fizz-whatif",
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
pip: {
|
|
104
|
+
id: "pip",
|
|
105
|
+
name: "Pip",
|
|
106
|
+
role: "Gentle cheerleader / hype friend",
|
|
107
|
+
description: "Softer than Bloop but still excited. Great for encouragement without being over-the-top.",
|
|
108
|
+
openaiVoice: "alloy",
|
|
109
|
+
speed: 0.9,
|
|
110
|
+
fallback: { rate: 0.85, pitch: 1.15, preferFemale: true },
|
|
111
|
+
celebrationPhrases: [
|
|
112
|
+
"celebrate-pip-yay",
|
|
113
|
+
"celebrate-pip-hooray",
|
|
114
|
+
"celebrate-pip-great",
|
|
115
|
+
"celebrate-pip-fantastic",
|
|
116
|
+
],
|
|
117
|
+
encouragementPhrases: [
|
|
118
|
+
"encourage-pip-youcan",
|
|
119
|
+
"encourage-pip-together",
|
|
120
|
+
"encourage-pip-believe",
|
|
121
|
+
],
|
|
122
|
+
tryAgainPhrases: [
|
|
123
|
+
"tryagain-pip-okay",
|
|
124
|
+
"tryagain-pip-gotthis",
|
|
125
|
+
"tryagain-pip-onemore",
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
/** Get a persona by ID */
|
|
130
|
+
export function getPersona(id) {
|
|
131
|
+
return PERSONAS[id];
|
|
132
|
+
}
|
|
133
|
+
/** Get a random phrase ID from a persona's phrase pool */
|
|
134
|
+
export function getRandomPhrase(personaId, pool) {
|
|
135
|
+
const persona = PERSONAS[personaId];
|
|
136
|
+
const phrases = pool === "celebration"
|
|
137
|
+
? persona.celebrationPhrases
|
|
138
|
+
: pool === "encouragement"
|
|
139
|
+
? persona.encouragementPhrases
|
|
140
|
+
: persona.tryAgainPhrases;
|
|
141
|
+
return phrases[Math.floor(Math.random() * phrases.length)];
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=personas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personas.js","sourceRoot":"","sources":["../../voice/personas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { PersonaId } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* AudioPlayer — plays pre-generated .mp3 files from public/audio/.
|
|
4
|
+
* No runtime API calls. All audio is static files served by the app.
|
|
5
|
+
*/
|
|
6
|
+
export declare class AudioPlayer {
|
|
7
|
+
private audioElement;
|
|
8
|
+
private audioContext;
|
|
9
|
+
private muted;
|
|
10
|
+
private volume;
|
|
11
|
+
private cancelFlag;
|
|
12
|
+
private audioBasePath;
|
|
13
|
+
private storagePrefix;
|
|
14
|
+
private unlocked;
|
|
15
|
+
private cachedVoices;
|
|
16
|
+
constructor(storagePrefix: string, audioBasePath: string);
|
|
17
|
+
/** Must be called from a user interaction to unlock audio on mobile browsers */
|
|
18
|
+
unlock(): void;
|
|
19
|
+
private getAudioElement;
|
|
20
|
+
private getAudioContext;
|
|
21
|
+
/** Get the Web Audio context for SFX synthesis */
|
|
22
|
+
getContext(): AudioContext;
|
|
23
|
+
/**
|
|
24
|
+
* Play a pre-generated phrase by ID.
|
|
25
|
+
* Resolves to the audio file at: {audioBasePath}/{personaId}/{phraseId}.mp3
|
|
26
|
+
*/
|
|
27
|
+
playPhrase(phraseId: string, personaId: PersonaId): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Play a sequence of phrases with pauses between them.
|
|
30
|
+
* Useful for Letter Land's "A... ah... Apple" pattern.
|
|
31
|
+
*/
|
|
32
|
+
playSequence(phrases: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
persona: PersonaId;
|
|
35
|
+
}>, pauseMs?: number): Promise<void>;
|
|
36
|
+
/** Play an audio file by URL */
|
|
37
|
+
private playAudioFile;
|
|
38
|
+
/** Fallback: speak text using Web Speech API with persona voice settings */
|
|
39
|
+
speakDynamic(text: string, personaId: PersonaId): Promise<void>;
|
|
40
|
+
/** Cancel any in-progress audio playback */
|
|
41
|
+
cancel(): void;
|
|
42
|
+
/** Set muted state */
|
|
43
|
+
setMuted(muted: boolean): void;
|
|
44
|
+
/** Get muted state */
|
|
45
|
+
isMuted(): boolean;
|
|
46
|
+
/** Set volume (0.0 to 1.0) */
|
|
47
|
+
setVolume(vol: number): void;
|
|
48
|
+
/** Get volume */
|
|
49
|
+
getVolume(): number;
|
|
50
|
+
private pause;
|
|
51
|
+
/** Clean up resources */
|
|
52
|
+
destroy(): void;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=player.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../voice/player.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC;;;GAGG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAO;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAA8B;gBAEtC,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM;IAmBxD,gFAAgF;IAChF,MAAM,IAAI,IAAI;IAmBd,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IAavB,kDAAkD;IAClD,UAAU,IAAI,YAAY;IAI1B;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvE;;;OAGG;IACG,YAAY,CAChB,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,SAAS,CAAA;KAAE,CAAC,EAClD,OAAO,SAAM,GACZ,OAAO,CAAC,IAAI,CAAC;IAYhB,gCAAgC;IAChC,OAAO,CAAC,aAAa;IA2CrB,4EAA4E;IACtE,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCrE,4CAA4C;IAC5C,MAAM,IAAI,IAAI;IAed,sBAAsB;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAU9B,sBAAsB;IACtB,OAAO,IAAI,OAAO;IAIlB,8BAA8B;IAC9B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAU5B,iBAAiB;IACjB,SAAS,IAAI,MAAM;IAInB,OAAO,CAAC,KAAK;IAIb,yBAAyB;IACzB,OAAO,IAAI,IAAI;CAQhB"}
|