Mobile-first Next.js 15.5 + React 19 app to practice and review Spanish Bible passages with four memorization modes, local grading, Whisper transcription, and client-side persistence.
- Home & Hub: Home surfaces "Práctica" and a "Repaso" shortcut (unlocked after memorizing passages).
/practiceis the core workspace with a floating header, progress list, saved passages carousel, and a mobile flow to choose book -> chapter -> range -> mode. - Selection & Saved: Browse by book/chapter or search the full Bible (accentless matching, range parsing like "Juan 3:16-18"). The search index is cached in IndexedDB (
bible_search_index_v1) and versioned viapublic/bible_data/version.json. Ranges trigger a large-selection warning (~6 verses/~120 words) unlessbm_skip_large_selection_warningis set. Passages can be saved for later and reopened from the Guardados carousel. - Practice modes (all track 3 perfect attempts per mode via
modeCompletions):- Sequence: Chunks verses with
chunkVerseForSequenceMode, guarantees the expected fragment is always shown, deduplicates duplicates, provides hints and citation bubbles, and shows a perfect-score modal. - Stealth: Word-by-word recall with
HiddenInlineInput, per-word stats (WPM, streaks, mistakes), supports multi-verse ranges, and blocks navigation while active. - Type: Offline grading with
gradeAttempt, up to three timed peeks, Ctrl+Enter submit / Esc clear, diff + history, and perfect-score modal. - Speech: Records with
AudioRecorder+ silence detection and mic tester, dynamic recording limit from verse length, preview + editable transcription before grading, and navigation blocking while recording. Grading is local/naive; transcription goes through/api/transcribe.
- Sequence: Chunks verses with
- Read mode: Splits sanitized text by punctuation, reveals fragments stepwise, and requires reassembling the citation bubbles before marking "read." CTA routes back to practice with the same range.
- Progress & Memorization: History lives in ProgressList (snippets, last attempt time, completion badges). Clearing history resets mode completions.
lastSelectedVerseIdreopens the last passage. Memorized passages require all four modes at 3x100%. - Repaso: Unlocked for memorized (built-in) passages only.
/repasolists memorized items; Rally shuffles rounds across Sequence/Stealth/Type/Speech/Citas and requires 100% to advance (with an option to skip Speech). Citas drills "where is this passage?" using the book index; both sessions track completion and allow restarting.
- Bible data:
public/bible_data/_index.jsonlists books;<book>.jsonisstring[][](chapters -> verses). Text is sanitized client-side (strip/n, underscores, collapse whitespace).version.jsondrives search cache invalidation. - Progress (
bm_progress_v1): Stored inlocalStorageand mirrored to IndexedDB (bm_progress_db->kv). Shape matchesProgressState:verses[verseId]:reference,translation, optionaltext,source(built-in|custom),attempts(with diff tokens plus speech/stealth/sequence stats), andmodeCompletions.saved[verseId]: snapshot of the verse plusstart/endandsavedAt.lastSelectedVerseId: used to reopen the last selection in practice/mode routes.
- Caches & flags: Verse search cache lives at
bible_search_index_v1with version keybible_search_version. Large-range warning opt-out isbm_skip_large_selection_warning. All access goes throughlib/storage.ts(mirrors to IndexedDB and rebuildsmodeCompletionson load).
- POST
/api/transcribe: Multipart form withaudioand optionallanguage(esdefault). Rejects files >25MB or unsupported MIME (mp3/mp4/m4a/wav/webm). CallsWhisperService(gpt-4o-transcribe) and returns{ success, transcription, language, duration, segments }. Currently ignores anyexpectedTextand logs filename/type/size to the server console; runtime is not forced tonodejs. - No AI feedback endpoint is present; all grading is client-side and naive.
- Requires
OPENAI_API_KEYin.env.localfor Speech Mode and the API route.
- Prereqs: Node 20+, npm/pnpm. Tailwind CSS v4 tokens live in
app/globals.css; UI primitives are undercomponents/ui. - Install & run:
npm install,npm run dev(Turbopack). Build:npm run build; start:npm start. - Lint:
npm run lint. - Tests:
node --test tests/grade.test.js(grading) andnode --test tests/sequence-duplicates.test.js(Sequence duplicate handling); extra debug scripts live undertests/. - All state is local to the browser; clearing storage wipes attempts and saved passages. No server-side profiles or sync exist.
/api/transcribeshould addexport const runtime = 'nodejs'before deploying; consider passingexpectedTextinto the Whisper prompt and trimming request logging for privacy.- Speech Mode depends on client-side silence detection; the server does no content validation beyond size/type.
- Repaso and Rally exclude custom passages by design (only built-in verses count toward memorization).