Rebuild flashcard study screen from mockup, with real backend-backed stats

Study screen now matches the provided mockup's layout: a "Current
Deck" panel with a circular mastery-progress ring, and a "Spaced
Repetition" panel (New/Due/Learning/Review counts, streak, daily
goal) on either side of the card. Kept the app's real global sidebar
rather than the mockup's own nav, per direction.

Mastery/Streak/Daily Goal weren't tracked anywhere, so this adds real
backend support rather than showing fabricated numbers:
- Mastered = graduated to review state AND FSRS stability >= 21 days
  (FlashcardScheduleStore.MasteryStabilityDaysThreshold), stricter
  than the plain Review-state count
- CurrentStreakDays = consecutive UTC days with a review, computed
  from review-log timestamps in the new pure FlashcardScheduling
  .ComputeStreakDays (unit tested, habit-streak semantics: doesn't
  reset to 0 the instant a new day starts before today's first review)
- DailyGoal/ReviewsToday reuse the account's existing ReviewsPerDay
  setting rather than inventing a new one
Also removes the exit "X" button in favor of the same Escape/pause
menu the question-player flow uses (main.gd's shared app_menu, with
an "Exit Study Session" action), and adds a new circular_progress.gd
reusable ring control plus a real "Options" menu (bury/suspend the
current card) wired to schedule endpoints that had no UI before.
This commit is contained in:
hz
2026-07-21 23:20:14 -05:00
parent 72fb6e4776
commit 70aa425fde
10 changed files with 600 additions and 23 deletions
+22
View File
@@ -63,6 +63,28 @@ pattern as the rest of the API. Review submission is idempotent on `ClientReview
allows reverting the single most recent review on a card (it refuses if the card's revision has
moved since).
## Progress metrics
`GET /decks` (`FlashcardDeckSummary`) and `GET /counts` (`FlashcardDashboardCounts`) expose a few
derived stats the client can't compute itself:
- **Mastered** (per-deck, in `FlashcardDeckSummary.Mastered`) - a card counts once it's graduated
to `review` state *and* its FSRS stability has reached
`FlashcardScheduleStore.MasteryStabilityDaysThreshold` (21 days), the same rough "won't need
attention again soon" cutoff several SRS apps use. Deliberately stricter than the plain
`Review`-state count, which includes cards that just graduated and haven't proven stable yet.
- **CurrentStreakDays** (global, in `FlashcardDashboardCounts`) - consecutive UTC calendar days
with at least one non-undone review, computed in `FlashcardScheduling.ComputeStreakDays` from a
400-day lookback of review timestamps. The streak counts through today even before today's
first review (it only breaks once *yesterday* has no review either), matching typical
habit-streak semantics rather than resetting to 0 at midnight.
- **DailyGoal** / **ReviewsToday** (global) - `DailyGoal` is simply the account's configured
`FlashcardSettings.ReviewsPerDay`; `ReviewsToday` is reviews logged since the client's
`studyDayStartUtc` for `/study/next` calls, but since `/counts` has no per-request client-day
concept it uses the server's own UTC calendar day instead - a day-boundary edge case is possible
right around midnight, unlike the study-session endpoints which always trust the client's local
day.
## Import/export
CSV is UTF-8, text-only, headers `stable_id,deck,type,prompt,answer,extra,tags,