mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-01 06:06:05 +08:00
The overview rendered four anonymous module tiles and half a page of empty space, which undersold 63 existing lessons. It now stacks four module sections that list every lesson as a chip with status and variant dots, and the header states the real scope: 4 modules, 63 lessons, 262 variations. Mastery moves from a flat two-variation counter to per-module thresholds (3/4/4/5) backed by persisted variant signatures — a hash over each roll's expected answers. That triples the practice runs to full mastery without authoring a single new lesson, and fixes a dedup hole where the same variation counted twice after a page reload. Existing progress is preserved: `done` is sticky, so raising the bar never revokes a check mark someone already earned. Also fixes `yarn test`, which passed quoted globs Node 20 does not expand — it exited 0 without running a single test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
export interface LessonProgress {
|
|
best: number
|
|
done: boolean
|
|
assessmentVersion?: number
|
|
successfulVariants?: number
|
|
/**
|
|
* Signatures of the rolled scenarios already passed for this lesson, so the
|
|
* same variation cannot be counted twice — not even across page reloads.
|
|
*/
|
|
variantSignatures?: string[]
|
|
}
|
|
|
|
export type LearnProgress = Record<string, Record<string, LessonProgress>>
|
|
|
|
export interface LearnConfig {
|
|
tts: boolean
|
|
radioLevel: number
|
|
voice: string
|
|
audioChallenge: boolean
|
|
audioSpeed: number
|
|
}
|
|
|
|
export interface LearnState {
|
|
/** Legacy compatibility only. Classroom no longer awards or displays XP. */
|
|
xp: number
|
|
progress: LearnProgress
|
|
config: LearnConfig
|
|
/** Legacy compatibility only. All Classroom modules are now available. */
|
|
unlockedModules: string[]
|
|
}
|
|
|
|
export const CLASSROOM_ASSESSMENT_VERSION = 2
|
|
|
|
/**
|
|
* Clean variations required before a lesson counts as mastered. Short drills
|
|
* need less repetition than a full scenario, so the bar rises with module
|
|
* complexity. Every lesson rolls a fresh scenario, so the material is there.
|
|
*/
|
|
export const CLASSROOM_VARIANTS_BY_MODULE: Record<string, number> = {
|
|
'normalize': 3,
|
|
'arc': 4,
|
|
'decision-tree': 4,
|
|
'full-flight': 5,
|
|
}
|
|
|
|
export const CLASSROOM_VARIANTS_DEFAULT = 3
|
|
|
|
export function variantsForModule(moduleId: string): number {
|
|
return CLASSROOM_VARIANTS_BY_MODULE[moduleId] ?? CLASSROOM_VARIANTS_DEFAULT
|
|
}
|
|
|
|
export const LEARN_CONFIG_DEFAULTS: LearnConfig = {
|
|
tts: false,
|
|
radioLevel: 5,
|
|
voice: '',
|
|
audioChallenge: true,
|
|
audioSpeed: 0.85,
|
|
}
|
|
|
|
export function createDefaultLearnConfig(): LearnConfig {
|
|
return { ...LEARN_CONFIG_DEFAULTS }
|
|
}
|
|
|
|
export function createDefaultLearnState(): LearnState {
|
|
return {
|
|
xp: 0,
|
|
progress: {} as LearnProgress,
|
|
config: createDefaultLearnConfig(),
|
|
unlockedModules: [],
|
|
}
|
|
}
|