Skip to content

fix(vocab-quiz): UI cluster — anti-cheat + UX (#190 #192 #193 #194)#196

Merged
davidortinau merged 3 commits intomainfrom
fix/vocab-quiz-ui-cluster-189-194
May 3, 2026
Merged

fix(vocab-quiz): UI cluster — anti-cheat + UX (#190 #192 #193 #194)#196
davidortinau merged 3 commits intomainfrom
fix/vocab-quiz-ui-cluster-189-194

Conversation

@davidortinau
Copy link
Copy Markdown
Owner

@davidortinau davidortinau commented May 3, 2026

Summary

Stream A of the Vocabulary Quiz bug cluster — UI-only fixes in src/SentenceStudio.UI/Pages/VocabQuiz.razor plus resource entries.

Closes #189
Closes #190
Closes #192
Closes #193
Closes #194

Changes

#189 — Strip legacy field readouts from Learning Details panel

Per Jayne's service-side repro tests on main (Repro189_SingleCorrectRecognitionAttempt_ProducesExpectedPanelState and Repro189_SingleCorrectRecognition_LegacyProductionFieldsRemainZero both pass), the service math is clean. The "2 attempts / 50% accuracy" confusion was a UI-rendering artifact — the panel was showing pre-streak-truth metadata (IsKnown, IsUserDeclared, VerificationState) alongside the current streak fields, conflating two different mental models of "is this word known".

Restructured the two stat grids in the panel into one streak-truth grid showing only the allowlist:

  • TotalAttempts, CorrectAttempts, Accuracy
  • CurrentStreak, ProductionInStreak, EffectiveStreak
  • MasteryScore
  • status badge (already kept)

Schema fields (IsKnown, VerificationState, ProductionAttempts, RecognitionAttempts, etc.) on VocabularyProgress are unchanged for sync/back-compat — only the rendering is tightened.

Also audited RecordPendingAttemptAsync call sites (NextItem, override-to-correct, DisposeAsync): no double-invocation found. The method is idempotent via the if (pendingAttempt == null) return; guard followed by pendingAttempt = null; — calling it twice is a no-op on the second call. The sentence-shortcut path at line 980 deliberately records one attempt per credited sentence, which is correct.

#194 — Anti-cheat: hide terms in Learning Details panel

The info (ⓘ) panel is opened mid-quiz. Previously it rendered the target word as a heading and the native gloss right under it — completely defeating the purpose of testing recognition. Now the panel shows ONLY the status badge and learning metadata. The terms are removed from this view entirely.

Learning Details panel — terms removed

#190 — Multiple-choice always has 4 options

Pre-fix, distractors were drawn from vocabItems (the active round). When the round was small (e.g. SRS DueOnly with 1–3 words), the user got 1–3 options, often making the answer obvious. Now we capture the FULL filtered+deduped vocabulary scope into a new distractorScope field during LoadVocabulary, and GenerateChoiceOptions samples from it. Verified e2e: with 7 review-due Korean words, the quiz now shows distractors like "to come out" / "to learn" / "cheese" alongside the correct answer drawn from the full 327-word vocab.

Quiz with full-scope distractors

#193 — Prompt audio follows prompt direction

Audio play button on the prompt previously called GetTargetAudioText / GetTargetAudioLanguage regardless of direction. When the user has prompt direction set to native→target (English shown, Korean hidden), pressing play would speak the Korean answer — leaking the test. New helpers GetPromptAudioText and GetPromptAudioLanguage switch on promptUsesNativeLanguage and return the actually-shown side. PlayWordAudio and the audioPromptAvailable check now route through these helpers.

#192 — Submit button for text-entry mode

Text-entry quiz mode previously required pressing Enter to submit. Added a visible Submit button inside the form. Disabled while input is empty; hidden once feedback is shown in non-correction mode. New resource key VocabQuiz_SubmitAnswer ("Submit" / "제출"). The button is type="submit" and routes through the existing @onsubmit="SubmitTypedAnswer" handler.

Files changed

  • src/SentenceStudio.UI/Pages/VocabQuiz.razor — all five fixes
  • src/SentenceStudio.Shared/Resources/Strings/AppResources.resxVocabQuiz_SubmitAnswer = "Submit"
  • src/SentenceStudio.Shared/Resources/Strings/AppResources.ko.resxVocabQuiz_SubmitAnswer = "제출"
  • src/SentenceStudio.Shared/Resources/Strings/AppResources.Designer.cs — auto-regenerated

Verification

Out of scope

Stream A of the Vocabulary Quiz bug cluster. Single-file Razor changes
(plus three resource entries) covering four issues:

#190 — Distractor pool now drawn from the full filtered+deduped
vocabulary scope, not just the active round. New `distractorScope`
field captured in LoadVocabulary; GenerateChoiceOptions samples from
it with a defensive vocabItems fallback. Guarantees 4 MC options even
when only 1 word remains to learn. Split CreateChoiceOption into
CreateChoiceOption(item) + CreateChoiceOptionForWord(word) to support
sampling from raw VocabularyWord.

#192 — Added explicit Submit button inside the text-entry form. Hidden
once feedback is shown in non-correction mode; disabled while userInput
is empty/whitespace. Wired through existing @onsubmit="SubmitTypedAnswer".
New resource key VocabQuiz_SubmitAnswer (en: "Submit", ko: "제출").

#193 — Prompt audio now follows prompt direction. New helpers
GetPromptAudioText / GetPromptAudioLanguage switch on
promptUsesNativeLanguage and return the side actually shown as the
prompt. PlayWordAudio and audioPromptAvailable now route through these
helpers so audio can never leak the answer in native→target prompts.

#194 — Removed TargetLanguageTerm and NativeLanguageTerm from the
Learning Details info panel (anti-cheat). Panel is opened mid-quiz;
showing either side leaks the answer. Status badge moved to its own
div; only stats and metadata remain.

Closes #190
Closes #192
Closes #193
Closes #194
)

The Learning Details panel was rendering pre-streak-truth metadata
(IsKnown, IsUserDeclared, VerificationState) alongside the current
streak-based fields. Per Jayne's service-side repro tests on main,
service math is clean — the '2 attempts / 50% accuracy' confusion in
issue #189 was a UI-rendering artifact.

Restructured the two stat grids into one streak-truth grid showing
only the allowlist:
  TotalAttempts, CorrectAttempts, Accuracy,
  CurrentStreak, ProductionInStreak, EffectiveStreak, MasteryScore
plus the status badge.

Schema fields on VocabularyProgress (IsKnown, VerificationState,
ProductionAttempts, RecognitionAttempts, etc.) are unchanged for
backward compatibility — only the panel rendering is tightened.

Audited RecordPendingAttemptAsync call sites (NextItem, override-to-
correct, DisposeAsync): no double-invocation. The method is idempotent
via 'if (pendingAttempt == null) return;' guard.
@davidortinau davidortinau merged commit c996299 into main May 3, 2026
2 of 6 checks passed
@davidortinau davidortinau deleted the fix/vocab-quiz-ui-cluster-189-194 branch May 3, 2026 02:16
davidortinau added a commit that referenced this pull request May 3, 2026
- PR #196 (Stream A UI fixes): closes #189/#190/#192/#193/#194
- PR #198 (Stream B scoring fix): closes #191
- PR #195 (test-only draft): superseded, closed
- Follow-ups filed: #197 (decouple Mastery from SessionRotation),
  #199 (test helper DifficultyWeight bug)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant