Graduately/content
FBukovina f7470d333c Add the Next.js web app and shared lesson content.
Move lessons out of the C sources into JSON so the web client can use the same material as the desktop app, and point release builds at desktop/.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 16:06:15 +02:00
..
i18n Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
lessons Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
manifest.json Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
paths.json Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
README.md Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
schema.ts Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
subjects.json Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00
themes.json Add the Next.js web app and shared lesson content. 2026-09-17 16:06:15 +02:00

content/

Shared lesson content for the whole project. One folder, one file per lesson — instead of lesson text being buried inside each unit's C source file.

schema.ts is the single description of every shape here. Both the extractor in tools/ and the web app in web/ type against it, so a change there shows up as a type error on both sides.

Layout

content/
  schema.ts              TypeScript types for everything below
  themes.json            10 palettes, dark + light (24 colors each)
  subjects.json          the 13 subjects, their i18n keys and routes
  paths.json             serpentine unit paths per subject
  manifest.json          subject and lesson ids
  i18n/
    ui.json              UI chrome strings, keyed by identifier
    content.json         lesson strings, keyed by the Czech text
  lessons/
    deutsch/unit-01.json … unit-03.json
    networks/unit-01.json … unit-27.json
    hardware/unit-01.json … unit-03.json
    czech/mluvnice.json, orwell-1984.json, fuks-spalovac-mrtvol.json

paths.json holds one entry per path. A subject usually has exactly one, but Czech has two: the top-level area picker and the reading branch listing the books, distinguished by the branch field.

Lesson shape

A lesson holds theory slides and a list of tasks. Each task is one bubble on the unit map and contains items of a single kind:

kind what it is
choice multiple choice, correct indexes options
fill sentence split into segments with a gap between each; input picks dropdown or text
dialogue dialogue lines with one missing word each
assembly build a sentence from scrambled words
assign sort labelled cards into named groups
free open question with a model answer
typed type the answer, answers lists accepted spellings
fields one prompt, several labelled fields (Czech grammatical categories)
reveal self-assessed: write an answer, then compare it with the model
hangman guess the word letter by letter
profile "Steckbrief" line with a model answer
table editable grid, some cells prefilled
subnet VLSM subnetting worksheet

fill absorbs every gap variant in the desktop app. A few flags steer it: letters marks gaps holding missing letters inside a word rather than whole words, gapPool picks the task's pool or altPool per gap (the "Ordne zu" question-word and answer columns), and heading starts a labelled group of rows.

Two conventions carried over from the desktop app:

  • Pipe-separated answers. "bin|heiße" means either spelling is accepted.
  • Lenient comparison. Answers are matched after lowercasing, expanding ä ö ü ß to ae oe ue ss, dropping accents and ignoring punctuation, so Czech diacritics and German umlauts are optional. See web/src/lib/answers.ts.

Titles come either as a literal title or as a titleKey to look up in the i18n tables, because German unit names are identical in both languages while network lesson names are translated.

Regenerating

The C sources in desktop/ are still where content is authored, so this folder is a generated one-way export:

npm run content     # from the repo root, or: node tools/build-content.mjs

The extractor parses the C initializers directly and throws on any shape it does not recognise, so a bad export fails loudly rather than emitting empty lessons.