diff --git a/README.md b/README.md index 99acd4d..5e9b0c3 100644 --- a/README.md +++ b/README.md @@ -1434,6 +1434,20 @@ Things I chose while building, with the reasoning: (measured: 268..1280 at a 1280 window) — a flat pane floating 40px inside the scene read as "cut off next to the sidebar". +96. **Skipping a card is not a mistake.** "You can just scroll all the way down and it counts + everything as incorrect even if you technically just skip it to do it later." The study feed's + results treated *every* card without a result as missed: swiping to the end recorded a 0% + attempt against the deck and listed all twelve cards under "Missed items" with their answers. + Now only answered cards count: the score line is `correct / answered`, the attempt records the + answered count, an empty run records nothing at all, and the cards you scrolled past appear + under **Not answered (n)** — listed as prompts, labelled "not counted as mistakes", with + **Answer the rest (n)** starting a catch-up run of exactly those cards (the same `restart` + path as retry-missed, so history and the SRS see the answers as usual). "Aced all" now also + requires that nothing was left waiting. Phase 2 pins the whole flow: all-skipped → no attempt, + no mistake list, no score line; one-of-two → "You got 1 of 1 right", "Not answered (1)", + `attempts: 1, bestScore: 100`; the catch-up run records the remaining answer. Exam mode keeps + its own rule — in a timed exam an unanswered question *is* a miss. + 95. **Aero, second pass: a heavier shared blur, and a field that reads as a field.** Two follow-ups on 94. The composer's input was still a flat 60% white bar (the "clean, even tile" from 91) on a panel that had — rightly — become translucent, so it read as a white slab: "especially the diff --git a/components/ResultsScreen.tsx b/components/ResultsScreen.tsx index 49f7481..820fbd5 100644 --- a/components/ResultsScreen.tsx +++ b/components/ResultsScreen.tsx @@ -9,7 +9,16 @@ import type { Activity, Deck } from "@/lib/schemas"; import { downloadDeck } from "@/lib/storage"; import { useIsPhone } from "@/lib/use-phone"; import { cn } from "@/lib/utils"; -import { BoltIcon, DownloadIcon, FlameIcon, HomeIcon, RefreshIcon, SnowflakeIcon, SparklesIcon } from "@/components/icons"; +import { + BoltIcon, + DownloadIcon, + FlameIcon, + HistoryIcon, + HomeIcon, + RefreshIcon, + SnowflakeIcon, + SparklesIcon, +} from "@/components/icons"; import { Button, Chip } from "@/components/ui"; import { AskButton } from "@/components/chat/AskButton"; @@ -62,9 +71,11 @@ export function ResultsScreen({ total, correct, missed, + unanswered, results, retryMode, onRetryMissed, + onStudyUnanswered, onRestart, onRegenerate, regenerating, @@ -73,12 +84,17 @@ export function ResultsScreen({ deck: Deck; xp: number; bestStreak: number; + /** Cards that were answered (right or wrong). */ total: number; correct: number; + /** Answered, but wrong. */ missed: Activity[]; + /** Scrolled past without answering — waiting, not wrong. */ + unanswered: Activity[]; results: Record; retryMode: boolean; onRetryMissed: () => void; + onStudyUnanswered: () => void; onRestart: () => void; onRegenerate: () => void; regenerating: boolean; @@ -90,7 +106,7 @@ export function ResultsScreen({ /* Phones: the score and XP count up; desktop jumps straight in. */ const percentShown = useCountUp(percent, !isPhone); const xpShown = useCountUp(xp, !isPhone); - const perfect = missed.length === 0 && total > 0; + const perfect = missed.length === 0 && unanswered.length === 0 && total > 0; const tone = percent >= 80 ? "emerald" : percent >= 50 ? "frost" : "amber"; return ( @@ -107,11 +123,13 @@ export function ResultsScreen({

{perfect ? t("results.acedAll", { n: total, title: deck.title }) - : t("results.gotOf", { - correct, - total, - retry: retryMode ? t("results.retrySuffix") : "", - })} + : total === 0 + ? t("results.noneAnswered") + : t("results.gotOf", { + correct, + total, + retry: retryMode ? t("results.retrySuffix") : "", + })}

@@ -164,21 +182,45 @@ export function ResultsScreen({
) : null} + {unanswered.length > 0 ? ( +
+

+ {t("results.unanswered", { n: unanswered.length })} +

+

{t("results.unansweredNote")}

+ +
+ ) : null} +
{missed.length > 0 ? ( - ) : ( + ) : null} + {unanswered.length > 0 ? ( + + ) : null} + {missed.length === 0 && unanswered.length === 0 ? ( - )} - {missed.length > 0 ? ( + ) : ( - ) : null} + )}