Lightweight, local-first flashcard system built on Markdown and Next.js, with robust LaTeX rendering, image handling, and a pluggable scheduler.
- Cards are plain Markdown files with
## Frontand## Backsections. - The web UI (Next.js) lets you study, browse, and edit cards.
- LaTeX is rendered with KaTeX. Inline and display math supported.
- Media files live under
obsidian_notes/flashcards/files/(orFLASHCARDS_DIR/files/) and are referenced asfiles/<name>. - XML decks can be converted to Markdown (and blobs resolved) with
xml_to_markdown.py.
- Markdown + LaTeX (
$...$,\(...\),\[...\],$$...$$) - Image/media support via
files/paths - Pluggable scheduling algorithms (default SM-2–like)
- Local JSON history storage (no DB)
- Debug modes for card parsing and Markdown rendering
- Node.js 18+
- npm (comes with Node)
- Python 3.9+
- Install web dependencies
- cd
website - Run
npm install
- Start the dev server
npm run dev- Open http://localhost:3000
- Add cards
- Put Markdown files in
obsidian_notes/flashcards/(or setFLASHCARDS_DIR) - Place images/media in
obsidian_notes/flashcards/files/
- Optional: enable debug logging
npm run dev:debugto log parsing/markdown debug info in console
Each card is a single Markdown file with a title and two sections:
# Any Title
## Front
What is the L2 norm?
## Back
Also known as the Euclidean norm. $\lVert x \rVert_2 = \sqrt{\sum_i x_i^2}$
- You can organize cards into groups by creating subfolders under
obsidian_notes/flashcards/. The UI lets you filter by group. - LaTeX is supported in both sections:
- Inline:
$...$or\(...\) - Display:
$$...$$or\[...\] - Escaped dollars
\$render as literal$. - Inline code is protected from math parsing.
- Inline:
- Put media in
obsidian_notes/flashcards/files/. - Reference them from Markdown as
. - The site serves them at
/files/<filename>.
Use xml_to_markdown.py to convert deck XML into Markdown and resolve blobs to files.
Example:
python xml_to_markdown.py "ML.xml" --output_dir obsidian_notes/flashcards
What it does:
- Parses cards from the XML.
- Resolves
{{blob <hash>}}references:- Moves files from
blobs/(and migrates legacyfiles/) intoobsidian_notes/flashcards/files/<hash>.<ext>(orFLASHCARDS_DIR/files/) - Rewrites
{{blob <hash>}}toor[File](files/<hash>.<ext>)
- Moves files from
- Produces Markdown card files with
## Frontand## Backsections.
Tip: If google-fire is not installed, the script falls back to a simple CLI:
python xml_to_markdown.py <deck1.xml> [<deck2.xml> ...]
- Default scheduler lives in
website/src/algorithms/default.ts. - Add custom schedulers in
website/src/algorithms/and register them inindex.ts. - API endpoint
/api/nextpulls the next N cards according to the scheduler.
GET /api/cards– list all cardsGET /api/cards/[id]– get a single cardPUT /api/cards/[id]– update a card (title/front/back)GET /api/next– get next N cards (scheduler)POST /api/review– append a review logGET /api/history– get review logs
- Dev with extra logs:
npm run dev:debug- Markdown debug:
NEXT_PUBLIC_DEBUG_MD=1 - Card parsing debug:
DEBUG_CARDS=1
- Markdown debug:
- Extractor regression test:
npm run test:cards- Validates that
## Front/## Backextraction captures multi-line sections.
- Validates that
website/– Next.js web appsrc/app/– routes and API endpointssrc/lib/markdown.ts– Markdown + KaTeX renderingsrc/lib/cards.ts– card loading and parsingpublic/files/– served media fallbackobsidian_notes/flashcards/– cards (checked at runtime; override withFLASHCARDS_DIR)
xml_to_markdown.py– XML → Markdown converter with blob resolution
- History is stored in
website/data/history.json(created on first use). - No external DB or services are required.
No license specified. Ask the author before distribution.