feat: Puente Mayúscula-Minúscula + TTS SpeechSynthesis + E2E fixes
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
import type { Stage } from '../types'
|
||||
|
||||
function vocalChoice(vocal: string, correctLabel: string, distractors: string[], emoji: string, instruction: string, audioText?: string) {
|
||||
const options = [
|
||||
{ label: correctLabel, correct: true, id: vocal },
|
||||
...distractors.map((d) => ({ label: d, correct: false, id: d })),
|
||||
].sort(() => Math.random() - 0.5)
|
||||
|
||||
return {
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'eleccion' as const,
|
||||
prerequisites: getVocalPrereqs(vocal),
|
||||
content: {
|
||||
instruction,
|
||||
text: correctLabel,
|
||||
options,
|
||||
audioText: audioText || `${correctLabel}`,
|
||||
emoji,
|
||||
color: getVocalColor(vocal),
|
||||
},
|
||||
functionalContext: `Identificar la vocal ${vocal.toUpperCase()} en diferentes contextos.`,
|
||||
errorType: 'confusion-letra' as const,
|
||||
}
|
||||
}
|
||||
|
||||
function getVocalColor(vocal: string): string {
|
||||
const colors: Record<string, string> = { a: '#FF6B6B', o: '#4ECDC4', i: '#45B7D1', e: '#96CEB4', u: '#DDA0DD' }
|
||||
return colors[vocal] || '#FF6B6B'
|
||||
}
|
||||
|
||||
function getVocalPrereqs(vocal: string): string[] {
|
||||
const order = ['a', 'o', 'i', 'e', 'u']
|
||||
const idx = order.indexOf(vocal)
|
||||
if (idx === 0) return ['lec2-segmentar-pato']
|
||||
return [`vocal-${order[idx - 1]}-completa`]
|
||||
}
|
||||
|
||||
const gestos: Record<string, { gesto: string, palabra: string, fonema: string, emoji: string }> = {
|
||||
a: { gesto: 'Abrí la mano como una boca grande', palabra: 'ÁRBOL', fonema: 'AAA', emoji: '🌳' },
|
||||
o: { gesto: 'Redondeá la mano como una O', palabra: 'OJO', fonema: 'OOO', emoji: '👁️' },
|
||||
i: { gesto: 'Señalá el cielo con el dedo', palabra: 'INDIO', fonema: 'III', emoji: '🪶' },
|
||||
e: { gesto: 'Estirá la mano como una trompa de elefante', palabra: 'ELEFANTE', fonema: 'EEE', emoji: '🐘' },
|
||||
u: { gesto: 'Señalá el suelo con la mano', palabra: 'UNO', fonema: 'UUU', emoji: '1️⃣' },
|
||||
}
|
||||
|
||||
function buildVocalExercises(vocal: string, index: number): any[] {
|
||||
const g = gestos[vocal]
|
||||
const exs: any[] = []
|
||||
|
||||
// Ejercicio de gesto (sensibilizacion)
|
||||
exs.push({
|
||||
id: `lec3-${vocal}-gesto`,
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'sensibilizacion',
|
||||
prerequisites: getVocalPrereqs(vocal),
|
||||
content: {
|
||||
instruction: `Hacé así para la ${vocal.toUpperCase()}: ${g.gesto}`,
|
||||
audioText: `Esta es la letra ${vocal.toUpperCase()}. ${g.palabra}. ${g.fonema}`,
|
||||
emoji: g.emoji,
|
||||
color: getVocalColor(vocal),
|
||||
},
|
||||
functionalContext: `Aprender el gesto Borel-Maisonny para la vocal ${vocal.toUpperCase()}.`,
|
||||
})
|
||||
|
||||
// Reconocer la vocal entre otras
|
||||
exs.push(vocalChoice(
|
||||
vocal, vocal.toUpperCase(),
|
||||
['A', 'O', 'I', 'E', 'U'].filter((v) => v !== vocal.toUpperCase()),
|
||||
g.emoji,
|
||||
`Señalá la letra ${vocal.toUpperCase()}`,
|
||||
))
|
||||
|
||||
// ¿Con qué vocal empieza?
|
||||
exs.push({
|
||||
id: `lec3-${vocal}-inicial`,
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'eleccion',
|
||||
prerequisites: [`lec3-${vocal}-gesto`],
|
||||
content: {
|
||||
instruction: `¿Con qué vocal empieza ${g.palabra}?`,
|
||||
text: g.palabra,
|
||||
audioText: `${g.palabra} empieza con ${vocal.toUpperCase()}`,
|
||||
options: [
|
||||
{ label: vocal.toUpperCase(), correct: true, id: vocal },
|
||||
...['A', 'O', 'I', 'E', 'U'].filter((v) => v !== vocal.toUpperCase()).slice(0, 2).map((v) => ({ label: v, correct: false, id: v })),
|
||||
],
|
||||
emoji: g.emoji,
|
||||
},
|
||||
functionalContext: `Reconocer la vocal inicial en palabras.`,
|
||||
errorType: 'discriminacion-auditiva',
|
||||
})
|
||||
|
||||
// Emparejar imagen con vocal
|
||||
exs.push({
|
||||
id: `lec3-${vocal}-emparejar`,
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'conciencia',
|
||||
prerequisites: [`lec3-${vocal}-inicial`],
|
||||
content: {
|
||||
instruction: `¿Cuál empieza con ${vocal.toUpperCase()}?`,
|
||||
audioText: `¿Qué empieza con ${vocal.toUpperCase()}?`,
|
||||
options: [
|
||||
{ label: g.emoji, correct: true, id: vocal },
|
||||
...Object.entries(gestos)
|
||||
.filter(([k]) => k !== vocal)
|
||||
.slice(0, 2)
|
||||
.map(([k, v]) => ({ label: v.emoji, correct: false, id: k })),
|
||||
],
|
||||
},
|
||||
functionalContext: `Asociar la vocal con imágenes que empiezan con ella.`,
|
||||
errorType: 'confusion-letra',
|
||||
})
|
||||
|
||||
// Discriminación auditiva
|
||||
exs.push({
|
||||
id: `lec3-${vocal}-auditiva`,
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'conciencia',
|
||||
prerequisites: [`lec3-${vocal}-emparejar`],
|
||||
content: {
|
||||
instruction: `Escuchá bien. ¿Cuál suena como ${vocal.toUpperCase()}?`,
|
||||
audioText: `${g.fonema}... ${g.fonema}... como ${g.palabra}`,
|
||||
options: [
|
||||
{ label: vocal.toUpperCase(), correct: true, id: vocal },
|
||||
...['A', 'O', 'I', 'E', 'U'].filter((v) => v !== vocal.toUpperCase()).slice(0, 2).map((v) => ({ label: v, correct: false, id: v })),
|
||||
],
|
||||
emoji: '👂',
|
||||
},
|
||||
functionalContext: `Discriminar auditivamente la vocal ${vocal.toUpperCase()}.`,
|
||||
errorType: 'discriminacion-auditiva',
|
||||
})
|
||||
|
||||
// Escribir con el dedo (trazo)
|
||||
exs.push({
|
||||
id: `lec3-${vocal}-trazo`,
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'trazo',
|
||||
prerequisites: [`lec3-${vocal}-auditiva`],
|
||||
content: {
|
||||
instruction: `Trazá la letra ${vocal.toUpperCase()} con tu dedo`,
|
||||
text: vocal.toUpperCase(),
|
||||
audioText: `Trazá la ${vocal.toUpperCase()}`,
|
||||
color: getVocalColor(vocal),
|
||||
emoji: g.emoji,
|
||||
},
|
||||
functionalContext: `Desarrollar memoria motriz de la vocal ${vocal.toUpperCase()}.`,
|
||||
errorType: 'confusion-letra',
|
||||
})
|
||||
|
||||
// Repaso final
|
||||
exs.push({
|
||||
id: `lec3-${vocal}-completa`,
|
||||
stage: 3,
|
||||
skillCode: `vocal-${vocal}`,
|
||||
type: 'eleccion',
|
||||
prerequisites: [`lec3-${vocal}-trazo`],
|
||||
content: {
|
||||
instruction: `¡Repaso! Señalá la letra ${vocal.toUpperCase()}`,
|
||||
audioText: `Buscá la ${vocal.toUpperCase()}`,
|
||||
options: [
|
||||
{ label: vocal.toUpperCase(), correct: true, id: vocal },
|
||||
...['A', 'O', 'I', 'E', 'U'].filter((v) => v !== vocal.toUpperCase()).map((v) => ({ label: v, correct: false, id: v })),
|
||||
],
|
||||
emoji: g.emoji,
|
||||
color: getVocalColor(vocal),
|
||||
},
|
||||
functionalContext: `Consolidar reconocimiento de la vocal ${vocal.toUpperCase()}.`,
|
||||
errorType: 'confusion-letra',
|
||||
})
|
||||
|
||||
return exs
|
||||
}
|
||||
|
||||
// Additional cross-vowel discrimination exercises
|
||||
const mixedExercises: any[] = [
|
||||
{
|
||||
id: 'lec3-mix-ae',
|
||||
stage: 3,
|
||||
skillCode: 'vocal-discriminacion',
|
||||
type: 'eleccion',
|
||||
prerequisites: ['lec3-a-completa', 'lec3-e-completa'],
|
||||
content: {
|
||||
instruction: '¿Cuál es la A y cuál es la E? Señalá la A',
|
||||
options: [
|
||||
{ label: 'A', correct: true, id: 'a' },
|
||||
{ label: 'E', correct: false, id: 'e' },
|
||||
{ label: 'O', correct: false, id: 'o' },
|
||||
],
|
||||
emoji: '🔤',
|
||||
},
|
||||
functionalContext: 'Discriminar entre A y E.',
|
||||
errorType: 'confusion-letra',
|
||||
},
|
||||
{
|
||||
id: 'lec3-mix-iu',
|
||||
stage: 3,
|
||||
skillCode: 'vocal-discriminacion',
|
||||
type: 'eleccion',
|
||||
prerequisites: ['lec3-i-completa', 'lec3-u-completa'],
|
||||
content: {
|
||||
instruction: '¿Cuál es la I y cuál es la U? Señalá la I',
|
||||
options: [
|
||||
{ label: 'I', correct: true, id: 'i' },
|
||||
{ label: 'U', correct: false, id: 'u' },
|
||||
{ label: 'A', correct: false, id: 'a' },
|
||||
],
|
||||
emoji: '🔤',
|
||||
},
|
||||
functionalContext: 'Discriminar entre I y U.',
|
||||
errorType: 'confusion-letra',
|
||||
},
|
||||
{
|
||||
id: 'lec3-mix-ao',
|
||||
stage: 3,
|
||||
skillCode: 'vocal-discriminacion',
|
||||
type: 'eleccion',
|
||||
prerequisites: ['lec3-a-completa', 'lec3-o-completa'],
|
||||
content: {
|
||||
instruction: 'Señalá la letra O',
|
||||
options: [
|
||||
{ label: 'A', correct: false, id: 'a' },
|
||||
{ label: 'O', correct: true, id: 'o' },
|
||||
{ label: 'U', correct: false, id: 'u' },
|
||||
],
|
||||
emoji: '🔤',
|
||||
},
|
||||
functionalContext: 'Discriminar entre A y O.',
|
||||
errorType: 'confusion-letra',
|
||||
},
|
||||
{
|
||||
id: 'lec3-mix-ei',
|
||||
stage: 3,
|
||||
skillCode: 'vocal-discriminacion',
|
||||
type: 'eleccion',
|
||||
prerequisites: ['lec3-e-completa', 'lec3-i-completa'],
|
||||
content: {
|
||||
instruction: 'Señalá la E',
|
||||
options: [
|
||||
{ label: 'I', correct: false, id: 'i' },
|
||||
{ label: 'E', correct: true, id: 'e' },
|
||||
{ label: 'U', correct: false, id: 'u' },
|
||||
],
|
||||
emoji: '🔤',
|
||||
},
|
||||
functionalContext: 'Discriminar entre E e I.',
|
||||
errorType: 'confusion-letra',
|
||||
},
|
||||
]
|
||||
|
||||
const exercises: any[] = []
|
||||
for (const [i, vocal] of ['a', 'o', 'i', 'e', 'u'].entries()) {
|
||||
exercises.push(...buildVocalExercises(vocal, i))
|
||||
}
|
||||
exercises.push(...mixedExercises)
|
||||
|
||||
const stage: Stage = {
|
||||
id: 3,
|
||||
name: 'Vocales A-O-I-E-U',
|
||||
description: 'Aprender las 5 vocales con gestos Borel-Maisonny, discriminación auditiva y visual, trazo y reconocimiento.',
|
||||
exercises,
|
||||
}
|
||||
|
||||
export default stage
|
||||
Reference in New Issue
Block a user