From 39d6cc4389c88dafa363e5f57aa3c72a09608d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C4=ABlav=C4=81pi=20Cheesley?= Date: Mon, 21 Sep 2026 00:43:10 +0100 Subject: [PATCH] Show progress: the ladder, the statistics, and export and import The drill could score a lesson but had nowhere to put the result, so stars were computed, shown once and thrown away, and a reload started the learner from nothing. This closes that loop and builds the three views around the drill. The ladder lists every lesson the layout produced, says which are unlocked and how many stars each has earned, and has a button on each unlocked rung so an earlier lesson can be returned to. Unlocking is one-way, so the ladder stays open behind a learner. A locked rung says what unlocks it and names the lesson, rather than only looking dimmer: stars are stated in words with the glyph row marked aria-hidden beside them. The statistics group weak keys by finger and by row, which is the thing this project exists to do. A list of missed characters is trivia; the same numbers gathered under the finger that presses them and the row it reaches for say "your left ring finger cannot find the upper row", and that is what a lesson can be built from. The grouping is a pure module, src/stats/keystats.ts, with no DOM, so the diagnosis is unit-tested without a browser; every level carries its own numbers, its own wording and a severity band named in words, with the tint and the little miss-rate bars as reinforcement only. Fingers that need work lead; the ones that are behaving sit in a disclosure. A key whose statistics the current layout does not bind is reported separately rather than dropped, so the totals are never quietly short. Progress is now actually persisted. Storage is probed rather than assumed, because localStorage throws in a private window, and when it is unavailable the page says plainly that progress will not outlive the tab. Export writes a dated JSON file, and import merges it back, taking the better of the file and this browser for every field so importing your own export can never cost you this morning's practice. A partly unreadable import reports what it dropped, item by item, instead of failing silently or refusing the whole file; only a file that is not JSON at all is refused, and then the message says so. Clearing saved progress takes two deliberate goes. The export is handed over as an object URL, not a relative href. The site deploys to GitHub Pages from /touchwright/, and a relative href resolves against the page's own path, so an export that worked at the root would have pointed at the wrong place one directory down. There is an end-to-end test that pushes a deeper path and exports from there. Regression 1 is kept, not undone: every load goes through ProgressStore, which thaws and validates, and the thawed copy is handed to applyLoadedProgress, which merges field by field. Nothing from a file or from storage becomes live state by reference. Clearing empties the session's own Progress object in place rather than replacing it. Two end-to-end fixmes are filled in: "keeps progress across a reload" and "exports progress, clears storage, and imports it back". The other four are left alone. nextLessonName is deliberately still null on the drill view. Issue #3 has not landed, so the result card would otherwise offer "Start " on a button that could only restart the same sample text. The lesson is chosen on the ladder instead, and showDrill already has the lesson's id, name and key set in hand, so wiring generated text in is one line. Co-Authored-By: Claude Opus 5 --- index.html | 74 ++ src/stats/keystats.ts | 552 ++++++++++++++ src/ui/drill-view.ts | 80 +- src/ui/load-form.ts | 141 +++- src/ui/progress-view.ts | 714 ++++++++++++++++++ src/ui/theme.css | 289 +++++++ tests/e2e/pending.spec.ts | 69 +- tests/e2e/progress.spec.ts | 352 +++++++++ tests/functional/progress-view-wiring.test.ts | 288 +++++++ tests/functional/progress-view.test.ts | 613 +++++++++++++++ tests/unit/keystats.test.ts | 267 +++++++ 11 files changed, 3427 insertions(+), 12 deletions(-) create mode 100644 src/stats/keystats.ts create mode 100644 src/ui/progress-view.ts create mode 100644 tests/e2e/progress.spec.ts create mode 100644 tests/functional/progress-view-wiring.test.ts create mode 100644 tests/functional/progress-view.test.ts create mode 100644 tests/unit/keystats.test.ts diff --git a/index.html b/index.html index f272359..ee3d788 100644 --- a/index.html +++ b/index.html @@ -111,6 +111,80 @@

How that went

+ + + + + + + + +