# HTML-Kurs erstellen: Python-Stil (Schritt-für-Schritt mit Code-Erklärungen)

Diese Anleitung beschreibt, wie ein KI-Agent eine eigenständige HTML-Datei im Stil von `python.html` erstellt — mit interaktivem Code-Visualizer, Annotationen und eingebettetem Python-Runner.

> **Hinweis:** Diese Variante ist für komplexe, hochinteraktive Kurse gedacht, die über den JSON-Import hinausgehen. Die fertige `.html`-Datei wird direkt auf dem Server abgelegt und in `index.html` als Kurs verlinkt.

---

## Dateistruktur

Eine einzelne HTML-Datei, self-contained. Keine externen Abhängigkeiten außer:
- **Pyodide** (Python im Browser) — wird lazy geladen von `https://cdn.jsdelivr.net/pyodide/v0.25.0/full/`
- Keine CSS-Frameworks, keine JS-Frameworks

Dateiname: z.B. `meinpythonkurs.html`
Verlinkung: in `index.html` als neuer Kurs-Button hinzufügen (wie die bestehenden Kurse)

---

## Grundgerüst

```html
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mein Python Kurs</title>
  <style>
    /* CSS-Variablen — gleiche wie im LMS (siehe ki-html-allgemein.md Abschnitt 2) */
    :root {
      --primary: #4f46e5; --primary-dark: #3730a3; --primary-mid: #6366f1; --primary-light: #eef2ff;
      --bg: #f4f5f9; --surface: #ffffff; --surface-2: #f9fafb;
      --border: #e3e6ef; --border-strong: #c8cdd9;
      --green: #16a34a; --green-light: #f0fdf4;
      --yellow: #d97706; --yellow-light: #fffbeb;
      --red: #dc2626; --red-light: #fef2f2;
      --text: #111827; --text-2: #374151; --muted: #6b7280;
      --mono: 'JetBrains Mono','Fira Code',monospace;
      --r-sm: 6px; --r-md: 10px; --r-lg: 16px;
      --shadow-sm: 0 1px 4px rgba(0,0,0,.07), 0 0 0 1px rgba(0,0,0,.03);
      --t: .18s ease;
    }
    /* ... weitere CSS ... */
  </style>
</head>
<body>

<header>
  <a href="index.html" class="back-to-lms">← Zurück zur Übersicht</a>
  <h1>🐍 Mein Python Kurs</h1>
  <p>Untertitel</p>
</header>

<!-- Concept-Tabs (eine Lektion pro Tab) -->
<div class="concept-tabs" id="cTabs">
  <button class="concept-tab active" data-c="variablen">📦 Variablen</button>
  <button class="concept-tab" data-c="schleifen">🔁 Schleifen</button>
</div>

<main>
  <!-- Lektion: Variablen -->
  <div class="concept-section active" id="sec-variablen">
    <!-- Mode-Tabs: Erklärung / Aufgaben / Quiz -->
    <div class="mode-tabs">
      <button class="mode-tab active" data-c="variablen" data-m="erkl">Erklärung</button>
      <button class="mode-tab" data-c="variablen" data-m="aufg">Aufgaben</button>
      <button class="mode-tab" data-c="variablen" data-m="quiz">Quiz</button>
    </div>

    <!-- Erklärungs-Panel -->
    <div class="mode-panel active" id="variablen-erkl">
      <!-- Code-Visualizer -->
      <div class="visualizer">
        <div class="vis-code-box">
          <div class="box-lbl">Code</div>
          <ol class="code-lines" id="code-variablen">
            <!-- Zeilen werden per JS eingefügt -->
          </ol>
          <div class="vis-ctrl">
            <button class="btn" onclick="step('variablen',-1)">← Zurück</button>
            <span class="step-ctr" id="ctr-variablen"></span>
            <button class="btn pri" onclick="step('variablen',1)">Weiter →</button>
          </div>
        </div>
        <div class="vis-right">
          <!-- Annotationen — rechte Seite -->
          <div class="intro-annotations" id="annos-variablen"></div>
        </div>
      </div>
    </div>

    <!-- Aufgaben-Panel -->
    <div class="mode-panel" id="variablen-aufg">
      <!-- Aufgaben hier -->
    </div>

    <!-- Quiz-Panel -->
    <div class="mode-panel" id="variablen-quiz">
      <!-- Quiz hier -->
    </div>
  </div>
</main>

<script>
// ... JS ...
</script>
</body>
</html>
```

---

## Code-Visualizer mit Annotationen

Das Herzstück des Python-Kurses: Code-Zeilen links, klickbare Erklärungen rechts.

### Datenstruktur

```js
const AUFBAU_ANNOS = [
  {
    lines: [0],          // 0-basierte Zeilennummern die hervorgehoben werden
    label: '1 – Variable anlegen',
    text:  '<code>alter = 15</code> speichert die Zahl 15 unter dem Namen <code>alter</code>.'
  },
  {
    lines: [1, 2],       // mehrere Zeilen gleichzeitig hervorheben
    label: '2 – Ausgabe',
    text:  '<code>print()</code> gibt Werte in der Konsole aus.'
  }
];

const CODE_VARIABLEN = [
  'alter = 15',
  "name = 'Anna'",
  'print(name, alter)'
];
```

### Visualizer initialisieren

```js
const _steps = {};  // sectionId → aktueller Schritt

function initVisualizer(id, codeLines, annos) {
  _steps[id] = 0;

  // Code-Zeilen rendern
  const ol = document.getElementById('code-' + id);
  ol.innerHTML = codeLines.map((line, i) =>
    `<li class="code-line" id="${id}-line-${i}">${esc(line) || '&nbsp;'}</li>`
  ).join('');

  // Annotationen rendern
  const annoEl = document.getElementById('annos-' + id);
  annoEl.innerHTML = annos.map((a, ai) =>
    `<div class="anno" id="${id}-anno-${ai}" onclick="jumpTo('${id}',${ai})">
      <div class="anno-label">${esc(a.label)}</div>
      <div class="anno-text">${a.text}</div>
    </div>`
  ).join('');

  showStep(id, codeLines, annos, 0);
}

function showStep(id, codeLines, annos, idx) {
  const anno = annos[idx];
  // Alle Zeilen zurücksetzen
  codeLines.forEach((_, i) =>
    document.getElementById(`${id}-line-${i}`)?.classList.remove('active','done')
  );
  // Vergangene Zeilen als "done" markieren
  annos.slice(0, idx).forEach(a =>
    a.lines.forEach(l => document.getElementById(`${id}-line-${l}`)?.classList.add('done'))
  );
  // Aktuelle Zeilen hervorheben
  anno.lines.forEach(l =>
    document.getElementById(`${id}-line-${l}`)?.classList.add('active')
  );
  // Annotationen hervorheben
  annos.forEach((_, ai) =>
    document.getElementById(`${id}-anno-${ai}`)?.classList.toggle('highlight', ai === idx)
  );
  // Zähler
  document.getElementById('ctr-' + id).textContent = `${idx + 1} / ${annos.length}`;
}

function step(id, dir) {
  // Daten pro Sektion separat halten (s. Initialisierung)
  const data = SECTION_DATA[id];
  if (!data) return;
  _steps[id] = Math.max(0, Math.min(data.annos.length - 1, (_steps[id] || 0) + dir));
  showStep(id, data.code, data.annos, _steps[id]);
}

function jumpTo(id, idx) {
  const data = SECTION_DATA[id];
  if (!data) return;
  _steps[id] = idx;
  showStep(id, data.code, data.annos, idx);
}

// Alle Sektionsdaten zentral ablegen
const SECTION_DATA = {
  variablen: { code: CODE_VARIABLEN, annos: AUFBAU_ANNOS },
  // weitere Sektionen...
};

// Beim Laden initialisieren
Object.entries(SECTION_DATA).forEach(([id, d]) => initVisualizer(id, d.code, d.annos));
```

---

## Python-Runner (Aufgaben)

Python läuft im Browser via **Pyodide**. Erst beim ersten Klick auf "Ausführen" laden.

```js
let _pyodide = null;
let _pyodideLoading = false;

async function loadPyodide() {
  if (_pyodide) return _pyodide;
  if (_pyodideLoading) { while (_pyodideLoading) await new Promise(r => setTimeout(r, 100)); return _pyodide; }
  _pyodideLoading = true;
  const script = document.createElement('script');
  script.src = 'https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js';
  document.head.appendChild(script);
  await new Promise(r => script.onload = r);
  _pyodide = await window.loadPyodide();
  _pyodideLoading = false;
  return _pyodide;
}

async function runCode(textareaId, outputId, btnId) {
  const ta = document.getElementById(textareaId);
  const out = document.getElementById(outputId);
  const btn = document.getElementById(btnId);
  if (!ta || !out) return;
  btn.textContent = '⏳ Lädt…';
  btn.disabled = true;
  out.style.display = 'block';
  out.textContent = 'Python wird geladen…';
  try {
    const py = await loadPyodide();
    // stdout abfangen
    let stdout = '';
    py.setStdout({ batched: s => stdout += s + '\n' });
    py.setStderr({ batched: s => stdout += '⚠ ' + s + '\n' });
    await py.runPythonAsync(ta.value);
    out.textContent = stdout || '(keine Ausgabe)';
    out.style.color = '';
  } catch (e) {
    out.textContent = '❌ ' + e.message;
    out.style.color = 'var(--red)';
  }
  btn.textContent = '▶ Ausführen';
  btn.disabled = false;
}
```

### Aufgabe mit Code-Editor rendern

```html
<div class="aufgabe-card">
  <div class="aufg-num">Aufgabe 1</div>
  <div class="aufg-title">Variablen anlegen</div>
  <p class="aufg-desc">Erstelle zwei Variablen <code>vorname</code> und <code>alter</code> und gib sie aus.</p>
  <textarea id="code-a1" class="code-ta" spellcheck="false">
# Dein Code hier
</textarea>
  <div style="display:flex;gap:.5rem;margin-bottom:.5rem">
    <button class="btn pri" onclick="runCode('code-a1','out-a1','btn-a1')" id="btn-a1">▶ Ausführen</button>
    <button class="btn" onclick="document.getElementById('code-a1').value='# Dein Code hier\n'">↺ Reset</button>
  </div>
  <pre id="out-a1" class="code-output" style="display:none"></pre>
  <label class="aufg-done">
    <input type="checkbox" onchange="markDone('a1', this.checked)"> Als erledigt markieren
  </label>
</div>
```

---

## Fortschritts-System

Identisch zu den anderen Kursen — SHA-256(Passphrase) → `hash/get` → `hash/report`:

```js
const COURSE_ID = 'meinpythonkurs';  // muss eindeutig sein
let IDENTITY = null;
let PB = new Uint8Array(/* Anzahl Lektionen */ 4);

// Lesson-Bits: pro Lektion 8 Bit
// Bit 0 = Lektion abgeschlossen
// Bit 1,2,3... = einzelne Aufgaben

async function hashPhrase(phrase) {
  const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(phrase));
  return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2,'0')).join('');
}
```

Fortschritt wird beim Logout und bei Aufgaben-Abschluss an `api.php` gesendet (`hash/report`).

---

## Tab-Navigation (Concept-Tabs + Mode-Tabs)

```js
// Concept-Tabs (Lektionen)
document.getElementById('cTabs').addEventListener('click', e => {
  const t = e.target.closest('.concept-tab');
  if (!t) return;
  const key = t.dataset.c;
  document.querySelectorAll('.concept-tab').forEach(b => b.classList.toggle('active', b.dataset.c === key));
  document.querySelectorAll('.concept-section').forEach(s => s.classList.toggle('active', s.id === 'sec-' + key));
});

// Mode-Tabs (Erklärung / Aufgaben / Quiz)
document.addEventListener('click', e => {
  const t = e.target.closest('.mode-tab');
  if (!t) return;
  const c = t.dataset.c, m = t.dataset.m;
  document.querySelectorAll(`.mode-tab[data-c="${c}"]`).forEach(b => b.classList.toggle('active', b.dataset.m === m));
  document.querySelectorAll(`#sec-${c} .mode-panel`).forEach(p => p.classList.toggle('active', p.id === `${c}-${m}`));
});
```

---

## Quiz-System

```js
const QUIZ = {
  variablen: [
    {
      q: 'Was speichert <code>x = 5</code>?',
      opts: ['Den Text "5"', 'Die Zahl 5', 'Den Namen x', 'Nichts'],
      ok: 1,
      ex: 'x = 5 weist der Variable x den ganzzahligen Wert 5 zu.'
    }
  ]
};

let _quizIdx = {};
let _quizScore = {};

function initQuiz(id) {
  _quizIdx[id] = 0;
  _quizScore[id] = 0;
  showQuestion(id);
}

function showQuestion(id) {
  const q = QUIZ[id][_quizIdx[id]];
  // ... Quiz-HTML rendern ...
}
```

---

## Checkliste für den KI-Agenten

Beim Erstellen eines Python-HTML-Kurses sicherstellen:

- [ ] `COURSE_ID` ist eindeutig (keine Überschneidung mit anderen Kursen)
- [ ] In `index.html` als neuer Kurs-Button verlinkt
- [ ] CSS-Variablen aus `:root` verwendet (kein hardcodiertes Styling)
- [ ] `esc()`-Funktion für alle user-generierten Inhalte verwendet
- [ ] Pyodide lazy geladen (nicht beim Seitenaufruf)
- [ ] Fortschritt per `hash/report` an `api.php` gesendet
- [ ] Passphrase aus `localStorage['lms_passphrase']` gelesen
- [ ] Auth-Token in `sessionStorage['lms_auth_COURSE_ID']` gesetzt
- [ ] Kein `console.log` im Produktionscode
- [ ] Alle Texte auf Deutsch
