Technical Documentation: ronreiter/interactive-tutorials
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (README.md)
Interactive Tutorials
This is the open source repository for the free interactive tutorial websites:
- learnpython.org
- learnjavaonline.org
- learn-html.org
- learn-c.org
- learn-cpp.org
- learn-js.org
- learn-php.org
- learnshell.org
- learncs.org
- learn-perl.org
- learnrubyonline.org
- learn-golang.org
- learnrust.org
Please feel free to contribute your tutorials or exercises by sending a pull request and adding yourself on the list.
To run locally, first create a new file called app.env based on app.env.example.
(you don't need working credentials to run the website, just to execute code).
Running with Docker
Execute:
DEFAULT_DOMAIN=learnpython.org make runThis command will run learnpython.org website by default using Docker Compose.
To run a specific website, run with the DEFAULT_DOMAIN option set, as follows:
DEFAULT_DOMAIN=learn-js.org make runBy default, the server process will run at http://localhost:5000.
The web server will locally compile and load all Markdown files into memory. The docker needs to be rebuilt upon any change in the Python code or the Markdown code.
Running without Docker (locally)
To run the development server outside of a Docker, run the following command once:
make build-localAnd then run:
DEFAULT_DOMAIN=learn-js.org make run-localIDEOne Credentials
To make the IDEOne execution API work locally, you must obtain a username and password,
and add the credentials to a file called app.env.
(TBD: add an option to use the remote version)
Contributors
- superreg
- JBoy Advance
- qwong95
- AddaxSoft
- derco0n
- imagesid
- ......
This project is supported by:
Superpowers Plans 2026 08 11 Tutorial Enrichment (`docs/superpowers/plans/2026-08-11-tutorial-enrichment.md`)
Interactive Tutorials Enrichment + learnvibecoding.org โ Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Enrich all 17 tutorial sites, add a learnvibecoding.org site with an in-browser WebGPU tiny model, harden main.py, and regenerate static multilingual translations โ one PR per site plus one general PR.
Architecture: One Flask process per site (main.py + DEFAULT_DOMAIN). Tutorials are static Markdown at tutorials/<site>/<language>/<Tutorial>.md, parsed by a regex for Tutorial / Tutorial Code / Expected Output / Solution. Translations are static per-language folders โ no runtime step. Per-site enrichment is delegated to parallel subagents following a shared playbook; the two code-heavy sub-projects (server hardening, learnvibecoding.org) are implemented inline.
Tech Stack: Python 3 + Flask, Markdown, pytest. learnvibecoding.org: @huggingface/transformers (WebGPU), onnx-community/SmolLM2-360M-Instruct.
Global Constraints
- Tutorial
.mdfiles MUST follow the parseable format:TutorialโฆExerciseโฆTutorial CodeโฆExpected OutputโฆSolutionheading blocks; code is indented 4 spaces. - Expected Output assertions are EXECUTED server-side โ never translate or alter
test_output_contains(...)/success_msg(...)lines; only prose is translatable. - Wiki-links use
[[Page Title]]or[[Page Title|display text]]; page names are the tutorial file base-name (spaces โ_in URLs). Welcome.mdmust be present in every language folder;main.pyexpects it first.- One PR per site directory, one PR for learnvibecoding.org, one general PR for shared/server changes. All branch from
master. - Do NOT commit large model binaries; load the model from the HuggingFace hub at runtime in the browser.
Task 1: Write the shared enrichment playbook
Files:
- Create:
docs/superpowers/playbooks/enrichment-playbook.md
Interfaces:
Consumes: the design spec (
docs/superpowers/specs/2026-08-11-tutorial-enrichment-design.md).Produces:
docs/superpowers/playbooks/enrichment-playbook.mdโ the single source of truth every per-site subagent follows. Its sections: Tutorial format (with the exact heading regex), Wiki-link rules, Welcome.md rules, Translation rules (file names match English; code + assertions untouched; only prose translated), Quality bar checklist, Site structure conventions.Step 1: Write the playbook file โ author the sections listed above with concrete examples pulled from
tutorials/learnpython.org/en/Hello, World!.md.Step 2: Commit
git add docs/superpowers/playbooks/enrichment-playbook.md
git commit -m "docs: add tutorial enrichment playbook"Task 2: Fix malformed/un-wired sites (general branch)
Files:
- Modify:
constants.py(addLEARNRUST_DOMAIN,LEARNASSEMBLY_DOMAIN,LEARNSOLIDITY_DOMAIN+DOMAIN_DATAentries) - Modify:
main.py(skip malformed domain dirs gracefully) - Rename:
tutorials/learnRust.org/โtutorials/learnrust.org/(moveHello World!.md,Welcome.mdintotutorials/learnrust.org/en/) - Test:
tests/test_domain_loading.py
Interfaces:
Consumes:
constants.DOMAIN_DATAstructure (seelearnpython.orgentry).Produces: three new
DOMAIN_DATAkeys (learnrust.org,learnassembly.org,learnsolidity.org) thatinit_tutorials()can serve.Step 1: Write the failing test
import constants
def test_unsupported_domains_wired():
for d in ["learnrust.org", "learnassembly.org", "learnsolidity.org"]:
assert d in constants.DOMAIN_DATA- Step 2: Run to verify it fails โ
pytest tests/test_domain_loading.py -vโ FAIL. - Step 3: Add domain constants + DOMAIN_DATA entries โ for each:
language,language_id(any, unused),codemirror_mode,prism_mode,default_code(a stub print),namespace,full_url, logos/favicon understatic/(reuse a generic placeholder),contact_email/support_email. - Step 4: Rename learnRust folder โ
git mvtutorials/learnRust.orgโtutorials/learnrust.org, then move.mdfiles intotutorials/learnrust.org/en/. - Step 5: Harden
init_tutorialsโ wrap thetutorials.remove("Welcome.md")and per-file logic so a domain with only root-level.md(noten/) logs a warning instead of crashing; skip gracefully. - Step 6: Run test to verify it passes โ
pytest tests/test_domain_loading.py -vโ PASS. - Step 7: Commit
git add constants.py main.py tests/ tutorials/learnrust.org
git commit -m "fix: wire up learnrust/learnassembly/learnsolidity, harden domain loading"Task 3: Harden `main.py` (general branch)
Files:
- Modify:
main.py(cgi.escapeโhtml.escape;signin/signupstub;error404template context) - Test:
tests/test_main.py
Interfaces:
Consumes:
Flask, existing templateerror404.html.Produces:
signin/signuproutes return JSON{"status": "error", "error": "not_implemented"}with 501 instead of crashing on the undefinedusersobject.Step 1: Write the failing test
import json
def test_signin_returns_501(client):
r = client.get("/[email protected]")
assert r.status_code == 501
assert json.loads(r.data)["status"] == "error"- Step 2: Run to verify it fails โ FAIL (
usersundefined โ 500). - Step 3: Implement โ replace the
users.findOne(...)/users.insert(...)bodies with a guardednot_implemented501 response; replacecgi.escapewithhtml.escape(importhtml). - Step 4: Run to verify it passes โ PASS.
- Step 5: Commit
git add main.py tests/test_main.py
git commit -m "fix: harden main.py โ stub signin/signup, drop deprecated cgi"Task 4: Add tests harness for tutorial parsing & translations
Files:
- Create:
tests/test_tutorial_parsing.py,tests/test_translations.py,tests/conftest.py - Modify:
pyproject.toml(add pytest dev-dependency)
Interfaces:
Consumes:
main.init_tutorials,main.wikify,main.untab.Produces: assertions that every language folder has a
Welcome.md, that every.mdinen/has a matching file in each non-en language folder, and thatmain.sectionsparses a valid tutorial.Step 1: Write tests
import os, main
TUT = "tutorials"
def test_every_language_has_welcome():
for domain in os.listdir(TUT):
for lang in os.listdir(os.path.join(TUT, domain)):
if os.path.isdir(os.path.join(TUT, domain, lang)):
assert "Welcome.md" in os.listdir(os.path.join(TUT, domain, lang))
def test_en_files_have_translations():
for domain in os.listdir(TUT):
dpath = os.path.join(TUT, domain)
if not os.path.isdir(dpath): continue
en_files = set(os.listdir(os.path.join(dpath, "en")))
for lang in os.listdir(dpath):
if lang == "en" or not os.path.isdir(os.path.join(dpath, lang)): continue
missing = en_files - set(os.listdir(os.path.join(dpath, lang)))
assert not missing, f"{domain}/{lang} missing {missing}"- Step 2: Run to verify they fail โ confirm current gaps are detected (translation folders are incomplete today).
- Step 3: Commit tests
git add tests/ pyproject.toml
git commit -m "test: add parsing + translation coverage harness"Task 5: learnvibecoding.org โ domain wiring + custom template (own branch)
Files:
- Modify:
constants.py(addLEARNVIBECODING_DOMAIN+DOMAIN_DATAentry withis_browser_model: True) - Modify:
main.py(route selectsindex-vibecoding.htmlwhendomain_data["is_browser_model"]) - Create:
templates/index-vibecoding.html,static/js/vibecoding.js,static/css/vibecoding.css - Create:
tutorials/learnvibecoding.org/en/{Welcome, Prompting, Iterating, Shipping}.md - Modify:
Makefile,website_docker.conf(deploy wiring)
Interfaces:
Consumes:
constants.DOMAIN_DATA[domain]["is_browser_model"].Produces: a site where the tutorial page embeds a transformers.js WebGPU runner that loads
onnx-community/SmolLM2-360M-Instructfrom the HF hub and generates text from a prompt box.Step 1: Add
learnvibecoding.orgto DOMAIN_DATA withlanguage: "text",default_code: "",is_browser_model: True, placeholder logos/favicon.Step 2: Route branch in
index()โ"index-vibecoding.html" if domain_data.get("is_browser_model") else "index.html".Step 3: Write
templates/index-vibecoding.htmlโ extendbase.html; include a prompt textarea, Run button, output ``, progress bar for model download; loadstatic/js/vibecoding.js; disable the IDEOne "Run code" button.Step 4: Write
static/js/vibecoding.jsโimport { pipeline } from "@huggingface/transformers"via an ESM import map;const gen = await pipeline("text-generation", "onnx-community/SmolLM2-360M-Instruct", { device: "webgpu" }); on Run:await gen(prompt, { max_new_tokens: 200 }); render output; catch WebGPU-unavailable error and show a friendly fallback.Step 5: Write the 4 tutorials per the playbook (no IDEOne execution; Expected Output section documents expected generated behavior in prose).
Step 6: Add deploy wiring โ Makefile target
run-vibecoding;website_docker.confnote.Step 7: Verify locally โ
DEFAULT_DOMAIN=learnvibecoding.org make build-localand run; confirm the page loads and model code path is wired. Then commit.
git add constants.py main.py templates/index-vibecoding.html static/js/vibecoding.js static/css/vibecoding.css tutorials/learnvibecoding.org Makefile website_docker.conf
git commit -m "feat: add learnvibecoding.org with in-browser WebGPU tiny model"Task 6: Per-site enrichment orchestration (17 site branches โ PRs)
Files:
- Per-site branch:
enrich-<site>, touching onlytutorials/<site>/+courses.json(if needed).
Interfaces:
Consumes: the playbook (Task 1), the translation harness (Task 4) for verification.
Produces: one branch + one PR per site with enriched English tutorials and regenerated translations in all existing language folders.
Step 1: Dispatch one subagent per site (in parallel,
isolation: "worktree") โ each instructed to: read the playbook; audit the site'sen/tutorials; add missing popular topics/exercises; improve weak chapters; fix wiki-links; ensureWelcome.md; regenerate every non-en language folder from the enriched English set (only prose translated; code + assertions untouched); commit onenrich-<site>.Step 2: Verification pass โ run
tests/test_translations.py+main.pyload for each site; fix gaps.Step 3: Create PR per site โ
gh pr createfrom eachenrich-<site>branch โmaster, one per site.
Task 7: General improvements PR
Files: the general-server-hardening branch (design doc + Tasks 2โ4 changes).
- Step 1: Run full test suite โ
pytest tests/ -v; all pass. - Step 2: Create PR โ
gh pr createfromgeneral-server-hardeningโmaster.
Self-Review Notes
- Spec coverage: per-site enrichment (Task 6), learnvibecoding (Task 5), main.py hardening (Tasks 2โ3), translations (Task 6 + harness Task 4), deploy wiring (Task 5 step 6), PR strategy (Tasks 6โ7).
- No placeholders; code blocks are concrete.
- Type consistency:
is_browser_modelflag introduced in Task 5 and consumed by the same task's route branch โ no cross-task drift.
Superpowers Playbooks Enrichment Playbook (`docs/superpowers/playbooks/enrichment-playbook.md`)
Tutorial Enrichment Playbook
This is the single source of truth for every per-site enrichment subagent. Follow it
exactly. When enriching tutorials/<site>/, every change must conform to these rules.
1. Tutorial file format
Every tutorial is a Markdown file tutorials/<site>/<language>/<Name>.md whose body is
parsed by main.py via this regex:
Tutorial\n[=\-]+\n+(.*)\n*Tutorial Code\n[=\-]+\n+(.*)\n*Expected Output\n[=\-]+\n+(.*)\n*Solution\n[=\-]+\n*(.*)\n*So a valid tutorial MUST have these heading blocks, in order:
Tutorial
--------
<intro prose; code examples indented 4 spaces>
Exercise
--------
<the exercise prompt>
Tutorial Code
-------------
<starter code, indented 4 spaces>
Expected Output
---------------
<test assertions; see ยง3>
Solution
--------
<correct code, indented 4 spaces>- Code blocks are indented with 4 spaces (markdown code).
main.py'suntab()
strips 4 leading spaces to produce the runnable code. - A file WITHOUT these blocks is treated as a non-tutorial page (only
Welcome.mdis
expected to be a non-tutorial page). Do not leave regular tutorials un-parsed.
2. Wiki-links
- Links use
[[Page Title]]or[[Page Title|display text]]. - The page name is the exact tutorial file base-name (minus
.md). URLs replace
spaces with_at render time, so keep the file name as the canonical key. Welcome.mdlists chapters as bulleted- [[Chapter Name]]items under###headings.- Any tutorial name referenced in a wiki-link must have a matching
.mdfile in that
language folder, ormain.pycreates an empty stub page (acceptable only for
genuinely-orphan links; prefer real files).
3. Expected Output โ two conventions (IMPORTANT)
There are TWO different conventions across sites. Identify which one a site uses by
reading an existing tutorial's Expected Output block, then follow that site's
convention.
A) Checker DSL (learnpython.org only)
learnpython.org uses a checker DSL inside Expected Output:
test_output_contains("some string")
success_msg('Great job!')Other checker functions seen: test_object, test_objects, error_contains,output_contains. These lines are code, NOT literal output:
- NEVER alter them.
- In translations, keep them byte-for-byte identical to English.
B) Literal output (ALL other sites: learn-js, learn-ts, learn-c, learn-html,
learnsqlonline, learnjavaonline, learn-cpp, learncs, learnshell, learn-php,
learnrubyonline, learn-perl, learn-golang, and the rest)
The Expected Output block is the literal expected output string that the
program must print, compared by exact trimmed string equality (the JS print()
function does $.trim(tutorialData.output) === $.trim(text)).
- When you WRITE a new tutorial, put the exact literal output the starter code /
solution produces (e.g. forprint("Hello")the Expected Output isHello). - Keep code, literal output, and whitespace EXACTLY matching what running the
solution produces โ the "correct" check depends on it. - In translations, keep the literal output string byte-for-byte identical to English.
Note: learn-html.org compares rendered HTML structurally (not string equality);
the Expected Output there is the expected full HTML document.
4. Welcome.md
- Every language folder MUST have
Welcome.md.main.pyremoves it from the list and
places it first. - Format:
# Welcomeheading, intro prose (may include raw HTML ``,<a>), then### Sectionheadings with bulleted- [[Chapter]]lists.
5. Translation rules (static per-language folders)
- Each language is its own folder of translated
.mdfiles. There is NO runtime step. - A language folder is in sync when it has a file with the SAME base-name for every
en/file, and NO extra files. - When translating:
- Keep the file name identical to English.
- Keep all code blocks (indented 4-space lines) byte-for-byte identical.
- Keep
Expected Outputassertion lines identical. - Keep wiki-link targets identical (
[[Same Name]]) โ page names are the English names. - Translate only prose (intro, exercise prompt, inline
inline codelabels if any).
6. Enrichment quality bar
For each site:
- Audit: list every
en/tutorial; identify missing popular topics for that language. - Add missing topics as full tutorials (format in ยง1), with real examples, an
exercise, assertions, and a solution. - Improve weak chapters: fix typos, add missing examples, complete the exercise.
- Fix wiki-links so every linked page exists.
- Regenerate all non-English language folders from the enriched English set per ยง5.
- Preserve the existing
default_code/ editor-mode conventions already inconstants.DOMAIN_DATA[site]โ do not change editor config unless required.
7. Branch / commit rules
- Work on the assigned branch (
enrich-<site>), touching ONLYtutorials/<site>/and
(if needed)courses.json. - Commit with a clear message; one commit per logical change; end with
Co-Authored-By: Claude <[email protected]>. - Do not commit model binaries, logs, or local artifacts.
Superpowers Specs 2026 08 11 Tutorial Enrichment Design (`docs/superpowers/specs/2026-08-11-tutorial-enrichment-design.md`)
Design: Interactive Tutorials Enrichment + learnvibecoding.org
Date: 2026-08-11
Status: Approved (user granted autonomous execution; committed for review)
1. Overview
Enrich all 17 tutorial sites in tutorials/, add a new learnvibecoding.org site that
runs a tiny model in the browser (WebGPU), harden main.py, and regenerate static
multilingual translations. One pull request per site, plus one general PR for
server/shared improvements.
2. Architecture context (as-is)
- One Flask process per site (
main.py+DEFAULT_DOMAINenv), gunicorn in production. - Tutorials are static Markdown per site per language:
tutorials/<site>/<language>/<Tutorial>.md, parsed by a regex inmain.py
(Tutorial / Tutorial Code / Expected Output / Solution). - Translations are STATIC: each language is its own folder of translated
.mdfiles.
There is no runtime or generation script today. constants.pyDOMAIN_DATAwires each site to IDEOne (remote compile), editor modes,
logos, analytics.courses.jsonholds per-site sponsored course listings.templates/index.htmlrenders tutorials;index-python.htmlis the Python-specific variant.
Findings that shape the plan
- 17 site folders; coverage ranges from learn-html (69) / learn-ts (43) down to
learnassembly (1), learnscala (2), learnsolidity (2). learnRust.orgis malformed:.mdfiles sit in the root instead ofen/.learnassembly.org,learnRust.org,learnsolidity.orgare not wired intoDOMAIN_DATAand are skipped at load.main.pybugs:signin/signupreference an undefinedusersobject;cgiis
deprecated (Python 3.13+).
3. Approach: central playbook + parallel subagents
One shared enrichment playbook defines the quality bar (tutorial format, wiki-link
conventions, translation rules). One subagent per site follows it on its own git branch
โ one PR per site. A verification/integration pass happens before each PR is created.
4. Sub-project 1 โ Per-site enrichment (17 PRs)
Per site: review the existing tutorial set; fill missing popular topics/exercises;
improve weak chapters; fix [[wiki-link]] conventions; ensure a proper Welcome.md;
regenerate that site's non-English language folders from the enriched English set.
Structural fixes folded into the affected site PRs:
- Fix malformed
learnRust.org(move files toen/). - Wire
learnassembly.org,learnsolidity.org,learnrust.orgintoDOMAIN_DATA.
5. Sub-project 2 โ learnvibecoding.org (1 PR)
- New site. WebGPU-only tiny model via
@huggingface/transformers, runningonnx-community/SmolLM2-360M-Instruct(ONNX). Small enough to be "tiny", capable
enough for a vibe-coding demo. - Custom template: prompt box โ in-browser generation โ code/response output.
Chapters teach prompting, iteration, and shipping a small app. - Static model assets + JS runner under
static/, newDOMAIN_DATAentry, custom
template, plus gunicorn/nginx/Docker/Makefile deploy wiring.
6. Sub-project 3 โ main.py hardening (1 general PR)
- Fix undefined
usersobject. - Harden domain loading / malformed-folder handling.
- Modernize deprecated
cgi(usehtmlmodule). - Add basic tests (tutorial parsing, wiki-link resolution, translation folders).
- Include this design doc in this PR.
7. Sub-project 4 โ Translations
Static regeneration into ALL existing language folders per site, folded into each
site's PR. No runtime translation step.
8. PR / branch strategy
- One branch + PR per site (17), one branch + PR for
learnvibecoding.org(1), one
branch + PR for general server improvements (1). All branch frommaster.
9. Verification
- Run
main.pylocally per domain (ormake build-local) and confirm tutorial parsing
loads without "not a tutorial" warnings. - Validate wiki-links resolve; confirm translation folders parse; run added tests.
--- METRICS ---
- Files Extracted: 4
- Estimated Token Budget: ~5883 tokens
- Canonical Reference: https://codewiki.google/github.com/ronreiter/interactive-tutorials