From 2c0c2f270158d17101e3dee629cac5b456dca0b7 Mon Sep 17 00:00:00 2001 From: Markus Graf Date: Sun, 29 Mar 2026 13:45:54 +0200 Subject: [PATCH] feat(03-02): implement word mode for typing engine - Add createWordExerciseState() that splits words into letters with boundaries - Add getCurrentWordIndex() helper for UI word tracking - Extend TypingExerciseState with mode, words, wordBoundaries fields - Backwards compatible: createExerciseState sets mode='letters' - All 85 tests pass Co-Authored-By: Claude Opus 4.6 (1M context) --- src/game/typing.ts | 38 ++++++++++++++++++++++++++++++++++++++ src/types.ts | 3 +++ 2 files changed, 41 insertions(+) diff --git a/src/game/typing.ts b/src/game/typing.ts index adf9ed0..0691707 100644 --- a/src/game/typing.ts +++ b/src/game/typing.ts @@ -40,9 +40,47 @@ export function createExerciseState(level: number): TypingExerciseState { correctStreak: 0, totalCorrect: 0, totalErrors: 0, + mode: "letters", + words: undefined, + wordBoundaries: undefined, }; } +export function createWordExerciseState(words: string[]): TypingExerciseState { + const letters: string[] = []; + const wordBoundaries: number[] = []; + for (const word of words) { + wordBoundaries.push(letters.length); + for (const ch of word) { + letters.push(ch); + } + } + return { + currentLetterIndex: 0, + letters, + intervalMs: 0, + correctStreak: 0, + totalCorrect: 0, + totalErrors: 0, + mode: "words", + words, + wordBoundaries, + }; +} + +export function getCurrentWordIndex(state: TypingExerciseState): number { + if (!state.wordBoundaries || state.wordBoundaries.length === 0) return 0; + let wordIndex = 0; + for (let i = 0; i < state.wordBoundaries.length; i++) { + if (state.wordBoundaries[i]! <= state.currentLetterIndex) { + wordIndex = i; + } else { + break; + } + } + return wordIndex; +} + export function handleKeyPress( state: TypingExerciseState, pressedKey: string, diff --git a/src/types.ts b/src/types.ts index 6867192..0d39db8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -74,6 +74,9 @@ export interface TypingExerciseState { correctStreak: number; totalCorrect: number; totalErrors: number; + mode: "letters" | "words"; + words?: string[]; + wordBoundaries?: number[]; } // Screen names