A spaced-repetition flashcard trainer. Pick a deck, take a short daily flight, and let the scheduler decide what you see next. One engine, any number of decks: the scheduling and the mastery numbers mean the same thing whatever you are studying.
Five decks ship today:
-
Wines, Grapes, Regions — 161 cards. Learning wine the way it helps you in a restaurant: which grapes grow where, and how to read a label you have never seen. France-first, then Tuscany-led Italy, then the wines you will actually meet on a US list.
-
Mexican Spanish – English — 254 cards. The 15 most common verbs and the 15 most common reflexive verbs across present, past and future, plus the vocabulary for a house, a town and a table. Mexican usage throughout: no
vosotros, and carro/departamento/refrigerador rather than the Peninsular words. -
French – English — 254 cards. The same shape, with the passé composé as the past tense.
-
Payment Cards — 148 cards. How a swipe actually works: who is involved, authorization through settlement, interchange and the economics, card data and EMV, risk and compliance, and the rails underneath.
-
California Plant Families — 149 cards, and the deck with pictures. The ten families that account for most of the California flora: a floral diagram for each, the diagnostic characters that give it away and what they are called, and sets of field clues to key out. An eleventh group holds the lookalikes — how to know a mint from a penstemon, a sedge from a grass. Flights are 20 cards here rather than 35, because a diagram takes longer to read than a word.
Every diagram is drawn in code as SVG from numbers stored on the card, not fetched as an image, so the deck is offline and weightless like the rest. The floral formula printed under a diagram is derived from that diagram, and
npm run validatederives it a second time independently and fails if the two disagree — a card cannot claim five petals over a drawing of four.
Live: https://cvw-hmb.github.io/memory-trainer/ — served by GitHub Pages from
main at the repo root. Every path in the app is relative, so it works under the
/memory-trainer/ subpath with no build step. Pushing to main redeploys.
The repo is named
memory-trainerbecause wine was the first deck, not the product — seePLAN.md. The app is called La Cave; each deck names itself.
The app loads data/decks.json and the deck files under data/decks/ over fetch, so it needs a local web server (opening index.html directly from the file system will not work in most browsers).
# option A: Node
npm run dev # serves on http://localhost:8000
# option B: Python, via the local uv venv
npm run setup:py # one time: creates ./.venv (uv sync)
npm start # or: uv run python -m http.server 8000Then open http://localhost:8000.
npm run cards # regenerate the wine deck
npm run cards:es # regenerate the Mexican Spanish deck
npm run cards:fr # regenerate the French deck
npm run cards:pay # regenerate the Payment Cards deck
npm run cards:bot # regenerate the California Plant Families deck
npm test # scheduler tests (node --test, no dependencies)
npm run deck:import -- sheet.csv --name "My Deck" # spreadsheet -> deck JSON
npm run deck:check my-deck.json # check a deck before using it
npm run validate # every deck: schema, unique ids, duplicate prompts,
# spoiler check, and floral formula vs. floral diagramThe card generator is Python. It uses only the standard library, so the venv exists to pin the interpreter, not to install packages.
uv sync # creates ./.venv from .python-version + pyproject.toml
uv run python scripts/generate_cards.py # same as: npm run cardsuv run activates ./.venv for you — no source .venv/bin/activate needed. If you
already have another project's venv active, uv prints a VIRTUAL_ENV does not match
warning and correctly uses this project's .venv anyway.
Point the IDE at the venv this repo already builds — do not let it create its own.
npm run setup:py(oruv sync) so./.venvexists.- Settings → Project → Python Interpreter → Add Interpreter → Add Local Interpreter.
- Choose Existing environment and select:
<repo>/.venv/bin/python - Mark
scripts/as a Sources Root if you want imports resolved there.
.venv/ and .idea/ are both gitignored, so this is per-machine setup: anyone
cloning the repo runs step 1 and repeats it.
- Opens on a deck chooser — a dropdown, so a phone gets its native picker.
- Build your own deck. The deck screen copies out a brief describing the format and the rules; hand it to Claude or any other AI, and paste the JSON back. The app checks it — duplicate prompts, undeclared groups, answer-leaking labels, lopsided groups — and shows you what it found before saving anything. Your decks live in your browser, never in this repo, and they behave like any other deck: own progress, own groups, own place in the dropdown. Re-paste a revised deck under the same name and it replaces the old one, keeping progress on every card whose id you kept.
- Already have a spreadsheet?
npm run deck:import -- cards.xlsx --name "…"converts.csv,.tsvor.xlsxinto the same format, andnpm run deck:checkruns the app's own check over the result. A sheet withterm/answer/definitioncolumns becomes a one-directional deck; add--vocab(or atranslationcolumn) only when every front has exactly one back and every back exactly one front. - Ten card types across the five decks (see
CLAUDE.md), including afigurerender mode for decks that draw rather than describe. - A flight is 35 cards by default, or whatever the deck asks for (20 in the botany deck), drawn at random each time and weighted 4:1 toward the cards you keep missing.
- A card runs one way unless its type declares otherwise. Reversibility is declared, never assumed: a card flips only when both directions have exactly one right answer.
- Miss a card and it comes back later in the same flight. You do not finish a flight until every card in it is right; only the first attempt counts toward your box and stats.
- A 5-level Leitner scheduler: cards you miss come back every session, mastered cards fade to occasional review.
- Streaks, per-region accuracy, and a "hardest for you" list in the cellar book.
- Progress is saved in IndexedDB, mirrored to
localStorageas a fallback, under the keysrs_v2:<profile>:<deck>, so several people can share a browser and no two decks collide. Progress from the oldwine_srs_v1key migrates automatically on first load. - Installable as a PWA and fully usable offline: the service worker precaches
the shell, every deck and the fonts, so a flight runs in airplane mode.
Online, every same-origin request is network-first with the cache as the
fallback, so code and deck data always refresh together — a deploy is
live on the next load, and you can never get new cards against an old
renderer.
npm run validatefails if a source file is missing from the precache list.
The deck JSON files under data/ are the sources of truth, but prefer editing
the matching generator in scripts/ and regenerating (Python 3.14, run through
the local uv venv) — see the table of npm run cards:* commands above. Validate
with npm run validate. Keep ids stable and only add cards additively so saved
progress survives.
index.html app shell
data/decks.json deck index (the "choose a deck" screen)
data/decks/wine.json the wine deck (161 cards)
data/decks/spanish.json the Mexican Spanish deck
data/decks/french.json the French deck
data/decks/payments.json the Payment Cards deck
data/decks/botany.json the California Plant Families deck
src/decks/schema.js the deck schema: field tables + the shared check
src/decks/authoring.js the AI brief, and the check for a deck pasted back in
src/decks/ card types: registry, render specs, wine, vocab,
glossary, botany — and figures.js, the SVG drawings
src/app.js UI, rendering, storage, wiring
src/engine/schedule.js the Leitner scheduler (pure, no DOM)
src/styles.css styling
tests/ scheduler tests (node --test)
sw.js service worker (offline precache)
manifest.webmanifest PWA manifest
icons/ app icons (192, 512, maskable, apple-touch, favicon)
scripts/ deck generators, the validator, the spreadsheet importer
pyproject.toml Python project for the generator (uv)
.python-version pinned interpreter for uv
CLAUDE.md architecture + roadmap for Claude Code