feat: A1 短材料复现本单元理解词,并加入黑夜模式

课程内容
- 重写全部 21 个 A1 单元的听读材料,把本单元理解词织进听/读文本,
  单元内理解词复现率从约 20% 提升到约 77%(各单元 56–96%)。
- 修正 U01 房间号与机场大巴同为 thirty 的撞车(改为 17/30/40)。
- 校验器容差按级别读取(A1 为 8%),materials 覆盖率、字数、
  选项子串等校验全部通过;course_content_test 通过。

黑夜模式
- app_theme 拆分明/暗两套调色板,AppColors 随亮度切换;
  main 用 theme/darkTheme/themeMode + builder 镜像已解析亮度;
  主题偏好持久化到快照;进度页新增“外观主题”切换。

文档
- COURSE-PACK-JSON.md 更新 A1 词池覆盖(840/933)与材料复现约定。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-18 11:16:09 +09:00
co-authored by Claude Opus 4.8
parent 9ab08c25ef
commit febfd30f49
93 changed files with 72676 additions and 274 deletions
+29 -4
View File
@@ -1,4 +1,5 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:kouyu_english/core/a0_core.dart';
import 'package:kouyu_english/core/models.dart';
import 'package:kouyu_english/core/review_feedback.dart';
@@ -99,11 +100,35 @@ void main() {
);
// Multi-space on word item
expect(
ReviewFeedback.check(
review('A0-W08', 'three'),
' THREE ',
).complete,
ReviewFeedback.check(review('A0-W08', 'three'), ' THREE ').complete,
isTrue,
);
});
test('a dictation review needs the whole sentence that was played', () {
final item = review('A0-W24', 'water').copyWith(
skill: dictationSkill,
prompt: coreReviewVariant('A0-W24', 1).prompt,
);
expect(ReviewFeedback.check(item, 'water').complete, isFalse);
expect(ReviewFeedback.check(item, 'I like watter.').complete, isFalse);
expect(ReviewFeedback.check(item, 'i like water').complete, isTrue);
});
test('offline review variants rotate recall, dictation and speaking', () {
expect(coreReviewVariant('A0-P12', 0).skill, '回忆表达');
expect(coreReviewVariant('A0-P12', 1).skill, dictationSkill);
expect(coreReviewVariant('A0-P12', 2).skill, spokenRecallSkill);
expect(coreReviewVariant('A0-P12', 3).skill, '回忆表达');
for (final id in a0CoreItems.keys) {
final sentence = a0DictationSentences[id];
expect(sentence, isNotNull, reason: id);
// Each dictation sentence really contains the item it checks.
expect(
coreItemUsedIn(id, sentence!.sentence, word: a0CoreItems[id]),
isTrue,
reason: id,
);
}
});
}