Skip to content

Repository files navigation

Simple SRS

CI

A dataset-driven spaced-repetition and practice app for learning Vocabulary. Each dataset declared in backend/data/datasets.json becomes its own deck:

  • Practice - Endless, no-SRS stages
  • SRS - Lessons + stage-based reviews

Demo

Simple-SRS-Demo.mp4

Monorepo with three projects:

  • backend Express + better-sqlite3 API and SRS scheduler (port 3000)
  • frontend React + Vite SPA (dev on 5173; built and served by the backend on 3000)
  • e2e Playwright end-to-end suite (boots backend + frontend on 3100 / 5174)

Prerequisites

  • Node.js
  • npm
# Use the pinned Node version
nvm use

Install

npm ci
npm --prefix backend  ci
npm --prefix frontend ci
npm --prefix e2e ci

Run (development)

Start both servers together from the repo root:

npm start

Start just one of them:

npm run backend
npm run frontend

Ports default to backend: 3000, frontend: 5173. Override with the BACKEND_PORT, FRONTEND_PORT env vars:

BACKEND_PORT=3100 FRONTEND_PORT=5174 npm start

Open http://localhost:5173

Run (production)

Build the frontend, then run the backend (which also serves the frontend app)

cd frontend && npm run build
cd backend  && npm start

Open http://localhost:3000

Datasets

The app is fully driven by backend/data/datasets.json (the registry), which points at one JSON file per dataset.

Each entry carries the deck's metadata (name, mode, badge, description); the backend seeds the DB from these on start:

[
  {
    "id": "hangul",
    "name": "Hangul Alphabet",
    "file": "korean/hangul.json",
    "mode": "practice",
    "type": "character",
    "badge": "Practice",
    "description": "Practice recognizing Hangul endlessly! Grind for as long as you like."
  },
  {
    "id": "korean-words-6000",
    "name": "Korean Words",
    "file": "korean/korean-words-6000.json",
    "mode": "srs",
    "type": "vocabulary",
    "badge": "SRS",
    "description": "Learn the most common Korean words. Spaced repetition with stages."
  }
]
  • mode: "practice" - endless grind, no SRS.
  • mode: "srs" - lessons + stage-based reviews, with per-stage stats.

Decks

The app comes with the following decks:

Deck Language Mode Type
Hangul Alphabet Korean Practice character
Korean Words Korean SRS vocabulary
Hiragana Japanese Practice character
Katakana Japanese Practice character
WaniKani Radicals Japanese SRS character
WaniKani Kanji Japanese SRS character
WaniKani Vocabulary Japanese SRS vocabulary

Those decks are generated by build scripts in backend/scripts/, grouped by language:

  • Korean (scripts/korean/) - Hangul alphabet and the common Korean words list.
  • Japanese (scripts/japanese/) - Hiragana, Katakana, and WaniKani.

Each script writes a deck JSON file, registers it in backend/data/datasets.json, and downloads any audio. The decks are seeded in the DB automatically on start. See the backend README for run instructions and sources.

Notes

  • Keyboard shortcuts are configurable via Vite env vars (see frontend README and frontend/.env.example).
  • Formatting uses Prettier: npm run format formats the whole monorepo. Use format:backend, format:frontend, format:e2e, format:root for just one scope.
  • A Husky pre-commit hook runs npm run format on every commit.

Tests

Run the whole suite (backend + e2e) with npm test.

Backend

Unit tests for the Express API, grading, and SRS scheduling:

npm run test:backend

E2E

A Playwright suite (e2e/) exercises the frontend against a fresh, self-contained fixture.

It covers the dashboard, practice (grading, tally, input clearing, Enter-to-advance), self-graded cards (accept/reject + audio), and the word lesson + review flows - all data-driven from datasets.json.

npm --prefix e2e ci

# Run only once
npm --prefix e2e run install-browsers

npm run test:e2e

Releases

Packages

Contributors

Languages