interactive-tutorials Documentation

Python repository by @ronreiter. Docker Compose & container deployment with 4.8k stars.

4,838 stars Python Python 4 files ยท ~5,974 tokens
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy interactive-tutorials on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name interactive-tutorials -p 5001:5001 --restart always ronreiter/interactive-tutorials:latest
Configured Services & Ports
โš™๏ธ app
image: build locally
๐Ÿ”Œ Ports: 5001:5000
docker-compose.yml
yaml (docker-compose.yml)
version: "3"
services:
  app:
    build: ./
    ports:
      - "5001:5000"
    volumes: ['./:/usr/src/app']
    environment:
    - DEFAULT_DOMAIN=learnpython.org
    env_file:
      - app.env
    entrypoint: ["python", "main.py", "-d", "$DEFAULT_DOMAIN", "-H", "0.0.0.0"]

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:

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:

text
DEFAULT_DOMAIN=learnpython.org make run

This 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:

text
DEFAULT_DOMAIN=learn-js.org make run

By 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:

text
make build-local

And then run:

text
DEFAULT_DOMAIN=learn-js.org make run-local

IDEOne 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 .md files MUST follow the parseable format: Tutorial โ€ฆ Exercise โ€ฆ Tutorial Code โ€ฆ Expected Output โ€ฆ Solution heading 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.md must be present in every language folder; main.py expects 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

bash
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 (add LEARNRUST_DOMAIN, LEARNASSEMBLY_DOMAIN, LEARNSOLIDITY_DOMAIN + DOMAIN_DATA entries)
  • Modify: main.py (skip malformed domain dirs gracefully)
  • Rename: tutorials/learnRust.org/ โ†’ tutorials/learnrust.org/ (move Hello World!.md, Welcome.md into tutorials/learnrust.org/en/)
  • Test: tests/test_domain_loading.py

Interfaces:

  • Consumes: constants.DOMAIN_DATA structure (see learnpython.org entry).

  • Produces: three new DOMAIN_DATA keys (learnrust.org, learnassembly.org, learnsolidity.org) that init_tutorials() can serve.

  • Step 1: Write the failing test

python
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 under static/ (reuse a generic placeholder), contact_email/support_email.
  • Step 4: Rename learnRust folder โ€” git mv tutorials/learnRust.org โ†’ tutorials/learnrust.org, then move .md files into tutorials/learnrust.org/en/.
  • Step 5: Harden init_tutorials โ€” wrap the tutorials.remove("Welcome.md") and per-file logic so a domain with only root-level .md (not en/) 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
bash
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/signup stub; error404 template context)
  • Test: tests/test_main.py

Interfaces:

  • Consumes: Flask, existing template error404.html.

  • Produces: signin/signup routes return JSON {"status": "error", "error": "not_implemented"} with 501 instead of crashing on the undefined users object.

  • Step 1: Write the failing test

python
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 (users undefined โ†’ 500).
  • Step 3: Implement โ€” replace the users.findOne(...) / users.insert(...) bodies with a guarded not_implemented 501 response; replace cgi.escape with html.escape (import html).
  • Step 4: Run to verify it passes โ€” PASS.
  • Step 5: Commit
bash
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 .md in en/ has a matching file in each non-en language folder, and that main.sections parses a valid tutorial.

  • Step 1: Write tests

python
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
bash
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 (add LEARNVIBECODING_DOMAIN + DOMAIN_DATA entry with is_browser_model: True)
  • Modify: main.py (route selects index-vibecoding.html when domain_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-Instruct from the HF hub and generates text from a prompt box.

  • Step 1: Add learnvibecoding.org to DOMAIN_DATA with language: "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 โ€” extend base.html; include a prompt textarea, Run button, output ``, progress bar for model download; load static/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.conf note.

  • Step 7: Verify locally โ€” DEFAULT_DOMAIN=learnvibecoding.org make build-local and run; confirm the page loads and model code path is wired. Then commit.

bash
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 only tutorials/<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's en/ tutorials; add missing popular topics/exercises; improve weak chapters; fix wiki-links; ensure Welcome.md; regenerate every non-en language folder from the enriched English set (only prose translated; code + assertions untouched); commit on enrich-<site>.

  • Step 2: Verification pass โ€” run tests/test_translations.py + main.py load for each site; fix gaps.

  • Step 3: Create PR per site โ€” gh pr create from each enrich-<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 create from general-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_model flag 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:

text
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:

text
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's untab()
    strips 4 leading spaces to produce the runnable code.
  • A file WITHOUT these blocks is treated as a non-tutorial page (only Welcome.md is
    expected to be a non-tutorial page). Do not leave regular tutorials un-parsed.
  • 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.md lists chapters as bulleted - [[Chapter Name]] items under ### headings.
  • Any tutorial name referenced in a wiki-link must have a matching .md file in that
    language folder, or main.py creates 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:

text
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. for print("Hello") the Expected Output is Hello).
  • 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.py removes it from the list and
    places it first.
  • Format: # Welcome heading, intro prose (may include raw HTML ``, <a>), then
    ### Section headings with bulleted - [[Chapter]] lists.

5. Translation rules (static per-language folders)

  • Each language is its own folder of translated .md files. 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 Output assertion lines identical.
    • Keep wiki-link targets identical ([[Same Name]]) โ€” page names are the English names.
    • Translate only prose (intro, exercise prompt, inline inline code labels 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 in
    constants.DOMAIN_DATA[site] โ€” do not change editor config unless required.

7. Branch / commit rules

  • Work on the assigned branch (enrich-<site>), touching ONLY tutorials/<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_DOMAIN env), gunicorn in production.
  • Tutorials are static Markdown per site per language:
    tutorials/<site>/<language>/<Tutorial>.md, parsed by a regex in main.py
    (Tutorial / Tutorial Code / Expected Output / Solution).
  • Translations are STATIC: each language is its own folder of translated .md files.
    There is no runtime or generation script today.
  • constants.py DOMAIN_DATA wires each site to IDEOne (remote compile), editor modes,
    logos, analytics. courses.json holds per-site sponsored course listings.
  • templates/index.html renders tutorials; index-python.html is 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.org is malformed: .md files sit in the root instead of en/.
  • learnassembly.org, learnRust.org, learnsolidity.org are not wired into
    DOMAIN_DATA and are skipped at load.
  • main.py bugs: signin/signup reference an undefined users object; cgi is
    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 to en/).
  • Wire learnassembly.org, learnsolidity.org, learnrust.org into DOMAIN_DATA.

5. Sub-project 2 โ€” learnvibecoding.org (1 PR)

  • New site. WebGPU-only tiny model via @huggingface/transformers, running
    onnx-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/, new DOMAIN_DATA entry, custom
    template, plus gunicorn/nginx/Docker/Makefile deploy wiring.

6. Sub-project 3 โ€” main.py hardening (1 general PR)

  • Fix undefined users object.
  • Harden domain loading / malformed-folder handling.
  • Modernize deprecated cgi (use html module).
  • 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 from master.

9. Verification

  • Run main.py locally per domain (or make build-local) and confirm tutorial parsing
    loads without "not a tutorial" warnings.
  • Validate wiki-links resolve; confirm translation folders parse; run added tests.

--- METRICS ---