Imported from welllog/english_course (
AGENTS.md). Install upstream withnpx skills add welllog/english_course. Copyright stays with the author.
AGENTS.md
项目概览
这是一个面向低龄儿童的中英双语词汇学习 SPA。核心体验是点读闪卡、听音选图测验、角色陪伴、Web Speech API 双语朗读,以及轻量音效和动画反馈。
技术栈:
- React 19 + Vite 8
- Tailwind CSS v4
- Framer Motion
- Lucide React
- Web Speech API
常用命令
npm install
npm run dev
npm run build
npm run preview
提交前至少运行:
npm run build
关键目录
src/App.jsx:页面模式切换、分类筛选、角色和 BGM 状态。Flashcard/Quiz/MemoryMatch 通过lazy()+Suspense实现代码分割。src/components/Home.jsx:首页、分类入口、角色选择、BGM 开关。BubbleLayer为独立React.memo组件,避免泡泡动画触发整体重渲染。src/components/FlashcardMode.jsx:点读学习主流程,自动朗读、左右切词、卡片动画。使用useRef管理定时器防止切换词时闭包引用过期值。src/components/QuizMode.jsx:听音选图测验流程、答题反馈、奖励动画。从quizTemplates.quizWordIds过滤出预生成音频的词汇子集作为出题池。src/components/BadgeWall.jsx:徽章墙。贴纸可拖拽,位置持久化到 localStorage。src/components/AdventureMap.jsx:冒险地图,显示各分类学习进度。src/components/WordCollectionBook.jsx:词汇收集册。src/data/vocabulary.js:词汇数据,字段包括id、wordEn、wordZh、category、image。导出vocabularyByCategoryMap 供组件高效按分类查找。src/data/characters.js:角色配置(含口号 sloganEn/sloganZh)。src/data/ttsConfig.js:TTS 数据聚合——拟声词映射(onomatopoeiaMap)、提示语(phrases)、测验模板(quizTemplates)、徽章奖励模板(badgeRewardTemplates)、徽章口号(badgeSlogans)。所有需要发音的非词汇/角色文本在此维护。src/utils/tts.js:TTS 封装。AUDIO_MAP将文本映射到本地 WAV 路径,speak()/playBilingual()优先播放本地音频,无匹配时回退浏览器 Web Speech API。新增cancel()方法同时停止本地音频和浏览器 TTS。src/utils/audio.js:Web Audio API 音效和 BGM。AudioContext延迟初始化(getAudioCtx()),避免模块加载时立即创建。src/utils/badgeSystem.js:徽章进度逻辑,每个徽章包含sloganEn/sloganZh字段。public/assets/images/:运行时图片资源,词汇图片路径通常指向这里。public/assets/audio/:预生成的 TTS 音频文件(.wav),按vocabulary/、characters/、onomatopoeia/、phrases/、quiz/分目录存放。
开发注意事项
- 保持改动小而聚焦,不要顺手重构无关文件。当前工作区可能有未提交改动,先看
git status --short。 - 点读和测验都依赖
tts。修改src/utils/tts.js时要特别注意playId去重、_currentAudio打断、onend/onerror回调和超时兜底的竞态,避免重复朗读或锁住播放状态。 FlashcardMode使用useRef管理bilingualTimerRef/safetyTimerRef,切换词时先clearTimeout+tts.cancel()再重新播放,防止闭包引用过期值。QuizMode使用useRef管理quizTimeoutRef/comboTimeoutRef(替代了之前的window.quizTimeout),退出时清理。FlashcardMode的拟声词和提示语从src/data/ttsConfig.js导入,不再在组件内硬编码。- 图片资源以静态路径使用,例如
/assets/images/dog.svg。添加词汇时要同时确认图片存在。 - 添加新词汇后需要在
ttsConfig.js的quizTemplates.quizWordIds中添加 id 才能出现在测验出题池中。 - UI 面向儿童,视觉可以活泼,但按钮、切换、发音和答题反馈要稳定清楚;移动端触控优先。
- 不要移除
React.StrictMode来掩盖生命周期或副作用问题。 - 不要把浏览器原生 TTS、Web Audio 状态改成全局隐式副作用,除非能说明清楚播放队列和取消语义。
src/utils/audio.js中AudioContext延迟初始化(getAudioCtx()),不要在模块顶层直接new AudioContext()。
TTS 音频体系
数据源(JS 代码,唯一维护点)
所有需要发音的文本在以下文件中维护,组件直接 import 使用:
| 文件 | 内容 |
|---|---|
src/data/vocabulary.js |
词汇(wordEn/wordZh/sentenceEn/sentenceZh) |
src/data/characters.js |
角色口号(sloganEn/sloganZh) |
src/data/ttsConfig.js |
拟声词、提示语、测验模板、徽章奖励模板 |
运行时
src/utils/tts.js:TTS 引擎。内含AUDIO_MAP(文本→本地 WAV 路径映射,由tts-generator自动生成),findAudio()通过精确匹配 + 去引号规范化查找音频,playAudio()用<audio>元素播放本地文件。speak(text, lang):优先播放本地音频,无匹配时回退浏览器 Web Speech API。内部维护_currentAudio引用,确保新调用能打断上一次播放。playBilingual(wordEn, wordZh):英/中各自独立查找本地音频,串行播放(间隔 120ms)。英文播放失败时有超时兜底确保中文仍播放。cancel():同时停止本地音频(pause + reset)和浏览器 TTS(synth.cancel()),并递增playId使旧回调失效。getVoice()结果按语言前缀缓存到_voiceCache,避免重复遍历。public/assets/audio/:预生成的 WAV 文件,按vocabulary/、characters/、onomatopoeia/、phrases/、quiz/分目录存放。
音频生成工具(本地,不入仓库)
tts-generator/ 目录(已 gitignore)通过 dump_tts_data.js 读取上述 JS 数据文件,生成 WAV 和 AUDIO_MAP。
扩展流程(以添加新词汇为例):
- 编辑
src/data/vocabulary.js添加词汇条目 - 运行
cd tts-generator && make allmake build-audio-map→ 自动更新tts.js中的AUDIO_MAPmake vocabulary→ 生成对应的.wav文件到public/assets/audio/vocabulary/
- 若需在测验出题池中使用,在
src/data/ttsConfig.js的quizTemplates.quizWordIds中添加词汇 id
音频文件命名规则:{id}_000.wav(_000 是 mlx-audio 库的输出后缀,findAudio() 会自动拼接)。
验证建议
对涉及不同区域的改动,优先这样验:
- 数据或构建配置:运行
npm run build。 - 点读学习:进入任意分类,确认首个词只朗读一次英文和一次中文;点击卡片可重播;左右切词正常。
- 测验模式:确认题目朗读、正确/错误反馈、下一题切换正常。
- 音效/BGM:确认首次用户交互后音效能播放,BGM 开关能开始和停止。